diff --git a/pkgs/ok_http/CHANGELOG.md b/pkgs/ok_http/CHANGELOG.md index fccf2fbeb2..aa4ab34b2c 100644 --- a/pkgs/ok_http/CHANGELOG.md +++ b/pkgs/ok_http/CHANGELOG.md @@ -2,8 +2,8 @@ - `OkHttpClient` now receives an `OkHttpClientConfiguration` to configure the client on a per-call basis. - `OkHttpClient` supports setting four types of timeouts: [`connectTimeout`](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-ok-http-client/-builder/connect-timeout.html), [`readTimeout`](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-ok-http-client/-builder/read-timeout.html), [`writeTimeout`](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-ok-http-client/-builder/write-timeout.html), and [`callTimeout`](https://square.github.io/okhttp/5.x/okhttp/okhttp3/-ok-http-client/-builder/call-timeout.html), using the `OkHttpClientConfiguration`. -- Upgrade to `jni` 0.13.0 -- Upgrade to `jnigen` 0.13.1 +- Upgrade to `jni` 0.14.0 +- Upgrade to `jnigen` 0.14.0 - `OKHttpClient` supports client certificates. ## 0.1.0 diff --git a/pkgs/ok_http/example/integration_test/certificate_test.dart b/pkgs/ok_http/example/integration_test/certificate_test.dart index 0a88339089..da2d29b3a8 100644 --- a/pkgs/ok_http/example/integration_test/certificate_test.dart +++ b/pkgs/ok_http/example/integration_test/certificate_test.dart @@ -38,6 +38,26 @@ void main() async { expect(chain[0].getType()!.toDartString(), 'X.509'); }); + test('no key', () async { + final certBytes = + await loadCertificateBytes('test_certs/server_chain.p12'); + expect( + () => loadPrivateKeyAndCertificateChainFromPKCS12( + certBytes, 'dartdart'), + throwsA(isA() + .having((e) => e.message, 'toString', contains('no key')))); + }); + + test('no chain', () async { + final certBytes = + await loadCertificateBytes('test_certs/server_key.p12'); + expect( + () => loadPrivateKeyAndCertificateChainFromPKCS12( + certBytes, 'dartdart'), + throwsA(isA().having((e) => e.message, 'toString', + contains('no certificate chain')))); + }); + test('bad password', () async { final certBytes = await loadCertificateBytes('test_certs/test-combined.p12'); diff --git a/pkgs/ok_http/lib/src/ok_http_client.dart b/pkgs/ok_http/lib/src/ok_http_client.dart index 5ce7103f09..7d96671216 100644 --- a/pkgs/ok_http/lib/src/ok_http_client.dart +++ b/pkgs/ok_http/lib/src/ok_http_client.dart @@ -23,10 +23,6 @@ import 'package:jni/jni.dart'; import 'jni/bindings.dart' as bindings; import 'jni/bindings.dart' show PrivateKey, X509Certificate; -extension on List { - JByteArray toJByteArray() => JByteArray(length)..setRange(0, length, this); -} - class _JavaIOException extends IOException { final String _message; _JavaIOException(JniException e) : _message = e.message; @@ -154,14 +150,20 @@ Future choosePrivateKeyAlias({ } try { keyStore.load( - bindings.ByteArrayInputStream(pkcs12Data.toJByteArray()), jPassword); + bindings.ByteArrayInputStream(JByteArray.from(pkcs12Data)), jPassword); } on JniException catch (e) { if (e.message.contains('java.io.IOException')) { throw _JavaIOException(e); } } - assert(keyStore.size() == 1, 'unexpected KeyStore size: ${keyStore.size()}'); + if (keyStore.size() == 0) { + throw ArgumentError('no key in PKC12 data', 'pkcs12Data'); + } + + if (keyStore.size() > 1) { + throw ArgumentError('multiple entries in PKC12 data', 'pkcs12Data'); + } final aliases = keyStore.aliases()!; final jAlias = aliases.nextElement()!; @@ -175,11 +177,17 @@ Future choosePrivateKeyAlias({ throw ArgumentError('no certificate chain in PKC12 data', 'pkcs12Data'); } - // TODO: Add `isA` type checks when - // https://github.com/dart-lang/native/pull/1943 is released. + if (!pk.isA(PrivateKey.type)) { + throw ArgumentError('certificate key is not a PrivateKey', 'pkcs12Data'); + } - final certificates = - jCertificates.map((c) => c!.as(X509Certificate.type)).toList(); + final certificates = jCertificates.map((c) { + if (c == null || !c.isA(X509Certificate.type)) { + throw ArgumentError( + 'certificate chain contains non-X509 certificates', 'pkcs12Data'); + } + return c.as(X509Certificate.type); + }).toList(); return (pk.as(PrivateKey.type), certificates); } @@ -241,14 +249,11 @@ class OkHttpClient extends BaseClient { final trustManagers = JArray(bindings.TrustManager.nullableType, 1); if (clientPrivateKey != null && clientCertificateChain != null) { - // TODO: Switch to `JArray.of` when package:jnigen 0.20 is released. - // This does not work if `clientCertificateChain` is empty list. - final chain = JArray.filled( - clientCertificateChain.length, clientCertificateChain[0]) - ..setRange(0, clientCertificateChain.length, clientCertificateChain); - final foo = bindings.FixedResponseX509ExtendedKeyManager( + final chain = + JArray.of(bindings.X509Certificate.type, clientCertificateChain); + final keyManager = bindings.FixedResponseX509ExtendedKeyManager( chain, clientPrivateKey, 'DUMMY'.toJString()); - keyManagers = JArray.filled(1, foo.as(bindings.KeyManager.type), + keyManagers = JArray.filled(1, keyManager.as(bindings.KeyManager.type), E: bindings.KeyManager.type); } @@ -357,7 +362,7 @@ class OkHttpClient extends BaseClient { // So, we need to handle this case separately. bindings.RequestBody? okReqBody; if (requestMethod != 'GET' && requestMethod != 'HEAD') { - okReqBody = bindings.RequestBody.create$10(requestBody.toJByteArray()); + okReqBody = bindings.RequestBody.create$10(JByteArray.from(requestBody)); } reqBuilder.method( diff --git a/pkgs/ok_http/pubspec.yaml b/pkgs/ok_http/pubspec.yaml index 7805d8b5ed..128f4d2f7c 100644 --- a/pkgs/ok_http/pubspec.yaml +++ b/pkgs/ok_http/pubspec.yaml @@ -13,13 +13,13 @@ dependencies: sdk: flutter http: ^1.2.1 http_profile: ^0.1.0 - jni: ^0.13.0 + jni: ^0.14.0 plugin_platform_interface: ^2.0.2 web_socket: ^0.1.5 dev_dependencies: dart_flutter_team_lints: ^3.0.0 - jnigen: ^0.13.1 + jnigen: ^0.14.0 flutter: plugin: