Merge upstream master

Main changes:

* Fix the SSLEngine state machine.  This should make SSLEngine follow
  the documented behavior much more closely.

* Add support for AES-GCM-SIV.

* Refactor tests to use a common pattern with helper classes.

* Refactor the OpenSSLCipher class into a bunch of separate classes.

Test: cts -m CtsLibcoreTestCases
Test: cts -m CtsLibcoreOkHttpTestCases
Change-Id: Id9db9bae66d629ee1cd34255c4ca5a7a3c03ba7f
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..3fe4c92
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,441 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_visibility: [
+        ":__subpackages__",
+    ],
+}
+
+//
+// Definitions for building the Conscrypt Java library, native code,
+// and associated tests.
+//
+
+// Conscrypt is divided into subdirectories.
+//
+// The structure is:
+//
+//   constants/
+//       src/gen             # Generates NativeConstants.java.
+//   common/
+//       src/main/java       # Common Java source for all platforms.
+//       src/jni/
+//            main           # Common C++ source for all platforms.
+//            unbundled      # C++ source used for OpenJDK and unbundled Android.
+//       src/test/java       # Common test files for all platforms.
+//   android/
+//       src/main/java       # Java source for unbundled Android.
+//   openjdk/
+//       src/main/java       # Java source for OpenJDK.
+//       src/test
+//            java/          # Java source for common tests.
+//            resources/     # Support files for tests
+//   platform/
+//       src/main/java       # Java source for bundled Android.
+//       src/test
+//            java/          # Java source for bundled tests.
+//
+
+cc_defaults {
+    name: "conscrypt_global",
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wunused",
+    ],
+
+    srcs: [
+        "common/src/jni/main/cpp/conscrypt/compatibility_close_monitor.cc",
+        "common/src/jni/main/cpp/conscrypt/jniload.cc",
+        "common/src/jni/main/cpp/conscrypt/jniutil.cc",
+        "common/src/jni/main/cpp/conscrypt/native_crypto.cc",
+        "common/src/jni/main/cpp/conscrypt/netutil.cc",
+        "common/src/jni/main/cpp/conscrypt/trace.cc",
+    ],
+
+    local_include_dirs: [
+        "common/src/jni/main/include",
+    ],
+
+    compile_multilib: "both",
+    stl: "c++_static",
+}
+
+cc_defaults {
+    name: "conscrypt_unbundled-jni-defaults",
+
+    local_include_dirs: [
+        "common/src/jni/unbundled/include",
+    ],
+
+    shared_libs: [
+        "liblog",
+    ],
+
+    static_libs: [
+        "libssl",
+        "libcrypto",
+    ],
+
+    sdk_version: "9",
+}
+
+cc_library {
+    name: "libconscrypt_jni",
+    defaults: [
+        "conscrypt_global",
+        "conscrypt_unbundled-jni-defaults",
+    ],
+}
+
+cc_library_host_shared {
+    name: "libconscrypt_openjdk_jni",
+    defaults: ["conscrypt_global"],
+
+    cflags: [
+        "-DCONSCRYPT_OPENJDK",
+    ],
+
+    local_include_dirs: [
+        "common/src/jni/unbundled/include",
+    ],
+
+    static_libs: [
+        "libssl",
+        "libcrypto",
+    ],
+
+    // TODO: b/26097626. ASAN breaks use of this library in JVM.
+    // Re-enable sanitization when the issue with making clients of this library
+    // preload ASAN runtime is resolved. Without that, clients are getting runtime
+    // errors due to unresolved ASAN symbols, such as
+    // __asan_option_detect_stack_use_after_return.
+    sanitize: {
+        never: true,
+    },
+
+    stl: "libc++_static",
+
+    // The post-build signing tools need signapk.jar and its shared libs
+    multilib: {
+        lib64: {
+            dist: {
+                targets: ["droidcore"],
+            },
+        },
+    },
+}
+
+cc_binary_host {
+    name: "conscrypt_generate_constants",
+    srcs: ["constants/src/gen/cpp/generate_constants.cc"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libcrypto",
+        "libssl",
+    ],
+}
+
+genrule {
+    name: "conscrypt-nojarjar_generated_constants",
+    out: ["org/conscrypt/NativeConstants.java"],
+    cmd: "$(location conscrypt_generate_constants) > $(out)",
+    tools: ["conscrypt_generate_constants"],
+}
+
+// Create the conscrypt library without jarjar for tests
+java_library {
+    name: "conscrypt-nojarjar",
+    visibility: [
+        "//build/make/tools/signapk",
+    ],
+    host_supported: true,
+    hostdex: true,
+
+    srcs: [
+        "common/src/main/java/**/*.java",
+        ":conscrypt-nojarjar_generated_constants",
+    ],
+
+    sdk_version: "none",
+    system_modules: "core-all-system-modules",
+    target: {
+        android: {
+            srcs: ["platform/src/main/java/**/*.java"],
+            libs: ["core-all"],
+        },
+        host: {
+            srcs: ["openjdk/src/main/java/**/*.java"],
+            javacflags: ["-XDignore.symbol.file"],
+        },
+    },
+
+    required: ["libjavacrypto"],
+    java_version: "1.7",
+}
+
+genrule {
+    name: "conscrypt_generated_constants",
+    out: ["com/android/org/conscrypt/NativeConstants.java"],
+    cmd: "$(location conscrypt_generate_constants) com.android.org.conscrypt > $(out)",
+    tools: ["conscrypt_generate_constants"],
+}
+
+filegroup {
+    name: "conscrypt_java_files",
+    visibility: [
+        "//libcore",
+    ],
+    srcs: [
+        "repackaged/common/src/main/java/**/*.java",
+        "repackaged/platform/src/main/java/**/*.java",
+        ":conscrypt_generated_constants",
+    ],
+}
+
+filegroup {
+    name: "conscrypt_public_api_files",
+    visibility: [
+        "//libcore",
+    ],
+    srcs: ["publicapi/src/main/java/**/*.java"],
+}
+
+// Create the conscrypt library from the source produced by the srcgen/generate_android_src.sh
+// script.
+java_library {
+    name: "conscrypt",
+    visibility: [
+        "//art/build",
+        "//device:__subpackages__",
+        "//external/okhttp",
+        "//external/robolectric-shadows",
+        "//external/wycheproof",
+        "//libcore",
+        "//system/apex/tests",
+        ":__subpackages__",
+    ],
+    installable: true,
+    hostdex: true,
+
+    srcs: [
+        ":conscrypt_java_files",
+        ":conscrypt_public_api_files",
+    ],
+
+    // Conscrypt can be updated independently from the other core libraries so it must only depend
+    // on public SDK and intra-core APIs.
+    sdk_version: "none",
+    libs: ["core.intra.stubs"],
+    patch_module: "java.base",
+    system_modules: "core-intra-stubs-system-modules",
+
+    // Workaround for b/124476339: libjavacrypto is required for both APEX and
+    // hostdex builds, but adding a top-level required property results in
+    // it being installed to /system on Android.
+    // TODO(b/124476339): move required back to a top level property
+    target: {
+        hostdex: {
+            required: ["libjavacrypto"],
+        },
+    },
+
+    permitted_packages: [
+        "android.net.ssl",
+        "com.android.org.conscrypt",
+    ],
+}
+
+// A guaranteed unstripped version of conscrypt.
+// The build system may or may not strip the conscrypt jar, but this one will
+// not be stripped. See b/24535627.
+java_library {
+    name: "conscrypt-testdex",
+    installable: true,
+
+    static_libs: ["conscrypt"],
+    dex_preopt: {
+        enabled: false,
+    },
+
+    sdk_version: "core_platform",
+
+    required: ["libjavacrypto"],
+}
+
+// Platform conscrypt crypto JNI library
+cc_defaults {
+    name: "libjavacrypto-defaults",
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wunused",
+        "-fvisibility=hidden",
+    ],
+
+    srcs: ["common/src/jni/main/cpp/**/*.cc"],
+    local_include_dirs: ["common/src/jni/main/include"],
+}
+
+// Platform conscrypt crypto JNI library
+cc_library_shared {
+    name: "libjavacrypto",
+    host_supported: true,
+    defaults: ["libjavacrypto-defaults"],
+
+    cflags: ["-DJNI_JARJAR_PREFIX=com/android/"],
+    header_libs: ["libnativehelper_header_only"],
+    shared_libs: [
+        "libcrypto",
+        "liblog",
+        "libssl",
+    ],
+
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}
+
+// Unbundled Conscrypt jar
+java_library {
+    name: "conscrypt_unbundled",
+
+    srcs: [
+        "common/src/main/java/**/*.java",
+        "android/src/main/java/**/*.java",
+        ":conscrypt-nojarjar_generated_constants",
+    ],
+
+    libs: ["conscrypt-stubs"],
+
+    sdk_version: "current",
+    java_version: "1.7",
+}
+
+// Stub library for unbundled builds
+java_library {
+    name: "conscrypt-stubs",
+
+    srcs: ["android-stub/src/main/java/**/*.java"],
+
+    sdk_version: "current",
+    java_version: "1.7",
+}
+
+// Static unbundled Conscrypt crypto JNI library
+cc_library_static {
+    name: "libconscrypt_static",
+    defaults: ["libjavacrypto-defaults"],
+
+    cflags: [
+        "-DJNI_JARJAR_PREFIX=com/google/android/gms/",
+        "-DCONSCRYPT_UNBUNDLED",
+        "-DSTATIC_LIB",
+    ],
+
+    local_include_dirs: ["common/src/jni/unbundled/include"],
+
+    static_libs: [
+        "libssl",
+        "libcrypto",
+    ],
+    sdk_version: "9",
+    stl: "c++_shared",
+}
+
+// Make the conscrypt-tests library.
+java_test {
+    name: "conscrypt-tests",
+    visibility: [
+        "//cts/tests/libcore/luni",
+    ],
+    hostdex: true,
+    srcs: [
+        "repackaged/platform/src/test/java/**/*.java",
+        "repackaged/common/src/test/java/**/*.java",
+        "repackaged/openjdk-integ-tests/src/test/java/**/*.java",
+        "repackaged/testing/src/main/java/**/*.java",
+        "publicapi/src/test/java/**/*.java",
+    ],
+    java_resource_dirs: [
+        // Resource directories do not need repackaging.
+        "openjdk/src/test/resources",
+        "openjdk-integ-tests/src/test/resources",
+    ],
+
+    sdk_version: "none",
+    libs: [
+        "core-all",
+        "conscrypt",
+        "junit",
+        "mockito-target-minus-junit4",
+    ],
+    system_modules: "core-all-system-modules",
+
+    static_libs: [
+        "bouncycastle-unbundled",
+        "bouncycastle-bcpkix-unbundled",
+        "bouncycastle-ocsp-unbundled",
+    ],
+    javacflags: [
+        "-Xmaxwarns 9999999",
+        //"-Xlint:all",
+        //"-Xlint:-serial,-deprecation,-unchecked",
+    ],
+
+    required: ["libjavacrypto"],
+    java_version: "1.7",
+}
+
+// Make the conscrypt-benchmarks library.
+java_test {
+    name: "conscrypt-benchmarks",
+    srcs: [
+        "repackaged/testing/src/main/java/**/*.java",
+        "repackaged/benchmark-base/src/main/java/**/*.java",
+        "repackaged/benchmark-android/src/main/java/**/*.java",
+    ],
+    sdk_version: "none",
+    libs: [
+        "core-all",
+        "conscrypt",
+        "junit",
+        "bouncycastle-unbundled",
+        "bouncycastle-bcpkix-unbundled",
+        "bouncycastle-ocsp-unbundled",
+        "caliper-api-target",
+    ],
+    system_modules: "core-all-system-modules",
+
+    javacflags: [
+        "-Xmaxwarns 9999999",
+        //"-Xlint:all",
+        //"-Xlint:-serial,-deprecation,-unchecked",
+    ],
+
+    required: ["libjavacrypto"],
+    java_version: "1.7",
+}
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..105c94b
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,6 @@
+flooey@google.com
+kroot@google.com
+narayan@google.com
+nfuller@google.com
+prb@google.com
+tobiast@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 4c8d137..84d310f 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,3 +1,4 @@
 [Builtin Hooks]
 commit_msg_test_field = true
 clang_format = true
+bpfmt = true
diff --git a/README.android b/README.android
new file mode 100644
index 0000000..1b2919c
--- /dev/null
+++ b/README.android
@@ -0,0 +1,12 @@
+Conscrypt in Android is made up of the contents of the Conscrypt
+GitHub repo (https://github.com/google/conscrypt) plus some
+Android-specific additions.  Specifically, the following are
+Android-only:
+
+Android.bp
+OWNERS
+README.android
+apex/...
+publicapi/...
+repackaged/...
+srcgen/...
diff --git a/apex/Android.bp b/apex/Android.bp
new file mode 100644
index 0000000..129da6a
--- /dev/null
+++ b/apex/Android.bp
@@ -0,0 +1,47 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Defaults shared between real and test versions of the APEX.
+apex_defaults {
+    name: "com.android.conscrypt-defaults",
+    androidManifest: ":com.android.conscrypt-androidManifest",
+    compile_multilib: "both",
+    java_libs: ["conscrypt"],
+    native_shared_libs: ["libjavacrypto"],
+    key: "apex.conscrypt.key",
+    certificate: ":com.android.conscrypt.certificate",
+}
+
+filegroup {
+    name: "com.android.conscrypt-androidManifest",
+    srcs: ["AndroidManifest.xml"],
+}
+
+apex_key {
+    name: "apex.conscrypt.key",
+    public_key: "com.android.conscrypt.avbpubkey",
+    private_key: "com.android.conscrypt.pem",
+}
+
+android_app_certificate {
+    name: "com.android.conscrypt.certificate",
+    certificate: "com.android.conscrypt",
+}
+
+// Production APEX
+apex {
+    name: "com.android.conscrypt",
+    defaults: ["com.android.conscrypt-defaults"],
+    manifest: "apex_manifest.json",
+}
diff --git a/apex/AndroidManifest.xml b/apex/AndroidManifest.xml
new file mode 100644
index 0000000..c25cc17
--- /dev/null
+++ b/apex/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+  package="com.android.conscrypt">
+  <!-- APEX does not have classes.dex -->
+  <application android:hasCode="false" />
+  <!--
+    *  API levels the Conscrypt APEX is known to work with.
+    -->
+  <uses-sdk android:minSdkVersion="28" android:maxSdkVersion="28" />
+</manifest>
diff --git a/apex/apex_manifest.json b/apex/apex_manifest.json
new file mode 100644
index 0000000..98222ad
--- /dev/null
+++ b/apex/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.conscrypt",
+  "version": 1
+}
diff --git a/apex/com.android.conscrypt.avbpubkey b/apex/com.android.conscrypt.avbpubkey
new file mode 100644
index 0000000..5ce0fbd
--- /dev/null
+++ b/apex/com.android.conscrypt.avbpubkey
Binary files differ
diff --git a/apex/com.android.conscrypt.pem b/apex/com.android.conscrypt.pem
new file mode 100644
index 0000000..7979607
--- /dev/null
+++ b/apex/com.android.conscrypt.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKgIBAAKCAgEAwWI5g+8IxjlN9HY4tNVcE12z7CHRoD1EU7WUnPIJR/OeaIBm
+eDQIyfDSkcE+KQ8RbiRLHbp5xxbTvstNNt5l7cb3U6HWhNar8bEkGadSy8jer+BI
+Hn9UY14SeEh3TUfLA8VBU7H20k3lfpzvOCdzECMthqPryIx4DWK3Thir1qwak/3b
+nF5hXAUx6FJalZ3eEvZPtFy+qW/Xk6PFKfKybQCLTb/3n0ppegMCPAaWuqYT9GOz
+JKtDEqekIeEYdMIh+Lf29aVMQNusB+3Smb+t9ys1qk/Bz3CyXTej3A+m55btWW2i
+GE5j3fWvdIGsBOjoeBE7WkXnvbNEwjkRDYY/IS6kLq10Y1uhdwuHv0VpuIkrm7c/
+bazPF6z5CRLqgCrik/Y00xV7/osR9e9+ywfAhJL0NMuta1bfu0Veny6rC3PlxbhK
+4EbUaGzj4mWnIwqml15QxmQFjTVOrnqFs93kpb/3ZTtA53bR8wLKQph5/TJv6IMH
+BOAVHNxC73sCH2TlabFAHPnJzdsHcdvtOIHRp5EYdV++T4a9k1syKWGKStzyIAA2
+tZ434ca/YqQpa3m6+H9NTcJeR6cfP1repbJouNhnlTMl07ttcxFKss+mbcXJhi78
+rllwGrGqVGb2+mcY2cgvpYYo3xAwa5e3qrMkaThCxrXpdleDzh/8S+qrky0CAwEA
+AQKCAgA4g0vrkl8uDnEJj8LilbdisW9zAi8QdNcS322er9ymejtArsVDO3GWEhp3
+HdHcjdq6JUEOXwBXfgRDLYZTEosXAZ4lgpX+G/4Dk5DJLmyzwO/S1eg9gVhX8ZXU
+MNksbF6Xq317x/YpXzpB1frSnjSx8pXLUvwEj4hF4SNQX0VYZtMdjIIxICOzkJEV
+I3v1XT0YrYJ9Yt0VBuoo6yMjLxmVYSNUhVUH5+cLEPYGNKa3xPDv6nLftGzVdsgw
+XmeXN2RWGuzAvJ2cFpYfexCgfbOJF8eHDefFh1mYhDoOhURxSVnzWGaWU9I7eyDb
+G0n55VaKP/7oUqV2en0NhT/GhATOW21whRm5D34oJyWSqry5mxE3wxmwJUOcon4q
+oiRn5iBq7rnUoEAzEK1pgdTKSNxIkuGmWmo/b1n0oxCSmDS+VTp1nC6i0eZ7YXGX
+aYxnouJDcNICTOnqKo2DYEZ26jQzqw53gcdeP9NXYgRrSa1ZUT0malD/I0m1CLUl
+lwdu+VDEEmnA0FXbs9BNUzWZut1XBelz6MsDrbcNKte/2Ikk7OhpvGKdnw7rqpWW
+MEueusIcBGK7YABJlwheIpynQBtaVWGalAy3hi1bZC5NqT0xQP2V1536pljsai+t
+cdi98bn5bZa7OluZLTN1uLIKfIKjDbPt43vYYuh/qi0D7sa8wQKCAQEA6fYRE8Dw
+3dUhkitkLuhmpVsBrxjfic9d9ypgLd1u57chd7tc8HlKE8+tTY+AI4EvKUcEweiE
+eO/DAnYijZRNY89g9JZcur4exo79UvPnayWRVS4wCwf57DrvjxkDJS97vWZRI1ns
+MS9/bNtg2RCrfReHpSC4vxZAfH6Gmt1wfJZpVcOAPPqeZX19n6N7VkFSzhw8axAj
+Py3HG5uGvE3jhouiMG1vHiMumUdkOOxkc7cjMojujQ0XIVIso02/zQSKf6QUe6bl
+ZUJTkl1GHWkEFU0bmKylfwW9GVW1fxw2qjlrLWpWFaKrbgQptvae+K1zSfQsGXab
+w6mhDvdrtDKKJwKCAQEA05mjcqPcBN3E1BI57ylcsWlD/8pVsIeBmFzrng9JJ672
+P4iZ8IxkQO4Y8zoywYNTEkgeVvertghMIy+hFVW+/7dKlXPwZhurtY6FUzy79BA7
+CKJwSyPDWJY7ol0yGkv4ME5RQZ8fGw23him7RuNl1LKHQ2NIplS5ezhZUHa6ckHG
+yM1pRjy9H9TK6XtlsSME8gej3BmU9UsAMWhMBmz8UpYYX8SmgTJIwhcZ7eM3RbyG
+S7bmw5jrxe9v0RYBh0EuVkBm+iAIY8pvCL4imNu/sEmjaBDpyNSe/0DXxjHPydAZ
+K46n92I5gRF8GfeKJMuogZGagxUFXijYVsjN4FrwiwKCAQEAvIJUjisGqGajDwhB
+R35fvloOiEutSXe5CX6uUiY5xyAKKlPf9a50nnPV+klmgLUFD+g2EBtOKbdd1Czl
+eFgG5yXfxBMnEhw/5dKukFkPnIh/ijeV5D2ABPQTs6P+ocaZmCjJtYctxQ+1RzuX
+1C4XTspXtBgiY20Fdonn4P4NNYVnx/+m4vs4ByROxQLPTNeDZajgkY9GJxC2fisZ
+K75CVlVJ0GVMg+brE/uu6CKoaYglJwGn3CSw/1sbtlTd9s3y3heRnXQDH0yK08Kt
+zOKhtJwVYTWyfx9blv81Jv7PmOZ80fH8/J++Kv3VsqMRweLXor3Hjpi/tq6Fo+59
+bIrQPwKCAQEApInWuCFEyB/umv2lQRzHsEjrkG60nTVxh5nRubGnMA1z/Elrcsqo
+dnjuu8uohiNpKFEeDoA+bKkdE0tJFf78K3pKq9Zgu/WOWvp9IBGdEZbBYOB3M2aW
+0z7XFlUjzaD/WAi+VKm4FWBgMhonP2M53uAIIFWGu5gsNu3FPbVlG82cFq7ryqsW
+YTclpdLHa6uQf+eC+naGBwuQFdtFKsX6mvmN8IJI+zOvsgUmq33AkCCdyShrXvN9
+ewfJyiszeworTvR7Xsoj2/0gxAqdeoF+GEXao+Rq9jinflLctogrTIHd4KnoYIxn
+rL584vBC4oPe/wnKvV1OND7Mowc4V9o/KwKCAQEAxe9wolYg+mz64b4D3JGH4Vs1
+vmGUqpk/CjrYVR/oQ0AeZIpzCVr3bTREbAjrhm/80CXkKiUvFFHV91desQui3kSo
+JC4aMOfBF0ykHhTHnQUD2LffUO8IrXFkzDp8pLNSzj9pxAFwAzvYRsJalYv24x0N
+B2Ek6m/bkiRBGHpDryCwf9R7C3QYyZlWzNWCiPSEcPtCdmLIIsG7ZZ4IzDr6Uphi
+Uc+2xqOW19BeVNXu7gi4xGpsOh/lmQbgg5jmX+MwQutM4l7KVe+rEqJu6tFvZkq2
+MQRWfwuqlTnMMweYXordjyKeCMRU20tgxYeBnayny98mc8QT6ENi8p0d9PFulQ==
+-----END RSA PRIVATE KEY-----
diff --git a/apex/com.android.conscrypt.pk8 b/apex/com.android.conscrypt.pk8
new file mode 100644
index 0000000..4c04deb
--- /dev/null
+++ b/apex/com.android.conscrypt.pk8
Binary files differ
diff --git a/apex/com.android.conscrypt.x509.pem b/apex/com.android.conscrypt.x509.pem
new file mode 100644
index 0000000..ba914fa
--- /dev/null
+++ b/apex/com.android.conscrypt.x509.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF1DCCA7ygAwIBAgIJAN0lMAb+9i0JMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
+aWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMR4wHAYDVQQD
+DBVjb20uYW5kcm9pZC5jb25zY3J5cHQwIBcNMTkwMTI1MTcxNjM3WhgPNDc1NjEy
+MjExNzE2MzdaMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYw
+FAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQL
+DAdBbmRyb2lkMR4wHAYDVQQDDBVjb20uYW5kcm9pZC5jb25zY3J5cHQwggIiMA0G
+CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCcfdThwuWhAk0362p7+s/8xL0Hi6Fe
+orQL30Kat8iK4ZPWM5HxOY1zmktBzjgKQ0qM4fuvHzrQJr2+SLl+l2miX4XTruAL
+w7MBS1EGzh0YC4cOBq9z8IsAW/lB6MDeAJ6TuflTk5BRun+bDB70IkWd0ylYSiTi
+AWEs4LM03rBbTcxpNuoLMj1PDNkNOmWiu0rtRbO2Y5+GI9Kw2lyPsWKkU0ixifq2
+o3gHAepmwD7bt5tDFSuOW+uCeF7TzyqsE/CFvqWYUn+5rlOlcyFARAgCh1NZXqxq
+mkcyI9vmtneIUimYw0dzgNoG0dijYAvpuQgSr0HSa2JBiQOgMrJug2PtmWpH2PNk
+UubVZYSfPiO4wTB9Jus63Y27KRdo4Bz4R4n/u0FjIi/cu0Zms07MTo60NPaGHNM/
+nMJSEDDjQ7ppoPXsamn0DID8HlZMivmcQgzokWswxw4b/i1O2fUotKEDf3wvJhUB
+4U7bAgF55FNWWfcaHYqhytWc0qO4zdMUyVqFF+/Db3hz4IkhJyzjCYxBovSSx4yN
+atHa93SwM+uH1s2iI3gOJbn/SrsfKbkrU85S5zVRDEOQg9lm2d99PMnXqXED7bRw
+G9pKmUfOzMANm8yhvL/urlSXVDHxtdw+IJGSVUlzYT3OAx2eFdnpiymiQyWotr8W
+IXfOyr/lB6IkvQIDAQABo1MwUTAdBgNVHQ4EFgQU/vXb6Hy1AxgAxhDuwnO457cD
+3+owHwYDVR0jBBgwFoAU/vXb6Hy1AxgAxhDuwnO457cD3+owDwYDVR0TAQH/BAUw
+AwEB/zANBgkqhkiG9w0BAQsFAAOCAgEARVpJBLitmnJXL/suxt+IsszEBz/pj5g1
+uwor1L3Zvb8j614BhqrKFlPia3PKlHJHotNugjs35CrQ6S4zC3Dx3R2Wb/hBOhtk
+bfpjKZuWaPvqHx1nlBsRa6H7tOKOW/6DHkkb/L5oJo7FPZT0Y0XNandFAmveBL69
+CLZS5dFnhkbVHi7PuvSwcFEXSr/fozcIMnCsQAds42cWqlWlDDVq90vYzOM+jQxS
+cctJ3cZiL0heWEyiZmG0J95Z6kB28gEmaDw8LNUwaUsyaO7U6GwXIe/KQvI2cT/F
+yjpkBmKqDr89Xvyd9DYtZv8GbndTQLwyrPhlKLNOoly3jqjbxqAZGBpyxwVDU+Mo
+/NwkCheQeEYLrGXBgv9fLfyOLSZA7yihPTc0IQcm27DnpkZmJ/lWJ5jDG0VEkNp+
+60O5GYCs2/r3tO+YoiDSFNjsFgCoTcnOQwYhbBZC7YJLOKV0lYDjfsohE6tcCUuS
+H8Ln6Em9HhFAXZBVQU0N+OyC0bAuDTD72ClvbMQikyeWwhdvY3F0x5dDima0liqe
+0jMTJxlizLoa2YKOiLqLomLt6+sJawF0+bGSKSHpcy0NoopXTb3UBlkFpxRp9UnN
+vrnN6KyRzj7OssDEHfXSZgSR0ZAVrAsgP3vc+lBpze9jtNbITDV1gOxUBX771hdU
+UIxewtyODcY=
+-----END CERTIFICATE-----
diff --git a/apex/testing/Android.bp b/apex/testing/Android.bp
new file mode 100644
index 0000000..12c2756
--- /dev/null
+++ b/apex/testing/Android.bp
@@ -0,0 +1,22 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+apex {
+    name: "test_com.android.conscrypt",
+    defaults: ["com.android.conscrypt-defaults"],
+    manifest: "test_apex_manifest.json",
+    file_contexts: "com.android.conscrypt",
+    // Test APEX, should never be installed
+    installable: false,
+}
diff --git a/apex/testing/test_apex_manifest.json b/apex/testing/test_apex_manifest.json
new file mode 100644
index 0000000..97d7cf5
--- /dev/null
+++ b/apex/testing/test_apex_manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.conscrypt",
+  "version": 2147483647
+}
diff --git a/constants/src/gen/cpp/generate_constants.cc b/constants/src/gen/cpp/generate_constants.cc
index 894a1fa..bd1e995 100644
--- a/constants/src/gen/cpp/generate_constants.cc
+++ b/constants/src/gen/cpp/generate_constants.cc
@@ -40,10 +40,16 @@
     " * See the License for the specific language governing permissions and\n"
     " * limitations under the License. */\n";
 
-int main(int /* argc */, char ** /* argv */) {
+int main(int argc, char **argv) {
+  const char *package;
+  if (argc == 1) {
+    package = "org.conscrypt";
+  } else {
+    package = argv[1];
+  }
   printf("%s\n", kCopyright);
   printf("/* This file was generated by generate_constants.cc. */\n\n");
-  printf("package org.conscrypt;\n\n");
+  printf("package %s;\n\n", package);
   printf("final class NativeConstants {\n");
 
 #define CONST(x) \
diff --git a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestAES.java b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestAES.java
index 49b9d23..d79dacb 100644
--- a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestAES.java
+++ b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestAES.java
@@ -18,18 +18,38 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
+import dalvik.system.VMRuntime;
 import java.security.AlgorithmParameters;
 import java.security.Provider;
 import javax.crypto.spec.IvParameterSpec;
 import org.conscrypt.TestUtils;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
 import tests.util.ServiceTester;
 
 @RunWith(JUnit4.class)
 public class AlgorithmParametersTestAES extends AbstractAlgorithmParametersTest {
 
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+            VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+            Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
     private static final byte[] parameterData = new byte[] {
         (byte) 0x04, (byte) 0x08, (byte) 0x68, (byte) 0xC8,
         (byte) 0xFF, (byte) 0x64, (byte) 0x72, (byte) 0xF5,
diff --git a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestDESede.java b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestDESede.java
index a0e8581..8af454b 100644
--- a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestDESede.java
+++ b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestDESede.java
@@ -18,18 +18,38 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
+import dalvik.system.VMRuntime;
 import java.security.AlgorithmParameters;
 import java.security.Provider;
 import javax.crypto.spec.IvParameterSpec;
 import org.conscrypt.TestUtils;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
 import tests.util.ServiceTester;
 
 @RunWith(JUnit4.class)
 public class AlgorithmParametersTestDESede extends AbstractAlgorithmParametersTest {
 
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+            VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+            Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
     private static final byte[] parameterData = new byte[] {
         (byte) 0x04, (byte) 0x08, (byte) 0x68, (byte) 0xC8,
         (byte) 0xFF, (byte) 0x64, (byte) 0x72, (byte) 0xF5 };
diff --git a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestGCM.java b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestGCM.java
index 1c435d3..72b1d23 100644
--- a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestGCM.java
+++ b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestGCM.java
@@ -19,18 +19,38 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import dalvik.system.VMRuntime;
 import java.security.AlgorithmParameters;
 import java.security.Provider;
 import javax.crypto.spec.GCMParameterSpec;
 import org.conscrypt.TestUtils;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
 import tests.util.ServiceTester;
 
 @RunWith(JUnit4.class)
 public class AlgorithmParametersTestGCM extends AbstractAlgorithmParametersTest {
 
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+            VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+            Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
     private static final byte[] IV = new byte[] {
         (byte) 0x04, (byte) 0x08, (byte) 0x68, (byte) 0xC8,
         (byte) 0xFF, (byte) 0x64, (byte) 0x72, (byte) 0xF5,
diff --git a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestOAEP.java b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestOAEP.java
index fb97173..4a847bd 100644
--- a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestOAEP.java
+++ b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/AlgorithmParametersTestOAEP.java
@@ -18,6 +18,7 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
+import dalvik.system.VMRuntime;
 import java.security.AlgorithmParameters;
 import java.security.Provider;
 import java.security.spec.MGF1ParameterSpec;
@@ -25,14 +26,33 @@
 import javax.crypto.spec.PSource;
 import javax.crypto.spec.PSource.PSpecified;
 import org.conscrypt.TestUtils;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
 import tests.util.ServiceTester;
 
 @RunWith(JUnit4.class)
 public class AlgorithmParametersTestOAEP extends AbstractAlgorithmParametersTest {
 
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+            VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+            Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
     // The ASN.1 encoding for OAEP params (specified in RFC 4055 section 4.1) specifies
     // default values for all parameters, so we need to consider encodings with those
     // values both explicitly specified and unspecified.  When encoding values, it is required
diff --git a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java
index b2ccc51..f8df5ad 100644
--- a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java
+++ b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/KeyPairGeneratorTest.java
@@ -54,14 +54,37 @@
 import javax.crypto.interfaces.DHPublicKey;
 import javax.crypto.spec.DHParameterSpec;
 import org.conscrypt.TestUtils;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import dalvik.system.VMRuntime;
+import sun.security.jca.Providers;
+import org.junit.Test;
 import tests.util.ServiceTester;
 
 @RunWith(JUnit4.class)
 public class KeyPairGeneratorTest {
 
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
     @Test
     public void test_getInstance() throws Exception {
         ServiceTester.test("KeyPairGenerator")
diff --git a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/MessageDigestTest.java b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/MessageDigestTest.java
index 599b3f4..880fc38 100644
--- a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/MessageDigestTest.java
+++ b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/MessageDigestTest.java
@@ -24,15 +24,36 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
+import dalvik.system.VMRuntime;
 import org.conscrypt.TestUtils;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
 import tests.util.ServiceTester;
 
 @RunWith(JUnit4.class)
 public final class MessageDigestTest {
 
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
     private final byte[] sha_456 = {
             -24,   9, -59, -47,  -50,  -92, 123, 69, -29,  71,
               1, -46,  63,  96, -118, -102,  88,  3,  77, -55
diff --git a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/SignatureTest.java b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/SignatureTest.java
index 7d4584b..ecb51ed 100644
--- a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/SignatureTest.java
+++ b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/SignatureTest.java
@@ -68,16 +68,37 @@
 import java.util.concurrent.TimeUnit;
 import org.conscrypt.Conscrypt;
 import org.conscrypt.TestUtils;
+import dalvik.system.VMRuntime;
 import org.conscrypt.testing.BrokenProvider;
 import org.conscrypt.testing.OpaqueProvider;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
 import tests.util.ServiceTester;
 
 @RunWith(JUnit4.class)
 public class SignatureTest {
 
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
     // 20 bytes for DSA
     private final byte[] DATA = new byte[20];
 
diff --git a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/cert/CertificateFactoryTest.java b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/cert/CertificateFactoryTest.java
index 82b8a45..40e88fe 100644
--- a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/cert/CertificateFactoryTest.java
+++ b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/cert/CertificateFactoryTest.java
@@ -63,14 +63,34 @@
 import org.conscrypt.Conscrypt;
 import org.conscrypt.TestUtils;
 import org.conscrypt.java.security.StandardNames;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import dalvik.system.VMRuntime;
+import sun.security.jca.Providers;
 import tests.util.ServiceTester;
 
 @RunWith(JUnit4.class)
 public class CertificateFactoryTest {
 
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
     private static final String VALID_CERTIFICATE_PEM =
             "-----BEGIN CERTIFICATE-----\n"
             + "MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM\n"
diff --git a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/cert/X509CertificateTest.java b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/cert/X509CertificateTest.java
index 6fb84f5..a0068cf 100644
--- a/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/cert/X509CertificateTest.java
+++ b/openjdk-integ-tests/src/test/java/org/conscrypt/java/security/cert/X509CertificateTest.java
@@ -27,15 +27,35 @@
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.CertificateParsingException;
+import org.junit.AfterClass;
+import dalvik.system.VMRuntime;
 import java.security.cert.X509Certificate;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
 import tests.util.ServiceTester;
 
 @RunWith(JUnit4.class)
 public class X509CertificateTest {
 
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
     private static final String VALID_CERT =
             "-----BEGIN CERTIFICATE-----\n"
             + "MIIFMjCCAxqgAwIBAgIJAL0mG5fOeJ7xMA0GCSqGSIb3DQEBCwUAMC0xCzAJBgNV\n"
diff --git a/openjdk-integ-tests/src/test/java/org/conscrypt/javax/crypto/CipherTest.java b/openjdk-integ-tests/src/test/java/org/conscrypt/javax/crypto/CipherTest.java
index 14af965..983ba06 100644
--- a/openjdk-integ-tests/src/test/java/org/conscrypt/javax/crypto/CipherTest.java
+++ b/openjdk-integ-tests/src/test/java/org/conscrypt/javax/crypto/CipherTest.java
@@ -74,15 +74,34 @@
 import org.conscrypt.TestUtils;
 import org.conscrypt.java.security.StandardNames;
 import org.conscrypt.java.security.TestKeyStore;
+import org.junit.AfterClass;
 import org.junit.Assume;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import dalvik.system.VMRuntime;
+import sun.security.jca.Providers;
 
 @RunWith(JUnit4.class)
 public final class CipherTest {
 
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
     @BeforeClass
     public static void setUp() {
         TestUtils.assumeAllowsUnsignedCrypto();
diff --git a/openjdk-integ-tests/src/test/java/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java b/openjdk-integ-tests/src/test/java/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java
index 94ddbc5..5699461 100644
--- a/openjdk-integ-tests/src/test/java/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java
+++ b/openjdk-integ-tests/src/test/java/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java
@@ -52,10 +52,13 @@
 import junit.framework.AssertionFailedError;
 import org.conscrypt.Conscrypt;
 import org.conscrypt.TestUtils;
+import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import dalvik.system.VMRuntime;
+import sun.security.jca.Providers;
 
 /**
  * Tests for all registered Elliptic Curve Diffie-Hellman {@link KeyAgreement} providers.
@@ -63,6 +66,22 @@
 @RunWith(JUnit4.class)
 public class ECDHKeyAgreementTest {
 
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
     // Two key pairs and the resulting shared secret for the Known Answer Test
     private static final byte[] KAT_PUBLIC_KEY1_X509 = TestUtils.decodeHex(
             "3059301306072a8648ce3d020106082a8648ce3d030107034200049fc2f71f85446b1371244491d83"
diff --git a/openjdk-integ-tests/src/test/java/org/conscrypt/javax/crypto/KeyGeneratorTest.java b/openjdk-integ-tests/src/test/java/org/conscrypt/javax/crypto/KeyGeneratorTest.java
index 4ebafb7..2685cef 100644
--- a/openjdk-integ-tests/src/test/java/org/conscrypt/javax/crypto/KeyGeneratorTest.java
+++ b/openjdk-integ-tests/src/test/java/org/conscrypt/javax/crypto/KeyGeneratorTest.java
@@ -29,14 +29,33 @@
 import javax.crypto.SecretKey;
 import org.conscrypt.TestUtils;
 import org.conscrypt.java.security.StandardNames;
+import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import dalvik.system.VMRuntime;
+import sun.security.jca.Providers;
 import tests.util.ServiceTester;
 
 @RunWith(JUnit4.class)
 public class KeyGeneratorTest {
+    
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
 
     private static boolean isUnsupported(KeyGenerator kg) {
         // Don't bother testing "Sun..." KeyGenerators or BC outside of Android
diff --git a/openjdk-integ-tests/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java b/openjdk-integ-tests/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java
index 63fc3f5..4b02e0a 100644
--- a/openjdk-integ-tests/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java
+++ b/openjdk-integ-tests/src/test/java/org/conscrypt/javax/net/ssl/SSLEngineTest.java
@@ -31,6 +31,7 @@
 import java.security.cert.X509Certificate;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.List;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 import javax.net.ssl.KeyManager;
@@ -583,8 +584,14 @@
                         // By the point of the handshake where we're validating certificates,
                         // the hostname is known and the cipher suite should be agreed
                         assertEquals(referenceContext.host.getHostName(), session.getPeerHost());
-                        assertEquals(referenceEngine.getEnabledCipherSuites()[0],
-                            session.getCipherSuite());
+                        String sessionSuite = session.getCipherSuite();
+                        List<String> enabledSuites =
+                                Arrays.asList(referenceEngine.getEnabledCipherSuites());
+                        String message = "Handshake session has invalid cipher suite: "
+                                + (sessionSuite == null ? "(null)" : sessionSuite);
+                        assertTrue("Expected enabled suites to contain " + sessionSuite
+                                        + ", got: " + enabledSuites,
+                                enabledSuites.contains(sessionSuite));
                         wasCalled[0] = true;
                     } catch (Exception e) {
                         throw new CertificateException("Something broke", e);
diff --git a/openjdk/src/test/java/org/conscrypt/OpenSSLServerSocketImplTest.java b/openjdk/src/test/java/org/conscrypt/OpenSSLServerSocketImplTest.java
new file mode 100644
index 0000000..1618e8a
--- /dev/null
+++ b/openjdk/src/test/java/org/conscrypt/OpenSSLServerSocketImplTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt;
+
+import static org.conscrypt.TestUtils.LOCALHOST;
+import static org.conscrypt.TestUtils.getConscryptServerSocketFactory;
+import static org.conscrypt.TestUtils.getJdkSocketFactory;
+import static org.conscrypt.TestUtils.getProtocols;
+import static org.conscrypt.TestUtils.newTextMessage;
+import static org.conscrypt.TestUtils.pickUnusedPort;
+import static org.junit.Assert.assertArrayEquals;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class OpenSSLServerSocketImplTest {
+    private static final String CIPHER = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256";
+    private static final int MESSAGE_SIZE = 4096;
+
+    /**
+     * Various factories for SSL server sockets.
+     */
+    public enum SocketType {
+        DEFAULT(getConscryptServerSocketFactory(false)),
+        ENGINE(getConscryptServerSocketFactory(true));
+
+        @SuppressWarnings("ImmutableEnumChecker")
+        private final SSLServerSocketFactory serverSocketFactory;
+
+        SocketType(SSLServerSocketFactory serverSocketFactory) {
+            this.serverSocketFactory = serverSocketFactory;
+        }
+
+        final SSLServerSocket newServerSocket(String cipher) {
+            try {
+                int port = pickUnusedPort();
+                SSLServerSocket sslSocket =
+                        (SSLServerSocket) serverSocketFactory.createServerSocket(port);
+                sslSocket.setEnabledProtocols(getProtocols());
+                sslSocket.setEnabledCipherSuites(new String[] {cipher});
+                return sslSocket;
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    @Parameters(name = "{0}")
+    public static Iterable<SocketType> data() {
+        // Android-changed: Temporarily (2017 Q2) disable ENGINE tests. http://b/37271061#comment9
+        // This experimental (unused by default) implementation is unstable and causing test
+        // failures on Android.
+        // return Arrays.asList(SocketType.DEFAULT, SocketType.ENGINE);
+        return Arrays.asList(SocketType.DEFAULT);
+    }
+
+    @Parameter public SocketType socketType;
+
+    private TestClient client;
+    private TestServer server;
+
+    @Before
+    public void setup() throws Exception {
+        // Create and start the server.
+        server = new TestServer(socketType.newServerSocket(CIPHER), MESSAGE_SIZE);
+        Future<?> connectedFuture = server.start();
+
+        // Create and start the client.
+        SSLSocketFactory socketFactory = getJdkSocketFactory();
+        SSLSocket socket = (SSLSocket) socketFactory.createSocket(LOCALHOST, server.port());
+        socket.setEnabledProtocols(getProtocols());
+        socket.setEnabledCipherSuites(new String[] {CIPHER});
+        client = new TestClient(socket);
+        client.start();
+
+        // Wait for the initial connection to complete.
+        connectedFuture.get(5, TimeUnit.SECONDS);
+    }
+
+    @After
+    public void teardown() throws Exception {
+        client.stop();
+        server.stop();
+    }
+
+    @Test
+    public void pingPong() throws IOException {
+        byte[] request = newTextMessage(MESSAGE_SIZE);
+        byte[] responseBuffer = new byte[MESSAGE_SIZE];
+        client.sendMessage(request);
+        client.flush();
+        int numBytes = client.readMessage(responseBuffer);
+        byte[] response = Arrays.copyOfRange(responseBuffer, 0, numBytes);
+        assertArrayEquals(request, response);
+    }
+}
diff --git a/publicapi/src/main/java/android/net/ssl/SSLEngines.java b/publicapi/src/main/java/android/net/ssl/SSLEngines.java
new file mode 100644
index 0000000..13bd771
--- /dev/null
+++ b/publicapi/src/main/java/android/net/ssl/SSLEngines.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ssl;
+
+import com.android.org.conscrypt.Conscrypt;
+import javax.net.ssl.SSLEngine;
+import libcore.util.NonNull;
+
+/**
+ * Static utility methods for accessing additional functionality of supported instances of
+ * {@link SSLEngine}.  Engines from the platform TLS provider will be compatible with all
+ * methods in this class.
+ */
+public class SSLEngines {
+    private SSLEngines() {}
+
+    /**
+     * Returns whether the given engine can be used with the methods in this class.  In general,
+     * only engines from the platform TLS provider are supported.
+     */
+    public static boolean isSupportedEngine(@NonNull SSLEngine engine) {
+        return Conscrypt.isConscrypt(engine);
+    }
+
+    private static void checkSupported(@NonNull SSLEngine e) {
+        if (!isSupportedEngine(e)) {
+            throw new IllegalArgumentException("Engine is not a supported engine.");
+        }
+    }
+
+    /**
+     * Enables or disables the use of session tickets.
+     *
+     * <p>This function must be called before the handshake is started or it will have no effect.
+     *
+     * @param engine the engine
+     * @param useSessionTickets whether to enable or disable the use of session tickets
+     * @throws IllegalArgumentException if the given engine is not a platform engine
+     */
+    public static void setUseSessionTickets(@NonNull SSLEngine engine, boolean useSessionTickets) {
+        checkSupported(engine);
+        Conscrypt.setUseSessionTickets(engine, useSessionTickets);
+    }
+}
diff --git a/publicapi/src/main/java/android/net/ssl/SSLSockets.java b/publicapi/src/main/java/android/net/ssl/SSLSockets.java
new file mode 100644
index 0000000..a669404
--- /dev/null
+++ b/publicapi/src/main/java/android/net/ssl/SSLSockets.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ssl;
+
+import com.android.org.conscrypt.Conscrypt;
+import javax.net.ssl.SSLSocket;
+import libcore.util.NonNull;
+
+/**
+ * Static utility methods for accessing additional functionality of supported instances of
+ * {@link SSLSocket}.  Sockets from the platform TLS provider will be compatible with all
+ * methods in this class.
+ */
+public class SSLSockets {
+    private SSLSockets() {}
+
+    /**
+     * Returns whether the given socket can be used with the methods in this class.  In general,
+     * only sockets from the platform TLS provider are supported.
+     */
+    public static boolean isSupportedSocket(@NonNull SSLSocket socket) {
+        return Conscrypt.isConscrypt(socket);
+    }
+
+    private static void checkSupported(@NonNull SSLSocket s) {
+        if (!isSupportedSocket(s)) {
+            throw new IllegalArgumentException("Socket is not a supported socket.");
+        }
+    }
+
+    /**
+     * Enables or disables the use of session tickets.
+     *
+     * <p>This function must be called before the handshake is started or it will have no effect.
+     *
+     * @param socket the socket
+     * @param useSessionTickets whether to enable or disable the use of session tickets
+     * @throws IllegalArgumentException if the given socket is not a platform socket
+     */
+    public static void setUseSessionTickets(@NonNull SSLSocket socket, boolean useSessionTickets) {
+        checkSupported(socket);
+        Conscrypt.setUseSessionTickets(socket, useSessionTickets);
+    }
+}
diff --git a/publicapi/src/test/java/android/net/ssl/SSLEnginesTest.java b/publicapi/src/test/java/android/net/ssl/SSLEnginesTest.java
new file mode 100644
index 0000000..66abec6
--- /dev/null
+++ b/publicapi/src/test/java/android/net/ssl/SSLEnginesTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ssl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.org.conscrypt.tlswire.TlsTester;
+import com.android.org.conscrypt.tlswire.handshake.ClientHello;
+import com.android.org.conscrypt.tlswire.handshake.HelloExtension;
+import java.nio.ByteBuffer;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SSLEnginesTest {
+
+    private static class BrokenSSLEngine extends SSLEngine {
+        @Override public SSLEngineResult wrap(ByteBuffer[] byteBuffers, int i, int i1,
+                ByteBuffer byteBuffer) throws SSLException { throw new AssertionError(); }
+        @Override public SSLEngineResult unwrap(ByteBuffer byteBuffer, ByteBuffer[] byteBuffers,
+                int i, int i1) throws SSLException { throw new AssertionError(); }
+        @Override public Runnable getDelegatedTask() { throw new AssertionError(); }
+        @Override public void closeInbound() throws SSLException { throw new AssertionError(); }
+        @Override public boolean isInboundDone() { throw new AssertionError(); }
+        @Override public void closeOutbound() { throw new AssertionError(); }
+        @Override public boolean isOutboundDone() { throw new AssertionError(); }
+        @Override public String[] getSupportedCipherSuites() { throw new AssertionError(); }
+        @Override public String[] getEnabledCipherSuites() { throw new AssertionError(); }
+        @Override public void setEnabledCipherSuites(String[] strings) { throw new AssertionError(); }
+        @Override public String[] getSupportedProtocols() { throw new AssertionError(); }
+        @Override public String[] getEnabledProtocols() { throw new AssertionError(); }
+        @Override public void setEnabledProtocols(String[] strings) { throw new AssertionError(); }
+        @Override public SSLSession getSession() { throw new AssertionError(); }
+        @Override public void beginHandshake() throws SSLException { throw new AssertionError(); }
+        @Override public SSLEngineResult.HandshakeStatus getHandshakeStatus() { throw new AssertionError(); }
+        @Override public void setUseClientMode(boolean b) { throw new AssertionError(); }
+        @Override public boolean getUseClientMode() { throw new AssertionError(); }
+        @Override public void setNeedClientAuth(boolean b) { throw new AssertionError(); }
+        @Override public boolean getNeedClientAuth() { throw new AssertionError(); }
+        @Override public void setWantClientAuth(boolean b) { throw new AssertionError(); }
+        @Override public boolean getWantClientAuth() { throw new AssertionError(); }
+        @Override public void setEnableSessionCreation(boolean b) { throw new AssertionError(); }
+        @Override public boolean getEnableSessionCreation() { throw new AssertionError(); }
+    }
+
+    private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
+
+    @Test
+    public void testIsSupported() throws Exception {
+        SSLEngine e = SSLContext.getDefault().createSSLEngine();
+        assertTrue(SSLEngines.isSupportedEngine(e));
+
+        e = new BrokenSSLEngine();
+        assertFalse(SSLEngines.isSupportedEngine(e));
+    }
+
+    @Test
+    public void testUseSessionTickets() throws Exception {
+        try {
+            SSLEngines.setUseSessionTickets(new BrokenSSLEngine(), true);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        SSLEngine e = SSLContext.getDefault().createSSLEngine();
+        e.setUseClientMode(true);
+        SSLEngines.setUseSessionTickets(e, true);
+
+        ClientHello hello = getClientHello(e);
+        assertNotNull(hello.findExtensionByType(HelloExtension.TYPE_SESSION_TICKET));
+
+        e = SSLContext.getDefault().createSSLEngine();
+        e.setUseClientMode(true);
+        SSLEngines.setUseSessionTickets(e, false);
+
+        hello = getClientHello(e);
+        assertNull(hello.findExtensionByType(HelloExtension.TYPE_SESSION_TICKET));
+    }
+
+    private static ClientHello getClientHello(SSLEngine e) throws Exception {
+        ByteBuffer out = ByteBuffer.allocate(64 * 1024);
+
+        e.wrap(EMPTY_BUFFER, out);
+        out.flip();
+        byte[] data = new byte[out.limit()];
+        out.get(data);
+
+        return TlsTester.parseClientHello(data);
+    }
+}
diff --git a/publicapi/src/test/java/android/net/ssl/SSLSocketsTest.java b/publicapi/src/test/java/android/net/ssl/SSLSocketsTest.java
new file mode 100644
index 0000000..02df047
--- /dev/null
+++ b/publicapi/src/test/java/android/net/ssl/SSLSocketsTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ssl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.org.conscrypt.tlswire.TlsTester;
+import com.android.org.conscrypt.tlswire.handshake.ClientHello;
+import com.android.org.conscrypt.tlswire.handshake.HelloExtension;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import tests.net.DelegatingSSLSocketFactory;
+
+@RunWith(JUnit4.class)
+public class SSLSocketsTest {
+
+    private static class BrokenSSLSocket extends SSLSocket {
+        @Override public String[] getSupportedCipherSuites() { throw new AssertionError(); }
+        @Override public String[] getEnabledCipherSuites() { throw new AssertionError(); }
+        @Override public void setEnabledCipherSuites(String[] strings) { throw new AssertionError(); }
+        @Override public String[] getSupportedProtocols() { throw new AssertionError(); }
+        @Override public String[] getEnabledProtocols() { throw new AssertionError(); }
+        @Override public void setEnabledProtocols(String[] strings) { throw new AssertionError(); }
+        @Override public SSLSession getSession() { throw new AssertionError(); }
+        @Override public void addHandshakeCompletedListener(
+                HandshakeCompletedListener handshakeCompletedListener) { throw new AssertionError(); }
+        @Override public void removeHandshakeCompletedListener(
+                HandshakeCompletedListener handshakeCompletedListener) { throw new AssertionError(); }
+        @Override public void startHandshake() { throw new AssertionError(); }
+        @Override public void setUseClientMode(boolean b) { throw new AssertionError(); }
+        @Override public boolean getUseClientMode() { throw new AssertionError(); }
+        @Override public void setNeedClientAuth(boolean b) { throw new AssertionError(); }
+        @Override public boolean getNeedClientAuth() { throw new AssertionError(); }
+        @Override public void setWantClientAuth(boolean b) { throw new AssertionError(); }
+        @Override public boolean getWantClientAuth() { throw new AssertionError(); }
+        @Override public void setEnableSessionCreation(boolean b) { throw new AssertionError(); }
+        @Override public boolean getEnableSessionCreation() { throw new AssertionError(); }
+    }
+
+    private ExecutorService executor;
+
+    @Before
+    public void setUp() {
+        executor = Executors.newCachedThreadPool();
+    }
+
+    @After
+    public void tearDown() throws InterruptedException {
+        executor.shutdown();
+        executor.awaitTermination(1, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void testIsSupported() throws Exception {
+        SSLSocket s = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
+        assertTrue(SSLSockets.isSupportedSocket(s));
+
+        s = new BrokenSSLSocket();
+        assertFalse(SSLSockets.isSupportedSocket(s));
+    }
+
+    @Test
+    public void testUseSessionTickets() throws Exception {
+        try {
+            SSLSockets.setUseSessionTickets(new BrokenSSLSocket(), true);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        SSLSocket s = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
+        SSLSockets.setUseSessionTickets(s, true);
+
+        ClientHello hello = TlsTester.captureTlsHandshakeClientHello(executor,
+                new DelegatingSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault()) {
+                    @Override public SSLSocket configureSocket(SSLSocket socket) {
+                        SSLSockets.setUseSessionTickets(socket, true);
+                        return socket;
+                    }
+                });
+        assertNotNull(hello.findExtensionByType(HelloExtension.TYPE_SESSION_TICKET));
+
+        hello = TlsTester.captureTlsHandshakeClientHello(executor,
+                new DelegatingSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault()) {
+                    @Override public SSLSocket configureSocket(SSLSocket socket) {
+                        SSLSockets.setUseSessionTickets(socket, false);
+                        return socket;
+                    }
+                });
+        assertNull(hello.findExtensionByType(HelloExtension.TYPE_SESSION_TICKET));
+    }
+}
diff --git a/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/AndroidEndpointFactory.java b/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/AndroidEndpointFactory.java
new file mode 100644
index 0000000..321c908
--- /dev/null
+++ b/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/AndroidEndpointFactory.java
@@ -0,0 +1,65 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.security.Provider;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * Utility for creating test client and server instances.
+ * @hide This class is not part of the Android public SDK API
+ */
+@SuppressWarnings("ImmutableEnumChecker")
+public enum AndroidEndpointFactory implements EndpointFactory {
+  @SuppressWarnings("unused")
+  CONSCRYPT(newConscryptFactories(false)),
+  CONSCRYPT_ENGINE(newConscryptFactories(true));
+
+  private final Factories factories;
+
+  AndroidEndpointFactory(Factories factories) {
+    this.factories = factories;
+  }
+
+  @Override
+  public ClientEndpoint newClient(ChannelType channelType, int port, String[] protocols,
+      String[] ciphers) throws IOException {
+    return new ClientEndpoint(
+        factories.clientFactory, channelType, port, protocols, ciphers);
+  }
+
+  @Override
+  public ServerEndpoint newServer(ChannelType channelType, int messageSize,
+      String[] protocols, String[] ciphers) throws IOException {
+    return new ServerEndpoint(factories.serverFactory, factories.serverSocketFactory,
+        channelType, messageSize, protocols, ciphers);
+  }
+
+  private static final class Factories {
+    final SSLSocketFactory clientFactory;
+    final SSLSocketFactory serverFactory;
+    final SSLServerSocketFactory serverSocketFactory;
+
+    private Factories(SSLSocketFactory clientFactory, SSLSocketFactory serverFactory,
+        SSLServerSocketFactory serverSocketFactory) {
+      this.clientFactory = clientFactory;
+      this.serverFactory = serverFactory;
+      this.serverSocketFactory = serverSocketFactory;
+    }
+  }
+
+  private static Factories newConscryptFactories(boolean useEngineSocket) {
+    Provider provider = TestUtils.getConscryptProvider();
+    SSLContext clientContext = TestUtils.newClientSslContext(provider);
+    SSLContext serverContext = TestUtils.newServerSslContext(provider);
+    final SSLSocketFactory clientFactory = clientContext.getSocketFactory();
+    final SSLSocketFactory serverFactory = serverContext.getSocketFactory();
+    final SSLServerSocketFactory serverSocketFactory = serverContext.getServerSocketFactory();
+    TestUtils.setUseEngineSocket(clientFactory, useEngineSocket);
+    TestUtils.setUseEngineSocket(serverFactory, useEngineSocket);
+    TestUtils.setUseEngineSocket(serverSocketFactory, useEngineSocket);
+    return new Factories(clientFactory, serverFactory, serverSocketFactory);
+  }
+}
diff --git a/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/AndroidEngineFactory.java b/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/AndroidEngineFactory.java
new file mode 100644
index 0000000..4978114
--- /dev/null
+++ b/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/AndroidEngineFactory.java
@@ -0,0 +1,103 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+import java.security.Security;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Enumeration of various types of engines for use with engine-based benchmarks.
+ * @hide This class is not part of the Android public SDK API
+ */
+@SuppressWarnings({"ImmutableEnumChecker", "unused"})
+public enum AndroidEngineFactory implements EngineFactory {
+    CONSCRYPT_UNPOOLED {
+        private final SSLContext clientContext = newConscryptClientContext();
+        private final SSLContext serverContext = newConscryptServerContext();
+
+        @Override
+        public SSLEngine newClientEngine(String cipher, boolean useAlpn) {
+            SSLEngine engine = initEngine(clientContext.createSSLEngine(), cipher, true);
+            if (useAlpn) {
+                Conscrypt.setApplicationProtocols(engine, new String[] {"h2"});
+            }
+            return engine;
+        }
+
+        @Override
+        public SSLEngine newServerEngine(String cipher, boolean useAlpn) {
+            SSLEngine engine = initEngine(serverContext.createSSLEngine(), cipher, false);
+            if (useAlpn) {
+                Conscrypt.setApplicationProtocols(engine, new String[] {"h2"});
+            }
+            return engine;
+        }
+
+        @Override
+        public void dispose(SSLEngine engine) {
+            engine.closeOutbound();
+        }
+    },
+    PLATFORM {
+        private final SSLContext clientContext =
+                TestUtils.newClientSslContext(Security.getProvider("AndroidOpenSSL"));
+        private final SSLContext serverContext =
+                TestUtils.newServerSslContext(Security.getProvider("AndroidOpenSSL"));
+
+        @Override
+        public SSLEngine newClientEngine(String cipher, boolean useAlpn) {
+            SSLEngine engine = initEngine(clientContext.createSSLEngine(), cipher, true);
+            if (useAlpn) {
+                Conscrypt.setApplicationProtocols(engine, new String[] {"h2"});
+            }
+            return engine;
+        }
+
+        @Override
+        public SSLEngine newServerEngine(String cipher, boolean useAlpn) {
+            SSLEngine engine = initEngine(serverContext.createSSLEngine(), cipher, false);
+            if (useAlpn) {
+                Conscrypt.setApplicationProtocols(engine, new String[] {"h2"});
+            }
+            return engine;
+        }
+
+        @Override
+        public void dispose(SSLEngine engine) {
+            engine.closeOutbound();
+        }
+    };
+
+    @Override
+    public void dispose(SSLEngine engine) {}
+
+    private static SSLContext newConscryptClientContext() {
+        return TestUtils.newClientSslContext(TestUtils.getConscryptProvider());
+    }
+
+    private static SSLContext newConscryptServerContext() {
+        return TestUtils.newServerSslContext(TestUtils.getConscryptProvider());
+    }
+
+    static SSLEngine initEngine(SSLEngine engine, String cipher, boolean client) {
+        engine.setEnabledProtocols(new String[]{"TLSv1.2"});
+        engine.setEnabledCipherSuites(new String[] {cipher});
+        engine.setUseClientMode(client);
+        return engine;
+    }
+}
diff --git a/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/CaliperAlpnBenchmark.java b/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/CaliperAlpnBenchmark.java
new file mode 100644
index 0000000..c181662
--- /dev/null
+++ b/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/CaliperAlpnBenchmark.java
@@ -0,0 +1,87 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
+import com.google.caliper.Param;
+import com.android.org.conscrypt.EngineHandshakeBenchmark.Config;
+
+/**
+ * Cipher benchmarks. Only runs on AES currently because of the combinatorial
+ * explosion of the test as it stands.
+ * @hide This class is not part of the Android public SDK API
+ */
+@SuppressWarnings("unused")
+public class CaliperAlpnBenchmark {
+    private final CaliperConfig config = new CaliperConfig();
+
+    @Param({TestUtils.TEST_CIPHER})
+    public String a_cipher;
+
+    @Param
+    public BufferType b_buffer;
+
+    @Param({"CONSCRYPT_UNPOOLED"}) public AndroidEngineFactory c_engine;
+
+    private EngineHandshakeBenchmark benchmark;
+
+    @BeforeExperiment
+    public void setUp() throws Exception {
+        benchmark = new EngineHandshakeBenchmark(config);
+    }
+
+    @Benchmark
+    public void timeHandshake(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            benchmark.handshake();
+        }
+    }
+
+    private final class CaliperConfig implements Config {
+        @Override
+        public BufferType bufferType() {
+            return b_buffer;
+        }
+
+        @Override
+        public EngineFactory engineFactory() {
+            return c_engine;
+        }
+
+        @Override
+        public String cipher() {
+            return a_cipher;
+        }
+
+        @Override
+        public boolean useAlpn() {
+            return true;
+        }
+
+        @Override
+        public BenchmarkProtocol protocol() {
+            return BenchmarkProtocol.TLSv12;
+        }
+
+        @Override
+        public int rttMillis() {
+            return 0;
+        }
+    }
+}
diff --git a/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/CaliperClientSocketBenchmark.java b/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/CaliperClientSocketBenchmark.java
new file mode 100644
index 0000000..c444952
--- /dev/null
+++ b/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/CaliperClientSocketBenchmark.java
@@ -0,0 +1,99 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import com.google.caliper.AfterExperiment;
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
+import com.google.caliper.Param;
+import com.android.org.conscrypt.ClientSocketBenchmark.Config;
+
+/**
+ * Benchmark for comparing performance of client socket implementations.
+ * @hide This class is not part of the Android public SDK API
+ */
+@SuppressWarnings("unused")
+public class CaliperClientSocketBenchmark {
+
+  private final CaliperConfig config = new CaliperConfig();
+
+  @Param
+  public AndroidEndpointFactory socketType;
+
+  @Param({"64", "512", "4096"})
+  public int messageSize;
+
+  @Param({TestUtils.TEST_CIPHER})
+  public String cipher;
+
+  @Param
+  public BenchmarkProtocol protocol;
+
+  @Param
+  public ChannelType channelType;
+
+  private ClientSocketBenchmark benchmark;
+
+  @BeforeExperiment
+  public void setup() throws Exception {
+    benchmark = new ClientSocketBenchmark(config);
+  }
+
+  @AfterExperiment
+  public void teardown() throws Exception {
+    benchmark.close();
+  }
+
+  @Benchmark
+  public final void time(int numMessages) throws Exception {
+    benchmark.time(numMessages);
+  }
+
+  private final class CaliperConfig implements Config {
+    @Override
+    public EndpointFactory clientFactory() {
+      return socketType;
+    }
+
+    @Override
+    public EndpointFactory serverFactory() {
+      // Use the same server for all benchmarks, since we're looking at the perf of the client.
+      return AndroidEndpointFactory.CONSCRYPT_ENGINE;
+    }
+
+    @Override
+    public int messageSize() {
+      return messageSize;
+    }
+
+    @Override
+    public String cipher() {
+      return cipher;
+    }
+
+    @Override
+    public ChannelType channelType() {
+      return channelType;
+    }
+
+    @Override
+    public BenchmarkProtocol protocol() {
+      return protocol;
+    }
+  }
+}
diff --git a/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/CaliperEngineHandshakeBenchmark.java b/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/CaliperEngineHandshakeBenchmark.java
new file mode 100644
index 0000000..a43c74e
--- /dev/null
+++ b/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/CaliperEngineHandshakeBenchmark.java
@@ -0,0 +1,108 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2017 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
+import com.google.caliper.Param;
+import com.android.org.conscrypt.EngineHandshakeBenchmark.Config;
+
+/**
+ * Benchmark comparing handshake performance of various engine implementations.
+ * @hide This class is not part of the Android public SDK API
+ */
+@SuppressWarnings("unused")
+public class CaliperEngineHandshakeBenchmark {
+    private final CaliperConfig config = new CaliperConfig();
+
+    @Param({TestUtils.TEST_CIPHER})
+    public String a_cipher;
+
+    @Param
+    public BufferType b_buffer;
+
+    @Param({"CONSCRYPT_UNPOOLED"}) public AndroidEngineFactory c_engine;
+
+    @Param
+    public BenchmarkProtocol d_protocol;
+
+    @Param({"100"})
+    public int e_rtt;
+
+    private EngineHandshakeBenchmark benchmark;
+
+    @BeforeExperiment
+    public void setUp() throws Exception {
+        benchmark = new EngineHandshakeBenchmark(config);
+    }
+
+    @Benchmark
+    public void timeHandshake(int reps) throws Exception {
+        for (int i = 0; i < reps; ++i) {
+            benchmark.handshake();
+        }
+    }
+
+    private final class CaliperConfig implements Config {
+        @Override
+        public BufferType bufferType() {
+            return b_buffer;
+        }
+
+        @Override
+        public EngineFactory engineFactory() {
+            return c_engine;
+        }
+
+        @Override
+        public String cipher() {
+            return a_cipher;
+        }
+
+        @Override
+        public boolean useAlpn() {
+            return false;
+        }
+
+        @Override
+        public BenchmarkProtocol protocol() {
+            return d_protocol;
+        }
+
+        @Override
+        public int rttMillis() {
+            return e_rtt;
+        }
+    }
+}
diff --git a/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/CaliperEngineWrapBenchmark.java b/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/CaliperEngineWrapBenchmark.java
new file mode 100644
index 0000000..4268c1e
--- /dev/null
+++ b/repackaged/benchmark-android/src/main/java/com/android/org/conscrypt/CaliperEngineWrapBenchmark.java
@@ -0,0 +1,109 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2017 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import com.google.caliper.AfterExperiment;
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
+import com.google.caliper.Param;
+import javax.net.ssl.SSLException;
+import com.android.org.conscrypt.EngineWrapBenchmark.Config;
+
+/**
+ * Benchmark comparing performance of various engine implementations to conscrypt.
+ * @hide This class is not part of the Android public SDK API
+ */
+@SuppressWarnings("unused")
+public class CaliperEngineWrapBenchmark {
+    private final CaliperConfig config = new CaliperConfig();
+
+    @Param({TestUtils.TEST_CIPHER})
+    public String a_cipher;
+
+    @Param
+    public BufferType b_buffer;
+
+    @Param({"64", "512", "4096"})
+    public int c_message;
+
+    @Param({"CONSCRYPT_UNPOOLED"}) public AndroidEngineFactory d_engine;
+
+    private EngineWrapBenchmark benchmark;
+
+    @BeforeExperiment
+    public void setUp() throws Exception {
+        benchmark = new EngineWrapBenchmark(config);
+    }
+
+    @AfterExperiment
+    public void teardown() {
+        benchmark.teardown();
+    }
+
+    @Benchmark
+    public void timeWrap(int reps) throws SSLException {
+        for (int i = 0; i < reps; ++i) {
+            benchmark.wrap();
+        }
+    }
+
+    public void timeWrapAndUnwrap(int reps) throws SSLException {
+        for (int i = 0; i < reps; ++i) {
+            benchmark.wrapAndUnwrap();
+        }
+    }
+
+    private final class CaliperConfig implements Config {
+
+        @Override
+        public BufferType bufferType() {
+            return b_buffer;
+        }
+
+        @Override
+        public EngineFactory engineFactory() {
+            return d_engine;
+        }
+
+        @Override
+        public int messageSize() {
+            return c_message;
+        }
+
+        @Override
+        public String cipher() {
+            return a_cipher;
+        }
+    }
+}
diff --git a/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/BenchmarkProtocol.java b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/BenchmarkProtocol.java
new file mode 100644
index 0000000..c31bb80
--- /dev/null
+++ b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/BenchmarkProtocol.java
@@ -0,0 +1,22 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.conscrypt;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@SuppressWarnings("ImmutableEnumChecker")
+public enum BenchmarkProtocol {
+
+    TLSv13("TLSv1.3"),
+    TLSv12("TLSv1.2");
+
+    private final String[] protocols;
+
+    BenchmarkProtocol(String... protocols) {
+        this.protocols = protocols;
+    }
+
+    public String[] getProtocols() {
+        return protocols.clone();
+    }
+}
diff --git a/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/BufferType.java b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/BufferType.java
new file mode 100644
index 0000000..a2a62d3
--- /dev/null
+++ b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/BufferType.java
@@ -0,0 +1,50 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+import java.nio.ByteBuffer;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Enumeration that provides allocation of direct or heap buffers.
+ * @hide This class is not part of the Android public SDK API
+ */
+@SuppressWarnings("unused")
+public enum BufferType {
+    HEAP {
+        @Override
+        ByteBuffer newBuffer(int size) {
+            return ByteBuffer.allocate(size);
+        }
+    },
+    DIRECT {
+        @Override
+        ByteBuffer newBuffer(int size) {
+            return ByteBuffer.allocateDirect(size);
+        }
+    };
+
+    abstract ByteBuffer newBuffer(int size);
+
+    ByteBuffer newApplicationBuffer(SSLEngine engine) {
+        return newBuffer(engine.getSession().getApplicationBufferSize());
+    }
+
+    ByteBuffer newPacketBuffer(SSLEngine engine) {
+        return newBuffer(engine.getSession().getPacketBufferSize());
+    }
+}
diff --git a/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/CipherEncryptBenchmark.java b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/CipherEncryptBenchmark.java
new file mode 100644
index 0000000..71fa3c4
--- /dev/null
+++ b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/CipherEncryptBenchmark.java
@@ -0,0 +1,162 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.nio.ByteBuffer;
+import java.security.Key;
+import javax.crypto.Cipher;
+
+/**
+ * Benchmark for comparing cipher encrypt performance.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class CipherEncryptBenchmark {
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum BufferType {
+        ARRAY,
+        HEAP_HEAP,
+        HEAP_DIRECT,
+        DIRECT_DIRECT,
+        DIRECT_HEAP
+    }
+
+    /**
+     * Provider for the benchmark configuration
+     */
+    interface Config {
+        BufferType bufferType();
+        CipherFactory cipherFactory();
+        Transformation transformation();
+    }
+
+    private final EncryptStrategy encryptStrategy;
+
+    CipherEncryptBenchmark(Config config) throws Exception {
+        switch (config.bufferType()) {
+            case ARRAY:
+                encryptStrategy = new ArrayStrategy(config);
+                break;
+            default:
+                encryptStrategy = new ByteBufferStrategy(config);
+                break;
+        }
+    }
+
+    int encrypt() throws Exception {
+        return encryptStrategy.encrypt();
+    }
+
+    private static abstract class EncryptStrategy {
+        private final Key key;
+        final Cipher cipher;
+        final int outputSize;
+
+        EncryptStrategy(Config config) throws Exception {
+            Transformation tx = config.transformation();
+            key = tx.newEncryptKey();
+            cipher = config.cipherFactory().newCipher(tx.toFormattedString());
+            initCipher();
+
+            int messageSize = messageSize(tx.toFormattedString());
+            outputSize = cipher.getOutputSize(messageSize);
+        }
+
+        final void initCipher() throws Exception {
+            cipher.init(Cipher.ENCRYPT_MODE, key);
+        }
+
+        final int messageSize(String transformation) throws Exception {
+            Cipher conscryptCipher = Cipher.getInstance(transformation, TestUtils.getConscryptProvider());
+            conscryptCipher.init(Cipher.ENCRYPT_MODE, key);
+            return conscryptCipher.getBlockSize() > 0 ? conscryptCipher.getBlockSize() : 128;
+        }
+
+        final byte[] newMessage() {
+            return TestUtils.newTextMessage(cipher.getBlockSize());
+        }
+
+        abstract int encrypt() throws Exception;
+    }
+
+    private static final class ArrayStrategy extends EncryptStrategy {
+        private final byte[] plainBytes;
+        private final byte[] cipherBytes;
+
+        ArrayStrategy(Config config) throws Exception {
+            super(config);
+
+            plainBytes = newMessage();
+            cipherBytes = new byte[outputSize];
+        }
+
+        @Override
+        int encrypt() throws Exception {
+            initCipher();
+            return cipher.doFinal(plainBytes, 0, plainBytes.length, cipherBytes, 0);
+        }
+    }
+
+    private static final class ByteBufferStrategy extends EncryptStrategy {
+        private final ByteBuffer input;
+        private final ByteBuffer output;
+
+        ByteBufferStrategy(Config config) throws Exception {
+            super(config);
+
+            switch (config.bufferType()) {
+                case HEAP_HEAP:
+                    input = ByteBuffer.wrap(newMessage());
+                    output = ByteBuffer.allocate(outputSize);
+                    break;
+                case HEAP_DIRECT:
+                    input = ByteBuffer.wrap(newMessage());
+                    output = ByteBuffer.allocateDirect(outputSize);
+                    break;
+                case DIRECT_DIRECT:
+                    input = toDirect(newMessage());
+                    output = ByteBuffer.allocateDirect(outputSize);
+                    break;
+                case DIRECT_HEAP:
+                    input = toDirect(newMessage());
+                    output = ByteBuffer.allocate(outputSize);
+                    break;
+                default: {
+                    throw new IllegalStateException(
+                            "Unexpected buffertype: " + config.bufferType());
+                }
+            }
+        }
+
+        @Override
+        int encrypt() throws Exception {
+            initCipher();
+            input.position(0);
+            output.clear();
+            return cipher.doFinal(input, output);
+        }
+
+        private static ByteBuffer toDirect(byte[] data) {
+            ByteBuffer buffer = ByteBuffer.allocateDirect(data.length);
+            buffer.put(data);
+            buffer.flip();
+            return buffer;
+        }
+    }
+}
diff --git a/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/CipherFactory.java b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/CipherFactory.java
new file mode 100644
index 0000000..f1ae323
--- /dev/null
+++ b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/CipherFactory.java
@@ -0,0 +1,29 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+
+/**
+ * Factory for {@link Cipher} instances.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface CipherFactory {
+  Cipher newCipher(String transformation) throws NoSuchPaddingException, NoSuchAlgorithmException;
+}
diff --git a/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/ClientEndpoint.java b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/ClientEndpoint.java
new file mode 100644
index 0000000..fd38975
--- /dev/null
+++ b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/ClientEndpoint.java
@@ -0,0 +1,109 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.nio.channels.ClosedChannelException;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * Client-side endpoint. Provides basic services for sending/receiving messages from the client
+ * socket.
+ */
+final class ClientEndpoint {
+    private final SSLSocket socket;
+    private InputStream input;
+    private OutputStream output;
+
+    ClientEndpoint(SSLSocketFactory socketFactory, ChannelType channelType, int port,
+            String[] protocols, String[] ciphers) throws IOException {
+        socket = channelType.newClientSocket(socketFactory, InetAddress.getLoopbackAddress(), port);
+        socket.setEnabledProtocols(protocols);
+        socket.setEnabledCipherSuites(ciphers);
+    }
+
+    void start() {
+        try {
+            socket.startHandshake();
+            input = socket.getInputStream();
+            output = socket.getOutputStream();
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
+    void stop() {
+        try {
+            socket.close();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    int readMessage(byte[] buffer) {
+        try {
+            int totalBytesRead = 0;
+            while (totalBytesRead < buffer.length) {
+                int remaining = buffer.length - totalBytesRead;
+                int bytesRead = input.read(buffer, totalBytesRead, remaining);
+                if (bytesRead == -1) {
+                    break;
+                }
+                totalBytesRead += bytesRead;
+            }
+            return totalBytesRead;
+        } catch (SSLException e) {
+            if (e.getCause() instanceof EOFException) {
+                return -1;
+            }
+            throw new RuntimeException(e);
+        } catch (ClosedChannelException e) {
+            // Thrown for channel-based sockets. Just treat like EOF.
+            return -1;
+        }  catch (SocketException e) {
+            // The socket was broken. Just treat like EOF.
+            return -1;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    void sendMessage(byte[] data) {
+        try {
+            output.write(data);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    void flush() {
+        try {
+            output.flush();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/ClientSocketBenchmark.java b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/ClientSocketBenchmark.java
new file mode 100644
index 0000000..1bb35b4
--- /dev/null
+++ b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/ClientSocketBenchmark.java
@@ -0,0 +1,149 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.TestUtils.newTextMessage;
+
+import java.io.OutputStream;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Benchmark for comparing performance of client socket implementations.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class ClientSocketBenchmark {
+    /**
+     * Provider for the benchmark configuration
+     */
+    interface Config {
+        EndpointFactory clientFactory();
+        EndpointFactory serverFactory();
+        int messageSize();
+        String cipher();
+        ChannelType channelType();
+        BenchmarkProtocol protocol();
+    }
+
+    private ClientEndpoint client;
+    private ServerEndpoint server;
+    private byte[] message;
+    private ExecutorService executor;
+    private Future<?> sendingFuture;
+    private volatile boolean stopping;
+
+    private static final AtomicLong bytesCounter = new AtomicLong();
+    private AtomicBoolean recording = new AtomicBoolean();
+
+    ClientSocketBenchmark(Config config) throws Exception {
+        recording.set(false);
+
+        message = newTextMessage(config.messageSize());
+
+        // Always use the same server for consistency across the benchmarks.
+        server = config.serverFactory().newServer(
+                ChannelType.CHANNEL, config.messageSize(), config.protocol().getProtocols(),
+                ciphers(config));
+
+        server.setMessageProcessor(new ServerEndpoint.MessageProcessor() {
+            @Override
+            public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
+                if (recording.get()) {
+                    // Server received a message, increment the count.
+                    bytesCounter.addAndGet(numBytes);
+                }
+            }
+        });
+        Future<?> connectedFuture = server.start();
+
+        client = config.clientFactory().newClient(
+            config.channelType(), server.port(), config.protocol().getProtocols(), ciphers(config));
+        client.start();
+
+        // Wait for the initial connection to complete.
+        connectedFuture.get(5, TimeUnit.SECONDS);
+
+        executor = Executors.newSingleThreadExecutor();
+        sendingFuture = executor.submit(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Thread thread = Thread.currentThread();
+                    while (!stopping && !thread.isInterrupted()) {
+                        client.sendMessage(message);
+                    }
+                } finally {
+                    client.flush();
+                }
+            }
+        });
+    }
+
+    void close() throws Exception {
+        stopping = true;
+
+        // Wait for the sending thread to stop.
+        sendingFuture.get(5, TimeUnit.SECONDS);
+
+        client.stop();
+        server.stop();
+        executor.shutdown();
+        executor.awaitTermination(5, TimeUnit.SECONDS);
+    }
+
+    /**
+     * Simple benchmark for the amount of time to send a given number of messages (used by
+     * Caliper).
+     */
+    void time(final int numMessages) throws Exception {
+        reset();
+        recording.set(true);
+
+        while (bytesCounter.get() < numMessages) {
+            Thread.sleep(50);
+        }
+
+        recording.set(false);
+    }
+
+    /**
+     * Simple benchmark for throughput (used by JMH).
+     */
+    void throughput() throws Exception {
+        recording.set(true);
+        // Send as many messages as we can in a second.
+        Thread.sleep(1001);
+        recording.set(false);
+    }
+
+    static void reset() {
+        bytesCounter.set(0);
+    }
+
+    static long bytesPerSecond() {
+        return bytesCounter.get();
+    }
+
+    private String[] ciphers(Config config) {
+        return new String[] {config.cipher()};
+    }
+}
diff --git a/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/EndpointFactory.java b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/EndpointFactory.java
new file mode 100644
index 0000000..0b09579
--- /dev/null
+++ b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/EndpointFactory.java
@@ -0,0 +1,15 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+
+/**
+ * Utility for creating test client and server endpoints.
+ */
+interface EndpointFactory {
+  ClientEndpoint newClient(ChannelType channelType, int port, String[] protocols,
+      String[] ciphers) throws IOException;
+
+  ServerEndpoint newServer(ChannelType channelType, int messageSize,
+      String[] protocols, String[] ciphers) throws IOException;
+}
diff --git a/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/EngineFactory.java b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/EngineFactory.java
new file mode 100644
index 0000000..662b3e9
--- /dev/null
+++ b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/EngineFactory.java
@@ -0,0 +1,31 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Factory for {@link SSLEngine} instances.
+ */
+interface EngineFactory {
+
+    SSLEngine newClientEngine(String cipher, boolean useAlpn);
+
+    SSLEngine newServerEngine(String cipher, boolean useAlpn);
+
+    void dispose(SSLEngine engine);
+}
diff --git a/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/EngineHandshakeBenchmark.java b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/EngineHandshakeBenchmark.java
new file mode 100644
index 0000000..84a48c8
--- /dev/null
+++ b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/EngineHandshakeBenchmark.java
@@ -0,0 +1,172 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2017 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.nio.ByteBuffer;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLException;
+
+/**
+ * Benchmark comparing handshake performance of various engine implementations to conscrypt.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class EngineHandshakeBenchmark {
+    /**
+     * Provider for the benchmark configuration
+     */
+    interface Config {
+        BufferType bufferType();
+        EngineFactory engineFactory();
+        String cipher();
+        boolean useAlpn();
+        BenchmarkProtocol protocol();
+        int rttMillis();
+    }
+
+    private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocateDirect(0);
+
+    private final EngineFactory engineFactory;
+    private final String cipher;
+    private final boolean useAlpn;
+    private final String[] protocols;
+    private final int rttMillis;
+
+    private final ByteBuffer clientApplicationBuffer;
+    private final ByteBuffer clientPacketBuffer;
+    private final ByteBuffer serverApplicationBuffer;
+    private final ByteBuffer serverPacketBuffer;
+
+    EngineHandshakeBenchmark(Config config) throws Exception {
+        engineFactory = config.engineFactory();
+        cipher = config.cipher();
+        useAlpn = config.useAlpn();
+        protocols = config.protocol().getProtocols();
+        rttMillis = config.rttMillis();
+        BufferType bufferType = config.bufferType();
+
+        SSLEngine clientEngine = engineFactory.newClientEngine(cipher, useAlpn);
+        SSLEngine serverEngine = engineFactory.newServerEngine(cipher, useAlpn);
+
+        // Create the application and packet buffers for both endpoints.
+        clientApplicationBuffer = bufferType.newApplicationBuffer(clientEngine);
+        serverApplicationBuffer = bufferType.newApplicationBuffer(serverEngine);
+        clientPacketBuffer = bufferType.newPacketBuffer(clientEngine);
+        serverPacketBuffer = bufferType.newPacketBuffer(serverEngine);
+
+        engineFactory.dispose(clientEngine);
+        engineFactory.dispose(serverEngine);
+    }
+
+    void handshake() throws SSLException {
+        SSLEngine client = engineFactory.newClientEngine(cipher, useAlpn);
+        SSLEngine server = engineFactory.newServerEngine(cipher, useAlpn);
+        clientApplicationBuffer.clear();
+        clientPacketBuffer.clear();
+        serverApplicationBuffer.clear();
+        serverPacketBuffer.clear();
+
+        client.setEnabledProtocols(protocols);
+        server.setEnabledProtocols(protocols);
+
+        client.beginHandshake();
+        server.beginHandshake();
+
+        doHandshake(client, server);
+
+        engineFactory.dispose(client);
+        engineFactory.dispose(server);
+    }
+
+    private void doHandshake(SSLEngine client, SSLEngine server) throws SSLException {
+        while (true) {
+            // Send as many client-to-server messages as possible
+            doHalfHandshake(client, server, clientPacketBuffer, serverApplicationBuffer);
+
+            if (client.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING
+                    && server.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) {
+                return;
+            }
+
+            // Do the same with server-to-client messages
+            doHalfHandshake(server, client, serverPacketBuffer, clientApplicationBuffer);
+
+            if (client.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING
+                    && server.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) {
+                return;
+            }
+        }
+    }
+
+    private void doHalfHandshake(SSLEngine sender, SSLEngine receiver,
+            ByteBuffer senderPacketBuffer, ByteBuffer receiverApplicationBuffer)
+            throws SSLException {
+        SSLEngineResult senderResult;
+        SSLEngineResult receiverResult;
+
+        do {
+            senderResult = sender.wrap(EMPTY_BUFFER, senderPacketBuffer);
+            runDelegatedTasks(senderResult, sender);
+            senderPacketBuffer.flip();
+            receiverResult = receiver.unwrap(senderPacketBuffer, receiverApplicationBuffer);
+            runDelegatedTasks(receiverResult, receiver);
+            senderPacketBuffer.compact();
+        } while (senderResult.getHandshakeStatus() == HandshakeStatus.NEED_WRAP);
+
+        if (rttMillis > 0) {
+            try {
+                Thread.sleep(rttMillis / 2);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private static void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) {
+        if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+            for (;;) {
+                Runnable task = engine.getDelegatedTask();
+                if (task == null) {
+                    break;
+                }
+                task.run();
+            }
+        }
+    }
+
+    private static boolean isHandshakeFinished(SSLEngineResult result) {
+        return result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED;
+    }
+}
diff --git a/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/EngineWrapBenchmark.java b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/EngineWrapBenchmark.java
new file mode 100644
index 0000000..5595485
--- /dev/null
+++ b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/EngineWrapBenchmark.java
@@ -0,0 +1,161 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2017 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.TestUtils.doEngineHandshake;
+import static com.android.org.conscrypt.TestUtils.newTextMessage;
+import static org.junit.Assert.assertEquals;
+
+import java.nio.ByteBuffer;
+import java.util.Locale;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+
+/**
+ * Benchmark comparing performance of various engine implementations to conscrypt.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class EngineWrapBenchmark {
+    /**
+     * Provider for the benchmark configuration
+     */
+    interface Config {
+        BufferType bufferType();
+        EngineFactory engineFactory();
+        int messageSize();
+        String cipher();
+    }
+
+    private final EngineFactory engineFactory;
+    private final String cipher;
+    private final SSLEngine clientEngine;
+    private final SSLEngine serverEngine;
+
+    private final ByteBuffer messageBuffer;
+    private final ByteBuffer clientApplicationBuffer;
+    private final ByteBuffer clientPacketBuffer;
+    private final ByteBuffer serverApplicationBuffer;
+    private final ByteBuffer serverPacketBuffer;
+    private final ByteBuffer preEncryptedBuffer;
+
+    EngineWrapBenchmark(Config config) throws Exception {
+        engineFactory = config.engineFactory();
+        cipher = config.cipher();
+        BufferType bufferType = config.bufferType();
+
+        clientEngine = engineFactory.newClientEngine(cipher, false);
+        serverEngine = engineFactory.newServerEngine(cipher, false);
+
+        // Create the application and packet buffers for both endpoints.
+        clientApplicationBuffer = bufferType.newApplicationBuffer(clientEngine);
+        serverApplicationBuffer = bufferType.newApplicationBuffer(serverEngine);
+        clientPacketBuffer = bufferType.newPacketBuffer(clientEngine);
+        serverPacketBuffer = bufferType.newPacketBuffer(serverEngine);
+
+        // Generate the message to be sent from the client.
+        int messageSize = config.messageSize();
+        messageBuffer = bufferType.newBuffer(messageSize);
+        messageBuffer.put(newTextMessage(messageSize));
+        messageBuffer.flip();
+
+        // Complete the initial TLS handshake.
+        doEngineHandshake(clientEngine, serverEngine, clientApplicationBuffer, clientPacketBuffer,
+                serverApplicationBuffer, serverPacketBuffer, true);
+
+        // Populate the pre-encrypted buffer for use with the unwrap benchmark.
+        preEncryptedBuffer = bufferType.newBuffer(clientEngine.getSession().getPacketBufferSize());
+        doWrap(messageBuffer, preEncryptedBuffer);
+        doUnwrap(preEncryptedBuffer, serverApplicationBuffer);
+    }
+
+    void teardown() {
+        engineFactory.dispose(clientEngine);
+        engineFactory.dispose(serverEngine);
+    }
+
+    void wrap() throws SSLException {
+        // Reset the buffers.
+        messageBuffer.position(0);
+        clientPacketBuffer.clear();
+
+        // Wrap the original message and create the encrypted data.
+        doWrap(messageBuffer, clientPacketBuffer);
+
+        // Lightweight comparison - just make sure the data length is correct.
+        assertEquals(preEncryptedBuffer.limit(), clientPacketBuffer.limit());
+    }
+
+    /**
+     * Simple benchmark that sends a single message from client to server.
+     */
+    void wrapAndUnwrap() throws SSLException {
+        // Reset the buffers.
+        messageBuffer.position(0);
+        clientPacketBuffer.clear();
+        serverApplicationBuffer.clear();
+
+        // Wrap the original message and create the encrypted data.
+        doWrap(messageBuffer, clientPacketBuffer);
+
+        // Unwrap the encrypted data and get back the original result.
+        doUnwrap(clientPacketBuffer, serverApplicationBuffer);
+
+        // Lightweight comparison - just make sure the unencrypted data length is correct.
+        assertEquals(messageBuffer.limit(), serverApplicationBuffer.limit());
+    }
+
+    private void doWrap(ByteBuffer src, ByteBuffer dst) throws SSLException {
+        // Wrap the original message and create the encrypted data.
+        verifyResult(src, clientEngine.wrap(src, dst));
+        dst.flip();
+    }
+
+    private void doUnwrap(ByteBuffer src, ByteBuffer dst) throws SSLException {
+        verifyResult(src, serverEngine.unwrap(src, dst));
+        dst.flip();
+    }
+
+    private void verifyResult(ByteBuffer src, SSLEngineResult result) {
+        if (result.getStatus() != SSLEngineResult.Status.OK) {
+            throw new RuntimeException("Operation returned unexpected result " + result);
+        }
+        if (result.bytesConsumed() != src.limit()) {
+            throw new RuntimeException(
+                    String.format(Locale.US,
+                            "Operation didn't consume all bytes. Expected %d, consumed %d.",
+                            src.limit(), result.bytesConsumed()));
+        }
+    }
+}
diff --git a/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/ServerEndpoint.java b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/ServerEndpoint.java
new file mode 100644
index 0000000..b15cc79
--- /dev/null
+++ b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/ServerEndpoint.java
@@ -0,0 +1,200 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.SocketException;
+import java.nio.channels.ClosedChannelException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * A simple socket-based test server.
+ */
+final class ServerEndpoint {
+    /**
+     * A processor for receipt of a single message.
+     * @hide This class is not part of the Android public SDK API
+     */
+    public interface MessageProcessor {
+        void processMessage(byte[] message, int numBytes, OutputStream os);
+    }
+
+    /**
+     * A {@link MessageProcessor} that simply echos back the received message to the client.
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static final class EchoProcessor implements MessageProcessor {
+        @Override
+        public void processMessage(byte[] message, int numBytes, OutputStream os) {
+            try {
+                os.write(message, 0, numBytes);
+                os.flush();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private final ServerSocket serverSocket;
+    private final ChannelType channelType;
+    private final SSLSocketFactory socketFactory;
+    private final int messageSize;
+    private final String[] protocols;
+    private final String[] cipherSuites;
+    private final byte[] buffer;
+    private SSLSocket socket;
+    private ExecutorService executor;
+    private InputStream inputStream;
+    private OutputStream outputStream;
+    private volatile boolean stopping;
+    private volatile MessageProcessor messageProcessor = new EchoProcessor();
+    private volatile Future<?> processFuture;
+
+    ServerEndpoint(SSLSocketFactory socketFactory, SSLServerSocketFactory serverSocketFactory,
+            ChannelType channelType, int messageSize, String[] protocols,
+            String[] cipherSuites) throws IOException {
+        this.serverSocket = channelType.newServerSocket(serverSocketFactory);
+        this.socketFactory = socketFactory;
+        this.channelType = channelType;
+        this.messageSize = messageSize;
+        this.protocols = protocols;
+        this.cipherSuites = cipherSuites;
+        buffer = new byte[messageSize];
+    }
+
+    void setMessageProcessor(MessageProcessor messageProcessor) {
+        this.messageProcessor = messageProcessor;
+    }
+
+    Future<?> start() throws IOException {
+        executor = Executors.newSingleThreadExecutor();
+        return executor.submit(new AcceptTask());
+    }
+
+    void stop() {
+        try {
+            stopping = true;
+
+            if (socket != null) {
+                socket.close();
+                socket = null;
+            }
+
+            if (processFuture != null) {
+                processFuture.get(5, TimeUnit.SECONDS);
+            }
+
+            serverSocket.close();
+
+            if (executor != null) {
+                executor.shutdown();
+                executor.awaitTermination(5, TimeUnit.SECONDS);
+                executor = null;
+            }
+        } catch (IOException | InterruptedException | ExecutionException | TimeoutException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public int port() {
+        return serverSocket.getLocalPort();
+    }
+
+    private final class AcceptTask implements Runnable {
+        @Override
+        public void run() {
+            try {
+                if (stopping) {
+                    return;
+                }
+                socket = channelType.accept(serverSocket, socketFactory);
+                socket.setEnabledProtocols(protocols);
+                socket.setEnabledCipherSuites(cipherSuites);
+
+                socket.startHandshake();
+
+                inputStream = socket.getInputStream();
+                outputStream = socket.getOutputStream();
+
+                if (stopping) {
+                    return;
+                }
+                processFuture = executor.submit(new ProcessTask());
+            } catch (IOException e) {
+                e.printStackTrace();
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private final class ProcessTask implements Runnable {
+        @Override
+        public void run() {
+            try {
+                Thread thread = Thread.currentThread();
+                while (!stopping && !thread.isInterrupted()) {
+                    int bytesRead = readMessage();
+                    if (!stopping && !thread.isInterrupted()) {
+                        messageProcessor.processMessage(buffer, bytesRead, outputStream);
+                    }
+                }
+            } catch (Throwable e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        private int readMessage() throws IOException {
+            int totalBytesRead = 0;
+            while (!stopping && totalBytesRead < messageSize) {
+                try {
+                    int remaining = messageSize - totalBytesRead;
+                    int bytesRead = inputStream.read(buffer, totalBytesRead, remaining);
+                    if (bytesRead == -1) {
+                        break;
+                    }
+                    totalBytesRead += bytesRead;
+                } catch (SSLException e) {
+                    if (e.getCause() instanceof EOFException) {
+                        break;
+                    }
+                    throw e;
+                } catch (ClosedChannelException e) {
+                    // Thrown for channel-based sockets. Just treat like EOF.
+                    break;
+                } catch (SocketException e) {
+                    // The socket was broken. Just treat like EOF.
+                    break;
+                }
+            }
+            return totalBytesRead;
+        }
+    }
+}
diff --git a/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/ServerSocketBenchmark.java b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/ServerSocketBenchmark.java
new file mode 100644
index 0000000..f8a80bb
--- /dev/null
+++ b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/ServerSocketBenchmark.java
@@ -0,0 +1,151 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.TestUtils.getProtocols;
+import static com.android.org.conscrypt.TestUtils.newTextMessage;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.SocketException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import com.android.org.conscrypt.ServerEndpoint.MessageProcessor;
+
+/**
+ * Benchmark for comparing performance of server socket implementations.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class ServerSocketBenchmark {
+    /**
+     * Provider for the benchmark configuration
+     */
+    interface Config {
+        EndpointFactory clientFactory();
+        EndpointFactory serverFactory();
+        int messageSize();
+        String cipher();
+        ChannelType channelType();
+    }
+
+    private ClientEndpoint client;
+    private ServerEndpoint server;
+    private ExecutorService executor;
+    private Future<?> receivingFuture;
+    private volatile boolean stopping;
+    private static final AtomicLong bytesCounter = new AtomicLong();
+    private AtomicBoolean recording = new AtomicBoolean();
+
+    ServerSocketBenchmark(final Config config) throws Exception {
+        recording.set(false);
+
+        byte[] message = newTextMessage(config.messageSize());
+
+        final ChannelType channelType = config.channelType();
+
+        server = config.serverFactory().newServer(
+            channelType, config.messageSize(), getProtocols(), ciphers(config));
+        server.setMessageProcessor(new MessageProcessor() {
+            @Override
+            public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
+                try {
+                    try {
+                        while (!stopping) {
+                            os.write(inMessage, 0, numBytes);
+                        }
+                    } finally {
+                        os.flush();
+                    }
+                } catch (SocketException e) {
+                    // Just ignore.
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+
+        Future<?> connectedFuture = server.start();
+
+        // Always use the same client for consistency across the benchmarks.
+        client = config.clientFactory().newClient(
+                ChannelType.CHANNEL, server.port(), getProtocols(), ciphers(config));
+        client.start();
+
+        // Wait for the initial connection to complete.
+        connectedFuture.get(5, TimeUnit.SECONDS);
+
+        // Start the server-side streaming by sending a message to the server.
+        client.sendMessage(message);
+        client.flush();
+
+        executor = Executors.newSingleThreadExecutor();
+        receivingFuture = executor.submit(new Runnable() {
+            @Override
+            public void run() {
+                Thread thread = Thread.currentThread();
+                byte[] buffer = new byte[config.messageSize()];
+                while (!stopping && !thread.isInterrupted()) {
+                    int numBytes = client.readMessage(buffer);
+                    if (numBytes < 0) {
+                        return;
+                    }
+                    assertEquals(config.messageSize(), numBytes);
+
+                    // Increment the message counter if we're recording.
+                    if (recording.get()) {
+                        bytesCounter.addAndGet(numBytes);
+                    }
+                }
+            }
+        });
+    }
+
+    void close() throws Exception {
+        stopping = true;
+        // Stop and wait for sending to complete.
+        server.stop();
+        client.stop();
+        executor.shutdown();
+        receivingFuture.get(5, TimeUnit.SECONDS);
+        executor.awaitTermination(5, TimeUnit.SECONDS);
+    }
+
+    void throughput() throws Exception {
+        recording.set(true);
+        // Send as many messages as we can in a second.
+        Thread.sleep(1001);
+        recording.set(false);
+    }
+
+    static void reset() {
+        bytesCounter.set(0);
+    }
+
+    static long bytesPerSecond() {
+        return bytesCounter.get();
+    }
+
+    private String[] ciphers(Config config) {
+        return new String[] {config.cipher()};
+    }
+}
diff --git a/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/Transformation.java b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/Transformation.java
new file mode 100644
index 0000000..f50cad5
--- /dev/null
+++ b/repackaged/benchmark-base/src/main/java/com/android/org/conscrypt/Transformation.java
@@ -0,0 +1,89 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.KeyGenerator;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+/**
+ * Supported cipher transformations.
+ * @hide This class is not part of the Android public SDK API
+ */
+@SuppressWarnings({"ImmutableEnumChecker", "unused"})
+public enum Transformation {
+    AES_CBC_PKCS5("AES", "CBC", "PKCS5Padding", new AesKeyGen()),
+    AES_ECB_PKCS5("AES", "ECB", "PKCS5Padding", new AesKeyGen()),
+    AES_GCM_NO("AES", "GCM", "NoPadding", new AesKeyGen()),
+    RSA_ECB_PKCS1("RSA", "ECB", "PKCS1Padding", new RsaKeyGen());
+
+    Transformation(String algorithm, String mode, String padding, KeyGen keyGen) {
+        this.algorithm = algorithm;
+        this.mode = mode;
+        this.padding = padding;
+        this.keyGen = keyGen;
+    }
+
+    final String algorithm;
+    final String mode;
+    final String padding;
+    final KeyGen keyGen;
+
+    String toFormattedString() {
+        return algorithm + "/" + mode + "/" + padding;
+    }
+
+    Key newEncryptKey() {
+        return keyGen.newEncryptKey();
+    }
+
+    private interface KeyGen { Key newEncryptKey(); }
+
+    private static final class RsaKeyGen implements KeyGen {
+        @Override
+        public Key newEncryptKey() {
+            try {
+                // Use Bouncy castle
+                KeyPairGenerator generator =
+                        KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
+                generator.initialize(2048);
+                KeyPair pair = generator.generateKeyPair();
+                return pair.getPublic();
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private static final class AesKeyGen implements KeyGen {
+        @Override
+        public Key newEncryptKey() {
+            try {
+                // Just use the JDK's provider.
+                KeyGenerator keyGen = KeyGenerator.getInstance("AES");
+                keyGen.init(256);
+                return keyGen.generateKey();
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractConscryptEngine.java b/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractConscryptEngine.java
new file mode 100644
index 0000000..a79178d
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractConscryptEngine.java
@@ -0,0 +1,184 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.nio.ByteBuffer;
+import java.security.PrivateKey;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+
+/**
+ * Abstract base class for all Conscrypt {@link SSLEngine} classes.
+ */
+abstract class AbstractConscryptEngine extends SSLEngine {
+    abstract void setBufferAllocator(BufferAllocator bufferAllocator);
+
+    /**
+     * Returns the maximum overhead, in bytes, of sealing a record with SSL.
+     */
+    abstract int maxSealOverhead();
+
+    /**
+     * Enables/disables TLS Channel ID for this server engine.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @throws IllegalStateException if this is a client engine or if the handshake has already
+     *         started.
+     */
+    abstract void setChannelIdEnabled(boolean enabled);
+
+    /**
+     * Gets the TLS Channel ID for this server engine. Channel ID is only available once the
+     * handshake completes.
+     *
+     * @return channel ID or {@code null} if not available.
+     *
+     * @throws IllegalStateException if this is a client engine or if the handshake has not yet
+     * completed.
+     * @throws SSLException if channel ID is available but could not be obtained.
+     */
+    abstract byte[] getChannelId() throws SSLException;
+
+    /**
+     * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client engine.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key (disables
+     *        TLS Channel ID). The private key must be an Elliptic Curve (EC) key based on the NIST
+     *        P-256 curve (aka SECG secp256r1 or ANSI X9.62 prime256v1).
+     *
+     * @throws IllegalStateException if this is a server engine or if the handshake has already
+     *         started.
+     */
+    abstract void setChannelIdPrivateKey(PrivateKey privateKey);
+
+    /**
+     * Sets the listener for the completion of the TLS handshake.
+     */
+    abstract void setHandshakeListener(HandshakeListener handshakeListener);
+
+    /**
+     * This method enables Server Name Indication (SNI) and overrides the {@link PeerInfoProvider}
+     * supplied during engine creation.
+     */
+    abstract void setHostname(String hostname);
+
+    /**
+     * Returns the hostname from {@link #setHostname(String)} or supplied by the
+     * {@link PeerInfoProvider} upon creation. No DNS resolution is attempted before
+     * returning the hostname.
+     */
+    abstract String getHostname();
+
+    @Override public abstract String getPeerHost();
+
+    @Override public abstract int getPeerPort();
+
+    /* @Override */
+    @SuppressWarnings("MissingOverride") // For compilation with Java 6.
+    public final SSLSession getHandshakeSession() {
+        return handshakeSession();
+    }
+
+    /**
+     * Work-around to allow this method to be called on older versions of Android.
+     */
+    abstract SSLSession handshakeSession();
+
+    @Override
+    public abstract SSLEngineResult unwrap(ByteBuffer src, ByteBuffer dst) throws SSLException;
+
+    @Override
+    public abstract SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts) throws SSLException;
+
+    @Override
+    public abstract SSLEngineResult unwrap(final ByteBuffer src, final ByteBuffer[] dsts,
+            final int offset, final int length) throws SSLException;
+
+    abstract SSLEngineResult unwrap(final ByteBuffer[] srcs, final ByteBuffer[] dsts)
+            throws SSLException;
+
+    abstract SSLEngineResult unwrap(final ByteBuffer[] srcs, int srcsOffset, final int srcsLength,
+            final ByteBuffer[] dsts, final int dstsOffset, final int dstsLength)
+            throws SSLException;
+
+    @Override
+    public abstract SSLEngineResult wrap(ByteBuffer src, ByteBuffer dst) throws SSLException;
+
+    @Override
+    public abstract SSLEngineResult wrap(
+            ByteBuffer[] srcs, int srcsOffset, int srcsLength, ByteBuffer dst) throws SSLException;
+
+    /**
+     * This method enables session ticket support.
+     *
+     * @param useSessionTickets True to enable session tickets
+     */
+    abstract void setUseSessionTickets(boolean useSessionTickets);
+
+    /**
+     * Sets the list of ALPN protocols.
+     *
+     * @param protocols the list of ALPN protocols
+     */
+    abstract void setApplicationProtocols(String[] protocols);
+
+    /**
+     * Returns the list of supported ALPN protocols.
+     */
+    abstract String[] getApplicationProtocols();
+
+    @SuppressWarnings("MissingOverride") // For compiling pre Java 9.
+    public abstract String getApplicationProtocol();
+
+    @SuppressWarnings("MissingOverride") // For compiling pre Java 9.
+    public abstract String getHandshakeApplicationProtocol();
+
+    /**
+     * Sets an application-provided ALPN protocol selector. If provided, this will override
+     * the list of protocols set by {@link #setApplicationProtocols(String[])}.
+     */
+    abstract void setApplicationProtocolSelector(ApplicationProtocolSelector selector);
+
+    /**
+     * Returns the tls-unique channel binding value for this connection, per RFC 5929.  This
+     * will return {@code null} if there is no such value available, such as if the handshake
+     * has not yet completed or this connection is closed.
+     */
+    abstract byte[] getTlsUnique();
+
+    /**
+     * Exports a value derived from the TLS master secret as described in RFC 5705.
+     *
+     * @param label the label to use in calculating the exported value.  This must be
+     * an ASCII-only string.
+     * @param context the application-specific context value to use in calculating the
+     * exported value.  This may be {@code null} to use no application context, which is
+     * treated differently than an empty byte array.
+     * @param length the number of bytes of keying material to return.
+     * @return a value of the specified length, or {@code null} if the handshake has not yet
+     * completed or the connection has been closed.
+     * @throws SSLException if the value could not be exported.
+     */
+    abstract byte[] exportKeyingMaterial(String label, byte[] context, int length)
+            throws SSLException;
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractConscryptSocket.java b/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractConscryptSocket.java
new file mode 100644
index 0000000..00aeb8b
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractConscryptSocket.java
@@ -0,0 +1,774 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.Preconditions.checkArgument;
+import static com.android.org.conscrypt.Preconditions.checkNotNull;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.channels.SocketChannel;
+import java.security.PrivateKey;
+import java.util.ArrayList;
+import java.util.List;
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+/**
+ * Abstract base class for all Conscrypt {@link SSLSocket} classes.
+ */
+abstract class AbstractConscryptSocket extends SSLSocket {
+    final Socket socket;
+    private final boolean autoClose;
+
+    /**
+     * The peer's DNS hostname if it was supplied during creation. Note that
+     * this may be a raw IP address, so it should be checked before use with
+     * extensions that don't use it like Server Name Indication (SNI).
+     */
+    private String peerHostname;
+
+    /**
+     * The peer's port if it was supplied during creation. Should only be set if
+     * {@link #peerHostname} is also set.
+     */
+    private final int peerPort;
+
+    private final PeerInfoProvider peerInfoProvider = new PeerInfoProvider() {
+        @Override
+        String getHostname() {
+            return AbstractConscryptSocket.this.getHostname();
+        }
+
+        @Override
+        String getHostnameOrIP() {
+            return AbstractConscryptSocket.this.getHostnameOrIP();
+        }
+
+        @Override
+        int getPort() {
+            return AbstractConscryptSocket.this.getPort();
+        }
+    };
+
+    private final List<HandshakeCompletedListener> listeners =
+            new ArrayList<HandshakeCompletedListener>(2);
+
+    /**
+     * Local cache of timeout to avoid getsockopt on every read and
+     * write for non-wrapped sockets. Note that this is not used when delegating
+     * to another socket.
+     */
+    private int readTimeoutMilliseconds;
+
+    AbstractConscryptSocket() throws IOException {
+        this.socket = this;
+        this.peerHostname = null;
+        this.peerPort = -1;
+        this.autoClose = false;
+    }
+
+    AbstractConscryptSocket(String hostname, int port) throws IOException {
+        super(hostname, port);
+        this.socket = this;
+        this.peerHostname = hostname;
+        this.peerPort = port;
+        this.autoClose = false;
+    }
+
+    AbstractConscryptSocket(InetAddress address, int port) throws IOException {
+        super(address, port);
+        this.socket = this;
+        this.peerHostname = null;
+        this.peerPort = -1;
+        this.autoClose = false;
+    }
+
+    AbstractConscryptSocket(String hostname, int port, InetAddress clientAddress, int clientPort)
+            throws IOException {
+        super(hostname, port, clientAddress, clientPort);
+        this.socket = this;
+        this.peerHostname = hostname;
+        this.peerPort = port;
+        this.autoClose = false;
+    }
+
+    AbstractConscryptSocket(InetAddress address, int port, InetAddress clientAddress,
+            int clientPort) throws IOException {
+        super(address, port, clientAddress, clientPort);
+        this.socket = this;
+        this.peerHostname = null;
+        this.peerPort = -1;
+        this.autoClose = false;
+    }
+
+    AbstractConscryptSocket(Socket socket, String hostname, int port, boolean autoClose)
+            throws IOException {
+        this.socket = checkNotNull(socket, "socket");
+        this.peerHostname = hostname;
+        this.peerPort = port;
+        this.autoClose = autoClose;
+    }
+
+    @Override
+    public final void connect(SocketAddress endpoint) throws IOException {
+        connect(endpoint, 0);
+    }
+
+    /**
+     * Try to extract the peer's hostname if it's available from the endpoint address.
+     */
+    @Override
+    public final void connect(SocketAddress endpoint, int timeout) throws IOException {
+        if (peerHostname == null && endpoint instanceof InetSocketAddress) {
+            peerHostname =
+                    Platform.getHostStringFromInetSocketAddress((InetSocketAddress) endpoint);
+        }
+
+        if (isDelegating()) {
+            socket.connect(endpoint, timeout);
+        } else {
+            super.connect(endpoint, timeout);
+        }
+    }
+
+    @Override
+    public void bind(SocketAddress bindpoint) throws IOException {
+        if (isDelegating()) {
+            socket.bind(bindpoint);
+        } else {
+            super.bind(bindpoint);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+    public void close() throws IOException {
+        if (isDelegating()) {
+            if (autoClose && !socket.isClosed()) {
+                socket.close();
+            }
+        } else {
+            if (!super.isClosed()) {
+                super.close();
+            }
+        }
+    }
+
+    @Override
+    public InetAddress getInetAddress() {
+        if (isDelegating()) {
+            return socket.getInetAddress();
+        }
+        return super.getInetAddress();
+    }
+
+    @Override
+    public InetAddress getLocalAddress() {
+        if (isDelegating()) {
+            return socket.getLocalAddress();
+        }
+        return super.getLocalAddress();
+    }
+
+    @Override
+    public int getLocalPort() {
+        if (isDelegating()) {
+            return socket.getLocalPort();
+        }
+        return super.getLocalPort();
+    }
+
+    @Override
+    public SocketAddress getRemoteSocketAddress() {
+        if (isDelegating()) {
+            return socket.getRemoteSocketAddress();
+        }
+        return super.getRemoteSocketAddress();
+    }
+
+    @Override
+    public SocketAddress getLocalSocketAddress() {
+        if (isDelegating()) {
+            return socket.getLocalSocketAddress();
+        }
+        return super.getLocalSocketAddress();
+    }
+
+    @Override
+    public final int getPort() {
+        if (isDelegating()) {
+            return socket.getPort();
+        }
+
+        if (peerPort != -1) {
+            // Return the port that has been explicitly set in the constructor.
+            return peerPort;
+        }
+        return super.getPort();
+    }
+
+    @Override
+    public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
+        checkArgument(listener != null, "Provided listener is null");
+        listeners.add(listener);
+    }
+
+    @Override
+    public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
+        checkArgument(listener != null, "Provided listener is null");
+        if (!listeners.remove(listener)) {
+            throw new IllegalArgumentException("Provided listener is not registered");
+        }
+    }
+
+    /* @Override */
+    public FileDescriptor getFileDescriptor$() {
+        if (isDelegating()) {
+            return Platform.getFileDescriptor(socket);
+        }
+        return Platform.getFileDescriptorFromSSLSocket(this);
+    }
+
+    @Override
+    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+    public final void setSoTimeout(int readTimeoutMilliseconds) throws SocketException {
+        if (isDelegating()) {
+            socket.setSoTimeout(readTimeoutMilliseconds);
+        } else {
+            super.setSoTimeout(readTimeoutMilliseconds);
+            this.readTimeoutMilliseconds = readTimeoutMilliseconds;
+        }
+    }
+
+    @Override
+    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+    public final int getSoTimeout() throws SocketException {
+        if (isDelegating()) {
+            return socket.getSoTimeout();
+        }
+        return readTimeoutMilliseconds;
+    }
+
+    @Override
+    public final void sendUrgentData(int data) throws IOException {
+        throw new SocketException("Method sendUrgentData() is not supported.");
+    }
+
+    @Override
+    public final void setOOBInline(boolean on) throws SocketException {
+        throw new SocketException("Method setOOBInline() is not supported.");
+    }
+
+    @Override
+    public boolean getOOBInline() throws SocketException {
+        return false;
+    }
+
+    @Override
+    public SocketChannel getChannel() {
+        // TODO(nmittler): Support channels?
+        return null;
+    }
+
+    @Override
+    public InputStream getInputStream() throws IOException {
+        if (isDelegating()) {
+            return socket.getInputStream();
+        }
+        return super.getInputStream();
+    }
+
+    @Override
+    public OutputStream getOutputStream() throws IOException {
+        if (isDelegating()) {
+            return socket.getOutputStream();
+        }
+        return super.getOutputStream();
+    }
+
+    @Override
+    public void setTcpNoDelay(boolean on) throws SocketException {
+        if (isDelegating()) {
+            socket.setTcpNoDelay(on);
+        } else {
+            super.setTcpNoDelay(on);
+        }
+    }
+
+    @Override
+    public boolean getTcpNoDelay() throws SocketException {
+        if (isDelegating()) {
+            return socket.getTcpNoDelay();
+        }
+        return super.getTcpNoDelay();
+    }
+
+    @Override
+    public void setSoLinger(boolean on, int linger) throws SocketException {
+        if (isDelegating()) {
+            socket.setSoLinger(on, linger);
+        } else {
+            super.setSoLinger(on, linger);
+        }
+    }
+
+    @Override
+    public int getSoLinger() throws SocketException {
+        if (isDelegating()) {
+            return socket.getSoLinger();
+        }
+        return super.getSoLinger();
+    }
+
+    @Override
+    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+    public void setSendBufferSize(int size) throws SocketException {
+        if (isDelegating()) {
+            socket.setSendBufferSize(size);
+        } else {
+            super.setSendBufferSize(size);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+    public int getSendBufferSize() throws SocketException {
+        if (isDelegating()) {
+            return socket.getSendBufferSize();
+        }
+        return super.getSendBufferSize();
+    }
+
+    @Override
+    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+    public void setReceiveBufferSize(int size) throws SocketException {
+        if (isDelegating()) {
+            socket.setReceiveBufferSize(size);
+        } else {
+            super.setReceiveBufferSize(size);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+    public int getReceiveBufferSize() throws SocketException {
+        if (isDelegating()) {
+            return socket.getReceiveBufferSize();
+        }
+        return super.getReceiveBufferSize();
+    }
+
+    @Override
+    public void setKeepAlive(boolean on) throws SocketException {
+        if (isDelegating()) {
+            socket.setKeepAlive(on);
+        } else {
+            super.setKeepAlive(on);
+        }
+    }
+
+    @Override
+    public boolean getKeepAlive() throws SocketException {
+        if (isDelegating()) {
+            return socket.getKeepAlive();
+        }
+        return super.getKeepAlive();
+    }
+
+    @Override
+    public void setTrafficClass(int tc) throws SocketException {
+        if (isDelegating()) {
+            socket.setTrafficClass(tc);
+        } else {
+            super.setTrafficClass(tc);
+        }
+    }
+
+    @Override
+    public int getTrafficClass() throws SocketException {
+        if (isDelegating()) {
+            return socket.getTrafficClass();
+        }
+        return super.getTrafficClass();
+    }
+
+    @Override
+    public void setReuseAddress(boolean on) throws SocketException {
+        if (isDelegating()) {
+            socket.setReuseAddress(on);
+        } else {
+            super.setReuseAddress(on);
+        }
+    }
+
+    @Override
+    public boolean getReuseAddress() throws SocketException {
+        if (isDelegating()) {
+            return socket.getReuseAddress();
+        }
+        return super.getReuseAddress();
+    }
+
+    @Override
+    public void shutdownInput() throws IOException {
+        if (isDelegating()) {
+            socket.shutdownInput();
+        } else {
+            super.shutdownInput();
+        }
+    }
+
+    @Override
+    public void shutdownOutput() throws IOException {
+        if (isDelegating()) {
+            socket.shutdownOutput();
+        } else {
+            super.shutdownOutput();
+        }
+    }
+
+    @Override
+    public boolean isConnected() {
+        if (isDelegating()) {
+            return socket.isConnected();
+        }
+        return super.isConnected();
+    }
+
+    @Override
+    public boolean isBound() {
+        if (isDelegating()) {
+            return socket.isBound();
+        }
+        return super.isBound();
+    }
+
+    @Override
+    public boolean isClosed() {
+        if (isDelegating()) {
+            return socket.isClosed();
+        }
+        return super.isClosed();
+    }
+
+    @Override
+    public boolean isInputShutdown() {
+        if (isDelegating()) {
+            return socket.isInputShutdown();
+        }
+        return super.isInputShutdown();
+    }
+
+    @Override
+    public boolean isOutputShutdown() {
+        if (isDelegating()) {
+            return socket.isOutputShutdown();
+        }
+        return super.isOutputShutdown();
+    }
+
+    @Override
+    public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
+        if (isDelegating()) {
+            socket.setPerformancePreferences(connectionTime, latency, bandwidth);
+        } else {
+            super.setPerformancePreferences(connectionTime, latency, bandwidth);
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("SSL socket over ");
+        if (isDelegating()) {
+            builder.append(socket.toString());
+        } else {
+            builder.append(super.toString());
+        }
+        return builder.toString();
+    }
+
+    /**
+     * Returns the hostname that was supplied during socket creation. No DNS resolution is
+     * attempted before returning the hostname.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    String getHostname() {
+        return peerHostname;
+    }
+
+    /**
+     * This method enables Server Name Indication
+     *
+     * @param hostname the desired SNI hostname, or null to disable
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    void setHostname(String hostname) {
+        peerHostname = hostname;
+    }
+
+    /**
+     * For the purposes of an SSLSession, we want a way to represent the supplied hostname
+     * or the IP address in a textual representation. We do not want to perform reverse DNS
+     * lookups on this address.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    String getHostnameOrIP() {
+        if (peerHostname != null) {
+            return peerHostname;
+        }
+
+        InetAddress peerAddress = getInetAddress();
+        if (peerAddress != null) {
+            return Platform.getOriginalHostNameFromInetAddress(peerAddress);
+        }
+
+        return null;
+    }
+
+    /**
+     * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
+        throw new SocketException("Method setSoWriteTimeout() is not supported.");
+    }
+
+    /**
+     * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    int getSoWriteTimeout() throws SocketException {
+        return 0;
+    }
+
+    /**
+     * Set the handshake timeout on this socket.  This timeout is specified in
+     * milliseconds and will be used only during the handshake process.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
+        throw new SocketException("Method setHandshakeTimeout() is not supported.");
+    }
+
+    final void checkOpen() throws SocketException {
+        if (isClosed()) {
+            throw new SocketException("Socket is closed");
+        }
+    }
+
+    final PeerInfoProvider peerInfoProvider() {
+        return peerInfoProvider;
+    }
+
+    /**
+     * Called by {@link #notifyHandshakeCompletedListeners()} to get the currently active session.
+     * Unlike {@link #getSession()}, this method must not block.
+     */
+    abstract SSLSession getActiveSession();
+
+    abstract void setApplicationProtocolSelector(ApplicationProtocolSelectorAdapter selector);
+
+    final void notifyHandshakeCompletedListeners() {
+        if (listeners != null && !listeners.isEmpty()) {
+            // notify the listeners
+            HandshakeCompletedEvent event = new HandshakeCompletedEvent(this, getActiveSession());
+            for (HandshakeCompletedListener listener : listeners) {
+                try {
+                    listener.handshakeCompleted(event);
+                } catch (RuntimeException e) {
+                    // The RI runs the handlers in a separate thread,
+                    // which we do not. But we try to preserve their
+                    // behavior of logging a problem and not killing
+                    // the handshaking thread just because a listener
+                    // has a problem.
+                    Thread thread = Thread.currentThread();
+                    thread.getUncaughtExceptionHandler().uncaughtException(thread, e);
+                }
+            }
+        }
+    }
+
+    private boolean isDelegating() {
+        // Checking for null to handle the case of calling virtual methods in the super class
+        // constructor.
+        return socket != null && socket != this;
+    }
+    /* @Override */
+    @SuppressWarnings("MissingOverride") // For compilation with Java 6.
+    public abstract SSLSession getHandshakeSession();
+
+    /**
+     * This method enables session ticket support.
+     *
+     * @param useSessionTickets True to enable session tickets
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    abstract void setUseSessionTickets(boolean useSessionTickets);
+
+    /**
+     * Enables/disables TLS Channel ID for this server socket.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @throws IllegalStateException if this is a client socket or if the handshake has already
+     *         started.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    abstract void setChannelIdEnabled(boolean enabled);
+
+    /**
+     * Gets the TLS Channel ID for this server socket. Channel ID is only available once the
+     * handshake completes.
+     *
+     * @return channel ID or {@code null} if not available.
+     *
+     * @throws IllegalStateException if this is a client socket or if the handshake has not yet
+     *         completed.
+     * @throws SSLException if channel ID is available but could not be obtained.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    abstract byte[] getChannelId() throws SSLException;
+
+    /**
+     * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client socket.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key (disables
+     *        TLS Channel ID). The private key must be an Elliptic Curve (EC) key based on the NIST
+     *        P-256 curve (aka SECG secp256r1 or ANSI X9.62 prime256v1).
+     *
+     * @throws IllegalStateException if this is a server socket or if the handshake has already
+     *         started.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    abstract void setChannelIdPrivateKey(PrivateKey privateKey);
+
+    /**
+     * Returns null always for backward compatibility.
+     * @deprecated NPN is not supported
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @Deprecated
+    byte[] getNpnSelectedProtocol() {
+        return null;
+    }
+
+    /**
+     * This method does nothing and is kept for backward compatibility.
+     * @deprecated NPN is not supported
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @Deprecated
+    void setNpnProtocols(byte[] npnProtocols) {}
+
+    /**
+     * Returns the protocol agreed upon by client and server, or {@code null} if
+     * no protocol was agreed upon.
+     *
+     * @deprecated use {@link #getApplicationProtocol()} instead.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @Deprecated
+    abstract byte[] getAlpnSelectedProtocol();
+
+    /**
+     * Sets the list of ALPN protocols. This method internally converts the protocols to their
+     * wire-format form.
+     *
+     * @param alpnProtocols the list of ALPN protocols
+     * @deprecated use {@link #setApplicationProtocols(String[])} instead.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @Deprecated
+    abstract void setAlpnProtocols(String[] alpnProtocols);
+
+    /**
+     * Alternate version of {@link #setAlpnProtocols(String[])} that directly sets the list of
+     * ALPN in the wire-format form used by BoringSSL (length-prefixed 8-bit strings).
+     * Requires that all strings be encoded with US-ASCII.
+     *
+     * @param alpnProtocols the encoded form of the ALPN protocol list
+     * @deprecated Use {@link #setApplicationProtocols(String[])} instead.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @Deprecated
+    abstract void setAlpnProtocols(byte[] alpnProtocols);
+
+    /**
+     * Sets the list of ALPN protocols.
+     *
+     * @param protocols the list of ALPN protocols
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @SuppressWarnings("MissingOverride") // For compiling pre Java 9.
+    abstract void setApplicationProtocols(String[] protocols);
+
+    /**
+     * Returns the list of supported ALPN protocols.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @SuppressWarnings("MissingOverride") // For compiling pre Java 9.
+    abstract String[] getApplicationProtocols();
+
+    @SuppressWarnings("MissingOverride") // For compiling pre Java 9.
+    public abstract String getApplicationProtocol();
+
+    @SuppressWarnings("MissingOverride") // For compiling pre Java 9.
+    public abstract String getHandshakeApplicationProtocol();
+
+    /**
+     * Sets an application-provided ALPN protocol selector. If provided, this will override
+     * the list of protocols set by {@link #setApplicationProtocols(String[])}.
+     */
+    abstract void setApplicationProtocolSelector(ApplicationProtocolSelector selector);
+
+    /**
+     * Returns the tls-unique channel binding value for this connection, per RFC 5929.  This
+     * will return {@code null} if there is no such value available, such as if the handshake
+     * has not yet completed or this connection is closed.
+     */
+    abstract byte[] getTlsUnique();
+
+    /**
+     * Exports a value derived from the TLS master secret as described in RFC 5705.
+     *
+     * @param label the label to use in calculating the exported value.  This must be
+     * an ASCII-only string.
+     * @param context the application-specific context value to use in calculating the
+     * exported value.  This may be {@code null} to use no application context, which is
+     * treated differently than an empty byte array.
+     * @param length the number of bytes of keying material to return.
+     * @return a value of the specified length, or {@code null} if the handshake has not yet
+     * completed or the connection has been closed.
+     * @throws SSLException if the value could not be exported.
+     */
+    abstract byte[] exportKeyingMaterial(String label, byte[] context, int length)
+            throws SSLException;
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractSessionContext.java b/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractSessionContext.java
new file mode 100644
index 0000000..d777735
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/AbstractSessionContext.java
@@ -0,0 +1,305 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+
+/**
+ * Supports SSL session caches.
+ */
+abstract class AbstractSessionContext implements SSLSessionContext {
+
+    /**
+     * Maximum lifetime of a session (in seconds) after which it's considered invalid and should not
+     * be used to for new connections.
+     */
+    private static final int DEFAULT_SESSION_TIMEOUT_SECONDS = 8 * 60 * 60;
+
+    private volatile int maximumSize;
+    private volatile int timeout = DEFAULT_SESSION_TIMEOUT_SECONDS;
+
+    final long sslCtxNativePointer = NativeCrypto.SSL_CTX_new();
+
+    @SuppressWarnings("serial")
+    private final Map<ByteArray, NativeSslSession> sessions =
+            new LinkedHashMap<ByteArray, NativeSslSession>() {
+                @Override
+                protected boolean removeEldestEntry(
+                        Map.Entry<ByteArray, NativeSslSession> eldest) {
+                    // NOTE: does not take into account any session that may have become
+                    // invalid.
+                    if (maximumSize > 0 && size() > maximumSize) {
+                        // Let the subclass know.
+                        onBeforeRemoveSession(eldest.getValue());
+                        return true;
+                    }
+                    return false;
+                }
+            };
+
+    /**
+     * Constructs a new session context.
+     *
+     * @param maximumSize of cache
+     */
+    AbstractSessionContext(int maximumSize) {
+        this.maximumSize = maximumSize;
+    }
+
+    /**
+     * This method is provided for API-compatibility only, not intended for use. No guarantees
+     * are made WRT performance.
+     */
+    @Override
+    public final Enumeration<byte[]> getIds() {
+        // Make a copy of the IDs.
+        final Iterator<NativeSslSession> iter;
+        synchronized (sessions) {
+            iter = Arrays.asList(sessions.values().toArray(new NativeSslSession[sessions.size()]))
+                    .iterator();
+        }
+        return new Enumeration<byte[]>() {
+            private NativeSslSession next;
+
+            @Override
+            public boolean hasMoreElements() {
+                if (next != null) {
+                    return true;
+                }
+                while (iter.hasNext()) {
+                    NativeSslSession session = iter.next();
+                    if (session.isValid()) {
+                        next = session;
+                        return true;
+                    }
+                }
+                next = null;
+                return false;
+            }
+
+            @Override
+            public byte[] nextElement() {
+                if (hasMoreElements()) {
+                    byte[] id = next.getId();
+                    next = null;
+                    return id;
+                }
+                throw new NoSuchElementException();
+            }
+        };
+    }
+
+    /**
+     * This is provided for API-compatibility only, not intended for use. No guarantees are
+     * made WRT performance or the validity of the returned session.
+     */
+    @Override
+    public final SSLSession getSession(byte[] sessionId) {
+        if (sessionId == null) {
+            throw new NullPointerException("sessionId");
+        }
+        ByteArray key = new ByteArray(sessionId);
+        NativeSslSession session;
+        synchronized (sessions) {
+            session = sessions.get(key);
+        }
+        if (session != null && session.isValid()) {
+            return session.toSSLSession();
+        }
+        return null;
+    }
+
+    @Override
+    public final int getSessionCacheSize() {
+        return maximumSize;
+    }
+
+    @Override
+    public final int getSessionTimeout() {
+        return timeout;
+    }
+
+    @Override
+    public final void setSessionTimeout(int seconds) throws IllegalArgumentException {
+        if (seconds < 0) {
+            throw new IllegalArgumentException("seconds < 0");
+        }
+
+        synchronized (sessions) {
+            // Set the timeout on this context.
+            timeout = seconds;
+            // setSessionTimeout(0) is defined to remove the timeout, but passing 0
+            // to SSL_CTX_set_timeout in BoringSSL sets it to the default timeout instead.
+            // Pass INT_MAX seconds (68 years), since that's equivalent for practical purposes.
+            if (seconds > 0) {
+                NativeCrypto.SSL_CTX_set_timeout(sslCtxNativePointer, this, seconds);
+            } else {
+                NativeCrypto.SSL_CTX_set_timeout(sslCtxNativePointer, this, Integer.MAX_VALUE);
+            }
+
+            Iterator<NativeSslSession> i = sessions.values().iterator();
+            while (i.hasNext()) {
+                NativeSslSession session = i.next();
+                // SSLSession's know their context and consult the
+                // timeout as part of their validity condition.
+                if (!session.isValid()) {
+                    // Let the subclass know.
+                    onBeforeRemoveSession(session);
+                    i.remove();
+                }
+            }
+        }
+    }
+
+    @Override
+    public final void setSessionCacheSize(int size) throws IllegalArgumentException {
+        if (size < 0) {
+            throw new IllegalArgumentException("size < 0");
+        }
+
+        int oldMaximum = maximumSize;
+        maximumSize = size;
+
+        // Trim cache to size if necessary.
+        if (size < oldMaximum) {
+            trimToSize();
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            NativeCrypto.SSL_CTX_free(sslCtxNativePointer, this);
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Adds the given session to the cache.
+     */
+    final void cacheSession(NativeSslSession session) {
+        byte[] id = session.getId();
+        if (id == null || id.length == 0) {
+            return;
+        }
+
+        synchronized (sessions) {
+            ByteArray key = new ByteArray(id);
+            if (sessions.containsKey(key)) {
+                removeSession(sessions.get(key));
+            }
+            // Let the subclass know.
+            onBeforeAddSession(session);
+
+            sessions.put(key, session);
+        }
+    }
+
+    /**
+     * Removes the given session from the cache.
+     */
+    final void removeSession(NativeSslSession session) {
+        byte[] id = session.getId();
+        if (id == null || id.length == 0) {
+            return;
+        }
+
+        onBeforeRemoveSession(session);
+
+        ByteArray key = new ByteArray(id);
+        synchronized (sessions) {
+            sessions.remove(key);
+        }
+    }
+
+    /**
+     * Called for server sessions only. Retrieves the session by its ID. Overridden by
+     * {@link ServerSessionContext} to
+     */
+    final NativeSslSession getSessionFromCache(byte[] sessionId) {
+        if (sessionId == null) {
+            return null;
+        }
+
+        // First, look in the in-memory cache.
+        NativeSslSession session;
+        synchronized (sessions) {
+            session = sessions.get(new ByteArray(sessionId));
+        }
+        if (session != null && session.isValid()) {
+            if (session.isSingleUse()) {
+                removeSession(session);
+            }
+            return session;
+        }
+
+        // Look in persistent cache.  We don't currently delete sessions from the persistent
+        // cache, so we may find a multi-use (aka TLS 1.2) session after having received and
+        // then used up one or more single-use (aka TLS 1.3) sessions.
+        return getSessionFromPersistentCache(sessionId);
+    }
+
+    /**
+     * Called when the given session is about to be added. Used by {@link ClientSessionContext} to
+     * update its host-and-port based cache.
+     *
+     * <p>Visible for extension only, not intended to be called directly.
+     */
+    abstract void onBeforeAddSession(NativeSslSession session);
+
+    /**
+     * Called when a session is about to be removed. Used by {@link ClientSessionContext}
+     * to update its host-and-port based cache.
+     *
+     * <p>Visible for extension only, not intended to be called directly.
+     */
+    abstract void onBeforeRemoveSession(NativeSslSession session);
+
+    /**
+     * Called for server sessions only. Retrieves the session by ID from the persistent cache.
+     *
+     * <p>Visible for extension only, not intended to be called directly.
+     */
+    abstract NativeSslSession getSessionFromPersistentCache(byte[] sessionId);
+
+    /**
+     * Makes sure cache size is < maximumSize.
+     */
+    private void trimToSize() {
+        synchronized (sessions) {
+            int size = sessions.size();
+            if (size > maximumSize) {
+                int removals = size - maximumSize;
+                Iterator<NativeSslSession> i = sessions.values().iterator();
+                while (removals-- > 0) {
+                    NativeSslSession session = i.next();
+                    onBeforeRemoveSession(session);
+                    i.remove();
+                }
+            }
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ActiveSession.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ActiveSession.java
new file mode 100644
index 0000000..a6ac475
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ActiveSession.java
@@ -0,0 +1,326 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.Preconditions.checkNotNull;
+
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.List;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSessionContext;
+
+/**
+ * A session that is dedicated a single connection and operates directly on the underlying
+ * {@code SSL}.
+ */
+final class ActiveSession implements ConscryptSession {
+    private final NativeSsl ssl;
+    private AbstractSessionContext sessionContext;
+    private byte[] id;
+    private long creationTime;
+    private String protocol;
+    private String peerHost;
+    private int peerPort = -1;
+    private long lastAccessedTime = 0;
+    private volatile javax.security.cert.X509Certificate[] peerCertificateChain;
+    private X509Certificate[] localCertificates;
+    private X509Certificate[] peerCertificates;
+    private byte[] peerCertificateOcspData;
+    private byte[] peerTlsSctData;
+
+    ActiveSession(NativeSsl ssl, AbstractSessionContext sessionContext) {
+        this.ssl = checkNotNull(ssl, "ssl");
+        this.sessionContext = checkNotNull(sessionContext, "sessionContext");
+    }
+
+    @Override
+    public byte[] getId() {
+        if (id == null) {
+            synchronized (ssl) {
+                id = ssl.getSessionId();
+            }
+        }
+        return id != null ? id.clone() : EmptyArray.BYTE;
+    }
+
+    @Override
+    public SSLSessionContext getSessionContext() {
+        return isValid() ? sessionContext : null;
+    }
+
+    @Override
+    public long getCreationTime() {
+        if (creationTime == 0) {
+            synchronized (ssl) {
+                creationTime = ssl.getTime();
+            }
+        }
+        return creationTime;
+    }
+
+    /**
+     * Returns the last time this SSL session was accessed. Accessing
+     * here is to mean that a new connection with the same SSL context data was
+     * established.
+     *
+     * @return the session's last access time in milliseconds since the epoch
+     */
+    // TODO(nathanmittler): Does lastAccessedTime need to account for session reuse?
+    @Override
+    public long getLastAccessedTime() {
+        return lastAccessedTime == 0 ? getCreationTime() : lastAccessedTime;
+    }
+
+    void setLastAccessedTime(long accessTimeMillis) {
+        lastAccessedTime = accessTimeMillis;
+    }
+
+    /**
+     * Returns the OCSP stapled response. Returns a copy of the internal arrays.
+     *
+     * The method signature matches
+     * <a
+     * href="http://download.java.net/java/jdk9/docs/api/javax/net/ssl/ExtendedSSLSession.html#getStatusResponses--">Java
+     * 9</a>.
+     *
+     * @see <a href="https://tools.ietf.org/html/rfc6066">RFC 6066</a>
+     * @see <a href="https://tools.ietf.org/html/rfc6961">RFC 6961</a>
+     */
+    @Override
+    public List<byte[]> getStatusResponses() {
+        if (peerCertificateOcspData == null) {
+            return Collections.<byte[]>emptyList();
+        }
+
+        return Collections.singletonList(peerCertificateOcspData.clone());
+    }
+
+    /**
+     * Returns the signed certificate timestamp (SCT) received from the peer. Returns a
+     * copy of the internal array.
+     *
+     * @see <a href="https://tools.ietf.org/html/rfc6962">RFC 6962</a>
+     */
+    @Override
+    public byte[] getPeerSignedCertificateTimestamp() {
+        if (peerTlsSctData == null) {
+            return null;
+        }
+        return peerTlsSctData.clone();
+    }
+
+    @Override
+    public String getRequestedServerName() {
+        synchronized (ssl) {
+            return ssl.getRequestedServerName();
+        }
+    }
+
+    @Override
+    public void invalidate() {
+        synchronized (ssl) {
+            ssl.setTimeout(0L);
+        }
+    }
+
+    @Override
+    public boolean isValid() {
+        synchronized (ssl) {
+            long creationTimeMillis = ssl.getTime();
+            long timeoutMillis = ssl.getTimeout();
+            return (System.currentTimeMillis() - timeoutMillis) < creationTimeMillis;
+        }
+    }
+
+    @Override
+    public void putValue(String name, Object value) {
+        throw new UnsupportedOperationException(
+                "All calls to this method should be intercepted by ProvidedSessionDecorator.");
+    }
+
+    @Override
+    public Object getValue(String name) {
+        throw new UnsupportedOperationException(
+                "All calls to this method should be intercepted by ProvidedSessionDecorator.");
+    }
+
+    @Override
+    public void removeValue(String name) {
+        throw new UnsupportedOperationException(
+                "All calls to this method should be intercepted by ProvidedSessionDecorator.");
+    }
+
+    @Override
+    public String[] getValueNames() {
+        throw new UnsupportedOperationException(
+                "All calls to this method should be intercepted by ProvidedSessionDecorator.");
+    }
+
+    @Override
+    public X509Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+        checkPeerCertificatesPresent();
+        return peerCertificates.clone();
+    }
+
+    @Override
+    public Certificate[] getLocalCertificates() {
+        // Local certificates never change, so set them locally as soon as they're available
+        if (localCertificates == null) {
+            synchronized (ssl) {
+                localCertificates = ssl.getLocalCertificates();
+            }
+        }
+        return localCertificates == null ? null : localCertificates.clone();
+    }
+
+    /**
+     * Returns the certificate(s) of the peer in this SSL session
+     * used in the handshaking phase of the connection.
+     * Please notice hat this method is superseded by
+     * <code>getPeerCertificates()</code>.
+     * @return an array of X509 certificates (the peer's one first and then
+     *         eventually that of the certification authority) or null if no
+     *         certificate were used during the SSL connection.
+     * @throws SSLPeerUnverifiedException if either a non-X.509 certificate
+     *         was used (i.e. Kerberos certificates) or the peer could not
+     *         be verified.
+     */
+    @Override
+    public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+            throws SSLPeerUnverifiedException {
+        checkPeerCertificatesPresent();
+        // TODO(nathanmittler): Should we clone?
+        javax.security.cert.X509Certificate[] result = peerCertificateChain;
+        if (result == null) {
+            // single-check idiom
+            peerCertificateChain = result = SSLUtils.toCertificateChain(peerCertificates);
+        }
+        return result;
+    }
+
+    @Override
+    public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+        checkPeerCertificatesPresent();
+        return peerCertificates[0].getSubjectX500Principal();
+    }
+
+    @Override
+    public Principal getLocalPrincipal() {
+        X509Certificate[] certs = (X509Certificate[]) getLocalCertificates();
+        if (certs != null && certs.length > 0) {
+            return certs[0].getSubjectX500Principal();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public String getCipherSuite() {
+        // Always get the Cipher from the SSL directly since it may have changed during a
+        // renegotiation.
+        String cipher;
+        synchronized (ssl) {
+            cipher = ssl.getCipherSuite();
+        }
+        return cipher == null ? SSLNullSession.INVALID_CIPHER : cipher;
+    }
+
+    @Override
+    public String getProtocol() {
+        String protocol = this.protocol;
+        if (protocol == null) {
+            synchronized (ssl) {
+                protocol = ssl.getVersion();
+            }
+            this.protocol = protocol;
+        }
+        return protocol;
+    }
+
+    @Override
+    public String getPeerHost() {
+        return peerHost;
+    }
+
+    @Override
+    public int getPeerPort() {
+        return peerPort;
+    }
+
+    @Override
+    public int getPacketBufferSize() {
+        return NativeConstants.SSL3_RT_MAX_PACKET_SIZE;
+    }
+
+    @Override
+    public int getApplicationBufferSize() {
+        return NativeConstants.SSL3_RT_MAX_PLAIN_LENGTH;
+    }
+
+    /**
+     * Configures the peer information once it has been received by the handshake.
+     */
+    void onPeerCertificatesReceived(
+            String peerHost, int peerPort, X509Certificate[] peerCertificates) {
+        configurePeer(peerHost, peerPort, peerCertificates);
+    }
+
+    private void configurePeer(String peerHost, int peerPort, X509Certificate[] peerCertificates) {
+        this.peerHost = peerHost;
+        this.peerPort = peerPort;
+        this.peerCertificates = peerCertificates;
+        synchronized (ssl) {
+            this.peerCertificateOcspData = ssl.getPeerCertificateOcspData();
+            this.peerTlsSctData = ssl.getPeerTlsSctData();
+        }
+    }
+
+    /**
+     * Updates the cached peer certificate after the handshake has completed
+     * (or entered False Start).
+     */
+    void onPeerCertificateAvailable(String peerHost, int peerPort) throws CertificateException {
+        synchronized (ssl) {
+            id = null;
+            if (localCertificates == null) {
+                this.localCertificates = ssl.getLocalCertificates();
+            }
+            if (this.peerCertificates == null) {
+                // When resuming a session, the cert_verify_callback (which calls
+                // onPeerCertificatesReceived) isn't called by BoringSSL during the handshake
+                // because it presumes the certs were verified in the previous connection on that
+                // session, leaving us without the peer certificates.  If that happens, fetch them
+                // explicitly.
+                configurePeer(peerHost, peerPort, ssl.getPeerCertificates());
+            }
+        }
+    }
+
+    /**
+     * Throw SSLPeerUnverifiedException on null or empty peerCertificates array
+     */
+    private void checkPeerCertificatesPresent() throws SSLPeerUnverifiedException {
+        if (peerCertificates == null || peerCertificates.length == 0) {
+            throw new SSLPeerUnverifiedException("No peer certificates");
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/AddressUtils.java b/repackaged/common/src/main/java/com/android/org/conscrypt/AddressUtils.java
new file mode 100644
index 0000000..bdd7c97
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/AddressUtils.java
@@ -0,0 +1,62 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.util.regex.Pattern;
+
+/**
+ * Utilities to check whether IP addresses meet some criteria.
+ */
+final class AddressUtils {
+    /*
+     * Regex that matches valid IPv4 and IPv6 addresses.
+     */
+    private static final String IP_PATTERN =
+            "^(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9]))|"
+            + "(?i:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:)))(?:%.+)?$";
+
+    private static Pattern ipPattern;
+
+    private AddressUtils() {}
+
+    /**
+     * Returns true when the supplied hostname is valid for SNI purposes.
+     */
+    static boolean isValidSniHostname(String sniHostname) {
+        if (sniHostname == null) {
+            return false;
+        }
+
+        // Must be a FQDN that does not have a trailing dot.
+        return (sniHostname.equalsIgnoreCase("localhost") || sniHostname.indexOf('.') != -1)
+                && !isLiteralIpAddress(sniHostname) && !sniHostname.endsWith(".")
+                && sniHostname.indexOf('\0') == -1;
+    }
+
+    /**
+     * Returns true if the supplied hostname is an literal IP address.
+     */
+    static boolean isLiteralIpAddress(String hostname) {
+        /* This is here for backwards compatibility for pre-Honeycomb devices. */
+        Pattern ipPattern = AddressUtils.ipPattern;
+        if (ipPattern == null) {
+            AddressUtils.ipPattern = ipPattern = Pattern.compile(IP_PATTERN);
+        }
+        return ipPattern.matcher(hostname).matches();
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/AllocatedBuffer.java b/repackaged/common/src/main/java/com/android/org/conscrypt/AllocatedBuffer.java
new file mode 100644
index 0000000..cc1bc07
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/AllocatedBuffer.java
@@ -0,0 +1,88 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.Preconditions.checkNotNull;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A buffer that was allocated by a {@link BufferAllocator}.
+ * @hide This class is not part of the Android public SDK API
+ */
+@ExperimentalApi
+public abstract class AllocatedBuffer {
+    /**
+     * Returns the {@link ByteBuffer} that backs this buffer.
+     */
+    public abstract ByteBuffer nioBuffer();
+
+    /**
+     * Returns the current instance for backward compatibility.
+     *
+     * @deprecated this method is not used
+     */
+    @Deprecated
+    public AllocatedBuffer retain() {
+        // Do nothing.
+        return this;
+    }
+
+    /**
+     * Returns the delegate {@link ByteBuffer} for reuse or garbage collection.
+     *
+     * @return this {@link AllocatedBuffer} instance
+     */
+    public abstract AllocatedBuffer release();
+
+    /**
+     * Creates a new {@link AllocatedBuffer} that is backed by the given {@link ByteBuffer}.
+     */
+    public static AllocatedBuffer wrap(final ByteBuffer buffer) {
+        checkNotNull(buffer, "buffer");
+
+        return new AllocatedBuffer() {
+
+            @Override
+            public ByteBuffer nioBuffer() {
+                return buffer;
+            }
+
+            @Override
+            public AllocatedBuffer release() {
+                // Do nothing.
+                return this;
+            }
+        };
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ApplicationProtocolSelector.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ApplicationProtocolSelector.java
new file mode 100644
index 0000000..6ac4713
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ApplicationProtocolSelector.java
@@ -0,0 +1,58 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.util.List;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSocket;
+
+/**
+ * Server-side selector for the ALPN protocol. This is a backward-compatibility shim for Java 9's
+ * new {@code setHandshakeApplicationProtocolSelector} API, which takes a {@code BiFunction}
+ * (available in Java 8+). This interface is provided to support protocol selection in Java < 8.
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class ApplicationProtocolSelector {
+    /**
+     * Selects the appropriate ALPN protocol.
+     *
+     * @param engine the server-side engine
+     * @param protocols The list of client-supplied protocols
+     * @return The function's result is an application protocol name, or {@code null} to indicate
+     * that none of the advertised names are acceptable. If the return value is an empty
+     * {@link String} then application protocol indications will not be used. If the return value
+     * is {@code null} (no value chosen) or is a value that was not advertised by the peer, a
+     * "no_application_protocol" alert will be sent to the peer and the connection will be
+     * terminated.
+     */
+    public abstract String selectApplicationProtocol(SSLEngine engine, List<String> protocols);
+
+    /**
+     * Selects the appropriate ALPN protocol.
+     *
+     * @param socket the server-side socket
+     * @param protocols The list of client-supplied protocols
+     * @return The function's result is an application protocol name, or {@code null} to indicate
+     * that none of the advertised names are acceptable. If the return value is an empty
+     * {@link String} then application protocol indications will not be used. If the return value
+     * is {@code null} (no value chosen) or is a value that was not advertised by the peer, a
+     * "no_application_protocol" alert will be sent to the peer and the connection will be
+     * terminated.
+     */
+    public abstract String selectApplicationProtocol(SSLSocket socket, List<String> protocols);
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ApplicationProtocolSelectorAdapter.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ApplicationProtocolSelectorAdapter.java
new file mode 100644
index 0000000..64f5dce
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ApplicationProtocolSelectorAdapter.java
@@ -0,0 +1,89 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.Preconditions.checkNotNull;
+
+import java.util.Arrays;
+import java.util.List;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSocket;
+
+/**
+ * An adapter to bridge between the native code and the {@link ApplicationProtocolSelector} API.
+ */
+final class ApplicationProtocolSelectorAdapter {
+    private static final int NO_PROTOCOL_SELECTED = -1;
+
+    private final SSLEngine engine;
+    private final SSLSocket socket;
+    private final ApplicationProtocolSelector selector;
+
+    ApplicationProtocolSelectorAdapter(SSLEngine engine, ApplicationProtocolSelector selector) {
+        this.engine = checkNotNull(engine, "engine");
+        this.socket = null;
+        this.selector = checkNotNull(selector, "selector");
+    }
+
+    ApplicationProtocolSelectorAdapter(SSLSocket socket, ApplicationProtocolSelector selector) {
+        this.engine = null;
+        this.socket = checkNotNull(socket, "socket");
+        this.selector = checkNotNull(selector, "selector");
+    }
+
+    /**
+     * Performs the ALPN protocol selection from the given list of length-delimited peer protocols.
+     * @param encodedProtocols the peer protocols in length-delimited form.
+     * @return If successful, returns the offset into the {@code lenghPrefixedList} array of the
+     * selected protocol (i.e. points to the length prefix). Otherwise, returns
+     * {@link #NO_PROTOCOL_SELECTED}.
+     */
+    int selectApplicationProtocol(byte[] encodedProtocols) {
+        if (encodedProtocols == null || encodedProtocols.length == 0) {
+            return NO_PROTOCOL_SELECTED;
+        }
+
+        // Decode the protocols.
+        List<String> protocols = Arrays.asList(SSLUtils.decodeProtocols(encodedProtocols));
+
+        // Select the protocol.
+        final String selected;
+        if (engine != null ) {
+            selected = selector.selectApplicationProtocol(engine, protocols);
+        } else {
+            selected = selector.selectApplicationProtocol(socket, protocols);
+        }
+        if (selected == null || selected.isEmpty()) {
+            return NO_PROTOCOL_SELECTED;
+        }
+
+        int offset = 0;
+        for (String protocol : protocols) {
+            if (selected.equals(protocol)) {
+                // Found the selected protocol. Return the index position of the beginning of
+                // the protocol.
+                return offset;
+            }
+
+            // Add 1 byte for the length prefix.
+            offset += 1 + protocol.length();
+        }
+
+        return NO_PROTOCOL_SELECTED;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ArrayUtils.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ArrayUtils.java
new file mode 100644
index 0000000..49a06c0
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ArrayUtils.java
@@ -0,0 +1,36 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+/**
+ * Compatibility utility for Arrays.
+ */
+final class ArrayUtils {
+    private ArrayUtils() {}
+
+    /**
+     * Checks that the range described by {@code offset} and {@code count}
+     * doesn't exceed {@code arrayLength}.
+     */
+    static void checkOffsetAndCount(int arrayLength, int offset, int count) {
+        if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) {
+            throw new ArrayIndexOutOfBoundsException("length=" + arrayLength + "; regionStart="
+                    + offset + "; regionLength=" + count);
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/BufferAllocator.java b/repackaged/common/src/main/java/com/android/org/conscrypt/BufferAllocator.java
new file mode 100644
index 0000000..6dc1e75
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/BufferAllocator.java
@@ -0,0 +1,46 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+import java.nio.ByteBuffer;
+
+/**
+ * An object responsible for allocation of buffers. This is an extension point to enable buffer
+ * pooling within an application.
+ * @hide This class is not part of the Android public SDK API
+ */
+@ExperimentalApi
+public abstract class BufferAllocator {
+    private static final BufferAllocator UNPOOLED = new BufferAllocator() {
+        @Override
+        public AllocatedBuffer allocateDirectBuffer(int capacity) {
+            return AllocatedBuffer.wrap(ByteBuffer.allocateDirect(capacity));
+        }
+    };
+
+    /**
+     * Returns an unpooled buffer allocator, which will create a new buffer for each request.
+     */
+    public static BufferAllocator unpooled() {
+        return UNPOOLED;
+    }
+
+    /**
+     * Allocates a direct (i.e. non-heap) buffer with the given capacity.
+     */
+    public abstract AllocatedBuffer allocateDirectBuffer(int capacity);
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ByteArray.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ByteArray.java
new file mode 100644
index 0000000..c4f7da3
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ByteArray.java
@@ -0,0 +1,47 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.util.Arrays;
+
+/**
+ * Byte array wrapper for hashtable use. Implements equals() and hashCode().
+ */
+final class ByteArray {
+    private final byte[] bytes;
+    private final int hashCode;
+
+    ByteArray(byte[] bytes) {
+        this.bytes = bytes;
+        this.hashCode = Arrays.hashCode(bytes);
+    }
+
+    @Override
+    public int hashCode() {
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof ByteArray)) {
+            return false;
+        }
+        ByteArray lhs = (ByteArray) o;
+        return Arrays.equals(bytes, lhs.bytes);
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/CertBlacklist.java b/repackaged/common/src/main/java/com/android/org/conscrypt/CertBlacklist.java
new file mode 100644
index 0000000..cecc8ba
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/CertBlacklist.java
@@ -0,0 +1,38 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.math.BigInteger;
+import java.security.PublicKey;
+
+/**
+ * A set of certificates that are blacklisted from trust.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface CertBlacklist {
+
+    /**
+     * Returns whether the given public key is in the blacklist.
+     */
+    boolean isPublicKeyBlackListed(PublicKey publicKey);
+
+    /**
+     * Returns whether the given serial number is in the blacklist.
+     */
+    boolean isSerialNumberBlackListed(BigInteger serial);
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/CertPinManager.java b/repackaged/common/src/main/java/com/android/org/conscrypt/CertPinManager.java
new file mode 100644
index 0000000..ff3bda2
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/CertPinManager.java
@@ -0,0 +1,38 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+/**
+ * Interface for classes that implement certificate pinning for use in {@link TrustManagerImpl}.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.CorePlatformApi
+@Internal
+public interface CertPinManager {
+    /**
+     * Given a {@code hostname} and a {@code chain} this verifies that the
+     * certificate chain includes pinned certificates if pinning is requested
+     * for {@code hostname}.
+     */
+    void checkChainPinning(String hostname, List<X509Certificate> chain)
+            throws CertificateException;
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/CertificatePriorityComparator.java b/repackaged/common/src/main/java/com/android/org/conscrypt/CertificatePriorityComparator.java
new file mode 100644
index 0000000..34d6222
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/CertificatePriorityComparator.java
@@ -0,0 +1,170 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * {@link Comparator} for prioritizing certificates in path building.
+ *
+ * <p>
+ * The sort order is as follows:
+ * <ol>
+ * <li>Self-issued certificates first.</li>
+ * <li>Strength of certificates descending (EC before RSA, key size descending, signature
+ * algorithm strength descending).</li>
+ * <li>notAfter date descending.</li>
+ * <li>notBefore date descending.</li>
+ * </ol>
+ * </p>
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public final class CertificatePriorityComparator implements Comparator<X509Certificate> {
+
+    /**
+     * Map of signature algorithm OIDs to priorities. OIDs with a lower priority will be sorted
+     * before those with higher.
+     */
+    private static final Map<String, Integer> ALGORITHM_OID_PRIORITY_MAP;
+
+    /*
+     * Priorities of digest algorithms. Lower is better.
+     */
+    private static final Integer PRIORITY_MD5 = 6;
+    private static final Integer PRIORITY_SHA1 = 5;
+    private static final Integer PRIORITY_SHA224 = 4;
+    private static final Integer PRIORITY_SHA256 = 3;
+    private static final Integer PRIORITY_SHA384 = 2;
+    private static final Integer PRIORITY_SHA512 = 1;
+    private static final Integer PRIORITY_UNKNOWN = -1;
+    static {
+        ALGORITHM_OID_PRIORITY_MAP = new HashMap<String, Integer>();
+        // RSA oids
+        ALGORITHM_OID_PRIORITY_MAP.put("1.2.840.113549.1.1.13", PRIORITY_SHA512);
+        ALGORITHM_OID_PRIORITY_MAP.put("1.2.840.113549.1.1.12", PRIORITY_SHA384);
+        ALGORITHM_OID_PRIORITY_MAP.put("1.2.840.113549.1.1.11", PRIORITY_SHA256);
+        ALGORITHM_OID_PRIORITY_MAP.put("1.2.840.113549.1.1.14", PRIORITY_SHA224);
+        ALGORITHM_OID_PRIORITY_MAP.put("1.2.840.113549.1.1.5", PRIORITY_SHA1);
+        ALGORITHM_OID_PRIORITY_MAP.put("1.2.840.113549.1.1.4", PRIORITY_MD5);
+        // ECDSA oids
+        ALGORITHM_OID_PRIORITY_MAP.put("1.2.840.10045.4.3.4", PRIORITY_SHA512);
+        ALGORITHM_OID_PRIORITY_MAP.put("1.2.840.10045.4.3.3", PRIORITY_SHA384);
+        ALGORITHM_OID_PRIORITY_MAP.put("1.2.840.10045.4.3.2", PRIORITY_SHA256);
+        ALGORITHM_OID_PRIORITY_MAP.put("1.2.840.10045.4.3.1", PRIORITY_SHA224);
+        ALGORITHM_OID_PRIORITY_MAP.put("1.2.840.10045.4.1", PRIORITY_SHA1);
+    }
+
+    @Override
+    public int compare(X509Certificate lhs, X509Certificate rhs) {
+        int result;
+        boolean lhsSelfSigned = lhs.getSubjectDN().equals(lhs.getIssuerDN());
+        boolean rhsSelfSigned = rhs.getSubjectDN().equals(rhs.getIssuerDN());
+        // Self-issued before not self-issued to avoid trying bridge certs first.
+        if (lhsSelfSigned != rhsSelfSigned) {
+            return rhsSelfSigned ? 1 : -1;
+        }
+        // Strength descending.
+        result = compareStrength(rhs, lhs);
+        if (result != 0) {
+            return result;
+        }
+        // notAfter descending.
+        Date lhsNotAfter = lhs.getNotAfter();
+        Date rhsNotAfter = rhs.getNotAfter();
+        result = rhsNotAfter.compareTo(lhsNotAfter);
+        if (result != 0) {
+            return result;
+        }
+        // notBefore descending.
+        Date lhsNotBefore = lhs.getNotBefore();
+        Date rhsNotBefore = rhs.getNotBefore();
+        return rhsNotBefore.compareTo(lhsNotBefore);
+    }
+
+    private int compareStrength(X509Certificate lhs, X509Certificate rhs) {
+        int result;
+        PublicKey lhsPublicKey = lhs.getPublicKey();
+        PublicKey rhsPublicKey = rhs.getPublicKey();
+        result = compareKeyAlgorithm(lhsPublicKey, rhsPublicKey);
+        if (result != 0) {
+            return result;
+        }
+        result = compareKeySize(lhsPublicKey, rhsPublicKey);
+        if (result != 0) {
+            return result;
+        }
+        return compareSignatureAlgorithm(lhs, rhs);
+    }
+
+    private int compareKeyAlgorithm(PublicKey lhs, PublicKey rhs) {
+        String lhsAlgorithm = lhs.getAlgorithm();
+        String rhsAlgorithm = rhs.getAlgorithm();
+
+        if (lhsAlgorithm.equalsIgnoreCase(rhsAlgorithm)) {
+            return 0;
+        }
+
+        // Prefer EC to RSA.
+        if ("EC".equalsIgnoreCase(lhsAlgorithm)) {
+            return 1;
+        } else {
+            return -1;
+        }
+    }
+
+    private int compareKeySize(PublicKey lhs, PublicKey rhs) {
+        String lhsAlgorithm = lhs.getAlgorithm();
+        String rhsAlgorithm = rhs.getAlgorithm();
+        if (!lhsAlgorithm.equalsIgnoreCase(rhsAlgorithm)) {
+            throw new IllegalArgumentException("Keys are not of the same type");
+        }
+        int lhsSize = getKeySize(lhs);
+        int rhsSize = getKeySize(rhs);
+        return lhsSize - rhsSize;
+    }
+
+    private int getKeySize(PublicKey pkey) {
+        if (pkey instanceof ECPublicKey) {
+            return ((ECPublicKey) pkey).getParams().getCurve().getField().getFieldSize();
+        } else if (pkey instanceof RSAPublicKey) {
+            return ((RSAPublicKey) pkey).getModulus().bitLength();
+        } else {
+            throw new IllegalArgumentException(
+                    "Unsupported public key type: " + pkey.getClass().getName());
+        }
+    }
+
+    private int compareSignatureAlgorithm(X509Certificate lhs, X509Certificate rhs) {
+        Integer lhsPriority = ALGORITHM_OID_PRIORITY_MAP.get(lhs.getSigAlgOID());
+        Integer rhsPriority = ALGORITHM_OID_PRIORITY_MAP.get(rhs.getSigAlgOID());
+        if (lhsPriority == null) {
+            lhsPriority = PRIORITY_UNKNOWN;
+        }
+        if (rhsPriority == null) {
+            rhsPriority = PRIORITY_UNKNOWN;
+        }
+        return rhsPriority - lhsPriority;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ChainStrengthAnalyzer.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ChainStrengthAnalyzer.java
new file mode 100644
index 0000000..e34f8c5
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ChainStrengthAnalyzer.java
@@ -0,0 +1,116 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.List;
+
+/**
+ * Analyzes the cryptographic strength of a chain of X.509 certificates.
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public final class ChainStrengthAnalyzer {
+
+    private static final int MIN_RSA_MODULUS_LEN_BITS = 1024;
+
+    private static final int MIN_EC_FIELD_SIZE_BITS = 160;
+
+    private static final int MIN_DSA_P_LEN_BITS = 1024;
+    private static final int MIN_DSA_Q_LEN_BITS = 160;
+
+    private static final String[] SIGNATURE_ALGORITHM_OID_BLACKLIST = {
+        "1.2.840.113549.1.1.2", // md2WithRSAEncryption
+        "1.2.840.113549.1.1.3", // md4WithRSAEncryption
+        "1.2.840.113549.1.1.4", // md5WithRSAEncryption
+        "1.2.840.113549.1.1.5", // sha1WithRSAEncryption
+        "1.2.840.10040.4.3", //dsa-with-sha1
+        "1.2.840.10045.4.1", //ecdsa-with-sha1
+    };
+
+    public static final void check(X509Certificate[] chain) throws CertificateException {
+        for (X509Certificate cert : chain) {
+            try {
+                checkCert(cert);
+            } catch (CertificateException e) {
+                throw new CertificateException("Unacceptable certificate: "
+                        + cert.getSubjectX500Principal(), e);
+            }
+        }
+    }
+
+    public static final void check(List<X509Certificate> chain) throws CertificateException {
+        for (X509Certificate cert : chain) {
+            try {
+                checkCert(cert);
+            } catch (CertificateException e) {
+                throw new CertificateException("Unacceptable certificate: "
+                        + cert.getSubjectX500Principal(), e);
+            }
+        }
+    }
+
+    public static final void checkCert(X509Certificate cert) throws CertificateException {
+        checkKeyLength(cert);
+        checkSignatureAlgorithm(cert);
+    }
+
+    private static void checkKeyLength(X509Certificate cert) throws CertificateException {
+        Object pubkey = cert.getPublicKey();
+        if (pubkey instanceof RSAPublicKey) {
+            int modulusLength = ((RSAPublicKey) pubkey).getModulus().bitLength();
+            if (modulusLength < MIN_RSA_MODULUS_LEN_BITS) {
+                throw new CertificateException(
+                        "RSA modulus is < " + MIN_RSA_MODULUS_LEN_BITS + " bits");
+            }
+        } else if (pubkey instanceof ECPublicKey) {
+            int fieldSizeBits =
+                    ((ECPublicKey) pubkey).getParams().getCurve().getField().getFieldSize();
+            if (fieldSizeBits < MIN_EC_FIELD_SIZE_BITS) {
+                throw new CertificateException(
+                        "EC key field size is < " + MIN_EC_FIELD_SIZE_BITS + " bits");
+            }
+        } else if (pubkey instanceof DSAPublicKey) {
+            int pLength = ((DSAPublicKey) pubkey).getParams().getP().bitLength();
+            int qLength = ((DSAPublicKey) pubkey).getParams().getQ().bitLength();
+            if ((pLength < MIN_DSA_P_LEN_BITS) || (qLength < MIN_DSA_Q_LEN_BITS)) {
+                throw new CertificateException(
+                        "DSA key length is < (" + MIN_DSA_P_LEN_BITS + ", " + MIN_DSA_Q_LEN_BITS
+                        + ") bits");
+            }
+        } else {
+            // Unknown keys will be of type X509PublicKey.
+            throw new CertificateException("Rejecting unknown key class " + pubkey.getClass().getName());
+        }
+    }
+
+    private static void checkSignatureAlgorithm(
+            X509Certificate cert) throws CertificateException {
+        String oid = cert.getSigAlgOID();
+        for (String blacklisted : SIGNATURE_ALGORITHM_OID_BLACKLIST) {
+            if (oid.equals(blacklisted)) {
+                throw new CertificateException("Signature uses an insecure hash function: " + oid);
+            }
+        }
+    }
+}
+
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ClientSessionContext.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ClientSessionContext.java
new file mode 100644
index 0000000..789e716
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ClientSessionContext.java
@@ -0,0 +1,251 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.net.ssl.SSLContext;
+
+/**
+ * Caches client sessions. Indexes by host and port. Users are typically
+ * looking to reuse any session for a given host and port.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.CorePlatformApi
+@Internal
+public final class ClientSessionContext extends AbstractSessionContext {
+    /**
+     * Sessions indexed by host and port. Protect from concurrent
+     * access by holding a lock on sessionsByHostAndPort.
+     *
+     * Invariant: Each list includes either exactly one multi-use session or one
+     * or more single-use sessions.  The types of sessions are never mixed, and adding
+     * a session of one kind will remove all sessions of the other kind.
+     */
+    @SuppressWarnings("serial")
+    private final Map<HostAndPort, List<NativeSslSession>> sessionsByHostAndPort = new HashMap<HostAndPort, List<NativeSslSession>>();
+
+    private SSLClientSessionCache persistentCache;
+
+    ClientSessionContext() {
+        super(10);
+    }
+
+    /**
+     * Applications should not use this method. Instead use {@link
+     * Conscrypt#setClientSessionCache(SSLContext, SSLClientSessionCache)}.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    public void setPersistentCache(SSLClientSessionCache persistentCache) {
+        this.persistentCache = persistentCache;
+    }
+
+    /**
+     * Gets the suitable session reference from the session cache container.
+     */
+    synchronized NativeSslSession getCachedSession(String hostName, int port,
+            SSLParametersImpl sslParameters) {
+        if (hostName == null) {
+            return null;
+        }
+
+        NativeSslSession session = getSession(hostName, port);
+        if (session == null) {
+            return null;
+        }
+
+        String protocol = session.getProtocol();
+        boolean protocolFound = false;
+        for (String enabledProtocol : sslParameters.enabledProtocols) {
+            if (protocol.equals(enabledProtocol)) {
+                protocolFound = true;
+                break;
+            }
+        }
+        if (!protocolFound) {
+            return null;
+        }
+
+        String cipherSuite = session.getCipherSuite();
+        boolean cipherSuiteFound = false;
+        for (String enabledCipherSuite : sslParameters.getEnabledCipherSuites()) {
+            if (cipherSuite.equals(enabledCipherSuite)) {
+                cipherSuiteFound = true;
+                break;
+            }
+        }
+        if (!cipherSuiteFound) {
+            return null;
+        }
+
+        if (session.isSingleUse()) {
+            removeSession(session);
+        }
+        return session;
+    }
+
+    int size() {
+        int size = 0;
+        synchronized (sessionsByHostAndPort) {
+            for (List<NativeSslSession> sessions : sessionsByHostAndPort.values()) {
+                size += sessions.size();
+            }
+        }
+        return size;
+    }
+
+    /**
+     * Finds a cached session for the given host name and port.
+     *
+     * @param host of server
+     * @param port of server
+     * @return cached session or null if none found
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    private NativeSslSession getSession(String host, int port) {
+        if (host == null) {
+            return null;
+        }
+
+        HostAndPort key = new HostAndPort(host, port);
+        NativeSslSession session = null;
+        synchronized (sessionsByHostAndPort) {
+            List<NativeSslSession> sessions = sessionsByHostAndPort.get(key);
+            if (sessions != null && sessions.size() > 0) {
+                session = sessions.get(0);
+            }
+        }
+        if (session != null && session.isValid()) {
+            return session;
+        }
+
+        // Look in persistent cache.  We don't currently delete sessions from the persistent
+        // cache, so we may find a multi-use (aka TLS 1.2) session after having received and
+        // then used up one or more single-use (aka TLS 1.3) sessions.
+        if (persistentCache != null) {
+            byte[] data = persistentCache.getSessionData(host, port);
+            if (data != null) {
+                session = NativeSslSession.newInstance(this, data, host, port);
+                if (session != null && session.isValid()) {
+                    putSession(key, session);
+                    return session;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private void putSession(HostAndPort key, NativeSslSession session) {
+        synchronized (sessionsByHostAndPort) {
+            List<NativeSslSession> sessions = sessionsByHostAndPort.get(key);
+            if (sessions == null) {
+                sessions = new ArrayList<NativeSslSession>();
+                sessionsByHostAndPort.put(key, sessions);
+            }
+            // To maintain the invariant that single- and multi-use sessions aren't
+            // mixed, check what the current list contains and remove those sessions if
+            // they're of the other type.
+            if (sessions.size() > 0 && sessions.get(0).isSingleUse() != session.isSingleUse()) {
+                while (!sessions.isEmpty()) {
+                    removeSession(sessions.get(0));
+                }
+                // The last removeSession() call will have removed the list from
+                // the map, so put it back.
+                sessionsByHostAndPort.put(key, sessions);
+            }
+            sessions.add(session);
+        }
+    }
+
+    private void removeSession(HostAndPort key, NativeSslSession session) {
+        synchronized (sessionsByHostAndPort) {
+            List<NativeSslSession> sessions = sessionsByHostAndPort.get(key);
+            if (sessions != null) {
+                sessions.remove(session);
+                if (sessions.isEmpty()) {
+                    sessionsByHostAndPort.remove(key);
+                }
+            }
+        }
+    }
+
+    @Override
+    void onBeforeAddSession(NativeSslSession session) {
+        String host = session.getPeerHost();
+        int port = session.getPeerPort();
+        if (host == null) {
+            return;
+        }
+
+        HostAndPort key = new HostAndPort(host, port);
+        putSession(key, session);
+
+        // TODO: Do this in a background thread.
+        if (persistentCache != null && !session.isSingleUse()) {
+            byte[] data = session.toBytes();
+            if (data != null) {
+                persistentCache.putSessionData(session.toSSLSession(), data);
+            }
+        }
+    }
+
+    @Override
+    void onBeforeRemoveSession(NativeSslSession session) {
+        String host = session.getPeerHost();
+        if (host == null) {
+            return;
+        }
+        int port = session.getPeerPort();
+        HostAndPort hostAndPortKey = new HostAndPort(host, port);
+        removeSession(hostAndPortKey, session);
+    }
+
+    @Override
+    NativeSslSession getSessionFromPersistentCache(byte[] sessionId) {
+        // Not implemented for clients.
+        return null;
+    }
+
+    private static final class HostAndPort {
+        final String host;
+        final int port;
+
+        HostAndPort(String host, int port) {
+            this.host = host;
+            this.port = port;
+        }
+
+        @Override
+        public int hashCode() {
+            return host.hashCode() * 31 + port;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof HostAndPort)) {
+                return false;
+            }
+            HostAndPort lhs = (HostAndPort) o;
+            return host.equals(lhs.host) && port == lhs.port;
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/Conscrypt.java b/repackaged/common/src/main/java/com/android/org/conscrypt/Conscrypt.java
new file mode 100644
index 0000000..7184dc9
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/Conscrypt.java
@@ -0,0 +1,782 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.security.KeyManagementException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.util.Properties;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLContextSpi;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * Core API for creating and configuring all Conscrypt types.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.CorePlatformApi
+@SuppressWarnings("unused")
+public final class Conscrypt {
+    private Conscrypt() {}
+
+    /**
+     * Returns {@code true} if the Conscrypt native library has been successfully loaded.
+     */
+    public static boolean isAvailable() {
+        try {
+            checkAvailability();
+            return true;
+        } catch (Throwable e) {
+            return false;
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class Version {
+        private final int major;
+        private final int minor;
+        private final int patch;
+
+        private Version(int major, int minor, int patch) {
+            this.major = major;
+            this.minor = minor;
+            this.patch = patch;
+        }
+
+        public int major() { return major; }
+        public int minor() { return minor; }
+        public int patch() { return patch; }
+    }
+
+    private static final Version VERSION;
+
+    static {
+        int major = -1;
+        int minor = -1;
+        int patch = -1;
+        try {
+            InputStream stream = Conscrypt.class.getResourceAsStream("conscrypt.properties");
+            if (stream != null) {
+                Properties props = new Properties();
+                props.load(stream);
+                major = Integer.parseInt(props.getProperty("com.android.org.conscrypt.version.major", "-1"));
+                minor = Integer.parseInt(props.getProperty("com.android.org.conscrypt.version.minor", "-1"));
+                patch = Integer.parseInt(props.getProperty("com.android.org.conscrypt.version.patch", "-1"));
+            }
+        } catch (IOException e) {
+        }
+        if ((major >= 0) && (minor >= 0) && (patch >= 0)) {
+            VERSION = new Version(major, minor, patch);
+        } else {
+            VERSION = null;
+        }
+    }
+
+    /**
+     * Returns the version of this distribution of Conscrypt.  If version information is
+     * unavailable, returns {@code null}.
+     */
+    public static Version version() {
+        return VERSION;
+    }
+
+    /**
+     * Checks that the Conscrypt support is available for the system.
+     *
+     * @throws UnsatisfiedLinkError if unavailable
+     */
+    public static void checkAvailability() {
+        NativeCrypto.checkAvailability();
+    }
+
+    /**
+     * Indicates whether the given {@link Provider} was created by this distribution of Conscrypt.
+     */
+    public static boolean isConscrypt(Provider provider) {
+        return provider instanceof OpenSSLProvider;
+    }
+
+    /**
+     * Constructs a new {@link Provider} with the default name.
+     */
+    public static Provider newProvider() {
+        checkAvailability();
+        return new OpenSSLProvider();
+    }
+
+    /**
+     * Constructs a new {@link Provider} with the given name.
+     *
+     * @deprecated Use {@link #newProviderBuilder()} instead.
+     */
+    @Deprecated
+    public static Provider newProvider(String providerName) {
+        checkAvailability();
+        return new OpenSSLProvider(providerName, Platform.provideTrustManagerByDefault());
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class ProviderBuilder {
+        private String name = Platform.getDefaultProviderName();
+        private boolean provideTrustManager = Platform.provideTrustManagerByDefault();
+
+        private ProviderBuilder() {}
+
+        /**
+         * Sets the name of the Provider to be built.
+         */
+        public ProviderBuilder setName(String name) {
+            this.name = name;
+            return this;
+        }
+
+        /**
+         * Causes the returned provider to provide an implementation of
+         * {@link javax.net.ssl.TrustManagerFactory}.
+         * @deprecated Use provideTrustManager(true)
+         */
+        @Deprecated
+        public ProviderBuilder provideTrustManager() {
+            return provideTrustManager(true);
+        }
+
+        /**
+         * Specifies whether the returned provider will provide an implementation of
+         * {@link javax.net.ssl.TrustManagerFactory}.
+         */
+        public ProviderBuilder provideTrustManager(boolean provide) {
+            this.provideTrustManager = provide;
+            return this;
+        }
+
+        public Provider build() {
+            return new OpenSSLProvider(name, provideTrustManager);
+        }
+    }
+
+    public static ProviderBuilder newProviderBuilder() {
+        return new ProviderBuilder();
+    }
+
+    /**
+     * Returns the maximum length (in bytes) of an encrypted packet.
+     */
+    public static int maxEncryptedPacketLength() {
+        return NativeConstants.SSL3_RT_MAX_PACKET_SIZE;
+    }
+
+    /**
+     * Gets the default X.509 trust manager.
+     */
+    @libcore.api.CorePlatformApi
+    @ExperimentalApi
+    public static X509TrustManager getDefaultX509TrustManager() throws KeyManagementException {
+        checkAvailability();
+        return SSLParametersImpl.getDefaultX509TrustManager();
+    }
+
+    /**
+     * Indicates whether the given {@link SSLContext} was created by this distribution of Conscrypt.
+     */
+    public static boolean isConscrypt(SSLContext context) {
+        return context.getProvider() instanceof OpenSSLProvider;
+    }
+
+    /**
+     * Constructs a new instance of the preferred {@link SSLContextSpi}.
+     */
+    public static SSLContextSpi newPreferredSSLContextSpi() {
+        checkAvailability();
+        return OpenSSLContextImpl.getPreferred();
+    }
+
+    /**
+     * Sets the client-side persistent cache to be used by the context.
+     */
+    public static void setClientSessionCache(SSLContext context, SSLClientSessionCache cache) {
+        SSLSessionContext clientContext = context.getClientSessionContext();
+        if (!(clientContext instanceof ClientSessionContext)) {
+            throw new IllegalArgumentException(
+                    "Not a conscrypt client context: " + clientContext.getClass().getName());
+        }
+        ((ClientSessionContext) clientContext).setPersistentCache(cache);
+    }
+
+    /**
+     * Sets the server-side persistent cache to be used by the context.
+     */
+    public static void setServerSessionCache(SSLContext context, SSLServerSessionCache cache) {
+        SSLSessionContext serverContext = context.getServerSessionContext();
+        if (!(serverContext instanceof ServerSessionContext)) {
+            throw new IllegalArgumentException(
+                    "Not a conscrypt client context: " + serverContext.getClass().getName());
+        }
+        ((ServerSessionContext) serverContext).setPersistentCache(cache);
+    }
+
+    /**
+     * Indicates whether the given {@link SSLSocketFactory} was created by this distribution of
+     * Conscrypt.
+     */
+    public static boolean isConscrypt(SSLSocketFactory factory) {
+        return factory instanceof OpenSSLSocketFactoryImpl;
+    }
+
+    private static OpenSSLSocketFactoryImpl toConscrypt(SSLSocketFactory factory) {
+        if (!isConscrypt(factory)) {
+            throw new IllegalArgumentException(
+                    "Not a conscrypt socket factory: " + factory.getClass().getName());
+        }
+        return (OpenSSLSocketFactoryImpl) factory;
+    }
+
+    /**
+     * Configures the default socket to be created for all socket factory instances.
+     */
+    @ExperimentalApi
+    public static void setUseEngineSocketByDefault(boolean useEngineSocket) {
+        OpenSSLSocketFactoryImpl.setUseEngineSocketByDefault(useEngineSocket);
+        OpenSSLServerSocketFactoryImpl.setUseEngineSocketByDefault(useEngineSocket);
+    }
+
+    /**
+     * Configures the socket to be created for the given socket factory instance.
+     */
+    @ExperimentalApi
+    public static void setUseEngineSocket(SSLSocketFactory factory, boolean useEngineSocket) {
+        toConscrypt(factory).setUseEngineSocket(useEngineSocket);
+    }
+
+    /**
+     * Indicates whether the given {@link SSLServerSocketFactory} was created by this distribution
+     * of Conscrypt.
+     */
+    public static boolean isConscrypt(SSLServerSocketFactory factory) {
+        return factory instanceof OpenSSLServerSocketFactoryImpl;
+    }
+
+    private static OpenSSLServerSocketFactoryImpl toConscrypt(SSLServerSocketFactory factory) {
+        if (!isConscrypt(factory)) {
+            throw new IllegalArgumentException(
+                    "Not a conscrypt server socket factory: " + factory.getClass().getName());
+        }
+        return (OpenSSLServerSocketFactoryImpl) factory;
+    }
+
+    /**
+     * Configures the socket to be created for the given server socket factory instance.
+     */
+    @ExperimentalApi
+    public static void setUseEngineSocket(SSLServerSocketFactory factory, boolean useEngineSocket) {
+        toConscrypt(factory).setUseEngineSocket(useEngineSocket);
+    }
+
+    /**
+     * Indicates whether the given {@link SSLSocket} was created by this distribution of Conscrypt.
+     */
+    public static boolean isConscrypt(SSLSocket socket) {
+        return socket instanceof AbstractConscryptSocket;
+    }
+
+    private static AbstractConscryptSocket toConscrypt(SSLSocket socket) {
+        if (!isConscrypt(socket)) {
+            throw new IllegalArgumentException(
+                    "Not a conscrypt socket: " + socket.getClass().getName());
+        }
+        return (AbstractConscryptSocket) socket;
+    }
+
+    /**
+     * This method enables Server Name Indication (SNI) and overrides the hostname supplied
+     * during socket creation.  If the hostname is not a valid SNI hostname, the SNI extension
+     * will be omitted from the handshake.
+     *
+     * @param socket the socket
+     * @param hostname the desired SNI hostname, or null to disable
+     */
+    public static void setHostname(SSLSocket socket, String hostname) {
+        toConscrypt(socket).setHostname(hostname);
+    }
+
+    /**
+     * Returns either the hostname supplied during socket creation or via
+     * {@link #setHostname(SSLSocket, String)}. No DNS resolution is attempted before
+     * returning the hostname.
+     */
+    public static String getHostname(SSLSocket socket) {
+        return toConscrypt(socket).getHostname();
+    }
+
+    /**
+     * This method attempts to create a textual representation of the peer host or IP. Does
+     * not perform a reverse DNS lookup. This is typically used during session creation.
+     */
+    public static String getHostnameOrIP(SSLSocket socket) {
+        return toConscrypt(socket).getHostnameOrIP();
+    }
+
+    /**
+     * This method enables session ticket support.
+     *
+     * @param socket the socket
+     * @param useSessionTickets True to enable session tickets
+     */
+    public static void setUseSessionTickets(SSLSocket socket, boolean useSessionTickets) {
+        toConscrypt(socket).setUseSessionTickets(useSessionTickets);
+    }
+
+    /**
+     * Enables/disables TLS Channel ID for the given server-side socket.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @param socket the socket
+     * @param enabled Whether to enable channel ID.
+     * @throws IllegalStateException if this is a client socket or if the handshake has already
+     * started.
+     */
+    public static void setChannelIdEnabled(SSLSocket socket, boolean enabled) {
+        toConscrypt(socket).setChannelIdEnabled(enabled);
+    }
+
+    /**
+     * Gets the TLS Channel ID for the given server-side socket. Channel ID is only available
+     * once the handshake completes.
+     *
+     * @param socket the socket
+     * @return channel ID or {@code null} if not available.
+     * @throws IllegalStateException if this is a client socket or if the handshake has not yet
+     * completed.
+     * @throws SSLException if channel ID is available but could not be obtained.
+     */
+    public static byte[] getChannelId(SSLSocket socket) throws SSLException {
+        return toConscrypt(socket).getChannelId();
+    }
+
+    /**
+     * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client socket.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @param socket the socket
+     * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key
+     * (disables TLS Channel ID).
+     * The private key must be an Elliptic Curve (EC) key based on the NIST P-256 curve (aka
+     * SECG secp256r1 or ANSI
+     * X9.62 prime256v1).
+     * @throws IllegalStateException if this is a server socket or if the handshake has already
+     * started.
+     */
+    public static void setChannelIdPrivateKey(SSLSocket socket, PrivateKey privateKey) {
+        toConscrypt(socket).setChannelIdPrivateKey(privateKey);
+    }
+
+    /**
+     * Returns the ALPN protocol agreed upon by client and server.
+     *
+     * @param socket the socket
+     * @return the selected protocol or {@code null} if no protocol was agreed upon.
+     */
+    public static String getApplicationProtocol(SSLSocket socket) {
+        return toConscrypt(socket).getApplicationProtocol();
+    }
+
+    /**
+     * Sets an application-provided ALPN protocol selector. If provided, this will override
+     * the list of protocols set by {@link #setApplicationProtocols(SSLSocket, String[])}.
+     *
+     * @param socket the socket
+     * @param selector the ALPN protocol selector
+     */
+    public static void setApplicationProtocolSelector(SSLSocket socket,
+        ApplicationProtocolSelector selector) {
+        toConscrypt(socket).setApplicationProtocolSelector(selector);
+    }
+
+    /**
+     * Sets the application-layer protocols (ALPN) in prioritization order.
+     *
+     * @param socket the socket being configured
+     * @param protocols the protocols in descending order of preference. If empty, no protocol
+     * indications will be used. This array will be copied.
+     * @throws IllegalArgumentException - if protocols is null, or if any element in a non-empty
+     * array is null or an empty (zero-length) string
+     */
+    public static void setApplicationProtocols(SSLSocket socket, String[] protocols) {
+        toConscrypt(socket).setApplicationProtocols(protocols);
+    }
+
+    /**
+     * Gets the application-layer protocols (ALPN) in prioritization order.
+     *
+     * @param socket the socket
+     * @return the protocols in descending order of preference, or an empty array if protocol
+     * indications are not being used. Always returns a new array.
+     */
+    public static String[] getApplicationProtocols(SSLSocket socket) {
+        return toConscrypt(socket).getApplicationProtocols();
+    }
+
+    /**
+     * Returns the tls-unique channel binding value for this connection, per RFC 5929.  This
+     * will return {@code null} if there is no such value available, such as if the handshake
+     * has not yet completed or this connection is closed.
+     */
+    public static byte[] getTlsUnique(SSLSocket socket) {
+        return toConscrypt(socket).getTlsUnique();
+    }
+
+    /**
+     * Exports a value derived from the TLS master secret as described in RFC 5705.
+     *
+     * @param label the label to use in calculating the exported value.  This must be
+     * an ASCII-only string.
+     * @param context the application-specific context value to use in calculating the
+     * exported value.  This may be {@code null} to use no application context, which is
+     * treated differently than an empty byte array.
+     * @param length the number of bytes of keying material to return.
+     * @return a value of the specified length, or {@code null} if the handshake has not yet
+     * completed or the connection has been closed.
+     * @throws SSLException if the value could not be exported.
+     */
+    public static byte[] exportKeyingMaterial(SSLSocket socket, String label, byte[] context,
+            int length) throws SSLException {
+        return toConscrypt(socket).exportKeyingMaterial(label, context, length);
+    }
+
+    /**
+     * Indicates whether the given {@link SSLEngine} was created by this distribution of Conscrypt.
+     */
+    public static boolean isConscrypt(SSLEngine engine) {
+        return engine instanceof AbstractConscryptEngine;
+    }
+
+    private static AbstractConscryptEngine toConscrypt(SSLEngine engine) {
+        if (!isConscrypt(engine)) {
+            throw new IllegalArgumentException(
+                    "Not a conscrypt engine: " + engine.getClass().getName());
+        }
+        return (AbstractConscryptEngine) engine;
+    }
+
+    /**
+     * Provides the given engine with the provided bufferAllocator.
+     * @throws IllegalArgumentException if the provided engine is not a Conscrypt engine.
+     * @throws IllegalStateException if the provided engine has already begun its handshake.
+     */
+    @ExperimentalApi
+    public static void setBufferAllocator(SSLEngine engine, BufferAllocator bufferAllocator) {
+        toConscrypt(engine).setBufferAllocator(bufferAllocator);
+    }
+
+    /**
+     * Provides the given socket with the provided bufferAllocator.  If the given socket is a
+     * Conscrypt socket but does not use buffer allocators, this method does nothing.
+     * @throws IllegalArgumentException if the provided socket is not a Conscrypt socket.
+     * @throws IllegalStateException if the provided socket has already begun its handshake.
+     */
+    @ExperimentalApi
+    public static void setBufferAllocator(SSLSocket socket, BufferAllocator bufferAllocator) {
+        AbstractConscryptSocket s = toConscrypt(socket);
+        if (s instanceof ConscryptEngineSocket) {
+            ((ConscryptEngineSocket) s).setBufferAllocator(bufferAllocator);
+        }
+    }
+
+    /**
+     * Configures the default {@link BufferAllocator} to be used by all future
+     * {@link SSLEngine} instances from this provider.
+     */
+    @ExperimentalApi
+    public static void setDefaultBufferAllocator(BufferAllocator bufferAllocator) {
+        ConscryptEngine.setDefaultBufferAllocator(bufferAllocator);
+    }
+
+    /**
+     * This method enables Server Name Indication (SNI) and overrides the hostname supplied
+     * during engine creation.
+     *
+     * @param engine the engine
+     * @param hostname the desired SNI hostname, or {@code null} to disable
+     */
+    public static void setHostname(SSLEngine engine, String hostname) {
+        toConscrypt(engine).setHostname(hostname);
+    }
+
+    /**
+     * Returns either the hostname supplied during socket creation or via
+     * {@link #setHostname(SSLEngine, String)}. No DNS resolution is attempted before
+     * returning the hostname.
+     */
+    public static String getHostname(SSLEngine engine) {
+        return toConscrypt(engine).getHostname();
+    }
+
+    /**
+     * Returns the maximum overhead, in bytes, of sealing a record with SSL.
+     */
+    public static int maxSealOverhead(SSLEngine engine) {
+        return toConscrypt(engine).maxSealOverhead();
+    }
+
+    /**
+     * Sets a listener on the given engine for completion of the TLS handshake
+     */
+    public static void setHandshakeListener(SSLEngine engine, HandshakeListener handshakeListener) {
+        toConscrypt(engine).setHandshakeListener(handshakeListener);
+    }
+
+    /**
+     * Enables/disables TLS Channel ID for the given server-side engine.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @param engine the engine
+     * @param enabled Whether to enable channel ID.
+     * @throws IllegalStateException if this is a client engine or if the handshake has already
+     * started.
+     */
+    public static void setChannelIdEnabled(SSLEngine engine, boolean enabled) {
+        toConscrypt(engine).setChannelIdEnabled(enabled);
+    }
+
+    /**
+     * Gets the TLS Channel ID for the given server-side engine. Channel ID is only available
+     * once the handshake completes.
+     *
+     * @param engine the engine
+     * @return channel ID or {@code null} if not available.
+     * @throws IllegalStateException if this is a client engine or if the handshake has not yet
+     * completed.
+     * @throws SSLException if channel ID is available but could not be obtained.
+     */
+    public static byte[] getChannelId(SSLEngine engine) throws SSLException {
+        return toConscrypt(engine).getChannelId();
+    }
+
+    /**
+     * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client engine.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @param engine the engine
+     * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key
+     * (disables TLS Channel ID).
+     * The private key must be an Elliptic Curve (EC) key based on the NIST P-256 curve (aka
+     * SECG secp256r1 or ANSI X9.62 prime256v1).
+     * @throws IllegalStateException if this is a server engine or if the handshake has already
+     * started.
+     */
+    public static void setChannelIdPrivateKey(SSLEngine engine, PrivateKey privateKey) {
+        toConscrypt(engine).setChannelIdPrivateKey(privateKey);
+    }
+
+    /**
+     * Extended unwrap method for multiple source and destination buffers.
+     *
+     * @param engine the target engine for the unwrap
+     * @param srcs the source buffers
+     * @param dsts the destination buffers
+     * @return the result of the unwrap operation
+     * @throws SSLException thrown if an SSL error occurred
+     */
+    public static SSLEngineResult unwrap(SSLEngine engine, final ByteBuffer[] srcs,
+            final ByteBuffer[] dsts) throws SSLException {
+        return toConscrypt(engine).unwrap(srcs, dsts);
+    }
+
+    /**
+     * Exteneded unwrap method for multiple source and destination buffers.
+     *
+     * @param engine the target engine for the unwrap.
+     * @param srcs the source buffers
+     * @param srcsOffset the offset in the {@code srcs} array of the first source buffer
+     * @param srcsLength the number of source buffers starting at {@code srcsOffset}
+     * @param dsts the destination buffers
+     * @param dstsOffset the offset in the {@code dsts} array of the first destination buffer
+     * @param dstsLength the number of destination buffers starting at {@code dstsOffset}
+     * @return the result of the unwrap operation
+     * @throws SSLException thrown if an SSL error occurred
+     */
+    public static SSLEngineResult unwrap(SSLEngine engine, final ByteBuffer[] srcs, int srcsOffset,
+            final int srcsLength, final ByteBuffer[] dsts, final int dstsOffset,
+            final int dstsLength) throws SSLException {
+        return toConscrypt(engine).unwrap(
+                srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
+    }
+
+    /**
+     * This method enables session ticket support.
+     *
+     * @param engine the engine
+     * @param useSessionTickets True to enable session tickets
+     */
+    public static void setUseSessionTickets(SSLEngine engine, boolean useSessionTickets) {
+        toConscrypt(engine).setUseSessionTickets(useSessionTickets);
+    }
+
+    /**
+     * Sets the application-layer protocols (ALPN) in prioritization order.
+     *
+     * @param engine the engine being configured
+     * @param protocols the protocols in descending order of preference. If empty, no protocol
+     * indications will be used.  This array will be copied.
+     * @throws IllegalArgumentException - if protocols is null, or if any element in a non-empty
+     * array is null or an empty (zero-length) string
+     */
+    public static void setApplicationProtocols(SSLEngine engine, String[] protocols) {
+        toConscrypt(engine).setApplicationProtocols(protocols);
+    }
+
+    /**
+     * Gets the application-layer protocols (ALPN) in prioritization order.
+     *
+     * @param engine the engine
+     * @return the protocols in descending order of preference, or an empty array if protocol
+     * indications are not being used. Always returns a new array.
+     */
+    public static String[] getApplicationProtocols(SSLEngine engine) {
+        return toConscrypt(engine).getApplicationProtocols();
+    }
+
+    /**
+     * Sets an application-provided ALPN protocol selector. If provided, this will override
+     * the list of protocols set by {@link #setApplicationProtocols(SSLEngine, String[])}.
+     *
+     * @param engine the engine
+     * @param selector the ALPN protocol selector
+     */
+    public static void setApplicationProtocolSelector(SSLEngine engine,
+        ApplicationProtocolSelector selector) {
+        toConscrypt(engine).setApplicationProtocolSelector(selector);
+    }
+
+    /**
+     * Returns the ALPN protocol agreed upon by client and server.
+     *
+     * @param engine the engine
+     * @return the selected protocol or {@code null} if no protocol was agreed upon.
+     */
+    public static String getApplicationProtocol(SSLEngine engine) {
+        return toConscrypt(engine).getApplicationProtocol();
+    }
+
+    /**
+     * Returns the tls-unique channel binding value for this connection, per RFC 5929.  This
+     * will return {@code null} if there is no such value available, such as if the handshake
+     * has not yet completed or this connection is closed.
+     */
+    public static byte[] getTlsUnique(SSLEngine engine) {
+        return toConscrypt(engine).getTlsUnique();
+    }
+
+    /**
+     * Exports a value derived from the TLS master secret as described in RFC 5705.
+     *
+     * @param label the label to use in calculating the exported value.  This must be
+     * an ASCII-only string.
+     * @param context the application-specific context value to use in calculating the
+     * exported value.  This may be {@code null} to use no application context, which is
+     * treated differently than an empty byte array.
+     * @param length the number of bytes of keying material to return.
+     * @return a value of the specified length, or {@code null} if the handshake has not yet
+     * completed or the connection has been closed.
+     * @throws SSLException if the value could not be exported.
+     */
+    public static byte[] exportKeyingMaterial(SSLEngine engine, String label, byte[] context,
+            int length) throws SSLException {
+        return toConscrypt(engine).exportKeyingMaterial(label, context, length);
+    }
+
+    /**
+     * Indicates whether the given {@link TrustManager} was created by this distribution of
+     * Conscrypt.
+     */
+    public static boolean isConscrypt(TrustManager trustManager) {
+        return trustManager instanceof TrustManagerImpl;
+    }
+
+    private static TrustManagerImpl toConscrypt(TrustManager trustManager) {
+        if (!isConscrypt(trustManager)) {
+            throw new IllegalArgumentException(
+                    "Not a Conscrypt trust manager: " + trustManager.getClass().getName());
+        }
+        return (TrustManagerImpl) trustManager;
+    }
+
+    /**
+     * Set the default hostname verifier that will be used for HTTPS endpoint identification by
+     * Conscrypt trust managers.  If {@code null} (the default), endpoint identification will use
+     * the default hostname verifier set in
+     * {@link HttpsURLConnection#setDefaultHostnameVerifier(javax.net.ssl.HostnameVerifier)}.
+     */
+    public synchronized static void setDefaultHostnameVerifier(ConscryptHostnameVerifier verifier) {
+        TrustManagerImpl.setDefaultHostnameVerifier(verifier);
+    }
+
+    /**
+     * Returns the currently-set default hostname verifier for Conscrypt trust managers.
+     *
+     * @see #setDefaultHostnameVerifier(ConscryptHostnameVerifier)
+     */
+    public synchronized static ConscryptHostnameVerifier getDefaultHostnameVerifier(
+            TrustManager trustManager) {
+        return TrustManagerImpl.getDefaultHostnameVerifier();
+    }
+
+    /**
+     * Set the hostname verifier that will be used for HTTPS endpoint identification by the
+     * given trust manager.  If {@code null} (the default), endpoint identification will use the
+     * default hostname verifier set in {@link
+     * #setDefaultHostnameVerifier(ConscryptHostnameVerifier)}.
+     *
+     * @throws IllegalArgumentException if the provided trust manager is not a Conscrypt trust
+     * manager per {@link #isConscrypt(TrustManager)}
+     */
+    public static void setHostnameVerifier(
+            TrustManager trustManager, ConscryptHostnameVerifier verifier) {
+        toConscrypt(trustManager).setHostnameVerifier(verifier);
+    }
+
+    /**
+     * Returns the currently-set hostname verifier for the given trust manager.
+     *
+     * @throws IllegalArgumentException if the provided trust manager is not a Conscrypt trust
+     * manager per {@link #isConscrypt(TrustManager)}
+     *
+     * @see #setHostnameVerifier(TrustManager, ConscryptHostnameVerifier)
+     */
+    public static ConscryptHostnameVerifier getHostnameVerifier(TrustManager trustManager) {
+        return toConscrypt(trustManager).getHostnameVerifier();
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptCertStore.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptCertStore.java
new file mode 100644
index 0000000..472e2da
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptCertStore.java
@@ -0,0 +1,44 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.cert.X509Certificate;
+import java.util.Set;
+
+/**
+ * A certificate store that supports additional operations that are used in
+ * TrustManagerImpl.  This is primarily implemented by the cert store on the
+ * Android platform.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.CorePlatformApi
+@Internal
+public interface ConscryptCertStore {
+
+    /**
+     * Returns a stored CA certificate with the same name and public key as the
+     * provided {@link X509Certificate}.
+     */
+    X509Certificate getTrustAnchor(X509Certificate c);
+
+    /**
+     * Returns all CA certificates with the public key that was used to sign the
+     * provided {@link X509Certificate}.
+     */
+    Set<X509Certificate> findAllIssuers(X509Certificate c);
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptEngine.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptEngine.java
new file mode 100644
index 0000000..646be02
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptEngine.java
@@ -0,0 +1,1824 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+import static javax.net.ssl.SSLEngineResult.HandshakeStatus.FINISHED;
+import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
+import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NEED_WRAP;
+import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
+import static javax.net.ssl.SSLEngineResult.Status.BUFFER_OVERFLOW;
+import static javax.net.ssl.SSLEngineResult.Status.BUFFER_UNDERFLOW;
+import static javax.net.ssl.SSLEngineResult.Status.CLOSED;
+import static javax.net.ssl.SSLEngineResult.Status.OK;
+import static com.android.org.conscrypt.NativeConstants.SSL3_RT_HEADER_LENGTH;
+import static com.android.org.conscrypt.NativeConstants.SSL3_RT_MAX_PACKET_SIZE;
+import static com.android.org.conscrypt.NativeConstants.SSL3_RT_MAX_PLAIN_LENGTH;
+import static com.android.org.conscrypt.NativeConstants.SSL_CB_HANDSHAKE_DONE;
+import static com.android.org.conscrypt.NativeConstants.SSL_CB_HANDSHAKE_START;
+import static com.android.org.conscrypt.NativeConstants.SSL_ERROR_WANT_READ;
+import static com.android.org.conscrypt.NativeConstants.SSL_ERROR_WANT_WRITE;
+import static com.android.org.conscrypt.NativeConstants.SSL_ERROR_ZERO_RETURN;
+import static com.android.org.conscrypt.Preconditions.checkArgument;
+import static com.android.org.conscrypt.Preconditions.checkNotNull;
+import static com.android.org.conscrypt.Preconditions.checkPositionIndexes;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_CLOSED;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_CLOSED_INBOUND;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_CLOSED_OUTBOUND;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_HANDSHAKE_COMPLETED;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_HANDSHAKE_STARTED;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_MODE_SET;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_NEW;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_READY;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_READY_HANDSHAKE_CUT_THROUGH;
+import static com.android.org.conscrypt.SSLUtils.calculateOutNetBufSize;
+import static com.android.org.conscrypt.SSLUtils.toSSLHandshakeException;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.nio.ByteBuffer;
+import java.nio.ReadOnlyBufferException;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.ECKey;
+import java.security.spec.ECParameterSpec;
+import javax.crypto.SecretKey;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
+import com.android.org.conscrypt.ExternalSession.Provider;
+import com.android.org.conscrypt.NativeRef.SSL_SESSION;
+import com.android.org.conscrypt.NativeSsl.BioWrapper;
+
+/**
+ * Implements the {@link SSLEngine} API using OpenSSL's non-blocking interfaces.
+ */
+final class ConscryptEngine extends AbstractConscryptEngine implements NativeCrypto.SSLHandshakeCallbacks,
+                                                         SSLParametersImpl.AliasChooser,
+                                                         SSLParametersImpl.PSKCallbacks {
+    private static final SSLEngineResult NEED_UNWRAP_OK =
+            new SSLEngineResult(OK, NEED_UNWRAP, 0, 0);
+    private static final SSLEngineResult NEED_UNWRAP_CLOSED =
+            new SSLEngineResult(CLOSED, NEED_UNWRAP, 0, 0);
+    private static final SSLEngineResult NEED_WRAP_OK = new SSLEngineResult(OK, NEED_WRAP, 0, 0);
+    private static final SSLEngineResult NEED_WRAP_CLOSED =
+            new SSLEngineResult(CLOSED, NEED_WRAP, 0, 0);
+    private static final SSLEngineResult CLOSED_NOT_HANDSHAKING =
+            new SSLEngineResult(CLOSED, NOT_HANDSHAKING, 0, 0);
+
+    private static BufferAllocator defaultBufferAllocator = null;
+
+    private final SSLParametersImpl sslParameters;
+    private BufferAllocator bufferAllocator = defaultBufferAllocator;
+
+    /**
+     * A lazy-created direct buffer used as a bridge between heap buffers provided by the
+     * application and JNI. This avoids the overhead of calling JNI with heap buffers.
+     * Used only when no {@link #bufferAllocator} has been provided.
+     */
+    private ByteBuffer lazyDirectBuffer;
+
+    /**
+     * Hostname used with the TLS extension SNI hostname.
+     */
+    private String peerHostname;
+
+    // @GuardedBy("ssl");
+    private int state = STATE_NEW;
+    private boolean handshakeFinished;
+
+    /**
+     * Wrapper around the underlying SSL object.
+     */
+    private final NativeSsl ssl;
+
+    /**
+     * The BIO used for reading/writing encrypted bytes.
+     */
+    // @GuardedBy("ssl");
+    private final BioWrapper networkBio;
+
+    /**
+     * Set during startHandshake.
+     */
+    private ActiveSession activeSession;
+
+    /**
+     * A snapshot of the active session when the engine was closed.
+     */
+    private SessionSnapshot closedSession;
+
+    /**
+     * The session object exposed externally from this class.
+     */
+    private final SSLSession externalSession =
+        Platform.wrapSSLSession(new ExternalSession(new Provider() {
+            @Override
+            public ConscryptSession provideSession() {
+                return ConscryptEngine.this.provideSession();
+            }
+        }));
+
+    /**
+     * Private key for the TLS Channel ID extension. This field is client-side only. Set during
+     * startHandshake.
+     */
+    private OpenSSLKey channelIdPrivateKey;
+
+    private int maxSealOverhead;
+
+    private HandshakeListener handshakeListener;
+
+    private final ByteBuffer[] singleSrcBuffer = new ByteBuffer[1];
+    private final ByteBuffer[] singleDstBuffer = new ByteBuffer[1];
+    private final PeerInfoProvider peerInfoProvider;
+
+    ConscryptEngine(SSLParametersImpl sslParameters) {
+        this.sslParameters = sslParameters;
+        peerInfoProvider = PeerInfoProvider.nullProvider();
+        this.ssl = newSsl(sslParameters, this);
+        this.networkBio = ssl.newBio();
+    }
+
+    ConscryptEngine(String host, int port, SSLParametersImpl sslParameters) {
+        this.sslParameters = sslParameters;
+        this.peerInfoProvider = PeerInfoProvider.forHostAndPort(host, port);
+        this.ssl = newSsl(sslParameters, this);
+        this.networkBio = ssl.newBio();
+    }
+
+    ConscryptEngine(SSLParametersImpl sslParameters, PeerInfoProvider peerInfoProvider) {
+        this.sslParameters = sslParameters;
+        this.peerInfoProvider = checkNotNull(peerInfoProvider, "peerInfoProvider");
+        this.ssl = newSsl(sslParameters, this);
+        this.networkBio = ssl.newBio();
+    }
+
+    private static NativeSsl newSsl(SSLParametersImpl sslParameters, ConscryptEngine engine) {
+        try {
+            return NativeSsl.newInstance(sslParameters, engine, engine, engine);
+        } catch (SSLException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Configures the default {@link BufferAllocator} to be used by all future
+     * {@link SSLEngine} and {@link ConscryptEngineSocket} instances from this provider.
+     */
+    static void setDefaultBufferAllocator(BufferAllocator bufferAllocator) {
+        defaultBufferAllocator = bufferAllocator;
+    }
+
+    /**
+     * Returns the default {@link BufferAllocator}, which may be {@code null} if no default
+     * has been explicitly set.
+     */
+    static BufferAllocator getDefaultBufferAllocator() {
+        return defaultBufferAllocator;
+    }
+
+    @Override
+    void setBufferAllocator(BufferAllocator bufferAllocator) {
+        synchronized (ssl) {
+            if (isHandshakeStarted()) {
+                throw new IllegalStateException(
+                        "Could not set buffer allocator after the initial handshake has begun.");
+            }
+            this.bufferAllocator = bufferAllocator;
+        }
+    }
+
+    /**
+     * Returns the maximum overhead, in bytes, of sealing a record with SSL.
+     */
+    @Override
+    int maxSealOverhead() {
+        return maxSealOverhead;
+    }
+
+    /**
+     * Enables/disables TLS Channel ID for this server engine.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @throws IllegalStateException if this is a client engine or if the handshake has already
+     *         started.
+     */
+    @Override
+    void setChannelIdEnabled(boolean enabled) {
+        synchronized (ssl) {
+            if (getUseClientMode()) {
+                throw new IllegalStateException("Not allowed in client mode");
+            }
+            if (isHandshakeStarted()) {
+                throw new IllegalStateException(
+                        "Could not enable/disable Channel ID after the initial handshake has begun.");
+            }
+            sslParameters.channelIdEnabled = enabled;
+        }
+    }
+
+    /**
+     * Gets the TLS Channel ID for this server engine. Channel ID is only available once the
+     * handshake completes.
+     *
+     * @return channel ID or {@code null} if not available.
+     *
+     * @throws IllegalStateException if this is a client engine or if the handshake has not yet
+     * completed.
+     * @throws SSLException if channel ID is available but could not be obtained.
+     */
+    @Override
+    byte[] getChannelId() throws SSLException {
+        synchronized (ssl) {
+            if (getUseClientMode()) {
+                throw new IllegalStateException("Not allowed in client mode");
+            }
+
+            if (isHandshakeStarted()) {
+                throw new IllegalStateException(
+                        "Channel ID is only available after handshake completes");
+            }
+            return ssl.getTlsChannelId();
+        }
+    }
+
+    /**
+     * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client engine.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key (disables
+     *        TLS Channel ID). The private key must be an Elliptic Curve (EC) key based on the NIST
+     *        P-256 curve (aka SECG secp256r1 or ANSI X9.62 prime256v1).
+     *
+     * @throws IllegalStateException if this is a server engine or if the handshake has already
+     *         started.
+     */
+    @Override
+    void setChannelIdPrivateKey(PrivateKey privateKey) {
+        if (!getUseClientMode()) {
+            throw new IllegalStateException("Not allowed in server mode");
+        }
+
+        synchronized (ssl) {
+            if (isHandshakeStarted()) {
+                throw new IllegalStateException("Could not change Channel ID private key "
+                        + "after the initial handshake has begun.");
+            }
+
+            if (privateKey == null) {
+                sslParameters.channelIdEnabled = false;
+                channelIdPrivateKey = null;
+                return;
+            }
+
+            sslParameters.channelIdEnabled = true;
+            try {
+                ECParameterSpec ecParams = null;
+                if (privateKey instanceof ECKey) {
+                    ecParams = ((ECKey) privateKey).getParams();
+                }
+                if (ecParams == null) {
+                    // Assume this is a P-256 key, as specified in the contract of this method.
+                    ecParams =
+                            OpenSSLECGroupContext.getCurveByName("prime256v1").getECParameterSpec();
+                }
+                channelIdPrivateKey =
+                        OpenSSLKey.fromECPrivateKeyForTLSStackOnly(privateKey, ecParams);
+            } catch (InvalidKeyException e) {
+                // Will have error in startHandshake
+            }
+        }
+    }
+
+    /**
+     * Sets the listener for the completion of the TLS handshake.
+     */
+    @Override
+    void setHandshakeListener(HandshakeListener handshakeListener) {
+        synchronized (ssl) {
+            if (isHandshakeStarted()) {
+                throw new IllegalStateException(
+                        "Handshake listener must be set before starting the handshake.");
+            }
+            this.handshakeListener = handshakeListener;
+        }
+    }
+
+    private boolean isHandshakeStarted() {
+        switch (state) {
+            case STATE_NEW:
+            case STATE_MODE_SET:
+                return false;
+            default:
+                return true;
+        }
+    }
+
+    /**
+     * This method enables Server Name Indication (SNI) and overrides the {@link PeerInfoProvider}
+     * supplied during engine creation.  If the hostname is not a valid SNI hostname, the SNI
+     * extension will be omitted from the handshake.
+     */
+    @Override
+    void setHostname(String hostname) {
+        sslParameters.setUseSni(hostname != null);
+        this.peerHostname = hostname;
+    }
+
+    /**
+     * Returns the hostname from {@link #setHostname(String)} or supplied by the
+     * {@link PeerInfoProvider} upon creation. No DNS resolution is attempted before
+     * returning the hostname.
+     */
+    @Override
+    String getHostname() {
+        return peerHostname != null ? peerHostname : peerInfoProvider.getHostname();
+    }
+
+    @Override
+    public String getPeerHost() {
+        return peerHostname != null ? peerHostname : peerInfoProvider.getHostnameOrIP();
+    }
+
+    @Override
+    public int getPeerPort() {
+        return peerInfoProvider.getPort();
+    }
+
+    @Override
+    public void beginHandshake() throws SSLException {
+        synchronized (ssl) {
+            beginHandshakeInternal();
+        }
+    }
+
+    private void beginHandshakeInternal() throws SSLException {
+        switch (state) {
+            case STATE_NEW: {
+                throw new IllegalStateException("Client/server mode must be set before handshake");
+            }
+            case STATE_MODE_SET: {
+                // We know what mode to handshake in but have not started the handshake, proceed
+                break;
+            }
+            case STATE_CLOSED_INBOUND:
+            case STATE_CLOSED_OUTBOUND:
+            case STATE_CLOSED:
+                throw new IllegalStateException("Engine has already been closed");
+            default:
+                // We've already started the handshake, just return
+                return;
+        }
+
+        transitionTo(STATE_HANDSHAKE_STARTED);
+
+        boolean releaseResources = true;
+        try {
+            // Prepare the SSL object for the handshake.
+            ssl.initialize(getHostname(), channelIdPrivateKey);
+
+            // For clients, offer to resume a previously cached session to avoid the
+            // full TLS handshake.
+            if (getUseClientMode()) {
+                NativeSslSession cachedSession = clientSessionContext().getCachedSession(
+                        getHostname(), getPeerPort(), sslParameters);
+                if (cachedSession != null) {
+                    cachedSession.offerToResume(ssl);
+                }
+            }
+
+            maxSealOverhead = ssl.getMaxSealOverhead();
+            handshake();
+            releaseResources = false;
+        } catch (IOException e) {
+            // Write CCS errors to EventLog
+            String message = e.getMessage();
+            // Must match error reason string of SSL_R_UNEXPECTED_CCS (in ssl/ssl_err.c)
+            if (message.contains("unexpected CCS")) {
+                String logMessage = String.format("ssl_unexpected_ccs: host=%s", getPeerHost());
+                Platform.logEvent(logMessage);
+            }
+            throw SSLUtils.toSSLHandshakeException(e);
+        } finally {
+            if (releaseResources) {
+                closeAndFreeResources();
+            }
+        }
+    }
+
+    @Override
+    public void closeInbound() {
+        synchronized (ssl) {
+            if (state == STATE_CLOSED || state == STATE_CLOSED_INBOUND) {
+                return;
+            }
+            if (isHandshakeStarted()) {
+                if (state == STATE_CLOSED_OUTBOUND) {
+                    transitionTo(STATE_CLOSED);
+                } else {
+                    transitionTo(STATE_CLOSED_INBOUND);
+                }
+                freeIfDone();
+            } else {
+                // Never started the handshake. Just close now.
+                closeAndFreeResources();
+            }
+        }
+    }
+
+    @Override
+    public void closeOutbound() {
+        synchronized (ssl) {
+            if (state == STATE_CLOSED || state == STATE_CLOSED_OUTBOUND) {
+                return;
+            }
+            if (isHandshakeStarted()) {
+                if (state == STATE_CLOSED_INBOUND) {
+                    transitionTo(STATE_CLOSED);
+                } else {
+                    transitionTo(STATE_CLOSED_OUTBOUND);
+                }
+                sendSSLShutdown();
+                freeIfDone();
+            } else {
+                // Never started the handshake. Just close now.
+                closeAndFreeResources();
+            }
+        }
+    }
+
+    @Override
+    public Runnable getDelegatedTask() {
+        // This implementation doesn't use any delegated tasks.
+        return null;
+    }
+
+    @Override
+    public String[] getEnabledCipherSuites() {
+        return sslParameters.getEnabledCipherSuites();
+    }
+
+    @Override
+    public String[] getEnabledProtocols() {
+        return sslParameters.getEnabledProtocols();
+    }
+
+    @Override
+    public boolean getEnableSessionCreation() {
+        return sslParameters.getEnableSessionCreation();
+    }
+
+    @Override
+    public SSLParameters getSSLParameters() {
+        SSLParameters params = super.getSSLParameters();
+        Platform.getSSLParameters(params, sslParameters, this);
+        return params;
+    }
+
+    @Override
+    public void setSSLParameters(SSLParameters p) {
+        super.setSSLParameters(p);
+        Platform.setSSLParameters(p, sslParameters, this);
+    }
+
+    @Override
+    public HandshakeStatus getHandshakeStatus() {
+        synchronized (ssl) {
+            return getHandshakeStatusInternal();
+        }
+    }
+
+    private HandshakeStatus getHandshakeStatusInternal() {
+        if (handshakeFinished) {
+            return HandshakeStatus.NOT_HANDSHAKING;
+        }
+        switch (state) {
+            case STATE_HANDSHAKE_STARTED:
+                return pendingStatus(pendingOutboundEncryptedBytes());
+            case STATE_HANDSHAKE_COMPLETED:
+                return HandshakeStatus.NEED_WRAP;
+            case STATE_NEW:
+            case STATE_MODE_SET:
+            case STATE_CLOSED:
+            case STATE_CLOSED_INBOUND:
+            case STATE_CLOSED_OUTBOUND:
+            case STATE_READY:
+            case STATE_READY_HANDSHAKE_CUT_THROUGH:
+                return HandshakeStatus.NOT_HANDSHAKING;
+            default:
+                break;
+        }
+        throw new IllegalStateException("Unexpected engine state: " + state);
+    }
+
+    int pendingOutboundEncryptedBytes() {
+        return networkBio.getPendingWrittenBytes();
+    }
+
+    private int pendingInboundCleartextBytes() {
+        return ssl.getPendingReadableBytes();
+    }
+
+    private static SSLEngineResult.HandshakeStatus pendingStatus(int pendingOutboundBytes) {
+        // Depending on if there is something left in the BIO we need to WRAP or UNWRAP
+        return pendingOutboundBytes > 0 ? NEED_WRAP : NEED_UNWRAP;
+    }
+
+    @Override
+    public boolean getNeedClientAuth() {
+        return sslParameters.getNeedClientAuth();
+    }
+
+    /**
+     * Work-around to allow this method to be called on older versions of Android.
+     */
+    @Override
+    SSLSession handshakeSession() {
+        synchronized (ssl) {
+            if (state == STATE_HANDSHAKE_STARTED) {
+                return Platform.wrapSSLSession(new ExternalSession(new Provider() {
+                    @Override
+                    public ConscryptSession provideSession() {
+                        return ConscryptEngine.this.provideHandshakeSession();
+                    }
+                }));
+            }
+            return null;
+        }
+    }
+
+    @Override
+    public SSLSession getSession() {
+        return externalSession;
+    }
+
+    private ConscryptSession provideSession() {
+        synchronized (ssl) {
+            if (state == STATE_CLOSED) {
+                return closedSession != null ? closedSession : SSLNullSession.getNullSession();
+            }
+            if (state < STATE_HANDSHAKE_COMPLETED) {
+                // Return an invalid session with invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
+                return SSLNullSession.getNullSession();
+            }
+            return activeSession;
+        }
+    }
+
+    private ConscryptSession provideHandshakeSession() {
+        synchronized (ssl) {
+            return state == STATE_HANDSHAKE_STARTED ? activeSession
+                : SSLNullSession.getNullSession();
+        }
+    }
+
+    @Override
+    public String[] getSupportedCipherSuites() {
+        return NativeCrypto.getSupportedCipherSuites();
+    }
+
+    @Override
+    public String[] getSupportedProtocols() {
+        return NativeCrypto.getSupportedProtocols();
+    }
+
+    @Override
+    public boolean getUseClientMode() {
+        return sslParameters.getUseClientMode();
+    }
+
+    @Override
+    public boolean getWantClientAuth() {
+        return sslParameters.getWantClientAuth();
+    }
+
+    @Override
+    public boolean isInboundDone() {
+        synchronized (ssl) {
+            return (state == STATE_CLOSED || state == STATE_CLOSED_INBOUND
+                           || ssl.wasShutdownReceived())
+                    && (pendingInboundCleartextBytes() == 0);
+        }
+    }
+
+    @Override
+    public boolean isOutboundDone() {
+        synchronized (ssl) {
+            return (state == STATE_CLOSED || state == STATE_CLOSED_OUTBOUND
+                           || ssl.wasShutdownSent())
+                    && (pendingOutboundEncryptedBytes() == 0);
+        }
+    }
+
+    @Override
+    public void setEnabledCipherSuites(String[] suites) {
+        sslParameters.setEnabledCipherSuites(suites);
+    }
+
+    @Override
+    public void setEnabledProtocols(String[] protocols) {
+        sslParameters.setEnabledProtocols(protocols);
+    }
+
+    @Override
+    public void setEnableSessionCreation(boolean flag) {
+        sslParameters.setEnableSessionCreation(flag);
+    }
+
+    @Override
+    public void setNeedClientAuth(boolean need) {
+        sslParameters.setNeedClientAuth(need);
+    }
+
+    @Override
+    public void setUseClientMode(boolean mode) {
+        synchronized (ssl) {
+            if (isHandshakeStarted()) {
+                throw new IllegalArgumentException(
+                        "Can not change mode after handshake: state == " + state);
+            }
+            transitionTo(STATE_MODE_SET);
+            sslParameters.setUseClientMode(mode);
+        }
+    }
+
+    @Override
+    public void setWantClientAuth(boolean want) {
+        sslParameters.setWantClientAuth(want);
+    }
+
+    @Override
+    public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer dst) throws SSLException {
+        synchronized (ssl) {
+            try {
+                return unwrap(singleSrcBuffer(src), singleDstBuffer(dst));
+            } finally {
+                resetSingleSrcBuffer();
+                resetSingleDstBuffer();
+            }
+        }
+    }
+
+    @Override
+    public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts) throws SSLException {
+        synchronized (ssl) {
+            try {
+                return unwrap(singleSrcBuffer(src), dsts);
+            } finally {
+                resetSingleSrcBuffer();
+            }
+        }
+    }
+
+    @Override
+    public SSLEngineResult unwrap(final ByteBuffer src, final ByteBuffer[] dsts, final int offset,
+            final int length) throws SSLException {
+        synchronized (ssl) {
+            try {
+                return unwrap(singleSrcBuffer(src), 0, 1, dsts, offset, length);
+            } finally {
+                resetSingleSrcBuffer();
+            }
+        }
+    }
+
+    @Override
+    SSLEngineResult unwrap(final ByteBuffer[] srcs, final ByteBuffer[] dsts) throws SSLException {
+        checkArgument(srcs != null, "srcs is null");
+        checkArgument(dsts != null, "dsts is null");
+        return unwrap(srcs, 0, srcs.length, dsts, 0, dsts.length);
+    }
+
+    @Override
+    SSLEngineResult unwrap(final ByteBuffer[] srcs, int srcsOffset, final int srcsLength,
+            final ByteBuffer[] dsts, final int dstsOffset, final int dstsLength)
+            throws SSLException {
+        checkArgument(srcs != null, "srcs is null");
+        checkArgument(dsts != null, "dsts is null");
+        checkPositionIndexes(srcsOffset, srcsOffset + srcsLength, srcs.length);
+        checkPositionIndexes(dstsOffset, dstsOffset + dstsLength, dsts.length);
+
+        // Determine the output capacity.
+        final int dstLength = calcDstsLength(dsts, dstsOffset, dstsLength);
+        final int endOffset = dstsOffset + dstsLength;
+
+        final int srcsEndOffset = srcsOffset + srcsLength;
+        final long srcLength = calcSrcsLength(srcs, srcsOffset, srcsEndOffset);
+
+        synchronized (ssl) {
+            switch (state) {
+                case STATE_MODE_SET:
+                    // Begin the handshake implicitly.
+                    beginHandshakeInternal();
+                    break;
+                case STATE_CLOSED_INBOUND:
+                case STATE_CLOSED:
+                    freeIfDone();
+                    // If the inbound direction is closed. we can't send anymore.
+                    return new SSLEngineResult(Status.CLOSED, getHandshakeStatusInternal(), 0, 0);
+                case STATE_NEW:
+                    throw new IllegalStateException(
+                            "Client/server mode must be set before calling unwrap");
+                default:
+                    break;
+            }
+
+            HandshakeStatus handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
+            if (!handshakeFinished) {
+                handshakeStatus = handshake();
+                if (handshakeStatus == NEED_WRAP) {
+                    return NEED_WRAP_OK;
+                }
+                if (state == STATE_CLOSED) {
+                    return NEED_WRAP_CLOSED;
+                }
+                // NEED_UNWRAP - just fall through to perform the unwrap.
+            }
+
+            // Consume any source data. Skip this if there are unread cleartext data.
+            boolean noCleartextDataAvailable = pendingInboundCleartextBytes() <= 0;
+            int lenRemaining = 0;
+            if (srcLength > 0 && noCleartextDataAvailable) {
+                if (srcLength < SSL3_RT_HEADER_LENGTH) {
+                    // Need to be able to read a full TLS header.
+                    return new SSLEngineResult(BUFFER_UNDERFLOW, getHandshakeStatus(), 0, 0);
+                }
+
+                int packetLength = SSLUtils.getEncryptedPacketLength(srcs, srcsOffset);
+                if (packetLength < 0) {
+                    throw new SSLException("Unable to parse TLS packet header");
+                }
+
+                if (srcLength < packetLength) {
+                    // We either have not enough data to read the packet header or not enough for
+                    // reading the whole packet.
+                    return new SSLEngineResult(BUFFER_UNDERFLOW, getHandshakeStatus(), 0, 0);
+                }
+
+                // Limit the amount of data to be read to a single packet.
+                lenRemaining = packetLength;
+            } else if (noCleartextDataAvailable) {
+                // No pending data and nothing provided as input.  Need more data.
+                return new SSLEngineResult(BUFFER_UNDERFLOW, getHandshakeStatus(), 0, 0);
+            }
+
+            // Write all of the encrypted source data to the networkBio
+            int bytesConsumed = 0;
+            if (lenRemaining > 0 && srcsOffset < srcsEndOffset) {
+                do {
+                    ByteBuffer src = srcs[srcsOffset];
+                    int remaining = src.remaining();
+                    if (remaining == 0) {
+                        // We must skip empty buffers as BIO_write will return 0 if asked to
+                        // write something with length 0.
+                        srcsOffset++;
+                        continue;
+                    }
+                    // Write the source encrypted data to the networkBio.
+                    int written = writeEncryptedData(src, min(lenRemaining, remaining));
+                    if (written > 0) {
+                        bytesConsumed += written;
+                        lenRemaining -= written;
+                        if (lenRemaining == 0) {
+                            // A whole packet has been consumed.
+                            break;
+                        }
+
+                        if (written == remaining) {
+                            srcsOffset++;
+                        } else {
+                            // We were not able to write everything into the BIO so break the
+                            // write loop as otherwise we will produce an error on the next
+                            // write attempt, which will trigger a SSL.clearError() later.
+                            break;
+                        }
+                    } else {
+                        // BIO_write returned a negative or zero number, this means we could not
+                        // complete the write operation and should retry later.
+                        // We ignore BIO_* errors here as we use in memory BIO anyway and will
+                        // do another SSL_* call later on in which we will produce an exception
+                        // in case of an error
+                        NativeCrypto.SSL_clear_error();
+                        break;
+                    }
+                } while (srcsOffset < srcsEndOffset);
+            }
+
+            // Now read any available plaintext data.
+            int bytesProduced = 0;
+            try {
+                if (dstLength > 0) {
+                    // Write decrypted data to dsts buffers
+                    for (int idx = dstsOffset; idx < endOffset; ++idx) {
+                        ByteBuffer dst = dsts[idx];
+                        if (!dst.hasRemaining()) {
+                            continue;
+                        }
+
+                        int bytesRead = readPlaintextData(dst);
+                        if (bytesRead > 0) {
+                            bytesProduced += bytesRead;
+                            if (dst.hasRemaining()) {
+                                // We haven't filled this buffer fully, break out of the loop
+                                // and determine the correct response status below.
+                                break;
+                            }
+                        } else {
+                            switch (bytesRead) {
+                                case -SSL_ERROR_WANT_READ:
+                                case -SSL_ERROR_WANT_WRITE: {
+                                    return newResult(bytesConsumed, bytesProduced, handshakeStatus);
+                                }
+                                case -SSL_ERROR_ZERO_RETURN: {
+                                    // We received a close_notify from the peer, so mark the
+                                    // inbound direction as closed and shut down the SSL object
+                                    closeInbound();
+                                    sendSSLShutdown();
+                                    return new SSLEngineResult(Status.CLOSED,
+                                            pendingOutboundEncryptedBytes() > 0
+                                                    ? NEED_WRAP : NOT_HANDSHAKING,
+                                            bytesConsumed, bytesProduced);
+                                }
+                                default: {
+                                    // Should never get here.
+                                    sendSSLShutdown();
+                                    throw newSslExceptionWithMessage("SSL_read");
+                                }
+                            }
+                        }
+                    }
+                } else {
+                    // If the capacity of all destination buffers is 0 we need to trigger a SSL_read
+                    // anyway to ensure everything is flushed in the BIO pair and so we can detect
+                    // it in the pendingInboundCleartextBytes() call.
+                    ssl.forceRead();
+                }
+            } catch (SSLException e) {
+                // Shut down the SSL and rethrow the exception.  Users will need to drain any alerts
+                // from the SSL before closing.
+                sendSSLShutdown();
+                throw convertException(e);
+            } catch (InterruptedIOException e) {
+                return newResult(bytesConsumed, bytesProduced, handshakeStatus);
+            } catch (EOFException e) {
+                closeAll();
+                throw convertException(e);
+            } catch (IOException e) {
+                sendSSLShutdown();
+                throw convertException(e);
+            }
+
+            // There won't be any application data until we're done handshaking.
+            // We first check handshakeFinished to eliminate the overhead of extra JNI call if
+            // possible.
+            int pendingCleartextBytes = handshakeFinished ? pendingInboundCleartextBytes() : 0;
+            if (pendingCleartextBytes > 0) {
+                // We filled all buffers but there is still some data pending in the BIO buffer,
+                // return BUFFER_OVERFLOW.
+                return new SSLEngineResult(BUFFER_OVERFLOW,
+                        mayFinishHandshake(handshakeStatus == FINISHED
+                                        ? handshakeStatus
+                                        : getHandshakeStatusInternal()),
+                        bytesConsumed, bytesProduced);
+            }
+
+            return newResult(bytesConsumed, bytesProduced, handshakeStatus);
+        }
+    }
+
+    private static int calcDstsLength(ByteBuffer[] dsts, int dstsOffset, int dstsLength) {
+        int capacity = 0;
+        for (int i = 0; i < dsts.length; i++) {
+            ByteBuffer dst = dsts[i];
+            checkArgument(dst != null, "dsts[%d] is null", i);
+            if (dst.isReadOnly()) {
+                throw new ReadOnlyBufferException();
+            }
+            if (i >= dstsOffset && i < dstsOffset + dstsLength) {
+                capacity += dst.remaining();
+            }
+        }
+        return capacity;
+    }
+
+    private static long calcSrcsLength(ByteBuffer[] srcs, int srcsOffset, int srcsEndOffset) {
+        long len = 0;
+        for (int i = srcsOffset; i < srcsEndOffset; i++) {
+            ByteBuffer src = srcs[i];
+            if (src == null) {
+                throw new IllegalArgumentException("srcs[" + i + "] is null");
+            }
+            len += src.remaining();
+        }
+        return len;
+    }
+
+    private SSLEngineResult.HandshakeStatus handshake() throws SSLException {
+        try {
+            // Only actually perform the handshake if we haven't already just completed it
+            // via BIO operations.
+            try {
+                int ssl_error_code = ssl.doHandshake();
+                switch (ssl_error_code) {
+                    case SSL_ERROR_WANT_READ:
+                        return pendingStatus(pendingOutboundEncryptedBytes());
+                    case SSL_ERROR_WANT_WRITE: {
+                        return NEED_WRAP;
+                    }
+                    default: {
+                        // SSL_ERROR_NONE.
+                    }
+                }
+            } catch (SSLException e) {
+                // Shut down the SSL and rethrow the exception.  Users will need to drain any alerts
+                // from the SSL before closing.
+                sendSSLShutdown();
+                throw e;
+            } catch (IOException e) {
+                sendSSLShutdown();
+                throw e;
+            }
+
+            // The handshake has completed successfully...
+
+            // Update the session from the current state of the SSL object.
+            activeSession.onPeerCertificateAvailable(getPeerHost(), getPeerPort());
+
+            finishHandshake();
+            return FINISHED;
+        } catch (Exception e) {
+            throw toSSLHandshakeException(e);
+        }
+    }
+
+    private void finishHandshake() throws SSLException {
+        handshakeFinished = true;
+        // Notify the listener, if provided.
+        if (handshakeListener != null) {
+            handshakeListener.onHandshakeFinished();
+        }
+    }
+
+    /**
+     * Write plaintext data to the OpenSSL internal BIO
+     *
+     * Calling this function with src.remaining == 0 is undefined.
+     */
+    private int writePlaintextData(final ByteBuffer src, int len) throws SSLException {
+        try {
+            final int pos = src.position();
+            final int sslWrote;
+            if (src.isDirect()) {
+                sslWrote = writePlaintextDataDirect(src, pos, len);
+            } else {
+                sslWrote = writePlaintextDataHeap(src, pos, len);
+            }
+            if (sslWrote > 0) {
+                src.position(pos + sslWrote);
+            }
+            return sslWrote;
+        } catch (Exception e) {
+            throw convertException(e);
+        }
+    }
+
+    private int writePlaintextDataDirect(ByteBuffer src, int pos, int len) throws IOException {
+        return ssl.writeDirectByteBuffer(directByteBufferAddress(src, pos), len);
+    }
+
+    private int writePlaintextDataHeap(ByteBuffer src, int pos, int len) throws IOException {
+        AllocatedBuffer allocatedBuffer = null;
+        try {
+            final ByteBuffer buffer;
+            if (bufferAllocator != null) {
+                allocatedBuffer = bufferAllocator.allocateDirectBuffer(len);
+                buffer = allocatedBuffer.nioBuffer();
+            } else {
+                // We don't have a buffer allocator, but we don't want to send a heap
+                // buffer to JNI. So lazy-create a direct buffer that we will use from now
+                // on to copy plaintext data.
+                buffer = getOrCreateLazyDirectBuffer();
+            }
+
+            // Copy the data to the direct buffer.
+            int limit = src.limit();
+            int bytesToWrite = min(len, buffer.remaining());
+            src.limit(pos + bytesToWrite);
+            buffer.put(src);
+            buffer.flip();
+            // Restore the original position and limit.
+            src.limit(limit);
+            src.position(pos);
+
+            return writePlaintextDataDirect(buffer, 0, bytesToWrite);
+        } finally {
+            if (allocatedBuffer != null) {
+                // Release the buffer back to the pool.
+                allocatedBuffer.release();
+            }
+        }
+    }
+
+    /**
+     * Read plaintext data from the OpenSSL internal BIO
+     */
+    private int readPlaintextData(final ByteBuffer dst) throws IOException {
+        try {
+            final int pos = dst.position();
+            final int limit = dst.limit();
+            final int len = min(SSL3_RT_MAX_PACKET_SIZE, limit - pos);
+            if (dst.isDirect()) {
+                int bytesRead = readPlaintextDataDirect(dst, pos, len);
+                if (bytesRead > 0) {
+                    dst.position(pos + bytesRead);
+                }
+                return bytesRead;
+            }
+
+            // The heap method updates the dst position automatically.
+            return readPlaintextDataHeap(dst, len);
+        } catch (CertificateException e) {
+            throw convertException(e);
+        }
+    }
+
+    private int readPlaintextDataDirect(ByteBuffer dst, int pos, int len)
+            throws IOException, CertificateException {
+        return ssl.readDirectByteBuffer(directByteBufferAddress(dst, pos), len);
+    }
+
+    private int readPlaintextDataHeap(ByteBuffer dst, int len)
+            throws IOException, CertificateException {
+        AllocatedBuffer allocatedBuffer = null;
+        try {
+            final ByteBuffer buffer;
+            if (bufferAllocator != null) {
+                allocatedBuffer = bufferAllocator.allocateDirectBuffer(len);
+                buffer = allocatedBuffer.nioBuffer();
+            } else {
+                // We don't have a buffer allocator, but we don't want to send a heap
+                // buffer to JNI. So lazy-create a direct buffer that we will use from now
+                // on to copy plaintext data.
+                buffer = getOrCreateLazyDirectBuffer();
+            }
+
+            // Read the data to the direct buffer.
+            int bytesToRead = min(len, buffer.remaining());
+            int bytesRead = readPlaintextDataDirect(buffer, 0, bytesToRead);
+            if (bytesRead > 0) {
+                // Copy the data to the heap buffer.
+                buffer.position(bytesRead);
+                buffer.flip();
+                dst.put(buffer);
+            }
+
+            return bytesRead;
+        } finally {
+            if (allocatedBuffer != null) {
+                // Release the buffer back to the pool.
+                allocatedBuffer.release();
+            }
+        }
+    }
+
+    private SSLException convertException(Throwable e) {
+        if (e instanceof SSLHandshakeException || !handshakeFinished) {
+            return SSLUtils.toSSLHandshakeException(e);
+        }
+        return SSLUtils.toSSLException(e);
+    }
+
+    /**
+     * Write encrypted data to the OpenSSL network BIO.
+     */
+    private int writeEncryptedData(final ByteBuffer src, int len) throws SSLException {
+        try {
+            final int pos = src.position();
+            final int bytesWritten;
+            if (src.isDirect()) {
+                bytesWritten = writeEncryptedDataDirect(src, pos, len);
+            } else {
+                bytesWritten = writeEncryptedDataHeap(src, pos, len);
+            }
+
+            if (bytesWritten > 0) {
+                src.position(pos + bytesWritten);
+            }
+
+            return bytesWritten;
+        } catch (IOException e) {
+            throw new SSLException(e);
+        }
+    }
+
+    private int writeEncryptedDataDirect(ByteBuffer src, int pos, int len) throws IOException {
+        return networkBio.writeDirectByteBuffer(directByteBufferAddress(src, pos), len);
+    }
+
+    private int writeEncryptedDataHeap(ByteBuffer src, int pos, int len) throws IOException {
+        AllocatedBuffer allocatedBuffer = null;
+        try {
+            final ByteBuffer buffer;
+            if (bufferAllocator != null) {
+                allocatedBuffer = bufferAllocator.allocateDirectBuffer(len);
+                buffer = allocatedBuffer.nioBuffer();
+            } else {
+                // We don't have a buffer allocator, but we don't want to send a heap
+                // buffer to JNI. So lazy-create a direct buffer that we will use from now
+                // on to copy encrypted packets.
+                buffer = getOrCreateLazyDirectBuffer();
+            }
+
+            int limit = src.limit();
+            int bytesToCopy = min(min(limit - pos, len), buffer.remaining());
+            src.limit(pos + bytesToCopy);
+            buffer.put(src);
+            // Restore the original limit.
+            src.limit(limit);
+
+            // Reset the original position on the source buffer.
+            src.position(pos);
+
+            int bytesWritten = writeEncryptedDataDirect(buffer, 0, bytesToCopy);
+
+            // Restore the original position.
+            src.position(pos);
+
+            return bytesWritten;
+        } finally {
+            if (allocatedBuffer != null) {
+                // Release the buffer back to the pool.
+                allocatedBuffer.release();
+            }
+        }
+    }
+
+    private ByteBuffer getOrCreateLazyDirectBuffer() {
+        if (lazyDirectBuffer == null) {
+            lazyDirectBuffer = ByteBuffer.allocateDirect(
+                    max(SSL3_RT_MAX_PLAIN_LENGTH, SSL3_RT_MAX_PACKET_SIZE));
+        }
+        lazyDirectBuffer.clear();
+        return lazyDirectBuffer;
+    }
+
+    private long directByteBufferAddress(ByteBuffer directBuffer, int pos) {
+        return NativeCrypto.getDirectBufferAddress(directBuffer) + pos;
+    }
+
+    private SSLEngineResult readPendingBytesFromBIO(ByteBuffer dst, int bytesConsumed,
+            int bytesProduced, SSLEngineResult.HandshakeStatus status) throws SSLException {
+        try {
+            // Check to see if the engine wrote data into the network BIO
+            int pendingNet = pendingOutboundEncryptedBytes();
+            if (pendingNet > 0) {
+                // Do we have enough room in dst to write encrypted data?
+                int capacity = dst.remaining();
+                if (capacity < pendingNet) {
+                    return new SSLEngineResult(BUFFER_OVERFLOW,
+                            mayFinishHandshake(
+                                    status == FINISHED ? status : getHandshakeStatus(pendingNet)),
+                            bytesConsumed, bytesProduced);
+                }
+
+                // Write the pending data from the network BIO into the dst buffer
+                int produced = readEncryptedData(dst, pendingNet);
+
+                if (produced <= 0) {
+                    // We ignore BIO_* errors here as we use in memory BIO anyway and will do
+                    // another SSL_* call later on in which we will produce an exception in
+                    // case of an error
+                    NativeCrypto.SSL_clear_error();
+                } else {
+                    bytesProduced += produced;
+                    pendingNet -= produced;
+                }
+
+                return new SSLEngineResult(getEngineStatus(),
+                        mayFinishHandshake(
+                                status == FINISHED ? status : getHandshakeStatus(pendingNet)),
+                        bytesConsumed, bytesProduced);
+            }
+            return null;
+        } catch (Exception e) {
+            throw convertException(e);
+        }
+    }
+
+    /**
+     * Read encrypted data from the OpenSSL network BIO
+     */
+    private int readEncryptedData(final ByteBuffer dst, final int pending) throws SSLException {
+        try {
+            int bytesRead = 0;
+            final int pos = dst.position();
+            if (dst.remaining() >= pending) {
+                final int limit = dst.limit();
+                final int len = min(pending, limit - pos);
+                if (dst.isDirect()) {
+                    bytesRead = readEncryptedDataDirect(dst, pos, len);
+                    // Need to update the position on the dst buffer.
+                    if (bytesRead > 0) {
+                        dst.position(pos + bytesRead);
+                    }
+                } else {
+                    // The heap method will update the position on the dst buffer automatically.
+                    bytesRead = readEncryptedDataHeap(dst, len);
+                }
+            }
+
+            return bytesRead;
+        } catch (Exception e) {
+            throw convertException(e);
+        }
+    }
+
+    private int readEncryptedDataDirect(ByteBuffer dst, int pos, int len) throws IOException {
+        return networkBio.readDirectByteBuffer(directByteBufferAddress(dst, pos), len);
+    }
+
+    private int readEncryptedDataHeap(ByteBuffer dst, int len) throws IOException {
+        AllocatedBuffer allocatedBuffer = null;
+        try {
+            final ByteBuffer buffer;
+            if (bufferAllocator != null) {
+                allocatedBuffer = bufferAllocator.allocateDirectBuffer(len);
+                buffer = allocatedBuffer.nioBuffer();
+            } else {
+                // We don't have a buffer allocator, but we don't want to send a heap
+                // buffer to JNI. So lazy-create a direct buffer that we will use from now
+                // on to copy encrypted packets.
+                buffer = getOrCreateLazyDirectBuffer();
+            }
+
+            int bytesToRead = min(len, buffer.remaining());
+            int bytesRead = readEncryptedDataDirect(buffer, 0, bytesToRead);
+            if (bytesRead > 0) {
+                buffer.position(bytesRead);
+                buffer.flip();
+                dst.put(buffer);
+            }
+
+            return bytesRead;
+        } finally {
+            if (allocatedBuffer != null) {
+                // Release the buffer back to the pool.
+                allocatedBuffer.release();
+            }
+        }
+    }
+
+    private SSLEngineResult.HandshakeStatus mayFinishHandshake(
+            SSLEngineResult.HandshakeStatus status) throws SSLException {
+        if (!handshakeFinished && status == NOT_HANDSHAKING) {
+            // If the status was NOT_HANDSHAKING and we not finished the handshake we need to call
+            // SSL_do_handshake() again
+            return handshake();
+        }
+        return status;
+    }
+
+    private SSLEngineResult.HandshakeStatus getHandshakeStatus(int pending) {
+        // Check if we are in the initial handshake phase or shutdown phase
+        return !handshakeFinished ? pendingStatus(pending) : NOT_HANDSHAKING;
+    }
+
+    private SSLEngineResult.Status getEngineStatus() {
+        switch (state) {
+            case STATE_CLOSED_INBOUND:
+            case STATE_CLOSED_OUTBOUND:
+            case STATE_CLOSED:
+                return CLOSED;
+            default:
+                return OK;
+        }
+    }
+
+    private void closeAll() {
+        closeOutbound();
+        closeInbound();
+    }
+
+    private void freeIfDone() {
+        if (isInboundDone() && isOutboundDone()) {
+            closeAndFreeResources();
+        }
+    }
+
+    private SSLException newSslExceptionWithMessage(String err) {
+        if (!handshakeFinished) {
+            return new SSLException(err);
+        }
+        return new SSLHandshakeException(err);
+    }
+
+    private SSLEngineResult newResult(int bytesConsumed, int bytesProduced,
+            SSLEngineResult.HandshakeStatus status) throws SSLException {
+        return new SSLEngineResult(getEngineStatus(),
+                mayFinishHandshake(status == FINISHED ? status : getHandshakeStatusInternal()),
+                bytesConsumed, bytesProduced);
+    }
+
+    @Override
+    public SSLEngineResult wrap(ByteBuffer src, ByteBuffer dst) throws SSLException {
+        synchronized (ssl) {
+            try {
+                return wrap(singleSrcBuffer(src), dst);
+            } finally {
+                resetSingleSrcBuffer();
+            }
+        }
+    }
+
+    @Override
+    public SSLEngineResult wrap(ByteBuffer[] srcs, int srcsOffset, int srcsLength, ByteBuffer dst)
+            throws SSLException {
+        checkArgument(srcs != null, "srcs is null");
+        checkArgument(dst != null, "dst is null");
+        checkPositionIndexes(srcsOffset, srcsOffset + srcsLength, srcs.length);
+        if (dst.isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
+
+        synchronized (ssl) {
+            switch (state) {
+                case STATE_MODE_SET:
+                    // Begin the handshake implicitly.
+                    beginHandshakeInternal();
+                    break;
+                case STATE_CLOSED_OUTBOUND:
+                case STATE_CLOSED:
+                    // We may have pending encrypted bytes from a close_notify alert, so
+                    // try to read them out
+                    SSLEngineResult pendingNetResult =
+                            readPendingBytesFromBIO(dst, 0, 0, HandshakeStatus.NOT_HANDSHAKING);
+                    if (pendingNetResult != null) {
+                        freeIfDone();
+                        return pendingNetResult;
+                    }
+                    return new SSLEngineResult(Status.CLOSED, getHandshakeStatusInternal(), 0, 0);
+                case STATE_NEW:
+                    throw new IllegalStateException(
+                            "Client/server mode must be set before calling wrap");
+                default:
+                    break;
+            }
+
+            // If we haven't completed the handshake yet, just let the caller know.
+            HandshakeStatus handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
+            // Prepare OpenSSL to work in server mode and receive handshake
+            if (!handshakeFinished) {
+                handshakeStatus = handshake();
+                if (handshakeStatus == NEED_UNWRAP) {
+                    return NEED_UNWRAP_OK;
+                }
+
+                if (state == STATE_CLOSED) {
+                    return NEED_UNWRAP_CLOSED;
+                }
+                // NEED_WRAP - just fall through to perform the wrap.
+            }
+
+            int srcsLen = 0;
+            final int endOffset = srcsOffset + srcsLength;
+            for (int i = srcsOffset; i < endOffset; ++i) {
+                final ByteBuffer src = srcs[i];
+                if (src == null) {
+                    throw new IllegalArgumentException("srcs[" + i + "] is null");
+                }
+                if (srcsLen == SSL3_RT_MAX_PLAIN_LENGTH) {
+                    continue;
+                }
+
+                srcsLen += src.remaining();
+                if (srcsLen > SSL3_RT_MAX_PLAIN_LENGTH || srcsLen < 0) {
+                    // If srcLen > MAX_PLAINTEXT_LENGTH or secLen < 0 just set it to
+                    // MAX_PLAINTEXT_LENGTH.
+                    // This also help us to guard against overflow.
+                    // We not break out here as we still need to check for null entries in srcs[].
+                    srcsLen = SSL3_RT_MAX_PLAIN_LENGTH;
+                }
+            }
+
+            if (dst.remaining() < calculateOutNetBufSize(srcsLen)) {
+                return new SSLEngineResult(
+                    Status.BUFFER_OVERFLOW, getHandshakeStatusInternal(), 0, 0);
+            }
+
+            int bytesProduced = 0;
+            int bytesConsumed = 0;
+        loop:
+            for (int i = srcsOffset; i < endOffset; ++i) {
+                final ByteBuffer src = srcs[i];
+                checkArgument(src != null, "srcs[%d] is null", i);
+                while (src.hasRemaining()) {
+                    final SSLEngineResult pendingNetResult;
+                    // Write plaintext application data to the SSL engine
+                    int result = writePlaintextData(
+                        src, min(src.remaining(), SSL3_RT_MAX_PLAIN_LENGTH - bytesConsumed));
+                    if (result > 0) {
+                        bytesConsumed += result;
+
+                        pendingNetResult = readPendingBytesFromBIO(
+                            dst, bytesConsumed, bytesProduced, handshakeStatus);
+                        if (pendingNetResult != null) {
+                            if (pendingNetResult.getStatus() != OK) {
+                                return pendingNetResult;
+                            }
+                            bytesProduced = pendingNetResult.bytesProduced();
+                        }
+                        if (bytesConsumed == SSL3_RT_MAX_PLAIN_LENGTH) {
+                            // If we consumed the maximum amount of bytes for the plaintext length
+                            // break out of the loop and start to fill the dst buffer.
+                            break loop;
+                        }
+                    } else {
+                        int sslError = ssl.getError(result);
+                        switch (sslError) {
+                            case SSL_ERROR_ZERO_RETURN:
+                                // This means the connection was shutdown correctly, close inbound
+                                // and outbound
+                                closeAll();
+                                pendingNetResult = readPendingBytesFromBIO(
+                                        dst, bytesConsumed, bytesProduced, handshakeStatus);
+                                return pendingNetResult != null ? pendingNetResult
+                                                                : CLOSED_NOT_HANDSHAKING;
+                            case SSL_ERROR_WANT_READ:
+                                // If there is no pending data to read from BIO we should go back to
+                                // event loop and try
+                                // to read more data [1]. It is also possible that event loop will
+                                // detect the socket
+                                // has been closed. [1]
+                                // https://www.openssl.org/docs/manmaster/man3/SSL_write.html
+                                pendingNetResult = readPendingBytesFromBIO(
+                                        dst, bytesConsumed, bytesProduced, handshakeStatus);
+                                return pendingNetResult != null
+                                        ? pendingNetResult
+                                        : new SSLEngineResult(getEngineStatus(), NEED_UNWRAP,
+                                                  bytesConsumed, bytesProduced);
+                            case SSL_ERROR_WANT_WRITE:
+                                // SSL_ERROR_WANT_WRITE typically means that the underlying
+                                // transport is not writable
+                                // and we should set the "want write" flag on the selector and try
+                                // again when the
+                                // underlying transport is writable [1]. However we are not directly
+                                // writing to the
+                                // underlying transport and instead writing to a BIO buffer. The
+                                // OpenSsl documentation
+                                // says we should do the following [1]:
+                                //
+                                // "When using a buffering BIO, like a BIO pair, data must be
+                                // written into or retrieved
+                                // out of the BIO before being able to continue."
+                                //
+                                // So we attempt to drain the BIO buffer below, but if there is no
+                                // data this condition
+                                // is undefined and we assume their is a fatal error with the
+                                // openssl engine and close.
+                                // [1] https://www.openssl.org/docs/manmaster/man3/SSL_write.html
+                                pendingNetResult = readPendingBytesFromBIO(
+                                        dst, bytesConsumed, bytesProduced, handshakeStatus);
+                                return pendingNetResult != null ? pendingNetResult
+                                                                : NEED_WRAP_CLOSED;
+                            default:
+                                // Everything else is considered as error
+                                sendSSLShutdown();
+                                throw newSslExceptionWithMessage("SSL_write");
+                        }
+                    }
+                }
+            }
+            // We need to check if pendingWrittenBytesInBIO was checked yet, as we may not checked
+            // if the srcs was
+            // empty, or only contained empty buffers.
+            if (bytesConsumed == 0) {
+                SSLEngineResult pendingNetResult =
+                        readPendingBytesFromBIO(dst, 0, bytesProduced, handshakeStatus);
+                if (pendingNetResult != null) {
+                    return pendingNetResult;
+                }
+            }
+
+            // return new SSLEngineResult(OK, getHandshakeStatusInternal(), bytesConsumed,
+            // bytesProduced);
+            return newResult(bytesConsumed, bytesProduced, handshakeStatus);
+        }
+    }
+
+    @Override
+    public int clientPSKKeyRequested(String identityHint, byte[] identity, byte[] key) {
+        return ssl.clientPSKKeyRequested(identityHint, identity, key);
+    }
+
+    @Override
+    public int serverPSKKeyRequested(String identityHint, String identity, byte[] key) {
+        return ssl.serverPSKKeyRequested(identityHint, identity, key);
+    }
+
+    @Override
+    public void onSSLStateChange(int type, int val) {
+        synchronized (ssl) {
+            switch (type) {
+                case SSL_CB_HANDSHAKE_START: {
+                    // For clients, this will allow the NEED_UNWRAP status to be
+                    // returned.
+                    transitionTo(STATE_HANDSHAKE_STARTED);
+                    break;
+                }
+                case SSL_CB_HANDSHAKE_DONE: {
+                    if (state != STATE_HANDSHAKE_STARTED
+                            && state != STATE_READY_HANDSHAKE_CUT_THROUGH) {
+                        throw new IllegalStateException(
+                                "Completed handshake while in mode " + state);
+                    }
+                    transitionTo(STATE_HANDSHAKE_COMPLETED);
+                    break;
+                }
+                default:
+                    // Ignore
+            }
+        }
+    }
+
+    @Override
+    public void onNewSessionEstablished(long sslSessionNativePtr) {
+        try {
+            // Increment the reference count to "take ownership" of the session resource.
+            NativeCrypto.SSL_SESSION_up_ref(sslSessionNativePtr);
+
+            // Create a native reference which will release the SSL_SESSION in its finalizer.
+            // This constructor will only throw if the native pointer passed in is NULL, which
+            // BoringSSL guarantees will not happen.
+            NativeRef.SSL_SESSION ref = new SSL_SESSION(sslSessionNativePtr);
+
+            NativeSslSession nativeSession = NativeSslSession.newInstance(ref, activeSession);
+
+            // Cache the newly established session.
+            AbstractSessionContext ctx = sessionContext();
+            ctx.cacheSession(nativeSession);
+        } catch (Exception ignored) {
+            // Ignore.
+        }
+    }
+
+    @Override
+    public long serverSessionRequested(byte[] id) {
+        // TODO(nathanmittler): Implement server-side caching for TLS < 1.3
+        return 0;
+    }
+
+    @Override
+    public void verifyCertificateChain(byte[][] certChain, String authMethod)
+            throws CertificateException {
+        try {
+            if (certChain == null || certChain.length == 0) {
+                throw new CertificateException("Peer sent no certificate");
+            }
+            X509Certificate[] peerCertChain = SSLUtils.decodeX509CertificateChain(certChain);
+
+            X509TrustManager x509tm = sslParameters.getX509TrustManager();
+            if (x509tm == null) {
+                throw new CertificateException("No X.509 TrustManager");
+            }
+
+            // Update the peer information on the session.
+            activeSession.onPeerCertificatesReceived(getPeerHost(), getPeerPort(), peerCertChain);
+
+            if (getUseClientMode()) {
+                Platform.checkServerTrusted(x509tm, peerCertChain, authMethod, this);
+            } else {
+                String authType = peerCertChain[0].getPublicKey().getAlgorithm();
+                Platform.checkClientTrusted(x509tm, peerCertChain, authType, this);
+            }
+        } catch (CertificateException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new CertificateException(e);
+        }
+    }
+
+    @Override
+    public void clientCertificateRequested(byte[] keyTypeBytes, int[] signatureAlgs,
+            byte[][] asn1DerEncodedPrincipals)
+            throws CertificateEncodingException, SSLException {
+        ssl.chooseClientCertificate(keyTypeBytes, signatureAlgs, asn1DerEncodedPrincipals);
+    }
+
+    private void sendSSLShutdown() {
+        try {
+            ssl.shutdown();
+        } catch (IOException ignored) {
+            // TODO: The RI ignores close failures in SSLSocket, but need to
+            // investigate whether it does for SSLEngine.
+        }
+    }
+
+    private void closeAndFreeResources() {
+        transitionTo(STATE_CLOSED);
+        if (!ssl.isClosed()) {
+            ssl.close();
+            networkBio.close();
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            transitionTo(STATE_CLOSED);
+        } finally {
+            super.finalize();
+        }
+    }
+
+    @Override
+    public String chooseServerAlias(X509KeyManager keyManager, String keyType) {
+        if (keyManager instanceof X509ExtendedKeyManager) {
+            X509ExtendedKeyManager ekm = (X509ExtendedKeyManager) keyManager;
+            return ekm.chooseEngineServerAlias(keyType, null, this);
+        } else {
+            return keyManager.chooseServerAlias(keyType, null, null);
+        }
+    }
+
+    @Override
+    public String chooseClientAlias(
+            X509KeyManager keyManager, X500Principal[] issuers, String[] keyTypes) {
+        if (keyManager instanceof X509ExtendedKeyManager) {
+            X509ExtendedKeyManager ekm = (X509ExtendedKeyManager) keyManager;
+            return ekm.chooseEngineClientAlias(keyTypes, issuers, this);
+        } else {
+            return keyManager.chooseClientAlias(keyTypes, issuers, null);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+    public String chooseServerPSKIdentityHint(PSKKeyManager keyManager) {
+        return keyManager.chooseServerKeyIdentityHint(this);
+    }
+
+    @Override
+    @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+    public String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint) {
+        return keyManager.chooseClientKeyIdentity(identityHint, this);
+    }
+
+    @Override
+    @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+    public SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity) {
+        return keyManager.getKey(identityHint, identity, this);
+    }
+
+    /**
+     * This method enables session ticket support.
+     *
+     * @param useSessionTickets True to enable session tickets
+     */
+    @Override
+    void setUseSessionTickets(boolean useSessionTickets) {
+        sslParameters.setUseSessionTickets(useSessionTickets);
+    }
+
+    @Override
+    String[] getApplicationProtocols() {
+        return sslParameters.getApplicationProtocols();
+    }
+
+    @Override
+    void setApplicationProtocols(String[] protocols) {
+        sslParameters.setApplicationProtocols(protocols);
+    }
+
+    @Override
+    void setApplicationProtocolSelector(ApplicationProtocolSelector selector) {
+        setApplicationProtocolSelector(
+                selector == null ? null : new ApplicationProtocolSelectorAdapter(this, selector));
+    }
+
+    @Override
+    byte[] getTlsUnique() {
+        return ssl.getTlsUnique();
+    }
+
+    @Override
+    byte[] exportKeyingMaterial(String label, byte[] context, int length) throws SSLException {
+        synchronized (ssl) {
+            if (state < STATE_HANDSHAKE_COMPLETED || state == STATE_CLOSED) {
+                return null;
+            }
+        }
+        return ssl.exportKeyingMaterial(label, context, length);
+    }
+
+    void setApplicationProtocolSelector(ApplicationProtocolSelectorAdapter adapter) {
+        sslParameters.setApplicationProtocolSelector(adapter);
+    }
+
+    @Override
+    public String getApplicationProtocol() {
+        return SSLUtils.toProtocolString(ssl.getApplicationProtocol());
+    }
+
+    @Override
+    public String getHandshakeApplicationProtocol() {
+        synchronized (ssl) {
+            return state == STATE_HANDSHAKE_STARTED ? getApplicationProtocol() : null;
+        }
+    }
+
+    private ByteBuffer[] singleSrcBuffer(ByteBuffer src) {
+        singleSrcBuffer[0] = src;
+        return singleSrcBuffer;
+    }
+
+    private void resetSingleSrcBuffer() {
+        singleSrcBuffer[0] = null;
+    }
+
+    private ByteBuffer[] singleDstBuffer(ByteBuffer src) {
+        singleDstBuffer[0] = src;
+        return singleDstBuffer;
+    }
+
+    private void resetSingleDstBuffer() {
+        singleDstBuffer[0] = null;
+    }
+
+    private ClientSessionContext clientSessionContext() {
+        return sslParameters.getClientSessionContext();
+    }
+
+    private AbstractSessionContext sessionContext() {
+        return sslParameters.getSessionContext();
+    }
+
+    private void transitionTo(int newState) {
+        switch (newState) {
+            case STATE_HANDSHAKE_STARTED: {
+                handshakeFinished = false;
+                activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+                break;
+            }
+            case STATE_CLOSED: {
+                if (!ssl.isClosed() && state >= STATE_HANDSHAKE_STARTED && state < STATE_CLOSED) {
+                    closedSession = new SessionSnapshot(activeSession);
+                }
+                break;
+            }
+            default: {
+                break;
+            }
+        }
+
+        // Update the state
+        this.state = newState;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptEngineSocket.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptEngineSocket.java
new file mode 100644
index 0000000..96489f1
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptEngineSocket.java
@@ -0,0 +1,901 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static javax.net.ssl.SSLEngineResult.Status.OK;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_CLOSED;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_HANDSHAKE_COMPLETED;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_HANDSHAKE_STARTED;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_NEW;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_READY;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_READY_HANDSHAKE_CUT_THROUGH;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.security.PrivateKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.X509ExtendedTrustManager;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * Implements crypto handling by delegating to {@link ConscryptEngine}.
+ */
+class ConscryptEngineSocket extends OpenSSLSocketImpl {
+    private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
+
+    private final ConscryptEngine engine;
+    private final Object stateLock = new Object();
+    private final Object handshakeLock = new Object();
+
+    private SSLOutputStream out;
+    private SSLInputStream in;
+
+    private BufferAllocator bufferAllocator = ConscryptEngine.getDefaultBufferAllocator();
+
+    // @GuardedBy("stateLock");
+    private int state = STATE_NEW;
+
+    // The constructors should not be called except from the Platform class, because we may
+    // want to construct a subclass instead.
+    ConscryptEngineSocket(SSLParametersImpl sslParameters) throws IOException {
+        engine = newEngine(sslParameters, this);
+    }
+
+    ConscryptEngineSocket(String hostname, int port, SSLParametersImpl sslParameters)
+            throws IOException {
+        super(hostname, port);
+        engine = newEngine(sslParameters, this);
+    }
+
+    ConscryptEngineSocket(InetAddress address, int port, SSLParametersImpl sslParameters)
+            throws IOException {
+        super(address, port);
+        engine = newEngine(sslParameters, this);
+    }
+
+    ConscryptEngineSocket(String hostname, int port, InetAddress clientAddress, int clientPort,
+            SSLParametersImpl sslParameters) throws IOException {
+        super(hostname, port, clientAddress, clientPort);
+        engine = newEngine(sslParameters, this);
+    }
+
+    ConscryptEngineSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort,
+            SSLParametersImpl sslParameters) throws IOException {
+        super(address, port, clientAddress, clientPort);
+        engine = newEngine(sslParameters, this);
+    }
+
+    ConscryptEngineSocket(Socket socket, String hostname, int port, boolean autoClose,
+            SSLParametersImpl sslParameters) throws IOException {
+        super(socket, hostname, port, autoClose);
+        engine = newEngine(sslParameters, this);
+    }
+
+    private static ConscryptEngine newEngine(
+            SSLParametersImpl sslParameters, final ConscryptEngineSocket socket) {
+        SSLParametersImpl modifiedParams;
+        if (Platform.supportsX509ExtendedTrustManager()) {
+            modifiedParams = sslParameters.cloneWithTrustManager(
+                    getDelegatingTrustManager(sslParameters.getX509TrustManager(), socket));
+        } else {
+            modifiedParams = sslParameters;
+        }
+        ConscryptEngine engine = new ConscryptEngine(modifiedParams, socket.peerInfoProvider());
+
+        // When the handshake completes, notify any listeners.
+        engine.setHandshakeListener(new HandshakeListener() {
+            /**
+             * Protected by {@code stateLock}
+             */
+            @Override
+            public void onHandshakeFinished() {
+                // Just call the outer class method.
+                socket.onHandshakeFinished();
+            }
+        });
+
+        // Transition the engine state to MODE_SET
+        engine.setUseClientMode(sslParameters.getUseClientMode());
+        return engine;
+    }
+
+    // Returns a trust manager that delegates to the given trust manager, but maps SSLEngine
+    // references to the given ConscryptEngineSocket.  Our internal engine will call
+    // the SSLEngine-receiving methods, but our callers expect the SSLSocket-receiving
+    // methods to get called.
+    private static X509TrustManager getDelegatingTrustManager(
+            final X509TrustManager delegate, final ConscryptEngineSocket socket) {
+        if (delegate instanceof X509ExtendedTrustManager) {
+            final X509ExtendedTrustManager extendedDelegate = (X509ExtendedTrustManager) delegate;
+            return new X509ExtendedTrustManager() {
+                @Override
+                public void checkClientTrusted(X509Certificate[] x509Certificates, String s,
+                        Socket socket) throws CertificateException {
+                    throw new AssertionError("Should not be called");
+                }
+                @Override
+                public void checkServerTrusted(X509Certificate[] x509Certificates, String s,
+                        Socket socket) throws CertificateException {
+                    throw new AssertionError("Should not be called");
+                }
+                @Override
+                public void checkClientTrusted(X509Certificate[] x509Certificates, String s,
+                        SSLEngine sslEngine) throws CertificateException {
+                    extendedDelegate.checkClientTrusted(x509Certificates, s, socket);
+                }
+                @Override
+                public void checkServerTrusted(X509Certificate[] x509Certificates, String s,
+                        SSLEngine sslEngine) throws CertificateException {
+                    extendedDelegate.checkServerTrusted(x509Certificates, s, socket);
+                }
+                @Override
+                public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
+                        throws CertificateException {
+                    extendedDelegate.checkClientTrusted(x509Certificates, s);
+                }
+                @Override
+                public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
+                        throws CertificateException {
+                    extendedDelegate.checkServerTrusted(x509Certificates, s);
+                }
+                @Override
+                public X509Certificate[] getAcceptedIssuers() {
+                    return extendedDelegate.getAcceptedIssuers();
+                }
+            };
+        }
+        return delegate;
+    }
+
+    @Override
+    public final SSLParameters getSSLParameters() {
+        return engine.getSSLParameters();
+    }
+
+    @Override
+    public final void setSSLParameters(SSLParameters sslParameters) {
+        engine.setSSLParameters(sslParameters);
+    }
+
+    @Override
+    public final void startHandshake() throws IOException {
+        checkOpen();
+
+        try {
+            synchronized (handshakeLock) {
+                // Only lock stateLock when we begin the handshake. This is done so that we don't
+                // hold the stateLock when we invoke the handshake completion listeners.
+                synchronized (stateLock) {
+                    // Initialize the handshake if we haven't already.
+                    if (state == STATE_NEW) {
+                        state = STATE_HANDSHAKE_STARTED;
+                        engine.beginHandshake();
+                        in = new SSLInputStream();
+                        out = new SSLOutputStream();
+                    } else {
+                        // We've either started the handshake already or have been closed.
+                        // Do nothing in both cases.
+                        //
+                        // NOTE: BoringSSL does not support initiating renegotiation, so we always
+                        // ignore addition handshake calls.
+                        return;
+                    }
+                }
+
+                doHandshake();
+            }
+        } catch (SSLException e) {
+            close();
+            throw e;
+        } catch (IOException e) {
+            close();
+            throw e;
+        } catch (Exception e) {
+            close();
+            // Convert anything else to a handshake exception.
+            throw SSLUtils.toSSLHandshakeException(e);
+        }
+    }
+
+    private void doHandshake() throws IOException {
+        try {
+            boolean finished = false;
+            while (!finished) {
+                switch (engine.getHandshakeStatus()) {
+                    case NEED_UNWRAP:
+                        if (in.processDataFromSocket(EmptyArray.BYTE, 0, 0) < 0) {
+                            // Can't complete the handshake due to EOF.
+                            throw SSLUtils.toSSLHandshakeException(new EOFException());
+                        }
+                        break;
+                    case NEED_WRAP: {
+                        out.writeInternal(EMPTY_BUFFER);
+                        // Always flush handshake frames immediately.
+                        out.flushInternal();
+                        break;
+                    }
+                    case NEED_TASK: {
+                        // Should never get here, since our engine never provides tasks.
+                        throw new IllegalStateException("Engine tasks are unsupported");
+                    }
+                    case NOT_HANDSHAKING:
+                    case FINISHED: {
+                        // Handshake is complete.
+                        finished = true;
+                        break;
+                    }
+                    default: {
+                        throw new IllegalStateException(
+                            "Unknown handshake status: " + engine.getHandshakeStatus());
+                    }
+                }
+            }
+        } catch (SSLException e) {
+            drainOutgoingQueue();
+            close();
+            throw e;
+        } catch (IOException e) {
+            close();
+            throw e;
+        } catch (Exception e) {
+            close();
+            // Convert anything else to a handshake exception.
+            throw SSLUtils.toSSLHandshakeException(e);
+        }
+    }
+
+    @Override
+    public final InputStream getInputStream() throws IOException {
+        checkOpen();
+
+        // Block waiting for a handshake without a lock held. It's possible that the socket
+        // is closed at this point. If that happens, we'll still return the input stream but
+        // all reads on it will throw.
+        waitForHandshake();
+        return in;
+    }
+
+    @Override
+    public final OutputStream getOutputStream() throws IOException {
+        checkOpen();
+
+        // Block waiting for a handshake without a lock held. It's possible that the socket
+        // is closed at this point. If that happens, we'll still return the input stream but
+        // all reads on it will throw.
+        waitForHandshake();
+
+        return out;
+    }
+
+    @Override
+    public final SSLSession getHandshakeSession() {
+        return engine.handshakeSession();
+    }
+
+    @Override
+    public final SSLSession getSession() {
+        if (isConnected()) {
+            try {
+                waitForHandshake();
+            } catch (IOException e) {
+                // Fall through
+            }
+        }
+        return engine.getSession();
+    }
+
+    @Override
+    final SSLSession getActiveSession() {
+        return engine.getSession();
+    }
+
+    @Override
+    public final boolean getEnableSessionCreation() {
+        return engine.getEnableSessionCreation();
+    }
+
+    @Override
+    public final void setEnableSessionCreation(boolean flag) {
+        engine.setEnableSessionCreation(flag);
+    }
+
+    @Override
+    public final String[] getSupportedCipherSuites() {
+        return engine.getSupportedCipherSuites();
+    }
+
+    @Override
+    public final String[] getEnabledCipherSuites() {
+        return engine.getEnabledCipherSuites();
+    }
+
+    @Override
+    public final void setEnabledCipherSuites(String[] suites) {
+        engine.setEnabledCipherSuites(suites);
+    }
+
+    @Override
+    public final String[] getSupportedProtocols() {
+        return engine.getSupportedProtocols();
+    }
+
+    @Override
+    public final String[] getEnabledProtocols() {
+        return engine.getEnabledProtocols();
+    }
+
+    @Override
+    public final void setEnabledProtocols(String[] protocols) {
+        engine.setEnabledProtocols(protocols);
+    }
+
+    /**
+     * This method enables Server Name Indication.  If the hostname is not a valid SNI hostname,
+     * the SNI extension will be omitted from the handshake.
+     *
+     * @param hostname the desired SNI hostname, or null to disable
+     */
+    @Override
+    public final void setHostname(String hostname) {
+        engine.setHostname(hostname);
+        super.setHostname(hostname);
+    }
+
+    @Override
+    public final void setUseSessionTickets(boolean useSessionTickets) {
+        engine.setUseSessionTickets(useSessionTickets);
+    }
+
+    @Override
+    public final void setChannelIdEnabled(boolean enabled) {
+        engine.setChannelIdEnabled(enabled);
+    }
+
+    @Override
+    public final byte[] getChannelId() throws SSLException {
+        return engine.getChannelId();
+    }
+
+    @Override
+    public final void setChannelIdPrivateKey(PrivateKey privateKey) {
+        engine.setChannelIdPrivateKey(privateKey);
+    }
+
+    @Override
+    byte[] getTlsUnique() {
+        return engine.getTlsUnique();
+    }
+
+    @Override
+    byte[] exportKeyingMaterial(String label, byte[] context, int length) throws SSLException {
+        return engine.exportKeyingMaterial(label, context, length);
+    }
+
+    @Override
+    public final boolean getUseClientMode() {
+        return engine.getUseClientMode();
+    }
+
+    @Override
+    public final void setUseClientMode(boolean mode) {
+        engine.setUseClientMode(mode);
+    }
+
+    @Override
+    public final boolean getWantClientAuth() {
+        return engine.getWantClientAuth();
+    }
+
+    @Override
+    public final boolean getNeedClientAuth() {
+        return engine.getNeedClientAuth();
+    }
+
+    @Override
+    public final void setNeedClientAuth(boolean need) {
+        engine.setNeedClientAuth(need);
+    }
+
+    @Override
+    public final void setWantClientAuth(boolean want) {
+        engine.setWantClientAuth(want);
+    }
+
+    @Override
+    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+    public final void close() throws IOException {
+        // TODO: Close SSL sockets using a background thread so they close gracefully.
+
+        if (stateLock == null) {
+            // close() has been called before we've initialized the socket, so just
+            // return.
+            return;
+        }
+
+        synchronized (stateLock) {
+            if (state == STATE_CLOSED) {
+                // close() has already been called, so do nothing and return.
+                return;
+            }
+
+            state = STATE_CLOSED;
+
+            stateLock.notifyAll();
+        }
+
+        try {
+            // Close the underlying socket.
+            super.close();
+        } finally {
+            // Close the engine.
+            engine.closeInbound();
+            engine.closeOutbound();
+            
+            // Release any resources we're holding
+            if (in != null) {
+                in.release();
+            }
+        }
+    }
+
+    @Override
+    final void setApplicationProtocols(String[] protocols) {
+        engine.setApplicationProtocols(protocols);
+    }
+
+    @Override
+    final String[] getApplicationProtocols() {
+        return engine.getApplicationProtocols();
+    }
+
+    @Override
+    public final String getApplicationProtocol() {
+        return engine.getApplicationProtocol();
+    }
+
+    @Override
+    public final String getHandshakeApplicationProtocol() {
+        return engine.getHandshakeApplicationProtocol();
+    }
+
+    @Override
+    public final void setApplicationProtocolSelector(ApplicationProtocolSelector selector) {
+        setApplicationProtocolSelector(
+                selector == null ? null : new ApplicationProtocolSelectorAdapter(this, selector));
+    }
+
+    @Override
+    final void setApplicationProtocolSelector(ApplicationProtocolSelectorAdapter selector) {
+        engine.setApplicationProtocolSelector(selector);
+    }
+
+    void setBufferAllocator(BufferAllocator bufferAllocator) {
+        engine.setBufferAllocator(bufferAllocator);
+        this.bufferAllocator = bufferAllocator;
+    }
+
+    private void onHandshakeFinished() {
+        boolean notify = false;
+        synchronized (stateLock) {
+            if (state != STATE_CLOSED) {
+                if (state == STATE_HANDSHAKE_STARTED) {
+                    state = STATE_READY_HANDSHAKE_CUT_THROUGH;
+                } else if (state == STATE_HANDSHAKE_COMPLETED) {
+                    state = STATE_READY;
+                }
+
+                // Unblock threads that are waiting for our state to transition
+                // into STATE_READY or STATE_READY_HANDSHAKE_CUT_THROUGH.
+                stateLock.notifyAll();
+                notify = true;
+            }
+        }
+
+        if (notify) {
+            notifyHandshakeCompletedListeners();
+        }
+    }
+
+    /**
+     * Waits for the handshake to complete.
+     */
+    private void waitForHandshake() throws IOException {
+        startHandshake();
+
+        synchronized (stateLock) {
+            while (state != STATE_READY && state != STATE_READY_HANDSHAKE_CUT_THROUGH
+                    && state != STATE_CLOSED) {
+                try {
+                    stateLock.wait();
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    throw new IOException("Interrupted waiting for handshake", e);
+                }
+            }
+
+            if (state == STATE_CLOSED) {
+                throw new SocketException("Socket is closed");
+            }
+        }
+    }
+
+    private void drainOutgoingQueue() {
+        try {
+            while (engine.pendingOutboundEncryptedBytes() > 0) {
+                out.writeInternal(EMPTY_BUFFER);
+                // Always flush handshake frames immediately.
+                out.flushInternal();
+            }
+        } catch (IOException e) {
+            // Ignore
+        }
+    }
+
+    private OutputStream getUnderlyingOutputStream() throws IOException {
+        return super.getOutputStream();
+    }
+
+    private InputStream getUnderlyingInputStream() throws IOException {
+        return super.getInputStream();
+    }
+
+    /**
+     * Wrap bytes written to the underlying socket.
+     */
+    private final class SSLOutputStream extends OutputStream {
+        private final Object writeLock = new Object();
+        private final ByteBuffer target;
+        private final int targetArrayOffset;
+        private OutputStream socketOutputStream;
+
+        SSLOutputStream() {
+            target = ByteBuffer.allocate(engine.getSession().getPacketBufferSize());
+            targetArrayOffset = target.arrayOffset();
+        }
+
+        @Override
+        public void close() throws IOException {
+            ConscryptEngineSocket.this.close();
+        }
+
+        @Override
+        public void write(int b) throws IOException {
+            startHandshake();
+            synchronized (writeLock) {
+                write(new byte[] {(byte) b});
+            }
+        }
+
+        @Override
+        public void write(byte[] b) throws IOException {
+            startHandshake();
+            synchronized (writeLock) {
+                writeInternal(ByteBuffer.wrap(b));
+            }
+        }
+
+        @Override
+        public void write(byte[] b, int off, int len) throws IOException {
+            startHandshake();
+            synchronized (writeLock) {
+                writeInternal(ByteBuffer.wrap(b, off, len));
+            }
+        }
+
+        private void writeInternal(ByteBuffer buffer) throws IOException {
+            Platform.blockGuardOnNetwork();
+            checkOpen();
+            init();
+
+            // Need to loop through at least once to enable handshaking where no application
+            // bytes are
+            // processed.
+            int len = buffer.remaining();
+            SSLEngineResult engineResult;
+            do {
+                target.clear();
+                engineResult = engine.wrap(buffer, target);
+                if (engineResult.getStatus() != OK) {
+                    throw new SSLException("Unexpected engine result " + engineResult.getStatus());
+                }
+                if (target.position() != engineResult.bytesProduced()) {
+                    throw new SSLException("Engine bytesProduced " + engineResult.bytesProduced()
+                            + " does not match bytes written " + target.position());
+                }
+                len -= engineResult.bytesConsumed();
+                if (len != buffer.remaining()) {
+                    throw new SSLException("Engine did not read the correct number of bytes");
+                }
+
+                target.flip();
+
+                // Write the data to the socket.
+                writeToSocket();
+            } while (len > 0);
+        }
+
+        @Override
+        public void flush() throws IOException {
+            startHandshake();
+            synchronized (writeLock) {
+                flushInternal();
+            }
+        }
+
+        private void flushInternal() throws IOException {
+            checkOpen();
+            init();
+            socketOutputStream.flush();
+        }
+
+        private void init() throws IOException {
+            if (socketOutputStream == null) {
+                socketOutputStream = getUnderlyingOutputStream();
+            }
+        }
+
+        private void writeToSocket() throws IOException {
+            // Write the data to the socket.
+            socketOutputStream.write(target.array(), targetArrayOffset, target.limit());
+        }
+    }
+
+    /**
+     * Unwrap bytes read from the underlying socket.
+     */
+    private final class SSLInputStream extends InputStream {
+        private final Object readLock = new Object();
+        private final byte[] singleByte = new byte[1];
+        private final ByteBuffer fromEngine;
+        private final ByteBuffer fromSocket;
+        private final int fromSocketArrayOffset;
+        private final AllocatedBuffer allocatedBuffer;
+        private InputStream socketInputStream;
+
+        SSLInputStream() {
+            if (bufferAllocator != null) {
+                allocatedBuffer = bufferAllocator.allocateDirectBuffer(
+                        engine.getSession().getApplicationBufferSize());
+                fromEngine = allocatedBuffer.nioBuffer();
+            } else {
+                allocatedBuffer = null;
+                fromEngine = ByteBuffer.allocateDirect(engine.getSession().getApplicationBufferSize());
+            }
+            // Initially fromEngine.remaining() == 0.
+            fromEngine.flip();
+            fromSocket = ByteBuffer.allocate(engine.getSession().getPacketBufferSize());
+            fromSocketArrayOffset = fromSocket.arrayOffset();
+        }
+
+        @Override
+        public void close() throws IOException {
+            ConscryptEngineSocket.this.close();
+        }
+
+        void release() {
+            synchronized (readLock) {
+                if (allocatedBuffer != null) {
+                    allocatedBuffer.release();
+                }
+            }
+        }
+
+        @Override
+        public int read() throws IOException {
+            startHandshake();
+            synchronized (readLock) {
+                // Handle returning of -1 if EOF is reached.
+                int count = read(singleByte, 0, 1);
+                if (count == -1) {
+                    // Handle EOF.
+                    return -1;
+                }
+                if (count != 1) {
+                    throw new SSLException("read incorrect number of bytes " + count);
+                }
+                return (int) singleByte[0];
+            }
+        }
+
+        @Override
+        public int read(byte[] b) throws IOException {
+            startHandshake();
+            synchronized (readLock) {
+                return read(b, 0, b.length);
+            }
+        }
+
+        @Override
+        public int read(byte[] b, int off, int len) throws IOException {
+            startHandshake();
+            synchronized (readLock) {
+                return readUntilDataAvailable(b, off, len);
+            }
+        }
+
+        @Override
+        public int available() throws IOException {
+            startHandshake();
+            synchronized (readLock) {
+                init();
+                return fromEngine.remaining()
+                        + (fromSocket.hasRemaining() || socketInputStream.available() > 0 ? 1 : 0);
+            }
+        }
+
+        private boolean isHandshaking(HandshakeStatus status) {
+            switch(status) {
+                case NEED_TASK:
+                case NEED_WRAP:
+                case NEED_UNWRAP:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
+        private int readUntilDataAvailable(byte[] b, int off, int len) throws IOException {
+            int count;
+            do {
+                count = processDataFromSocket(b, off, len);
+            } while (count == 0);
+            return count;
+        }
+
+        // Returns any decrypted data from the engine.  If no data is currently present in the
+        // engine's output buffer, reads from the input socket until the engine has processed
+        // at least one TLS record, then returns any data in the output buffer or 0 if no
+        // data is available.  This is used both during handshaking (in which case, the records
+        // will produce no data and this method will return 0) and by the InputStream read()
+        // methods that expect records to produce application data.
+        private int processDataFromSocket(byte[] b, int off, int len) throws IOException {
+            Platform.blockGuardOnNetwork();
+            checkOpen();
+
+            // Make sure the input stream has been created.
+            init();
+
+            for (;;) {
+                // Serve any remaining data from the engine first.
+                if (fromEngine.remaining() > 0) {
+                    int readFromEngine = Math.min(fromEngine.remaining(), len);
+                    fromEngine.get(b, off, readFromEngine);
+                    return readFromEngine;
+                }
+
+                // Try to unwrap any data already in the socket buffer.
+                boolean needMoreDataFromSocket = true;
+
+                // Unwrap the unencrypted bytes into the engine buffer.
+                fromSocket.flip();
+                fromEngine.clear();
+
+                boolean engineHandshaking = isHandshaking(engine.getHandshakeStatus());
+                SSLEngineResult engineResult = engine.unwrap(fromSocket, fromEngine);
+
+                // Shift any remaining data to the beginning of the buffer so that
+                // we can accommodate the next full packet. After this is called,
+                // limit will be restored to capacity and position will point just
+                // past the end of the data.
+                fromSocket.compact();
+                fromEngine.flip();
+
+                switch (engineResult.getStatus()) {
+                    case BUFFER_UNDERFLOW: {
+                        if (engineResult.bytesProduced() == 0) {
+                            // Need to read more data from the socket.
+                            break;
+                        }
+                        // Also serve the data that was produced.
+                        needMoreDataFromSocket = false;
+                        break;
+                    }
+                    case OK: {
+                        // We processed the entire packet successfully...
+
+                        if (!engineHandshaking && isHandshaking(engineResult.getHandshakeStatus())
+                            && isHandshakeFinished()) {
+                            // The received packet is the beginning of a renegotiation handshake.
+                            // Perform another handshake.
+                            renegotiate();
+                            return 0;
+                        }
+
+                        needMoreDataFromSocket = false;
+                        break;
+                    }
+                    case CLOSED: {
+                        // EOF
+                        return -1;
+                    }
+                    default: {
+                        // Anything else is an error.
+                        throw new SSLException(
+                                "Unexpected engine result " + engineResult.getStatus());
+                    }
+                }
+
+                if (!needMoreDataFromSocket && engineResult.bytesProduced() == 0) {
+                    // Read successfully, but produced no data. Possibly part of a
+                    // handshake.
+                    return 0;
+                }
+
+                // Read more data from the socket.
+                if (needMoreDataFromSocket && readFromSocket() == -1) {
+                    // Failed to read the next encrypted packet before reaching EOF.
+                    return -1;
+                }
+
+                // Continue the loop and return the data from the engine buffer.
+            }
+        }
+
+        private boolean isHandshakeFinished() {
+            synchronized (stateLock) {
+                return state >= STATE_READY_HANDSHAKE_CUT_THROUGH;
+            }
+        }
+
+        /**
+         * Processes a renegotiation received from the remote peer.
+         */
+        private void renegotiate() throws IOException {
+            synchronized (handshakeLock) {
+                doHandshake();
+            }
+        }
+
+        private void init() throws IOException {
+            if (socketInputStream == null) {
+                socketInputStream = getUnderlyingInputStream();
+            }
+        }
+
+        private int readFromSocket() throws IOException {
+            try {
+                // Read directly to the underlying array and increment the buffer position if
+                // appropriate.
+                int pos = fromSocket.position();
+                int lim = fromSocket.limit();
+                int read = socketInputStream.read(
+                    fromSocket.array(), fromSocketArrayOffset + pos, lim - pos);
+
+                if (read > 0) {
+                    fromSocket.position(pos + read);
+                }
+                return read;
+            } catch (EOFException e) {
+                return -1;
+            }
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptFileDescriptorSocket.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptFileDescriptorSocket.java
new file mode 100644
index 0000000..05af7c1
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptFileDescriptorSocket.java
@@ -0,0 +1,1180 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_CLOSED;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_HANDSHAKE_COMPLETED;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_HANDSHAKE_STARTED;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_NEW;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_READY;
+import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_READY_HANDSHAKE_CUT_THROUGH;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.ECKey;
+import java.security.spec.ECParameterSpec;
+import javax.crypto.SecretKey;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLProtocolException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
+import com.android.org.conscrypt.ExternalSession.Provider;
+import com.android.org.conscrypt.NativeRef.SSL_SESSION;
+
+/**
+ * Implementation of the class OpenSSLSocketImpl based on OpenSSL.
+ * <p>
+ * Extensions to SSLSocket include:
+ * <ul>
+ * <li>handshake timeout
+ * <li>session tickets
+ * <li>Server Name Indication
+ * </ul>
+ */
+class ConscryptFileDescriptorSocket extends OpenSSLSocketImpl
+        implements NativeCrypto.SSLHandshakeCallbacks, SSLParametersImpl.AliasChooser,
+                   SSLParametersImpl.PSKCallbacks {
+    private static final boolean DBG_STATE = false;
+
+    // @GuardedBy("ssl");
+    private int state = STATE_NEW;
+
+    /**
+     * Wrapper around the underlying SSL object.
+     */
+    private final NativeSsl ssl;
+
+    /**
+     * Protected by synchronizing on ssl. Starts as null, set by
+     * getInputStream.
+     */
+    // @GuardedBy("ssl");
+    private SSLInputStream is;
+
+    /**
+     * Protected by synchronizing on ssl. Starts as null, set by
+     * getInputStream.
+     */
+    // @GuardedBy("ssl");
+    private SSLOutputStream os;
+
+    private final SSLParametersImpl sslParameters;
+
+    /*
+     * A CloseGuard object on Android. On other platforms, this is nothing.
+     */
+    private final Object guard = Platform.closeGuardGet();
+
+    /**
+     * Private key for the TLS Channel ID extension. This field is client-side
+     * only. Set during startHandshake.
+     */
+    private OpenSSLKey channelIdPrivateKey;
+
+    private final ActiveSession activeSession;
+    /**
+     * A snapshot of the active session when the engine was closed.
+     */
+    private SessionSnapshot closedSession;
+    /**
+     * The session object exposed externally from this class.
+     */
+    private final SSLSession externalSession =
+        Platform.wrapSSLSession(new ExternalSession(new Provider() {
+            @Override
+            public ConscryptSession provideSession() {
+                return ConscryptFileDescriptorSocket.this.provideSession();
+            }
+        }));
+
+    private int writeTimeoutMilliseconds = 0;
+    private int handshakeTimeoutMilliseconds = -1; // -1 = same as timeout; 0 = infinite
+
+    // The constructors should not be called except from the Platform class, because we may
+    // want to construct a subclass instead.
+    ConscryptFileDescriptorSocket(SSLParametersImpl sslParameters) throws IOException {
+        this.sslParameters = sslParameters;
+        this.ssl = newSsl(sslParameters, this);
+        activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+    }
+
+    ConscryptFileDescriptorSocket(String hostname, int port, SSLParametersImpl sslParameters)
+            throws IOException {
+        super(hostname, port);
+        this.sslParameters = sslParameters;
+        this.ssl = newSsl(sslParameters, this);
+        activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+    }
+
+    ConscryptFileDescriptorSocket(InetAddress address, int port, SSLParametersImpl sslParameters)
+            throws IOException {
+        super(address, port);
+        this.sslParameters = sslParameters;
+        this.ssl = newSsl(sslParameters, this);
+        activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+    }
+
+    ConscryptFileDescriptorSocket(String hostname, int port, InetAddress clientAddress,
+            int clientPort, SSLParametersImpl sslParameters) throws IOException {
+        super(hostname, port, clientAddress, clientPort);
+        this.sslParameters = sslParameters;
+        this.ssl = newSsl(sslParameters, this);
+        activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+    }
+
+    ConscryptFileDescriptorSocket(InetAddress address, int port, InetAddress clientAddress,
+            int clientPort, SSLParametersImpl sslParameters) throws IOException {
+        super(address, port, clientAddress, clientPort);
+        this.sslParameters = sslParameters;
+        this.ssl = newSsl(sslParameters, this);
+        activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+    }
+
+    ConscryptFileDescriptorSocket(Socket socket, String hostname, int port, boolean autoClose,
+            SSLParametersImpl sslParameters) throws IOException {
+        super(socket, hostname, port, autoClose);
+        this.sslParameters = sslParameters;
+        this.ssl = newSsl(sslParameters, this);
+        activeSession = new ActiveSession(ssl, sslParameters.getSessionContext());
+    }
+
+    private static NativeSsl newSsl(SSLParametersImpl sslParameters,
+            ConscryptFileDescriptorSocket engine) throws SSLException {
+        return NativeSsl.newInstance(sslParameters, engine, engine, engine);
+    }
+
+    /**
+     * Starts a TLS/SSL handshake on this connection using some native methods
+     * from the OpenSSL library. It can negotiate new encryption keys, change
+     * cipher suites, or initiate a new session. The certificate chain is
+     * verified if the correspondent property in java.Security is set. All
+     * listeners are notified at the end of the TLS/SSL handshake.
+     */
+    @Override
+    public final void startHandshake() throws IOException {
+        checkOpen();
+        synchronized (ssl) {
+            if (state == STATE_NEW) {
+                transitionTo(STATE_HANDSHAKE_STARTED);
+            } else {
+                // We've either started the handshake already or have been closed.
+                // Do nothing in both cases.
+                return;
+            }
+        }
+
+        boolean releaseResources = true;
+        try {
+            Platform.closeGuardOpen(guard, "close");
+
+            // Prepare the SSL object for the handshake.
+            ssl.initialize(getHostname(), channelIdPrivateKey);
+
+            // For clients, offer to resume a previously cached session to avoid the
+            // full TLS handshake.
+            if (getUseClientMode()) {
+                NativeSslSession cachedSession = clientSessionContext().getCachedSession(
+                        getHostnameOrIP(), getPort(), sslParameters);
+                if (cachedSession != null) {
+                    cachedSession.offerToResume(ssl);
+                }
+            }
+
+            // Temporarily use a different timeout for the handshake process
+            int savedReadTimeoutMilliseconds = getSoTimeout();
+            int savedWriteTimeoutMilliseconds = getSoWriteTimeout();
+            if (handshakeTimeoutMilliseconds >= 0) {
+                setSoTimeout(handshakeTimeoutMilliseconds);
+                setSoWriteTimeout(handshakeTimeoutMilliseconds);
+            }
+
+            synchronized (ssl) {
+                if (state == STATE_CLOSED) {
+                    return;
+                }
+            }
+
+            try {
+                ssl.doHandshake(Platform.getFileDescriptor(socket), getSoTimeout());
+
+                // Update the session from the current state of the SSL object.
+                activeSession.onPeerCertificateAvailable(getHostnameOrIP(), getPort());
+            } catch (CertificateException e) {
+                SSLHandshakeException wrapper = new SSLHandshakeException(e.getMessage());
+                wrapper.initCause(e);
+                throw wrapper;
+            } catch (SSLException e) {
+                // Swallow this exception if it's thrown as the result of an interruption.
+                //
+                // TODO: SSL_read and SSL_write return -1 when interrupted, but SSL_do_handshake
+                // will throw the last sslError that it saw before sslSelect, usually SSL_WANT_READ
+                // (or WANT_WRITE). Catching that exception here doesn't seem much worse than
+                // changing the native code to return a "special" native pointer value when that
+                // happens.
+                synchronized (ssl) {
+                    if (state == STATE_CLOSED) {
+                        return;
+                    }
+                }
+
+                // Write CCS errors to EventLog
+                String message = e.getMessage();
+                // Must match error string of SSL_R_UNEXPECTED_CCS
+                if (message.contains("unexpected CCS")) {
+                    String logMessage =
+                            String.format("ssl_unexpected_ccs: host=%s", getHostnameOrIP());
+                    Platform.logEvent(logMessage);
+                }
+
+                throw e;
+            }
+
+            synchronized (ssl) {
+                if (state == STATE_CLOSED) {
+                    return;
+                }
+            }
+
+            // Restore the original timeout now that the handshake is complete
+            if (handshakeTimeoutMilliseconds >= 0) {
+                setSoTimeout(savedReadTimeoutMilliseconds);
+                setSoWriteTimeout(savedWriteTimeoutMilliseconds);
+            }
+
+            synchronized (ssl) {
+                releaseResources = (state == STATE_CLOSED);
+
+                if (state == STATE_HANDSHAKE_STARTED) {
+                    transitionTo(STATE_READY_HANDSHAKE_CUT_THROUGH);
+                } else {
+                    transitionTo(STATE_READY);
+                }
+
+                if (!releaseResources) {
+                    // Unblock threads that are waiting for our state to transition
+                    // into STATE_READY or STATE_READY_HANDSHAKE_CUT_THROUGH.
+                    ssl.notifyAll();
+                }
+            }
+        } catch (SSLProtocolException e) {
+            throw(SSLHandshakeException) new SSLHandshakeException("Handshake failed").initCause(e);
+        } finally {
+            // on exceptional exit, treat the socket as closed
+            if (releaseResources) {
+                synchronized (ssl) {
+                    // Mark the socket as closed since we might have reached this as
+                    // a result on an exception thrown by the handshake process.
+                    //
+                    // The state will already be set to closed if we reach this as a result of
+                    // an early return or an interruption due to a concurrent call to close().
+                    transitionTo(STATE_CLOSED);
+                    ssl.notifyAll();
+                }
+
+                try {
+                    shutdownAndFreeSslNative();
+                } catch (IOException ignored) {
+                    // Ignored.
+                }
+            }
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / client_cert_cb
+    public final void clientCertificateRequested(byte[] keyTypeBytes, int[] signatureAlgs,
+            byte[][] asn1DerEncodedPrincipals)
+            throws CertificateEncodingException, SSLException {
+        ssl.chooseClientCertificate(keyTypeBytes, signatureAlgs, asn1DerEncodedPrincipals);
+    }
+
+    @Override
+    @SuppressWarnings("unused") // used by native psk_client_callback
+    public final int clientPSKKeyRequested(String identityHint, byte[] identity, byte[] key) {
+        return ssl.clientPSKKeyRequested(identityHint, identity, key);
+    }
+
+    @Override
+    @SuppressWarnings("unused") // used by native psk_server_callback
+    public final int serverPSKKeyRequested(String identityHint, String identity, byte[] key) {
+        return ssl.serverPSKKeyRequested(identityHint, identity, key);
+    }
+
+    @Override
+    @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / info_callback
+    public final void onSSLStateChange(int type, int val) {
+        if (type != NativeConstants.SSL_CB_HANDSHAKE_DONE) {
+            // We only care about successful completion.
+            return;
+        }
+
+        // The handshake has completed successfully ...
+
+        // First, update the state.
+        synchronized (ssl) {
+            if (state == STATE_CLOSED) {
+                // Someone called "close" but the handshake hasn't been interrupted yet.
+                return;
+            }
+
+            // Now that we've fixed up our state, we can tell waiting threads that
+            // we're ready.
+            transitionTo(STATE_READY);
+        }
+
+        // Let listeners know we are finally done
+        notifyHandshakeCompletedListeners();
+
+        synchronized (ssl) {
+            // Notify all threads waiting for the handshake to complete.
+            ssl.notifyAll();
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / new_session_callback
+    public final void onNewSessionEstablished(long sslSessionNativePtr) {
+        try {
+            // Increment the reference count to "take ownership" of the session resource.
+            NativeCrypto.SSL_SESSION_up_ref(sslSessionNativePtr);
+
+            // Create a native reference which will release the SSL_SESSION in its finalizer.
+            // This constructor will only throw if the native pointer passed in is NULL, which
+            // BoringSSL guarantees will not happen.
+            NativeRef.SSL_SESSION ref = new SSL_SESSION(sslSessionNativePtr);
+
+            NativeSslSession nativeSession = NativeSslSession.newInstance(ref, activeSession);
+
+            // Cache the newly established session.
+            AbstractSessionContext ctx = sessionContext();
+            ctx.cacheSession(nativeSession);
+        } catch (Exception ignored) {
+            // Ignore.
+        }
+    }
+
+    @Override
+    public final long serverSessionRequested(byte[] id) {
+        // TODO(nathanmittler): Implement server-side caching for TLS < 1.3
+        return 0;
+    }
+
+    @Override
+    public final void verifyCertificateChain(byte[][] certChain, String authMethod)
+            throws CertificateException {
+        try {
+            if (certChain == null || certChain.length == 0) {
+                throw new CertificateException("Peer sent no certificate");
+            }
+            X509Certificate[] peerCertChain = SSLUtils.decodeX509CertificateChain(certChain);
+
+            X509TrustManager x509tm = sslParameters.getX509TrustManager();
+            if (x509tm == null) {
+                throw new CertificateException("No X.509 TrustManager");
+            }
+            // Update the peer information on the session.
+            activeSession.onPeerCertificatesReceived(getHostnameOrIP(), getPort(), peerCertChain);
+
+            if (getUseClientMode()) {
+                Platform.checkServerTrusted(x509tm, peerCertChain, authMethod, this);
+            } else {
+                String authType = peerCertChain[0].getPublicKey().getAlgorithm();
+                Platform.checkClientTrusted(x509tm, peerCertChain, authType, this);
+            }
+        } catch (CertificateException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new CertificateException(e);
+        }
+    }
+
+    @Override
+    public final InputStream getInputStream() throws IOException {
+        checkOpen();
+
+        InputStream returnVal;
+        synchronized (ssl) {
+            if (state == STATE_CLOSED) {
+                throw new SocketException("Socket is closed.");
+            }
+
+            if (is == null) {
+                is = new SSLInputStream();
+            }
+
+            returnVal = is;
+        }
+
+        // Block waiting for a handshake without a lock held. It's possible that the socket
+        // is closed at this point. If that happens, we'll still return the input stream but
+        // all reads on it will throw.
+        waitForHandshake();
+        return returnVal;
+    }
+
+    @Override
+    public final OutputStream getOutputStream() throws IOException {
+        checkOpen();
+
+        OutputStream returnVal;
+        synchronized (ssl) {
+            if (state == STATE_CLOSED) {
+                throw new SocketException("Socket is closed.");
+            }
+
+            if (os == null) {
+                os = new SSLOutputStream();
+            }
+
+            returnVal = os;
+        }
+
+        // Block waiting for a handshake without a lock held. It's possible that the socket
+        // is closed at this point. If that happens, we'll still return the output stream but
+        // all writes on it will throw.
+        waitForHandshake();
+        return returnVal;
+    }
+
+    private void assertReadableOrWriteableState() {
+        if (state == STATE_READY || state == STATE_READY_HANDSHAKE_CUT_THROUGH) {
+            return;
+        }
+
+        throw new AssertionError("Invalid state: " + state);
+    }
+
+    private void waitForHandshake() throws IOException {
+        startHandshake();
+
+        synchronized (ssl) {
+            while (state != STATE_READY &&
+                    state != STATE_READY_HANDSHAKE_CUT_THROUGH &&
+                    state != STATE_CLOSED) {
+                try {
+                    ssl.wait();
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    throw new IOException("Interrupted waiting for handshake", e);
+                }
+            }
+
+            if (state == STATE_CLOSED) {
+                throw new SocketException("Socket is closed");
+            }
+        }
+    }
+
+    /**
+     * This inner class provides input data stream functionality
+     * for the OpenSSL native implementation. It is used to
+     * read data received via SSL protocol.
+     */
+    private class SSLInputStream extends InputStream {
+        /**
+         * OpenSSL only lets one thread read at a time, so this is used to
+         * make sure we serialize callers of SSL_read. Thread is already
+         * expected to have completed handshaking.
+         */
+        private final Object readLock = new Object();
+
+        SSLInputStream() {
+        }
+
+        /**
+         * Reads one byte. If there is no data in the underlying buffer,
+         * this operation can block until the data will be
+         * available.
+         */
+        @Override
+        public int read() throws IOException {
+            byte[] buffer = new byte[1];
+            int result = read(buffer, 0, 1);
+            return (result != -1) ? buffer[0] & 0xff : -1;
+        }
+
+        /**
+         * Method acts as described in spec for superclass.
+         * @see java.io.InputStream#read(byte[],int,int)
+         */
+        @Override
+        public int read(byte[] buf, int offset, int byteCount) throws IOException {
+            Platform.blockGuardOnNetwork();
+
+            checkOpen();
+            ArrayUtils.checkOffsetAndCount(buf.length, offset, byteCount);
+            if (byteCount == 0) {
+                return 0;
+            }
+
+            synchronized (readLock) {
+                synchronized (ssl) {
+                    if (state == STATE_CLOSED) {
+                        throw new SocketException("socket is closed");
+                    }
+
+                    if (DBG_STATE) {
+                        assertReadableOrWriteableState();
+                    }
+                }
+
+                int ret =  ssl.read(
+                        Platform.getFileDescriptor(socket), buf, offset, byteCount, getSoTimeout());
+                if (ret == -1) {
+                    synchronized (ssl) {
+                        if (state == STATE_CLOSED) {
+                            throw new SocketException("socket is closed");
+                        }
+                    }
+                }
+                return ret;
+            }
+        }
+
+        void awaitPendingOps() {
+            if (DBG_STATE) {
+                synchronized (ssl) {
+                    if (state != STATE_CLOSED) {
+                        throw new AssertionError("State is: " + state);
+                    }
+                }
+            }
+
+            synchronized (readLock) {}
+        }
+    }
+
+    /**
+     * This inner class provides output data stream functionality
+     * for the OpenSSL native implementation. It is used to
+     * write data according to the encryption parameters given in SSL context.
+     */
+    private class SSLOutputStream extends OutputStream {
+        /**
+         * OpenSSL only lets one thread write at a time, so this is used
+         * to make sure we serialize callers of SSL_write. Thread is
+         * already expected to have completed handshaking.
+         */
+        private final Object writeLock = new Object();
+
+        SSLOutputStream() {
+        }
+
+        /**
+         * Method acts as described in spec for superclass.
+         * @see java.io.OutputStream#write(int)
+         */
+        @Override
+        public void write(int oneByte) throws IOException {
+            byte[] buffer = new byte[1];
+            buffer[0] = (byte) (oneByte & 0xff);
+            write(buffer);
+        }
+
+        /**
+         * Method acts as described in spec for superclass.
+         * @see java.io.OutputStream#write(byte[],int,int)
+         */
+        @Override
+        public void write(byte[] buf, int offset, int byteCount) throws IOException {
+            Platform.blockGuardOnNetwork();
+            checkOpen();
+            ArrayUtils.checkOffsetAndCount(buf.length, offset, byteCount);
+            if (byteCount == 0) {
+                return;
+            }
+
+            synchronized (writeLock) {
+                synchronized (ssl) {
+                    if (state == STATE_CLOSED) {
+                        throw new SocketException("socket is closed");
+                    }
+
+                    if (DBG_STATE) {
+                        assertReadableOrWriteableState();
+                    }
+                }
+
+                ssl.write(Platform.getFileDescriptor(socket), buf, offset, byteCount,
+                        writeTimeoutMilliseconds);
+
+                synchronized (ssl) {
+                    if (state == STATE_CLOSED) {
+                        throw new SocketException("socket is closed");
+                    }
+                }
+            }
+        }
+
+        void awaitPendingOps() {
+            if (DBG_STATE) {
+                synchronized (ssl) {
+                    if (state != STATE_CLOSED) {
+                        throw new AssertionError("State is: " + state);
+                    }
+                }
+            }
+
+            synchronized (writeLock) {}
+        }
+    }
+
+    @Override
+    public final SSLSession getSession() {
+        return externalSession;
+    }
+
+    private ConscryptSession provideSession() {
+        boolean handshakeCompleted = false;
+        synchronized (ssl) {
+            if (state == STATE_CLOSED) {
+                return closedSession != null ? closedSession : SSLNullSession.getNullSession();
+            }
+
+            try {
+                handshakeCompleted = state >= STATE_READY;
+                if (!handshakeCompleted && isConnected()) {
+                    waitForHandshake();
+                    handshakeCompleted = true;
+                }
+            } catch (IOException e) {
+                // Fall through.
+            }
+        }
+
+        if (!handshakeCompleted) {
+            // return an invalid session with
+            // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
+            return SSLNullSession.getNullSession();
+        }
+
+        return activeSession;
+    }
+
+    private ConscryptSession provideHandshakeSession() {
+        synchronized (ssl) {
+            return state >= STATE_HANDSHAKE_STARTED && state < STATE_READY ? activeSession
+                : SSLNullSession.getNullSession();
+        }
+    }
+
+    @Override
+    final SSLSession getActiveSession() {
+        return activeSession;
+    }
+
+    @Override
+    public final SSLSession getHandshakeSession() {
+        synchronized (ssl) {
+            if (state >= STATE_HANDSHAKE_STARTED && state < STATE_READY) {
+                return Platform.wrapSSLSession(new ExternalSession(new Provider() {
+                    @Override
+                    public ConscryptSession provideSession() {
+                        return ConscryptFileDescriptorSocket.this.provideHandshakeSession();
+                    }
+                }));
+            }
+            return null;
+        }
+    }
+
+    @Override
+    public final boolean getEnableSessionCreation() {
+        return sslParameters.getEnableSessionCreation();
+    }
+
+    @Override
+    public final void setEnableSessionCreation(boolean flag) {
+        sslParameters.setEnableSessionCreation(flag);
+    }
+
+    @Override
+    public final String[] getSupportedCipherSuites() {
+        return NativeCrypto.getSupportedCipherSuites();
+    }
+
+    @Override
+    public final String[] getEnabledCipherSuites() {
+        return sslParameters.getEnabledCipherSuites();
+    }
+
+    @Override
+    public final void setEnabledCipherSuites(String[] suites) {
+        sslParameters.setEnabledCipherSuites(suites);
+    }
+
+    @Override
+    public final String[] getSupportedProtocols() {
+        return NativeCrypto.getSupportedProtocols();
+    }
+
+    @Override
+    public final String[] getEnabledProtocols() {
+        return sslParameters.getEnabledProtocols();
+    }
+
+    @Override
+    public final void setEnabledProtocols(String[] protocols) {
+        sslParameters.setEnabledProtocols(protocols);
+    }
+
+    /**
+     * This method enables session ticket support.
+     *
+     * @param useSessionTickets True to enable session tickets
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @Override
+    public final void setUseSessionTickets(boolean useSessionTickets) {
+        sslParameters.setUseSessionTickets(useSessionTickets);
+    }
+
+    /**
+     * This method enables Server Name Indication.  If the hostname is not a valid SNI hostname,
+     * the SNI extension will be omitted from the handshake.
+     *
+     * @param hostname the desired SNI hostname, or null to disable
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @Override
+    public final void setHostname(String hostname) {
+        sslParameters.setUseSni(hostname != null);
+        super.setHostname(hostname);
+    }
+
+    /**
+     * Enables/disables TLS Channel ID for this server socket.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @throws IllegalStateException if this is a client socket or if the handshake has already
+     *         started.
+     */
+    @Override
+    public final void setChannelIdEnabled(boolean enabled) {
+        if (getUseClientMode()) {
+            throw new IllegalStateException("Client mode");
+        }
+
+        synchronized (ssl) {
+            if (state != STATE_NEW) {
+                throw new IllegalStateException(
+                        "Could not enable/disable Channel ID after the initial handshake has"
+                                + " begun.");
+            }
+        }
+        sslParameters.channelIdEnabled = enabled;
+    }
+
+    /**
+     * Gets the TLS Channel ID for this server socket. Channel ID is only available once the
+     * handshake completes.
+     *
+     * @return channel ID or {@code null} if not available.
+     *
+     * @throws IllegalStateException if this is a client socket or if the handshake has not yet
+     *         completed.
+     * @throws SSLException if channel ID is available but could not be obtained.
+     */
+    @Override
+    public final byte[] getChannelId() throws SSLException {
+        if (getUseClientMode()) {
+            throw new IllegalStateException("Client mode");
+        }
+
+        synchronized (ssl) {
+            if (state != STATE_READY) {
+                throw new IllegalStateException(
+                        "Channel ID is only available after handshake completes");
+            }
+        }
+        return ssl.getTlsChannelId();
+    }
+
+    /**
+     * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client socket.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key (disables
+     *        TLS Channel ID). The private key must be an Elliptic Curve (EC) key based on the NIST
+     *        P-256 curve (aka SECG secp256r1 or ANSI X9.62 prime256v1).
+     *
+     * @throws IllegalStateException if this is a server socket or if the handshake has already
+     *         started.
+     */
+    @Override
+    public final void setChannelIdPrivateKey(PrivateKey privateKey) {
+        if (!getUseClientMode()) {
+            throw new IllegalStateException("Server mode");
+        }
+
+        synchronized (ssl) {
+            if (state != STATE_NEW) {
+                throw new IllegalStateException(
+                        "Could not change Channel ID private key after the initial handshake has"
+                                + " begun.");
+            }
+        }
+
+        if (privateKey == null) {
+            sslParameters.channelIdEnabled = false;
+            channelIdPrivateKey = null;
+        } else {
+            sslParameters.channelIdEnabled = true;
+            try {
+                ECParameterSpec ecParams = null;
+                if (privateKey instanceof ECKey) {
+                    ecParams = ((ECKey) privateKey).getParams();
+                }
+                if (ecParams == null) {
+                    // Assume this is a P-256 key, as specified in the contract of this method.
+                    ecParams =
+                            OpenSSLECGroupContext.getCurveByName("prime256v1").getECParameterSpec();
+                }
+                channelIdPrivateKey =
+                        OpenSSLKey.fromECPrivateKeyForTLSStackOnly(privateKey, ecParams);
+            } catch (InvalidKeyException e) {
+                // Will have error in startHandshake
+            }
+        }
+    }
+
+    @Override
+    byte[] getTlsUnique() {
+        return ssl.getTlsUnique();
+    }
+
+    @Override
+    byte[] exportKeyingMaterial(String label, byte[] context, int length) throws SSLException {
+        synchronized (ssl) {
+            if (state < STATE_HANDSHAKE_COMPLETED || state == STATE_CLOSED) {
+                return null;
+            }
+        }
+        return ssl.exportKeyingMaterial(label, context, length);
+    }
+
+    @Override
+    public final boolean getUseClientMode() {
+        return sslParameters.getUseClientMode();
+    }
+
+    @Override
+    public final void setUseClientMode(boolean mode) {
+        synchronized (ssl) {
+            if (state != STATE_NEW) {
+                throw new IllegalArgumentException(
+                        "Could not change the mode after the initial handshake has begun.");
+            }
+        }
+        sslParameters.setUseClientMode(mode);
+    }
+
+    @Override
+    public final boolean getWantClientAuth() {
+        return sslParameters.getWantClientAuth();
+    }
+
+    @Override
+    public final boolean getNeedClientAuth() {
+        return sslParameters.getNeedClientAuth();
+    }
+
+    @Override
+    public final void setNeedClientAuth(boolean need) {
+        sslParameters.setNeedClientAuth(need);
+    }
+
+    @Override
+    public final void setWantClientAuth(boolean want) {
+        sslParameters.setWantClientAuth(want);
+    }
+
+    /**
+     * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
+     */
+    @Override
+    public final void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
+        this.writeTimeoutMilliseconds = writeTimeoutMilliseconds;
+
+        Platform.setSocketWriteTimeout(this, writeTimeoutMilliseconds);
+    }
+
+    /**
+     * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
+     */
+    @Override
+    public final int getSoWriteTimeout() throws SocketException {
+        return writeTimeoutMilliseconds;
+    }
+
+    /**
+     * Set the handshake timeout on this socket.  This timeout is specified in
+     * milliseconds and will be used only during the handshake process.
+     */
+    @Override
+    public final void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
+        this.handshakeTimeoutMilliseconds = handshakeTimeoutMilliseconds;
+    }
+
+    @Override
+    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
+    public final void close() throws IOException {
+        // TODO: Close SSL sockets using a background thread so they close gracefully.
+
+        SSLInputStream sslInputStream;
+        SSLOutputStream sslOutputStream;
+
+        if (ssl == null) {
+            // close() has been called before we've initialized the socket, so just
+            // return.
+            return;
+        }
+
+        synchronized (ssl) {
+            if (state == STATE_CLOSED) {
+                // close() has already been called, so do nothing and return.
+                return;
+            }
+
+            int oldState = state;
+            transitionTo(STATE_CLOSED);
+
+            if (oldState == STATE_NEW) {
+                // The handshake hasn't been started yet, so there's no OpenSSL related
+                // state to clean up. We still need to close the underlying socket if
+                // we're wrapping it and were asked to autoClose.
+                free();
+                closeUnderlyingSocket();
+
+                ssl.notifyAll();
+                return;
+            }
+
+            if (oldState != STATE_READY && oldState != STATE_READY_HANDSHAKE_CUT_THROUGH) {
+                // If we're in these states, we still haven't returned from startHandshake.
+                // We call SSL_interrupt so that we can interrupt SSL_do_handshake and then
+                // set the state to STATE_CLOSED. startHandshake will handle all cleanup
+                // after SSL_do_handshake returns, so we don't have anything to do here.
+                ssl.interrupt();
+
+                ssl.notifyAll();
+                return;
+            }
+
+            ssl.notifyAll();
+            // We've already returned from startHandshake, so we potentially have
+            // input and output streams to clean up.
+            sslInputStream = is;
+            sslOutputStream = os;
+        }
+
+        // Don't bother interrupting unless we have something to interrupt.
+        if (sslInputStream != null || sslOutputStream != null) {
+            ssl.interrupt();
+        }
+
+        // Wait for the input and output streams to finish any reads they have in
+        // progress. If there are no reads in progress at this point, future reads will
+        // throw because state == STATE_CLOSED
+        if (sslInputStream != null) {
+            sslInputStream.awaitPendingOps();
+        }
+        if (sslOutputStream != null) {
+            sslOutputStream.awaitPendingOps();
+        }
+
+        shutdownAndFreeSslNative();
+    }
+
+    private void shutdownAndFreeSslNative() throws IOException {
+        try {
+            Platform.blockGuardOnNetwork();
+            ssl.shutdown(Platform.getFileDescriptor(socket));
+        } catch (IOException ignored) {
+            /*
+             * Note that although close() can throw
+             * IOException, the RI does not throw if there
+             * is problem sending a "close notify" which
+             * can happen if the underlying socket is closed.
+             */
+        } finally {
+            free();
+            closeUnderlyingSocket();
+        }
+    }
+
+    private void closeUnderlyingSocket() throws IOException {
+        super.close();
+    }
+
+    private void free() {
+        if (!ssl.isClosed()) {
+            ssl.close();
+            Platform.closeGuardClose(guard);
+        }
+    }
+
+    @Override
+    protected final void finalize() throws Throwable {
+        try {
+            /*
+             * Just worry about our own state. Notably we do not try and
+             * close anything. The SocketImpl, either our own
+             * PlainSocketImpl, or the Socket we are wrapping, will do
+             * that. This might mean we do not properly SSL_shutdown, but
+             * if you want to do that, properly close the socket yourself.
+             *
+             * The reason why we don't try to SSL_shutdown, is that there
+             * can be a race between finalizers where the PlainSocketImpl
+             * finalizer runs first and closes the socket. However, in the
+             * meanwhile, the underlying file descriptor could be reused
+             * for another purpose. If we call SSL_shutdown, the
+             * underlying socket BIOs still have the old file descriptor
+             * and will write the close notify to some unsuspecting
+             * reader.
+             */
+            if (guard != null) {
+                Platform.closeGuardWarnIfOpen(guard);
+            }
+            if (ssl != null) {
+                synchronized (ssl) {
+                    transitionTo(STATE_CLOSED);
+                }
+            }
+        } finally {
+            super.finalize();
+        }
+
+    }
+
+    @Override
+    public final void setApplicationProtocolSelector(ApplicationProtocolSelector selector) {
+        setApplicationProtocolSelector(
+                selector == null ? null : new ApplicationProtocolSelectorAdapter(this, selector));
+    }
+
+    @Override
+    final void setApplicationProtocolSelector(ApplicationProtocolSelectorAdapter selector) {
+        sslParameters.setApplicationProtocolSelector(selector);
+    }
+
+    @Override
+    final void setApplicationProtocols(String[] protocols) {
+        sslParameters.setApplicationProtocols(protocols);
+    }
+
+    @Override
+    final String[] getApplicationProtocols() {
+        return sslParameters.getApplicationProtocols();
+    }
+
+    @Override
+    public final String getApplicationProtocol() {
+        return SSLUtils.toProtocolString(ssl.getApplicationProtocol());
+    }
+
+    @Override
+    public final String getHandshakeApplicationProtocol() {
+        synchronized (ssl) {
+            return state >= STATE_HANDSHAKE_STARTED && state < STATE_READY
+                ? getApplicationProtocol() : null;
+        }
+    }
+
+    @Override
+    public final SSLParameters getSSLParameters() {
+        SSLParameters params = super.getSSLParameters();
+        Platform.getSSLParameters(params, sslParameters, this);
+        return params;
+    }
+
+    @Override
+    public final void setSSLParameters(SSLParameters p) {
+        super.setSSLParameters(p);
+        Platform.setSSLParameters(p, sslParameters, this);
+    }
+
+    @Override
+    public final String chooseServerAlias(X509KeyManager keyManager, String keyType) {
+        return keyManager.chooseServerAlias(keyType, null, this);
+    }
+
+    @Override
+    public final String chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers,
+            String[] keyTypes) {
+        return keyManager.chooseClientAlias(keyTypes, issuers, this);
+    }
+
+    @Override
+    @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+    public final String chooseServerPSKIdentityHint(PSKKeyManager keyManager) {
+        return keyManager.chooseServerKeyIdentityHint(this);
+    }
+
+    @Override
+    @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+    public final String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint) {
+        return keyManager.chooseClientKeyIdentity(identityHint, this);
+    }
+
+    @Override
+    @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+    public final SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity) {
+        return keyManager.getKey(identityHint, identity, this);
+    }
+
+    private ClientSessionContext clientSessionContext() {
+        return sslParameters.getClientSessionContext();
+    }
+
+    private AbstractSessionContext sessionContext() {
+        return sslParameters.getSessionContext();
+    }
+
+    private void transitionTo(int newState) {
+        switch (newState) {
+            case STATE_CLOSED: {
+                if (!ssl.isClosed() && state >= STATE_HANDSHAKE_STARTED && state < STATE_CLOSED ) {
+                    closedSession = new SessionSnapshot(activeSession);
+                }
+                break;
+            }
+            default: {
+                break;
+            }
+        }
+
+        // Update the state
+        this.state = newState;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptHostnameVerifier.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptHostnameVerifier.java
new file mode 100644
index 0000000..a62cf9f
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptHostnameVerifier.java
@@ -0,0 +1,36 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import javax.net.ssl.SSLSession;
+
+/**
+ * This interface is used to implement hostname verification in Conscrypt.  Unlike with
+ * {@link javax.net.ssl.HostnameVerifier}, the hostname verifier is called whenever hostname
+ * verification is needed, without any use of default rules.
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface ConscryptHostnameVerifier {
+
+  /**
+   * Returns whether the given hostname is allowable given the peer's authentication information
+   * from the given session.
+   */
+  boolean verify(String hostname, SSLSession session);
+
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptServerSocket.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptServerSocket.java
new file mode 100644
index 0000000..c667e8b
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptServerSocket.java
@@ -0,0 +1,190 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import javax.net.ssl.SSLServerSocket;
+
+/**
+ * BoringSSL-based implementation of server sockets.
+ */
+final class ConscryptServerSocket extends SSLServerSocket {
+    private final SSLParametersImpl sslParameters;
+    private boolean channelIdEnabled;
+    private boolean useEngineSocket;
+
+    ConscryptServerSocket(SSLParametersImpl sslParameters) throws IOException {
+        this.sslParameters = sslParameters;
+    }
+
+    ConscryptServerSocket(int port, SSLParametersImpl sslParameters)
+        throws IOException {
+        super(port);
+        this.sslParameters = sslParameters;
+    }
+
+    ConscryptServerSocket(int port, int backlog, SSLParametersImpl sslParameters)
+        throws IOException {
+        super(port, backlog);
+        this.sslParameters = sslParameters;
+    }
+
+    ConscryptServerSocket(int port,
+                                      int backlog,
+                                      InetAddress iAddress,
+                                      SSLParametersImpl sslParameters)
+        throws IOException {
+        super(port, backlog, iAddress);
+        this.sslParameters = sslParameters;
+    }
+
+    /**
+     * Configures the socket to be created for this instance.
+     */
+    ConscryptServerSocket setUseEngineSocket(boolean useEngineSocket) {
+        this.useEngineSocket = useEngineSocket;
+        return this;
+    }
+
+    @Override
+    public boolean getEnableSessionCreation() {
+        return sslParameters.getEnableSessionCreation();
+    }
+
+    @Override
+    public void setEnableSessionCreation(boolean flag) {
+        sslParameters.setEnableSessionCreation(flag);
+    }
+
+    /**
+     * The names of the protocols' versions that may be used on this SSL
+     * connection.
+     * @return an array of protocols names
+     */
+    @Override
+    public String[] getSupportedProtocols() {
+        return NativeCrypto.getSupportedProtocols();
+    }
+
+    /**
+     * The names of the protocols' versions that in use on this SSL connection.
+     *
+     * @return an array of protocols names
+     */
+    @Override
+    public String[] getEnabledProtocols() {
+        return sslParameters.getEnabledProtocols();
+    }
+
+    /**
+     * This method enables the protocols' versions listed by
+     * getSupportedProtocols().
+     *
+     * @param protocols names of all the protocols to enable.
+     *
+     * @throws IllegalArgumentException when one or more of the names in the
+     *             array are not supported, or when the array is null.
+     */
+    @Override
+    public void setEnabledProtocols(String[] protocols) {
+        sslParameters.setEnabledProtocols(protocols);
+    }
+
+    @Override
+    public String[] getSupportedCipherSuites() {
+        return NativeCrypto.getSupportedCipherSuites();
+    }
+
+    @Override
+    public String[] getEnabledCipherSuites() {
+        return sslParameters.getEnabledCipherSuites();
+    }
+
+    /**
+     * Enables/disables the TLS Channel ID extension for this server socket.
+     */
+    void setChannelIdEnabled(boolean enabled) {
+      channelIdEnabled = enabled;
+    }
+
+    /**
+     * Checks whether the TLS Channel ID extension is enabled for this server socket.
+     */
+    boolean isChannelIdEnabled() {
+      return channelIdEnabled;
+    }
+
+    /**
+     * This method enables the cipher suites listed by
+     * getSupportedCipherSuites().
+     *
+     * @param suites the names of all the cipher suites to enable
+     * @throws IllegalArgumentException when one or more of the ciphers in array
+     *         suites are not supported, or when the array is null.
+     */
+    @Override
+    public void setEnabledCipherSuites(String[] suites) {
+        sslParameters.setEnabledCipherSuites(suites);
+    }
+
+    @Override
+    public boolean getWantClientAuth() {
+        return sslParameters.getWantClientAuth();
+    }
+
+    @Override
+    public void setWantClientAuth(boolean want) {
+        sslParameters.setWantClientAuth(want);
+    }
+
+    @Override
+    public boolean getNeedClientAuth() {
+        return sslParameters.getNeedClientAuth();
+    }
+
+    @Override
+    public void setNeedClientAuth(boolean need) {
+        sslParameters.setNeedClientAuth(need);
+    }
+
+    @Override
+    public void setUseClientMode(boolean mode) {
+        sslParameters.setUseClientMode(mode);
+    }
+
+    @Override
+    public boolean getUseClientMode() {
+        return sslParameters.getUseClientMode();
+    }
+
+    @Override
+    public Socket accept() throws IOException {
+        final AbstractConscryptSocket socket;
+        if (useEngineSocket) {
+            socket = Platform.createEngineSocket(sslParameters);
+        } else {
+            socket = Platform.createFileDescriptorSocket(sslParameters);
+        }
+
+        socket.setChannelIdEnabled(channelIdEnabled);
+        implAccept(socket);
+        return socket;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptSession.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptSession.java
new file mode 100644
index 0000000..c4732f1
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ConscryptSession.java
@@ -0,0 +1,56 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.cert.X509Certificate;
+import java.util.List;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+
+/**
+ * Extends the default interface for {@link SSLSession} to provide additional properties exposed
+ * by Conscrypt.
+ */
+interface ConscryptSession extends SSLSession {
+
+  String getRequestedServerName();
+
+  /**
+   * Returns the OCSP stapled response. Returns a copy of the internal arrays.
+   *
+   * The method signature matches
+   * <a
+   * href="http://download.java.net/java/jdk9/docs/api/javax/net/ssl/ExtendedSSLSession.html#getStatusResponses--">Java
+   * 9</a>.
+   *
+   * @see <a href="https://tools.ietf.org/html/rfc6066">RFC 6066</a>
+   * @see <a href="https://tools.ietf.org/html/rfc6961">RFC 6961</a>
+   */
+  List<byte[]> getStatusResponses();
+
+  /**
+   * Returns the signed certificate timestamp (SCT) received from the peer. Returns a
+   * copy of the internal array.
+   *
+   * @see <a href="https://tools.ietf.org/html/rfc6962">RFC 6962</a>
+   */
+  byte[] getPeerSignedCertificateTimestamp();
+
+  @Override
+  X509Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException;
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/CryptoUpcalls.java b/repackaged/common/src/main/java/com/android/org/conscrypt/CryptoUpcalls.java
new file mode 100644
index 0000000..7e2a4e9
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/CryptoUpcalls.java
@@ -0,0 +1,230 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.Security;
+import java.security.Signature;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+
+/**
+ * Provides a place where NativeCrypto can call back up to do Java language
+ * calls to work on delegated key types from native code. Delegated keys are
+ * usually backed by hardware so we don't have access directly to the private
+ * key material. If it were a key where we can get to the private key, we
+ * would not ever call into this class.
+ */
+final class CryptoUpcalls {
+    private static final Logger logger = Logger.getLogger(CryptoUpcalls.class.getName());
+
+    private CryptoUpcalls() {}
+
+    /**
+     * Finds providers that are not us that provide the requested algorithms.
+     */
+    private static ArrayList<Provider> getExternalProviders(String algorithm) {
+        ArrayList<Provider> providers = new ArrayList<Provider>(1);
+        for (Provider p : Security.getProviders(algorithm)) {
+            if (!Conscrypt.isConscrypt(p)) {
+                providers.add(p);
+            }
+        }
+        if (providers.isEmpty()) {
+            logger.warning("Could not find external provider for algorithm: " + algorithm);
+        }
+        return providers;
+    }
+
+    static byte[] ecSignDigestWithPrivateKey(PrivateKey javaKey, byte[] message) {
+        // Hint: Algorithm names come from:
+        // http://docs.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html
+        String keyAlgorithm = javaKey.getAlgorithm();
+        if (!"EC".equals(keyAlgorithm)) {
+            throw new RuntimeException("Unexpected key type: " + javaKey.toString());
+        }
+
+        return signDigestWithPrivateKey(javaKey, message, "NONEwithECDSA");
+    }
+
+    private static byte[] signDigestWithPrivateKey(PrivateKey javaKey, byte[] message,
+            String algorithm) {
+        Signature signature;
+
+        // Since this is a delegated key, we cannot handle providing a signature using this key.
+        // Otherwise we wouldn't end up in this class in the first place. The first step is to
+        // try to get the most preferred provider as long as it isn't us.
+        try {
+            signature = Signature.getInstance(algorithm);
+            signature.initSign(javaKey);
+
+            // Ignore it if it points back to us.
+            if (Conscrypt.isConscrypt(signature.getProvider())) {
+                signature = null;
+            }
+        } catch (NoSuchAlgorithmException e) {
+            logger.warning("Unsupported signature algorithm: " + algorithm);
+            return null;
+        } catch (InvalidKeyException e) {
+            logger.warning("Preferred provider doesn't support key:");
+            e.printStackTrace();
+            signature = null;
+        }
+
+        // If the preferred provider was us, fall back to trying to find the
+        // first not-us provider that initializes correctly.
+        if (signature == null) {
+            ArrayList<Provider> providers = getExternalProviders("Signature." + algorithm);
+            RuntimeException savedRuntimeException = null;
+            for (Provider p : providers) {
+                try {
+                    signature = Signature.getInstance(algorithm, p);
+                    signature.initSign(javaKey);
+                    break;
+                } catch (NoSuchAlgorithmException | InvalidKeyException e) {
+                    signature = null;
+                } catch (RuntimeException e) {
+                    signature = null;
+                    if (savedRuntimeException == null) {
+                        savedRuntimeException = e;
+                    }
+                }
+            }
+            if (signature == null) {
+                if (savedRuntimeException != null) {
+                    throw savedRuntimeException;
+                }
+                logger.warning("Could not find provider for algorithm: " + algorithm);
+                return null;
+            }
+        }
+
+        // Sign the message.
+        try {
+            signature.update(message);
+            return signature.sign();
+        } catch (Exception e) {
+            logger.log(Level.WARNING,
+                    "Exception while signing message with " + javaKey.getAlgorithm()
+                            + " private key:",
+                    e);
+            return null;
+        }
+    }
+
+    static byte[] rsaSignDigestWithPrivateKey(PrivateKey javaKey, int openSSLPadding,
+            byte[] message) {
+        // An RSA cipher + ENCRYPT_MODE produces a standard RSA signature
+        return rsaOpWithPrivateKey(javaKey, openSSLPadding, Cipher.ENCRYPT_MODE, message);
+    }
+
+    static byte[] rsaDecryptWithPrivateKey(PrivateKey javaKey, int openSSLPadding, byte[] input) {
+        return rsaOpWithPrivateKey(javaKey, openSSLPadding, Cipher.DECRYPT_MODE, input);
+    }
+
+    private static byte[] rsaOpWithPrivateKey(PrivateKey javaKey, int openSSLPadding,
+            int cipherMode, byte[] input) {
+        String keyAlgorithm = javaKey.getAlgorithm();
+        if (!"RSA".equals(keyAlgorithm)) {
+            logger.warning("Unexpected key type: " + keyAlgorithm);
+            return null;
+        }
+
+        String jcaPadding;
+        switch (openSSLPadding) {
+            case NativeConstants.RSA_PKCS1_PADDING:
+                // Since we're using this with a private key, this will produce RSASSA-PKCS1-v1_5
+                // (signature) padding rather than RSAES-PKCS1-v1_5 (encryption) padding
+                jcaPadding = "PKCS1Padding";
+                break;
+            case NativeConstants.RSA_NO_PADDING:
+                jcaPadding = "NoPadding";
+                break;
+            case NativeConstants.RSA_PKCS1_OAEP_PADDING:
+                jcaPadding = "OAEPPadding";
+                break;
+            default:
+                logger.warning("Unsupported OpenSSL/BoringSSL padding: " + openSSLPadding);
+                return null;
+        }
+
+        String transformation = "RSA/ECB/" + jcaPadding;
+        Cipher c = null;
+
+        // Since this is a delegated key, we cannot handle providing a cipher using this key.
+        // Otherwise we wouldn't end up in this class in the first place. The first step is to
+        // try to get the most preferred provider as long as it isn't us.
+        try {
+            c = Cipher.getInstance(transformation);
+            c.init(cipherMode, javaKey);
+
+            // Ignore it if it points back to us.
+            if (Conscrypt.isConscrypt(c.getProvider())) {
+                c = null;
+            }
+        } catch (NoSuchAlgorithmException e) {
+            logger.warning("Unsupported cipher algorithm: " + transformation);
+            return null;
+        } catch (NoSuchPaddingException e) {
+            logger.warning("Unsupported cipher algorithm: " + transformation);
+            return null;
+        } catch (InvalidKeyException e) {
+            logger.log(Level.WARNING, "Preferred provider doesn't support key:", e);
+            c = null;
+        }
+
+        // If the preferred provider was us, fall back to trying to find the
+        // first not-us provider that initializes correctly.
+        if (c == null) {
+            ArrayList<Provider> providers = getExternalProviders("Cipher." + transformation);
+            for (Provider p : providers) {
+                try {
+                    c = Cipher.getInstance(transformation, p);
+                    c.init(cipherMode, javaKey);
+                    break;
+                } catch (NoSuchAlgorithmException e) {
+                    c = null;
+                } catch (InvalidKeyException e) {
+                    c = null;
+                } catch (NoSuchPaddingException e) {
+                    c = null;
+                }
+            }
+            if (c == null) {
+                logger.warning("Could not find provider for algorithm: " + transformation);
+                return null;
+            }
+        }
+
+        try {
+            return c.doFinal(input);
+        } catch (Exception e) {
+            logger.log(Level.WARNING,
+                    "Exception while decrypting message with " + javaKey.getAlgorithm()
+                            + " private key using " + transformation + ":",
+                    e);
+            return null;
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/DESEDESecretKeyFactory.java b/repackaged/common/src/main/java/com/android/org/conscrypt/DESEDESecretKeyFactory.java
new file mode 100644
index 0000000..2fce21f
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/DESEDESecretKeyFactory.java
@@ -0,0 +1,102 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.InvalidKeyException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactorySpi;
+import javax.crypto.spec.DESedeKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * An implementation of {@link javax.crypto.SecretKeyFactory} for use with DESEDE keys.  This
+ * class supports {@link SecretKeySpec} and {@link DESedeKeySpec} for key specs.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public class DESEDESecretKeyFactory extends SecretKeyFactorySpi {
+
+    @libcore.api.IntraCoreApi
+    public DESEDESecretKeyFactory() {}
+
+    @Override
+    protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException {
+        if (keySpec == null) {
+            throw new InvalidKeySpecException("Null KeySpec");
+        }
+        if (keySpec instanceof SecretKeySpec) {
+            SecretKeySpec key = (SecretKeySpec) keySpec;
+            try {
+                if (!DESedeKeySpec.isParityAdjusted(key.getEncoded(), 0)) {
+                    throw new InvalidKeySpecException(
+                            "SecretKeySpec is not a parity-adjusted DESEDE key");
+                }
+            } catch (InvalidKeyException e) {
+                throw new InvalidKeySpecException(e);
+            }
+            return key;
+        } else if (keySpec instanceof DESedeKeySpec) {
+            DESedeKeySpec desKeySpec = (DESedeKeySpec) keySpec;
+            return new SecretKeySpec(desKeySpec.getKey(), "DESEDE");
+        } else {
+            throw new InvalidKeySpecException(
+                    "Unsupported KeySpec class: " + keySpec.getClass().getName());
+        }
+    }
+
+    @Override
+    protected KeySpec engineGetKeySpec(SecretKey secretKey,
+            @SuppressWarnings("rawtypes") Class aClass) throws InvalidKeySpecException {
+        if (secretKey == null) {
+            throw new InvalidKeySpecException("Null SecretKey");
+        }
+        if (aClass == SecretKeySpec.class) {
+            try {
+                if (!DESedeKeySpec.isParityAdjusted(secretKey.getEncoded(), 0)) {
+                    throw new InvalidKeySpecException("SecretKey is not a parity-adjusted DESEDE key");
+                }
+            } catch (InvalidKeyException e) {
+                throw new InvalidKeySpecException(e);
+            }
+            if (secretKey instanceof SecretKeySpec) {
+                return (KeySpec) secretKey;
+            } else {
+                return new SecretKeySpec(secretKey.getEncoded(), "DESEDE");
+            }
+        } else if (aClass == DESedeKeySpec.class) {
+            try {
+                return new DESedeKeySpec(secretKey.getEncoded());
+            } catch (InvalidKeyException e) {
+                throw new InvalidKeySpecException(e);
+            }
+        } else {
+            throw new InvalidKeySpecException("Unsupported KeySpec class: " + aClass);
+        }
+    }
+
+    @Override
+    protected SecretKey engineTranslateKey(SecretKey secretKey) throws InvalidKeyException {
+        if (secretKey == null) {
+            throw new InvalidKeyException("Null SecretKey");
+        }
+        return new SecretKeySpec(secretKey.getEncoded(), secretKey.getAlgorithm());
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/DefaultSSLContextImpl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/DefaultSSLContextImpl.java
new file mode 100644
index 0000000..26676f0
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/DefaultSSLContextImpl.java
@@ -0,0 +1,133 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
+/**
+ * Support class for this package.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public final class DefaultSSLContextImpl extends OpenSSLContextImpl {
+
+    /**
+     * Accessed by SSLContextImpl(DefaultSSLContextImpl) holding the
+     * DefaultSSLContextImpl.class monitor
+     */
+    private static KeyManager[] KEY_MANAGERS;
+
+    /**
+     * Accessed by SSLContextImpl(DefaultSSLContextImpl) holding the
+     * DefaultSSLContextImpl.class monitor
+     */
+    private static TrustManager[] TRUST_MANAGERS;
+
+    /**
+     * DefaultSSLContextImpl delegates the work to the super class since there
+     * is no way to put a synchronized around both the call to super and the
+     * rest of this constructor to guarantee that we don't have races in
+     * creating the state shared between all default SSLContexts.
+     */
+    @libcore.api.IntraCoreApi
+    public DefaultSSLContextImpl() throws GeneralSecurityException, IOException {
+        super();
+    }
+
+    // TODO javax.net.ssl.keyStoreProvider system property
+    KeyManager[] getKeyManagers () throws GeneralSecurityException, IOException {
+        if (KEY_MANAGERS != null) {
+            return KEY_MANAGERS;
+        }
+        // find KeyStore, KeyManagers
+        String keystore = System.getProperty("javax.net.ssl.keyStore");
+        if (keystore == null) {
+            return null;
+        }
+        String keystorepwd = System.getProperty("javax.net.ssl.keyStorePassword");
+        char[] pwd = (keystorepwd == null) ? null : keystorepwd.toCharArray();
+
+        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+        InputStream is = null;
+        try {
+            is = new BufferedInputStream(new FileInputStream(keystore));
+            ks.load(is, pwd);
+        } finally {
+            if (is != null) {
+                is.close();
+            }
+        }
+
+        String kmfAlg = KeyManagerFactory.getDefaultAlgorithm();
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfAlg);
+        kmf.init(ks, pwd);
+        KEY_MANAGERS = kmf.getKeyManagers();
+        return KEY_MANAGERS;
+    }
+
+    // TODO javax.net.ssl.trustStoreProvider system property
+    TrustManager[] getTrustManagers() throws GeneralSecurityException, IOException {
+        if (TRUST_MANAGERS != null) {
+            return TRUST_MANAGERS;
+        }
+
+        // find TrustStore, TrustManagers
+        String keystore = System.getProperty("javax.net.ssl.trustStore");
+        if (keystore == null) {
+            return null;
+        }
+        String keystorepwd = System.getProperty("javax.net.ssl.trustStorePassword");
+        char[] pwd = (keystorepwd == null) ? null : keystorepwd.toCharArray();
+
+        // TODO Defaults: jssecacerts; cacerts
+        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+        InputStream is = null;
+        try {
+            is = new BufferedInputStream(new FileInputStream(keystore));
+            ks.load(is, pwd);
+        } finally {
+            if (is != null) {
+                is.close();
+            }
+        }
+        String tmfAlg = TrustManagerFactory.getDefaultAlgorithm();
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlg);
+        tmf.init(ks);
+        TRUST_MANAGERS = tmf.getTrustManagers();
+        return TRUST_MANAGERS;
+    }
+
+    @Override
+    public void engineInit(KeyManager[] kms, TrustManager[] tms,
+            SecureRandom sr) throws KeyManagementException {
+        throw new KeyManagementException("Do not init() the default SSLContext ");
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/DuckTypedPSKKeyManager.java b/repackaged/common/src/main/java/com/android/org/conscrypt/DuckTypedPSKKeyManager.java
new file mode 100644
index 0000000..7c6eccf
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/DuckTypedPSKKeyManager.java
@@ -0,0 +1,138 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.lang.reflect.Method;
+import java.net.Socket;
+import javax.crypto.SecretKey;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Reflection-based {@link PSKKeyManager} adaptor for objects which expose all the methods of the
+ * {@code PSKKeyManager} interface but do not implement the interface.
+ *
+ * <p>This is expected to be useful on platforms where there are multiple instances of the
+ * {@code PSKKeyManager} interface.
+ *
+ * Visible for testing only.
+ *
+ * @deprecated This abstraction is deprecated because it does not work with TLS 1.3.
+ */
+@Deprecated
+final class DuckTypedPSKKeyManager implements PSKKeyManager {
+
+    private final Object mDelegate;
+
+    private DuckTypedPSKKeyManager(Object delegate) {
+        mDelegate = delegate;
+    }
+
+    /**
+     * Gets an instance of {@code DuckTypedPSKKeyManager} which delegates all invocations of methods
+     * of the {@link PSKKeyManager} interface to the same methods of the provided object.
+     *
+     * @throws NoSuchMethodException if {@code obj} does not implement a method of the
+     *         {@code PSKKeyManager} interface.
+     */
+    static DuckTypedPSKKeyManager getInstance(Object obj) throws NoSuchMethodException {
+        Class<?> sourceClass = obj.getClass();
+        for (Method targetMethod : PSKKeyManager.class.getMethods()) {
+            if (targetMethod.isSynthetic()) {
+                continue;
+            }
+            // Check that obj exposes the target method (same name and parameter types)
+            Method sourceMethod =
+                    sourceClass.getMethod(targetMethod.getName(), targetMethod.getParameterTypes());
+            // Check that the return type of obj's method matches the target method.
+            Class<?> sourceReturnType = sourceMethod.getReturnType();
+            Class<?> targetReturnType = targetMethod.getReturnType();
+            if (!targetReturnType.isAssignableFrom(sourceReturnType)) {
+                throw new NoSuchMethodException(sourceMethod + " return value (" + sourceReturnType
+                        + ") incompatible with target return value (" + targetReturnType + ")");
+            }
+        }
+
+        return new DuckTypedPSKKeyManager(obj);
+    }
+
+    @Override
+    public String chooseServerKeyIdentityHint(Socket socket) {
+        try {
+            return (String) mDelegate.getClass()
+                    .getMethod("chooseServerKeyIdentityHint", Socket.class)
+                    .invoke(mDelegate, socket);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to invoke chooseServerKeyIdentityHint", e);
+        }
+    }
+
+    @Override
+    public String chooseServerKeyIdentityHint(SSLEngine engine) {
+        try {
+            return (String) mDelegate.getClass()
+                    .getMethod("chooseServerKeyIdentityHint", SSLEngine.class)
+                    .invoke(mDelegate, engine);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to invoke chooseServerKeyIdentityHint", e);
+        }
+    }
+
+    @Override
+    public String chooseClientKeyIdentity(String identityHint, Socket socket) {
+        try {
+            return (String) mDelegate.getClass()
+                    .getMethod("chooseClientKeyIdentity", String.class, Socket.class)
+                    .invoke(mDelegate, identityHint, socket);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to invoke chooseClientKeyIdentity", e);
+        }
+    }
+
+    @Override
+    public String chooseClientKeyIdentity(String identityHint, SSLEngine engine) {
+        try {
+            return (String) mDelegate.getClass()
+                    .getMethod("chooseClientKeyIdentity", String.class, SSLEngine.class)
+                    .invoke(mDelegate, identityHint, engine);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to invoke chooseClientKeyIdentity", e);
+        }
+    }
+
+    @Override
+    public SecretKey getKey(String identityHint, String identity, Socket socket) {
+        try {
+            return (SecretKey) mDelegate.getClass()
+                    .getMethod("getKey", String.class, String.class, Socket.class)
+                    .invoke(mDelegate, identityHint, identity, socket);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to invoke getKey", e);
+        }
+    }
+
+    @Override
+    public SecretKey getKey(String identityHint, String identity, SSLEngine engine) {
+        try {
+            return (SecretKey) mDelegate.getClass()
+                    .getMethod("getKey", String.class, String.class, SSLEngine.class)
+                    .invoke(mDelegate, identityHint, identity, engine);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to invoke getKey", e);
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ECParameters.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ECParameters.java
new file mode 100644
index 0000000..1e995fd
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ECParameters.java
@@ -0,0 +1,117 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.security.AlgorithmParametersSpi;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+
+/**
+ * AlgorithmParameters implementation for elliptic curves.  The only supported encoding format is
+ * ASN.1, as specified in RFC 3279, section 2.3.5.  However, only named curves are supported.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public class ECParameters extends AlgorithmParametersSpi {
+
+    private OpenSSLECGroupContext curve;
+
+    @libcore.api.IntraCoreApi
+    public ECParameters() {}
+
+    @Override
+    protected void engineInit(AlgorithmParameterSpec algorithmParameterSpec)
+            throws InvalidParameterSpecException {
+        if (algorithmParameterSpec instanceof ECGenParameterSpec) {
+            String newCurveName = ((ECGenParameterSpec) algorithmParameterSpec).getName();
+            OpenSSLECGroupContext newCurve = OpenSSLECGroupContext.getCurveByName(newCurveName);
+            if (newCurve == null) {
+                throw new InvalidParameterSpecException("Unknown EC curve name: " + newCurveName);
+            }
+            this.curve = newCurve;
+        } else if (algorithmParameterSpec instanceof ECParameterSpec) {
+            ECParameterSpec ecParamSpec = (ECParameterSpec) algorithmParameterSpec;
+            try {
+                OpenSSLECGroupContext newCurve = OpenSSLECGroupContext.getInstance(ecParamSpec);
+                if (newCurve == null) {
+                    throw new InvalidParameterSpecException("Unknown EC curve: " + ecParamSpec);
+                }
+                this.curve = newCurve;
+            } catch (InvalidAlgorithmParameterException e) {
+                throw new InvalidParameterSpecException(e.getMessage());
+            }
+        } else {
+            throw new InvalidParameterSpecException(
+                    "Only ECParameterSpec and ECGenParameterSpec are supported");
+        }
+    }
+
+    @Override
+    protected void engineInit(byte[] bytes) throws IOException {
+        long ref = NativeCrypto.EC_KEY_parse_curve_name(bytes);
+        if (ref == 0) {
+            throw new IOException("Error reading ASN.1 encoding");
+        }
+        this.curve = new OpenSSLECGroupContext(new NativeRef.EC_GROUP(ref));
+    }
+
+    @Override
+    protected void engineInit(byte[] bytes, String format) throws IOException {
+        if (format == null || format.equals("ASN.1")) {
+            engineInit(bytes);
+        } else {
+            throw new IOException("Unsupported format: " + format);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(Class<T> aClass)
+            throws InvalidParameterSpecException {
+        if (aClass == ECParameterSpec.class) {
+            return (T) curve.getECParameterSpec();
+        } else if (aClass == ECGenParameterSpec.class) {
+            return (T) new ECGenParameterSpec(Platform.getCurveName(curve.getECParameterSpec()));
+        } else {
+            throw new InvalidParameterSpecException("Unsupported class: " + aClass);
+        }
+    }
+
+    @Override
+    protected byte[] engineGetEncoded() throws IOException {
+        return NativeCrypto.EC_KEY_marshal_curve_name(curve.getNativeRef());
+    }
+
+    @Override
+    protected byte[] engineGetEncoded(String format) throws IOException {
+        if (format == null || format.equals("ASN.1")) {
+            return engineGetEncoded();
+        }
+        throw new IOException("Unsupported format: " + format);
+    }
+
+    @Override
+    protected String engineToString() {
+        return "Conscrypt EC AlgorithmParameters";
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/EmptyArray.java b/repackaged/common/src/main/java/com/android/org/conscrypt/EmptyArray.java
new file mode 100644
index 0000000..a36d337
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/EmptyArray.java
@@ -0,0 +1,36 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Copied from libcore.util.EmptyArray
+
+package com.android.org.conscrypt;
+
+final class EmptyArray {
+    private EmptyArray() {}
+
+    static final boolean[] BOOLEAN = new boolean[0];
+    static final byte[] BYTE = new byte[0];
+    static final char[] CHAR = new char[0];
+    static final double[] DOUBLE = new double[0];
+    static final int[] INT = new int[0];
+
+    static final Class<?>[] CLASS = new Class<?>[ 0 ];
+    static final Object[] OBJECT = new Object[0];
+    static final String[] STRING = new String[0];
+    static final Throwable[] THROWABLE = new Throwable[0];
+    static final StackTraceElement[] STACK_TRACE_ELEMENT = new StackTraceElement[0];
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/EvpMdRef.java b/repackaged/common/src/main/java/com/android/org/conscrypt/EvpMdRef.java
new file mode 100644
index 0000000..fd62130
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/EvpMdRef.java
@@ -0,0 +1,159 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.NoSuchAlgorithmException;
+import java.util.Locale;
+
+/**
+ * Utility class to convert between BoringSSL- and JCE-style message digest identifiers.
+ */
+final class EvpMdRef {
+    static final String MGF1_ALGORITHM_NAME = "MGF1";
+    static final String MGF1_OID = "1.2.840.113549.1.1.8";
+
+    /**
+     * Returns the canonical JCA digest algorithm name for the provided digest
+     * algorithm name or {@code null} if the digest algorithm is not known.
+     */
+    static String getJcaDigestAlgorithmStandardName(String algorithm) {
+        String algorithmUpper = algorithm.toUpperCase(Locale.US);
+        if (SHA256.JCA_NAME.equals(algorithmUpper) || SHA256.OID.equals(algorithmUpper)) {
+            return SHA256.JCA_NAME;
+        } else if (SHA512.JCA_NAME.equals(algorithmUpper) || SHA512.OID.equals(algorithmUpper)) {
+            return SHA512.JCA_NAME;
+        } else if (SHA1.JCA_NAME.equals(algorithmUpper) || SHA1.OID.equals(algorithmUpper)) {
+            return SHA1.JCA_NAME;
+        } else if (SHA384.JCA_NAME.equals(algorithmUpper) || SHA384.OID.equals(algorithmUpper)) {
+            return SHA384.JCA_NAME;
+        } else if (SHA224.JCA_NAME.equals(algorithmUpper) || SHA224.OID.equals(algorithmUpper)) {
+            return SHA224.JCA_NAME;
+        } else {
+            return null;
+        }
+    }
+
+    static long getEVP_MDByJcaDigestAlgorithmStandardName(String algorithm)
+            throws NoSuchAlgorithmException {
+        String algorithmUpper = algorithm.toUpperCase(Locale.US);
+        if (SHA256.JCA_NAME.equals(algorithmUpper)) {
+            return EvpMdRef.SHA256.EVP_MD;
+        } else if (SHA512.JCA_NAME.equals(algorithmUpper)) {
+            return EvpMdRef.SHA512.EVP_MD;
+        } else if (SHA1.JCA_NAME.equals(algorithmUpper)) {
+            return EvpMdRef.SHA1.EVP_MD;
+        } else if (SHA384.JCA_NAME.equals(algorithmUpper)) {
+            return EvpMdRef.SHA384.EVP_MD;
+        } else if (SHA224.JCA_NAME.equals(algorithmUpper)) {
+            return EvpMdRef.SHA224.EVP_MD;
+        } else {
+            throw new NoSuchAlgorithmException("Unsupported algorithm: " + algorithm);
+        }
+    }
+
+    static int getDigestSizeBytesByJcaDigestAlgorithmStandardName(String algorithm)
+            throws NoSuchAlgorithmException {
+        String algorithmUpper = algorithm.toUpperCase(Locale.US);
+        if (SHA256.JCA_NAME.equals(algorithmUpper)) {
+            return EvpMdRef.SHA256.SIZE_BYTES;
+        } else if (SHA512.JCA_NAME.equals(algorithmUpper)) {
+            return EvpMdRef.SHA512.SIZE_BYTES;
+        } else if (SHA1.JCA_NAME.equals(algorithmUpper)) {
+            return EvpMdRef.SHA1.SIZE_BYTES;
+        } else if (SHA384.JCA_NAME.equals(algorithmUpper)) {
+            return EvpMdRef.SHA384.SIZE_BYTES;
+        } else if (SHA224.JCA_NAME.equals(algorithmUpper)) {
+            return EvpMdRef.SHA224.SIZE_BYTES;
+        } else {
+            throw new NoSuchAlgorithmException("Unsupported algorithm: " + algorithm);
+        }
+    }
+
+    static String getJcaDigestAlgorithmStandardNameFromEVP_MD(long evpMdRef) {
+        if (evpMdRef == MD5.EVP_MD) {
+            return MD5.JCA_NAME;
+        } else if (evpMdRef == SHA1.EVP_MD) {
+            return SHA1.JCA_NAME;
+        } else if (evpMdRef == SHA224.EVP_MD) {
+            return SHA224.JCA_NAME;
+        } else if (evpMdRef == SHA256.EVP_MD) {
+            return SHA256.JCA_NAME;
+        } else if (evpMdRef == SHA384.EVP_MD) {
+            return SHA384.JCA_NAME;
+        } else if (evpMdRef == SHA512.EVP_MD) {
+            return SHA512.JCA_NAME;
+        } else {
+            throw new IllegalArgumentException("Unknown EVP_MD reference");
+        }
+    }
+
+    static final class MD5 {
+        static final String JCA_NAME = "MD5";
+        static final String OID = "1.2.840.113549.2.5";
+        static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("md5");
+        static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
+
+        private MD5() {}
+    }
+
+    static final class SHA1 {
+        static final String JCA_NAME = "SHA-1";
+        static final String OID = "1.3.14.3.2.26";
+        static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha1");
+        static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
+        private SHA1() {}
+    }
+
+    static final class SHA224 {
+        static final String JCA_NAME = "SHA-224";
+        static final String OID = "2.16.840.1.101.3.4.2.4";
+        static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha224");
+        static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
+
+        private SHA224() {}
+    }
+
+    static final class SHA256 {
+        static final String JCA_NAME = "SHA-256";
+        static final String OID = "2.16.840.1.101.3.4.2.1";
+        static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha256");
+        static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
+
+        private SHA256() {}
+    }
+
+    static final class SHA384 {
+        static final String JCA_NAME = "SHA-384";
+        static final String OID = "2.16.840.1.101.3.4.2.2";
+        static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha384");
+        static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
+
+        private SHA384() {}
+    }
+
+    static final class SHA512 {
+        static final String JCA_NAME = "SHA-512";
+        static final String OID = "2.16.840.1.101.3.4.2.3";
+        static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha512");
+        static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
+
+        private SHA512() {}
+    }
+
+    private EvpMdRef() {}
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ExperimentalApi.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ExperimentalApi.java
new file mode 100644
index 0000000..947ab63
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ExperimentalApi.java
@@ -0,0 +1,52 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates a public API that can change at any time, and has no guarantee of API stability and
+ * backward-compatibility.
+ *
+ * <p>Usage guidelines:
+ * <ol>
+ * <li>This annotation is used only on public API. Internal interfaces should not use it.</li>
+ * <li>This annotation should only be added to new APIs. Adding it to an existing API is
+ * considered API-breaking.</li>
+ * <li>Removing this annotation from an API gives it stable status.</li>
+ * </ol>
+ */
+@Internal
+@Retention(RetentionPolicy.SOURCE)
+@Target({
+        ElementType.ANNOTATION_TYPE,
+        ElementType.CONSTRUCTOR,
+        ElementType.FIELD,
+        ElementType.METHOD,
+        ElementType.PACKAGE,
+        ElementType.TYPE})
+@Documented
+public @interface ExperimentalApi {
+    /**
+     * Context information such as links to discussion thread, tracking issue etc.
+     */
+    String value() default "";
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ExternalSession.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ExternalSession.java
new file mode 100644
index 0000000..c6eaa41
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ExternalSession.java
@@ -0,0 +1,214 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.util.HashMap;
+import java.util.List;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionBindingEvent;
+import javax.net.ssl.SSLSessionBindingListener;
+import javax.net.ssl.SSLSessionContext;
+import javax.security.cert.X509Certificate;
+
+/**
+ * An externalized view of the underlying {@link SSLSession} used within a
+ * socket/engine. This class provides the caller with a consistent session
+ * handle which will continue to be usable regardless of internal changes
+ * to the connection.  It does this by delegating calls to the <b>current</b>
+ * internal session, which is provided by the session {@code Provider}
+ * (i.e. the socket or engine that owns the session).  This allows the provider
+ * to switch implementations (for instance, using a JNI implementation to
+ * access live values while the connection is open and a set of final values
+ * when the connection is closed), even if the caller stores a reference to
+ * the session object.
+ *
+ * <p>This class implements the {@link SSLSession} value API itself, rather
+ * than delegating to the provided session, to ensure the caller has a consistent
+ * value map, regardless of which internal session is currently being used by the
+ * socket/engine.  This class will never call the value API methods on the
+ * underlying sessions, so they need not be implemented.
+ */
+final class ExternalSession implements ConscryptSession {
+    // Use an initialcapacity of 2 to keep it small in the average case.
+    private final HashMap<String, Object> values = new HashMap<String, Object>(2);
+    private final Provider provider;
+
+    public ExternalSession(Provider provider) {
+        this.provider = provider;
+    }
+
+    @Override
+    public String getRequestedServerName() {
+        return provider.provideSession().getRequestedServerName();
+    }
+
+    @Override
+    public List<byte[]> getStatusResponses() {
+        return provider.provideSession().getStatusResponses();
+    }
+
+    @Override
+    public byte[] getPeerSignedCertificateTimestamp() {
+        return provider.provideSession().getPeerSignedCertificateTimestamp();
+    }
+
+    @Override
+    public byte[] getId() {
+        return provider.provideSession().getId();
+    }
+
+    @Override
+    public SSLSessionContext getSessionContext() {
+        return provider.provideSession().getSessionContext();
+    }
+
+    @Override
+    public long getCreationTime() {
+        return provider.provideSession().getCreationTime();
+    }
+
+    @Override
+    public long getLastAccessedTime() {
+        return provider.provideSession().getLastAccessedTime();
+    }
+
+    @Override
+    public void invalidate() {
+        provider.provideSession().invalidate();
+    }
+
+    @Override
+    public boolean isValid() {
+        return provider.provideSession().isValid();
+    }
+
+    @Override
+    public java.security.cert.X509Certificate[] getPeerCertificates()
+            throws SSLPeerUnverifiedException {
+        return provider.provideSession().getPeerCertificates();
+    }
+
+    @Override
+    public Certificate[] getLocalCertificates() {
+        return provider.provideSession().getLocalCertificates();
+    }
+
+    @Override
+    public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
+        return provider.provideSession().getPeerCertificateChain();
+    }
+
+    @Override
+    public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+        return provider.provideSession().getPeerPrincipal();
+    }
+
+    @Override
+    public Principal getLocalPrincipal() {
+        return provider.provideSession().getLocalPrincipal();
+    }
+
+    @Override
+    public String getCipherSuite() {
+        return provider.provideSession().getCipherSuite();
+    }
+
+    @Override
+    public String getProtocol() {
+        return provider.provideSession().getProtocol();
+    }
+
+    @Override
+    public String getPeerHost() {
+        return provider.provideSession().getPeerHost();
+    }
+
+    @Override
+    public int getPeerPort() {
+        return provider.provideSession().getPeerPort();
+    }
+
+    @Override
+    public int getPacketBufferSize() {
+        return provider.provideSession().getPacketBufferSize();
+    }
+
+    @Override
+    public int getApplicationBufferSize() {
+        return provider.provideSession().getApplicationBufferSize();
+    }
+
+    @Override
+    public Object getValue(String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("name == null");
+        }
+        return values.get(name);
+    }
+
+    @Override
+    public String[] getValueNames() {
+        return values.keySet().toArray(new String[values.size()]);
+    }
+
+    @Override
+    public void putValue(String name, Object value) {
+        putValue(this, name, value);
+    }
+
+    void putValue(SSLSession session, String name, Object value) {
+        if (name == null || value == null) {
+            throw new IllegalArgumentException("name == null || value == null");
+        }
+        Object old = values.put(name, value);
+        if (value instanceof SSLSessionBindingListener) {
+            ((SSLSessionBindingListener) value)
+                    .valueBound(new SSLSessionBindingEvent(session, name));
+        }
+        if (old instanceof SSLSessionBindingListener) {
+            ((SSLSessionBindingListener) old)
+                    .valueUnbound(new SSLSessionBindingEvent(session, name));
+        }
+    }
+
+    @Override
+    public void removeValue(String name) {
+        removeValue(this, name);
+    }
+
+    void removeValue(SSLSession session, String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("name == null");
+        }
+        Object old = values.remove(name);
+        if (old instanceof SSLSessionBindingListener) {
+            SSLSessionBindingListener listener = (SSLSessionBindingListener) old;
+            listener.valueUnbound(new SSLSessionBindingEvent(session, name));
+        }
+    }
+
+    /**
+     * The provider of the current delegate session.
+     */
+    interface Provider {
+        ConscryptSession provideSession();
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/FileClientSessionCache.java b/repackaged/common/src/main/java/com/android/org/conscrypt/FileClientSessionCache.java
new file mode 100644
index 0000000..2526cfe
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/FileClientSessionCache.java
@@ -0,0 +1,386 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.net.ssl.SSLSession;
+
+/**
+ * File-based cache implementation. Only one process should access the
+ * underlying directory at a time.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.CorePlatformApi
+@Internal
+public final class FileClientSessionCache {
+    private static final Logger logger = Logger.getLogger(FileClientSessionCache.class.getName());
+
+    public static final int MAX_SIZE = 12; // ~72k
+
+    private FileClientSessionCache() {}
+
+    /**
+     * This cache creates one file per SSL session using "host.port" for
+     * the file name. Files are created or replaced when session data is put
+     * in the cache (see {@link #putSessionData}). Files are read on
+     * cache hits, but not on cache misses.
+     *
+     * <p>When the number of session files exceeds MAX_SIZE, we delete the
+     * least-recently-used file. We don't current persist the last access time,
+     * so the ordering actually ends up being least-recently-modified in some
+     * cases and even just "not accessed in this process" if the filesystem
+     * doesn't track last modified times.
+     */
+    static class Impl implements SSLClientSessionCache {
+        /** Directory to store session files in. */
+        final File directory;
+
+        /**
+         * Map of name -> File. Keeps track of the order files were accessed in.
+         */
+        Map<String, File> accessOrder = newAccessOrder();
+
+        /** The number of files on disk. */
+        int size;
+
+        /**
+         * The initial set of files. We use this to defer adding information
+         * about all files to accessOrder until necessary.
+         */
+        String[] initialFiles;
+
+        /**
+         * Constructs a new cache backed by the given directory.
+         */
+        Impl(File directory) throws IOException {
+            boolean exists = directory.exists();
+            if (exists && !directory.isDirectory()) {
+                throw new IOException(directory + " exists but is not a directory.");
+            }
+
+            if (exists) {
+                // Read and sort initial list of files. We defer adding
+                // information about these files to accessOrder until necessary
+                // (see indexFiles()). Sorting the list enables us to detect
+                // cache misses in getSessionData().
+                // Note: Sorting an array here was faster than creating a
+                // HashSet on Dalvik.
+                initialFiles = directory.list();
+                if (initialFiles == null) {
+                    // File.list() will return null in error cases without throwing IOException
+                    // http://b/3363561
+                    throw new IOException(directory + " exists but cannot list contents.");
+                }
+                Arrays.sort(initialFiles);
+                size = initialFiles.length;
+            } else {
+                // Create directory.
+                if (!directory.mkdirs()) {
+                    throw new IOException("Creation of " + directory + " directory failed.");
+                }
+                size = 0;
+            }
+
+            this.directory = directory;
+        }
+
+        /**
+         * Creates a new access-ordered linked hash map.
+         */
+        private static Map<String, File> newAccessOrder() {
+            return new LinkedHashMap<String, File>(MAX_SIZE, 0.75f, true /* access order */);
+        }
+
+        /**
+         * Gets the file name for the given host and port.
+         */
+        private static String fileName(String host, int port) {
+            if (host == null) {
+                throw new NullPointerException("host == null");
+            }
+            return host + "." + port;
+        }
+
+        @dalvik.annotation.compat.UnsupportedAppUsage
+        @Override
+        public synchronized byte[] getSessionData(String host, int port) {
+            /*
+             * Note: This method is only called when the in-memory cache
+             * in SSLSessionContext misses, so it would be unnecessarily
+             * redundant for this cache to store data in memory.
+             */
+
+            String name = fileName(host, port);
+            File file = accessOrder.get(name);
+
+            if (file == null) {
+                // File wasn't in access order. Check initialFiles...
+                if (initialFiles == null) {
+                    // All files are in accessOrder, so it doesn't exist.
+                    return null;
+                }
+
+                // Look in initialFiles.
+                if (Arrays.binarySearch(initialFiles, name) < 0) {
+                    // Not found.
+                    return null;
+                }
+
+                // The file is on disk but not in accessOrder yet.
+                file = new File(directory, name);
+                accessOrder.put(name, file);
+            }
+
+            FileInputStream in;
+            try {
+                in = new FileInputStream(file);
+            } catch (FileNotFoundException e) {
+                logReadError(host, file, e);
+                return null;
+            }
+            try {
+                int size = (int) file.length();
+                byte[] data = new byte[size];
+                new DataInputStream(in).readFully(data);
+                return data;
+            } catch (IOException e) {
+                logReadError(host, file, e);
+                return null;
+            } finally {
+                if (in != null) {
+                    try {
+                        in.close();
+                    } catch (Exception ignored) {
+                    }
+                }
+            }
+        }
+
+        static void logReadError(String host, File file, Throwable t) {
+            logger.log(Level.WARNING,
+                    "FileClientSessionCache: Error reading session data for " + host + " from "
+                            + file + ".",
+                    t);
+        }
+
+        @Override
+        public synchronized void putSessionData(SSLSession session, byte[] sessionData) {
+            String host = session.getPeerHost();
+            if (sessionData == null) {
+                throw new NullPointerException("sessionData == null");
+            }
+
+            String name = fileName(host, session.getPeerPort());
+            File file = new File(directory, name);
+
+            // Used to keep track of whether or not we're expanding the cache.
+            boolean existedBefore = file.exists();
+
+            FileOutputStream out;
+            try {
+                out = new FileOutputStream(file);
+            } catch (FileNotFoundException e) {
+                // We can't write to the file.
+                logWriteError(host, file, e);
+                return;
+            }
+
+            // If we expanded the cache (by creating a new file)...
+            if (!existedBefore) {
+                size++;
+
+                // Delete an old file if necessary.
+                makeRoom();
+            }
+
+            boolean writeSuccessful = false;
+            try {
+                out.write(sessionData);
+                writeSuccessful = true;
+            } catch (IOException e) {
+                logWriteError(host, file, e);
+            } finally {
+                boolean closeSuccessful = false;
+                try {
+                    out.close();
+                    closeSuccessful = true;
+                } catch (IOException e) {
+                    logWriteError(host, file, e);
+                } finally {
+                    if (!writeSuccessful || !closeSuccessful) {
+                        // Storage failed. Clean up.
+                        delete(file);
+                    } else {
+                        // Success!
+                        accessOrder.put(name, file);
+                    }
+                }
+            }
+        }
+
+        /**
+         * Deletes old files if necessary.
+         */
+        private void makeRoom() {
+            if (size <= MAX_SIZE) {
+                return;
+            }
+
+            indexFiles();
+
+            // Delete LRUed files.
+            int removals = size - MAX_SIZE;
+            Iterator<File> i = accessOrder.values().iterator();
+            do {
+                delete(i.next());
+                i.remove();
+            } while (--removals > 0);
+        }
+
+        /**
+         * Lazily updates accessOrder to know about all files as opposed to
+         * just the files accessed since this process started.
+         */
+        private void indexFiles() {
+            String[] initialFiles = this.initialFiles;
+            if (initialFiles != null) {
+                this.initialFiles = null;
+
+                // Files on disk only, sorted by last modified time.
+                // TODO: Use last access time.
+                Set<CacheFile> diskOnly = new TreeSet<CacheFile>();
+                for (String name : initialFiles) {
+                    // If the file hasn't been accessed in this process...
+                    if (!accessOrder.containsKey(name)) {
+                        diskOnly.add(new CacheFile(directory, name));
+                    }
+                }
+
+                if (!diskOnly.isEmpty()) {
+                    // Add files not accessed in this process to the beginning
+                    // of accessOrder.
+                    Map<String, File> newOrder = newAccessOrder();
+                    for (CacheFile cacheFile : diskOnly) {
+                        newOrder.put(cacheFile.name, cacheFile);
+                    }
+                    newOrder.putAll(accessOrder);
+
+                    this.accessOrder = newOrder;
+                }
+            }
+        }
+
+        @SuppressWarnings("ThrowableInstanceNeverThrown")
+        private void delete(File file) {
+            if (!file.delete()) {
+                Exception e =
+                        new IOException("FileClientSessionCache: Failed to delete " + file + ".");
+                logger.log(Level.WARNING, e.getMessage(), e);
+            }
+            size--;
+        }
+
+        static void logWriteError(String host, File file, Throwable t) {
+            logger.log(Level.WARNING,
+                    "FileClientSessionCache: Error writing session data for " + host + " to " + file
+                            + ".",
+                    t);
+        }
+    }
+
+    /**
+     * Maps directories to the cache instances that are backed by those
+     * directories. We synchronize access using the cache instance, so it's
+     * important that everyone shares the same instance.
+     */
+    static final Map<File, FileClientSessionCache.Impl> caches =
+            new HashMap<File, FileClientSessionCache.Impl>();
+
+    /**
+     * Returns a cache backed by the given directory. Creates the directory
+     * (including parent directories) if necessary. This cache should have
+     * exclusive access to the given directory.
+     *
+     * @param directory to store files in
+     * @return a cache backed by the given directory
+     * @throws IOException if the file exists and is not a directory or if
+     *  creating the directories fails
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    public static synchronized SSLClientSessionCache usingDirectory(File directory)
+            throws IOException {
+        FileClientSessionCache.Impl cache = caches.get(directory);
+        if (cache == null) {
+            cache = new FileClientSessionCache.Impl(directory);
+            caches.put(directory, cache);
+        }
+        return cache;
+    }
+
+    /** For testing. */
+    static synchronized void reset() {
+        caches.clear();
+    }
+
+    /** A file containing a piece of cached data. */
+    @SuppressWarnings("serial")
+    static class CacheFile extends File {
+        final String name;
+
+        CacheFile(File dir, String name) {
+            super(dir, name);
+            this.name = name;
+        }
+
+        long lastModified = -1;
+
+        @Override
+        public long lastModified() {
+            long lastModified = this.lastModified;
+            if (lastModified == -1) {
+                lastModified = this.lastModified = super.lastModified();
+            }
+            return lastModified;
+        }
+
+        @Override
+        public int compareTo(File another) {
+            // Sort by last modified time.
+            long result = lastModified() - another.lastModified();
+            if (result == 0) {
+                return super.compareTo(another);
+            }
+            return result < 0 ? -1 : 1;
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/GCMParameters.java b/repackaged/common/src/main/java/com/android/org/conscrypt/GCMParameters.java
new file mode 100644
index 0000000..d6fd56a
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/GCMParameters.java
@@ -0,0 +1,156 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.security.AlgorithmParametersSpi;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+
+/**
+ * GCM parameters used during an ciphering operation with {@link OpenSSLCipher}.
+ * This class is used internally for backward compatibility with Android versions
+ * that did not have the {@code GCMParameterSpec} class, in addition to being the
+ * implementation of the GCM AlgorithmParameters implementation.
+ * <p>
+ * The only supported encoding format is ASN.1, as specified in RFC 5084 section 3.2.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public final class GCMParameters extends AlgorithmParametersSpi {
+
+    // The default value (in bits) for TLEN in the GCM ASN.1 module
+    private static final int DEFAULT_TLEN = 96;
+
+    /** The tag length in bits. */
+    private int tLen;
+
+    /** Actually the nonce value for the GCM operation. */
+    private byte[] iv;
+
+    @libcore.api.IntraCoreApi
+    public GCMParameters() { }
+
+    GCMParameters(int tLen, byte[] iv) {
+        this.tLen = tLen;
+        this.iv = iv;
+    }
+
+    /**
+     * Returns the tag length in bits.
+     */
+    int getTLen() {
+        return tLen;
+    }
+
+    /**
+     * Returns a non-cloned version of the IV.
+     */
+    byte[] getIV() {
+        return iv;
+    }
+
+    @Override
+    protected void engineInit(AlgorithmParameterSpec algorithmParameterSpec)
+            throws InvalidParameterSpecException {
+        GCMParameters params = Platform.fromGCMParameterSpec(algorithmParameterSpec);
+        if (params == null) {
+            throw new InvalidParameterSpecException("Only GCMParameterSpec is supported");
+        }
+        this.tLen = params.tLen;
+        this.iv = params.iv;
+    }
+
+    @Override
+    protected void engineInit(byte[] bytes) throws IOException {
+        long readRef = 0;
+        long seqRef = 0;
+        try {
+            readRef = NativeCrypto.asn1_read_init(bytes);
+            seqRef = NativeCrypto.asn1_read_sequence(readRef);
+            byte[] newIv = NativeCrypto.asn1_read_octetstring(seqRef);
+            int newTlen = DEFAULT_TLEN;
+            if (!NativeCrypto.asn1_read_is_empty(seqRef)) {
+                newTlen = 8 * (int) NativeCrypto.asn1_read_uint64(seqRef);
+            }
+            if (!NativeCrypto.asn1_read_is_empty(seqRef)
+                    || !NativeCrypto.asn1_read_is_empty(readRef)) {
+                throw new IOException("Error reading ASN.1 encoding");
+            }
+            this.iv = newIv;
+            this.tLen = newTlen;
+        } finally {
+            NativeCrypto.asn1_read_free(seqRef);
+            NativeCrypto.asn1_read_free(readRef);
+        }
+    }
+
+    @Override
+    protected void engineInit(byte[] bytes, String format) throws IOException {
+        if ((format == null) || format.equals("ASN.1")) {
+            engineInit(bytes);
+        } else {
+            throw new IOException("Unsupported format: " + format);
+        }
+    }
+
+    @Override
+    protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(Class<T> aClass)
+            throws InvalidParameterSpecException {
+        if ((aClass != null) && aClass.getName().equals("javax.crypto.spec.GCMParameterSpec")) {
+            return aClass.cast(Platform.toGCMParameterSpec(tLen, iv));
+        } else {
+            throw new InvalidParameterSpecException("Unsupported class: " + aClass);
+        }
+    }
+
+    @Override
+    protected byte[] engineGetEncoded() throws IOException {
+        long cbbRef = 0;
+        long seqRef = 0;
+        try {
+            cbbRef = NativeCrypto.asn1_write_init();
+            seqRef = NativeCrypto.asn1_write_sequence(cbbRef);
+            NativeCrypto.asn1_write_octetstring(seqRef, this.iv);
+            if (this.tLen != DEFAULT_TLEN) {
+                NativeCrypto.asn1_write_uint64(seqRef, this.tLen / 8);
+            }
+            return NativeCrypto.asn1_write_finish(cbbRef);
+        } catch (IOException e) {
+            NativeCrypto.asn1_write_cleanup(cbbRef);
+            throw e;
+        } finally {
+            NativeCrypto.asn1_write_free(seqRef);
+            NativeCrypto.asn1_write_free(cbbRef);
+        }
+    }
+
+    @Override
+    protected byte[] engineGetEncoded(String format) throws IOException {
+        if ((format == null) || format.equals("ASN.1")) {
+            return engineGetEncoded();
+        }
+        throw new IOException("Unsupported format: " + format);
+    }
+
+    @Override
+    protected String engineToString() {
+        return "Conscrypt GCM AlgorithmParameters";
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/HandshakeListener.java b/repackaged/common/src/main/java/com/android/org/conscrypt/HandshakeListener.java
new file mode 100644
index 0000000..72d7767
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/HandshakeListener.java
@@ -0,0 +1,33 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import javax.net.ssl.SSLException;
+
+/**
+ * Similar in concept to {@link javax.net.ssl.HandshakeCompletedListener}, but used for listening directly
+ * to the engine. Allows the caller to be notified immediately upon completion of the TLS handshake.
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class HandshakeListener {
+
+    /**
+     * Called by the engine when the TLS handshake has completed.
+     */
+    public abstract void onHandshakeFinished() throws SSLException;
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/Internal.java b/repackaged/common/src/main/java/com/android/org/conscrypt/Internal.java
new file mode 100644
index 0000000..febeda2
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/Internal.java
@@ -0,0 +1,36 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a program element (class, method, package etc) which is internal to Conscrypt, not part
+ * of
+ * the public API, and should not be used by users of Conscrypt.
+ */
+@Internal
+@Retention(RetentionPolicy.SOURCE)
+@Target({ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD,
+        ElementType.METHOD, ElementType.PACKAGE, ElementType.TYPE})
+@Documented
+public @interface Internal {}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/IvParameters.java b/repackaged/common/src/main/java/com/android/org/conscrypt/IvParameters.java
new file mode 100644
index 0000000..7c8f8dc
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/IvParameters.java
@@ -0,0 +1,140 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.security.AlgorithmParametersSpi;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * An implementation of {@link java.security.AlgorithmParameters} that contains only an IV.  The
+ * supported encoding formats are ASN.1 (primary) and RAW.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public class IvParameters extends AlgorithmParametersSpi {
+    private byte[] iv;
+
+    @libcore.api.IntraCoreApi
+    public IvParameters() {}
+
+    @Override
+    protected void engineInit(AlgorithmParameterSpec algorithmParameterSpec)
+            throws InvalidParameterSpecException {
+        if (!(algorithmParameterSpec instanceof IvParameterSpec)) {
+            throw new InvalidParameterSpecException("Only IvParameterSpec is supported");
+        }
+        iv = ((IvParameterSpec) algorithmParameterSpec).getIV().clone();
+    }
+
+    @Override
+    protected void engineInit(byte[] bytes) throws IOException {
+        long readRef = 0;
+        try {
+            readRef = NativeCrypto.asn1_read_init(bytes);
+            byte[] newIv = NativeCrypto.asn1_read_octetstring(readRef);
+            if (!NativeCrypto.asn1_read_is_empty(readRef)) {
+                throw new IOException("Error reading ASN.1 encoding");
+            }
+            this.iv = newIv;
+        } finally {
+            NativeCrypto.asn1_read_free(readRef);
+        }
+    }
+
+    @Override
+    protected void engineInit(byte[] bytes, String format) throws IOException {
+        if (format == null || format.equals("ASN.1")) {
+            engineInit(bytes);
+        } else if (format.equals("RAW")) {
+            iv = bytes.clone();
+        } else {
+            throw new IOException("Unsupported format: " + format);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(Class<T> aClass)
+            throws InvalidParameterSpecException {
+        if (aClass != IvParameterSpec.class) {
+            throw new InvalidParameterSpecException(
+                    "Incompatible AlgorithmParametersSpec class: " + aClass);
+        }
+        return (T) new IvParameterSpec(iv);
+    }
+
+    @Override
+    protected byte[] engineGetEncoded() throws IOException {
+        long cbbRef = 0;
+        try {
+            cbbRef = NativeCrypto.asn1_write_init();
+            NativeCrypto.asn1_write_octetstring(cbbRef, this.iv);
+            return NativeCrypto.asn1_write_finish(cbbRef);
+        } catch (IOException e) {
+            NativeCrypto.asn1_write_cleanup(cbbRef);
+            throw e;
+        } finally {
+            NativeCrypto.asn1_write_free(cbbRef);
+        }
+    }
+
+    @Override
+    protected byte[] engineGetEncoded(String format) throws IOException {
+        if (format == null || format.equals("ASN.1")) {
+            return engineGetEncoded();
+        } else if (format.equals("RAW")) {
+            return iv.clone();
+        } else {
+            throw new IOException("Unsupported format: " + format);
+        }
+    }
+
+    @Override
+    protected String engineToString() {
+        return "Conscrypt IV AlgorithmParameters";
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static class AES extends IvParameters {
+        @libcore.api.IntraCoreApi
+        public AES() {}
+    }
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static class DESEDE extends IvParameters {
+        @libcore.api.IntraCoreApi
+        public DESEDE() {}
+    }
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static class ChaCha20 extends IvParameters {
+        @libcore.api.IntraCoreApi
+        public ChaCha20() {}
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/Java7ExtendedSSLSession.java b/repackaged/common/src/main/java/com/android/org/conscrypt/Java7ExtendedSSLSession.java
new file mode 100644
index 0000000..4544eb2
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/Java7ExtendedSSLSession.java
@@ -0,0 +1,181 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.util.List;
+import javax.net.ssl.ExtendedSSLSession;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSessionContext;
+import javax.security.cert.X509Certificate;
+
+/**
+ * This is an adapter that wraps the active session with {@link ExtendedSSLSession}, if running
+ * on Java 7+.
+ */
+class Java7ExtendedSSLSession extends ExtendedSSLSession implements ConscryptSession {
+    // TODO: use BoringSSL API to actually fetch the real data
+    private static final String[] LOCAL_SUPPORTED_SIGNATURE_ALGORITHMS = new String[] {
+            "SHA512withRSA", "SHA512withECDSA", "SHA384withRSA", "SHA384withECDSA", "SHA256withRSA",
+            "SHA256withECDSA", "SHA224withRSA", "SHA224withECDSA", "SHA1withRSA", "SHA1withECDSA",
+    };
+    // TODO: use BoringSSL API to actually fetch the real data
+    private static final String[] PEER_SUPPORTED_SIGNATURE_ALGORITHMS =
+            new String[] {"SHA1withRSA", "SHA1withECDSA"};
+    protected final ExternalSession delegate;
+
+    Java7ExtendedSSLSession(ExternalSession delegate) {
+        this.delegate = delegate;
+    }
+
+    /* @Override */
+    @SuppressWarnings("MissingOverride") // For Android backward-compatibility.
+    public final String[] getLocalSupportedSignatureAlgorithms() {
+        return LOCAL_SUPPORTED_SIGNATURE_ALGORITHMS.clone();
+    }
+
+    /* @Override */
+    @SuppressWarnings("MissingOverride") // For Android backward-compatibility.
+    public final String[] getPeerSupportedSignatureAlgorithms() {
+        return PEER_SUPPORTED_SIGNATURE_ALGORITHMS.clone();
+    }
+
+    @Override
+    public final String getRequestedServerName() {
+        return delegate.getRequestedServerName();
+    }
+
+    /**
+     * Provides forward-compatibility with Java 9.
+     */
+    @Override
+    public final List<byte[]> getStatusResponses() {
+        return delegate.getStatusResponses();
+    }
+
+    @Override
+    public final byte[] getPeerSignedCertificateTimestamp() {
+        return delegate.getPeerSignedCertificateTimestamp();
+    }
+
+    @Override
+    public final byte[] getId() {
+        return delegate.getId();
+    }
+
+    @Override
+    public final SSLSessionContext getSessionContext() {
+        return delegate.getSessionContext();
+    }
+
+    @Override
+    public final long getCreationTime() {
+        return delegate.getCreationTime();
+    }
+
+    @Override
+    public final long getLastAccessedTime() {
+        return delegate.getLastAccessedTime();
+    }
+
+    @Override
+    public final void invalidate() {
+        delegate.invalidate();
+    }
+
+    @Override
+    public final boolean isValid() {
+        return delegate.isValid();
+    }
+
+    @Override
+    public final void putValue(String s, Object o) {
+        delegate.putValue(this, s, o);
+    }
+
+    @Override
+    public final Object getValue(String s) {
+        return delegate.getValue(s);
+    }
+
+    @Override
+    public final void removeValue(String s) {
+        delegate.removeValue(this, s);
+    }
+
+    @Override
+    public final String[] getValueNames() {
+        return delegate.getValueNames();
+    }
+
+    @Override
+    public java.security.cert.X509Certificate[] getPeerCertificates()
+        throws SSLPeerUnverifiedException {
+        return delegate.getPeerCertificates();
+    }
+
+    @Override
+    public final Certificate[] getLocalCertificates() {
+        return delegate.getLocalCertificates();
+    }
+
+    @Override
+    public final X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
+        return delegate.getPeerCertificateChain();
+    }
+
+    @Override
+    public final Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+        return delegate.getPeerPrincipal();
+    }
+
+    @Override
+    public final Principal getLocalPrincipal() {
+        return delegate.getLocalPrincipal();
+    }
+
+    @Override
+    public final String getCipherSuite() {
+        return delegate.getCipherSuite();
+    }
+
+    @Override
+    public final String getProtocol() {
+        return delegate.getProtocol();
+    }
+
+    @Override
+    public final String getPeerHost() {
+        return delegate.getPeerHost();
+    }
+
+    @Override
+    public final int getPeerPort() {
+        return delegate.getPeerPort();
+    }
+
+    @Override
+    public final int getPacketBufferSize() {
+        return delegate.getPacketBufferSize();
+    }
+
+    @Override
+    public final int getApplicationBufferSize() {
+        return delegate.getApplicationBufferSize();
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/Java8EngineSocket.java b/repackaged/common/src/main/java/com/android/org/conscrypt/Java8EngineSocket.java
new file mode 100644
index 0000000..b1c852b
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/Java8EngineSocket.java
@@ -0,0 +1,93 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.List;
+import java.util.function.BiFunction;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSocket;
+
+/**
+ * A version of ConscryptEngineSocket that includes the new Java 9 (and potentially later
+ * patches of 8) {@code setHandshakeApplicationProtocolSelector} API (which requires Java 8 for
+ * compilation, due to the use of {@link BiFunction}).
+ */
+final class Java8EngineSocket extends ConscryptEngineSocket {
+    private BiFunction<SSLSocket, List<String>, String> selector;
+
+    Java8EngineSocket(SSLParametersImpl sslParameters) throws IOException {
+        super(sslParameters);
+    }
+
+    Java8EngineSocket(String hostname, int port, SSLParametersImpl sslParameters)
+            throws IOException {
+        super(hostname, port, sslParameters);
+    }
+
+    Java8EngineSocket(InetAddress address, int port, SSLParametersImpl sslParameters)
+            throws IOException {
+        super(address, port, sslParameters);
+    }
+
+    Java8EngineSocket(String hostname, int port, InetAddress clientAddress, int clientPort,
+            SSLParametersImpl sslParameters) throws IOException {
+        super(hostname, port, clientAddress, clientPort, sslParameters);
+    }
+
+    Java8EngineSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort,
+            SSLParametersImpl sslParameters) throws IOException {
+        super(address, port, clientAddress, clientPort, sslParameters);
+    }
+
+    Java8EngineSocket(Socket socket, String hostname, int port, boolean autoClose,
+            SSLParametersImpl sslParameters) throws IOException {
+        super(socket, hostname, port, autoClose, sslParameters);
+    }
+
+    /* @Override */
+    @SuppressWarnings("MissingOverride") // For compilation with Java < 9.
+    public void setHandshakeApplicationProtocolSelector(
+            final BiFunction<SSLSocket, List<String>, String> selector) {
+        this.selector = selector;
+        setApplicationProtocolSelector(toApplicationProtocolSelector(selector));
+    }
+
+    /* @Override */
+    @SuppressWarnings("MissingOverride") // For compilation with Java < 9.
+    public BiFunction<SSLSocket, List<String>, String> getHandshakeApplicationProtocolSelector() {
+        return selector;
+    }
+
+    private static ApplicationProtocolSelector toApplicationProtocolSelector(
+        final BiFunction<SSLSocket, List<String>, String> selector) {
+        return selector == null ? null : new ApplicationProtocolSelector() {
+            @Override
+            public String selectApplicationProtocol(SSLEngine socket, List<String> protocols) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public String selectApplicationProtocol(SSLSocket socket, List<String> protocols) {
+                return selector.apply(socket, protocols);
+            }
+        };
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/Java8EngineWrapper.java b/repackaged/common/src/main/java/com/android/org/conscrypt/Java8EngineWrapper.java
new file mode 100644
index 0000000..6462219
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/Java8EngineWrapper.java
@@ -0,0 +1,342 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.Preconditions.checkNotNull;
+
+import java.nio.ByteBuffer;
+import java.security.PrivateKey;
+import java.util.List;
+import java.util.function.BiFunction;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+/**
+ * A wrapper around {@link ConscryptEngine} that adapts to the new Java 9 (and potentially later
+ * patches of 8) {@code setHandshakeApplicationProtocolSelector} API (which requires Java 8 for
+ * compilation, due to the use of {@link BiFunction}).
+ */
+final class Java8EngineWrapper extends AbstractConscryptEngine {
+    private final ConscryptEngine delegate;
+    private BiFunction<SSLEngine, List<String>, String> selector;
+
+    Java8EngineWrapper(ConscryptEngine delegate) {
+        this.delegate = checkNotNull(delegate, "delegate");
+    }
+
+    static SSLEngine getDelegate(SSLEngine engine) {
+        if (engine instanceof Java8EngineWrapper) {
+            return ((Java8EngineWrapper) engine).delegate;
+        }
+        return engine;
+    }
+
+    @Override
+    public SSLEngineResult wrap(ByteBuffer[] byteBuffers, ByteBuffer byteBuffer)
+            throws SSLException {
+        return delegate.wrap(byteBuffers, byteBuffer);
+    }
+
+    @Override
+    public SSLParameters getSSLParameters() {
+        return delegate.getSSLParameters();
+    }
+
+    @Override
+    public void setSSLParameters(SSLParameters sslParameters) {
+        delegate.setSSLParameters(sslParameters);
+    }
+
+    @Override
+    void setBufferAllocator(BufferAllocator bufferAllocator) {
+        delegate.setBufferAllocator(bufferAllocator);
+    }
+
+    @Override
+    int maxSealOverhead() {
+        return delegate.maxSealOverhead();
+    }
+
+    @Override
+    void setChannelIdEnabled(boolean enabled) {
+        delegate.setChannelIdEnabled(enabled);
+    }
+
+    @Override
+    byte[] getChannelId() throws SSLException {
+        return delegate.getChannelId();
+    }
+
+    @Override
+    void setChannelIdPrivateKey(PrivateKey privateKey) {
+        delegate.setChannelIdPrivateKey(privateKey);
+    }
+
+    @Override
+    void setHandshakeListener(HandshakeListener handshakeListener) {
+        delegate.setHandshakeListener(handshakeListener);
+    }
+
+    @Override
+    void setHostname(String hostname) {
+        delegate.setHostname(hostname);
+    }
+
+    @Override
+    String getHostname() {
+        return delegate.getHostname();
+    }
+
+    @Override
+    public String getPeerHost() {
+        return delegate.getPeerHost();
+    }
+
+    @Override
+    public int getPeerPort() {
+        return delegate.getPeerPort();
+    }
+
+    @Override
+    public void beginHandshake() throws SSLException {
+        delegate.beginHandshake();
+    }
+
+    @Override
+    public void closeInbound() throws SSLException {
+        delegate.closeInbound();
+    }
+
+    @Override
+    public void closeOutbound() {
+        delegate.closeOutbound();
+    }
+
+    @Override
+    public Runnable getDelegatedTask() {
+        return delegate.getDelegatedTask();
+    }
+
+    @Override
+    public String[] getEnabledCipherSuites() {
+        return delegate.getEnabledCipherSuites();
+    }
+
+    @Override
+    public String[] getEnabledProtocols() {
+        return delegate.getEnabledProtocols();
+    }
+
+    @Override
+    public boolean getEnableSessionCreation() {
+        return delegate.getEnableSessionCreation();
+    }
+
+    @Override
+    public HandshakeStatus getHandshakeStatus() {
+        return delegate.getHandshakeStatus();
+    }
+
+    @Override
+    public boolean getNeedClientAuth() {
+        return delegate.getNeedClientAuth();
+    }
+
+    @Override
+    SSLSession handshakeSession() {
+        return delegate.handshakeSession();
+    }
+
+    @Override
+    public SSLSession getSession() {
+        return delegate.getSession();
+    }
+
+    @Override
+    public String[] getSupportedCipherSuites() {
+        return delegate.getSupportedCipherSuites();
+    }
+
+    @Override
+    public String[] getSupportedProtocols() {
+        return delegate.getSupportedProtocols();
+    }
+
+    @Override
+    public boolean getUseClientMode() {
+        return delegate.getUseClientMode();
+    }
+
+    @Override
+    public boolean getWantClientAuth() {
+        return delegate.getWantClientAuth();
+    }
+
+    @Override
+    public boolean isInboundDone() {
+        return delegate.isInboundDone();
+    }
+
+    @Override
+    public boolean isOutboundDone() {
+        return delegate.isOutboundDone();
+    }
+
+    @Override
+    public void setEnabledCipherSuites(String[] suites) {
+        delegate.setEnabledCipherSuites(suites);
+    }
+
+    @Override
+    public void setEnabledProtocols(String[] protocols) {
+        delegate.setEnabledProtocols(protocols);
+    }
+
+    @Override
+    public void setEnableSessionCreation(boolean flag) {
+        delegate.setEnableSessionCreation(flag);
+    }
+
+    @Override
+    public void setNeedClientAuth(boolean need) {
+        delegate.setNeedClientAuth(need);
+    }
+
+    @Override
+    public void setUseClientMode(boolean mode) {
+        delegate.setUseClientMode(mode);
+    }
+
+    @Override
+    public void setWantClientAuth(boolean want) {
+        delegate.setWantClientAuth(want);
+    }
+
+    @Override
+    public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer dst) throws SSLException {
+        return delegate.unwrap(src, dst);
+    }
+
+    @Override
+    public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts) throws SSLException {
+        return delegate.unwrap(src, dsts);
+    }
+
+    @Override
+    public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts, int offset, int length)
+            throws SSLException {
+        return delegate.unwrap(src, dsts, offset, length);
+    }
+
+    @Override
+    SSLEngineResult unwrap(ByteBuffer[] srcs, ByteBuffer[] dsts) throws SSLException {
+        return delegate.unwrap(srcs, dsts);
+    }
+
+    @Override
+    SSLEngineResult unwrap(ByteBuffer[] srcs, int srcsOffset, int srcsLength, ByteBuffer[] dsts,
+            int dstsOffset, int dstsLength) throws SSLException {
+        return delegate.unwrap(srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
+    }
+
+    @Override
+    public SSLEngineResult wrap(ByteBuffer src, ByteBuffer dst) throws SSLException {
+        return delegate.wrap(src, dst);
+    }
+
+    @Override
+    public SSLEngineResult wrap(ByteBuffer[] srcs, int srcsOffset, int srcsLength, ByteBuffer dst)
+            throws SSLException {
+        return delegate.wrap(srcs, srcsOffset, srcsLength, dst);
+    }
+
+    @Override
+    void setUseSessionTickets(boolean useSessionTickets) {
+        delegate.setUseSessionTickets(useSessionTickets);
+    }
+
+    @Override
+    void setApplicationProtocols(String[] protocols) {
+        delegate.setApplicationProtocols(protocols);
+    }
+
+    @Override
+    String[] getApplicationProtocols() {
+        return delegate.getApplicationProtocols();
+    }
+
+    @Override
+    public String getApplicationProtocol() {
+        return delegate.getApplicationProtocol();
+    }
+
+    @Override
+    void setApplicationProtocolSelector(ApplicationProtocolSelector selector) {
+        delegate.setApplicationProtocolSelector(
+                selector == null ? null : new ApplicationProtocolSelectorAdapter(this, selector));
+    }
+
+    @Override
+    byte[] getTlsUnique() {
+        return delegate.getTlsUnique();
+    }
+
+    @Override
+    byte[] exportKeyingMaterial(String label, byte[] context, int length) throws SSLException {
+        return delegate.exportKeyingMaterial(label, context, length);
+    }
+
+    @Override
+    public String getHandshakeApplicationProtocol() {
+        return delegate.getHandshakeApplicationProtocol();
+    }
+
+    /* @Override */
+    @SuppressWarnings("MissingOverride") // For compilation with Java < 9.
+    public void setHandshakeApplicationProtocolSelector(
+            final BiFunction<SSLEngine, List<String>, String> selector) {
+        this.selector = selector;
+        setApplicationProtocolSelector(toApplicationProtocolSelector(selector));
+    }
+
+    /* @Override */
+    @SuppressWarnings("MissingOverride") // For compilation with Java < 9.
+    public BiFunction<SSLEngine, List<String>, String> getHandshakeApplicationProtocolSelector() {
+        return selector;
+    }
+
+    private static ApplicationProtocolSelector toApplicationProtocolSelector(
+            final BiFunction<SSLEngine, List<String>, String> selector) {
+        return selector == null ? null : new ApplicationProtocolSelector() {
+            @Override
+            public String selectApplicationProtocol(SSLEngine engine, List<String> protocols) {
+                return selector.apply(engine, protocols);
+            }
+
+            @Override
+            public String selectApplicationProtocol(SSLSocket socket, List<String> protocols) {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/Java8ExtendedSSLSession.java b/repackaged/common/src/main/java/com/android/org/conscrypt/Java8ExtendedSSLSession.java
new file mode 100644
index 0000000..16c4c38
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/Java8ExtendedSSLSession.java
@@ -0,0 +1,44 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.util.Collections;
+import java.util.List;
+import javax.net.ssl.ExtendedSSLSession;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIServerName;
+
+/**
+ * This is an adapter that wraps the active session with {@link ExtendedSSLSession}, if running
+ * on Java 8+.
+ */
+class Java8ExtendedSSLSession extends Java7ExtendedSSLSession {
+    public Java8ExtendedSSLSession(ExternalSession delegate) {
+        super(delegate);
+    }
+
+  @Override
+  public final List<SNIServerName> getRequestedServerNames() {
+      String requestedServerName = delegate.getRequestedServerName();
+      if (requestedServerName == null) {
+        return null;
+      }
+
+      return Collections.singletonList((SNIServerName) new SNIHostName(requestedServerName));
+  }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/Java8FileDescriptorSocket.java b/repackaged/common/src/main/java/com/android/org/conscrypt/Java8FileDescriptorSocket.java
new file mode 100644
index 0000000..2d9f3bc
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/Java8FileDescriptorSocket.java
@@ -0,0 +1,93 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.List;
+import java.util.function.BiFunction;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSocket;
+
+/**
+ * A version of ConscryptFileDescriptorSocket that includes the new Java 9 (and potentially later
+ * patches of 8) {@code setHandshakeApplicationProtocolSelector} API (which requires Java 8 for
+ * compilation, due to the use of {@link BiFunction}).
+ */
+final class Java8FileDescriptorSocket extends ConscryptFileDescriptorSocket {
+    private BiFunction<SSLSocket, List<String>, String> selector;
+
+    Java8FileDescriptorSocket(SSLParametersImpl sslParameters) throws IOException {
+        super(sslParameters);
+    }
+
+    Java8FileDescriptorSocket(String hostname, int port, SSLParametersImpl sslParameters)
+            throws IOException {
+        super(hostname, port, sslParameters);
+    }
+
+    Java8FileDescriptorSocket(InetAddress address, int port, SSLParametersImpl sslParameters)
+            throws IOException {
+        super(address, port, sslParameters);
+    }
+
+    Java8FileDescriptorSocket(String hostname, int port, InetAddress clientAddress, int clientPort,
+            SSLParametersImpl sslParameters) throws IOException {
+        super(hostname, port, clientAddress, clientPort, sslParameters);
+    }
+
+    Java8FileDescriptorSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort,
+            SSLParametersImpl sslParameters) throws IOException {
+        super(address, port, clientAddress, clientPort, sslParameters);
+    }
+
+    Java8FileDescriptorSocket(Socket socket, String hostname, int port, boolean autoClose,
+            SSLParametersImpl sslParameters) throws IOException {
+        super(socket, hostname, port, autoClose, sslParameters);
+    }
+
+    /* @Override */
+    @SuppressWarnings("MissingOverride") // For compilation with Java < 9.
+    public void setHandshakeApplicationProtocolSelector(
+            final BiFunction<SSLSocket, List<String>, String> selector) {
+        this.selector = selector;
+        setApplicationProtocolSelector(toApplicationProtocolSelector(selector));
+    }
+
+    /* @Override */
+    @SuppressWarnings("MissingOverride") // For compilation with Java < 9.
+    public BiFunction<SSLSocket, List<String>, String> getHandshakeApplicationProtocolSelector() {
+        return selector;
+    }
+
+    private static ApplicationProtocolSelector toApplicationProtocolSelector(
+        final BiFunction<SSLSocket, List<String>, String> selector) {
+        return selector == null ? null : new ApplicationProtocolSelector() {
+            @Override
+            public String selectApplicationProtocol(SSLEngine socket, List<String> protocols) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public String selectApplicationProtocol(SSLSocket socket, List<String> protocols) {
+                return selector.apply(socket, protocols);
+            }
+        };
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/KeyGeneratorImpl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/KeyGeneratorImpl.java
new file mode 100644
index 0000000..c1fc87d
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/KeyGeneratorImpl.java
@@ -0,0 +1,245 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.KeyGeneratorSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.DESedeKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * An implementation of {@link javax.crypto.KeyGenerator} suitable for use with other Conscrypt
+ * algorithms.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public abstract class KeyGeneratorImpl extends KeyGeneratorSpi {
+    private final String algorithm;
+    protected SecureRandom secureRandom;
+    private int keySizeBits;
+
+    private KeyGeneratorImpl(String algorithm, int defaultKeySizeBits) {
+        this.algorithm = algorithm;
+        this.keySizeBits = defaultKeySizeBits;
+    }
+
+    protected void checkKeySize(int keySize) {
+        if (keySize <= 0) {
+            throw new InvalidParameterException("Key size must be positive");
+        }
+    }
+
+    @Override
+    protected void engineInit(SecureRandom secureRandom) {
+        this.secureRandom = secureRandom;
+    }
+
+    @Override
+    protected void engineInit(AlgorithmParameterSpec params, SecureRandom secureRandom)
+            throws InvalidAlgorithmParameterException {
+        if (params == null) {
+            throw new InvalidAlgorithmParameterException("No params provided");
+        } else {
+            throw new InvalidAlgorithmParameterException(
+                    "Unknown param type: " + params.getClass().getName());
+        }
+    }
+
+    @Override
+    protected void engineInit(int keySize, SecureRandom secureRandom) {
+        checkKeySize(keySize);
+        this.keySizeBits = keySize;
+        this.secureRandom = secureRandom;
+    }
+
+    protected byte[] doKeyGeneration(int keyBytes) {
+        byte[] keyData = new byte[keyBytes];
+        secureRandom.nextBytes(keyData);
+        return keyData;
+    }
+
+    @Override
+    protected SecretKey engineGenerateKey() {
+        if (secureRandom == null) {
+            secureRandom = new SecureRandom();
+        }
+
+        return new SecretKeySpec(doKeyGeneration((keySizeBits + 7) / 8), algorithm);
+    }
+
+    // For HMAC, RFC 2104 recommends using the hash's output length as the key length
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class HmacMD5 extends KeyGeneratorImpl {
+        @libcore.api.IntraCoreApi
+        public HmacMD5() {
+            super("HmacMD5", 128);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class HmacSHA1 extends KeyGeneratorImpl {
+        @libcore.api.IntraCoreApi
+        public HmacSHA1() {
+            super("HmacSHA1", 160);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class HmacSHA224 extends KeyGeneratorImpl {
+        @libcore.api.IntraCoreApi
+        public HmacSHA224() {
+            super("HmacSHA224", 224);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class HmacSHA256 extends KeyGeneratorImpl {
+        @libcore.api.IntraCoreApi
+        public HmacSHA256() {
+            super("HmacSHA256", 256);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class HmacSHA384 extends KeyGeneratorImpl {
+        @libcore.api.IntraCoreApi
+        public HmacSHA384() {
+            super("HmacSHA384", 384);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class HmacSHA512 extends KeyGeneratorImpl {
+        @libcore.api.IntraCoreApi
+        public HmacSHA512() {
+            super("HmacSHA512", 512);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class DESEDE extends KeyGeneratorImpl {
+        @libcore.api.IntraCoreApi
+        public DESEDE() {
+            super("DESEDE", 192);
+        }
+
+        @Override
+        protected void checkKeySize(int keySize) {
+            if ((keySize != 112) && (keySize != 168)) {
+                throw new InvalidParameterException("Key size must be either 112 or 168 bits");
+            }
+        }
+
+        @Override
+        protected byte[] doKeyGeneration(int keyBytes) {
+            byte[] keyData = new byte[DESedeKeySpec.DES_EDE_KEY_LEN];
+            secureRandom.nextBytes(keyData);
+            // Set the parity bit for each byte
+            for (int i = 0; i < keyData.length; i++) {
+                if (Integer.bitCount(keyData[i]) % 2 == 0) {
+                    keyData[i] = (byte) (keyData[i] ^ 1);
+                }
+            }
+            if (keyBytes == 14) {
+                // The user requested an A-B-A key
+                System.arraycopy(keyData, 0, keyData, 16, 8);
+            }
+            return keyData;
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class AES extends KeyGeneratorImpl {
+        @libcore.api.IntraCoreApi
+        public AES() {
+            super("AES", 128);
+        }
+
+        @Override
+        protected void checkKeySize(int keySize) {
+            if ((keySize != 128) && (keySize != 192) && (keySize != 256)) {
+                throw new InvalidParameterException(
+                        "Key size must be either 128, 192, or 256 bits");
+            }
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class ChaCha20 extends KeyGeneratorImpl {
+        @libcore.api.IntraCoreApi
+        public ChaCha20() {
+            super("ChaCha20", 256);
+        }
+
+        @Override
+        protected void checkKeySize(int keySize) {
+            if (keySize != 256) {
+                throw new InvalidParameterException("Key size must be 256 bits");
+            }
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class ARC4 extends KeyGeneratorImpl {
+        @libcore.api.IntraCoreApi
+        public ARC4() {
+            super("ARC4", 128);
+        }
+
+        @Override
+        protected void checkKeySize(int keySize) {
+            if (keySize < 40 || 2048 < keySize) {
+                throw new InvalidParameterException("Key size must be between 40 and 2048 bits");
+            }
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/KeyManagerFactoryImpl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/KeyManagerFactoryImpl.java
new file mode 100644
index 0000000..5b02be2
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/KeyManagerFactoryImpl.java
@@ -0,0 +1,117 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactorySpi;
+import javax.net.ssl.ManagerFactoryParameters;
+
+/**
+ * KeyManagerFactory implementation.
+ * @see KeyManagerFactorySpi
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public class KeyManagerFactoryImpl extends KeyManagerFactorySpi {
+
+    // source of key material
+    private KeyStore keyStore;
+
+    //password
+    private char[] pwd;
+
+    /**
+     * @see KeyManagerFactorySpi#engineInit(KeyStore ks, char[] password)
+     */
+    @Override
+    protected void engineInit(KeyStore ks, char[] password)
+            throws KeyStoreException, NoSuchAlgorithmException,
+            UnrecoverableKeyException {
+        if (ks != null) {
+            keyStore = ks;
+            if (password != null) {
+                pwd = password.clone();
+            } else {
+                pwd = EmptyArray.CHAR;
+            }
+        } else {
+            keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+            String keyStoreName = System.getProperty("javax.net.ssl.keyStore");
+            String keyStorePwd = null;
+            if (keyStoreName == null || keyStoreName.equalsIgnoreCase("NONE") || keyStoreName.isEmpty()) {
+                try {
+                    keyStore.load(null, null);
+                } catch (IOException e) {
+                    throw new KeyStoreException(e);
+                } catch (CertificateException e) {
+                    throw new KeyStoreException(e);
+                }
+            } else {
+                keyStorePwd = System.getProperty("javax.net.ssl.keyStorePassword");
+                if (keyStorePwd == null) {
+                    pwd = EmptyArray.CHAR;
+                } else {
+                    pwd = keyStorePwd.toCharArray();
+                }
+                try {
+                    keyStore.load(new FileInputStream(new File(keyStoreName)), pwd);
+                } catch (FileNotFoundException e) {
+                    throw new KeyStoreException(e);
+                } catch (IOException e) {
+                    throw new KeyStoreException(e);
+                } catch (CertificateException e) {
+                    throw new KeyStoreException(e);
+                }
+            }
+
+        }
+
+    }
+
+    /**
+     * @see KeyManagerFactorySpi#engineInit(ManagerFactoryParameters spec)
+     */
+    @Override
+    protected void engineInit(ManagerFactoryParameters spec)
+            throws InvalidAlgorithmParameterException {
+        throw new InvalidAlgorithmParameterException(
+                "ManagerFactoryParameters not supported");
+
+    }
+
+    /**
+     * @see KeyManagerFactorySpi#engineGetKeyManagers()
+     */
+    @Override
+    protected KeyManager[] engineGetKeyManagers() {
+        if (keyStore == null) {
+            throw new IllegalStateException("KeyManagerFactory is not initialized");
+        }
+        return new KeyManager[] { new KeyManagerImpl(keyStore, pwd) };
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/KeyManagerImpl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/KeyManagerImpl.java
new file mode 100644
index 0000000..4682e1b
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/KeyManagerImpl.java
@@ -0,0 +1,232 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+import java.net.Socket;
+import java.security.KeyStore;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.UnrecoverableEntryException;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * KeyManager implementation.
+ *
+ * This implementation uses hashed key store information. It works faster than retrieving all of the
+ * data from the key store. Any key store changes, that happen after key manager was created, have
+ * no effect. The implementation does not use peer information (host, port) that may be obtained
+ * from socket or engine.
+ *
+ * @see javax.net.ssl.KeyManager
+ */
+class KeyManagerImpl extends X509ExtendedKeyManager {
+
+    // hashed key store information
+    private final HashMap<String, PrivateKeyEntry> hash;
+
+    /**
+     * Creates Key manager
+     */
+    KeyManagerImpl(KeyStore keyStore, char[] pwd) {
+        this.hash = new HashMap<String, PrivateKeyEntry>();
+        final Enumeration<String> aliases;
+        try {
+            aliases = keyStore.aliases();
+        } catch (KeyStoreException e) {
+            return;
+        }
+        for (; aliases.hasMoreElements();) {
+            final String alias = aliases.nextElement();
+            try {
+                if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {
+                    KeyStore.PrivateKeyEntry entry;
+                    try {
+                        entry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(
+                                alias, new KeyStore.PasswordProtection(pwd));
+                    } catch (UnsupportedOperationException e) {
+                        // If the KeyStore doesn't support getEntry(), as Android Keystore
+                        // doesn't, fall back to reading the two values separately.
+                        PrivateKey key = (PrivateKey) keyStore.getKey(alias, pwd);
+                        Certificate[] certs = keyStore.getCertificateChain(alias);
+                        entry = new KeyStore.PrivateKeyEntry(key, certs);
+                    }
+                    hash.put(alias, entry);
+                }
+            } catch (KeyStoreException ignored) {
+                // Ignored.
+            } catch (UnrecoverableEntryException ignored) {
+                // Ignored.
+            } catch (NoSuchAlgorithmException ignored) {
+                // Ignored.
+            }
+        }
+    }
+
+    @Override
+    public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
+        final String[] al = chooseAlias(keyTypes, issuers);
+        return (al == null ? null : al[0]);
+    }
+
+    @Override
+    public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
+        final String[] al = chooseAlias(new String[] { keyType }, issuers);
+        return (al == null ? null : al[0]);
+    }
+
+    @Override
+    public X509Certificate[] getCertificateChain(String alias) {
+        if (alias == null) {
+            return null;
+        }
+        if (hash.containsKey(alias)) {
+            Certificate[] certs = hash.get(alias).getCertificateChain();
+            if (certs[0] instanceof X509Certificate) {
+                X509Certificate[] xcerts = new X509Certificate[certs.length];
+                for (int i = 0; i < certs.length; i++) {
+                    xcerts[i] = (X509Certificate) certs[i];
+                }
+                return xcerts;
+            }
+        }
+        return null;
+
+    }
+
+    @Override
+    public String[] getClientAliases(String keyType, Principal[] issuers) {
+        return chooseAlias(new String[] { keyType }, issuers);
+    }
+
+    @Override
+    public String[] getServerAliases(String keyType, Principal[] issuers) {
+        return chooseAlias(new String[] { keyType }, issuers);
+    }
+
+    @Override
+    public PrivateKey getPrivateKey(String alias) {
+        if (alias == null) {
+            return null;
+        }
+        if (hash.containsKey(alias)) {
+            return hash.get(alias).getPrivateKey();
+        }
+        return null;
+    }
+
+    @Override
+    public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine) {
+        final String[] al = chooseAlias(keyTypes, issuers);
+        return (al == null ? null : al[0]);
+    }
+
+    @Override
+    public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) {
+        final String[] al = chooseAlias(new String[] { keyType }, issuers);
+        return (al == null ? null : al[0]);
+    }
+
+    private String[] chooseAlias(String[] keyTypes, Principal[] issuers) {
+        if (keyTypes == null || keyTypes.length == 0) {
+            return null;
+        }
+        List<Principal> issuersList = (issuers == null) ? null : Arrays.asList(issuers);
+        ArrayList<String> found = new ArrayList<String>();
+        for (final Map.Entry<String, PrivateKeyEntry> entry : hash.entrySet()) {
+            final String alias = entry.getKey();
+            final Certificate[] chain = entry.getValue().getCertificateChain();
+            final Certificate cert = chain[0];
+            final String certKeyAlg = cert.getPublicKey().getAlgorithm();
+            final String certSigAlg = (cert instanceof X509Certificate
+                                       ? ((X509Certificate) cert).getSigAlgName().toUpperCase(Locale.US)
+                                       : null);
+            for (String keyAlgorithm : keyTypes) {
+                if (keyAlgorithm == null) {
+                    continue;
+                }
+                final String sigAlgorithm;
+                // handle cases like EC_EC and EC_RSA
+                int index = keyAlgorithm.indexOf('_');
+                if (index == -1) {
+                    sigAlgorithm = null;
+                } else {
+                    sigAlgorithm = keyAlgorithm.substring(index + 1);
+                    keyAlgorithm = keyAlgorithm.substring(0, index);
+                }
+                // key algorithm does not match
+                if (!certKeyAlg.equals(keyAlgorithm)) {
+                    continue;
+                }
+                /*
+                 * TODO find a more reliable test for signature
+                 * algorithm. Unfortunately value varies with
+                 * provider. For example for "EC" it could be
+                 * "SHA1WithECDSA" or simply "ECDSA".
+                 */
+                // sig algorithm does not match
+                if (sigAlgorithm != null && certSigAlg != null
+                        && !certSigAlg.contains(sigAlgorithm)) {
+                    continue;
+                }
+                // no issuers to match, just add to return list and continue
+                if (issuers == null || issuers.length == 0) {
+                    found.add(alias);
+                    continue;
+                }
+                // check that a certificate in the chain was issued by one of the specified issuers
+                for (Certificate certFromChain : chain) {
+                    if (!(certFromChain instanceof X509Certificate)) {
+                        // skip non-X509Certificates
+                        continue;
+                    }
+                    X509Certificate xcertFromChain = (X509Certificate) certFromChain;
+                    /*
+                     * Note use of X500Principal from
+                     * getIssuerX500Principal as opposed to Principal
+                     * from getIssuerDN. Principal.equals test does
+                     * not work in the case where
+                     * xcertFromChain.getIssuerDN is a bouncycastle
+                     * org.bouncycastle.jce.X509Principal.
+                     */
+                    X500Principal issuerFromChain = xcertFromChain.getIssuerX500Principal();
+                    if (issuersList.contains(issuerFromChain)) {
+                        found.add(alias);
+                    }
+                }
+            }
+        }
+        if (!found.isEmpty()) {
+            return found.toArray(new String[found.size()]);
+        }
+        return null;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/NativeCrypto.java b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeCrypto.java
new file mode 100644
index 0000000..a313f7c
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeCrypto.java
@@ -0,0 +1,1472 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.SocketTimeoutException;
+import java.nio.Buffer;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.SignatureException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateParsingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+import javax.net.ssl.SSLException;
+import javax.security.auth.x500.X500Principal;
+import com.android.org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
+
+/**
+ * Provides the Java side of our JNI glue for OpenSSL.
+ * <p>
+ * Note: Many methods in this class take a reference to a Java object that holds a
+ * native pointer in the form of a long in addition to the long itself and don't use
+ * the Java object in the native implementation.  This is to prevent the Java object
+ * from becoming eligible for GC while the native method is executing.  See
+ * <a href="https://github.com/google/error-prone/blob/master/docs/bugpattern/UnsafeFinalization.md">this</a>
+ * for more details.
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public final class NativeCrypto {
+    // --- OpenSSL library initialization --------------------------------------
+    private static final UnsatisfiedLinkError loadError;
+    static {
+        UnsatisfiedLinkError error = null;
+        try {
+            NativeCryptoJni.init();
+            clinit();
+        } catch (UnsatisfiedLinkError t) {
+            // Don't rethrow the error, so that we can later on interrogate the
+            // value of loadError.
+            error = t;
+        }
+        loadError = error;
+    }
+
+    private native static void clinit();
+
+    /**
+     * Checks to see whether or not the native library was successfully loaded. If not, throws
+     * the {@link UnsatisfiedLinkError} that was encountered while attempting to load the library.
+     */
+    static void checkAvailability() {
+        if (loadError != null) {
+            throw loadError;
+        }
+    }
+
+    // --- DSA/RSA public/private key handling functions -----------------------
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q,
+            byte[] dmp1, byte[] dmq1, byte[] iqmp);
+
+    static native int EVP_PKEY_type(NativeRef.EVP_PKEY pkey);
+
+    static native String EVP_PKEY_print_public(NativeRef.EVP_PKEY pkeyRef);
+
+    static native String EVP_PKEY_print_params(NativeRef.EVP_PKEY pkeyRef);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native void EVP_PKEY_free(long pkey);
+
+    static native int EVP_PKEY_cmp(NativeRef.EVP_PKEY pkey1, NativeRef.EVP_PKEY pkey2);
+
+    static native byte[] EVP_marshal_private_key(NativeRef.EVP_PKEY pkey);
+
+    static native long EVP_parse_private_key(byte[] data) throws ParsingException;
+
+    static native byte[] EVP_marshal_public_key(NativeRef.EVP_PKEY pkey);
+
+    static native long EVP_parse_public_key(byte[] data) throws ParsingException;
+
+    static native long PEM_read_bio_PUBKEY(long bioCtx);
+
+    static native long PEM_read_bio_PrivateKey(long bioCtx);
+
+    static native long getRSAPrivateKeyWrapper(PrivateKey key, byte[] modulus);
+
+    static native long getECPrivateKeyWrapper(PrivateKey key, NativeRef.EC_GROUP ecGroupRef);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long RSA_generate_key_ex(int modulusBits, byte[] publicExponent);
+
+    static native int RSA_size(NativeRef.EVP_PKEY pkey);
+
+    static native int RSA_private_encrypt(
+            int flen, byte[] from, byte[] to, NativeRef.EVP_PKEY pkey, int padding);
+
+    static native int RSA_public_decrypt(int flen, byte[] from, byte[] to, NativeRef.EVP_PKEY pkey,
+            int padding) throws BadPaddingException, SignatureException;
+
+    static native int RSA_public_encrypt(
+            int flen, byte[] from, byte[] to, NativeRef.EVP_PKEY pkey, int padding);
+
+    static native int RSA_private_decrypt(int flen, byte[] from, byte[] to, NativeRef.EVP_PKEY pkey,
+            int padding) throws BadPaddingException, SignatureException;
+
+    /**
+     * @return array of {n, e}
+     */
+    static native byte[][] get_RSA_public_params(NativeRef.EVP_PKEY rsa);
+
+    /**
+     * @return array of {n, e, d, p, q, dmp1, dmq1, iqmp}
+     */
+    static native byte[][] get_RSA_private_params(NativeRef.EVP_PKEY rsa);
+
+    // --- ChaCha20 -----------------------
+
+    /**
+     * Returns the encrypted or decrypted version of the data.
+     */
+    static native void chacha20_encrypt_decrypt(byte[] in, int inOffset, byte[] out, int outOffset,
+            int length, byte[] key, byte[] nonce, int blockCounter);
+
+    // --- EC functions --------------------------
+
+    static native long EVP_PKEY_new_EC_KEY(
+            NativeRef.EC_GROUP groupRef, NativeRef.EC_POINT pubkeyRef, byte[] privkey);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long EC_GROUP_new_by_curve_name(String curveName);
+
+    static native long EC_GROUP_new_arbitrary(
+            byte[] p, byte[] a, byte[] b, byte[] x, byte[] y, byte[] order, int cofactor);
+
+    static native String EC_GROUP_get_curve_name(NativeRef.EC_GROUP groupRef);
+
+    static native byte[][] EC_GROUP_get_curve(NativeRef.EC_GROUP groupRef);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native void EC_GROUP_clear_free(long groupRef);
+
+    static native long EC_GROUP_get_generator(NativeRef.EC_GROUP groupRef);
+
+    static native byte[] EC_GROUP_get_order(NativeRef.EC_GROUP groupRef);
+
+    static native int EC_GROUP_get_degree(NativeRef.EC_GROUP groupRef);
+
+    static native byte[] EC_GROUP_get_cofactor(NativeRef.EC_GROUP groupRef);
+
+    static native long EC_POINT_new(NativeRef.EC_GROUP groupRef);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native void EC_POINT_clear_free(long pointRef);
+
+    static native byte[][] EC_POINT_get_affine_coordinates(
+            NativeRef.EC_GROUP groupRef, NativeRef.EC_POINT pointRef);
+
+    static native void EC_POINT_set_affine_coordinates(
+            NativeRef.EC_GROUP groupRef, NativeRef.EC_POINT pointRef, byte[] x, byte[] y);
+
+    static native long EC_KEY_generate_key(NativeRef.EC_GROUP groupRef);
+
+    static native long EC_KEY_get1_group(NativeRef.EVP_PKEY pkeyRef);
+
+    static native byte[] EC_KEY_get_private_key(NativeRef.EVP_PKEY keyRef);
+
+    static native long EC_KEY_get_public_key(NativeRef.EVP_PKEY keyRef);
+
+    static native byte[] EC_KEY_marshal_curve_name(NativeRef.EC_GROUP groupRef) throws IOException;
+
+    static native long EC_KEY_parse_curve_name(byte[] encoded) throws IOException;
+
+    static native int ECDH_compute_key(byte[] out, int outOffset, NativeRef.EVP_PKEY publicKeyRef,
+            NativeRef.EVP_PKEY privateKeyRef) throws InvalidKeyException, IndexOutOfBoundsException;
+
+    static native int ECDSA_size(NativeRef.EVP_PKEY pkey);
+
+    static native int ECDSA_sign(byte[] data, byte[] sig, NativeRef.EVP_PKEY pkey);
+
+    static native int ECDSA_verify(byte[] data, byte[] sig, NativeRef.EVP_PKEY pkey);
+
+    // --- Message digest functions --------------
+
+    // These return const references
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long EVP_get_digestbyname(String name);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native int EVP_MD_size(long evp_md_const);
+
+    // --- Message digest context functions --------------
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long EVP_MD_CTX_create();
+
+    static native void EVP_MD_CTX_cleanup(NativeRef.EVP_MD_CTX ctx);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native void EVP_MD_CTX_destroy(long ctx);
+
+    static native int EVP_MD_CTX_copy_ex(
+            NativeRef.EVP_MD_CTX dst_ctx, NativeRef.EVP_MD_CTX src_ctx);
+
+    // --- Digest handling functions -------------------------------------------
+
+    static native int EVP_DigestInit_ex(NativeRef.EVP_MD_CTX ctx, long evp_md);
+
+    static native void EVP_DigestUpdate(
+            NativeRef.EVP_MD_CTX ctx, byte[] buffer, int offset, int length);
+
+    static native void EVP_DigestUpdateDirect(NativeRef.EVP_MD_CTX ctx, long ptr, int length);
+
+    static native int EVP_DigestFinal_ex(NativeRef.EVP_MD_CTX ctx, byte[] hash, int offset);
+
+    // --- Signature handling functions ----------------------------------------
+
+    static native long EVP_DigestSignInit(
+            NativeRef.EVP_MD_CTX ctx, long evpMdRef, NativeRef.EVP_PKEY key);
+
+    static native long EVP_DigestVerifyInit(
+            NativeRef.EVP_MD_CTX ctx, long evpMdRef, NativeRef.EVP_PKEY key);
+
+    static native void EVP_DigestSignUpdate(
+            NativeRef.EVP_MD_CTX ctx, byte[] buffer, int offset, int length);
+
+    static native void EVP_DigestSignUpdateDirect(NativeRef.EVP_MD_CTX ctx, long ptr, int length);
+
+    static native void EVP_DigestVerifyUpdate(
+            NativeRef.EVP_MD_CTX ctx, byte[] buffer, int offset, int length);
+
+    static native void EVP_DigestVerifyUpdateDirect(NativeRef.EVP_MD_CTX ctx, long ptr, int length);
+
+    static native byte[] EVP_DigestSignFinal(NativeRef.EVP_MD_CTX ctx);
+
+    static native boolean EVP_DigestVerifyFinal(NativeRef.EVP_MD_CTX ctx, byte[] signature,
+            int offset, int length) throws IndexOutOfBoundsException;
+
+    static native long EVP_PKEY_encrypt_init(NativeRef.EVP_PKEY pkey) throws InvalidKeyException;
+
+    static native int EVP_PKEY_encrypt(NativeRef.EVP_PKEY_CTX ctx, byte[] out, int outOffset,
+            byte[] input, int inOffset, int inLength)
+            throws IndexOutOfBoundsException, BadPaddingException;
+
+    static native long EVP_PKEY_decrypt_init(NativeRef.EVP_PKEY pkey) throws InvalidKeyException;
+
+    static native int EVP_PKEY_decrypt(NativeRef.EVP_PKEY_CTX ctx, byte[] out, int outOffset,
+            byte[] input, int inOffset, int inLength)
+            throws IndexOutOfBoundsException, BadPaddingException;
+
+    static native void EVP_PKEY_CTX_free(long pkeyCtx);
+
+    static native void EVP_PKEY_CTX_set_rsa_padding(long ctx, int pad)
+            throws InvalidAlgorithmParameterException;
+
+    static native void EVP_PKEY_CTX_set_rsa_pss_saltlen(long ctx, int len)
+            throws InvalidAlgorithmParameterException;
+
+    static native void EVP_PKEY_CTX_set_rsa_mgf1_md(long ctx, long evpMdRef)
+            throws InvalidAlgorithmParameterException;
+
+    static native void EVP_PKEY_CTX_set_rsa_oaep_md(long ctx, long evpMdRef)
+            throws InvalidAlgorithmParameterException;
+
+    static native void EVP_PKEY_CTX_set_rsa_oaep_label(long ctx, byte[] label)
+            throws InvalidAlgorithmParameterException;
+
+    // --- Block ciphers -------------------------------------------------------
+
+    // These return const references
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long EVP_get_cipherbyname(String string);
+
+    static native void EVP_CipherInit_ex(NativeRef.EVP_CIPHER_CTX ctx, long evpCipher, byte[] key,
+            byte[] iv, boolean encrypting);
+
+    static native int EVP_CipherUpdate(NativeRef.EVP_CIPHER_CTX ctx, byte[] out, int outOffset,
+            byte[] in, int inOffset, int inLength) throws IndexOutOfBoundsException;
+
+    static native int EVP_CipherFinal_ex(NativeRef.EVP_CIPHER_CTX ctx, byte[] out, int outOffset)
+            throws BadPaddingException, IllegalBlockSizeException;
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native int EVP_CIPHER_iv_length(long evpCipher);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long EVP_CIPHER_CTX_new();
+
+    static native int EVP_CIPHER_CTX_block_size(NativeRef.EVP_CIPHER_CTX ctx);
+
+    static native int get_EVP_CIPHER_CTX_buf_len(NativeRef.EVP_CIPHER_CTX ctx);
+
+    static native boolean get_EVP_CIPHER_CTX_final_used(NativeRef.EVP_CIPHER_CTX ctx);
+
+    static native void EVP_CIPHER_CTX_set_padding(
+            NativeRef.EVP_CIPHER_CTX ctx, boolean enablePadding);
+
+    static native void EVP_CIPHER_CTX_set_key_length(NativeRef.EVP_CIPHER_CTX ctx, int keyBitSize);
+
+    static native void EVP_CIPHER_CTX_free(long ctx);
+
+    // --- AEAD ----------------------------------------------------------------
+    static native long EVP_aead_aes_128_gcm();
+
+    static native long EVP_aead_aes_256_gcm();
+
+    static native long EVP_aead_chacha20_poly1305();
+
+    static native long EVP_aead_aes_128_gcm_siv();
+
+    static native long EVP_aead_aes_256_gcm_siv();
+
+    static native int EVP_AEAD_max_overhead(long evpAead);
+
+    static native int EVP_AEAD_nonce_length(long evpAead);
+
+    static native int EVP_AEAD_CTX_seal(long evpAead, byte[] key, int tagLengthInBytes, byte[] out,
+            int outOffset, byte[] nonce, byte[] in, int inOffset, int inLength, byte[] ad)
+            throws ShortBufferException, BadPaddingException, IndexOutOfBoundsException;
+
+    static native int EVP_AEAD_CTX_open(long evpAead, byte[] key, int tagLengthInBytes, byte[] out,
+            int outOffset, byte[] nonce, byte[] in, int inOffset, int inLength, byte[] ad)
+            throws ShortBufferException, BadPaddingException, IndexOutOfBoundsException;
+
+    // --- HMAC functions ------------------------------------------------------
+
+    static native long HMAC_CTX_new();
+
+    static native void HMAC_CTX_free(long ctx);
+
+    static native void HMAC_Init_ex(NativeRef.HMAC_CTX ctx, byte[] key, long evp_md);
+
+    static native void HMAC_Update(NativeRef.HMAC_CTX ctx, byte[] in, int inOffset, int inLength);
+
+    static native void HMAC_UpdateDirect(NativeRef.HMAC_CTX ctx, long inPtr, int inLength);
+
+    static native byte[] HMAC_Final(NativeRef.HMAC_CTX ctx);
+
+    // --- RAND ----------------------------------------------------------------
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native void RAND_bytes(byte[] output);
+
+    // --- X509_NAME -----------------------------------------------------------
+
+    static int X509_NAME_hash(X500Principal principal) {
+        return X509_NAME_hash(principal, "SHA1");
+    }
+
+    public static int X509_NAME_hash_old(X500Principal principal) {
+        return X509_NAME_hash(principal, "MD5");
+    }
+    private static int X509_NAME_hash(X500Principal principal, String algorithm) {
+        try {
+            byte[] digest = MessageDigest.getInstance(algorithm).digest(principal.getEncoded());
+            int offset = 0;
+            return (((digest[offset++] & 0xff) << 0) | ((digest[offset++] & 0xff) << 8)
+                    | ((digest[offset++] & 0xff) << 16) | ((digest[offset] & 0xff) << 24));
+        } catch (NoSuchAlgorithmException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    // --- X509 ----------------------------------------------------------------
+
+    /** Used to request get_X509_GENERAL_NAME_stack get the "altname" field. */
+    static final int GN_STACK_SUBJECT_ALT_NAME = 1;
+
+    /**
+     * Used to request get_X509_GENERAL_NAME_stack get the issuerAlternativeName
+     * extension.
+     */
+    static final int GN_STACK_ISSUER_ALT_NAME = 2;
+
+    /**
+     * Used to request only non-critical types in get_X509*_ext_oids.
+     */
+    static final int EXTENSION_TYPE_NON_CRITICAL = 0;
+
+    /**
+     * Used to request only critical types in get_X509*_ext_oids.
+     */
+    static final int EXTENSION_TYPE_CRITICAL = 1;
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long d2i_X509_bio(long bioCtx);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long d2i_X509(byte[] encoded) throws ParsingException;
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long PEM_read_bio_X509(long bioCtx);
+
+    static native byte[] i2d_X509(long x509ctx, OpenSSLX509Certificate holder);
+
+    /** Takes an X509 context not an X509_PUBKEY context. */
+    static native byte[] i2d_X509_PUBKEY(long x509ctx, OpenSSLX509Certificate holder);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native byte[] ASN1_seq_pack_X509(long[] x509CertRefs);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long[] ASN1_seq_unpack_X509_bio(long bioRef) throws ParsingException;
+
+    static native void X509_free(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native long X509_dup(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native int X509_cmp(long x509ctx1, OpenSSLX509Certificate holder, long x509ctx2, OpenSSLX509Certificate holder2);
+
+    static native void X509_print_ex(long bioCtx, long x509ctx, OpenSSLX509Certificate holder, long nmflag, long certflag);
+
+    static native byte[] X509_get_issuer_name(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native byte[] X509_get_subject_name(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native String get_X509_sig_alg_oid(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native byte[] get_X509_sig_alg_parameter(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native boolean[] get_X509_issuerUID(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native boolean[] get_X509_subjectUID(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native long X509_get_pubkey(long x509ctx, OpenSSLX509Certificate holder)
+            throws NoSuchAlgorithmException, InvalidKeyException;
+
+    static native String get_X509_pubkey_oid(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native byte[] X509_get_ext_oid(long x509ctx, OpenSSLX509Certificate holder, String oid);
+
+    static native String[] get_X509_ext_oids(long x509ctx, OpenSSLX509Certificate holder, int critical);
+
+    static native Object[][] get_X509_GENERAL_NAME_stack(long x509ctx, OpenSSLX509Certificate holder, int type)
+            throws CertificateParsingException;
+
+    static native boolean[] get_X509_ex_kusage(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native String[] get_X509_ex_xkusage(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native int get_X509_ex_pathlen(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native long X509_get_notBefore(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native long X509_get_notAfter(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native long X509_get_version(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native byte[] X509_get_serialNumber(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native void X509_verify(long x509ctx, OpenSSLX509Certificate holder, NativeRef.EVP_PKEY pkeyCtx)
+            throws BadPaddingException;
+
+    static native byte[] get_X509_cert_info_enc(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native byte[] get_X509_signature(long x509ctx, OpenSSLX509Certificate holder);
+
+    static native int get_X509_ex_flags(long x509ctx, OpenSSLX509Certificate holder);
+
+    // Used by Android platform TrustedCertificateStore.
+    @SuppressWarnings("unused")
+    static native int X509_check_issued(long ctx, OpenSSLX509Certificate holder, long ctx2, OpenSSLX509Certificate holder2);
+
+    // --- PKCS7 ---------------------------------------------------------------
+
+    /** Used as the "which" field in d2i_PKCS7_bio and PEM_read_bio_PKCS7. */
+    static final int PKCS7_CERTS = 1;
+
+    /** Used as the "which" field in d2i_PKCS7_bio and PEM_read_bio_PKCS7. */
+    static final int PKCS7_CRLS = 2;
+
+    /** Returns an array of X509 or X509_CRL pointers. */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long[] d2i_PKCS7_bio(long bioCtx, int which) throws ParsingException;
+
+    /** Returns an array of X509 or X509_CRL pointers. */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native byte[] i2d_PKCS7(long[] certs);
+
+    /** Returns an array of X509 or X509_CRL pointers. */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long[] PEM_read_bio_PKCS7(long bioCtx, int which);
+
+    // --- X509_CRL ------------------------------------------------------------
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long d2i_X509_CRL_bio(long bioCtx);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long PEM_read_bio_X509_CRL(long bioCtx);
+
+    static native byte[] i2d_X509_CRL(long x509CrlCtx, OpenSSLX509CRL holder);
+
+    static native void X509_CRL_free(long x509CrlCtx, OpenSSLX509CRL holder);
+
+    static native void X509_CRL_print(long bioCtx, long x509CrlCtx, OpenSSLX509CRL holder);
+
+    static native String get_X509_CRL_sig_alg_oid(long x509CrlCtx, OpenSSLX509CRL holder);
+
+    static native byte[] get_X509_CRL_sig_alg_parameter(long x509CrlCtx, OpenSSLX509CRL holder);
+
+    static native byte[] X509_CRL_get_issuer_name(long x509CrlCtx, OpenSSLX509CRL holder);
+
+    /** Returns X509_REVOKED reference that is not duplicated! */
+    static native long X509_CRL_get0_by_cert(long x509CrlCtx, OpenSSLX509CRL holder, long x509Ctx, OpenSSLX509Certificate holder2);
+
+    /** Returns X509_REVOKED reference that is not duplicated! */
+    static native long X509_CRL_get0_by_serial(long x509CrlCtx, OpenSSLX509CRL holder, byte[] serial);
+
+    /** Returns an array of X509_REVOKED that are owned by the caller. */
+    static native long[] X509_CRL_get_REVOKED(long x509CrlCtx, OpenSSLX509CRL holder);
+
+    static native String[] get_X509_CRL_ext_oids(long x509Crlctx, OpenSSLX509CRL holder, int critical);
+
+    static native byte[] X509_CRL_get_ext_oid(long x509CrlCtx, OpenSSLX509CRL holder, String oid);
+
+    static native void X509_delete_ext(long x509, OpenSSLX509Certificate holder, String oid);
+
+    static native long X509_CRL_get_version(long x509CrlCtx, OpenSSLX509CRL holder);
+
+    static native long X509_CRL_get_ext(long x509CrlCtx, OpenSSLX509CRL holder, String oid);
+
+    static native byte[] get_X509_CRL_signature(long x509ctx, OpenSSLX509CRL holder);
+
+    static native void X509_CRL_verify(long x509CrlCtx, OpenSSLX509CRL holder, NativeRef.EVP_PKEY pkeyCtx);
+
+    static native byte[] get_X509_CRL_crl_enc(long x509CrlCtx, OpenSSLX509CRL holder);
+
+    static native long X509_CRL_get_lastUpdate(long x509CrlCtx, OpenSSLX509CRL holder);
+
+    static native long X509_CRL_get_nextUpdate(long x509CrlCtx, OpenSSLX509CRL holder);
+
+    // --- X509_REVOKED --------------------------------------------------------
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long X509_REVOKED_dup(long x509RevokedCtx);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native byte[] i2d_X509_REVOKED(long x509RevokedCtx);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native String[] get_X509_REVOKED_ext_oids(long x509ctx, int critical);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native byte[] X509_REVOKED_get_ext_oid(long x509RevokedCtx, String oid);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native byte[] X509_REVOKED_get_serialNumber(long x509RevokedCtx);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long X509_REVOKED_get_ext(long x509RevokedCtx, String oid);
+
+    /** Returns ASN1_TIME reference. */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long get_X509_REVOKED_revocationDate(long x509RevokedCtx);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native void X509_REVOKED_print(long bioRef, long x509RevokedCtx);
+
+    // --- X509_EXTENSION ------------------------------------------------------
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native int X509_supported_extension(long x509ExtensionRef);
+
+    // --- ASN1_TIME -----------------------------------------------------------
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native void ASN1_TIME_to_Calendar(long asn1TimeCtx, Calendar cal) throws ParsingException;
+
+    // --- ASN1 Encoding -------------------------------------------------------
+
+    /**
+     * Allocates and returns an opaque reference to an object that can be used with other
+     * asn1_read_* functions to read the ASN.1-encoded data in val.  The returned object must
+     * be freed after use by calling asn1_read_free.
+     */
+    static native long asn1_read_init(byte[] val) throws IOException;
+
+    /**
+     * Allocates and returns an opaque reference to an object that can be used with other
+     * asn1_read_* functions to read the ASN.1 sequence pointed to by cbsRef.  The returned
+     * object must be freed after use by calling asn1_read_free.
+     */
+    static native long asn1_read_sequence(long cbsRef) throws IOException;
+
+    /**
+     * Returns whether the next object in the given reference is explicitly tagged with the
+     * given tag number.
+     */
+    static native boolean asn1_read_next_tag_is(long cbsRef, int tag) throws IOException;
+
+    /**
+     * Allocates and returns an opaque reference to an object that can be used with
+     * other asn1_read_* functions to read the ASN.1 data pointed to by cbsRef.  The returned
+     * object must be freed after use by calling asn1_read_free.
+     */
+    static native long asn1_read_tagged(long cbsRef) throws IOException;
+
+    /**
+     * Returns the contents of an ASN.1 octet string from the given reference.
+     */
+    static native byte[] asn1_read_octetstring(long cbsRef) throws IOException;
+
+    /**
+     * Returns an ASN.1 integer from the given reference.  If the integer doesn't fit
+     * in a uint64, this method will throw an IOException.
+     */
+    static native long asn1_read_uint64(long cbsRef) throws IOException;
+
+    /**
+     * Consumes an ASN.1 NULL from the given reference.
+     */
+    static native void asn1_read_null(long cbsRef) throws IOException;
+
+    /**
+     * Returns an ASN.1 OID in dotted-decimal notation (eg, "1.3.14.3.2.26" for SHA-1) from the
+     * given reference.
+     */
+    static native String asn1_read_oid(long cbsRef) throws IOException;
+
+    /**
+     * Returns whether or not the given reference has been read completely.
+     */
+    static native boolean asn1_read_is_empty(long cbsRef);
+
+    /**
+     * Frees any resources associated with the given reference.  After calling, the reference
+     * must not be used again.  This may be called with a zero reference, in which case nothing
+     * will be done.
+     */
+    static native void asn1_read_free(long cbsRef);
+
+    /**
+     * Allocates and returns an opaque reference to an object that can be used with other
+     * asn1_write_* functions to write ASN.1-encoded data.  The returned object must be finalized
+     * after use by calling either asn1_write_finish or asn1_write_cleanup, and its resources
+     * must be freed by calling asn1_write_free.
+     */
+    static native long asn1_write_init() throws IOException;
+
+    /**
+     * Allocates and returns an opaque reference to an object that can be used with other
+     * asn1_write_* functions to write an ASN.1 sequence into the given reference.  The returned
+     * reference may only be used until the next call on the parent reference.  The returned
+     * object must be freed after use by calling asn1_write_free.
+     */
+    static native long asn1_write_sequence(long cbbRef) throws IOException;
+
+    /**
+     * Allocates and returns an opaque reference to an object that can be used with other
+     * asn1_write_* functions to write a explicitly-tagged ASN.1 object with the given tag
+     * into the given reference. The returned reference may only be used until the next
+     * call on the parent reference.  The returned object must be freed after use by
+     * calling asn1_write_free.
+     */
+    static native long asn1_write_tag(long cbbRef, int tag) throws IOException;
+
+    /**
+     * Writes the given data into the given reference as an ASN.1-encoded octet string.
+     */
+    static native void asn1_write_octetstring(long cbbRef, byte[] data) throws IOException;
+
+    /**
+     * Writes the given value into the given reference as an ASN.1-encoded integer.
+     */
+    static native void asn1_write_uint64(long cbbRef, long value) throws IOException;
+
+    /**
+     * Writes a NULL value into the given reference.
+     */
+    static native void asn1_write_null(long cbbRef) throws IOException;
+
+    /**
+     * Writes the given OID (which must be in dotted-decimal notation) into the given reference.
+     */
+    static native void asn1_write_oid(long cbbRef, String oid) throws IOException;
+
+    /**
+     * Flushes the given reference, invalidating any child references and completing their
+     * operations.  This must be called if the child references are to be freed before
+     * asn1_write_finish is called on the ultimate parent.  The child references must still
+     * be freed.
+     */
+    static native void asn1_write_flush(long cbbRef) throws IOException;
+
+    /**
+     * Completes any in-progress operations and returns the ASN.1-encoded data.  Either this
+     * or asn1_write_cleanup must be called on any reference returned from asn1_write_init
+     * before it is freed.
+     */
+    static native byte[] asn1_write_finish(long cbbRef) throws IOException;
+
+    /**
+     * Cleans up intermediate state in the given reference.  Either this or asn1_write_finish
+     * must be called on any reference returned from asn1_write_init before it is freed.
+     */
+    static native void asn1_write_cleanup(long cbbRef);
+
+    /**
+     * Frees resources associated with the given reference.  After calling, the reference
+     * must not be used again.  This may be called with a zero reference, in which case nothing
+     * will be done.
+     */
+    static native void asn1_write_free(long cbbRef);
+
+    // --- BIO stream creation -------------------------------------------------
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long create_BIO_InputStream(OpenSSLBIOInputStream is, boolean isFinite);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long create_BIO_OutputStream(OutputStream os);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native void BIO_free_all(long bioRef);
+
+    // --- SSL handling --------------------------------------------------------
+
+    static final String OBSOLETE_PROTOCOL_SSLV3 = "SSLv3";
+    private static final String SUPPORTED_PROTOCOL_TLSV1 = "TLSv1";
+    private static final String SUPPORTED_PROTOCOL_TLSV1_1 = "TLSv1.1";
+    private static final String SUPPORTED_PROTOCOL_TLSV1_2 = "TLSv1.2";
+    static final String SUPPORTED_PROTOCOL_TLSV1_3 = "TLSv1.3";
+
+    static final String[] SUPPORTED_TLS_1_3_CIPHER_SUITES = new String[] {
+            "TLS_AES_128_GCM_SHA256",
+            "TLS_AES_256_GCM_SHA384",
+            "TLS_CHACHA20_POLY1305_SHA256",
+    };
+
+    // SUPPORTED_TLS_1_2_CIPHER_SUITES_SET contains all the supported cipher suites for TLS 1.2,
+    // using their Java names.
+    static final Set<String> SUPPORTED_TLS_1_2_CIPHER_SUITES_SET = new HashSet<String>();
+
+    // SUPPORTED_LEGACY_CIPHER_SUITES_SET contains all the supported cipher suites using the legacy
+    // OpenSSL-style names.
+    private static final Set<String> SUPPORTED_LEGACY_CIPHER_SUITES_SET = new HashSet<String>();
+
+    static final Set<String> SUPPORTED_TLS_1_3_CIPHER_SUITES_SET = new HashSet<String>(
+            Arrays.asList(SUPPORTED_TLS_1_3_CIPHER_SUITES));
+
+    /**
+     * TLS_EMPTY_RENEGOTIATION_INFO_SCSV is RFC 5746's renegotiation
+     * indication signaling cipher suite value. It is not a real
+     * cipher suite. It is just an indication in the default and
+     * supported cipher suite lists indicates that the implementation
+     * supports secure renegotiation.
+     * <p>
+     * In the RI, its presence means that the SCSV is sent in the
+     * cipher suite list to indicate secure renegotiation support and
+     * its absense means to send an empty TLS renegotiation info
+     * extension instead.
+     * <p>
+     * However, OpenSSL doesn't provide an API to give this level of
+     * control, instead always sending the SCSV and always including
+     * the empty renegotiation info if TLS is used (as opposed to
+     * SSL). So we simply allow TLS_EMPTY_RENEGOTIATION_INFO_SCSV to
+     * be passed for compatibility as to provide the hint that we
+     * support secure renegotiation.
+     */
+    static final String TLS_EMPTY_RENEGOTIATION_INFO_SCSV = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV";
+
+    static String cipherSuiteToJava(String cipherSuite) {
+        // For historical reasons, Java uses a different name for TLS_RSA_WITH_3DES_EDE_CBC_SHA.
+        if ("TLS_RSA_WITH_3DES_EDE_CBC_SHA".equals(cipherSuite)) {
+            return "SSL_RSA_WITH_3DES_EDE_CBC_SHA";
+        }
+        return cipherSuite;
+    }
+
+    static String cipherSuiteFromJava(String javaCipherSuite) {
+        if ("SSL_RSA_WITH_3DES_EDE_CBC_SHA".equals(javaCipherSuite)) {
+            return "TLS_RSA_WITH_3DES_EDE_CBC_SHA";
+        }
+        return javaCipherSuite;
+    }
+
+    /**
+     * TLS_FALLBACK_SCSV is from
+     * https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00
+     * to indicate to the server that this is a fallback protocol
+     * request.
+     */
+    private static final String TLS_FALLBACK_SCSV = "TLS_FALLBACK_SCSV";
+
+    private static final boolean HAS_AES_HARDWARE;
+    private static final String[] SUPPORTED_TLS_1_2_CIPHER_SUITES;
+    static {
+        if (loadError == null) {
+            // If loadError is not null, it means the native code was not loaded, so
+            // get_cipher_names will throw UnsatisfiedLinkError.
+            String[] allCipherSuites = get_cipher_names("ALL:!DHE");
+
+            // get_cipher_names returns an array where even indices are the standard name and odd
+            // indices are the OpenSSL name.
+            int size = allCipherSuites.length;
+            if (size % 2 != 0) {
+                throw new IllegalArgumentException(
+                        "Invalid cipher list returned by get_cipher_names");
+            }
+            SUPPORTED_TLS_1_2_CIPHER_SUITES = new String[size / 2 + 2];
+            for (int i = 0; i < size; i += 2) {
+                String cipherSuite = cipherSuiteToJava(allCipherSuites[i]);
+                SUPPORTED_TLS_1_2_CIPHER_SUITES[i / 2] = cipherSuite;
+                SUPPORTED_TLS_1_2_CIPHER_SUITES_SET.add(cipherSuite);
+
+                SUPPORTED_LEGACY_CIPHER_SUITES_SET.add(allCipherSuites[i + 1]);
+            }
+            SUPPORTED_TLS_1_2_CIPHER_SUITES[size / 2] = TLS_EMPTY_RENEGOTIATION_INFO_SCSV;
+            SUPPORTED_TLS_1_2_CIPHER_SUITES[size / 2 + 1] = TLS_FALLBACK_SCSV;
+
+            HAS_AES_HARDWARE = EVP_has_aes_hardware() == 1;
+        } else {
+            HAS_AES_HARDWARE = false;
+            SUPPORTED_TLS_1_2_CIPHER_SUITES = new String[0];
+        }
+    }
+
+    /**
+     * Returns 1 if the BoringSSL believes the CPU has AES accelerated hardware
+     * instructions. Used to determine cipher suite ordering.
+     */
+    static native int EVP_has_aes_hardware();
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long SSL_CTX_new();
+
+    // IMPLEMENTATION NOTE: The default list of cipher suites is a trade-off between what we'd like
+    // to use and what servers currently support. We strive to be secure enough by default. We thus
+    // avoid unacceptably weak suites (e.g., those with bulk cipher secret key shorter than 128
+    // bits), while maintaining the capability to connect to the majority of servers.
+    //
+    // Cipher suites are listed in preference order (favorite choice first) of the client. However,
+    // servers are not required to honor the order. The key rules governing the preference order
+    // are:
+    // * Prefer Forward Secrecy (i.e., cipher suites that use ECDHE and DHE for key agreement).
+    // * Prefer ChaCha20-Poly1305 to AES-GCM unless hardware support for AES is available.
+    // * Prefer AES-GCM to AES-CBC whose MAC-pad-then-encrypt approach leads to weaknesses (e.g.,
+    //   Lucky 13).
+    // * Prefer 128-bit bulk encryption to 256-bit one, because 128-bit is safe enough while
+    //   consuming less CPU/time/energy.
+    //
+    // NOTE: Removing cipher suites from this list needs to be done with caution, because this may
+    // prevent apps from connecting to servers they were previously able to connect to.
+
+    /** X.509 based cipher suites enabled by default (if requested), in preference order. */
+    static final String[] DEFAULT_X509_CIPHER_SUITES = HAS_AES_HARDWARE ?
+            new String[] {
+                    "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+                    "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+                    "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
+                    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+                    "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+                    "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
+                    "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+                    "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
+                    "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
+                    "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
+                    "TLS_RSA_WITH_AES_128_GCM_SHA256",
+                    "TLS_RSA_WITH_AES_256_GCM_SHA384",
+                    "TLS_RSA_WITH_AES_128_CBC_SHA",
+                    "TLS_RSA_WITH_AES_256_CBC_SHA",
+            } :
+            new String[] {
+                    "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
+                    "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+                    "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+                    "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
+                    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+                    "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+                    "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+                    "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
+                    "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
+                    "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
+                    "TLS_RSA_WITH_AES_128_GCM_SHA256",
+                    "TLS_RSA_WITH_AES_256_GCM_SHA384",
+                    "TLS_RSA_WITH_AES_128_CBC_SHA",
+                    "TLS_RSA_WITH_AES_256_CBC_SHA",
+            };
+
+    /** TLS-PSK cipher suites enabled by default (if requested), in preference order. */
+    static final String[] DEFAULT_PSK_CIPHER_SUITES = new String[] {
+            "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
+            "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
+            "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
+            "TLS_PSK_WITH_AES_128_CBC_SHA",
+            "TLS_PSK_WITH_AES_256_CBC_SHA",
+    };
+
+    static String[] getSupportedCipherSuites() {
+        return SSLUtils.concat(SUPPORTED_TLS_1_3_CIPHER_SUITES, SUPPORTED_TLS_1_2_CIPHER_SUITES.clone());
+    }
+
+    static native void SSL_CTX_free(long ssl_ctx, AbstractSessionContext holder);
+
+    static native void SSL_CTX_set_session_id_context(long ssl_ctx, AbstractSessionContext holder, byte[] sid_ctx);
+
+    static native long SSL_CTX_set_timeout(long ssl_ctx, AbstractSessionContext holder, long seconds);
+
+    static native long SSL_new(long ssl_ctx, AbstractSessionContext holder) throws SSLException;
+
+    static native void SSL_enable_tls_channel_id(long ssl, NativeSsl ssl_holder) throws SSLException;
+
+    static native byte[] SSL_get_tls_channel_id(long ssl, NativeSsl ssl_holder) throws SSLException;
+
+    static native void SSL_set1_tls_channel_id(long ssl, NativeSsl ssl_holder, NativeRef.EVP_PKEY pkey);
+
+    /**
+     * Sets the local certificates and private key.
+     *
+     * @param ssl the SSL reference.
+     * @param encodedCertificates the encoded form of the local certificate chain.
+     * @param pkey a reference to the private key.
+     * @throws SSLException if a problem occurs setting the cert/key.
+     */
+    static native void setLocalCertsAndPrivateKey(long ssl, NativeSsl ssl_holder, byte[][] encodedCertificates,
+        NativeRef.EVP_PKEY pkey) throws SSLException;
+
+    static native void SSL_set_client_CA_list(long ssl, NativeSsl ssl_holder, byte[][] asn1DerEncodedX500Principals)
+            throws SSLException;
+
+    static native long SSL_set_mode(long ssl, NativeSsl ssl_holder, long mode);
+
+    static native long SSL_set_options(long ssl, NativeSsl ssl_holder, long options);
+
+    static native long SSL_clear_options(long ssl, NativeSsl ssl_holder, long options);
+
+    static native int SSL_set_protocol_versions(long ssl, NativeSsl ssl_holder, int min_version, int max_version);
+
+    static native void SSL_enable_signed_cert_timestamps(long ssl, NativeSsl ssl_holder);
+
+    static native byte[] SSL_get_signed_cert_timestamp_list(long ssl, NativeSsl ssl_holder);
+
+    static native void SSL_set_signed_cert_timestamp_list(long ssl, NativeSsl ssl_holder, byte[] list);
+
+    static native void SSL_enable_ocsp_stapling(long ssl, NativeSsl ssl_holder);
+
+    static native byte[] SSL_get_ocsp_response(long ssl, NativeSsl ssl_holder);
+
+    static native void SSL_set_ocsp_response(long ssl, NativeSsl ssl_holder, byte[] response);
+
+    static native byte[] SSL_get_tls_unique(long ssl, NativeSsl ssl_holder);
+
+    static native byte[] SSL_export_keying_material(long ssl, NativeSsl ssl_holder, byte[] label, byte[] context, int num_bytes) throws SSLException;
+
+    static native void SSL_use_psk_identity_hint(long ssl, NativeSsl ssl_holder, String identityHint) throws SSLException;
+
+    static native void set_SSL_psk_client_callback_enabled(long ssl, NativeSsl ssl_holder, boolean enabled);
+
+    static native void set_SSL_psk_server_callback_enabled(long ssl, NativeSsl ssl_holder, boolean enabled);
+
+    /** Protocols to enable by default when "TLSv1.3" is requested. */
+    static final String[] TLSV13_PROTOCOLS = new String[] {
+            SUPPORTED_PROTOCOL_TLSV1,
+            SUPPORTED_PROTOCOL_TLSV1_1,
+            SUPPORTED_PROTOCOL_TLSV1_2,
+            SUPPORTED_PROTOCOL_TLSV1_3,
+    };
+
+    /** Protocols to enable by default when "TLSv1.2" is requested. */
+    static final String[] TLSV12_PROTOCOLS = new String[] {
+            SUPPORTED_PROTOCOL_TLSV1,
+            SUPPORTED_PROTOCOL_TLSV1_1,
+            SUPPORTED_PROTOCOL_TLSV1_2,
+    };
+
+    /** Protocols to enable by default when "TLSv1.1" is requested. */
+    static final String[] TLSV11_PROTOCOLS = TLSV12_PROTOCOLS;
+
+    /** Protocols to enable by default when "TLSv1" is requested. */
+    static final String[] TLSV1_PROTOCOLS = TLSV11_PROTOCOLS;
+
+    static final String[] DEFAULT_PROTOCOLS = TLSV13_PROTOCOLS;
+    private static final String[] SUPPORTED_PROTOCOLS = new String[] {
+            SUPPORTED_PROTOCOL_TLSV1,
+            SUPPORTED_PROTOCOL_TLSV1_1,
+            SUPPORTED_PROTOCOL_TLSV1_2,
+            SUPPORTED_PROTOCOL_TLSV1_3,
+    };;
+
+    static String[] getSupportedProtocols() {
+        return SUPPORTED_PROTOCOLS.clone();
+    }
+
+    private static class Range {
+        public final String min;
+        public final String max;
+        public Range(String min, String max) {
+            this.min = min;
+            this.max = max;
+        }
+    }
+
+    private static Range getProtocolRange(String[] protocols) {
+        // TLS protocol negotiation only allows a min and max version
+        // to be set, despite the Java API allowing a sparse set of
+        // protocols to be enabled.  Use the lowest contiguous range
+        // of protocols provided by the caller, which is what we've
+        // done historically.
+        List<String> protocolsList = Arrays.asList(protocols);
+        String min = null;
+        String max = null;
+        for (int i = 0; i < SUPPORTED_PROTOCOLS.length; i++) {
+            String protocol = SUPPORTED_PROTOCOLS[i];
+            if (protocolsList.contains(protocol)) {
+                if (min == null) {
+                    min = protocol;
+                }
+                max = protocol;
+            } else if (min != null) {
+                break;
+            }
+        }
+        if ((min == null) || (max == null)) {
+            throw new IllegalArgumentException("No protocols enabled.");
+        }
+        return new Range(min, max);
+    }
+
+    static void setEnabledProtocols(long ssl, NativeSsl ssl_holder, String[] protocols) {
+        checkEnabledProtocols(protocols);
+        Range range = getProtocolRange(protocols);
+        SSL_set_protocol_versions(
+                ssl, ssl_holder, getProtocolConstant(range.min), getProtocolConstant(range.max));
+    }
+
+    private static int getProtocolConstant(String protocol) {
+        if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1)) {
+            return NativeConstants.TLS1_VERSION;
+        } else if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1_1)) {
+            return NativeConstants.TLS1_1_VERSION;
+        } else if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1_2)) {
+            return NativeConstants.TLS1_2_VERSION;
+        } else if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1_3)) {
+            return NativeConstants.TLS1_3_VERSION;
+        } else {
+            throw new AssertionError("Unknown protocol encountered: " + protocol);
+        }
+    }
+
+    static String[] checkEnabledProtocols(String[] protocols) {
+        if (protocols == null) {
+            throw new IllegalArgumentException("protocols == null");
+        }
+        for (String protocol : protocols) {
+            if (protocol == null) {
+                throw new IllegalArgumentException("protocols contains null");
+            }
+            if (!protocol.equals(SUPPORTED_PROTOCOL_TLSV1)
+                    && !protocol.equals(SUPPORTED_PROTOCOL_TLSV1_1)
+                    && !protocol.equals(SUPPORTED_PROTOCOL_TLSV1_2)
+                    && !protocol.equals(SUPPORTED_PROTOCOL_TLSV1_3)
+                    && !protocol.equals(OBSOLETE_PROTOCOL_SSLV3)) {
+                throw new IllegalArgumentException("protocol " + protocol + " is not supported");
+            }
+        }
+        return protocols;
+    }
+
+    static native void SSL_set_cipher_lists(long ssl, NativeSsl ssl_holder, String[] ciphers);
+
+    /**
+     * Gets the list of cipher suites enabled for the provided {@code SSL} instance.
+     *
+     * @return array of {@code SSL_CIPHER} references.
+     */
+    static native long[] SSL_get_ciphers(long ssl, NativeSsl ssl_holder);
+
+    static void setEnabledCipherSuites(
+            long ssl, NativeSsl ssl_holder, String[] cipherSuites, String[] protocols) {
+        checkEnabledCipherSuites(cipherSuites);
+        String maxProtocol = getProtocolRange(protocols).max;
+        List<String> opensslSuites = new ArrayList<String>();
+        for (int i = 0; i < cipherSuites.length; i++) {
+            String cipherSuite = cipherSuites[i];
+            if (cipherSuite.equals(TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) {
+                continue;
+            }
+            // Only send TLS_FALLBACK_SCSV if max version >= 1.2 to prevent inadvertent connection
+            // problems when servers upgrade.  See https://github.com/google/conscrypt/issues/574
+            // for more discussion.
+            if (cipherSuite.equals(TLS_FALLBACK_SCSV)
+                    && (maxProtocol.equals(SUPPORTED_PROTOCOL_TLSV1)
+                            || maxProtocol.equals(SUPPORTED_PROTOCOL_TLSV1_1))) {
+                SSL_set_mode(ssl, ssl_holder, NativeConstants.SSL_MODE_SEND_FALLBACK_SCSV);
+                continue;
+            }
+            opensslSuites.add(cipherSuiteFromJava(cipherSuite));
+        }
+        SSL_set_cipher_lists(ssl, ssl_holder, opensslSuites.toArray(new String[opensslSuites.size()]));
+    }
+
+    static String[] checkEnabledCipherSuites(String[] cipherSuites) {
+        if (cipherSuites == null) {
+            throw new IllegalArgumentException("cipherSuites == null");
+        }
+        // makes sure all suites are valid, throwing on error
+        for (int i = 0; i < cipherSuites.length; i++) {
+            if (cipherSuites[i] == null) {
+                throw new IllegalArgumentException("cipherSuites[" + i + "] == null");
+            }
+            if (cipherSuites[i].equals(TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+                    || cipherSuites[i].equals(TLS_FALLBACK_SCSV)) {
+                continue;
+            }
+            if (SUPPORTED_TLS_1_2_CIPHER_SUITES_SET.contains(cipherSuites[i])) {
+                continue;
+            }
+
+            // For backwards compatibility, it's allowed for |cipherSuite| to
+            // be an OpenSSL-style cipher-suite name.
+            if (SUPPORTED_LEGACY_CIPHER_SUITES_SET.contains(cipherSuites[i])) {
+                // TODO log warning about using backward compatability
+                continue;
+            }
+            throw new IllegalArgumentException(
+                    "cipherSuite " + cipherSuites[i] + " is not supported.");
+        }
+        return cipherSuites;
+    }
+
+    static native void SSL_set_accept_state(long ssl, NativeSsl ssl_holder);
+
+    static native void SSL_set_connect_state(long ssl, NativeSsl ssl_holder);
+
+    static native void SSL_set_verify(long ssl, NativeSsl ssl_holder, int mode);
+
+    static native void SSL_set_session(long ssl, NativeSsl ssl_holder, long sslSessionNativePointer)
+            throws SSLException;
+
+    static native void SSL_set_session_creation_enabled(
+            long ssl, NativeSsl ssl_holder, boolean creationEnabled) throws SSLException;
+
+    static native boolean SSL_session_reused(long ssl, NativeSsl ssl_holder);
+
+    static native void SSL_accept_renegotiations(long ssl, NativeSsl ssl_holder) throws SSLException;
+
+    static native void SSL_set_tlsext_host_name(long ssl, NativeSsl ssl_holder, String hostname)
+            throws SSLException;
+    static native String SSL_get_servername(long ssl, NativeSsl ssl_holder);
+
+    static native void SSL_do_handshake(
+            long ssl, NativeSsl ssl_holder, FileDescriptor fd, SSLHandshakeCallbacks shc, int timeoutMillis)
+            throws SSLException, SocketTimeoutException, CertificateException;
+
+    public static native String SSL_get_current_cipher(long ssl, NativeSsl ssl_holder);
+
+    public static native String SSL_get_version(long ssl, NativeSsl ssl_holder);
+
+    /**
+     * Returns the peer certificate chain.
+     */
+    static native byte[][] SSL_get0_peer_certificates(long ssl, NativeSsl ssl_holder);
+
+    /**
+     * Reads with the native SSL_read function from the encrypted data stream
+     * @return -1 if error or the end of the stream is reached.
+     */
+    static native int SSL_read(long ssl, NativeSsl ssl_holder, FileDescriptor fd, SSLHandshakeCallbacks shc,
+            byte[] b, int off, int len, int readTimeoutMillis) throws IOException;
+
+    /**
+     * Writes with the native SSL_write function to the encrypted data stream.
+     */
+    static native void SSL_write(long ssl, NativeSsl ssl_holder, FileDescriptor fd,
+            SSLHandshakeCallbacks shc, byte[] b, int off, int len, int writeTimeoutMillis)
+            throws IOException;
+
+    static native void SSL_interrupt(long ssl, NativeSsl ssl_holder);
+    static native void SSL_shutdown(
+            long ssl, NativeSsl ssl_holder, FileDescriptor fd, SSLHandshakeCallbacks shc) throws IOException;
+
+    static native int SSL_get_shutdown(long ssl, NativeSsl ssl_holder);
+
+    static native void SSL_free(long ssl, NativeSsl ssl_holder);
+
+    static native long SSL_get_time(long ssl, NativeSsl ssl_holder);
+
+    static native long SSL_set_timeout(long ssl, NativeSsl ssl_holder, long millis);
+
+    static native long SSL_get_timeout(long ssl, NativeSsl ssl_holder);
+
+    static native int SSL_get_signature_algorithm_key_type(int signatureAlg);
+
+    static native byte[] SSL_session_id(long ssl, NativeSsl ssl_holder);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native byte[] SSL_SESSION_session_id(long sslSessionNativePointer);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long SSL_SESSION_get_time(long sslSessionNativePointer);
+
+    static native long SSL_SESSION_get_timeout(long sslSessionNativePointer);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native String SSL_SESSION_get_version(long sslSessionNativePointer);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native String SSL_SESSION_cipher(long sslSessionNativePointer);
+
+    static native boolean SSL_SESSION_should_be_single_use(long sslSessionNativePointer);
+
+    static native void SSL_SESSION_up_ref(long sslSessionNativePointer);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native void SSL_SESSION_free(long sslSessionNativePointer);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native byte[] i2d_SSL_SESSION(long sslSessionNativePointer);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static native long d2i_SSL_SESSION(byte[] data) throws IOException;
+
+    /**
+     * A collection of callbacks from the native OpenSSL code that are
+     * related to the SSL handshake initiated by SSL_do_handshake.
+     */
+    interface SSLHandshakeCallbacks {
+        /**
+         * Verify that the certificate chain is trusted.
+         *
+         * @param certificateChain chain of X.509 certificates in their encoded form
+         * @param authMethod auth algorithm name
+         *
+         * @throws CertificateException if the certificate is untrusted
+         */
+        @SuppressWarnings("unused")
+        void verifyCertificateChain(byte[][] certificateChain, String authMethod)
+                throws CertificateException;
+
+        /**
+         * Called on an SSL client when the server requests (or
+         * requires a certificate). The client can respond by using
+         * SSL_use_certificate and SSL_use_PrivateKey to set a
+         * certificate if has an appropriate one available, similar to
+         * how the server provides its certificate.
+         *
+         * @param keyTypes key types supported by the server,
+         * convertible to strings with #keyType
+         * @param asn1DerEncodedX500Principals CAs known to the server
+         */
+        @SuppressWarnings("unused")
+        void clientCertificateRequested(byte[] keyTypes, int[] signatureAlgs,
+                byte[][] asn1DerEncodedX500Principals)
+                throws CertificateEncodingException, SSLException;
+
+        /**
+         * Gets the key to be used in client mode for this connection in Pre-Shared Key (PSK) key
+         * exchange.
+         *
+         * @param identityHint PSK identity hint provided by the server or {@code null} if no hint
+         *        provided.
+         * @param identity buffer to be populated with PSK identity (NULL-terminated modified UTF-8)
+         *        by this method. This identity will be provided to the server.
+         * @param key buffer to be populated with key material by this method.
+         *
+         * @return number of bytes this method stored in the {@code key} buffer or {@code 0} if an
+         *         error occurred in which case the handshake will be aborted.
+         */
+        int clientPSKKeyRequested(String identityHint, byte[] identity, byte[] key);
+
+        /**
+         * Gets the key to be used in server mode for this connection in Pre-Shared Key (PSK) key
+         * exchange.
+         *
+         * @param identityHint PSK identity hint provided by this server to the client or
+         *        {@code null} if no hint was provided.
+         * @param identity PSK identity provided by the client.
+         * @param key buffer to be populated with key material by this method.
+         *
+         * @return number of bytes this method stored in the {@code key} buffer or {@code 0} if an
+         *         error occurred in which case the handshake will be aborted.
+         */
+        int serverPSKKeyRequested(String identityHint, String identity, byte[] key);
+
+        /**
+         * Called when SSL state changes. This could be handshake completion.
+         */
+        @SuppressWarnings("unused")
+        void onSSLStateChange(int type, int val);
+
+        /**
+         * Called when a new session has been established and may be added to the session cache.
+         * The callee is responsible for incrementing the reference count on the returned session.
+         */
+        @SuppressWarnings("unused")
+        void onNewSessionEstablished(long sslSessionNativePtr);
+
+        /**
+         * Called for servers where TLS < 1.3 (TLS 1.3 uses session tickets rather than
+         * application session caches).
+         *
+         * <p/>Looks up the session by ID in the application's session cache. If a valid session
+         * is returned, this callback is responsible for incrementing the reference count (and any
+         * required synchronization).
+         *
+         * @param id the ID of the session to find.
+         * @return the cached session or {@code 0} if no session was found matching the given ID.
+         */
+        @SuppressWarnings("unused")
+        long serverSessionRequested(byte[] id);
+    }
+
+    static native String SSL_CIPHER_get_kx_name(long cipherAddress);
+
+    static native String[] get_cipher_names(String selection);
+
+    public static native byte[] get_ocsp_single_extension(
+            byte[] ocspResponse, String oid, long x509Ref, OpenSSLX509Certificate holder, long issuerX509Ref, OpenSSLX509Certificate holder2);
+
+    /**
+     * Returns the starting address of the memory region referenced by the provided direct
+     * {@link Buffer} or {@code 0} if the provided buffer is not direct or if such access to direct
+     * buffers is not supported by the platform.
+     *
+     * <p>NOTE: This method ignores the buffer's current {@code position}.
+     */
+    static native long getDirectBufferAddress(Buffer buf);
+
+    static native long SSL_BIO_new(long ssl, NativeSsl ssl_holder) throws SSLException;
+
+    static native int SSL_get_error(long ssl, NativeSsl ssl_holder, int ret);
+
+    static native void SSL_clear_error();
+
+    static native int SSL_pending_readable_bytes(long ssl, NativeSsl ssl_holder);
+
+    static native int SSL_pending_written_bytes_in_BIO(long bio);
+
+    /**
+     * Returns the maximum overhead, in bytes, of sealing a record with SSL.
+     */
+    static native int SSL_max_seal_overhead(long ssl, NativeSsl ssl_holder);
+
+    /**
+     * Enables ALPN for this TLS endpoint and sets the list of supported ALPN protocols in
+     * wire-format (length-prefixed 8-bit strings).
+     */
+    static native void setApplicationProtocols(
+            long ssl, NativeSsl ssl_holder, boolean client, byte[] protocols) throws IOException;
+
+    /**
+     * Called for a server endpoint only. Enables ALPN and sets a BiFunction that will
+     * be called to delegate protocol selection to the application. Calling this method overrides
+     * {@link #setApplicationProtocols(long, NativeSsl, boolean, byte[])}.
+     */
+    static native void setApplicationProtocolSelector(
+            long ssl, NativeSsl ssl_holder, ApplicationProtocolSelectorAdapter selector) throws IOException;
+
+    /**
+     * Returns the selected ALPN protocol. If the server did not select a
+     * protocol, {@code null} will be returned.
+     */
+    static native byte[] getApplicationProtocol(long ssl, NativeSsl ssl_holder);
+
+    /**
+     * Variant of the {@link #SSL_do_handshake} used by {@link ConscryptEngine}. This differs
+     * slightly from the raw BoringSSL API in that it returns the SSL error code from the
+     * operation, rather than the return value from {@code SSL_do_handshake}. This is done in
+     * order to allow to properly handle SSL errors and propagate useful exceptions.
+     *
+     * @return Returns the SSL error code for the operation when the error was {@code
+     * SSL_ERROR_NONE}, {@code SSL_ERROR_WANT_READ}, or {@code SSL_ERROR_WANT_WRITE}.
+     * @throws IOException when the error code is anything except those returned by this method.
+     */
+    static native int ENGINE_SSL_do_handshake(long ssl, NativeSsl ssl_holder, SSLHandshakeCallbacks shc)
+            throws IOException;
+
+    /**
+     * Variant of the {@link #SSL_read} for a direct {@link java.nio.ByteBuffer} used by {@link
+     * ConscryptEngine}.
+     *
+     * @return if positive, represents the number of bytes read into the given buffer.
+     * Returns {@code -SSL_ERROR_WANT_READ} if more data is needed. Returns
+     * {@code -SSL_ERROR_WANT_WRITE} if data needs to be written out to flush the BIO.
+     *
+     * @throws java.io.InterruptedIOException if the read was interrupted.
+     * @throws java.io.EOFException if the end of stream has been reached.
+     * @throws CertificateException if the application's certificate verification callback failed.
+     * Only occurs during handshake processing.
+     * @throws SSLException if any other error occurs.
+     */
+    static native int ENGINE_SSL_read_direct(long ssl, NativeSsl ssl_holder, long address, int length,
+            SSLHandshakeCallbacks shc) throws IOException, CertificateException;
+
+    /**
+     * Variant of the {@link #SSL_write} for a direct {@link java.nio.ByteBuffer} used by {@link
+     * ConscryptEngine}. This version does not lock or and does no error pre-processing.
+     */
+    static native int ENGINE_SSL_write_direct(long ssl, NativeSsl ssl_holder, long address, int length,
+            SSLHandshakeCallbacks shc) throws IOException;
+
+    /**
+     * Writes data from the given direct {@link java.nio.ByteBuffer} to the BIO.
+     */
+    static native int ENGINE_SSL_write_BIO_direct(long ssl, NativeSsl ssl_holder, long bioRef, long pos, int length,
+            SSLHandshakeCallbacks shc) throws IOException;
+
+    /**
+     * Writes data from the given array to the BIO.
+     */
+    static native int ENGINE_SSL_write_BIO_heap(long ssl, NativeSsl ssl_holder, long bioRef, byte[] sourceJava,
+            int sourceOffset, int sourceLength, SSLHandshakeCallbacks shc)
+            throws IOException, IndexOutOfBoundsException;
+
+    /**
+     * Reads data from the given BIO into a direct {@link java.nio.ByteBuffer}.
+     */
+    static native int ENGINE_SSL_read_BIO_direct(long ssl, NativeSsl ssl_holder, long bioRef, long address, int len,
+            SSLHandshakeCallbacks shc) throws IOException;
+
+    /**
+     * Reads data from the given BIO into an array.
+     */
+    static native int ENGINE_SSL_read_BIO_heap(long ssl, NativeSsl ssl_holder, long bioRef, byte[] destJava,
+            int destOffset, int destLength, SSLHandshakeCallbacks shc)
+            throws IOException, IndexOutOfBoundsException;
+
+    /**
+     * Forces the SSL object to process any data pending in the BIO.
+     */
+    static native void ENGINE_SSL_force_read(long ssl, NativeSsl ssl_holder,
+            SSLHandshakeCallbacks shc) throws IOException;
+
+    /**
+     * Variant of the {@link #SSL_shutdown} used by {@link ConscryptEngine}. This version does not
+     * lock.
+     */
+    static native void ENGINE_SSL_shutdown(long ssl, NativeSsl ssl_holder, SSLHandshakeCallbacks shc)
+            throws IOException;
+
+    /**
+     * Used for testing only.
+     */
+    static native int BIO_read(long bioRef, byte[] buffer) throws IOException;
+    static native void BIO_write(long bioRef, byte[] buffer, int offset, int length)
+            throws IOException, IndexOutOfBoundsException;
+    static native long SSL_clear_mode(long ssl, NativeSsl ssl_holder, long mode);
+    static native long SSL_get_mode(long ssl, NativeSsl ssl_holder);
+    static native long SSL_get_options(long ssl, NativeSsl ssl_holder);
+    static native long SSL_get1_session(long ssl, NativeSsl ssl_holder);
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/NativeRef.java b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeRef.java
new file mode 100644
index 0000000..cc981c2
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeRef.java
@@ -0,0 +1,149 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+/**
+ * Used to hold onto native OpenSSL references and run finalization on those
+ * objects. Individual types must subclass this and implement finalizer.
+ */
+abstract class NativeRef {
+    final long address;
+
+    NativeRef(long address) {
+        if (address == 0) {
+            throw new NullPointerException("address == 0");
+        }
+
+        this.address = address;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof NativeRef)) {
+            return false;
+        }
+
+        return ((NativeRef) o).address == address;
+    }
+
+    @Override
+    public int hashCode() {
+        return (int) (address ^ (address >>> 32));
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (address != 0) {
+                doFree(address);
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    abstract void doFree(long context);
+
+    static final class EC_GROUP extends NativeRef {
+        EC_GROUP(long ctx) {
+            super(ctx);
+        }
+
+        @Override
+        void doFree(long context) {
+            NativeCrypto.EC_GROUP_clear_free(context);
+        }
+    }
+
+    static final class EC_POINT extends NativeRef {
+        EC_POINT(long nativePointer) {
+            super(nativePointer);
+        }
+
+        @Override
+        void doFree(long context) {
+            NativeCrypto.EC_POINT_clear_free(context);
+        }
+    }
+
+    static final class EVP_CIPHER_CTX extends NativeRef {
+        EVP_CIPHER_CTX(long nativePointer) {
+            super(nativePointer);
+        }
+
+        @Override
+        void doFree(long context) {
+            NativeCrypto.EVP_CIPHER_CTX_free(context);
+        }
+    }
+
+    static final class EVP_MD_CTX extends NativeRef {
+        EVP_MD_CTX(long nativePointer) {
+            super(nativePointer);
+        }
+
+        @Override
+        void doFree(long context) {
+            NativeCrypto.EVP_MD_CTX_destroy(context);
+        }
+    }
+
+    static final class EVP_PKEY extends NativeRef {
+        EVP_PKEY(long nativePointer) {
+            super(nativePointer);
+        }
+
+        @Override
+        void doFree(long context) {
+            NativeCrypto.EVP_PKEY_free(context);
+        }
+    }
+
+    static final class EVP_PKEY_CTX extends NativeRef {
+        EVP_PKEY_CTX(long nativePointer) {
+            super(nativePointer);
+        }
+
+        @Override
+        void doFree(long context) {
+            NativeCrypto.EVP_PKEY_CTX_free(context);
+        }
+    }
+
+    static final class HMAC_CTX extends NativeRef {
+        HMAC_CTX(long nativePointer) {
+            super(nativePointer);
+        }
+
+        @Override
+        void doFree(long context) {
+            NativeCrypto.HMAC_CTX_free(context);
+        }
+    }
+
+    static final class SSL_SESSION extends NativeRef {
+        SSL_SESSION(long nativePointer) {
+            super(nativePointer);
+        }
+
+        @Override
+        void doFree(long context) {
+            NativeCrypto.SSL_SESSION_free(context);
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/NativeSsl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeSsl.java
new file mode 100644
index 0000000..a6d5a32
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeSsl.java
@@ -0,0 +1,662 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.NativeConstants.SSL_MODE_CBC_RECORD_SPLITTING;
+import static com.android.org.conscrypt.NativeConstants.SSL_OP_CIPHER_SERVER_PREFERENCE;
+import static com.android.org.conscrypt.NativeConstants.SSL_OP_NO_TICKET;
+import static com.android.org.conscrypt.NativeConstants.SSL_RECEIVED_SHUTDOWN;
+import static com.android.org.conscrypt.NativeConstants.SSL_SENT_SHUTDOWN;
+import static com.android.org.conscrypt.NativeConstants.SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+import static com.android.org.conscrypt.NativeConstants.SSL_VERIFY_NONE;
+import static com.android.org.conscrypt.NativeConstants.SSL_VERIFY_PEER;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.SocketException;
+import java.nio.charset.Charset;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import javax.crypto.SecretKey;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
+import com.android.org.conscrypt.NativeCrypto.SSLHandshakeCallbacks;
+import com.android.org.conscrypt.SSLParametersImpl.AliasChooser;
+import com.android.org.conscrypt.SSLParametersImpl.PSKCallbacks;
+
+/**
+ * A utility wrapper that abstracts operations on the underlying native SSL instance.
+ */
+final class NativeSsl {
+    private final SSLParametersImpl parameters;
+    private final SSLHandshakeCallbacks handshakeCallbacks;
+    private final AliasChooser aliasChooser;
+    private final PSKCallbacks pskCallbacks;
+    private X509Certificate[] localCertificates;
+    private final ReadWriteLock lock = new ReentrantReadWriteLock();
+    private volatile long ssl;
+
+    private NativeSsl(long ssl, SSLParametersImpl parameters,
+            SSLHandshakeCallbacks handshakeCallbacks, AliasChooser aliasChooser,
+            PSKCallbacks pskCallbacks) {
+        this.ssl = ssl;
+        this.parameters = parameters;
+        this.handshakeCallbacks = handshakeCallbacks;
+        this.aliasChooser = aliasChooser;
+        this.pskCallbacks = pskCallbacks;
+    }
+
+    static NativeSsl newInstance(SSLParametersImpl parameters,
+            SSLHandshakeCallbacks handshakeCallbacks, AliasChooser chooser,
+            PSKCallbacks pskCallbacks) throws SSLException {
+        AbstractSessionContext ctx = parameters.getSessionContext();
+        long ssl = NativeCrypto.SSL_new(ctx.sslCtxNativePointer, ctx);
+        return new NativeSsl(ssl, parameters, handshakeCallbacks, chooser, pskCallbacks);
+    }
+
+    BioWrapper newBio() {
+        try {
+            return new BioWrapper();
+        } catch (SSLException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    void offerToResumeSession(long sslSessionNativePointer) throws SSLException {
+        NativeCrypto.SSL_set_session(ssl, this, sslSessionNativePointer);
+    }
+
+    byte[] getSessionId() {
+        return NativeCrypto.SSL_session_id(ssl, this);
+    }
+
+    long getTime() {
+        return NativeCrypto.SSL_get_time(ssl, this);
+    }
+
+    long getTimeout() {
+        return NativeCrypto.SSL_get_timeout(ssl, this);
+    }
+
+    void setTimeout(long millis) {
+        NativeCrypto.SSL_set_timeout(ssl, this, millis);
+    }
+
+    String getCipherSuite() {
+        return NativeCrypto.cipherSuiteToJava(NativeCrypto.SSL_get_current_cipher(ssl, this));
+    }
+
+    X509Certificate[] getPeerCertificates() throws CertificateException {
+        byte[][] encoded = NativeCrypto.SSL_get0_peer_certificates(ssl, this);
+        return encoded == null ? null : SSLUtils.decodeX509CertificateChain(encoded);
+    }
+
+    X509Certificate[] getLocalCertificates() {
+        return localCertificates;
+    }
+
+    byte[] getPeerCertificateOcspData() {
+        return NativeCrypto.SSL_get_ocsp_response(ssl, this);
+    }
+
+    byte[] getTlsUnique() {
+        return NativeCrypto.SSL_get_tls_unique(ssl, this);
+    }
+
+    byte[] exportKeyingMaterial(String label, byte[] context, int length) throws SSLException {
+        if (label == null) {
+            throw new NullPointerException("Label is null");
+        }
+        byte[] labelBytes = label.getBytes(Charset.forName("US-ASCII"));
+        return NativeCrypto.SSL_export_keying_material(ssl, this, labelBytes, context, length);
+    }
+
+    byte[] getPeerTlsSctData() {
+        return NativeCrypto.SSL_get_signed_cert_timestamp_list(ssl, this);
+    }
+
+    /**
+     * @see NativeCrypto.SSLHandshakeCallbacks#clientPSKKeyRequested(String, byte[], byte[])
+     */
+    @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+    int clientPSKKeyRequested(String identityHint, byte[] identityBytesOut, byte[] key) {
+        PSKKeyManager pskKeyManager = parameters.getPSKKeyManager();
+        if (pskKeyManager == null) {
+            return 0;
+        }
+
+        String identity = pskCallbacks.chooseClientPSKIdentity(pskKeyManager, identityHint);
+        // Store identity in NULL-terminated modified UTF-8 representation into ientityBytesOut
+        byte[] identityBytes;
+        if (identity == null) {
+            identity = "";
+            identityBytes = EmptyArray.BYTE;
+        } else if (identity.isEmpty()) {
+            identityBytes = EmptyArray.BYTE;
+        } else {
+            try {
+                identityBytes = identity.getBytes("UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                throw new RuntimeException("UTF-8 encoding not supported", e);
+            }
+        }
+        if (identityBytes.length + 1 > identityBytesOut.length) {
+            // Insufficient space in the output buffer
+            return 0;
+        }
+        if (identityBytes.length > 0) {
+            System.arraycopy(identityBytes, 0, identityBytesOut, 0, identityBytes.length);
+        }
+        identityBytesOut[identityBytes.length] = 0;
+
+        SecretKey secretKey = pskCallbacks.getPSKKey(pskKeyManager, identityHint, identity);
+        byte[] secretKeyBytes = secretKey.getEncoded();
+        if (secretKeyBytes == null) {
+            return 0;
+        } else if (secretKeyBytes.length > key.length) {
+            // Insufficient space in the output buffer
+            return 0;
+        }
+        System.arraycopy(secretKeyBytes, 0, key, 0, secretKeyBytes.length);
+        return secretKeyBytes.length;
+    }
+
+    /**
+     * @see NativeCrypto.SSLHandshakeCallbacks#serverPSKKeyRequested(String, String, byte[])
+     */
+    @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+    int serverPSKKeyRequested(String identityHint, String identity, byte[] key) {
+        PSKKeyManager pskKeyManager = parameters.getPSKKeyManager();
+        if (pskKeyManager == null) {
+            return 0;
+        }
+        SecretKey secretKey = pskCallbacks.getPSKKey(pskKeyManager, identityHint, identity);
+        byte[] secretKeyBytes = secretKey.getEncoded();
+        if (secretKeyBytes == null) {
+            return 0;
+        } else if (secretKeyBytes.length > key.length) {
+            return 0;
+        }
+        System.arraycopy(secretKeyBytes, 0, key, 0, secretKeyBytes.length);
+        return secretKeyBytes.length;
+    }
+
+    void chooseClientCertificate(byte[] keyTypeBytes, int[] signatureAlgs,
+            byte[][] asn1DerEncodedPrincipals)
+            throws SSLException, CertificateEncodingException {
+        Set<String> keyTypesSet = SSLUtils.getSupportedClientKeyTypes(keyTypeBytes, signatureAlgs);
+        String[] keyTypes = keyTypesSet.toArray(new String[keyTypesSet.size()]);
+
+        X500Principal[] issuers;
+        if (asn1DerEncodedPrincipals == null) {
+            issuers = null;
+        } else {
+            issuers = new X500Principal[asn1DerEncodedPrincipals.length];
+            for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) {
+                issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
+            }
+        }
+        X509KeyManager keyManager = parameters.getX509KeyManager();
+        String alias = (keyManager != null)
+                ? aliasChooser.chooseClientAlias(keyManager, issuers, keyTypes)
+                : null;
+        setCertificate(alias);
+    }
+
+    void setCertificate(String alias) throws CertificateEncodingException, SSLException {
+        if (alias == null) {
+            return;
+        }
+        X509KeyManager keyManager = parameters.getX509KeyManager();
+        if (keyManager == null) {
+            return;
+        }
+        PrivateKey privateKey = keyManager.getPrivateKey(alias);
+        if (privateKey == null) {
+            return;
+        }
+        localCertificates = keyManager.getCertificateChain(alias);
+        if (localCertificates == null) {
+            return;
+        }
+        int numLocalCerts = localCertificates.length;
+        PublicKey publicKey = (numLocalCerts > 0) ? localCertificates[0].getPublicKey() : null;
+
+        // Encode the local certificates.
+        byte[][] encodedLocalCerts = new byte[numLocalCerts][];
+        for (int i = 0; i < numLocalCerts; ++i) {
+            encodedLocalCerts[i] = localCertificates[i].getEncoded();
+        }
+
+        // Convert the key so we can access a native reference.
+        final OpenSSLKey key;
+        try {
+            key = OpenSSLKey.fromPrivateKeyForTLSStackOnly(privateKey, publicKey);
+        } catch (InvalidKeyException e) {
+            throw new SSLException(e);
+        }
+
+        // Set the local certs and private key.
+        NativeCrypto.setLocalCertsAndPrivateKey(ssl, this, encodedLocalCerts, key.getNativeRef());
+    }
+
+    String getVersion() {
+        return NativeCrypto.SSL_get_version(ssl, this);
+    }
+
+    String getRequestedServerName() {
+        return NativeCrypto.SSL_get_servername(ssl, this);
+    }
+
+    byte[] getTlsChannelId() throws SSLException {
+        return NativeCrypto.SSL_get_tls_channel_id(ssl, this);
+    }
+
+    void initialize(String hostname, OpenSSLKey channelIdPrivateKey) throws IOException {
+        boolean enableSessionCreation = parameters.getEnableSessionCreation();
+        if (!enableSessionCreation) {
+            NativeCrypto.SSL_set_session_creation_enabled(ssl, this, false);
+        }
+
+        // Allow servers to trigger renegotiation. Some inadvisable server
+        // configurations cause them to attempt to renegotiate during
+        // certain protocols.
+        NativeCrypto.SSL_accept_renegotiations(ssl, this);
+
+        if (isClient()) {
+            NativeCrypto.SSL_set_connect_state(ssl, this);
+
+            // Configure OCSP and CT extensions for client
+            NativeCrypto.SSL_enable_ocsp_stapling(ssl, this);
+            if (parameters.isCTVerificationEnabled(hostname)) {
+                NativeCrypto.SSL_enable_signed_cert_timestamps(ssl, this);
+            }
+        } else {
+            NativeCrypto.SSL_set_accept_state(ssl, this);
+
+            // Configure OCSP for server
+            if (parameters.getOCSPResponse() != null) {
+                NativeCrypto.SSL_enable_ocsp_stapling(ssl, this);
+            }
+        }
+
+        if (parameters.getEnabledProtocols().length == 0 && parameters.isEnabledProtocolsFiltered) {
+            throw new SSLHandshakeException("No enabled protocols; "
+                    + NativeCrypto.OBSOLETE_PROTOCOL_SSLV3
+                    + " is no longer supported and was filtered from the list");
+        }
+        NativeCrypto.setEnabledProtocols(ssl, this, parameters.enabledProtocols);
+        NativeCrypto.setEnabledCipherSuites(
+                ssl, this, parameters.enabledCipherSuites, parameters.enabledProtocols);
+
+        if (parameters.applicationProtocols.length > 0) {
+            NativeCrypto.setApplicationProtocols(ssl, this, isClient(), parameters.applicationProtocols);
+        }
+        if (!isClient() && parameters.applicationProtocolSelector != null) {
+            NativeCrypto.setApplicationProtocolSelector(ssl, this, parameters.applicationProtocolSelector);
+        }
+
+        // setup server certificates and private keys.
+        // clients will receive a call back to request certificates.
+        if (!isClient()) {
+            Set<String> keyTypes = new HashSet<String>();
+            for (long sslCipherNativePointer : NativeCrypto.SSL_get_ciphers(ssl, this)) {
+                String keyType = SSLUtils.getServerX509KeyType(sslCipherNativePointer);
+                if (keyType != null) {
+                    keyTypes.add(keyType);
+                }
+            }
+            X509KeyManager keyManager = parameters.getX509KeyManager();
+            if (keyManager != null) {
+                for (String keyType : keyTypes) {
+                    try {
+                        setCertificate(aliasChooser.chooseServerAlias(keyManager, keyType));
+                    } catch (CertificateEncodingException e) {
+                        throw new IOException(e);
+                    }
+                }
+            }
+
+            NativeCrypto.SSL_set_options(ssl, this, SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+            if (parameters.sctExtension != null) {
+                NativeCrypto.SSL_set_signed_cert_timestamp_list(ssl, this, parameters.sctExtension);
+            }
+
+            if (parameters.ocspResponse != null) {
+                NativeCrypto.SSL_set_ocsp_response(ssl, this, parameters.ocspResponse);
+            }
+        }
+
+        enablePSKKeyManagerIfRequested();
+
+        if (parameters.useSessionTickets) {
+            NativeCrypto.SSL_clear_options(ssl, this, SSL_OP_NO_TICKET);
+        } else {
+            NativeCrypto.SSL_set_options(
+                    ssl, this, NativeCrypto.SSL_get_options(ssl, this) | SSL_OP_NO_TICKET);
+        }
+
+        if (parameters.getUseSni() && AddressUtils.isValidSniHostname(hostname)) {
+            NativeCrypto.SSL_set_tlsext_host_name(ssl, this, hostname);
+        }
+
+        // BEAST attack mitigation (1/n-1 record splitting for CBC cipher suites
+        // with TLSv1 and SSLv3).
+        NativeCrypto.SSL_set_mode(ssl, this, SSL_MODE_CBC_RECORD_SPLITTING);
+
+        setCertificateValidation();
+        setTlsChannelId(channelIdPrivateKey);
+    }
+
+    // TODO(nathanmittler): Remove once after we switch to the engine socket.
+    void doHandshake(FileDescriptor fd, int timeoutMillis)
+            throws CertificateException, IOException {
+        lock.readLock().lock();
+        try {
+            if (isClosed() || fd == null || !fd.valid()) {
+                throw new SocketException("Socket is closed");
+            }
+            NativeCrypto.SSL_do_handshake(ssl, this, fd, handshakeCallbacks, timeoutMillis);
+        } finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    int doHandshake() throws IOException {
+        lock.readLock().lock();
+        try {
+            return NativeCrypto.ENGINE_SSL_do_handshake(ssl, this, handshakeCallbacks);
+        } finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    // TODO(nathanmittler): Remove once after we switch to the engine socket.
+    int read(FileDescriptor fd, byte[] buf, int offset, int len, int timeoutMillis)
+            throws IOException {
+        lock.readLock().lock();
+        try {
+            if (isClosed() || fd == null || !fd.valid()) {
+                throw new SocketException("Socket is closed");
+            }
+            return NativeCrypto
+                    .SSL_read(ssl, this, fd, handshakeCallbacks, buf, offset, len, timeoutMillis);
+        } finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    // TODO(nathanmittler): Remove once after we switch to the engine socket.
+    void write(FileDescriptor fd, byte[] buf, int offset, int len, int timeoutMillis)
+            throws IOException {
+        lock.readLock().lock();
+        try {
+            if (isClosed() || fd == null || !fd.valid()) {
+                throw new SocketException("Socket is closed");
+            }
+            NativeCrypto
+                    .SSL_write(ssl, this, fd, handshakeCallbacks, buf, offset, len, timeoutMillis);
+        } finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+    private void enablePSKKeyManagerIfRequested() throws SSLException {
+        // Enable Pre-Shared Key (PSK) key exchange if requested
+        PSKKeyManager pskKeyManager = parameters.getPSKKeyManager();
+        if (pskKeyManager != null) {
+            boolean pskEnabled = false;
+            for (String enabledCipherSuite : parameters.enabledCipherSuites) {
+                if ((enabledCipherSuite != null) && enabledCipherSuite.contains("PSK")) {
+                    pskEnabled = true;
+                    break;
+                }
+            }
+            if (pskEnabled) {
+                if (isClient()) {
+                    NativeCrypto.set_SSL_psk_client_callback_enabled(ssl, this, true);
+                } else {
+                    NativeCrypto.set_SSL_psk_server_callback_enabled(ssl, this, true);
+                    String identityHint = pskCallbacks.chooseServerPSKIdentityHint(pskKeyManager);
+                    NativeCrypto.SSL_use_psk_identity_hint(ssl, this, identityHint);
+                }
+            }
+        }
+    }
+
+    private void setTlsChannelId(OpenSSLKey channelIdPrivateKey) throws SSLException {
+        if (!parameters.channelIdEnabled) {
+            return;
+        }
+
+        if (parameters.getUseClientMode()) {
+            // Client-side TLS Channel ID
+            if (channelIdPrivateKey == null) {
+                throw new SSLHandshakeException("Invalid TLS channel ID key specified");
+            }
+            NativeCrypto.SSL_set1_tls_channel_id(ssl, this, channelIdPrivateKey.getNativeRef());
+        } else {
+            // Server-side TLS Channel ID
+            NativeCrypto.SSL_enable_tls_channel_id(ssl, this);
+        }
+    }
+
+    private void setCertificateValidation() throws SSLException {
+        // setup peer certificate verification
+        if (!isClient()) {
+            // needing client auth takes priority...
+            boolean certRequested;
+            if (parameters.getNeedClientAuth()) {
+                NativeCrypto.SSL_set_verify(ssl, this, SSL_VERIFY_PEER
+                                | SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
+                certRequested = true;
+                // ... over just wanting it...
+            } else if (parameters.getWantClientAuth()) {
+                NativeCrypto.SSL_set_verify(ssl, this, SSL_VERIFY_PEER);
+                certRequested = true;
+                // ... and we must disable verification if we don't want client auth.
+            } else {
+                NativeCrypto.SSL_set_verify(ssl, this, SSL_VERIFY_NONE);
+                certRequested = false;
+            }
+
+            if (certRequested) {
+                X509TrustManager trustManager = parameters.getX509TrustManager();
+                X509Certificate[] issuers = trustManager.getAcceptedIssuers();
+                if (issuers != null && issuers.length != 0) {
+                    byte[][] issuersBytes;
+                    try {
+                        issuersBytes = SSLUtils.encodeSubjectX509Principals(issuers);
+                    } catch (CertificateEncodingException e) {
+                        throw new SSLException("Problem encoding principals", e);
+                    }
+                    NativeCrypto.SSL_set_client_CA_list(ssl, this, issuersBytes);
+                }
+            }
+        }
+    }
+
+    void interrupt() {
+        NativeCrypto.SSL_interrupt(ssl, this);
+    }
+
+    // TODO(nathanmittler): Remove once after we switch to the engine socket.
+    void shutdown(FileDescriptor fd) throws IOException {
+        NativeCrypto.SSL_shutdown(ssl, this, fd, handshakeCallbacks);
+    }
+
+    void shutdown() throws IOException {
+        lock.readLock().lock();
+        try {
+            NativeCrypto.ENGINE_SSL_shutdown(ssl, this, handshakeCallbacks);
+        } finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    boolean wasShutdownReceived() {
+        lock.readLock().lock();
+        try {
+            return (NativeCrypto.SSL_get_shutdown(ssl, this) & SSL_RECEIVED_SHUTDOWN) != 0;
+        } finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    boolean wasShutdownSent() {
+        lock.readLock().lock();
+        try {
+            return (NativeCrypto.SSL_get_shutdown(ssl, this) & SSL_SENT_SHUTDOWN) != 0;
+        } finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    int readDirectByteBuffer(long destAddress, int destLength)
+            throws IOException, CertificateException {
+        lock.readLock().lock();
+        try {
+            return NativeCrypto.ENGINE_SSL_read_direct(
+                    ssl, this, destAddress, destLength, handshakeCallbacks);
+        } finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    int writeDirectByteBuffer(long sourceAddress, int sourceLength) throws IOException {
+        lock.readLock().lock();
+        try {
+            return NativeCrypto.ENGINE_SSL_write_direct(
+                    ssl, this, sourceAddress, sourceLength, handshakeCallbacks);
+        } finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    void forceRead() throws IOException {
+        lock.readLock().lock();
+        try {
+            NativeCrypto.ENGINE_SSL_force_read(ssl, this, handshakeCallbacks);
+        } finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    int getPendingReadableBytes() {
+        lock.readLock().lock();
+        try {
+            if (!isClosed()) {
+                return NativeCrypto.SSL_pending_readable_bytes(ssl, this);
+            }
+            return 0;
+        } finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    int getMaxSealOverhead() {
+        return NativeCrypto.SSL_max_seal_overhead(ssl, this);
+    }
+
+    void close() {
+        lock.writeLock().lock();
+        try {
+            if (!isClosed()) {
+                long toFree = ssl;
+                ssl = 0L;
+                NativeCrypto.SSL_free(toFree, this);
+            }
+        } finally {
+            lock.writeLock().unlock();
+        }
+    }
+
+    boolean isClosed() {
+        return ssl == 0L;
+    }
+
+    int getError(int result) {
+        return NativeCrypto.SSL_get_error(ssl, this, result);
+    }
+
+    byte[] getApplicationProtocol() {
+        return NativeCrypto.getApplicationProtocol(ssl, this);
+    }
+
+    private boolean isClient() {
+        return parameters.getUseClientMode();
+    }
+
+    @Override
+    protected final void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * A utility wrapper that abstracts operations on the underlying native BIO instance.
+     */
+    final class BioWrapper {
+        private volatile long bio;
+
+        private BioWrapper() throws SSLException {
+            this.bio = NativeCrypto.SSL_BIO_new(ssl, NativeSsl.this);
+        }
+
+        int getPendingWrittenBytes() {
+            if (bio != 0) {
+                return NativeCrypto.SSL_pending_written_bytes_in_BIO(bio);
+            } else {
+                return 0;
+            }
+        }
+
+        int writeDirectByteBuffer(long address, int length) throws IOException {
+            return NativeCrypto.ENGINE_SSL_write_BIO_direct(
+                    ssl, NativeSsl.this, bio, address, length, handshakeCallbacks);
+        }
+
+        int readDirectByteBuffer(long destAddress, int destLength) throws IOException {
+            return NativeCrypto.ENGINE_SSL_read_BIO_direct(
+                    ssl, NativeSsl.this, bio, destAddress, destLength, handshakeCallbacks);
+        }
+
+        void close() {
+            long toFree = bio;
+            bio = 0L;
+            NativeCrypto.BIO_free_all(toFree);
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/NativeSslSession.java b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeSslSession.java
new file mode 100644
index 0000000..5ed56e1
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/NativeSslSession.java
@@ -0,0 +1,485 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License", "www.google.com", 443);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.SSLUtils.SessionType.OPEN_SSL_WITH_OCSP;
+import static com.android.org.conscrypt.SSLUtils.SessionType.OPEN_SSL_WITH_TLS_SCT;
+import static com.android.org.conscrypt.SSLUtils.SessionType.isSupportedType;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+import javax.security.cert.X509Certificate;
+
+/**
+ * A utility wrapper that abstracts operations on the underlying native SSL_SESSION instance.
+ *
+ * This is abstract only to support mocking for tests.
+ */
+abstract class NativeSslSession {
+    private static final Logger logger = Logger.getLogger(NativeSslSession.class.getName());
+
+    /**
+     * Creates a new instance. Since BoringSSL does not provide an API to get access to all
+     * session information via the SSL_SESSION, we get some values (e.g. peer certs) from
+     * the {@link ConscryptSession} instead (i.e. the SSL object).
+     */
+    static NativeSslSession newInstance(NativeRef.SSL_SESSION ref, ConscryptSession session)
+            throws SSLPeerUnverifiedException {
+        AbstractSessionContext context = (AbstractSessionContext) session.getSessionContext();
+        if (context instanceof ClientSessionContext) {
+            return new Impl(context, ref, session.getPeerHost(), session.getPeerPort(),
+                session.getPeerCertificates(), getOcspResponse(session),
+                session.getPeerSignedCertificateTimestamp());
+        }
+
+        // Server's will be cached by ID and won't have any of the extra fields.
+        return new Impl(context, ref, null, -1, null, null, null);
+    }
+
+    private static byte[] getOcspResponse(ConscryptSession session) {
+        List<byte[]> ocspResponseList = session.getStatusResponses();
+        if (ocspResponseList.size() >= 1) {
+            return ocspResponseList.get(0);
+        }
+        return null;
+    }
+
+    /**
+     * Creates a new {@link NativeSslSession} instance from the provided serialized bytes, which
+     * were generated by {@link #toBytes()}.
+     *
+     * @return The new instance if successful. If unable to parse the bytes for any reason, returns
+     * {@code null}.
+     */
+    static NativeSslSession newInstance(
+            AbstractSessionContext context, byte[] data, String host, int port) {
+        ByteBuffer buf = ByteBuffer.wrap(data);
+        try {
+            int type = buf.getInt();
+            if (!isSupportedType(type)) {
+                throw new IOException("Unexpected type ID: " + type);
+            }
+
+            int length = buf.getInt();
+            checkRemaining(buf, length);
+
+            byte[] sessionData = new byte[length];
+            buf.get(sessionData);
+
+            int count = buf.getInt();
+            checkRemaining(buf, count);
+
+            java.security.cert.X509Certificate[] peerCerts =
+                    new java.security.cert.X509Certificate[count];
+            for (int i = 0; i < count; i++) {
+                length = buf.getInt();
+                checkRemaining(buf, length);
+
+                byte[] certData = new byte[length];
+                buf.get(certData);
+                try {
+                    peerCerts[i] = OpenSSLX509Certificate.fromX509Der(certData);
+                } catch (Exception e) {
+                    throw new IOException("Can not read certificate " + i + "/" + count);
+                }
+            }
+
+            byte[] ocspData = null;
+            if (type >= OPEN_SSL_WITH_OCSP.value) {
+                // We only support one OCSP response now, but in the future
+                // we may support RFC 6961 which has multiple.
+                int countOcspResponses = buf.getInt();
+                checkRemaining(buf, countOcspResponses);
+
+                if (countOcspResponses >= 1) {
+                    int ocspLength = buf.getInt();
+                    checkRemaining(buf, ocspLength);
+
+                    ocspData = new byte[ocspLength];
+                    buf.get(ocspData);
+
+                    // Skip the rest of the responses.
+                    for (int i = 1; i < countOcspResponses; i++) {
+                        ocspLength = buf.getInt();
+                        checkRemaining(buf, ocspLength);
+                        buf.position(buf.position() + ocspLength);
+                    }
+                }
+            }
+
+            byte[] tlsSctData = null;
+            if (type == OPEN_SSL_WITH_TLS_SCT.value) {
+                int tlsSctDataLength = buf.getInt();
+                checkRemaining(buf, tlsSctDataLength);
+
+                if (tlsSctDataLength > 0) {
+                    tlsSctData = new byte[tlsSctDataLength];
+                    buf.get(tlsSctData);
+                }
+            }
+
+            if (buf.remaining() != 0) {
+                log(new AssertionError("Read entire session, but data still remains; rejecting"));
+                return null;
+            }
+
+            NativeRef.SSL_SESSION ref =
+                    new NativeRef.SSL_SESSION(NativeCrypto.d2i_SSL_SESSION(sessionData));
+            return new Impl(context, ref, host, port, peerCerts, ocspData, tlsSctData);
+        } catch (IOException e) {
+            log(e);
+            return null;
+        } catch (BufferUnderflowException e) {
+            log(e);
+            return null;
+        }
+    }
+
+    abstract byte[] getId();
+
+    abstract boolean isValid();
+
+    /**
+     * Returns whether this session should only ever be used for resumption once.
+     */
+    abstract boolean isSingleUse();
+
+    abstract void offerToResume(NativeSsl ssl) throws SSLException;
+
+    abstract String getCipherSuite();
+
+    abstract String getProtocol();
+
+    abstract String getPeerHost();
+
+    abstract int getPeerPort();
+
+    /**
+     * Returns the OCSP stapled response. The returned array is not copied; the caller must
+     * either not modify the returned array or make a copy.
+     *
+     * @see <a href="https://tools.ietf.org/html/rfc6066">RFC 6066</a>
+     * @see <a href="https://tools.ietf.org/html/rfc6961">RFC 6961</a>
+     */
+    abstract byte[] getPeerOcspStapledResponse();
+
+    /**
+     * Returns the signed certificate timestamp (SCT) received from the peer. The returned array
+     * is not copied; the caller must either not modify the returned array or make a copy.
+     *
+     * @see <a href="https://tools.ietf.org/html/rfc6962">RFC 6962</a>
+     */
+    abstract byte[] getPeerSignedCertificateTimestamp();
+
+    /**
+     * Converts the given session to bytes.
+     *
+     * @return session data as bytes or null if the session can't be converted
+     */
+    abstract byte[] toBytes();
+
+    /**
+     * Converts this object to a {@link SSLSession}. The returned session will support only a
+     * subset of the {@link SSLSession} API.
+     */
+    abstract SSLSession toSSLSession();
+
+    /**
+     * The session wrapper implementation.
+     */
+    private static final class Impl extends NativeSslSession {
+        private final NativeRef.SSL_SESSION ref;
+
+        // BoringSSL offers no API to obtain these values directly from the SSL_SESSION.
+        private final AbstractSessionContext context;
+        private final String host;
+        private final int port;
+        private final String protocol;
+        private final String cipherSuite;
+        private final java.security.cert.X509Certificate[] peerCertificates;
+        private final byte[] peerOcspStapledResponse;
+        private final byte[] peerSignedCertificateTimestamp;
+
+        private Impl(AbstractSessionContext context, NativeRef.SSL_SESSION ref, String host,
+                int port, java.security.cert.X509Certificate[] peerCertificates,
+                byte[] peerOcspStapledResponse, byte[] peerSignedCertificateTimestamp) {
+            this.context = context;
+            this.host = host;
+            this.port = port;
+            this.peerCertificates = peerCertificates;
+            this.peerOcspStapledResponse = peerOcspStapledResponse;
+            this.peerSignedCertificateTimestamp = peerSignedCertificateTimestamp;
+            this.protocol = NativeCrypto.SSL_SESSION_get_version(ref.address);
+            this.cipherSuite =
+                    NativeCrypto.cipherSuiteToJava(NativeCrypto.SSL_SESSION_cipher(ref.address));
+            this.ref = ref;
+        }
+
+        @Override
+        byte[] getId() {
+            return NativeCrypto.SSL_SESSION_session_id(ref.address);
+        }
+
+        private long getCreationTime() {
+            return NativeCrypto.SSL_SESSION_get_time(ref.address);
+        }
+
+        @Override
+        boolean isValid() {
+            long creationTimeMillis = getCreationTime();
+            // Use the minimum of the timeout from the context and the session.
+            long timeoutMillis = Math.max(0,
+                                         Math.min(context.getSessionTimeout(),
+                                                 NativeCrypto.SSL_SESSION_get_timeout(ref.address)))
+                    * 1000;
+            return (System.currentTimeMillis() - timeoutMillis) < creationTimeMillis;
+        }
+
+        @Override
+        boolean isSingleUse() {
+            return NativeCrypto.SSL_SESSION_should_be_single_use(ref.address);
+        }
+
+        @Override
+        void offerToResume(NativeSsl ssl) throws SSLException {
+            ssl.offerToResumeSession(ref.address);
+        }
+
+        @Override
+        String getCipherSuite() {
+            return cipherSuite;
+        }
+
+        @Override
+        String getProtocol() {
+            return protocol;
+        }
+
+        @Override
+        String getPeerHost() {
+            return host;
+        }
+
+        @Override
+        int getPeerPort() {
+            return port;
+        }
+
+        @Override
+        byte[] getPeerOcspStapledResponse() {
+            return peerOcspStapledResponse;
+        }
+
+        @Override
+        byte[] getPeerSignedCertificateTimestamp() {
+            return peerSignedCertificateTimestamp;
+        }
+
+        @Override
+        byte[] toBytes() {
+            try {
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                DataOutputStream daos = new DataOutputStream(baos);
+
+                daos.writeInt(OPEN_SSL_WITH_TLS_SCT.value); // session type ID
+
+                // Session data.
+                byte[] data = NativeCrypto.i2d_SSL_SESSION(ref.address);
+                daos.writeInt(data.length);
+                daos.write(data);
+
+                // Certificates.
+                daos.writeInt(peerCertificates.length);
+
+                for (Certificate cert : peerCertificates) {
+                    data = cert.getEncoded();
+                    daos.writeInt(data.length);
+                    daos.write(data);
+                }
+
+                if (peerOcspStapledResponse != null) {
+                    daos.writeInt(1);
+                    daos.writeInt(peerOcspStapledResponse.length);
+                    daos.write(peerOcspStapledResponse);
+                } else {
+                    daos.writeInt(0);
+                }
+
+                if (peerSignedCertificateTimestamp != null) {
+                    daos.writeInt(peerSignedCertificateTimestamp.length);
+                    daos.write(peerSignedCertificateTimestamp);
+                } else {
+                    daos.writeInt(0);
+                }
+
+                // TODO: local certificates?
+
+                return baos.toByteArray();
+            } catch (IOException e) {
+                // TODO(nathanmittler): Better error handling?
+                logger.log(Level.WARNING, "Failed to convert saved SSL Session: ", e);
+                return null;
+            } catch (CertificateEncodingException e) {
+                log(e);
+                return null;
+            }
+        }
+
+        @Override
+        SSLSession toSSLSession() {
+            return new SSLSession() {
+                @Override
+                public byte[] getId() {
+                    return Impl.this.getId();
+                }
+
+                @Override
+                public String getCipherSuite() {
+                    return Impl.this.getCipherSuite();
+                }
+
+                @Override
+                public String getProtocol() {
+                    return Impl.this.getProtocol();
+                }
+
+                @Override
+                public String getPeerHost() {
+                    return Impl.this.getPeerHost();
+                }
+
+                @Override
+                public int getPeerPort() {
+                    return Impl.this.getPeerPort();
+                }
+
+                @Override
+                public long getCreationTime() {
+                    return Impl.this.getCreationTime();
+                }
+
+                @Override
+                public boolean isValid() {
+                    return Impl.this.isValid();
+                }
+
+                // UNSUPPORTED OPERATIONS
+
+                @Override
+                public SSLSessionContext getSessionContext() {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public long getLastAccessedTime() {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public void invalidate() {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public void putValue(String s, Object o) {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public Object getValue(String s) {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public void removeValue(String s) {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public String[] getValueNames() {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public Certificate[] getLocalCertificates() {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public X509Certificate[] getPeerCertificateChain()
+                        throws SSLPeerUnverifiedException {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public Principal getLocalPrincipal() {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public int getPacketBufferSize() {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public int getApplicationBufferSize() {
+                    throw new UnsupportedOperationException();
+                }
+            };
+        }
+    }
+
+    private static void log(Throwable t) {
+        // TODO(nathanmittler): Better error handling?
+        logger.log(Level.INFO, "Error inflating SSL session: {0}",
+                (t.getMessage() != null ? t.getMessage() : t.getClass().getName()));
+    }
+
+    private static void checkRemaining(ByteBuffer buf, int length) throws IOException {
+        if (length < 0) {
+            throw new IOException("Length is negative: " + length);
+        }
+        if (length > buf.remaining()) {
+            throw new IOException(
+                    "Length of blob is longer than available: " + length + " > " + buf.remaining());
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OAEPParameters.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OAEPParameters.java
new file mode 100644
index 0000000..02c4acd
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OAEPParameters.java
@@ -0,0 +1,293 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.security.AlgorithmParametersSpi;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.MGF1ParameterSpec;
+import java.util.HashMap;
+import java.util.Map;
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.crypto.spec.PSource;
+
+/**
+ * AlgorithmParameters implementation for OAEP.  The only supported encoding format is ASN.1,
+ * as specified in RFC 4055 section 4.1.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public class OAEPParameters extends AlgorithmParametersSpi {
+
+    private static final Map<String, String> OID_TO_NAME = new HashMap<String, String>();
+    private static final Map<String, String> NAME_TO_OID = new HashMap<String, String>();
+    static {
+        OID_TO_NAME.put("1.3.14.3.2.26", "SHA-1");
+        OID_TO_NAME.put("2.16.840.1.101.3.4.2.4", "SHA-224");
+        OID_TO_NAME.put("2.16.840.1.101.3.4.2.1", "SHA-256");
+        OID_TO_NAME.put("2.16.840.1.101.3.4.2.2", "SHA-384");
+        OID_TO_NAME.put("2.16.840.1.101.3.4.2.3", "SHA-512");
+        for (Map.Entry<String, String> entry : OID_TO_NAME.entrySet()) {
+            NAME_TO_OID.put(entry.getValue(), entry.getKey());
+        }
+    }
+    private static final String MGF1_OID = "1.2.840.113549.1.1.8";
+    private static final String PSPECIFIED_OID = "1.2.840.113549.1.1.9";
+
+    private OAEPParameterSpec spec = OAEPParameterSpec.DEFAULT;
+
+    @libcore.api.IntraCoreApi
+    public OAEPParameters() {}
+
+    @Override
+    protected void engineInit(AlgorithmParameterSpec algorithmParameterSpec)
+            throws InvalidParameterSpecException {
+        if (algorithmParameterSpec instanceof OAEPParameterSpec) {
+            this.spec = (OAEPParameterSpec) algorithmParameterSpec;
+        } else {
+            throw new InvalidParameterSpecException("Only OAEPParameterSpec is supported");
+        }
+    }
+
+    @Override
+    protected void engineInit(byte[] bytes) throws IOException {
+        long readRef = 0;
+        long seqRef = 0;
+        try {
+            readRef = NativeCrypto.asn1_read_init(bytes);
+            seqRef = NativeCrypto.asn1_read_sequence(readRef);
+            PSource.PSpecified pSpecified = PSource.PSpecified.DEFAULT;
+            String hash = readHash(seqRef);
+            String mgfHash = readMgfHash(seqRef);
+            if (NativeCrypto.asn1_read_next_tag_is(seqRef, 2)) {
+                long pSourceRef = 0;
+                long pSourceSeqRef = 0;
+                try {
+                    pSourceRef = NativeCrypto.asn1_read_tagged(seqRef);
+                    pSourceSeqRef = NativeCrypto.asn1_read_sequence(pSourceRef);
+                    String pSourceOid = NativeCrypto.asn1_read_oid(pSourceSeqRef);
+                    if (!pSourceOid.equals(PSPECIFIED_OID)) {
+                        throw new IOException("Error reading ASN.1 encoding");
+                    }
+                    pSpecified = new PSource.PSpecified(
+                            NativeCrypto.asn1_read_octetstring(pSourceSeqRef));
+                    if (!NativeCrypto.asn1_read_is_empty(pSourceSeqRef)) {
+                        throw new IOException("Error reading ASN.1 encoding");
+                    }
+                } finally {
+                    NativeCrypto.asn1_read_free(pSourceSeqRef);
+                    NativeCrypto.asn1_read_free(pSourceRef);
+                }
+            }
+
+            if (!NativeCrypto.asn1_read_is_empty(seqRef)
+                    || !NativeCrypto.asn1_read_is_empty(readRef)) {
+                throw new IOException("Error reading ASN.1 encoding");
+            }
+            this.spec = new OAEPParameterSpec(hash, "MGF1", new MGF1ParameterSpec(mgfHash),
+                    pSpecified);
+        } finally {
+            NativeCrypto.asn1_read_free(seqRef);
+            NativeCrypto.asn1_read_free(readRef);
+        }
+    }
+
+    @Override
+    protected void engineInit(byte[] bytes, String format) throws IOException {
+        if ((format == null) || format.equals("ASN.1")) {
+            engineInit(bytes);
+        } else {
+            throw new IOException("Unsupported format: " + format);
+        }
+    }
+
+    // Shared with PSSParameters, since they share some of their encoded form
+    static String readHash(long seqRef) throws IOException {
+        if (NativeCrypto.asn1_read_next_tag_is(seqRef, 0)) {
+            long hashRef = 0;
+            try {
+                hashRef = NativeCrypto.asn1_read_tagged(seqRef);
+                return getHashName(hashRef);
+            } finally {
+                NativeCrypto.asn1_read_free(hashRef);
+            }
+        }
+        return "SHA-1";
+    }
+
+    // Shared with PSSParameters, since they share some of their encoded form
+    static String readMgfHash(long seqRef) throws IOException {
+        if (NativeCrypto.asn1_read_next_tag_is(seqRef, 1)) {
+            long mgfRef = 0;
+            long mgfSeqRef = 0;
+            try {
+                mgfRef = NativeCrypto.asn1_read_tagged(seqRef);
+                mgfSeqRef = NativeCrypto.asn1_read_sequence(mgfRef);
+                String mgfOid = NativeCrypto.asn1_read_oid(mgfSeqRef);
+                if (!mgfOid.equals(MGF1_OID)) {
+                    throw new IOException("Error reading ASN.1 encoding");
+                }
+                String mgfHash = getHashName(mgfSeqRef);
+                if (!NativeCrypto.asn1_read_is_empty(mgfSeqRef)) {
+                    throw new IOException("Error reading ASN.1 encoding");
+                }
+                return mgfHash;
+            } finally {
+                NativeCrypto.asn1_read_free(mgfSeqRef);
+                NativeCrypto.asn1_read_free(mgfRef);
+            }
+        }
+        return "SHA-1";
+    }
+
+    private static String getHashName(long hashRef) throws IOException {
+        long hashSeqRef = 0;
+        try {
+            hashSeqRef = NativeCrypto.asn1_read_sequence(hashRef);
+            String hashOid = NativeCrypto.asn1_read_oid(hashSeqRef);
+            if (!NativeCrypto.asn1_read_is_empty(hashSeqRef)) {
+                NativeCrypto.asn1_read_null(hashSeqRef);
+            }
+            if (!NativeCrypto.asn1_read_is_empty(hashSeqRef)
+                    || !OID_TO_NAME.containsKey(hashOid)) {
+                throw new IOException("Error reading ASN.1 encoding");
+            }
+            return OID_TO_NAME.get(hashOid);
+        } finally {
+            NativeCrypto.asn1_read_free(hashSeqRef);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(Class<T> aClass)
+            throws InvalidParameterSpecException {
+        if ((aClass != null) && aClass == OAEPParameterSpec.class) {
+            return (T) spec;
+        } else {
+            throw new InvalidParameterSpecException("Unsupported class: " + aClass);
+        }
+    }
+
+    @Override
+    protected byte[] engineGetEncoded() throws IOException {
+        long cbbRef = 0;
+        long seqRef = 0;
+        try {
+            cbbRef = NativeCrypto.asn1_write_init();
+            seqRef = NativeCrypto.asn1_write_sequence(cbbRef);
+            writeHashAndMgfHash(seqRef, spec.getDigestAlgorithm(),
+                    (MGF1ParameterSpec) spec.getMGFParameters());
+            PSource.PSpecified pSource = (PSource.PSpecified) spec.getPSource();
+            // Implementations are prohibited from writing the default value for any of the fields
+            if (pSource.getValue().length != 0) {
+                long pSourceRef = 0;
+                long pSourceParamsRef = 0;
+                try {
+                    pSourceRef = NativeCrypto.asn1_write_tag(seqRef, 2);
+                    pSourceParamsRef = writeAlgorithmIdentifier(pSourceRef, PSPECIFIED_OID);
+                    NativeCrypto.asn1_write_octetstring(pSourceParamsRef, pSource.getValue());
+                } finally {
+                    NativeCrypto.asn1_write_flush(seqRef);
+                    NativeCrypto.asn1_write_free(pSourceParamsRef);
+                    NativeCrypto.asn1_write_free(pSourceRef);
+                }
+            }
+            return NativeCrypto.asn1_write_finish(cbbRef);
+        } catch (IOException e) {
+            NativeCrypto.asn1_write_cleanup(cbbRef);
+            throw e;
+        } finally {
+            NativeCrypto.asn1_write_free(seqRef);
+            NativeCrypto.asn1_write_free(cbbRef);
+        }
+    }
+
+    @Override
+    protected byte[] engineGetEncoded(String format) throws IOException {
+        if ((format == null) || format.equals("ASN.1")) {
+            return engineGetEncoded();
+        }
+        throw new IOException("Unsupported format: " + format);
+    }
+
+    // Shared with PSSParameters, since they share some of their encoded form
+    static void writeHashAndMgfHash(long seqRef, String hash, MGF1ParameterSpec mgfSpec) throws IOException {
+        // Implementations are prohibited from writing the default value for any of the fields
+        if (!hash.equals("SHA-1")) {
+            long hashRef = 0;
+            long hashParamsRef = 0;
+            try {
+                hashRef = NativeCrypto.asn1_write_tag(seqRef, 0);
+                hashParamsRef = writeAlgorithmIdentifier(
+                        hashRef, NAME_TO_OID.get(hash));
+                NativeCrypto.asn1_write_null(hashParamsRef);
+            } finally {
+                NativeCrypto.asn1_write_flush(seqRef);
+                NativeCrypto.asn1_write_free(hashParamsRef);
+                NativeCrypto.asn1_write_free(hashRef);
+            }
+        }
+        if (!mgfSpec.getDigestAlgorithm().equals("SHA-1")) {
+            long mgfRef = 0;
+            long mgfParamsRef = 0;
+            long hashParamsRef = 0;
+            try {
+                mgfRef = NativeCrypto.asn1_write_tag(seqRef, 1);
+                mgfParamsRef = writeAlgorithmIdentifier(mgfRef, MGF1_OID);
+                hashParamsRef = writeAlgorithmIdentifier(
+                        mgfParamsRef, NAME_TO_OID.get(mgfSpec.getDigestAlgorithm()));
+                NativeCrypto.asn1_write_null(hashParamsRef);
+            } finally {
+                NativeCrypto.asn1_write_flush(seqRef);
+                NativeCrypto.asn1_write_free(hashParamsRef);
+                NativeCrypto.asn1_write_free(mgfParamsRef);
+                NativeCrypto.asn1_write_free(mgfRef);
+            }
+        }
+    }
+
+    /**
+     * Writes an ASN.1 AlgorithmIdentifier structure into container, which looks like
+     * <pre>
+     * SEQUENCE
+     *   OBJECT IDENTIFIER
+     *   PARAMS (based on the particular algorithm)
+     * </pre>
+     * This method returns a reference to the sequence such that the params may be added to it.
+     * The reference needs to be freed with asn1_write_free once it's used.
+     */
+    private static long writeAlgorithmIdentifier(long container, String oid) throws IOException {
+        long seqRef = 0;
+        try {
+            seqRef = NativeCrypto.asn1_write_sequence(container);
+            NativeCrypto.asn1_write_oid(seqRef, oid);
+        } catch (IOException e) {
+            NativeCrypto.asn1_write_free(seqRef);
+            throw e;
+        }
+        return seqRef;
+    }
+
+    @Override
+    protected String engineToString() {
+        return "Conscrypt OAEP AlgorithmParameters";
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OidData.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OidData.java
new file mode 100644
index 0000000..d84dc4d
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OidData.java
@@ -0,0 +1,64 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Data about OIDs.
+ */
+final class OidData {
+
+  private OidData() {}
+
+  private static final Map<String, String> OID_TO_NAME_MAP = new HashMap<>();
+
+  static {
+    // NOTE: For the time being, we only have X509 signature algorithms here, since we only need
+    // them for determining the name of signature algorithms in certs and CRLs.  We can add more in
+    // the future if we need them.
+
+    // Signatures
+
+    // RFC 3279
+    OID_TO_NAME_MAP.put("1.2.840.113549.1.1.2", "MD2withRSA");
+    OID_TO_NAME_MAP.put("1.2.840.113549.1.1.4", "MD5withRSA");
+    OID_TO_NAME_MAP.put("1.2.840.113549.1.1.5", "SHA1withRSA");
+    OID_TO_NAME_MAP.put("1.2.840.10040.4.3", "SHA1withDSA");
+    OID_TO_NAME_MAP.put("1.2.840.10045.4.1", "SHA1withECDSA");
+
+    // RFC 4055
+    OID_TO_NAME_MAP.put("1.2.840.113549.1.1.14", "SHA224withRSA");
+    OID_TO_NAME_MAP.put("1.2.840.113549.1.1.11", "SHA256withRSA");
+    OID_TO_NAME_MAP.put("1.2.840.113549.1.1.12", "SHA384withRSA");
+    OID_TO_NAME_MAP.put("1.2.840.113549.1.1.13", "SHA512withRSA");
+
+    // RFC 5758
+    OID_TO_NAME_MAP.put("2.16.840.1.101.3.4.3.1", "SHA224withDSA");
+    OID_TO_NAME_MAP.put("2.16.840.1.101.3.4.3.2", "SHA256withDSA");
+    OID_TO_NAME_MAP.put("1.2.840.10045.4.3.1", "SHA224withECDSA");
+    OID_TO_NAME_MAP.put("1.2.840.10045.4.3.2", "SHA256withECDSA");
+    OID_TO_NAME_MAP.put("1.2.840.10045.4.3.3", "SHA384withECDSA");
+    OID_TO_NAME_MAP.put("1.2.840.10045.4.3.4", "SHA512withECDSA");
+  }
+
+  public static String oidToAlgorithmName(String oid) {
+    return OID_TO_NAME_MAP.get(oid);
+  }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLAeadCipher.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLAeadCipher.java
new file mode 100644
index 0000000..5542eed
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLAeadCipher.java
@@ -0,0 +1,366 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.nio.ByteBuffer;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Arrays;
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public abstract class OpenSSLAeadCipher extends OpenSSLCipher {
+    /**
+     * The default tag size when one is not specified. Default to
+     * full-length tags (128-bits or 16 octets).
+     */
+    static final int DEFAULT_TAG_SIZE_BITS = 16 * 8;
+
+    /**
+     * Keeps track of the last used block size.
+     */
+    private static int lastGlobalMessageSize = 32;
+
+    /**
+     * The previously used key to prevent key + nonce (IV) reuse.
+     */
+    private byte[] previousKey;
+
+    /**
+     * The previously used nonce (IV) to prevent key + nonce reuse.
+     */
+    private byte[] previousIv;
+
+    /**
+     * When set this instance must be initialized before use again. This prevents key
+     * and IV reuse.
+     */
+    private boolean mustInitialize;
+
+    /**
+     * The byte array containing the bytes written.
+     */
+    byte[] buf;
+
+    /**
+     * The number of bytes written.
+     */
+    int bufCount;
+
+    /**
+     * AEAD cipher reference.
+     */
+    long evpAead;
+
+    /**
+     * Additional authenticated data.
+     */
+    private byte[] aad;
+
+    /**
+     * The length of the AEAD cipher tag in bytes.
+     */
+    int tagLengthInBytes;
+
+    public OpenSSLAeadCipher(Mode mode) {
+        super(mode, Padding.NOPADDING);
+    }
+
+    private void checkInitialization() {
+        if (mustInitialize) {
+            throw new IllegalStateException(
+                    "Cannot re-use same key and IV for multiple encryptions");
+        }
+    }
+
+    /** Constant-time array comparison.  Since we are using this to compare keys, we want to
+     * ensure there's no opportunity for a timing attack. */
+    private boolean arraysAreEqual(byte[] a, byte[] b) {
+        if (a.length != b.length) {
+            return false;
+        }
+
+        int diff = 0;
+        for (int i = 0; i < a.length; i++) {
+            diff |= a[i] ^ b[i];
+        }
+        return diff == 0;
+    }
+
+    private void expand(int i) {
+        /* Can the buffer handle i more bytes, if not expand it */
+        if (bufCount + i <= buf.length) {
+            return;
+        }
+
+        byte[] newbuf = new byte[(bufCount + i) * 2];
+        System.arraycopy(buf, 0, newbuf, 0, bufCount);
+        buf = newbuf;
+    }
+
+    private void reset() {
+        aad = null;
+        final int lastBufSize = lastGlobalMessageSize;
+        if (buf == null) {
+            buf = new byte[lastBufSize];
+        } else if (bufCount > 0 && bufCount != lastBufSize) {
+            lastGlobalMessageSize = bufCount;
+            if (buf.length != bufCount) {
+                buf = new byte[bufCount];
+            }
+        }
+        bufCount = 0;
+    }
+
+    @Override
+    void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params,
+            SecureRandom random) throws InvalidKeyException,
+        InvalidAlgorithmParameterException {
+        byte[] iv;
+        final int tagLenBits;
+        if (params == null) {
+            iv = null;
+            tagLenBits = DEFAULT_TAG_SIZE_BITS;
+        } else {
+            GCMParameters gcmParams = Platform.fromGCMParameterSpec(params);
+            if (gcmParams != null) {
+                iv = gcmParams.getIV();
+                tagLenBits = gcmParams.getTLen();
+            } else if (params instanceof IvParameterSpec) {
+                IvParameterSpec ivParams = (IvParameterSpec) params;
+                iv = ivParams.getIV();
+                tagLenBits = DEFAULT_TAG_SIZE_BITS;
+            } else {
+                iv = null;
+                tagLenBits = DEFAULT_TAG_SIZE_BITS;
+            }
+        }
+
+        checkSupportedTagLength(tagLenBits);
+
+        tagLengthInBytes = tagLenBits / 8;
+
+        final boolean encrypting = isEncrypting();
+
+        evpAead = getEVP_AEAD(encodedKey.length);
+
+        final int expectedIvLength = NativeCrypto.EVP_AEAD_nonce_length(evpAead);
+        if (iv == null && expectedIvLength != 0) {
+            if (!encrypting) {
+                throw new InvalidAlgorithmParameterException("IV must be specified in " + mode
+                        + " mode");
+            }
+
+            iv = new byte[expectedIvLength];
+            if (random != null) {
+                random.nextBytes(iv);
+            } else {
+                NativeCrypto.RAND_bytes(iv);
+            }
+        } else if (expectedIvLength == 0 && iv != null) {
+            throw new InvalidAlgorithmParameterException("IV not used in " + mode + " mode");
+        } else if (iv != null && iv.length != expectedIvLength) {
+            throw new InvalidAlgorithmParameterException("Expected IV length of "
+                    + expectedIvLength + " but was " + iv.length);
+        }
+
+        if (isEncrypting() && iv != null && !allowsNonceReuse()) {
+            if (previousKey != null && previousIv != null
+                    && arraysAreEqual(previousKey, encodedKey)
+                    && arraysAreEqual(previousIv, iv)) {
+                mustInitialize = true;
+                throw new InvalidAlgorithmParameterException(
+                        "When using AEAD key and IV must not be re-used");
+            }
+
+            this.previousKey = encodedKey;
+            this.previousIv = iv;
+        }
+        mustInitialize = false;
+        this.iv = iv;
+        reset();
+    }
+
+    void checkSupportedTagLength(int tagLenBits)
+            throws InvalidAlgorithmParameterException {
+        if (tagLenBits % 8 != 0) {
+            throw new InvalidAlgorithmParameterException(
+                    "Tag length must be a multiple of 8; was " + tagLenBits);
+        }
+    }
+
+    /**
+     * Returns whether reusing nonces is allowed (aka, whether this is nonce misuse-resistant).
+     * Most AEAD ciphers are not, but some are specially constructed so that reusing a key/nonce
+     * pair is safe.
+     */
+    boolean allowsNonceReuse() {
+        return false;
+    }
+
+    @Override
+    protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
+            int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
+        BadPaddingException {
+        // Because the EVP_AEAD updateInternal processes input but doesn't create any output
+        // (and thus can't check the output buffer), we need to add this check before the
+        // superclass' processing to ensure that updateInternal is never called if the
+        // output buffer isn't large enough.
+        if (output != null) {
+            if (getOutputSizeForFinal(inputLen) > output.length - outputOffset) {
+                throw new ShortBufferException("Insufficient output space");
+            }
+        }
+        return super.engineDoFinal(input, inputOffset, inputLen, output, outputOffset);
+    }
+
+    @Override
+    int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output,
+            int outputOffset, int maximumLen) throws ShortBufferException {
+        checkInitialization();
+        if (buf == null) {
+            throw new IllegalStateException("Cipher not initialized");
+        }
+
+        ArrayUtils.checkOffsetAndCount(input.length, inputOffset, inputLen);
+        if (inputLen > 0) {
+            expand(inputLen);
+            System.arraycopy(input, inputOffset, buf, this.bufCount, inputLen);
+            this.bufCount += inputLen;
+        }
+        return 0;
+    }
+
+    @SuppressWarnings("LiteralClassName")
+    private void throwAEADBadTagExceptionIfAvailable(String message, Throwable cause)
+            throws BadPaddingException {
+        Constructor<?> aeadBadTagConstructor;
+        try {
+            aeadBadTagConstructor = Class.forName("javax.crypto.AEADBadTagException")
+                                            .getConstructor(String.class);
+        } catch (Exception ignored) {
+            return;
+        }
+
+        BadPaddingException badTagException = null;
+        try {
+            badTagException = (BadPaddingException) aeadBadTagConstructor.newInstance(message);
+            badTagException.initCause(cause);
+        } catch (IllegalAccessException e2) {
+            // Fall through
+        } catch (InstantiationException e2) {
+            // Fall through
+        } catch (InvocationTargetException e2) {
+            throw(BadPaddingException) new BadPaddingException().initCause(
+                    e2.getTargetException());
+        }
+        if (badTagException != null) {
+            throw badTagException;
+        }
+    }
+
+    @Override
+    int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
+            throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
+        checkInitialization();
+        final int bytesWritten;
+        try {
+            if (isEncrypting()) {
+                bytesWritten = NativeCrypto.EVP_AEAD_CTX_seal(evpAead, encodedKey,
+                        tagLengthInBytes, output, outputOffset, iv, buf, 0, bufCount, aad);
+            } else {
+                bytesWritten = NativeCrypto.EVP_AEAD_CTX_open(evpAead, encodedKey,
+                        tagLengthInBytes, output, outputOffset, iv, buf, 0, bufCount, aad);
+            }
+        } catch (BadPaddingException e) {
+            throwAEADBadTagExceptionIfAvailable(e.getMessage(), e.getCause());
+            throw e;
+        }
+        if (isEncrypting()) {
+            mustInitialize = true;
+        }
+        reset();
+        return bytesWritten;
+    }
+
+    @Override
+    void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
+        if (padding != Padding.NOPADDING) {
+            throw new NoSuchPaddingException("Must be NoPadding for AEAD ciphers");
+        }
+    }
+
+    /**
+     * AEAD buffers everything until a final output.
+     */
+    @Override
+    int getOutputSizeForUpdate(int inputLen) {
+        return 0;
+    }
+
+    @Override
+    int getOutputSizeForFinal(int inputLen) {
+        return bufCount + inputLen
+                + (isEncrypting() ? NativeCrypto.EVP_AEAD_max_overhead(evpAead) : 0);
+    }
+
+    // Intentionally missing Override to compile on old versions of Android
+    @SuppressWarnings("MissingOverride")
+    protected void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) {
+        checkInitialization();
+        if (aad == null) {
+            aad = Arrays.copyOfRange(input, inputOffset, inputOffset + inputLen);
+        } else {
+            int newSize = aad.length + inputLen;
+            byte[] newaad = new byte[newSize];
+            System.arraycopy(aad, 0, newaad, 0, aad.length);
+            System.arraycopy(input, inputOffset, newaad, aad.length, inputLen);
+            aad = newaad;
+        }
+    }
+
+    // Intentionally missing Override to compile on old versions of Android
+    @SuppressWarnings("MissingOverride")
+    protected void engineUpdateAAD(ByteBuffer buf) {
+        checkInitialization();
+        if (aad == null) {
+            aad = new byte[buf.remaining()];
+            buf.get(aad);
+        } else {
+            int newSize = aad.length + buf.remaining();
+            byte[] newaad = new byte[newSize];
+            System.arraycopy(aad, 0, newaad, 0, aad.length);
+            buf.get(newaad, aad.length, buf.remaining());
+            aad = newaad;
+        }
+    }
+
+    abstract long getEVP_AEAD(int keyLength) throws InvalidKeyException;
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLAeadCipherAES.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLAeadCipherAES.java
new file mode 100644
index 0000000..59c59e9
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLAeadCipherAES.java
@@ -0,0 +1,256 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public abstract class OpenSSLAeadCipherAES extends OpenSSLAeadCipher {
+    private static final int AES_BLOCK_SIZE = 16;
+
+    OpenSSLAeadCipherAES(Mode mode) {
+        super(mode);
+    }
+
+    @Override
+    void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+        switch (keyLength) {
+            case 16: // AES 128
+            case 32: // AES 256
+                return;
+            default:
+                throw new InvalidKeyException("Unsupported key size: " + keyLength
+                    + " bytes (must be 16 or 32)");
+        }
+    }
+
+    @Override
+    String getBaseCipherName() {
+        return "AES";
+    }
+
+    @Override
+    int getCipherBlockSize() {
+        return AES_BLOCK_SIZE;
+    }
+
+    @Override
+    protected AlgorithmParameterSpec getParameterSpec(AlgorithmParameters params)
+        throws InvalidAlgorithmParameterException {
+        if (params != null) {
+            AlgorithmParameterSpec spec = Platform.fromGCMParameters(params);
+            if (spec != null) {
+                return spec;
+            }
+            return super.getParameterSpec(params);
+        }
+        return null;
+    }
+
+    @Override
+    protected AlgorithmParameters engineGetParameters() {
+        // iv will be non-null after initialization.
+        if (iv == null) {
+            return null;
+        }
+
+        AlgorithmParameterSpec spec = Platform.toGCMParameterSpec(
+            tagLengthInBytes * 8, iv);
+        if (spec == null) {
+            // The platform doesn't support GCMParameterSpec. Fall back to
+            // the generic AES parameters so at least the caller can get the
+            // IV.
+            return super.engineGetParameters();
+        }
+
+        try {
+            AlgorithmParameters params = AlgorithmParameters.getInstance("GCM");
+            params.init(spec);
+            return params;
+        } catch (NoSuchAlgorithmException e) {
+            // We should not get here.
+            throw (Error) new AssertionError("GCM not supported").initCause(e);
+        } catch (InvalidParameterSpecException e) {
+            // This may happen since Conscrypt doesn't provide this itself.
+            return null;
+        }
+    }
+
+    @Override
+    int getOutputSizeForFinal(int inputLen) {
+        // For GCM, the tag is a fixed length and there is no padding or other
+        // concerns, so we can calculate the exact length required without a
+        // native call
+        if (isEncrypting()) {
+            return bufCount + inputLen + tagLengthInBytes;
+        } else {
+            return Math.max(0, bufCount + inputLen - tagLengthInBytes);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static class GCM extends OpenSSLAeadCipherAES {
+
+        @libcore.api.IntraCoreApi
+        public GCM() {
+            super(Mode.GCM);
+        }
+
+        @Override
+        void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
+            if (mode != Mode.GCM) {
+                throw new NoSuchAlgorithmException("Mode must be GCM");
+            }
+        }
+
+        @Override
+        long getEVP_AEAD(int keyLength) throws InvalidKeyException {
+            if (keyLength == 16) {
+                return NativeCrypto.EVP_aead_aes_128_gcm();
+            } else if (keyLength == 32) {
+                return NativeCrypto.EVP_aead_aes_256_gcm();
+            } else {
+                throw new RuntimeException("Unexpected key length: " + keyLength);
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static class AES_128 extends GCM {
+            @libcore.api.IntraCoreApi
+            public AES_128() {}
+
+            @Override
+            void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+                if (keyLength != 16) { // 128 bits
+                    throw new InvalidKeyException(
+                        "Unsupported key size: " + keyLength + " bytes (must be 16)");
+                }
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static class AES_256 extends GCM {
+            @libcore.api.IntraCoreApi
+            public AES_256() {}
+
+            @Override
+            void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+                if (keyLength != 32) { // 256 bits
+                    throw new InvalidKeyException(
+                        "Unsupported key size: " + keyLength + " bytes (must be 32)");
+                }
+            }
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static class GCM_SIV extends OpenSSLAeadCipherAES {
+        @libcore.api.IntraCoreApi
+        public GCM_SIV() {
+            super(Mode.GCM_SIV);
+        }
+
+        @Override
+        void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
+            if (mode != Mode.GCM_SIV) {
+                throw new NoSuchAlgorithmException("Mode must be GCM-SIV");
+            }
+        }
+
+        @Override
+        boolean allowsNonceReuse() {
+            return true;
+        }
+
+        @Override
+        void checkSupportedTagLength(int tagLengthInBits)
+            throws InvalidAlgorithmParameterException {
+            // GCM_SIV only supports full-size tags
+            if (tagLengthInBits != DEFAULT_TAG_SIZE_BITS) {
+                throw new InvalidAlgorithmParameterException(
+                    "Tag length must be " + DEFAULT_TAG_SIZE_BITS + " bits");
+            }
+        }
+
+        @Override
+        long getEVP_AEAD(int keyLength) throws InvalidKeyException {
+            if (keyLength == 16) {
+                return NativeCrypto.EVP_aead_aes_128_gcm_siv();
+            } else if (keyLength == 32) {
+                return NativeCrypto.EVP_aead_aes_256_gcm_siv();
+            } else {
+                throw new RuntimeException("Unexpected key length: " + keyLength);
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static class AES_128 extends GCM_SIV {
+            @libcore.api.IntraCoreApi
+            public AES_128() {}
+
+            @Override
+            void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+                if (keyLength != 16) { // 128 bits
+                    throw new InvalidKeyException(
+                        "Unsupported key size: " + keyLength + " bytes (must be 16)");
+                }
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static class AES_256 extends GCM_SIV {
+            @libcore.api.IntraCoreApi
+            public AES_256() {}
+
+            @Override
+            void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+                if (keyLength != 32) { // 256 bits
+                    throw new InvalidKeyException(
+                        "Unsupported key size: " + keyLength + " bytes (must be 32)");
+                }
+            }
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLAeadCipherChaCha20.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLAeadCipherChaCha20.java
new file mode 100644
index 0000000..3d753eb
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLAeadCipherChaCha20.java
@@ -0,0 +1,79 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public class OpenSSLAeadCipherChaCha20 extends OpenSSLAeadCipher {
+    @libcore.api.IntraCoreApi
+    public OpenSSLAeadCipherChaCha20() {
+        super(Mode.POLY1305);
+    }
+
+    @Override
+    void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+        if (keyLength != 32) {
+            throw new InvalidKeyException("Unsupported key size: " + keyLength
+                    + " bytes (must be 32)");
+        }
+    }
+
+    @Override
+    String getBaseCipherName() {
+        return "ChaCha20";
+    }
+
+    @Override
+    int getCipherBlockSize() {
+        return 0;
+    }
+
+    @Override
+    void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
+        if (mode != Mode.POLY1305) {
+            throw new NoSuchAlgorithmException("Mode must be Poly1305");
+        }
+    }
+
+    @Override
+    long getEVP_AEAD(int keyLength) throws InvalidKeyException {
+        if (keyLength == 32) {
+            return NativeCrypto.EVP_aead_chacha20_poly1305();
+        } else {
+            throw new RuntimeException("Unexpected key length: " + keyLength);
+        }
+    }
+
+    @Override
+    int getOutputSizeForFinal(int inputLen) {
+        // For ChaCha20+Poly1305, the tag is always 16 bytes long and there is no
+        // padding or other concerns, so we can calculate the exact length required
+        // without a native call
+        if (isEncrypting()) {
+            return bufCount + inputLen + 16;
+        } else {
+            return Math.max(0, bufCount + inputLen - 16);
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBIOInputStream.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBIOInputStream.java
new file mode 100644
index 0000000..6a14d01
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBIOInputStream.java
@@ -0,0 +1,111 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Provides an interface to OpenSSL's BIO system directly from a Java
+ * InputStream. It allows an OpenSSL API to read directly from something more
+ * flexible interface than a byte array.
+ */
+class OpenSSLBIOInputStream extends FilterInputStream {
+    private long ctx;
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    OpenSSLBIOInputStream(InputStream is, boolean isFinite) {
+        super(is);
+
+        ctx = NativeCrypto.create_BIO_InputStream(this, isFinite);
+    }
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    long getBioContext() {
+        return ctx;
+    }
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    void release() {
+        NativeCrypto.BIO_free_all(ctx);
+    }
+
+    /**
+     * Similar to a {@code readLine} method, but matches what OpenSSL expects
+     * from a {@code BIO_gets} method.
+     */
+    int gets(byte[] buffer) throws IOException {
+        if (buffer == null || buffer.length == 0) {
+            return 0;
+        }
+
+        int offset = 0;
+        int inputByte = 0;
+        while (offset < buffer.length) {
+            inputByte = read();
+            if (inputByte == -1) {
+                // EOF
+                break;
+            }
+            if (inputByte == '\n') {
+                if (offset == 0) {
+                    // If we haven't read anything yet, ignore CRLF.
+                    continue;
+                } else {
+                    break;
+                }
+            }
+
+            buffer[offset++] = (byte) inputByte;
+        }
+
+        return offset;
+    }
+
+    @Override
+    public int read(byte[] buffer) throws IOException {
+        return read(buffer, 0, buffer.length);
+    }
+
+    // InputStream.read() is allowed to return less than len bytes, and the socket InputStreams
+    // that are used with this class typically do so when the length requested is more than what
+    // is already buffered, but some users of BoringSSL's BIO APIs expect that any read call either
+    // fills the buffer completely, fails, or reaches EOF.  We patch over this difference by
+    // repeatedly calling super.read() until it indicates EOF or we fill the buffer.
+    @Override
+    public int read(byte[] buffer, int offset, int len) throws IOException {
+        if (offset < 0 || len < 0 || len > buffer.length - offset) {
+            throw new IndexOutOfBoundsException("Invalid bounds");
+        }
+        if (len == 0) {
+            return 0;
+        }
+        int totalRead = 0;
+        int read;
+        do {
+            read = super.read(buffer, offset + totalRead, len - totalRead - offset);
+            if (read == -1) {
+                break;
+            }
+            totalRead += read;
+        } while (offset + totalRead < len);
+
+        return totalRead == 0 ? -1 : totalRead;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBIOSink.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBIOSink.java
new file mode 100644
index 0000000..76fc6e1
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBIOSink.java
@@ -0,0 +1,78 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Wraps a BoringSSL BIO to act as a place to write out data.
+ */
+final class OpenSSLBIOSink {
+    private final long ctx;
+    private final ByteArrayOutputStream buffer;
+    private int position;
+
+    static OpenSSLBIOSink create() {
+        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+        return new OpenSSLBIOSink(buffer);
+    }
+
+    private OpenSSLBIOSink(ByteArrayOutputStream buffer) {
+        ctx = NativeCrypto.create_BIO_OutputStream(buffer);
+        this.buffer = buffer;
+    }
+
+    int available() {
+        return buffer.size() - position;
+    }
+
+    void reset() {
+        buffer.reset();
+        position = 0;
+    }
+
+    long skip(long byteCount) {
+        int maxLength = Math.min(available(), (int) byteCount);
+        position += maxLength;
+        if (position == buffer.size()) {
+            reset();
+        }
+        return maxLength;
+    }
+
+    long getContext() {
+        return ctx;
+    }
+
+    byte[] toByteArray() {
+        return buffer.toByteArray();
+    }
+
+    int position() {
+        return position;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            NativeCrypto.BIO_free_all(ctx);
+        } finally {
+            super.finalize();
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBIOSource.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBIOSource.java
new file mode 100644
index 0000000..dbbd985
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLBIOSource.java
@@ -0,0 +1,107 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+/**
+ * Wrapped by a BoringSSL BIO to act as a source of bytes.
+ */
+final class OpenSSLBIOSource {
+    private OpenSSLBIOInputStream source;
+
+    static OpenSSLBIOSource wrap(ByteBuffer buffer) {
+        return new OpenSSLBIOSource(
+            new OpenSSLBIOInputStream(new ByteBufferInputStream(buffer), false));
+    }
+
+    private OpenSSLBIOSource(OpenSSLBIOInputStream source) {
+        this.source = source;
+    }
+
+    long getContext() {
+        return source.getBioContext();
+    }
+
+    private synchronized void release() {
+        if (source != null) {
+            NativeCrypto.BIO_free_all(source.getBioContext());
+            source = null;
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            release();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private static class ByteBufferInputStream extends InputStream {
+        private final ByteBuffer source;
+
+        ByteBufferInputStream(ByteBuffer source) {
+            this.source = source;
+        }
+
+        @Override
+        public int read() throws IOException {
+            if (source.remaining() > 0) {
+                return source.get();
+            } else {
+                return -1;
+            }
+        }
+
+        @Override
+        public int available() throws IOException {
+            return source.limit() - source.position();
+        }
+
+        @Override
+        public int read(byte[] buffer) throws IOException {
+            int originalPosition = source.position();
+            source.get(buffer);
+            return source.position() - originalPosition;
+        }
+
+        @Override
+        public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+            int toRead = Math.min(source.remaining(), byteCount);
+            int originalPosition = source.position();
+            source.get(buffer, byteOffset, toRead);
+            return source.position() - originalPosition;
+        }
+
+        @Override
+        public void reset() throws IOException {
+            source.reset();
+        }
+
+        @Override
+        public long skip(long byteCount) throws IOException {
+            long originalPosition = source.position();
+            source.position((int) (originalPosition + byteCount));
+            return source.position() - originalPosition;
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLCipher.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLCipher.java
new file mode 100644
index 0000000..b80803a
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLCipher.java
@@ -0,0 +1,490 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
+import java.util.Locale;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * An implementation of {@link Cipher} using BoringSSL as the backing library.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public abstract class OpenSSLCipher extends CipherSpi {
+
+    /**
+     * Modes that a block cipher may support.
+     */
+    enum Mode {
+        NONE,
+        CBC,
+        CTR,
+        ECB,
+        GCM,
+        GCM_SIV,
+        POLY1305,
+        ;
+
+        public static Mode getNormalized(String modeString) {
+            modeString = modeString.toUpperCase(Locale.US);
+            // We use GCM-SIV as the mode string, but - isn't a valid identifier character, so
+            // we need to ensure GCM-SIV is recognized and GCM_SIV isn't.
+            if (modeString.equals("GCM-SIV")) {
+                return GCM_SIV;
+            } else if (modeString.equals("GCM_SIV")) {
+                throw new IllegalArgumentException("Invalid mode");
+            } else {
+                return Mode.valueOf(modeString);
+            }
+        }
+    }
+
+    /**
+     * Paddings that a block cipher may support.
+     */
+    enum Padding {
+        NOPADDING,
+        PKCS5PADDING,
+        PKCS7PADDING,
+        ;
+
+        public static Padding getNormalized(String value) {
+            Padding p = Padding.valueOf(value.toUpperCase(Locale.US));
+            if (p == PKCS7PADDING) {
+                return PKCS5PADDING;
+            }
+            return p;
+        }
+    }
+
+    /**
+     * The current cipher mode.
+     */
+    Mode mode = Mode.ECB;
+
+    /**
+     * The current cipher padding.
+     */
+    private Padding padding = Padding.PKCS5PADDING;
+
+    /**
+     * May be used when reseting the cipher instance after calling
+     * {@code doFinal}.
+     */
+    byte[] encodedKey;
+
+    /**
+     * The Initial Vector (IV) used for the current cipher.
+     */
+    byte[] iv;
+
+    /**
+     * Current cipher mode: encrypting or decrypting.
+     */
+    private boolean encrypting;
+
+    /**
+     * The block size of the current cipher.
+     */
+    private int blockSize;
+
+    OpenSSLCipher() {
+    }
+
+    OpenSSLCipher(Mode mode, Padding padding) {
+        this.mode = mode;
+        this.padding = padding;
+        blockSize = getCipherBlockSize();
+    }
+
+    /**
+     * API-specific implementation of initializing the cipher. The
+     * {@link #isEncrypting()} function will tell whether it should be
+     * initialized for encryption or decryption. The {@code encodedKey} will be
+     * the bytes of a supported key size.
+     */
+    abstract void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params,
+            SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException;
+
+    /**
+     * API-specific implementation of updating the cipher. The
+     * {@code maximumLen} will be the maximum length of the output as returned
+     * by {@link #getOutputSizeForUpdate(int)}. The return value must be the
+     * number of bytes processed and placed into {@code output}. On error, an
+     * exception must be thrown.
+     */
+    abstract int updateInternal(byte[] input, int inputOffset, int inputLen,
+            byte[] output, int outputOffset, int maximumLen) throws ShortBufferException;
+
+    /**
+     * API-specific implementation of the final block. The {@code maximumLen}
+     * will be the maximum length of the possible output as returned by
+     * {@link #getOutputSizeForFinal(int)}. The return value must be the number
+     * of bytes processed and placed into {@code output}. On error, an exception
+     * must be thrown.
+     */
+    abstract int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
+            throws IllegalBlockSizeException, BadPaddingException, ShortBufferException;
+
+    /**
+     * Returns the standard name for the particular algorithm.
+     */
+    abstract String getBaseCipherName();
+
+    /**
+     * Checks whether the cipher supports this particular {@code keySize} (in
+     * bytes) and throws {@code InvalidKeyException} if it doesn't.
+     */
+    abstract void checkSupportedKeySize(int keySize) throws InvalidKeyException;
+
+    /**
+     * Checks whether the cipher supports this particular cipher {@code mode}
+     * and throws {@code NoSuchAlgorithmException} if it doesn't.
+     */
+    abstract void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException;
+
+    /**
+     * Checks whether the cipher supports this particular cipher {@code padding}
+     * and throws {@code NoSuchPaddingException} if it doesn't.
+     */
+    abstract void checkSupportedPadding(Padding padding) throws NoSuchPaddingException;
+
+    abstract int getCipherBlockSize();
+
+    boolean supportsVariableSizeKey() {
+        return false;
+    }
+
+    boolean supportsVariableSizeIv() {
+        return false;
+    }
+
+    @Override
+    protected void engineSetMode(String modeStr) throws NoSuchAlgorithmException {
+        final Mode mode;
+        try {
+            mode = Mode.getNormalized(modeStr);
+        } catch (IllegalArgumentException e) {
+            NoSuchAlgorithmException newE = new NoSuchAlgorithmException("No such mode: " + modeStr);
+            newE.initCause(e);
+            throw newE;
+        }
+        checkSupportedMode(mode);
+        this.mode = mode;
+    }
+
+    @Override
+    protected void engineSetPadding(String paddingStr) throws NoSuchPaddingException {
+        final Padding padding;
+        try {
+            padding = Padding.getNormalized(paddingStr);
+        } catch (IllegalArgumentException e) {
+            NoSuchPaddingException newE = new NoSuchPaddingException("No such padding: "
+                    + paddingStr);
+            newE.initCause(e);
+            throw newE;
+        }
+        checkSupportedPadding(padding);
+        this.padding = padding;
+    }
+
+    /**
+     * Returns the padding type for which this cipher is initialized.
+     */
+    Padding getPadding() {
+        return padding;
+    }
+
+    @Override
+    protected int engineGetBlockSize() {
+        return blockSize;
+    }
+
+    /**
+     * The size of output if {@code doFinal()} is called with this
+     * {@code inputLen}. If padding is enabled and the size of the input puts it
+     * right at the block size, it will add another block for the padding.
+     */
+    abstract int getOutputSizeForFinal(int inputLen);
+
+    /**
+     * The size of output if {@code update()} is called with this
+     * {@code inputLen}. If padding is enabled and the size of the input puts it
+     * right at the block size, it will add another block for the padding.
+     */
+    abstract int getOutputSizeForUpdate(int inputLen);
+
+    @Override
+    protected int engineGetOutputSize(int inputLen) {
+        return Math.max(getOutputSizeForUpdate(inputLen), getOutputSizeForFinal(inputLen));
+    }
+
+    @Override
+    protected byte[] engineGetIV() {
+        return iv;
+    }
+
+    @Override
+    protected AlgorithmParameters engineGetParameters() {
+        if (iv != null && iv.length > 0) {
+            try {
+                AlgorithmParameters params = AlgorithmParameters.getInstance(getBaseCipherName());
+                params.init(new IvParameterSpec(iv));
+                return params;
+            } catch (NoSuchAlgorithmException e) {
+                return null;
+            } catch (InvalidParameterSpecException e) {
+                return null;
+            }
+        }
+        return null;
+    }
+
+    protected AlgorithmParameterSpec getParameterSpec(AlgorithmParameters params)
+            throws InvalidAlgorithmParameterException {
+        if (params != null) {
+            try {
+                return params.getParameterSpec(IvParameterSpec.class);
+            } catch (InvalidParameterSpecException e) {
+                throw new InvalidAlgorithmParameterException(
+                        "Params must be convertible to IvParameterSpec", e);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
+        checkAndSetEncodedKey(opmode, key);
+        try {
+            engineInitInternal(this.encodedKey, null, random);
+        } catch (InvalidAlgorithmParameterException e) {
+            // This can't actually happen since we pass in null.
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
+            SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+        checkAndSetEncodedKey(opmode, key);
+        engineInitInternal(this.encodedKey, params, random);
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        AlgorithmParameterSpec spec = getParameterSpec(params);
+        engineInit(opmode, key, spec, random);
+    }
+
+    @Override
+    protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
+        final int maximumLen = getOutputSizeForUpdate(inputLen);
+
+        /* See how large our output buffer would need to be. */
+        final byte[] output;
+        if (maximumLen > 0) {
+            output = new byte[maximumLen];
+        } else {
+            output = EmptyArray.BYTE;
+        }
+
+        final int bytesWritten;
+        try {
+            bytesWritten = updateInternal(input, inputOffset, inputLen, output, 0, maximumLen);
+        } catch (ShortBufferException e) {
+            /* This shouldn't happen. */
+            throw new RuntimeException("calculated buffer size was wrong: " + maximumLen);
+        }
+
+        if (output.length == bytesWritten) {
+            return output;
+        } else if (bytesWritten == 0) {
+            return EmptyArray.BYTE;
+        } else {
+            return Arrays.copyOfRange(output, 0, bytesWritten);
+        }
+    }
+
+    @Override
+    protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
+            int outputOffset) throws ShortBufferException {
+        final int maximumLen = getOutputSizeForUpdate(inputLen);
+        return updateInternal(input, inputOffset, inputLen, output, outputOffset, maximumLen);
+    }
+
+    @Override
+    protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
+            throws IllegalBlockSizeException, BadPaddingException {
+        final int maximumLen = getOutputSizeForFinal(inputLen);
+        /* Assume that we'll output exactly on a byte boundary. */
+        final byte[] output = new byte[maximumLen];
+
+        int bytesWritten;
+        if (inputLen > 0) {
+            try {
+                bytesWritten = updateInternal(input, inputOffset, inputLen, output, 0, maximumLen);
+            } catch (ShortBufferException e) {
+                /* This should not happen since we sized our own buffer. */
+                throw new RuntimeException("our calculated buffer was too small", e);
+            }
+        } else {
+            bytesWritten = 0;
+        }
+
+        try {
+            bytesWritten += doFinalInternal(output, bytesWritten, maximumLen - bytesWritten);
+        } catch (ShortBufferException e) {
+            /* This should not happen since we sized our own buffer. */
+            throw new RuntimeException("our calculated buffer was too small", e);
+        }
+
+        if (bytesWritten == output.length) {
+            return output;
+        } else if (bytesWritten == 0) {
+            return EmptyArray.BYTE;
+        } else {
+            return Arrays.copyOfRange(output, 0, bytesWritten);
+        }
+    }
+
+    @Override
+    protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
+            int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
+            BadPaddingException {
+        if (output == null) {
+            throw new NullPointerException("output == null");
+        }
+
+        int maximumLen = getOutputSizeForFinal(inputLen);
+
+        final int bytesWritten;
+        if (inputLen > 0) {
+            bytesWritten = updateInternal(input, inputOffset, inputLen, output, outputOffset,
+                    maximumLen);
+            outputOffset += bytesWritten;
+            maximumLen -= bytesWritten;
+        } else {
+            bytesWritten = 0;
+        }
+
+        return bytesWritten + doFinalInternal(output, outputOffset, maximumLen);
+    }
+
+    @Override
+    protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException {
+        try {
+            byte[] encoded = key.getEncoded();
+            return engineDoFinal(encoded, 0, encoded.length);
+        } catch (BadPaddingException e) {
+            IllegalBlockSizeException newE = new IllegalBlockSizeException();
+            newE.initCause(e);
+            throw newE;
+        }
+    }
+
+    @Override
+    protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)
+            throws InvalidKeyException, NoSuchAlgorithmException {
+        try {
+            byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
+            if (wrappedKeyType == Cipher.PUBLIC_KEY) {
+                KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+                return keyFactory.generatePublic(new X509EncodedKeySpec(encoded));
+            } else if (wrappedKeyType == Cipher.PRIVATE_KEY) {
+                KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+                return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded));
+            } else if (wrappedKeyType == Cipher.SECRET_KEY) {
+                return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
+            } else {
+                throw new UnsupportedOperationException("wrappedKeyType == " + wrappedKeyType);
+            }
+        } catch (IllegalBlockSizeException e) {
+            throw new InvalidKeyException(e);
+        } catch (BadPaddingException e) {
+            throw new InvalidKeyException(e);
+        } catch (InvalidKeySpecException e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    @Override
+    protected int engineGetKeySize(Key key) throws InvalidKeyException {
+        if (!(key instanceof SecretKey)) {
+            throw new InvalidKeyException("Only SecretKey is supported");
+        }
+        byte[] encodedKey = key.getEncoded();
+        if (encodedKey == null) {
+            throw new InvalidKeyException("key.getEncoded() == null");
+        }
+        checkSupportedKeySize(encodedKey.length);
+        // The return value is in bits
+        return encodedKey.length * 8;
+    }
+
+    private byte[] checkAndSetEncodedKey(int opmode, Key key) throws InvalidKeyException {
+        if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) {
+            encrypting = true;
+        } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) {
+            encrypting = false;
+        } else {
+            throw new InvalidParameterException("Unsupported opmode " + opmode);
+        }
+
+        if (!(key instanceof SecretKey)) {
+            throw new InvalidKeyException("Only SecretKey is supported");
+        }
+
+        final byte[] encodedKey = key.getEncoded();
+        if (encodedKey == null) {
+            throw new InvalidKeyException("key.getEncoded() == null");
+        }
+        checkSupportedKeySize(encodedKey.length);
+        this.encodedKey = encodedKey;
+        return encodedKey;
+    }
+
+    boolean isEncrypting() {
+        return encrypting;
+    }
+
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLCipherChaCha20.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLCipherChaCha20.java
new file mode 100644
index 0000000..77dcbeb
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLCipherChaCha20.java
@@ -0,0 +1,164 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * Implementation of the ChaCha20 stream cipher.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public class OpenSSLCipherChaCha20 extends OpenSSLCipher {
+
+    private static final int BLOCK_SIZE_BYTES = 64;
+    private static final int NONCE_SIZE_BYTES = 12;
+
+    // BoringSSL's interface encrypts by the block, so we need to keep track of whether we
+    // had unused keystream bytes at the end of the previous encryption operation, so that
+    // we can use them before moving on to the next block.
+    private int currentBlockConsumedBytes = 0;
+    private int blockCounter = 0;
+
+    @libcore.api.IntraCoreApi
+    public OpenSSLCipherChaCha20() {}
+
+    @Override
+    void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        if (params instanceof IvParameterSpec) {
+            IvParameterSpec ivParams = (IvParameterSpec) params;
+            if (ivParams.getIV().length != NONCE_SIZE_BYTES) {
+                throw new InvalidAlgorithmParameterException("IV must be 12 bytes long");
+            }
+            iv = ivParams.getIV();
+        } else {
+            if (!isEncrypting()) {
+                throw new InvalidAlgorithmParameterException(
+                        "IV must be specified when decrypting");
+            }
+            iv = new byte[NONCE_SIZE_BYTES];
+            if (random != null) {
+                random.nextBytes(iv);
+            } else {
+                NativeCrypto.RAND_bytes(iv);
+            }
+        }
+    }
+
+    @Override
+    int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset,
+            int maximumLen) throws ShortBufferException {
+        if (inputLen > output.length - outputOffset) {
+            throw new ShortBufferException("Insufficient output space");
+        }
+        int inputLenRemaining = inputLen;
+        if (currentBlockConsumedBytes > 0) {
+            // A previous operation ended with a partial block, so we need to encrypt using
+            // the remainder of that block before beginning to use the next block
+            int len = Math.min(BLOCK_SIZE_BYTES - currentBlockConsumedBytes, inputLenRemaining);
+            byte[] singleBlock = new byte[BLOCK_SIZE_BYTES];
+            byte[] singleBlockOut = new byte[BLOCK_SIZE_BYTES];
+            System.arraycopy(input, inputOffset, singleBlock, currentBlockConsumedBytes, len);
+            NativeCrypto.chacha20_encrypt_decrypt(singleBlock, 0, singleBlockOut, 0,
+                    BLOCK_SIZE_BYTES, encodedKey, iv, blockCounter);
+            System.arraycopy(singleBlockOut, currentBlockConsumedBytes, output, outputOffset, len);
+            currentBlockConsumedBytes += len;
+            if (currentBlockConsumedBytes < BLOCK_SIZE_BYTES) {
+                // We still didn't finish this block, so we're done.
+                return len;
+            }
+            assert currentBlockConsumedBytes == BLOCK_SIZE_BYTES;
+            currentBlockConsumedBytes = 0;
+            inputOffset += len;
+            outputOffset += len;
+            inputLenRemaining -= len;
+            blockCounter++;
+        }
+        NativeCrypto.chacha20_encrypt_decrypt(input, inputOffset, output,
+                outputOffset, inputLenRemaining, encodedKey, iv, blockCounter);
+        currentBlockConsumedBytes = inputLenRemaining % BLOCK_SIZE_BYTES;
+        blockCounter += inputLenRemaining / BLOCK_SIZE_BYTES;
+        return inputLen;
+    }
+
+    @Override
+    int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
+            throws IllegalBlockSizeException, BadPaddingException, ShortBufferException {
+        reset();
+        return 0;
+    }
+
+    private void reset() {
+        blockCounter = 0;
+        currentBlockConsumedBytes = 0;
+    }
+
+    @Override
+    String getBaseCipherName() {
+        return "ChaCha20";
+    }
+
+    @Override
+    void checkSupportedKeySize(int keySize) throws InvalidKeyException {
+        if (keySize != 32) {
+            throw new InvalidKeyException("Unsupported key size: " + keySize
+                    + " bytes (must be 32)");
+        }
+    }
+
+    @Override
+    void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
+        if (mode != Mode.NONE) {
+            throw new NoSuchAlgorithmException("Mode must be NONE");
+        }
+    }
+
+    @Override
+    void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
+        if (padding != Padding.NOPADDING) {
+            throw new NoSuchPaddingException("Must be NoPadding");
+        }
+    }
+
+    @Override
+    int getCipherBlockSize() {
+        return 0;
+    }
+
+    @Override
+    int getOutputSizeForFinal(int inputLen) {
+        return inputLen;
+    }
+
+    @Override
+    int getOutputSizeForUpdate(int inputLen) {
+        return inputLen;
+    }
+
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLCipherRSA.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLCipherRSA.java
new file mode 100644
index 0000000..ed6ecb5
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLCipherRSA.java
@@ -0,0 +1,675 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.MGF1ParameterSpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
+import java.util.Locale;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.crypto.spec.PSource;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public abstract class OpenSSLCipherRSA extends CipherSpi {
+    /**
+     * The current OpenSSL key we're operating on.
+     */
+    OpenSSLKey key;
+
+    /**
+     * Current key type: private or public.
+     */
+    boolean usingPrivateKey;
+
+    /**
+     * Current cipher mode: encrypting or decrypting.
+     */
+    boolean encrypting;
+
+    /**
+     * Buffer for operations
+     */
+    private byte[] buffer;
+
+    /**
+     * Current offset in the buffer.
+     */
+    private int bufferOffset;
+
+    /**
+     * Flag that indicates an exception should be thrown when the input is too
+     * large during doFinal.
+     */
+    private boolean inputTooLarge;
+
+    /**
+     * Current padding mode
+     */
+    int padding = NativeConstants.RSA_PKCS1_PADDING;
+
+    OpenSSLCipherRSA(int padding) {
+        this.padding = padding;
+    }
+
+    @Override
+    protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
+        final String modeUpper = mode.toUpperCase(Locale.ROOT);
+        if ("NONE".equals(modeUpper) || "ECB".equals(modeUpper)) {
+            return;
+        }
+
+        throw new NoSuchAlgorithmException("mode not supported: " + mode);
+    }
+
+    @Override
+    protected void engineSetPadding(String padding) throws NoSuchPaddingException {
+        final String paddingUpper = padding.toUpperCase(Locale.ROOT);
+        if ("PKCS1PADDING".equals(paddingUpper)) {
+            this.padding = NativeConstants.RSA_PKCS1_PADDING;
+            return;
+        }
+        if ("NOPADDING".equals(paddingUpper)) {
+            this.padding = NativeConstants.RSA_NO_PADDING;
+            return;
+        }
+
+        throw new NoSuchPaddingException("padding not supported: " + padding);
+    }
+
+    @Override
+    protected int engineGetBlockSize() {
+        if (encrypting) {
+            return paddedBlockSizeBytes();
+        }
+        return keySizeBytes();
+    }
+
+    @Override
+    protected int engineGetOutputSize(int inputLen) {
+        if (encrypting) {
+            return keySizeBytes();
+        }
+        return paddedBlockSizeBytes();
+    }
+
+    int paddedBlockSizeBytes() {
+        int paddedBlockSizeBytes = keySizeBytes();
+        if (padding == NativeConstants.RSA_PKCS1_PADDING) {
+            paddedBlockSizeBytes--;  // for 0 prefix
+            paddedBlockSizeBytes -= 10;  // PKCS1 padding header length
+        }
+        return paddedBlockSizeBytes;
+    }
+
+    int keySizeBytes() {
+        if (!isInitialized()) {
+            throw new IllegalStateException("cipher is not initialized");
+        }
+        return NativeCrypto.RSA_size(this.key.getNativeRef());
+    }
+
+    /**
+     * Returns {@code true} if the cipher has been initialized.
+     */
+    boolean isInitialized() {
+        return key != null;
+    }
+
+    @Override
+    protected byte[] engineGetIV() {
+        return null;
+    }
+
+    @Override
+    protected AlgorithmParameters engineGetParameters() {
+        return null;
+    }
+
+    void doCryptoInit(AlgorithmParameterSpec spec)
+        throws InvalidAlgorithmParameterException, InvalidKeyException {}
+
+    void engineInitInternal(int opmode, Key key, AlgorithmParameterSpec spec)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) {
+            encrypting = true;
+        } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) {
+            encrypting = false;
+        } else {
+            throw new InvalidParameterException("Unsupported opmode " + opmode);
+        }
+
+        if (key instanceof OpenSSLRSAPrivateKey) {
+            OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) key;
+            usingPrivateKey = true;
+            this.key = rsaPrivateKey.getOpenSSLKey();
+        } else if (key instanceof RSAPrivateCrtKey) {
+            RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) key;
+            usingPrivateKey = true;
+            this.key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey);
+        } else if (key instanceof RSAPrivateKey) {
+            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) key;
+            usingPrivateKey = true;
+            this.key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey);
+        } else if (key instanceof OpenSSLRSAPublicKey) {
+            OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) key;
+            usingPrivateKey = false;
+            this.key = rsaPublicKey.getOpenSSLKey();
+        } else if (key instanceof RSAPublicKey) {
+            RSAPublicKey rsaPublicKey = (RSAPublicKey) key;
+            usingPrivateKey = false;
+            this.key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey);
+        } else {
+            if (null == key) {
+                throw new InvalidKeyException("RSA private or public key is null");
+            }
+
+            throw new InvalidKeyException("Need RSA private or public key");
+        }
+
+        buffer = new byte[NativeCrypto.RSA_size(this.key.getNativeRef())];
+        bufferOffset = 0;
+        inputTooLarge = false;
+
+        doCryptoInit(spec);
+    }
+
+    @Override
+    protected int engineGetKeySize(Key key) throws InvalidKeyException {
+        if (key instanceof OpenSSLRSAPrivateKey) {
+            return ((OpenSSLRSAPrivateKey) key).getModulus().bitLength();
+        }
+        if (key instanceof RSAPrivateCrtKey) {
+            return ((RSAPrivateCrtKey) key).getModulus().bitLength();
+        }
+        if (key instanceof RSAPrivateKey) {
+            return ((RSAPrivateKey) key).getModulus().bitLength();
+        }
+        if (key instanceof OpenSSLRSAPublicKey) {
+            return ((OpenSSLRSAPublicKey) key).getModulus().bitLength();
+        }
+        if (key instanceof RSAPublicKey) {
+            return ((RSAPublicKey) key).getModulus().bitLength();
+        }
+        if (null == key) {
+            throw new InvalidKeyException("RSA private or public key is null");
+        }
+        throw new InvalidKeyException("Need RSA private or public key");
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
+        try {
+            engineInitInternal(opmode, key, null);
+        } catch (InvalidAlgorithmParameterException e) {
+            throw new InvalidKeyException("Algorithm parameters rejected when none supplied", e);
+        }
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
+            SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+        if (params != null) {
+            throw new InvalidAlgorithmParameterException("unknown param type: "
+                    + params.getClass().getName());
+        }
+
+        engineInitInternal(opmode, key, params);
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        if (params != null) {
+            throw new InvalidAlgorithmParameterException("unknown param type: "
+                    + params.getClass().getName());
+        }
+
+        engineInitInternal(opmode, key, null);
+    }
+
+    @Override
+    protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
+        if (bufferOffset + inputLen > buffer.length) {
+            inputTooLarge = true;
+            return EmptyArray.BYTE;
+        }
+
+        System.arraycopy(input, inputOffset, buffer, bufferOffset, inputLen);
+        bufferOffset += inputLen;
+        return EmptyArray.BYTE;
+    }
+
+    @Override
+    protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
+            int outputOffset) throws ShortBufferException {
+        engineUpdate(input, inputOffset, inputLen);
+        return 0;
+    }
+
+    @Override
+    protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
+            throws IllegalBlockSizeException, BadPaddingException {
+        if (input != null) {
+            engineUpdate(input, inputOffset, inputLen);
+        }
+
+        if (inputTooLarge) {
+            throw new IllegalBlockSizeException("input must be under " + buffer.length + " bytes");
+        }
+
+        final byte[] tmpBuf;
+        if (bufferOffset != buffer.length) {
+            if (padding == NativeConstants.RSA_NO_PADDING) {
+                tmpBuf = new byte[buffer.length];
+                System.arraycopy(buffer, 0, tmpBuf, buffer.length - bufferOffset, bufferOffset);
+            } else {
+                tmpBuf = Arrays.copyOf(buffer, bufferOffset);
+            }
+        } else {
+            tmpBuf = buffer;
+        }
+
+        byte[] output = new byte[buffer.length];
+        int resultSize = doCryptoOperation(tmpBuf, output);
+        if (!encrypting && resultSize != output.length) {
+            output = Arrays.copyOf(output, resultSize);
+        }
+
+        bufferOffset = 0;
+        return output;
+    }
+
+    abstract int doCryptoOperation(final byte[] tmpBuf, byte[] output)
+            throws BadPaddingException, IllegalBlockSizeException;
+
+    @Override
+    protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
+            int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
+            BadPaddingException {
+        byte[] b = engineDoFinal(input, inputOffset, inputLen);
+
+        final int lastOffset = outputOffset + b.length;
+        if (lastOffset > output.length) {
+            throw new ShortBufferException("output buffer is too small " + output.length + " < "
+                    + lastOffset);
+        }
+
+        System.arraycopy(b, 0, output, outputOffset, b.length);
+        return b.length;
+    }
+
+    @Override
+    protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException {
+        try {
+            byte[] encoded = key.getEncoded();
+            return engineDoFinal(encoded, 0, encoded.length);
+        } catch (BadPaddingException e) {
+            IllegalBlockSizeException newE = new IllegalBlockSizeException();
+            newE.initCause(e);
+            throw newE;
+        }
+    }
+
+    @Override
+    protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
+            int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException {
+        try {
+            byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
+            if (wrappedKeyType == Cipher.PUBLIC_KEY) {
+                KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+                return keyFactory.generatePublic(new X509EncodedKeySpec(encoded));
+            } else if (wrappedKeyType == Cipher.PRIVATE_KEY) {
+                KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+                return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded));
+            } else if (wrappedKeyType == Cipher.SECRET_KEY) {
+                return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
+            } else {
+                throw new UnsupportedOperationException("wrappedKeyType == " + wrappedKeyType);
+            }
+        } catch (IllegalBlockSizeException e) {
+            throw new InvalidKeyException(e);
+        } catch (BadPaddingException e) {
+            throw new InvalidKeyException(e);
+        } catch (InvalidKeySpecException e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public abstract static class DirectRSA extends OpenSSLCipherRSA {
+        public DirectRSA(int padding) {
+            super(padding);
+        }
+
+        @Override
+        int doCryptoOperation(final byte[] tmpBuf, byte[] output)
+                throws BadPaddingException, IllegalBlockSizeException {
+            int resultSize;
+            if (encrypting) {
+                if (usingPrivateKey) {
+                    resultSize = NativeCrypto.RSA_private_encrypt(
+                            tmpBuf.length, tmpBuf, output, key.getNativeRef(), padding);
+                } else {
+                    resultSize = NativeCrypto.RSA_public_encrypt(
+                            tmpBuf.length, tmpBuf, output, key.getNativeRef(), padding);
+                }
+            } else {
+                try {
+                    if (usingPrivateKey) {
+                        resultSize = NativeCrypto.RSA_private_decrypt(
+                                tmpBuf.length, tmpBuf, output, key.getNativeRef(), padding);
+                    } else {
+                        resultSize = NativeCrypto.RSA_public_decrypt(
+                                tmpBuf.length, tmpBuf, output, key.getNativeRef(), padding);
+                    }
+                } catch (SignatureException e) {
+                    IllegalBlockSizeException newE = new IllegalBlockSizeException();
+                    newE.initCause(e);
+                    throw newE;
+                }
+            }
+            return resultSize;
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class PKCS1 extends DirectRSA {
+        @libcore.api.IntraCoreApi
+        public PKCS1() {
+            super(NativeConstants.RSA_PKCS1_PADDING);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class Raw extends DirectRSA {
+        @libcore.api.IntraCoreApi
+        public Raw() {
+            super(NativeConstants.RSA_NO_PADDING);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static class OAEP extends OpenSSLCipherRSA {
+        private long oaepMd;
+        private int oaepMdSizeBytes;
+
+        private long mgf1Md;
+
+        private byte[] label;
+
+        private NativeRef.EVP_PKEY_CTX pkeyCtx;
+
+        public OAEP(long defaultMd, int defaultMdSizeBytes) {
+            super(NativeConstants.RSA_PKCS1_OAEP_PADDING);
+            oaepMd = mgf1Md = defaultMd;
+            oaepMdSizeBytes = defaultMdSizeBytes;
+        }
+
+        @Override
+        protected AlgorithmParameters engineGetParameters() {
+            if (!isInitialized()) {
+                return null;
+            }
+
+            try {
+                AlgorithmParameters params = AlgorithmParameters.getInstance("OAEP");
+
+                final PSource pSrc;
+                if (label == null) {
+                    pSrc = PSource.PSpecified.DEFAULT;
+                } else {
+                    pSrc = new PSource.PSpecified(label);
+                }
+
+                params.init(new OAEPParameterSpec(
+                        EvpMdRef.getJcaDigestAlgorithmStandardNameFromEVP_MD(oaepMd),
+                        EvpMdRef.MGF1_ALGORITHM_NAME,
+                        new MGF1ParameterSpec(
+                                EvpMdRef.getJcaDigestAlgorithmStandardNameFromEVP_MD(mgf1Md)),
+                        pSrc));
+                return params;
+            } catch (NoSuchAlgorithmException e) {
+                // We should not get here.
+                throw (Error) new AssertionError("OAEP not supported").initCause(e);
+            } catch (InvalidParameterSpecException e) {
+                throw new RuntimeException("No providers of AlgorithmParameters.OAEP available");
+            }
+        }
+
+        @Override
+        protected void engineSetPadding(String padding) throws NoSuchPaddingException {
+            String paddingUpper = padding.toUpperCase(Locale.US);
+            if (paddingUpper.equals("OAEPPADDING")) {
+                this.padding = NativeConstants.RSA_PKCS1_OAEP_PADDING;
+                return;
+            }
+
+            throw new NoSuchPaddingException("Only OAEP padding is supported");
+        }
+
+        @Override
+        protected void engineInit(
+                int opmode, Key key, AlgorithmParameterSpec spec, SecureRandom random)
+                throws InvalidKeyException, InvalidAlgorithmParameterException {
+            if (spec != null && !(spec instanceof OAEPParameterSpec)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Only OAEPParameterSpec accepted in OAEP mode");
+            }
+
+            engineInitInternal(opmode, key, spec);
+        }
+
+        @Override
+        protected void engineInit(
+                int opmode, Key key, AlgorithmParameters params, SecureRandom random)
+                throws InvalidKeyException, InvalidAlgorithmParameterException {
+            OAEPParameterSpec spec = null;
+            if (params != null) {
+                try {
+                    spec = params.getParameterSpec(OAEPParameterSpec.class);
+                } catch (InvalidParameterSpecException e) {
+                    throw new InvalidAlgorithmParameterException(
+                            "Only OAEP parameters are supported", e);
+                }
+            }
+
+            engineInitInternal(opmode, key, spec);
+        }
+
+        @Override
+        void engineInitInternal(int opmode, Key key, AlgorithmParameterSpec spec)
+                throws InvalidKeyException, InvalidAlgorithmParameterException {
+            if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) {
+                if (!(key instanceof PublicKey)) {
+                    throw new InvalidKeyException("Only public keys may be used to encrypt");
+                }
+            } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) {
+                if (!(key instanceof PrivateKey)) {
+                    throw new InvalidKeyException("Only private keys may be used to decrypt");
+                }
+            }
+            super.engineInitInternal(opmode, key, spec);
+        }
+
+        @Override
+        void doCryptoInit(AlgorithmParameterSpec spec)
+            throws InvalidAlgorithmParameterException, InvalidKeyException {
+            pkeyCtx = new NativeRef.EVP_PKEY_CTX(encrypting
+                            ? NativeCrypto.EVP_PKEY_encrypt_init(key.getNativeRef())
+                            : NativeCrypto.EVP_PKEY_decrypt_init(key.getNativeRef()));
+
+            if (spec instanceof OAEPParameterSpec) {
+                readOAEPParameters((OAEPParameterSpec) spec);
+            }
+
+            NativeCrypto.EVP_PKEY_CTX_set_rsa_padding(
+                    pkeyCtx.address, NativeConstants.RSA_PKCS1_OAEP_PADDING);
+            NativeCrypto.EVP_PKEY_CTX_set_rsa_oaep_md(pkeyCtx.address, oaepMd);
+            NativeCrypto.EVP_PKEY_CTX_set_rsa_mgf1_md(pkeyCtx.address, mgf1Md);
+            if (label != null && label.length > 0) {
+                NativeCrypto.EVP_PKEY_CTX_set_rsa_oaep_label(pkeyCtx.address, label);
+            }
+        }
+
+        @Override
+        int paddedBlockSizeBytes() {
+            int paddedBlockSizeBytes = keySizeBytes();
+            // Size described in step 2 of decoding algorithm, but extra byte
+            // needed to make sure it's smaller than the RSA key modulus size.
+            // https://tools.ietf.org/html/rfc2437#section-9.1.1.2
+            return paddedBlockSizeBytes - (2 * oaepMdSizeBytes + 2);
+        }
+
+        private void readOAEPParameters(OAEPParameterSpec spec)
+                throws InvalidAlgorithmParameterException {
+            String mgfAlgUpper = spec.getMGFAlgorithm().toUpperCase(Locale.US);
+            AlgorithmParameterSpec mgfSpec = spec.getMGFParameters();
+            if ((!EvpMdRef.MGF1_ALGORITHM_NAME.equals(mgfAlgUpper)
+                        && !EvpMdRef.MGF1_OID.equals(mgfAlgUpper))
+                    || !(mgfSpec instanceof MGF1ParameterSpec)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Only MGF1 supported as mask generation function");
+            }
+
+            MGF1ParameterSpec mgf1spec = (MGF1ParameterSpec) mgfSpec;
+            String oaepAlgUpper = spec.getDigestAlgorithm().toUpperCase(Locale.US);
+            try {
+                oaepMd = EvpMdRef.getEVP_MDByJcaDigestAlgorithmStandardName(oaepAlgUpper);
+                oaepMdSizeBytes =
+                        EvpMdRef.getDigestSizeBytesByJcaDigestAlgorithmStandardName(oaepAlgUpper);
+                mgf1Md = EvpMdRef.getEVP_MDByJcaDigestAlgorithmStandardName(
+                        mgf1spec.getDigestAlgorithm());
+            } catch (NoSuchAlgorithmException e) {
+                throw new InvalidAlgorithmParameterException(e);
+            }
+
+            PSource pSource = spec.getPSource();
+            if (!"PSpecified".equals(pSource.getAlgorithm())
+                    || !(pSource instanceof PSource.PSpecified)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Only PSpecified accepted for PSource");
+            }
+            label = ((PSource.PSpecified) pSource).getValue();
+        }
+
+        @Override
+        int doCryptoOperation(byte[] tmpBuf, byte[] output)
+                throws BadPaddingException, IllegalBlockSizeException {
+            if (encrypting) {
+                return NativeCrypto.EVP_PKEY_encrypt(pkeyCtx, output, 0, tmpBuf, 0, tmpBuf.length);
+            } else {
+                return NativeCrypto.EVP_PKEY_decrypt(pkeyCtx, output, 0, tmpBuf, 0, tmpBuf.length);
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static final class SHA1 extends OAEP {
+            @libcore.api.IntraCoreApi
+            public SHA1() {
+                super(EvpMdRef.SHA1.EVP_MD, EvpMdRef.SHA1.SIZE_BYTES);
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static final class SHA224 extends OAEP {
+            @libcore.api.IntraCoreApi
+            public SHA224() {
+                super(EvpMdRef.SHA224.EVP_MD, EvpMdRef.SHA224.SIZE_BYTES);
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static final class SHA256 extends OAEP {
+            @libcore.api.IntraCoreApi
+            public SHA256() {
+                super(EvpMdRef.SHA256.EVP_MD, EvpMdRef.SHA256.SIZE_BYTES);
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static final class SHA384 extends OAEP {
+            @libcore.api.IntraCoreApi
+            public SHA384() {
+                super(EvpMdRef.SHA384.EVP_MD, EvpMdRef.SHA384.SIZE_BYTES);
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static final class SHA512 extends OAEP {
+            @libcore.api.IntraCoreApi
+            public SHA512() {
+                super(EvpMdRef.SHA512.EVP_MD, EvpMdRef.SHA512.SIZE_BYTES);
+            }
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLContextImpl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLContextImpl.java
new file mode 100644
index 0000000..7f5cabc
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLContextImpl.java
@@ -0,0 +1,209 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.Platform.wrapEngine;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyManagementException;
+import java.security.SecureRandom;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContextSpi;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+
+/**
+ * OpenSSL-backed SSLContext service provider interface.
+ *
+ * <p>Public to allow contruction via the provider framework.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public abstract class OpenSSLContextImpl extends SSLContextSpi {
+    /**
+     * The default SSLContextImpl for use with
+     * SSLContext.getInstance("Default"). Protected by the
+     * DefaultSSLContextImpl.class monitor.
+     */
+    private static DefaultSSLContextImpl defaultSslContextImpl;
+
+    /** TLS algorithm to initialize all sockets. */
+    private final String[] algorithms;
+
+    /** Client session cache. */
+    private final ClientSessionContext clientSessionContext;
+
+    /** Server session cache. */
+    private final ServerSessionContext serverSessionContext;
+
+    SSLParametersImpl sslParameters;
+
+    /** Allows outside callers to get the preferred SSLContext. */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static OpenSSLContextImpl getPreferred() {
+        return new TLSv13();
+    }
+
+    OpenSSLContextImpl(String[] algorithms) {
+        this.algorithms = algorithms;
+        clientSessionContext = new ClientSessionContext();
+        serverSessionContext = new ServerSessionContext();
+    }
+
+    /**
+     * Constuctor for the DefaultSSLContextImpl.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    OpenSSLContextImpl() throws GeneralSecurityException, IOException {
+        synchronized (DefaultSSLContextImpl.class) {
+            this.algorithms = null;
+            if (defaultSslContextImpl == null) {
+                clientSessionContext = new ClientSessionContext();
+                serverSessionContext = new ServerSessionContext();
+                defaultSslContextImpl = (DefaultSSLContextImpl) this;
+            } else {
+                clientSessionContext =
+                        (ClientSessionContext)
+                                defaultSslContextImpl.engineGetClientSessionContext();
+                serverSessionContext =
+                        (ServerSessionContext)
+                                defaultSslContextImpl.engineGetServerSessionContext();
+            }
+            sslParameters = new SSLParametersImpl(defaultSslContextImpl.getKeyManagers(),
+                    defaultSslContextImpl.getTrustManagers(), null, clientSessionContext,
+                    serverSessionContext, algorithms);
+        }
+    }
+
+    /**
+     * Initializes this {@code SSLContext} instance. All of the arguments are
+     * optional, and the security providers will be searched for the required
+     * implementations of the needed algorithms.
+     *
+     * @param kms the key sources or {@code null}
+     * @param tms the trust decision sources or {@code null}
+     * @param sr the randomness source or {@code null}
+     * @throws KeyManagementException if initializing this instance fails
+     */
+    @Override
+    public void engineInit(KeyManager[] kms, TrustManager[] tms, SecureRandom sr)
+            throws KeyManagementException {
+        sslParameters = new SSLParametersImpl(
+                kms, tms, sr, clientSessionContext, serverSessionContext, algorithms);
+    }
+
+    @Override
+    public SSLSocketFactory engineGetSocketFactory() {
+        if (sslParameters == null) {
+            throw new IllegalStateException("SSLContext is not initialized.");
+        }
+        return Platform.wrapSocketFactoryIfNeeded(new OpenSSLSocketFactoryImpl(sslParameters));
+    }
+
+    @Override
+    public SSLServerSocketFactory engineGetServerSocketFactory() {
+        if (sslParameters == null) {
+            throw new IllegalStateException("SSLContext is not initialized.");
+        }
+        return new OpenSSLServerSocketFactoryImpl(sslParameters);
+    }
+
+    @Override
+    public SSLEngine engineCreateSSLEngine(String host, int port) {
+        if (sslParameters == null) {
+            throw new IllegalStateException("SSLContext is not initialized.");
+        }
+        SSLParametersImpl p = (SSLParametersImpl) sslParameters.clone();
+        p.setUseClientMode(false);
+        return wrapEngine(new ConscryptEngine(host, port, p));
+    }
+
+    @Override
+    public SSLEngine engineCreateSSLEngine() {
+        if (sslParameters == null) {
+            throw new IllegalStateException("SSLContext is not initialized.");
+        }
+        SSLParametersImpl p = (SSLParametersImpl) sslParameters.clone();
+        p.setUseClientMode(false);
+        return wrapEngine(new ConscryptEngine(p));
+    }
+
+    @Override
+    public SSLSessionContext engineGetServerSessionContext() {
+        return serverSessionContext;
+    }
+
+    @Override
+    public SSLSessionContext engineGetClientSessionContext() {
+        return clientSessionContext;
+    }
+
+    /**
+     * Public to allow construction via the provider framework.
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class TLSv13 extends OpenSSLContextImpl {
+        @libcore.api.IntraCoreApi
+        public TLSv13() {
+            super(NativeCrypto.TLSV13_PROTOCOLS);
+        }
+    }
+
+    /**
+     * Public to allow construction via the provider framework.
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class TLSv12 extends OpenSSLContextImpl {
+        @dalvik.annotation.compat.UnsupportedAppUsage
+        @libcore.api.IntraCoreApi
+        public TLSv12() {
+            super(NativeCrypto.TLSV12_PROTOCOLS);
+        }
+    }
+
+    /**
+     * Public to allow construction via the provider framework.
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class TLSv11 extends OpenSSLContextImpl {
+        @libcore.api.IntraCoreApi
+        public TLSv11() {
+            super(NativeCrypto.TLSV11_PROTOCOLS);
+        }
+    }
+
+    /**
+     * Public to allow construction via the provider framework.
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class TLSv1 extends OpenSSLContextImpl {
+        @libcore.api.IntraCoreApi
+        public TLSv1() {
+            super(NativeCrypto.TLSV1_PROTOCOLS);
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECDHKeyAgreement.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECDHKeyAgreement.java
new file mode 100644
index 0000000..7b11d8c
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECDHKeyAgreement.java
@@ -0,0 +1,155 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.KeyAgreementSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Elliptic Curve Diffie-Hellman key agreement backed by the OpenSSL engine.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public final class OpenSSLECDHKeyAgreement extends KeyAgreementSpi {
+
+    /** OpenSSL handle of the private key. Only available after the engine has been initialized. */
+    private OpenSSLKey mOpenSslPrivateKey;
+
+    /**
+     * Expected length (in bytes) of the agreed key ({@link #mResult}). Only available after the
+     * engine has been initialized.
+     */
+    private int mExpectedResultLength;
+
+    /** Agreed key. Only available after {@link #engineDoPhase(Key, boolean)} completes. */
+    private byte[] mResult;
+
+    @libcore.api.IntraCoreApi
+    public OpenSSLECDHKeyAgreement() {}
+
+    @Override
+    public Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException {
+        if (mOpenSslPrivateKey == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+        if (!lastPhase) {
+            throw new IllegalStateException("ECDH only has one phase");
+        }
+
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        }
+        if (!(key instanceof PublicKey)) {
+            throw new InvalidKeyException("Not a public key: " + key.getClass());
+        }
+        OpenSSLKey openSslPublicKey = OpenSSLKey.fromPublicKey((PublicKey) key);
+
+        byte[] buffer = new byte[mExpectedResultLength];
+        int actualResultLength = NativeCrypto.ECDH_compute_key(
+                buffer,
+                0,
+                openSslPublicKey.getNativeRef(),
+                mOpenSslPrivateKey.getNativeRef());
+        byte[] result;
+        if (actualResultLength == -1) {
+            throw new RuntimeException("Engine returned " + actualResultLength);
+        } else if (actualResultLength == mExpectedResultLength) {
+            // The output is as long as expected -- use the whole buffer
+            result = buffer;
+        } else if (actualResultLength < mExpectedResultLength) {
+            // The output is shorter than expected -- use only what's produced by the engine
+            result = new byte[actualResultLength];
+            System.arraycopy(buffer, 0, mResult, 0, mResult.length);
+        } else {
+            // The output is longer than expected
+            throw new RuntimeException("Engine produced a longer than expected result. Expected: "
+                + mExpectedResultLength + ", actual: " + actualResultLength);
+        }
+        mResult = result;
+
+        return null; // No intermediate key
+    }
+
+    @Override
+    protected int engineGenerateSecret(byte[] sharedSecret, int offset)
+            throws ShortBufferException {
+        checkCompleted();
+        int available = sharedSecret.length - offset;
+        if (mResult.length > available) {
+            throw new ShortBufferException(
+                    "Needed: " + mResult.length + ", available: " + available);
+        }
+
+        System.arraycopy(mResult, 0, sharedSecret, offset, mResult.length);
+        return mResult.length;
+    }
+
+    @Override
+    protected byte[] engineGenerateSecret() {
+        checkCompleted();
+        return mResult;
+    }
+
+    @Override
+    protected SecretKey engineGenerateSecret(String algorithm) {
+        checkCompleted();
+        return new SecretKeySpec(engineGenerateSecret(), algorithm);
+    }
+
+    @Override
+    protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException {
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        }
+        if (!(key instanceof PrivateKey)) {
+            throw new InvalidKeyException("Not a private key: " + key.getClass());
+        }
+
+        OpenSSLKey openSslKey = OpenSSLKey.fromPrivateKey((PrivateKey) key);
+        int fieldSizeBits = NativeCrypto.EC_GROUP_get_degree(new NativeRef.EC_GROUP(
+                NativeCrypto.EC_KEY_get1_group(openSslKey.getNativeRef())));
+        mExpectedResultLength = (fieldSizeBits + 7) / 8;
+        mOpenSslPrivateKey = openSslKey;
+    }
+
+    @Override
+    protected void engineInit(Key key, AlgorithmParameterSpec params,
+            SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+        // ECDH doesn't need an AlgorithmParameterSpec
+        if (params != null) {
+          throw new InvalidAlgorithmParameterException("No algorithm parameters supported");
+        }
+        engineInit(key, random);
+    }
+
+    private void checkCompleted() {
+        if (mResult == null) {
+            throw new IllegalStateException("Key agreement not completed");
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECGroupContext.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECGroupContext.java
new file mode 100644
index 0000000..6774258
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECGroupContext.java
@@ -0,0 +1,181 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.spec.ECField;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.EllipticCurve;
+
+/**
+ * Represents a BoringSSL EC_GROUP object.
+ */
+final class OpenSSLECGroupContext {
+    private final NativeRef.EC_GROUP groupCtx;
+
+    OpenSSLECGroupContext(NativeRef.EC_GROUP groupCtx) {
+        this.groupCtx = groupCtx;
+    }
+
+    static OpenSSLECGroupContext getCurveByName(String curveName) {
+        // Workaround for OpenSSL not supporting SECG names for NIST P-256 (aka
+        // ANSI X9.62 prime256v1).
+        if ("secp256r1".equals(curveName)) {
+            curveName = "prime256v1";
+        }
+
+        final long ctx = NativeCrypto.EC_GROUP_new_by_curve_name(curveName);
+        if (ctx == 0) {
+            return null;
+        }
+        NativeRef.EC_GROUP groupRef = new NativeRef.EC_GROUP(ctx);
+
+        return new OpenSSLECGroupContext(groupRef);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        throw new IllegalArgumentException("OpenSSLECGroupContext.equals is not defined");
+    }
+
+    @Override
+    public int hashCode() {
+        // TODO Auto-generated method stub
+        return super.hashCode();
+    }
+
+    NativeRef.EC_GROUP getNativeRef() {
+        return groupCtx;
+    }
+
+    static OpenSSLECGroupContext getInstance(ECParameterSpec params)
+            throws InvalidAlgorithmParameterException {
+        String curveName = Platform.getCurveName(params);
+        if (curveName != null) {
+            return OpenSSLECGroupContext.getCurveByName(curveName);
+        }
+
+        // Try to find recognise the underlying curve from the parameters.
+        final EllipticCurve curve = params.getCurve();
+        final ECField field = curve.getField();
+
+        final BigInteger p;
+        if (field instanceof ECFieldFp) {
+            p = ((ECFieldFp) field).getP();
+        } else {
+            throw new InvalidParameterException("unhandled field class "
+                    + field.getClass().getName());
+        }
+
+        final ECPoint generator = params.getGenerator();
+        final BigInteger b = curve.getB();
+        final BigInteger x = generator.getAffineX();
+        final BigInteger y = generator.getAffineY();
+
+        // The 'a' value isn't checked in the following because it's unclear
+        // whether users would set it to -3 or p-3.
+        switch (p.bitLength()) {
+            case 224:
+                if (p.toString(16).equals("ffffffffffffffffffffffffffffffff000000000000000000000001") &&
+                    b.toString(16).equals("b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4") &&
+                    x.toString(16).equals("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21") &&
+                    y.toString(16).equals("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34")) {
+                    curveName = "secp224r1";
+                }
+                break;
+            case 256:
+                if (p.toString(16).equals("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff") &&
+                    b.toString(16).equals("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b") &&
+                    x.toString(16).equals("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296") &&
+                    y.toString(16).equals("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5")) {
+                    curveName = "prime256v1";
+                }
+                break;
+            case 384:
+                if (p.toString(16).equals("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff") &&
+                    b.toString(16).equals("b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef") &&
+                    x.toString(16).equals("aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7") &&
+                    y.toString(16).equals("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f")) {
+                    curveName = "secp384r1";
+                }
+                break;
+            case 521:
+                if (p.toString(16).equals("1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") &&
+                    b.toString(16).equals("51953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00") &&
+                    x.toString(16).equals("c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66") &&
+                    y.toString(16).equals("11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650")) {
+                    curveName = "secp521r1";
+                }
+                break;
+        }
+
+        if (curveName != null) {
+            return OpenSSLECGroupContext.getCurveByName(curveName);
+        }
+
+        final BigInteger a = curve.getA();
+        final BigInteger order = params.getOrder();
+        final int cofactor = params.getCofactor();
+
+        long group;
+        try {
+            group = NativeCrypto.EC_GROUP_new_arbitrary(
+                        p.toByteArray(), a.toByteArray(), b.toByteArray(), x.toByteArray(),
+                        y.toByteArray(), order.toByteArray(), cofactor);
+        } catch (Throwable exception) {
+            throw new InvalidAlgorithmParameterException("EC_GROUP_new_arbitrary failed",
+                                                         exception);
+        }
+
+        if (group == 0) {
+            throw new InvalidAlgorithmParameterException("EC_GROUP_new_arbitrary returned NULL");
+        }
+
+        NativeRef.EC_GROUP groupRef = new NativeRef.EC_GROUP(group);
+
+        return new OpenSSLECGroupContext(groupRef);
+    }
+
+    ECParameterSpec getECParameterSpec() {
+        final String curveName = NativeCrypto.EC_GROUP_get_curve_name(groupCtx);
+
+        final byte[][] curveParams = NativeCrypto.EC_GROUP_get_curve(groupCtx);
+        final BigInteger p = new BigInteger(curveParams[0]);
+        final BigInteger a = new BigInteger(curveParams[1]);
+        final BigInteger b = new BigInteger(curveParams[2]);
+
+        final ECField field = new ECFieldFp(p);
+
+        final EllipticCurve curve = new EllipticCurve(field, a, b);
+
+        final OpenSSLECPointContext generatorCtx = new OpenSSLECPointContext(this,
+                new NativeRef.EC_POINT(NativeCrypto.EC_GROUP_get_generator(groupCtx)));
+        final ECPoint generator = generatorCtx.getECPoint();
+
+        final BigInteger order = new BigInteger(NativeCrypto.EC_GROUP_get_order(groupCtx));
+        final BigInteger cofactor = new BigInteger(NativeCrypto.EC_GROUP_get_cofactor(groupCtx));
+
+        ECParameterSpec spec = new ECParameterSpec(curve, generator, order, cofactor.intValue());
+        Platform.setCurveName(spec, curveName);
+        return spec;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECKeyFactory.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECKeyFactory.java
new file mode 100644
index 0000000..4891b50
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECKeyFactory.java
@@ -0,0 +1,207 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactorySpi;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+/**
+ * An implementation of a {@link KeyFactorySpi} for EC keys based on BoringSSL.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public final class OpenSSLECKeyFactory extends KeyFactorySpi {
+
+    @libcore.api.IntraCoreApi
+    public OpenSSLECKeyFactory() {}
+
+    @Override
+    protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException {
+        if (keySpec == null) {
+            throw new InvalidKeySpecException("keySpec == null");
+        }
+
+        if (keySpec instanceof ECPublicKeySpec) {
+            return new OpenSSLECPublicKey((ECPublicKeySpec) keySpec);
+        } else if (keySpec instanceof X509EncodedKeySpec) {
+            return OpenSSLKey.getPublicKey((X509EncodedKeySpec) keySpec, NativeConstants.EVP_PKEY_EC);
+        }
+        throw new InvalidKeySpecException("Must use ECPublicKeySpec or X509EncodedKeySpec; was "
+                + keySpec.getClass().getName());
+    }
+
+    @Override
+    protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException {
+        if (keySpec == null) {
+            throw new InvalidKeySpecException("keySpec == null");
+        }
+
+        if (keySpec instanceof ECPrivateKeySpec) {
+            return new OpenSSLECPrivateKey((ECPrivateKeySpec) keySpec);
+        } else if (keySpec instanceof PKCS8EncodedKeySpec) {
+            return OpenSSLKey.getPrivateKey((PKCS8EncodedKeySpec) keySpec,
+                    NativeConstants.EVP_PKEY_EC);
+        }
+        throw new InvalidKeySpecException("Must use ECPrivateKeySpec or PKCS8EncodedKeySpec; was "
+                + keySpec.getClass().getName());
+    }
+
+    @Override
+    protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
+            throws InvalidKeySpecException {
+        if (key == null) {
+            throw new InvalidKeySpecException("key == null");
+        }
+
+        if (keySpec == null) {
+            throw new InvalidKeySpecException("keySpec == null");
+        }
+
+        if (!"EC".equals(key.getAlgorithm())) {
+            throw new InvalidKeySpecException("Key must be an EC key");
+        }
+
+        if (key instanceof ECPublicKey && ECPublicKeySpec.class.isAssignableFrom(keySpec)) {
+            ECPublicKey ecKey = (ECPublicKey) key;
+            @SuppressWarnings("unchecked")
+            T result = (T) new ECPublicKeySpec(ecKey.getW(), ecKey.getParams());
+            return result;
+        } else if (key instanceof PublicKey && ECPublicKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"X.509".equals(key.getFormat()) || encoded == null) {
+                throw new InvalidKeySpecException("Not a valid X.509 encoding");
+            }
+            ECPublicKey ecKey = (ECPublicKey) engineGeneratePublic(new X509EncodedKeySpec(encoded));
+            @SuppressWarnings("unchecked")
+            T result = (T) new ECPublicKeySpec(ecKey.getW(), ecKey.getParams());
+            return result;
+        } else if (key instanceof ECPrivateKey
+                && ECPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+            ECPrivateKey ecKey = (ECPrivateKey) key;
+            @SuppressWarnings("unchecked")
+            T result = (T) new ECPrivateKeySpec(ecKey.getS(), ecKey.getParams());
+            return result;
+        } else if (key instanceof PrivateKey && ECPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"PKCS#8".equals(key.getFormat()) || encoded == null) {
+                throw new InvalidKeySpecException("Not a valid PKCS#8 encoding");
+            }
+            ECPrivateKey ecKey =
+                    (ECPrivateKey) engineGeneratePrivate(new PKCS8EncodedKeySpec(encoded));
+            @SuppressWarnings("unchecked")
+            T result = (T) new ECPrivateKeySpec(ecKey.getS(), ecKey.getParams());
+            return result;
+        } else if (key instanceof PrivateKey
+                && PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"PKCS#8".equals(key.getFormat())) {
+                throw new InvalidKeySpecException("Encoding type must be PKCS#8; was "
+                        + key.getFormat());
+            } else if (encoded == null) {
+                throw new InvalidKeySpecException("Key is not encodable");
+            }
+            @SuppressWarnings("unchecked") T result = (T) new PKCS8EncodedKeySpec(encoded);
+            return result;
+        } else if (key instanceof PublicKey && X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"X.509".equals(key.getFormat())) {
+                throw new InvalidKeySpecException("Encoding type must be X.509; was "
+                        + key.getFormat());
+            } else if (encoded == null) {
+                throw new InvalidKeySpecException("Key is not encodable");
+            }
+            @SuppressWarnings("unchecked") T result = (T) new X509EncodedKeySpec(encoded);
+            return result;
+        } else {
+            throw new InvalidKeySpecException("Unsupported key type and key spec combination; key="
+                    + key.getClass().getName() + ", keySpec=" + keySpec.getName());
+        }
+    }
+
+    @Override
+    protected Key engineTranslateKey(Key key) throws InvalidKeyException {
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        }
+        if ((key instanceof OpenSSLECPublicKey) || (key instanceof OpenSSLECPrivateKey)) {
+            return key;
+        } else if (key instanceof ECPublicKey) {
+            ECPublicKey ecKey = (ECPublicKey) key;
+
+            ECPoint w = ecKey.getW();
+
+            ECParameterSpec params = ecKey.getParams();
+
+            try {
+                return engineGeneratePublic(new ECPublicKeySpec(w, params));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else if (key instanceof ECPrivateKey) {
+            ECPrivateKey ecKey = (ECPrivateKey) key;
+
+            BigInteger s = ecKey.getS();
+
+            ECParameterSpec params = ecKey.getParams();
+
+            try {
+                return engineGeneratePrivate(new ECPrivateKeySpec(s, params));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else if ((key instanceof PrivateKey) && "PKCS#8".equals(key.getFormat())) {
+            byte[] encoded = key.getEncoded();
+            if (encoded == null) {
+                throw new InvalidKeyException("Key does not support encoding");
+            }
+            try {
+                return engineGeneratePrivate(new PKCS8EncodedKeySpec(encoded));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else if ((key instanceof PublicKey) && "X.509".equals(key.getFormat())) {
+            byte[] encoded = key.getEncoded();
+            if (encoded == null) {
+                throw new InvalidKeyException("Key does not support encoding");
+            }
+            try {
+                return engineGeneratePublic(new X509EncodedKeySpec(encoded));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else {
+            throw new InvalidKeyException("Key must be EC public or private key; was "
+                    + key.getClass().getName());
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECKeyPairGenerator.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECKeyPairGenerator.java
new file mode 100644
index 0000000..59e2ec0
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECKeyPairGenerator.java
@@ -0,0 +1,138 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An implementation of {@link KeyPairGenerator} for EC keys which uses BoringSSL to perform all the
+ * operations.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public final class OpenSSLECKeyPairGenerator extends KeyPairGenerator {
+    private static final String ALGORITHM = "EC";
+
+    private static final int DEFAULT_KEY_SIZE = 256;
+
+    private static final Map<Integer, String> SIZE_TO_CURVE_NAME = new HashMap<Integer, String>();
+
+    static {
+        /* NIST curves */
+        SIZE_TO_CURVE_NAME.put(224, "secp224r1");
+        SIZE_TO_CURVE_NAME.put(256, "prime256v1");
+        SIZE_TO_CURVE_NAME.put(384, "secp384r1");
+        SIZE_TO_CURVE_NAME.put(521, "secp521r1");
+    }
+
+    private OpenSSLECGroupContext group;
+
+    @libcore.api.IntraCoreApi
+    public OpenSSLECKeyPairGenerator() {
+        super(ALGORITHM);
+    }
+
+    @Override
+    public KeyPair generateKeyPair() {
+        if (group == null) {
+            final String curveName = SIZE_TO_CURVE_NAME.get(DEFAULT_KEY_SIZE);
+            group = OpenSSLECGroupContext.getCurveByName(curveName);
+            if (group == null) {
+                throw new RuntimeException("Curve not recognized: " + curveName);
+            }
+        }
+
+        final OpenSSLKey key = new OpenSSLKey(
+                NativeCrypto.EC_KEY_generate_key(group.getNativeRef()));
+        return new KeyPair(new OpenSSLECPublicKey(group, key), new OpenSSLECPrivateKey(group, key));
+    }
+
+    @Override
+    public void initialize(int keysize, SecureRandom random) {
+        final String name = SIZE_TO_CURVE_NAME.get(keysize);
+        if (name == null) {
+            throw new InvalidParameterException("unknown key size " + keysize);
+        }
+
+        /*
+         * Store the group in a temporary variable until we know this is a valid
+         * group.
+         */
+        final OpenSSLECGroupContext possibleGroup = OpenSSLECGroupContext.getCurveByName(name);
+        if (possibleGroup == null) {
+            throw new InvalidParameterException("unknown curve " + name);
+        }
+
+        group = possibleGroup;
+    }
+
+    @Override
+    public void initialize(AlgorithmParameterSpec param, SecureRandom random)
+            throws InvalidAlgorithmParameterException {
+        if (param instanceof ECParameterSpec) {
+            ECParameterSpec ecParam = (ECParameterSpec) param;
+
+            group = OpenSSLECGroupContext.getInstance(ecParam);
+        } else if (param instanceof ECGenParameterSpec) {
+            ECGenParameterSpec ecParam = (ECGenParameterSpec) param;
+
+            final String curveName = ecParam.getName();
+
+            /*
+             * Store the group in a temporary variable until we know this is a
+             * valid group.
+             */
+            final OpenSSLECGroupContext possibleGroup = OpenSSLECGroupContext
+                    .getCurveByName(curveName);
+            if (possibleGroup == null) {
+                throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName);
+            }
+
+            group = possibleGroup;
+        } else {
+            throw new InvalidAlgorithmParameterException(
+                    "parameter must be ECParameterSpec or ECGenParameterSpec");
+        }
+    }
+
+    /** For testing. */
+    public static void assertCurvesAreValid() {
+        ArrayList<String> invalidCurves = new ArrayList<String>();
+        for (String curveName : SIZE_TO_CURVE_NAME.values()) {
+            if (OpenSSLECGroupContext.getCurveByName(curveName) == null) {
+                invalidCurves.add(curveName);
+            }
+        }
+        if (invalidCurves.size() > 0) {
+            throw new AssertionError("Invalid curve names: "
+                    + Arrays.toString(invalidCurves.toArray()));
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPointContext.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPointContext.java
new file mode 100644
index 0000000..8a6824d
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPointContext.java
@@ -0,0 +1,64 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.math.BigInteger;
+import java.security.spec.ECPoint;
+
+final class OpenSSLECPointContext {
+    private final OpenSSLECGroupContext group;
+    private final NativeRef.EC_POINT pointCtx;
+
+    OpenSSLECPointContext(OpenSSLECGroupContext group, NativeRef.EC_POINT pointCtx) {
+        this.group = group;
+        this.pointCtx = pointCtx;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        throw new IllegalArgumentException("OpenSSLECPointContext.equals is not defined.");
+    }
+
+    ECPoint getECPoint() {
+        final byte[][] generatorCoords = NativeCrypto.EC_POINT_get_affine_coordinates(
+                group.getNativeRef(), pointCtx);
+        final BigInteger x = new BigInteger(generatorCoords[0]);
+        final BigInteger y = new BigInteger(generatorCoords[1]);
+        return new ECPoint(x, y);
+    }
+
+    @Override
+    public int hashCode() {
+        // TODO Auto-generated method stub
+        return super.hashCode();
+    }
+
+    NativeRef.EC_POINT getNativeRef() {
+        return pointCtx;
+    }
+
+    static OpenSSLECPointContext getInstance(OpenSSLECGroupContext group,
+            ECPoint javaPoint) {
+        OpenSSLECPointContext point = new OpenSSLECPointContext(group, new NativeRef.EC_POINT(
+                NativeCrypto.EC_POINT_new(group.getNativeRef())));
+        NativeCrypto.EC_POINT_set_affine_coordinates(group.getNativeRef(),
+                point.getNativeRef(), javaPoint.getAffineX().toByteArray(),
+                javaPoint.getAffineY().toByteArray());
+        return point;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPrivateKey.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPrivateKey.java
new file mode 100644
index 0000000..433ebc4
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPrivateKey.java
@@ -0,0 +1,249 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.ECKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import com.android.org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
+
+/**
+ * An implementation of a {@link PrivateKey} for EC keys based on BoringSSL.
+ */
+final class OpenSSLECPrivateKey implements ECPrivateKey, OpenSSLKeyHolder {
+    private static final long serialVersionUID = -4036633595001083922L;
+
+    private static final String ALGORITHM = "EC";
+
+    protected transient OpenSSLKey key;
+
+    protected transient OpenSSLECGroupContext group;
+
+    OpenSSLECPrivateKey(OpenSSLECGroupContext group, OpenSSLKey key) {
+        this.group = group;
+        this.key = key;
+    }
+
+    OpenSSLECPrivateKey(OpenSSLKey key) {
+        this.group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP(
+                NativeCrypto.EC_KEY_get1_group(key.getNativeRef())));
+        this.key = key;
+    }
+
+    OpenSSLECPrivateKey(ECPrivateKeySpec ecKeySpec) throws InvalidKeySpecException {
+        try {
+            group = OpenSSLECGroupContext.getInstance(ecKeySpec.getParams());
+            final BigInteger privKey = ecKeySpec.getS();
+            key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getNativeRef(), null,
+                    privKey.toByteArray()));
+        } catch (Exception e) {
+            throw new InvalidKeySpecException(e);
+        }
+    }
+
+    static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey) throws InvalidKeyException {
+        OpenSSLECGroupContext group;
+        try {
+            group = OpenSSLECGroupContext.getInstance(ecPrivateKey.getParams());
+        } catch (InvalidAlgorithmParameterException e) {
+            throw new InvalidKeyException("Unknown group parameters", e);
+        }
+        return wrapPlatformKey(ecPrivateKey, group);
+    }
+
+    /**
+     * Wraps the provided private key for use in the TLS/SSL stack only. Sign/decrypt operations
+     * using the key will be delegated to the {@code Signature}/{@code Cipher} implementation of the
+     * provider which accepts the key.
+     */
+    static OpenSSLKey wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey,
+            PublicKey publicKey) throws InvalidKeyException {
+        ECParameterSpec params = null;
+        if (privateKey instanceof ECKey) {
+            params = ((ECKey) privateKey).getParams();
+        } else if (publicKey instanceof ECKey) {
+            params = ((ECKey) publicKey).getParams();
+        }
+        if (params == null) {
+            throw new InvalidKeyException("EC parameters not available. Private: " + privateKey
+                    + ", public: " + publicKey);
+        }
+        return wrapJCAPrivateKeyForTLSStackOnly(privateKey, params);
+    }
+
+    /**
+     * Wraps the provided private key for use in the TLS/SSL stack only. Sign/decrypt operations
+     * using the key will be delegated to the {@code Signature}/{@code Cipher} implementation of the
+     * provider which accepts the key.
+     */
+    static OpenSSLKey wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey,
+            ECParameterSpec params) throws InvalidKeyException {
+        if (params == null) {
+            if (privateKey instanceof ECKey) {
+                params = ((ECKey) privateKey).getParams();
+            }
+        }
+        if (params == null) {
+            throw new InvalidKeyException("EC parameters not available: " + privateKey);
+        }
+
+        OpenSSLECGroupContext group;
+        try {
+            group = OpenSSLECGroupContext.getInstance(params);
+        } catch (InvalidAlgorithmParameterException e) {
+            throw new InvalidKeyException("Invalid EC parameters: " + params);
+        }
+
+        return new OpenSSLKey(
+                NativeCrypto.getECPrivateKeyWrapper(privateKey, group.getNativeRef()), true);
+    }
+
+    private static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey,
+            OpenSSLECGroupContext group) throws InvalidKeyException {
+        return new OpenSSLKey(NativeCrypto.getECPrivateKeyWrapper(ecPrivateKey,
+                group.getNativeRef()), true);
+    }
+
+    static OpenSSLKey getInstance(ECPrivateKey ecPrivateKey) throws InvalidKeyException {
+        try {
+            OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(ecPrivateKey
+                    .getParams());
+
+            /*
+             * If the key is not encodable (PKCS11-like key), then wrap it and
+             * use JNI upcalls to satisfy requests.
+             */
+            if (ecPrivateKey.getFormat() == null) {
+                return wrapPlatformKey(ecPrivateKey, group);
+            }
+
+            final BigInteger privKey = ecPrivateKey.getS();
+            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getNativeRef(), null,
+                    privKey.toByteArray()));
+        } catch (Exception e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return ALGORITHM;
+    }
+
+    @Override
+    public String getFormat() {
+        return "PKCS#8";
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        return NativeCrypto.EVP_marshal_private_key(key.getNativeRef());
+    }
+
+    @Override
+    public ECParameterSpec getParams() {
+        return group.getECParameterSpec();
+    }
+
+    @Override
+    public BigInteger getS() {
+        return getPrivateKey();
+    }
+
+    private BigInteger getPrivateKey() {
+        return new BigInteger(NativeCrypto.EC_KEY_get_private_key(key.getNativeRef()));
+    }
+
+    @Override
+    public OpenSSLKey getOpenSSLKey() {
+        return key;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (o instanceof OpenSSLECPrivateKey) {
+            OpenSSLECPrivateKey other = (OpenSSLECPrivateKey) o;
+            return key.equals(other.key);
+        }
+
+        if (!(o instanceof ECPrivateKey)) {
+            return false;
+        }
+
+        final ECPrivateKey other = (ECPrivateKey) o;
+        if (!getPrivateKey().equals(other.getS())) {
+            return false;
+        }
+
+        final ECParameterSpec spec = getParams();
+        final ECParameterSpec otherSpec = other.getParams();
+
+        return spec.getCurve().equals(otherSpec.getCurve())
+                && spec.getGenerator().equals(otherSpec.getGenerator())
+                && spec.getOrder().equals(otherSpec.getOrder())
+                && spec.getCofactor() == otherSpec.getCofactor();
+    }
+
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(NativeCrypto.EVP_marshal_private_key(key.getNativeRef()));
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("OpenSSLECPrivateKey{");
+        sb.append("params={");
+        sb.append(NativeCrypto.EVP_PKEY_print_params(key.getNativeRef()));
+        sb.append("}}");
+        return sb.toString();
+    }
+
+    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
+        stream.defaultReadObject();
+
+        byte[] encoded = (byte[]) stream.readObject();
+
+        try {
+            key = new OpenSSLKey(NativeCrypto.EVP_parse_private_key(encoded));
+        } catch (ParsingException e) {
+            throw new IOException(e);
+        }
+        group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP(
+                NativeCrypto.EC_KEY_get1_group(key.getNativeRef())));
+    }
+
+    private void writeObject(ObjectOutputStream stream) throws IOException {
+        stream.defaultWriteObject();
+        stream.writeObject(getEncoded());
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPublicKey.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPublicKey.java
new file mode 100644
index 0000000..0476493
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLECPublicKey.java
@@ -0,0 +1,174 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.InvalidKeyException;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import com.android.org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
+
+/**
+ * An implementation of a {@link java.security.PublicKey} for EC keys based on BoringSSL.
+ */
+final class OpenSSLECPublicKey implements ECPublicKey, OpenSSLKeyHolder {
+    private static final long serialVersionUID = 3215842926808298020L;
+
+    private static final String ALGORITHM = "EC";
+
+    protected transient OpenSSLKey key;
+
+    protected transient OpenSSLECGroupContext group;
+
+    OpenSSLECPublicKey(OpenSSLECGroupContext group, OpenSSLKey key) {
+        this.group = group;
+        this.key = key;
+    }
+
+    OpenSSLECPublicKey(OpenSSLKey key) {
+        this.group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP(
+                NativeCrypto.EC_KEY_get1_group(key.getNativeRef())));
+        this.key = key;
+    }
+
+    OpenSSLECPublicKey(ECPublicKeySpec ecKeySpec) throws InvalidKeySpecException {
+        try {
+            group = OpenSSLECGroupContext.getInstance(ecKeySpec.getParams());
+            OpenSSLECPointContext pubKey = OpenSSLECPointContext.getInstance(group,
+                    ecKeySpec.getW());
+            key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getNativeRef(),
+                    pubKey.getNativeRef(), null));
+        } catch (Exception e) {
+            throw new InvalidKeySpecException(e);
+        }
+    }
+
+    static OpenSSLKey getInstance(ECPublicKey ecPublicKey) throws InvalidKeyException {
+        try {
+            OpenSSLECGroupContext group = OpenSSLECGroupContext
+                    .getInstance(ecPublicKey.getParams());
+            OpenSSLECPointContext pubKey = OpenSSLECPointContext.getInstance(group,
+                    ecPublicKey.getW());
+            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_EC_KEY(group.getNativeRef(),
+                    pubKey.getNativeRef(), null));
+        } catch (Exception e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return ALGORITHM;
+    }
+
+    @Override
+    public String getFormat() {
+        return "X.509";
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        return NativeCrypto.EVP_marshal_public_key(key.getNativeRef());
+    }
+
+    @Override
+    public ECParameterSpec getParams() {
+        return group.getECParameterSpec();
+    }
+
+    private ECPoint getPublicKey() {
+        final OpenSSLECPointContext pubKey = new OpenSSLECPointContext(group,
+                new NativeRef.EC_POINT(NativeCrypto.EC_KEY_get_public_key(key.getNativeRef())));
+
+        return pubKey.getECPoint();
+    }
+
+    @Override
+    public ECPoint getW() {
+        return getPublicKey();
+    }
+
+    @Override
+    public OpenSSLKey getOpenSSLKey() {
+        return key;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (o instanceof OpenSSLECPublicKey) {
+            OpenSSLECPublicKey other = (OpenSSLECPublicKey) o;
+            return key.equals(other.key);
+        }
+
+        if (!(o instanceof ECPublicKey)) {
+            return false;
+        }
+
+        final ECPublicKey other = (ECPublicKey) o;
+        if (!getPublicKey().equals(other.getW())) {
+            return false;
+        }
+
+        final ECParameterSpec spec = getParams();
+        final ECParameterSpec otherSpec = other.getParams();
+
+        return spec.getCurve().equals(otherSpec.getCurve())
+                && spec.getGenerator().equals(otherSpec.getGenerator())
+                && spec.getOrder().equals(otherSpec.getOrder())
+                && spec.getCofactor() == otherSpec.getCofactor();
+    }
+
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(NativeCrypto.EVP_marshal_public_key(key.getNativeRef()));
+    }
+
+    @Override
+    public String toString() {
+        return NativeCrypto.EVP_PKEY_print_public(key.getNativeRef());
+    }
+
+    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
+        stream.defaultReadObject();
+
+        byte[] encoded = (byte[]) stream.readObject();
+
+        try {
+            key = new OpenSSLKey(NativeCrypto.EVP_parse_public_key(encoded));
+        } catch (ParsingException e) {
+            throw new IOException(e);
+        }
+        group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP(
+                NativeCrypto.EC_KEY_get1_group(key.getNativeRef())));
+    }
+
+    private void writeObject(ObjectOutputStream stream) throws IOException {
+        stream.defaultWriteObject();
+        stream.writeObject(getEncoded());
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipher.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipher.java
new file mode 100644
index 0000000..34bf157
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipher.java
@@ -0,0 +1,213 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+import com.android.org.conscrypt.NativeRef.EVP_CIPHER_CTX;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public abstract class OpenSSLEvpCipher extends OpenSSLCipher {
+    /**
+     * Native pointer for the OpenSSL EVP_CIPHER context.
+     */
+    private final EVP_CIPHER_CTX cipherCtx = new EVP_CIPHER_CTX(
+            NativeCrypto.EVP_CIPHER_CTX_new());
+
+    /**
+     * Whether the cipher has processed any data yet. EVP_CIPHER doesn't
+     * like calling "doFinal()" in decryption mode without processing any
+     * updates.
+     */
+    private boolean calledUpdate;
+
+    /**
+     * The block size of the current mode.
+     */
+    private int modeBlockSize;
+
+    public OpenSSLEvpCipher(Mode mode, Padding padding) {
+        super(mode, padding);
+    }
+
+    @Override
+    void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params,
+            SecureRandom random) throws InvalidKeyException,
+        InvalidAlgorithmParameterException {
+        byte[] iv;
+        if (params instanceof IvParameterSpec) {
+            IvParameterSpec ivParams = (IvParameterSpec) params;
+            iv = ivParams.getIV();
+        } else {
+            iv = null;
+        }
+
+        final long cipherType = NativeCrypto.EVP_get_cipherbyname(getCipherName(
+                encodedKey.length, mode));
+        if (cipherType == 0) {
+            throw new InvalidAlgorithmParameterException("Cannot find name for key length = "
+                    + (encodedKey.length * 8) + " and mode = " + mode);
+        }
+
+        final boolean encrypting = isEncrypting();
+
+        final int expectedIvLength = NativeCrypto.EVP_CIPHER_iv_length(cipherType);
+        if (iv == null && expectedIvLength != 0) {
+            if (!encrypting) {
+                throw new InvalidAlgorithmParameterException("IV must be specified in " + mode
+                        + " mode");
+            }
+
+            iv = new byte[expectedIvLength];
+            if (random != null) {
+                random.nextBytes(iv);
+            } else {
+                NativeCrypto.RAND_bytes(iv);
+            }
+        } else if (expectedIvLength == 0 && iv != null) {
+            throw new InvalidAlgorithmParameterException("IV not used in " + mode + " mode");
+        } else if (iv != null && iv.length != expectedIvLength) {
+            throw new InvalidAlgorithmParameterException("expected IV length of "
+                    + expectedIvLength + " but was " + iv.length);
+        }
+
+        this.iv = iv;
+
+        if (supportsVariableSizeKey()) {
+            NativeCrypto.EVP_CipherInit_ex(cipherCtx, cipherType, null, null, encrypting);
+            NativeCrypto.EVP_CIPHER_CTX_set_key_length(cipherCtx, encodedKey.length);
+            NativeCrypto.EVP_CipherInit_ex(cipherCtx, 0, encodedKey, iv, isEncrypting());
+        } else {
+            NativeCrypto.EVP_CipherInit_ex(cipherCtx, cipherType, encodedKey, iv, encrypting);
+        }
+
+        // OpenSSL only supports PKCS5 Padding.
+        NativeCrypto
+                .EVP_CIPHER_CTX_set_padding(cipherCtx, getPadding() == Padding.PKCS5PADDING);
+        modeBlockSize = NativeCrypto.EVP_CIPHER_CTX_block_size(cipherCtx);
+        calledUpdate = false;
+    }
+
+    @Override
+    int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output,
+            int outputOffset, int maximumLen) throws ShortBufferException {
+        final int intialOutputOffset = outputOffset;
+
+        final int bytesLeft = output.length - outputOffset;
+        if (bytesLeft < maximumLen) {
+            throw new ShortBufferException("output buffer too small during update: "
+                    + bytesLeft + " < " + maximumLen);
+        }
+
+        outputOffset += NativeCrypto.EVP_CipherUpdate(cipherCtx, output, outputOffset, input,
+                inputOffset, inputLen);
+
+        calledUpdate = true;
+
+        return outputOffset - intialOutputOffset;
+    }
+
+    @Override
+    int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
+            throws IllegalBlockSizeException, BadPaddingException, ShortBufferException {
+        /* Remember this so we can tell how many characters were written. */
+        final int initialOutputOffset = outputOffset;
+
+        /*
+         * If we're decrypting and haven't had any input, we should return
+         * null. Otherwise OpenSSL will complain if we call final.
+         */
+        if (!isEncrypting() && !calledUpdate) {
+            return 0;
+        }
+
+        /* Allow OpenSSL to pad if necessary and clean up state. */
+        final int bytesLeft = output.length - outputOffset;
+        final int writtenBytes;
+        if (bytesLeft >= maximumLen) {
+            writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx, output, outputOffset);
+        } else {
+            final byte[] lastBlock = new byte[maximumLen];
+            writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx, lastBlock, 0);
+            if (writtenBytes > bytesLeft) {
+                throw new ShortBufferException("buffer is too short: " + writtenBytes + " > "
+                        + bytesLeft);
+            } else if (writtenBytes > 0) {
+                System.arraycopy(lastBlock, 0, output, outputOffset, writtenBytes);
+            }
+        }
+        outputOffset += writtenBytes;
+
+        reset();
+
+        return outputOffset - initialOutputOffset;
+    }
+
+    @Override
+    int getOutputSizeForFinal(int inputLen) {
+        if (modeBlockSize == 1) {
+            return inputLen;
+        } else {
+            final int buffered = NativeCrypto.get_EVP_CIPHER_CTX_buf_len(cipherCtx);
+
+            if (getPadding() == Padding.NOPADDING) {
+                return buffered + inputLen;
+            } else {
+                final boolean finalUsed = NativeCrypto.get_EVP_CIPHER_CTX_final_used(cipherCtx);
+                // There is an additional buffer containing the possible final block.
+                int totalLen = inputLen + buffered + (finalUsed ? modeBlockSize : 0);
+                // Extra block for remainder bytes plus padding.
+                // In case it's encrypting and there are no remainder bytes, add an extra block
+                // consisting only of padding.
+                totalLen += ((totalLen % modeBlockSize != 0) || isEncrypting())
+                        ? modeBlockSize : 0;
+                // The minimum multiple of {@code modeBlockSize} that can hold all the bytes.
+                return totalLen - (totalLen % modeBlockSize);
+            }
+        }
+    }
+
+    @Override
+    int getOutputSizeForUpdate(int inputLen) {
+        return getOutputSizeForFinal(inputLen);
+    }
+
+    /**
+     * Returns the OpenSSL cipher name for the particular {@code keySize}
+     * and cipher {@code mode}.
+     */
+    abstract String getCipherName(int keySize, Mode mode);
+
+    /**
+     * Reset this Cipher instance state to process a new chunk of data.
+     */
+    private void reset() {
+        NativeCrypto.EVP_CipherInit_ex(cipherCtx, 0, encodedKey, iv, isEncrypting());
+        calledUpdate = false;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipherAES.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipherAES.java
new file mode 100644
index 0000000..3a7cf5e
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipherAES.java
@@ -0,0 +1,353 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Locale;
+import javax.crypto.NoSuchPaddingException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public abstract class OpenSSLEvpCipherAES extends OpenSSLEvpCipher {
+    private static final int AES_BLOCK_SIZE = 16;
+
+    OpenSSLEvpCipherAES(Mode mode, Padding padding) {
+        super(mode, padding);
+    }
+
+    @Override
+    void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
+        switch (mode) {
+            case CBC:
+            case CTR:
+            case ECB:
+                return;
+            default:
+                throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString());
+        }
+    }
+
+    @Override
+    void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
+        switch (padding) {
+            case NOPADDING:
+            case PKCS5PADDING:
+                return;
+            default:
+                throw new NoSuchPaddingException(
+                        "Unsupported padding " + padding.toString());
+        }
+    }
+
+    @Override
+    String getBaseCipherName() {
+        return "AES";
+    }
+
+    @Override
+    String getCipherName(int keyLength, Mode mode) {
+        return "aes-" + (keyLength * 8) + "-" + mode.toString().toLowerCase(Locale.US);
+    }
+
+    @Override
+    int getCipherBlockSize() {
+        return AES_BLOCK_SIZE;
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static class AES extends OpenSSLEvpCipherAES {
+        AES(Mode mode, Padding padding) {
+            super(mode, padding);
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static class CBC extends AES {
+            CBC(Padding padding) {
+                super(Mode.CBC, padding);
+            }
+
+            /**
+             * @hide This class is not part of the Android public SDK API
+             */
+            @libcore.api.IntraCoreApi
+            public static class NoPadding extends CBC {
+                @libcore.api.IntraCoreApi
+                public NoPadding() {
+                    super(Padding.NOPADDING);
+                }
+            }
+
+            /**
+             * @hide This class is not part of the Android public SDK API
+             */
+            @libcore.api.IntraCoreApi
+            public static class PKCS5Padding extends CBC {
+                @libcore.api.IntraCoreApi
+                public PKCS5Padding() {
+                    super(Padding.PKCS5PADDING);
+                }
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static class CTR extends AES {
+            @libcore.api.IntraCoreApi
+            public CTR() {
+                super(Mode.CTR, Padding.NOPADDING);
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static class ECB extends AES {
+            ECB(Padding padding) {
+                super(Mode.ECB, padding);
+            }
+
+            /**
+             * @hide This class is not part of the Android public SDK API
+             */
+            @libcore.api.IntraCoreApi
+            public static class NoPadding extends ECB {
+                @libcore.api.IntraCoreApi
+                public NoPadding() {
+                    super(Padding.NOPADDING);
+                }
+            }
+
+            /**
+             * @hide This class is not part of the Android public SDK API
+             */
+            @libcore.api.IntraCoreApi
+            public static class PKCS5Padding extends ECB {
+                @libcore.api.IntraCoreApi
+                public PKCS5Padding() {
+                    super(Padding.PKCS5PADDING);
+                }
+            }
+        }
+
+        @Override
+        void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+            switch (keyLength) {
+                case 16: // AES 128
+                case 24: // AES 192
+                case 32: // AES 256
+                    return;
+                default:
+                    throw new InvalidKeyException("Unsupported key size: " + keyLength
+                        + " bytes");
+            }
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static class AES_128 extends OpenSSLEvpCipherAES {
+        AES_128(Mode mode, Padding padding) {
+            super(mode, padding);
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static class CBC extends AES_128 {
+            CBC(Padding padding) {
+                super(Mode.CBC, padding);
+            }
+
+            /**
+             * @hide This class is not part of the Android public SDK API
+             */
+            @libcore.api.IntraCoreApi
+            public static class NoPadding extends CBC {
+                @libcore.api.IntraCoreApi
+                public NoPadding() {
+                    super(Padding.NOPADDING);
+                }
+            }
+
+            /**
+             * @hide This class is not part of the Android public SDK API
+             */
+            @libcore.api.IntraCoreApi
+            public static class PKCS5Padding extends CBC {
+                @libcore.api.IntraCoreApi
+                public PKCS5Padding() {
+                    super(Padding.PKCS5PADDING);
+                }
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        public static class CTR extends AES_128 {
+            public CTR() {
+                super(Mode.CTR, Padding.NOPADDING);
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static class ECB extends AES_128 {
+            ECB(Padding padding) {
+                super(Mode.ECB, padding);
+            }
+
+            /**
+             * @hide This class is not part of the Android public SDK API
+             */
+            @libcore.api.IntraCoreApi
+            public static class NoPadding extends ECB {
+                @libcore.api.IntraCoreApi
+                public NoPadding() {
+                    super(Padding.NOPADDING);
+                }
+            }
+
+            /**
+             * @hide This class is not part of the Android public SDK API
+             */
+            @libcore.api.IntraCoreApi
+            public static class PKCS5Padding extends ECB {
+                @libcore.api.IntraCoreApi
+                public PKCS5Padding() {
+                    super(Padding.PKCS5PADDING);
+                }
+            }
+        }
+
+        @Override
+        void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+            if (keyLength != 16) { // 128 bits
+                throw new InvalidKeyException("Unsupported key size: " + keyLength + " bytes");
+            }
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static class AES_256 extends OpenSSLEvpCipherAES {
+        AES_256(Mode mode, Padding padding) {
+            super(mode, padding);
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static class CBC extends AES_256 {
+            CBC(Padding padding) {
+                super(Mode.CBC, padding);
+            }
+
+            /**
+             * @hide This class is not part of the Android public SDK API
+             */
+            @libcore.api.IntraCoreApi
+            public static class NoPadding extends CBC {
+                @libcore.api.IntraCoreApi
+                public NoPadding() {
+                    super(Padding.NOPADDING);
+                }
+            }
+
+            /**
+             * @hide This class is not part of the Android public SDK API
+             */
+            @libcore.api.IntraCoreApi
+            public static class PKCS5Padding extends CBC {
+                @libcore.api.IntraCoreApi
+                public PKCS5Padding() {
+                    super(Padding.PKCS5PADDING);
+                }
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        public static class CTR extends AES_256 {
+            public CTR() {
+                super(Mode.CTR, Padding.NOPADDING);
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static class ECB extends AES_256 {
+            ECB(Padding padding) {
+                super(Mode.ECB, padding);
+            }
+
+            /**
+             * @hide This class is not part of the Android public SDK API
+             */
+            @libcore.api.IntraCoreApi
+            public static class NoPadding extends ECB {
+                @libcore.api.IntraCoreApi
+                public NoPadding() {
+                    super(Padding.NOPADDING);
+                }
+            }
+
+            /**
+             * @hide This class is not part of the Android public SDK API
+             */
+            @libcore.api.IntraCoreApi
+            public static class PKCS5Padding extends ECB {
+                @libcore.api.IntraCoreApi
+                public PKCS5Padding() {
+                    super(Padding.PKCS5PADDING);
+                }
+            }
+        }
+
+        @Override
+        void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+            if (keyLength != 32) { // 256 bits
+                throw new InvalidKeyException("Unsupported key size: " + keyLength + " bytes");
+            }
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipherARC4.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipherARC4.java
new file mode 100644
index 0000000..5e86792
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipherARC4.java
@@ -0,0 +1,73 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.NoSuchPaddingException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public class OpenSSLEvpCipherARC4 extends OpenSSLEvpCipher {
+    @libcore.api.IntraCoreApi
+    public OpenSSLEvpCipherARC4() {
+        // Modes and padding don't make sense for ARC4.
+        super(Mode.ECB, Padding.NOPADDING);
+    }
+
+    @Override
+    String getBaseCipherName() {
+        return "ARCFOUR";
+    }
+
+    @Override
+    String getCipherName(int keySize, Mode mode) {
+        return "rc4";
+    }
+
+    @Override
+    void checkSupportedKeySize(int keySize) throws InvalidKeyException {
+    }
+
+    @Override
+    void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
+        if (mode != Mode.NONE && mode != Mode.ECB) {
+            throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString());
+        }
+    }
+
+    @Override
+    void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
+        if (padding != Padding.NOPADDING) {
+            throw new NoSuchPaddingException("Unsupported padding " + padding.toString());
+        }
+    }
+
+    @Override
+    int getCipherBlockSize() {
+        return 0;
+    }
+
+    @Override
+    boolean supportsVariableSizeKey() {
+        return true;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipherDESEDE.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipherDESEDE.java
new file mode 100644
index 0000000..bd8f40a
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLEvpCipherDESEDE.java
@@ -0,0 +1,116 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Locale;
+import javax.crypto.NoSuchPaddingException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public abstract class OpenSSLEvpCipherDESEDE extends OpenSSLEvpCipher {
+    private static final int DES_BLOCK_SIZE = 8;
+
+    OpenSSLEvpCipherDESEDE(Mode mode, Padding padding) {
+        super(mode, padding);
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static class CBC extends OpenSSLEvpCipherDESEDE {
+        CBC(Padding padding) {
+            super(Mode.CBC, padding);
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static class NoPadding extends CBC {
+            @libcore.api.IntraCoreApi
+            public NoPadding() {
+                super(Padding.NOPADDING);
+            }
+        }
+
+        /**
+         * @hide This class is not part of the Android public SDK API
+         */
+        @libcore.api.IntraCoreApi
+        public static class PKCS5Padding extends CBC {
+            @libcore.api.IntraCoreApi
+            public PKCS5Padding() {
+                super(Padding.PKCS5PADDING);
+            }
+        }
+    }
+
+    @Override
+    String getBaseCipherName() {
+        return "DESede";
+    }
+
+    @Override
+    String getCipherName(int keySize, Mode mode) {
+        final String baseCipherName;
+        if (keySize == 16) {
+            baseCipherName = "des-ede";
+        } else {
+            baseCipherName = "des-ede3";
+        }
+
+        return baseCipherName + "-" + mode.toString().toLowerCase(Locale.US);
+    }
+
+    @Override
+    void checkSupportedKeySize(int keySize) throws InvalidKeyException {
+        if (keySize != 16 && keySize != 24) {
+            throw new InvalidKeyException("key size must be 128 or 192 bits");
+        }
+    }
+
+    @Override
+    void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
+        if (mode != Mode.CBC) {
+            throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString());
+        }
+    }
+
+    @Override
+    void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
+        switch (padding) {
+            case NOPADDING:
+            case PKCS5PADDING:
+                return;
+            default:
+                throw new NoSuchPaddingException("Unsupported padding "
+                        + padding.toString());
+        }
+    }
+
+    @Override
+    int getCipherBlockSize() {
+        return DES_BLOCK_SIZE;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLKey.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLKey.java
new file mode 100644
index 0000000..0307073
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLKey.java
@@ -0,0 +1,355 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.InputStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import com.android.org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
+
+/**
+ * Represents a BoringSSL {@code EVP_PKEY}.
+ */
+final class OpenSSLKey {
+    private final NativeRef.EVP_PKEY ctx;
+
+    private final boolean wrapped;
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    OpenSSLKey(long ctx) {
+        this(ctx, false);
+    }
+
+    OpenSSLKey(long ctx, boolean wrapped) {
+        this.ctx = new NativeRef.EVP_PKEY(ctx);
+        this.wrapped = wrapped;
+    }
+
+    /**
+     * Returns the EVP_PKEY context for use in JNI calls.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    NativeRef.EVP_PKEY getNativeRef() {
+        return ctx;
+    }
+
+    boolean isWrapped() {
+        return wrapped;
+    }
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static OpenSSLKey fromPrivateKey(PrivateKey key) throws InvalidKeyException {
+        if (key instanceof OpenSSLKeyHolder) {
+            return ((OpenSSLKeyHolder) key).getOpenSSLKey();
+        }
+
+        final String keyFormat = key.getFormat();
+        if (keyFormat == null) {
+            return wrapPrivateKey(key);
+        } else if (!"PKCS#8".equals(key.getFormat())) {
+            throw new InvalidKeyException("Unknown key format " + keyFormat);
+        }
+
+        final byte[] encoded = key.getEncoded();
+        if (encoded == null) {
+            throw new InvalidKeyException("Key encoding is null");
+        }
+
+        try {
+            return new OpenSSLKey(NativeCrypto.EVP_parse_private_key(key.getEncoded()));
+        } catch (ParsingException e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    /**
+     * Parse a private key in PEM encoding from the provided input stream.
+     *
+     * @throws InvalidKeyException if parsing fails
+     */
+    static OpenSSLKey fromPrivateKeyPemInputStream(InputStream is)
+            throws InvalidKeyException {
+        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
+        try {
+            long keyCtx = NativeCrypto.PEM_read_bio_PrivateKey(bis.getBioContext());
+            if (keyCtx == 0L) {
+                return null;
+            }
+
+            return new OpenSSLKey(keyCtx);
+        } catch (Exception e) {
+            throw new InvalidKeyException(e);
+        } finally {
+            bis.release();
+        }
+    }
+
+    /**
+     * Gets an {@code OpenSSLKey} instance backed by the provided private key. The resulting key is
+     * usable only by this provider's TLS/SSL stack.
+     *
+     * @param privateKey private key.
+     * @param publicKey corresponding public key or {@code null} if not available. Some opaque
+     *        private keys cannot be used by the TLS/SSL stack without the public key.
+     */
+    static OpenSSLKey fromPrivateKeyForTLSStackOnly(
+            PrivateKey privateKey, PublicKey publicKey) throws InvalidKeyException {
+        OpenSSLKey result = getOpenSSLKey(privateKey);
+        if (result != null) {
+            return result;
+        }
+
+        result = fromKeyMaterial(privateKey);
+        if (result != null) {
+            return result;
+        }
+
+        return wrapJCAPrivateKeyForTLSStackOnly(privateKey, publicKey);
+    }
+
+    /**
+     * Gets an {@code OpenSSLKey} instance backed by the provided EC private key. The resulting key
+     * is usable only by this provider's TLS/SSL stack.
+     *
+     * @param key private key.
+     * @param ecParams EC parameters {@code null} if not available. Some opaque private keys cannot
+     *        be used by the TLS/SSL stack without the parameters because the private key itself
+     *        might not expose the parameters.
+     */
+    static OpenSSLKey fromECPrivateKeyForTLSStackOnly(
+            PrivateKey key, ECParameterSpec ecParams) throws InvalidKeyException {
+        OpenSSLKey result = getOpenSSLKey(key);
+        if (result != null) {
+            return result;
+        }
+
+        result = fromKeyMaterial(key);
+        if (result != null) {
+            return result;
+        }
+
+        return OpenSSLECPrivateKey.wrapJCAPrivateKeyForTLSStackOnly(key, ecParams);
+    }
+
+    /**
+     * Gets the {@code OpenSSLKey} instance of the provided key.
+     *
+     * @return instance or {@code null} if the {@code key} is not backed by OpenSSL's
+     *         {@code EVP_PKEY}.
+     */
+    private static OpenSSLKey getOpenSSLKey(PrivateKey key) {
+        if (key instanceof OpenSSLKeyHolder) {
+            return ((OpenSSLKeyHolder) key).getOpenSSLKey();
+        }
+
+        if ("RSA".equals(key.getAlgorithm())) {
+            return Platform.wrapRsaKey(key);
+        }
+
+        return null;
+    }
+
+    /**
+     * Gets an {@code OpenSSLKey} instance initialized with the key material of the provided key.
+     *
+     * @return instance or {@code null} if the {@code key} does not export its key material in a
+     *         suitable format.
+     */
+    private static OpenSSLKey fromKeyMaterial(PrivateKey key) throws InvalidKeyException {
+        if (!"PKCS#8".equals(key.getFormat())) {
+            return null;
+        }
+        byte[] encoded = key.getEncoded();
+        if (encoded == null) {
+            return null;
+        }
+        try {
+            return new OpenSSLKey(NativeCrypto.EVP_parse_private_key(encoded));
+        } catch (ParsingException e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    /**
+     * Wraps the provided private key for use in the TLS/SSL stack only. Sign/decrypt operations
+     * using the key will be delegated to the {@code Signature}/{@code Cipher} implementation of the
+     * provider which accepts the key.
+     */
+    private static OpenSSLKey wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey,
+            PublicKey publicKey) throws InvalidKeyException {
+        String keyAlgorithm = privateKey.getAlgorithm();
+        if ("RSA".equals(keyAlgorithm)) {
+            return OpenSSLRSAPrivateKey.wrapJCAPrivateKeyForTLSStackOnly(privateKey, publicKey);
+        } else if ("EC".equals(keyAlgorithm)) {
+            return OpenSSLECPrivateKey.wrapJCAPrivateKeyForTLSStackOnly(privateKey, publicKey);
+        } else {
+            throw new InvalidKeyException("Unsupported key algorithm: " + keyAlgorithm);
+        }
+    }
+
+    private static OpenSSLKey wrapPrivateKey(PrivateKey key) throws InvalidKeyException {
+        if (key instanceof RSAPrivateKey) {
+            return OpenSSLRSAPrivateKey.wrapPlatformKey((RSAPrivateKey) key);
+        } else if (key instanceof ECPrivateKey) {
+            return OpenSSLECPrivateKey.wrapPlatformKey((ECPrivateKey) key);
+        } else {
+            throw new InvalidKeyException("Unknown key type: " + key.toString());
+        }
+    }
+
+    static OpenSSLKey fromPublicKey(PublicKey key) throws InvalidKeyException {
+        if (key instanceof OpenSSLKeyHolder) {
+            return ((OpenSSLKeyHolder) key).getOpenSSLKey();
+        }
+
+        if (!"X.509".equals(key.getFormat())) {
+            throw new InvalidKeyException("Unknown key format " + key.getFormat());
+        }
+
+        final byte[] encoded = key.getEncoded();
+        if (encoded == null) {
+            throw new InvalidKeyException("Key encoding is null");
+        }
+
+        try {
+            return new OpenSSLKey(NativeCrypto.EVP_parse_public_key(key.getEncoded()));
+        } catch (Exception e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    /**
+     * Parse a public key in PEM encoding from the provided input stream.
+     *
+     * @throws InvalidKeyException if parsing fails
+     */
+    static OpenSSLKey fromPublicKeyPemInputStream(InputStream is)
+            throws InvalidKeyException {
+        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
+        try {
+            long keyCtx = NativeCrypto.PEM_read_bio_PUBKEY(bis.getBioContext());
+            if (keyCtx == 0L) {
+                return null;
+            }
+
+            return new OpenSSLKey(keyCtx);
+        } catch (Exception e) {
+            throw new InvalidKeyException(e);
+        } finally {
+            bis.release();
+        }
+    }
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    PublicKey getPublicKey() throws NoSuchAlgorithmException {
+        switch (NativeCrypto.EVP_PKEY_type(ctx)) {
+            case NativeConstants.EVP_PKEY_RSA:
+                return new OpenSSLRSAPublicKey(this);
+            case NativeConstants.EVP_PKEY_EC:
+                return new OpenSSLECPublicKey(this);
+            default:
+                throw new NoSuchAlgorithmException("unknown PKEY type");
+        }
+    }
+
+    static PublicKey getPublicKey(X509EncodedKeySpec keySpec, int type)
+            throws InvalidKeySpecException {
+        X509EncodedKeySpec x509KeySpec = keySpec;
+
+        final OpenSSLKey key;
+        try {
+            key = new OpenSSLKey(NativeCrypto.EVP_parse_public_key(x509KeySpec.getEncoded()));
+        } catch (Exception e) {
+            throw new InvalidKeySpecException(e);
+        }
+
+        if (NativeCrypto.EVP_PKEY_type(key.getNativeRef()) != type) {
+            throw new InvalidKeySpecException("Unexpected key type");
+        }
+
+        try {
+            return key.getPublicKey();
+        } catch (NoSuchAlgorithmException e) {
+            throw new InvalidKeySpecException(e);
+        }
+    }
+
+    PrivateKey getPrivateKey() throws NoSuchAlgorithmException {
+        switch (NativeCrypto.EVP_PKEY_type(ctx)) {
+            case NativeConstants.EVP_PKEY_RSA:
+                return new OpenSSLRSAPrivateKey(this);
+            case NativeConstants.EVP_PKEY_EC:
+                return new OpenSSLECPrivateKey(this);
+            default:
+                throw new NoSuchAlgorithmException("unknown PKEY type");
+        }
+    }
+
+    static PrivateKey getPrivateKey(PKCS8EncodedKeySpec keySpec, int type)
+            throws InvalidKeySpecException {
+        PKCS8EncodedKeySpec pkcs8KeySpec = keySpec;
+
+        final OpenSSLKey key;
+        try {
+            key = new OpenSSLKey(NativeCrypto.EVP_parse_private_key(pkcs8KeySpec.getEncoded()));
+        } catch (Exception e) {
+            throw new InvalidKeySpecException(e);
+        }
+
+        if (NativeCrypto.EVP_PKEY_type(key.getNativeRef()) != type) {
+            throw new InvalidKeySpecException("Unexpected key type");
+        }
+
+        try {
+            return key.getPrivateKey();
+        } catch (NoSuchAlgorithmException e) {
+            throw new InvalidKeySpecException(e);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (!(o instanceof OpenSSLKey)) {
+            return false;
+        }
+
+        OpenSSLKey other = (OpenSSLKey) o;
+        if (ctx.equals(other.getNativeRef())) {
+            return true;
+        }
+
+        return NativeCrypto.EVP_PKEY_cmp(ctx, other.getNativeRef()) == 1;
+    }
+
+    @Override
+    public int hashCode() {
+        return ctx.hashCode();
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLKeyHolder.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLKeyHolder.java
new file mode 100644
index 0000000..7921778
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLKeyHolder.java
@@ -0,0 +1,28 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+/**
+ * Marker interface for classes that hold an {@link OpenSSLKey}.
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public interface OpenSSLKeyHolder {
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    OpenSSLKey getOpenSSLKey();
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLMac.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLMac.java
new file mode 100644
index 0000000..538aed1
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLMac.java
@@ -0,0 +1,226 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.nio.ByteBuffer;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.MacSpi;
+import javax.crypto.SecretKey;
+
+/**
+ * An implementation of {@link javax.crypto.Mac} which uses BoringSSL to perform all the operations.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public abstract class OpenSSLMac extends MacSpi {
+    private NativeRef.HMAC_CTX ctx;
+
+    /**
+     * Holds the EVP_MD for the hashing algorithm, e.g.
+     * EVP_get_digestbyname("sha1");
+     */
+    private final long evp_md;
+
+    /**
+     * The secret key used in this keyed MAC.
+     */
+    private byte[] keyBytes;
+
+    /**
+     * Holds the output size of the message digest.
+     */
+    private final int size;
+
+    /**
+     * Holds a dummy buffer for writing single bytes to the digest.
+     */
+    private final byte[] singleByte = new byte[1];
+
+    private OpenSSLMac(long evp_md, int size) {
+        this.evp_md = evp_md;
+        this.size = size;
+    }
+
+    @Override
+    protected int engineGetMacLength() {
+        return size;
+    }
+
+    @Override
+    protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
+            InvalidAlgorithmParameterException {
+        if (!(key instanceof SecretKey)) {
+            throw new InvalidKeyException("key must be a SecretKey");
+        }
+
+        if (params != null) {
+            throw new InvalidAlgorithmParameterException("unknown parameter type");
+        }
+
+        keyBytes = key.getEncoded();
+        if (keyBytes == null) {
+            throw new InvalidKeyException("key cannot be encoded");
+        }
+
+        resetContext();
+    }
+
+    private final void resetContext() {
+        NativeRef.HMAC_CTX ctxLocal = new NativeRef.HMAC_CTX(NativeCrypto.HMAC_CTX_new());
+        if (keyBytes != null) {
+            NativeCrypto.HMAC_Init_ex(ctxLocal, keyBytes, evp_md);
+        }
+
+        this.ctx = ctxLocal;
+    }
+
+    @Override
+    protected void engineUpdate(byte input) {
+        singleByte[0] = input;
+        engineUpdate(singleByte, 0, 1);
+    }
+
+    @Override
+    protected void engineUpdate(byte[] input, int offset, int len) {
+        final NativeRef.HMAC_CTX ctxLocal = ctx;
+        NativeCrypto.HMAC_Update(ctxLocal, input, offset, len);
+    }
+
+    @Override
+    protected void engineUpdate(ByteBuffer input) {
+        // Optimization: Avoid copying/allocation for direct buffers because their contents are
+        // stored as a contiguous region in memory and thus can be efficiently accessed from native
+        // code.
+
+        if (!input.hasRemaining()) {
+            return;
+        }
+
+        if (!input.isDirect()) {
+            super.engineUpdate(input);
+            return;
+        }
+
+        long baseAddress = NativeCrypto.getDirectBufferAddress(input);
+        if (baseAddress == 0) {
+            // Direct buffer's contents can't be accessed from JNI  -- superclass's implementation
+            // is good enough to handle this.
+            super.engineUpdate(input);
+            return;
+        }
+
+        // MAC the contents between Buffer's position and limit (remaining() number of bytes)
+        int position = input.position();
+        if (position < 0) {
+            throw new RuntimeException("Negative position");
+        }
+        long ptr = baseAddress + position;
+        int len = input.remaining();
+        if (len < 0) {
+            throw new RuntimeException("Negative remaining amount");
+        }
+
+        final NativeRef.HMAC_CTX ctxLocal = ctx;
+        NativeCrypto.HMAC_UpdateDirect(ctxLocal, ptr, len);
+        input.position(position + len);
+    }
+
+    @Override
+    protected byte[] engineDoFinal() {
+        final NativeRef.HMAC_CTX ctxLocal = ctx;
+        final byte[] output = NativeCrypto.HMAC_Final(ctxLocal);
+        resetContext();
+        return output;
+    }
+
+    @Override
+    protected void engineReset() {
+        resetContext();
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class HmacMD5 extends OpenSSLMac {
+        @libcore.api.IntraCoreApi
+        public HmacMD5() {
+            super(EvpMdRef.MD5.EVP_MD, EvpMdRef.MD5.SIZE_BYTES);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class HmacSHA1 extends OpenSSLMac {
+        @libcore.api.IntraCoreApi
+        public HmacSHA1() {
+            super(EvpMdRef.SHA1.EVP_MD, EvpMdRef.SHA1.SIZE_BYTES);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class HmacSHA224 extends OpenSSLMac {
+        @libcore.api.IntraCoreApi
+        public HmacSHA224() throws NoSuchAlgorithmException {
+            super(EvpMdRef.SHA224.EVP_MD, EvpMdRef.SHA224.SIZE_BYTES);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class HmacSHA256 extends OpenSSLMac {
+        @libcore.api.IntraCoreApi
+        public HmacSHA256() throws NoSuchAlgorithmException {
+            super(EvpMdRef.SHA256.EVP_MD, EvpMdRef.SHA256.SIZE_BYTES);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class HmacSHA384 extends OpenSSLMac {
+        @libcore.api.IntraCoreApi
+        public HmacSHA384() throws NoSuchAlgorithmException {
+            super(EvpMdRef.SHA384.EVP_MD, EvpMdRef.SHA384.SIZE_BYTES);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class HmacSHA512 extends OpenSSLMac {
+        @libcore.api.IntraCoreApi
+        public HmacSHA512() {
+            super(EvpMdRef.SHA512.EVP_MD, EvpMdRef.SHA512.SIZE_BYTES);
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLMessageDigestJDK.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLMessageDigestJDK.java
new file mode 100644
index 0000000..2e756b5
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLMessageDigestJDK.java
@@ -0,0 +1,237 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.nio.ByteBuffer;
+import java.security.MessageDigestSpi;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Implements the JDK MessageDigest interface using OpenSSL's EVP API.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public class OpenSSLMessageDigestJDK extends MessageDigestSpi implements Cloneable {
+    private final NativeRef.EVP_MD_CTX ctx;
+
+    /**
+     * Holds the EVP_MD for the hashing algorithm, e.g. EVP_get_digestbyname("sha1");
+     */
+    private final long evp_md;
+
+    /**
+     * Holds the output size of the message digest.
+     */
+    private final int size;
+
+    /**
+     * Holds a dummy buffer for writing single bytes to the digest.
+     */
+    private final byte[] singleByte = new byte[1];
+
+    /**
+     * Whether the digest struct has been initialized inside EVP_MD_CTX.
+     */
+    private boolean digestInitializedInContext;
+
+    /**
+     * Creates a new OpenSSLMessageDigest instance for the given algorithm name.
+     */
+    private OpenSSLMessageDigestJDK(long evp_md, int size) throws NoSuchAlgorithmException {
+        this.evp_md = evp_md;
+        this.size = size;
+        NativeRef.EVP_MD_CTX ctxLocal = new NativeRef.EVP_MD_CTX(NativeCrypto.EVP_MD_CTX_create());
+        this.ctx = ctxLocal;
+    }
+
+    private OpenSSLMessageDigestJDK(long evp_md, int size, NativeRef.EVP_MD_CTX ctx,
+            boolean digestInitializedInContext) {
+        this.evp_md = evp_md;
+        this.size = size;
+        this.ctx = ctx;
+        this.digestInitializedInContext = digestInitializedInContext;
+    }
+
+    private synchronized void ensureDigestInitializedInContext() {
+        if (!digestInitializedInContext) {
+            final NativeRef.EVP_MD_CTX ctxLocal = ctx;
+            NativeCrypto.EVP_DigestInit_ex(ctxLocal, evp_md);
+            digestInitializedInContext = true;
+        }
+    }
+
+    @Override
+    protected synchronized void engineReset() {
+        // Reset to the same state as at the end of the <init>(long evp_md, int size). We can avoid
+        // allocating a new EVP_MD_CTX by invoking EVP_MD_CTX_cleanup on the existing one.
+        // EVP_MD_CTX_cleanup cleans up and reinitializes the EVP_MD_CTX.
+        final NativeRef.EVP_MD_CTX ctxLocal = ctx;
+        NativeCrypto.EVP_MD_CTX_cleanup(ctxLocal);
+        digestInitializedInContext = false;
+    }
+
+    @Override
+    protected int engineGetDigestLength() {
+        return size;
+    }
+
+    @Override
+    protected synchronized void engineUpdate(byte input) {
+        singleByte[0] = input;
+        engineUpdate(singleByte, 0, 1);
+    }
+
+    @Override
+    protected synchronized void engineUpdate(byte[] input, int offset, int len) {
+        ensureDigestInitializedInContext();
+        NativeCrypto.EVP_DigestUpdate(ctx, input, offset, len);
+    }
+
+    @Override
+    protected synchronized void engineUpdate(ByteBuffer input) {
+        // Optimization: Avoid copying/allocation for direct buffers because their contents are
+        // stored as a contiguous region in memory and thus can be efficiently accessed from native
+        // code.
+
+        if (!input.hasRemaining()) {
+            return;
+        }
+
+        if (!input.isDirect()) {
+            super.engineUpdate(input);
+            return;
+        }
+
+        long baseAddress = NativeCrypto.getDirectBufferAddress(input);
+        if (baseAddress == 0) {
+            // Direct buffer's contents can't be accessed from JNI  -- superclass's implementation
+            // is good enough to handle this.
+            super.engineUpdate(input);
+            return;
+        }
+
+        // Digest the contents between Buffer's position and limit (remaining() number of bytes)
+        int position = input.position();
+        if (position < 0) {
+            throw new RuntimeException("Negative position");
+        }
+        long ptr = baseAddress + position;
+        int len = input.remaining();
+        if (len < 0) {
+            throw new RuntimeException("Negative remaining amount");
+        }
+
+        ensureDigestInitializedInContext();
+        NativeCrypto.EVP_DigestUpdateDirect(ctx, ptr, len);
+        input.position(position + len);
+    }
+
+    @Override
+    protected synchronized byte[] engineDigest() {
+        ensureDigestInitializedInContext();
+        final byte[] result = new byte[size];
+        NativeCrypto.EVP_DigestFinal_ex(ctx, result, 0);
+
+        // Optimized reset path:
+        // 1. No need to wipe EVP_MD_CTX because EVP_DigestFinal_ex has already cleansed any
+        //    sensitive state from it.
+        // 2. Require EVP_DigestInit_ex to be invoked before this MessageDigestSpi starts computing
+        //    a new digest.
+        digestInitializedInContext = false;
+
+        return result;
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class MD5 extends OpenSSLMessageDigestJDK {
+        @libcore.api.IntraCoreApi
+        public MD5() throws NoSuchAlgorithmException {
+            super(EvpMdRef.MD5.EVP_MD, EvpMdRef.MD5.SIZE_BYTES);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA1 extends OpenSSLMessageDigestJDK {
+        @libcore.api.IntraCoreApi
+        public SHA1() throws NoSuchAlgorithmException {
+            super(EvpMdRef.SHA1.EVP_MD, EvpMdRef.SHA1.SIZE_BYTES);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA224 extends OpenSSLMessageDigestJDK {
+        @libcore.api.IntraCoreApi
+        public SHA224() throws NoSuchAlgorithmException {
+            super(EvpMdRef.SHA224.EVP_MD, EvpMdRef.SHA224.SIZE_BYTES);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA256 extends OpenSSLMessageDigestJDK {
+        @libcore.api.IntraCoreApi
+        public SHA256() throws NoSuchAlgorithmException {
+            super(EvpMdRef.SHA256.EVP_MD, EvpMdRef.SHA256.SIZE_BYTES);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA384 extends OpenSSLMessageDigestJDK {
+        @libcore.api.IntraCoreApi
+        public SHA384() throws NoSuchAlgorithmException {
+            super(EvpMdRef.SHA384.EVP_MD, EvpMdRef.SHA384.SIZE_BYTES);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA512 extends OpenSSLMessageDigestJDK {
+        @libcore.api.IntraCoreApi
+        public SHA512() throws NoSuchAlgorithmException {
+            super(EvpMdRef.SHA512.EVP_MD, EvpMdRef.SHA512.SIZE_BYTES);
+        }
+    }
+
+    @Override
+    public Object clone() {
+        NativeRef.EVP_MD_CTX ctxCopy = new NativeRef.EVP_MD_CTX(NativeCrypto.EVP_MD_CTX_create());
+        // EVP_MD_CTX_copy_ex requires that the digest struct of source EVP_MD_CTX is initialized.
+        // There's no need to invoke EVP_MD_CTX_copy_ex when the digest struct isn't initialized.
+        if (digestInitializedInContext) {
+            NativeCrypto.EVP_MD_CTX_copy_ex(ctxCopy, ctx);
+        }
+        return new OpenSSLMessageDigestJDK(evp_md, size, ctxCopy, digestInitializedInContext);
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLProvider.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLProvider.java
new file mode 100644
index 0000000..63c53a8
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLProvider.java
@@ -0,0 +1,593 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.Provider;
+
+/**
+ * Provider that uses BoringSSL to perform the actual cryptographic operations.
+ * <p>
+ * Every algorithm should have its IANA assigned OID as an alias. See the following URLs for each
+ * type: <ul> <li><a
+ * href="http://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xml">Hash
+ * functions</a></li> <li><a href="http://www.iana.org/assignments/dssc/dssc.xml">Signature
+ * algorithms</a></li> <li><a
+ * href="http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html">NIST cryptographic
+ * algorithms</a></li>
+ * </ul>
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.CorePlatformApi
+@Internal
+public final class OpenSSLProvider extends Provider {
+    private static final long serialVersionUID = 2996752495318905136L;
+
+    private static final String PREFIX = OpenSSLProvider.class.getPackage().getName() + ".";
+
+    private static final String STANDARD_EC_PRIVATE_KEY_INTERFACE_CLASS_NAME =
+            "java.security.interfaces.ECPrivateKey";
+    private static final String STANDARD_RSA_PRIVATE_KEY_INTERFACE_CLASS_NAME =
+            "java.security.interfaces.RSAPrivateKey";
+    private static final String STANDARD_RSA_PUBLIC_KEY_INTERFACE_CLASS_NAME =
+            "java.security.interfaces.RSAPublicKey";
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    public OpenSSLProvider() {
+        this(Platform.getDefaultProviderName());
+    }
+
+    public OpenSSLProvider(String providerName) {
+        this(providerName, Platform.provideTrustManagerByDefault());
+    }
+
+    OpenSSLProvider(String providerName, boolean includeTrustManager) {
+        super(providerName, 1.0, "Android's OpenSSL-backed security provider");
+
+        // Ensure that the native library has been loaded.
+        NativeCrypto.checkAvailability();
+
+        // Make sure the platform is initialized.
+        Platform.setup();
+
+        /* === SSL Contexts === */
+        final String classOpenSSLContextImpl = PREFIX + "OpenSSLContextImpl";
+        final String tls13SSLContext = classOpenSSLContextImpl + "$TLSv13";
+        // Keep SSL as an alias to TLS
+        put("SSLContext.SSL", tls13SSLContext);
+        put("SSLContext.TLS", tls13SSLContext);
+        put("SSLContext.TLSv1", classOpenSSLContextImpl + "$TLSv1");
+        put("SSLContext.TLSv1.1", classOpenSSLContextImpl + "$TLSv11");
+        put("SSLContext.TLSv1.2", classOpenSSLContextImpl + "$TLSv12");
+        put("SSLContext.TLSv1.3", tls13SSLContext);
+        put("SSLContext.Default", PREFIX + "DefaultSSLContextImpl");
+
+        if (includeTrustManager) {
+            put("TrustManagerFactory.PKIX", TrustManagerFactoryImpl.class.getName());
+            put("Alg.Alias.TrustManagerFactory.X509", "PKIX");
+        }
+
+        /* === AlgorithmParameters === */
+        put("AlgorithmParameters.AES", PREFIX + "IvParameters$AES");
+        put("Alg.Alias.AlgorithmParameters.2.16.840.1.101.3.4.1.2", "AES");
+        put("Alg.Alias.AlgorithmParameters.2.16.840.1.101.3.4.1.22", "AES");
+        put("Alg.Alias.AlgorithmParameters.2.16.840.1.101.3.4.1.42", "AES");
+
+        put("AlgorithmParameters.ChaCha20", PREFIX + "IvParameters$ChaCha20");
+
+        put("AlgorithmParameters.DESEDE", PREFIX + "IvParameters$DESEDE");
+        put("Alg.Alias.AlgorithmParameters.TDEA", "DESEDE");
+        put("Alg.Alias.AlgorithmParameters.1.2.840.113549.3.7", "DESEDE");
+
+        put("AlgorithmParameters.GCM", PREFIX + "GCMParameters");
+        put("Alg.Alias.AlgorithmParameters.2.16.840.1.101.3.4.1.6", "GCM");
+        put("Alg.Alias.AlgorithmParameters.2.16.840.1.101.3.4.1.26", "GCM");
+        put("Alg.Alias.AlgorithmParameters.2.16.840.1.101.3.4.1.46", "GCM");
+        put("AlgorithmParameters.OAEP", PREFIX + "OAEPParameters");
+        put("AlgorithmParameters.PSS", PREFIX + "PSSParameters");
+        put("AlgorithmParameters.EC", PREFIX + "ECParameters");
+
+        /* === Message Digests === */
+        put("MessageDigest.SHA-1", PREFIX + "OpenSSLMessageDigestJDK$SHA1");
+        put("Alg.Alias.MessageDigest.SHA1", "SHA-1");
+        put("Alg.Alias.MessageDigest.SHA", "SHA-1");
+        put("Alg.Alias.MessageDigest.1.3.14.3.2.26", "SHA-1");
+
+        put("MessageDigest.SHA-224", PREFIX + "OpenSSLMessageDigestJDK$SHA224");
+        put("Alg.Alias.MessageDigest.SHA224", "SHA-224");
+        put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.4", "SHA-224");
+
+        put("MessageDigest.SHA-256", PREFIX + "OpenSSLMessageDigestJDK$SHA256");
+        put("Alg.Alias.MessageDigest.SHA256", "SHA-256");
+        put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.1", "SHA-256");
+
+        put("MessageDigest.SHA-384", PREFIX + "OpenSSLMessageDigestJDK$SHA384");
+        put("Alg.Alias.MessageDigest.SHA384", "SHA-384");
+        put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.2", "SHA-384");
+
+        put("MessageDigest.SHA-512", PREFIX + "OpenSSLMessageDigestJDK$SHA512");
+        put("Alg.Alias.MessageDigest.SHA512", "SHA-512");
+        put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.3", "SHA-512");
+
+        // iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) md5(5)
+        put("MessageDigest.MD5", PREFIX + "OpenSSLMessageDigestJDK$MD5");
+        put("Alg.Alias.MessageDigest.1.2.840.113549.2.5", "MD5");
+
+        /* == KeyGenerators == */
+        put("KeyGenerator.ARC4", PREFIX + "KeyGeneratorImpl$ARC4");
+        put("Alg.Alias.KeyGenerator.RC4", "ARC4");
+        put("Alg.Alias.KeyGenerator.1.2.840.113549.3.4", "ARC4");
+
+        put("KeyGenerator.AES", PREFIX + "KeyGeneratorImpl$AES");
+
+        put("KeyGenerator.ChaCha20", PREFIX + "KeyGeneratorImpl$ChaCha20");
+
+        put("KeyGenerator.DESEDE", PREFIX + "KeyGeneratorImpl$DESEDE");
+        put("Alg.Alias.KeyGenerator.TDEA", "DESEDE");
+
+        put("KeyGenerator.HmacMD5", PREFIX + "KeyGeneratorImpl$HmacMD5");
+        put("Alg.Alias.KeyGenerator.1.3.6.1.5.5.8.1.1", "HmacMD5");
+        put("Alg.Alias.KeyGenerator.HMAC-MD5", "HmacMD5");
+        put("Alg.Alias.KeyGenerator.HMAC/MD5", "HmacMD5");
+
+        put("KeyGenerator.HmacSHA1", PREFIX + "KeyGeneratorImpl$HmacSHA1");
+        put("Alg.Alias.KeyGenerator.1.2.840.113549.2.7", "HmacSHA1");
+        put("Alg.Alias.KeyGenerator.1.3.6.1.5.5.8.1.2", "HmacSHA1");
+        put("Alg.Alias.KeyGenerator.HMAC-SHA1", "HmacSHA1");
+        put("Alg.Alias.KeyGenerator.HMAC/SHA1", "HmacSHA1");
+
+        put("KeyGenerator.HmacSHA224", PREFIX + "KeyGeneratorImpl$HmacSHA224");
+        put("Alg.Alias.KeyGenerator.1.2.840.113549.2.8", "HmacSHA224");
+        put("Alg.Alias.KeyGenerator.HMAC-SHA224", "HmacSHA224");
+        put("Alg.Alias.KeyGenerator.HMAC/SHA224", "HmacSHA224");
+
+        put("KeyGenerator.HmacSHA256", PREFIX + "KeyGeneratorImpl$HmacSHA256");
+        put("Alg.Alias.KeyGenerator.1.2.840.113549.2.9", "HmacSHA256");
+        put("Alg.Alias.KeyGenerator.2.16.840.1.101.3.4.2.1", "HmacSHA256");
+        put("Alg.Alias.KeyGenerator.HMAC-SHA256", "HmacSHA256");
+        put("Alg.Alias.KeyGenerator.HMAC/SHA256", "HmacSHA256");
+
+        put("KeyGenerator.HmacSHA384", PREFIX + "KeyGeneratorImpl$HmacSHA384");
+        put("Alg.Alias.KeyGenerator.1.2.840.113549.2.10", "HmacSHA384");
+        put("Alg.Alias.KeyGenerator.HMAC-SHA384", "HmacSHA384");
+        put("Alg.Alias.KeyGenerator.HMAC/SHA384", "HmacSHA384");
+
+        put("KeyGenerator.HmacSHA512", PREFIX + "KeyGeneratorImpl$HmacSHA512");
+        put("Alg.Alias.KeyGenerator.1.2.840.113549.2.11", "HmacSHA512");
+        put("Alg.Alias.KeyGenerator.HMAC-SHA512", "HmacSHA512");
+        put("Alg.Alias.KeyGenerator.HMAC/SHA512", "HmacSHA512");
+
+        /* == KeyPairGenerators == */
+        put("KeyPairGenerator.RSA", PREFIX + "OpenSSLRSAKeyPairGenerator");
+        put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1.1", "RSA");
+        put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1.7", "RSA");
+        put("Alg.Alias.KeyPairGenerator.2.5.8.1.1", "RSA");
+
+        put("KeyPairGenerator.EC", PREFIX + "OpenSSLECKeyPairGenerator");
+        put("Alg.Alias.KeyPairGenerator.1.2.840.10045.2.1", "EC");
+        put("Alg.Alias.KeyPairGenerator.1.3.133.16.840.63.0.2", "EC");
+
+        /* == KeyFactory == */
+        put("KeyFactory.RSA", PREFIX + "OpenSSLRSAKeyFactory");
+        put("Alg.Alias.KeyFactory.1.2.840.113549.1.1.1", "RSA");
+        put("Alg.Alias.KeyFactory.1.2.840.113549.1.1.7", "RSA");
+        put("Alg.Alias.KeyFactory.2.5.8.1.1", "RSA");
+
+        put("KeyFactory.EC", PREFIX + "OpenSSLECKeyFactory");
+        put("Alg.Alias.KeyFactory.1.2.840.10045.2.1", "EC");
+        put("Alg.Alias.KeyFactory.1.3.133.16.840.63.0.2", "EC");
+
+        /* == SecretKeyFactory == */
+        put("SecretKeyFactory.DESEDE", PREFIX + "DESEDESecretKeyFactory");
+        put("Alg.Alias.SecretKeyFactory.TDEA", "DESEDE");
+
+        /* == KeyAgreement == */
+        putECDHKeyAgreementImplClass("OpenSSLECDHKeyAgreement");
+
+        /* == Signatures == */
+        putSignatureImplClass("MD5withRSA", "OpenSSLSignature$MD5RSA");
+        put("Alg.Alias.Signature.MD5withRSAEncryption", "MD5withRSA");
+        put("Alg.Alias.Signature.MD5/RSA", "MD5withRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA");
+        put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.4", "MD5withRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.2.5with1.2.840.113549.1.1.1", "MD5withRSA");
+
+        putSignatureImplClass("SHA1withRSA", "OpenSSLSignature$SHA1RSA");
+        put("Alg.Alias.Signature.SHA1withRSAEncryption", "SHA1withRSA");
+        put("Alg.Alias.Signature.SHA1/RSA", "SHA1withRSA");
+        put("Alg.Alias.Signature.SHA-1/RSA", "SHA1withRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1withRSA");
+        put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.5", "SHA1withRSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.1", "SHA1withRSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.5", "SHA1withRSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1withRSA");
+        put("Alg.Alias.Signature.OID.1.3.14.3.2.29", "SHA1withRSA");
+
+        putSignatureImplClass("SHA224withRSA", "OpenSSLSignature$SHA224RSA");
+        put("Alg.Alias.Signature.SHA224withRSAEncryption", "SHA224withRSA");
+        put("Alg.Alias.Signature.SHA224/RSA", "SHA224withRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.14", "SHA224withRSA");
+        put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.14", "SHA224withRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.113549.1.1.1",
+                "SHA224withRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.113549.1.1.14",
+                "SHA224withRSA");
+
+        putSignatureImplClass("SHA256withRSA", "OpenSSLSignature$SHA256RSA");
+        put("Alg.Alias.Signature.SHA256withRSAEncryption", "SHA256withRSA");
+        put("Alg.Alias.Signature.SHA256/RSA", "SHA256withRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256withRSA");
+        put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.11", "SHA256withRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.113549.1.1.1",
+                "SHA256withRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.113549.1.1.11",
+                "SHA256withRSA");
+
+        putSignatureImplClass("SHA384withRSA", "OpenSSLSignature$SHA384RSA");
+        put("Alg.Alias.Signature.SHA384withRSAEncryption", "SHA384withRSA");
+        put("Alg.Alias.Signature.SHA384/RSA", "SHA384withRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384withRSA");
+        put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.12", "SHA384withRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.2with1.2.840.113549.1.1.1",
+                "SHA384withRSA");
+
+        putSignatureImplClass("SHA512withRSA", "OpenSSLSignature$SHA512RSA");
+        put("Alg.Alias.Signature.SHA512withRSAEncryption", "SHA512withRSA");
+        put("Alg.Alias.Signature.SHA512/RSA", "SHA512withRSA");
+        put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512withRSA");
+        put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.13", "SHA512withRSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.3with1.2.840.113549.1.1.1",
+                "SHA512withRSA");
+
+        putRAWRSASignatureImplClass("OpenSSLSignatureRawRSA");
+
+        putSignatureImplClass("NONEwithECDSA", "OpenSSLSignatureRawECDSA");
+
+        putSignatureImplClass("SHA1withECDSA", "OpenSSLSignature$SHA1ECDSA");
+        put("Alg.Alias.Signature.ECDSA", "SHA1withECDSA");
+        put("Alg.Alias.Signature.ECDSAwithSHA1", "SHA1withECDSA");
+        // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA1(1)
+        put("Alg.Alias.Signature.1.2.840.10045.4.1", "SHA1withECDSA");
+        put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10045.2.1", "SHA1withECDSA");
+
+        // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA2(3)
+        putSignatureImplClass("SHA224withECDSA", "OpenSSLSignature$SHA224ECDSA");
+        put("Alg.Alias.Signature.SHA224/ECDSA", "SHA224withECDSA");
+        // ecdsa-with-SHA224(1)
+        put("Alg.Alias.Signature.1.2.840.10045.4.3.1", "SHA224withECDSA");
+        put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.1", "SHA224withECDSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.10045.2.1", "SHA224withECDSA");
+
+        // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA2(3)
+        putSignatureImplClass("SHA256withECDSA", "OpenSSLSignature$SHA256ECDSA");
+        put("Alg.Alias.Signature.SHA256/ECDSA", "SHA256withECDSA");
+        // ecdsa-with-SHA256(2)
+        put("Alg.Alias.Signature.1.2.840.10045.4.3.2", "SHA256withECDSA");
+        put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.2", "SHA256withECDSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.10045.2.1", "SHA256withECDSA");
+
+        putSignatureImplClass("SHA384withECDSA", "OpenSSLSignature$SHA384ECDSA");
+        put("Alg.Alias.Signature.SHA384/ECDSA", "SHA384withECDSA");
+        // ecdsa-with-SHA384(3)
+        put("Alg.Alias.Signature.1.2.840.10045.4.3.3", "SHA384withECDSA");
+        put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.3", "SHA384withECDSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.2with1.2.840.10045.2.1", "SHA384withECDSA");
+
+        putSignatureImplClass("SHA512withECDSA", "OpenSSLSignature$SHA512ECDSA");
+        put("Alg.Alias.Signature.SHA512/ECDSA", "SHA512withECDSA");
+        // ecdsa-with-SHA512(4)
+        put("Alg.Alias.Signature.1.2.840.10045.4.3.4", "SHA512withECDSA");
+        put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.4", "SHA512withECDSA");
+        put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.3with1.2.840.10045.2.1", "SHA512withECDSA");
+
+        putSignatureImplClass("SHA1withRSA/PSS", "OpenSSLSignature$SHA1RSAPSS");
+        put("Alg.Alias.Signature.SHA1withRSAandMGF1", "SHA1withRSA/PSS");
+
+        putSignatureImplClass("SHA224withRSA/PSS", "OpenSSLSignature$SHA224RSAPSS");
+        put("Alg.Alias.Signature.SHA224withRSAandMGF1", "SHA224withRSA/PSS");
+
+        putSignatureImplClass("SHA256withRSA/PSS", "OpenSSLSignature$SHA256RSAPSS");
+        put("Alg.Alias.Signature.SHA256withRSAandMGF1", "SHA256withRSA/PSS");
+
+        putSignatureImplClass("SHA384withRSA/PSS", "OpenSSLSignature$SHA384RSAPSS");
+        put("Alg.Alias.Signature.SHA384withRSAandMGF1", "SHA384withRSA/PSS");
+
+        putSignatureImplClass("SHA512withRSA/PSS", "OpenSSLSignature$SHA512RSAPSS");
+        put("Alg.Alias.Signature.SHA512withRSAandMGF1", "SHA512withRSA/PSS");
+
+        /* === SecureRandom === */
+        /*
+         * We have to specify SHA1PRNG because various documentation mentions
+         * that algorithm by name instead of just recommending calling
+         * "new SecureRandom()"
+         */
+        put("SecureRandom.SHA1PRNG", PREFIX + "OpenSSLRandom");
+        put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
+
+        /* === Cipher === */
+        putRSACipherImplClass("RSA/ECB/NoPadding", "OpenSSLCipherRSA$Raw");
+        put("Alg.Alias.Cipher.RSA/None/NoPadding", "RSA/ECB/NoPadding");
+        putRSACipherImplClass("RSA/ECB/PKCS1Padding", "OpenSSLCipherRSA$PKCS1");
+        put("Alg.Alias.Cipher.RSA/None/PKCS1Padding", "RSA/ECB/PKCS1Padding");
+
+        putRSACipherImplClass("RSA/ECB/OAEPPadding", "OpenSSLCipherRSA$OAEP$SHA1");
+        put("Alg.Alias.Cipher.RSA/None/OAEPPadding", "RSA/ECB/OAEPPadding");
+        putRSACipherImplClass("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", "OpenSSLCipherRSA$OAEP$SHA1");
+        put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-1AndMGF1Padding",
+                "RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
+        putRSACipherImplClass(
+                "RSA/ECB/OAEPWithSHA-224AndMGF1Padding", "OpenSSLCipherRSA$OAEP$SHA224");
+        put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-224AndMGF1Padding",
+                "RSA/ECB/OAEPWithSHA-224AndMGF1Padding");
+        putRSACipherImplClass(
+                "RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "OpenSSLCipherRSA$OAEP$SHA256");
+        put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-256AndMGF1Padding",
+                "RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
+        putRSACipherImplClass(
+                "RSA/ECB/OAEPWithSHA-384AndMGF1Padding", "OpenSSLCipherRSA$OAEP$SHA384");
+        put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-384AndMGF1Padding",
+                "RSA/ECB/OAEPWithSHA-384AndMGF1Padding");
+        putRSACipherImplClass(
+                "RSA/ECB/OAEPWithSHA-512AndMGF1Padding", "OpenSSLCipherRSA$OAEP$SHA512");
+        put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-512AndMGF1Padding",
+                "RSA/ECB/OAEPWithSHA-512AndMGF1Padding");
+
+        /*
+         * OpenSSL only supports a subset of modes, so we'll name them
+         * explicitly here.
+         *
+         * Moreover, OpenSSL only supports PKCS#7 padding. PKCS#5 padding
+         * is also supported because it's a special case of PKCS#7 for 64-bit
+         * blocks. PKCS#5 technically supports only 64-bit blocks and won't
+         * produce the same result as PKCS#7 for blocks that are not 64 bits
+         * long. However, everybody assumes PKCS#7 when they say PKCS#5. For
+         * example, lots of code uses PKCS#5 with AES whose blocks are longer
+         * than 64 bits. We solve this confusion by making PKCS7Padding an
+         * alias for PKCS5Padding.
+         */
+        putSymmetricCipherImplClass("AES/ECB/NoPadding", "OpenSSLEvpCipherAES$AES$ECB$NoPadding");
+        putSymmetricCipherImplClass(
+                "AES/ECB/PKCS5Padding", "OpenSSLEvpCipherAES$AES$ECB$PKCS5Padding");
+        put("Alg.Alias.Cipher.AES/ECB/PKCS7Padding", "AES/ECB/PKCS5Padding");
+        putSymmetricCipherImplClass("AES/CBC/NoPadding", "OpenSSLEvpCipherAES$AES$CBC$NoPadding");
+        putSymmetricCipherImplClass(
+                "AES/CBC/PKCS5Padding", "OpenSSLEvpCipherAES$AES$CBC$PKCS5Padding");
+        put("Alg.Alias.Cipher.AES/CBC/PKCS7Padding", "AES/CBC/PKCS5Padding");
+        putSymmetricCipherImplClass("AES/CTR/NoPadding", "OpenSSLEvpCipherAES$AES$CTR");
+
+        putSymmetricCipherImplClass(
+                "AES_128/ECB/NoPadding", "OpenSSLEvpCipherAES$AES_128$ECB$NoPadding");
+        putSymmetricCipherImplClass(
+                "AES_128/ECB/PKCS5Padding", "OpenSSLEvpCipherAES$AES_128$ECB$PKCS5Padding");
+        put("Alg.Alias.Cipher.AES_128/ECB/PKCS7Padding", "AES_128/ECB/PKCS5Padding");
+        putSymmetricCipherImplClass(
+                "AES_128/CBC/NoPadding", "OpenSSLEvpCipherAES$AES_128$CBC$NoPadding");
+        putSymmetricCipherImplClass(
+                "AES_128/CBC/PKCS5Padding", "OpenSSLEvpCipherAES$AES_128$CBC$PKCS5Padding");
+        put("Alg.Alias.Cipher.AES_128/CBC/PKCS7Padding", "AES_128/CBC/PKCS5Padding");
+
+        put("Alg.Alias.Cipher.PBEWithHmacSHA1AndAES_128", "AES_128/CBC/PKCS5PADDING");
+        put("Alg.Alias.Cipher.PBEWithHmacSHA224AndAES_128", "AES_128/CBC/PKCS5PADDING");
+        put("Alg.Alias.Cipher.PBEWithHmacSHA256AndAES_128", "AES_128/CBC/PKCS5PADDING");
+        put("Alg.Alias.Cipher.PBEWithHmacSHA384AndAES_128", "AES_128/CBC/PKCS5PADDING");
+        put("Alg.Alias.Cipher.PBEWithHmacSHA512AndAES_128", "AES_128/CBC/PKCS5PADDING");
+
+        putSymmetricCipherImplClass(
+                "AES_256/ECB/NoPadding", "OpenSSLEvpCipherAES$AES_256$ECB$NoPadding");
+        putSymmetricCipherImplClass(
+                "AES_256/ECB/PKCS5Padding", "OpenSSLEvpCipherAES$AES_256$ECB$PKCS5Padding");
+        put("Alg.Alias.Cipher.AES_256/ECB/PKCS7Padding", "AES_256/ECB/PKCS5Padding");
+        putSymmetricCipherImplClass(
+                "AES_256/CBC/NoPadding", "OpenSSLEvpCipherAES$AES_256$CBC$NoPadding");
+        putSymmetricCipherImplClass(
+                "AES_256/CBC/PKCS5Padding", "OpenSSLEvpCipherAES$AES_256$CBC$PKCS5Padding");
+        put("Alg.Alias.Cipher.AES_256/CBC/PKCS7Padding", "AES_256/CBC/PKCS5Padding");
+
+        put("Alg.Alias.Cipher.PBEWithHmacSHA1AndAES_256", "AES_256/CBC/PKCS5PADDING");
+        put("Alg.Alias.Cipher.PBEWithHmacSHA224AndAES_256", "AES_256/CBC/PKCS5PADDING");
+        put("Alg.Alias.Cipher.PBEWithHmacSHA256AndAES_256", "AES_256/CBC/PKCS5PADDING");
+        put("Alg.Alias.Cipher.PBEWithHmacSHA384AndAES_256", "AES_256/CBC/PKCS5PADDING");
+        put("Alg.Alias.Cipher.PBEWithHmacSHA512AndAES_256", "AES_256/CBC/PKCS5PADDING");
+
+        putSymmetricCipherImplClass("DESEDE/CBC/NoPadding", "OpenSSLEvpCipherDESEDE$CBC$NoPadding");
+        putSymmetricCipherImplClass(
+                "DESEDE/CBC/PKCS5Padding", "OpenSSLEvpCipherDESEDE$CBC$PKCS5Padding");
+        put("Alg.Alias.Cipher.DESEDE/CBC/PKCS7Padding", "DESEDE/CBC/PKCS5Padding");
+
+        putSymmetricCipherImplClass("ARC4", "OpenSSLEvpCipherARC4");
+        put("Alg.Alias.Cipher.ARCFOUR", "ARC4");
+        put("Alg.Alias.Cipher.RC4", "ARC4");
+        put("Alg.Alias.Cipher.1.2.840.113549.3.4", "ARC4");
+        put("Alg.Alias.Cipher.OID.1.2.840.113549.3.4", "ARC4");
+
+        putSymmetricCipherImplClass("AES/GCM/NoPadding", "OpenSSLAeadCipherAES$GCM");
+        put("Alg.Alias.Cipher.GCM", "AES/GCM/NoPadding");
+        put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.6", "AES/GCM/NoPadding");
+        put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.26", "AES/GCM/NoPadding");
+        put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.46", "AES/GCM/NoPadding");
+        putSymmetricCipherImplClass("AES_128/GCM/NoPadding", "OpenSSLAeadCipherAES$GCM$AES_128");
+        putSymmetricCipherImplClass("AES_256/GCM/NoPadding", "OpenSSLAeadCipherAES$GCM$AES_256");
+
+        putSymmetricCipherImplClass("AES/GCM-SIV/NoPadding", "OpenSSLAeadCipherAES$GCM_SIV");
+        putSymmetricCipherImplClass(
+                "AES_128/GCM-SIV/NoPadding", "OpenSSLAeadCipherAES$GCM_SIV$AES_128");
+        putSymmetricCipherImplClass(
+                "AES_256/GCM-SIV/NoPadding", "OpenSSLAeadCipherAES$GCM_SIV$AES_256");
+
+        putSymmetricCipherImplClass("ChaCha20",
+                "OpenSSLCipherChaCha20");
+        putSymmetricCipherImplClass("ChaCha20/Poly1305/NoPadding", "OpenSSLAeadCipherChaCha20");
+        put("Alg.Alias.Cipher.ChaCha20-Poly1305", "ChaCha20/Poly1305/NoPadding");
+
+        /* === Mac === */
+
+        putMacImplClass("HmacMD5", "OpenSSLMac$HmacMD5");
+        put("Alg.Alias.Mac.1.3.6.1.5.5.8.1.1", "HmacMD5");
+        put("Alg.Alias.Mac.HMAC-MD5", "HmacMD5");
+        put("Alg.Alias.Mac.HMAC/MD5", "HmacMD5");
+
+        // PKCS#2 - iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2)
+        // http://www.oid-info.com/get/1.2.840.113549.2
+
+        // HMAC-SHA-1 PRF (7)
+        putMacImplClass("HmacSHA1", "OpenSSLMac$HmacSHA1");
+        put("Alg.Alias.Mac.1.2.840.113549.2.7", "HmacSHA1");
+        put("Alg.Alias.Mac.1.3.6.1.5.5.8.1.2", "HmacSHA1");
+        put("Alg.Alias.Mac.HMAC-SHA1", "HmacSHA1");
+        put("Alg.Alias.Mac.HMAC/SHA1", "HmacSHA1");
+
+        // id-hmacWithSHA224 (8)
+        putMacImplClass("HmacSHA224", "OpenSSLMac$HmacSHA224");
+        put("Alg.Alias.Mac.1.2.840.113549.2.8", "HmacSHA224");
+        put("Alg.Alias.Mac.HMAC-SHA224", "HmacSHA224");
+        put("Alg.Alias.Mac.HMAC/SHA224", "HmacSHA224");
+        put("Alg.Alias.Mac.PBEWITHHMACSHA224", "HmacSHA224");
+
+        // id-hmacWithSHA256 (9)
+        putMacImplClass("HmacSHA256", "OpenSSLMac$HmacSHA256");
+        put("Alg.Alias.Mac.1.2.840.113549.2.9", "HmacSHA256");
+        put("Alg.Alias.Mac.2.16.840.1.101.3.4.2.1", "HmacSHA256");
+        put("Alg.Alias.Mac.HMAC-SHA256", "HmacSHA256");
+        put("Alg.Alias.Mac.HMAC/SHA256", "HmacSHA256");
+        put("Alg.Alias.Mac.PBEWITHHMACSHA256", "HmacSHA256");
+
+        // id-hmacWithSHA384 (10)
+        putMacImplClass("HmacSHA384", "OpenSSLMac$HmacSHA384");
+        put("Alg.Alias.Mac.1.2.840.113549.2.10", "HmacSHA384");
+        put("Alg.Alias.Mac.HMAC-SHA384", "HmacSHA384");
+        put("Alg.Alias.Mac.HMAC/SHA384", "HmacSHA384");
+        put("Alg.Alias.Mac.PBEWITHHMACSHA384", "HmacSHA384");
+
+        // id-hmacWithSHA384 (11)
+        putMacImplClass("HmacSHA512", "OpenSSLMac$HmacSHA512");
+        put("Alg.Alias.Mac.1.2.840.113549.2.11", "HmacSHA512");
+        put("Alg.Alias.Mac.HMAC-SHA512", "HmacSHA512");
+        put("Alg.Alias.Mac.HMAC/SHA512", "HmacSHA512");
+        put("Alg.Alias.Mac.PBEWITHHMACSHA512", "HmacSHA512");
+
+        /* === Certificate === */
+
+        put("CertificateFactory.X509", PREFIX + "OpenSSLX509CertificateFactory");
+        put("Alg.Alias.CertificateFactory.X.509", "X509");
+    }
+
+    private void putMacImplClass(String algorithm, String className) {
+        // Accept only keys for which any of the following is true:
+        // * the key is from this provider (subclass of OpenSSLKeyHolder),
+        // * the key provides its key material in "RAW" encoding via Key.getEncoded.
+        String supportedKeyClasses = PREFIX + "OpenSSLKeyHolder";
+        String supportedKeyFormats = "RAW";
+        putImplClassWithKeyConstraints(
+                "Mac." + algorithm,
+                PREFIX + className,
+                supportedKeyClasses,
+                supportedKeyFormats);
+    }
+
+    private void putSymmetricCipherImplClass(String transformation, String className) {
+        // Accept only keys for which any of the following is true:
+        // * the key provides its key material in "RAW" encoding via Key.getEncoded.
+        String supportedKeyClasses = null; // ignored -- filtered based on encoding format only
+        String supportedKeyFormats = "RAW";
+        putImplClassWithKeyConstraints(
+                "Cipher." + transformation,
+                PREFIX + className,
+                supportedKeyClasses,
+                supportedKeyFormats);
+    }
+
+    private void putRSACipherImplClass(String transformation, String className) {
+        // Accept only keys for which any of the following is true:
+        // * the key is instance of OpenSSLRSAPrivateKey, RSAPrivateKey, OpenSSLRSAPublicKey, or
+        //   RSAPublicKey.
+        String supportedKeyClasses = PREFIX + "OpenSSLRSAPrivateKey"
+                + "|" + STANDARD_RSA_PRIVATE_KEY_INTERFACE_CLASS_NAME
+                + "|" + PREFIX + "OpenSSLRSAPublicKey"
+                + "|" + STANDARD_RSA_PUBLIC_KEY_INTERFACE_CLASS_NAME;
+        String supportedKeyFormats = null; // ignored -- filtered based on class only
+        putImplClassWithKeyConstraints(
+                "Cipher." + transformation,
+                PREFIX + className,
+                supportedKeyClasses,
+                supportedKeyFormats);
+    }
+
+    private void putSignatureImplClass(String algorithm, String className) {
+        // Accept only keys for which any of the following is true:
+        // * the key is from this provider (subclass of OpenSSLKeyHolder),
+        // * the key provides its key material in "PKCS#8" or "X.509" encodings via Key.getEncoded.
+        // * the key is a transparent private key (subclass of RSAPrivateKey or ECPrivateKey). For
+        //   some reason this provider's Signature implementation does not unconditionally accept
+        //   transparent public keys -- it only accepts them if they provide their key material in
+        //   encoded form (see above).
+        String supportedKeyClasses = PREFIX + "OpenSSLKeyHolder"
+                + "|" + STANDARD_RSA_PRIVATE_KEY_INTERFACE_CLASS_NAME
+                + "|" + STANDARD_EC_PRIVATE_KEY_INTERFACE_CLASS_NAME
+                + "|" + STANDARD_RSA_PUBLIC_KEY_INTERFACE_CLASS_NAME;
+        String supportedKeyFormats = "PKCS#8|X.509";
+        putImplClassWithKeyConstraints(
+                "Signature." + algorithm,
+                PREFIX + className,
+                supportedKeyClasses,
+                supportedKeyFormats);
+    }
+
+    private void putRAWRSASignatureImplClass(String className) {
+        // Accept only keys for which any of the following is true:
+        // * the key is instance of OpenSSLRSAPrivateKey, RSAPrivateKey, OpenSSLRSAPublicKey, or
+        //   RSAPublicKey.
+        String supportedKeyClasses = PREFIX + "OpenSSLRSAPrivateKey"
+                + "|" + STANDARD_RSA_PRIVATE_KEY_INTERFACE_CLASS_NAME
+                + "|" + PREFIX + "OpenSSLRSAPublicKey"
+                + "|" + STANDARD_RSA_PUBLIC_KEY_INTERFACE_CLASS_NAME;
+        String supportedKeyFormats = null; // ignored -- filtered based on class only
+        putImplClassWithKeyConstraints(
+                "Signature.NONEwithRSA",
+                PREFIX + className,
+                supportedKeyClasses,
+                supportedKeyFormats);
+    }
+
+    private void putECDHKeyAgreementImplClass(String className) {
+        // Accept only keys for which any of the following is true:
+        // * the key is from this provider (subclass of OpenSSLKeyHolder),
+        // * the key provides its key material in "PKCS#8" encoding via Key.getEncoded.
+        // * the key is a transparent EC private key (subclass of ECPrivateKey).
+        String supportedKeyClasses = PREFIX + "OpenSSLKeyHolder"
+                + "|" + STANDARD_EC_PRIVATE_KEY_INTERFACE_CLASS_NAME;
+        String supportedKeyFormats = "PKCS#8";
+        putImplClassWithKeyConstraints(
+                "KeyAgreement.ECDH",
+                PREFIX + className,
+                supportedKeyClasses,
+                supportedKeyFormats);
+    }
+
+    private void putImplClassWithKeyConstraints(String typeAndAlgName,
+            String fullyQualifiedClassName,
+            String supportedKeyClasses,
+            String supportedKeyFormats) {
+        put(typeAndAlgName, fullyQualifiedClassName);
+        if (supportedKeyClasses != null) {
+            put(typeAndAlgName + " SupportedKeyClasses", supportedKeyClasses);
+        }
+        if (supportedKeyFormats != null) {
+            put(typeAndAlgName + " SupportedKeyFormats", supportedKeyFormats);
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRSAKeyFactory.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRSAKeyFactory.java
new file mode 100644
index 0000000..4565328
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRSAKeyFactory.java
@@ -0,0 +1,258 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactorySpi;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+/**
+ * An implementation of {@link java.security.KeyFactory} which uses BoringSSL to perform all the
+ * operations.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public final class OpenSSLRSAKeyFactory extends KeyFactorySpi {
+    @libcore.api.IntraCoreApi
+    public OpenSSLRSAKeyFactory() {}
+
+    @Override
+    protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException {
+        if (keySpec == null) {
+            throw new InvalidKeySpecException("keySpec == null");
+        }
+
+        if (keySpec instanceof RSAPublicKeySpec) {
+            return new OpenSSLRSAPublicKey((RSAPublicKeySpec) keySpec);
+        } else if (keySpec instanceof X509EncodedKeySpec) {
+            return OpenSSLKey.getPublicKey((X509EncodedKeySpec) keySpec, NativeConstants.EVP_PKEY_RSA);
+        }
+        throw new InvalidKeySpecException("Must use RSAPublicKeySpec or X509EncodedKeySpec; was "
+                + keySpec.getClass().getName());
+    }
+
+    @Override
+    protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException {
+        if (keySpec == null) {
+            throw new InvalidKeySpecException("keySpec == null");
+        }
+
+        if (keySpec instanceof RSAPrivateCrtKeySpec) {
+            return new OpenSSLRSAPrivateCrtKey((RSAPrivateCrtKeySpec) keySpec);
+        } else if (keySpec instanceof RSAPrivateKeySpec) {
+            return new OpenSSLRSAPrivateKey((RSAPrivateKeySpec) keySpec);
+        } else if (keySpec instanceof PKCS8EncodedKeySpec) {
+            return OpenSSLKey.getPrivateKey((PKCS8EncodedKeySpec) keySpec,
+                    NativeConstants.EVP_PKEY_RSA);
+        }
+        throw new InvalidKeySpecException("Must use RSAPublicKeySpec or PKCS8EncodedKeySpec; was "
+                + keySpec.getClass().getName());
+    }
+
+    @Override
+    protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
+            throws InvalidKeySpecException {
+        if (key == null) {
+            throw new InvalidKeySpecException("key == null");
+        }
+
+        if (keySpec == null) {
+            throw new InvalidKeySpecException("keySpec == null");
+        }
+
+        if (!"RSA".equals(key.getAlgorithm())) {
+            throw new InvalidKeySpecException("Key must be a RSA key");
+        }
+
+        if (key instanceof RSAPublicKey && RSAPublicKeySpec.class.isAssignableFrom(keySpec)) {
+            RSAPublicKey rsaKey = (RSAPublicKey) key;
+            @SuppressWarnings("unchecked")
+            T result = (T) new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent());
+            return result;
+        } else if (key instanceof PublicKey && RSAPublicKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"X.509".equals(key.getFormat()) || encoded == null) {
+                throw new InvalidKeySpecException("Not a valid X.509 encoding");
+            }
+            RSAPublicKey rsaKey =
+                    (RSAPublicKey) engineGeneratePublic(new X509EncodedKeySpec(encoded));
+            @SuppressWarnings("unchecked")
+            T result = (T) new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent());
+            return result;
+        } else if (key instanceof RSAPrivateCrtKey
+                && RSAPrivateCrtKeySpec.class.isAssignableFrom(keySpec)) {
+            RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;
+            @SuppressWarnings("unchecked")
+            T result = (T) new RSAPrivateCrtKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent(),
+                    rsaKey.getPrivateExponent(), rsaKey.getPrimeP(), rsaKey.getPrimeQ(),
+                    rsaKey.getPrimeExponentP(), rsaKey.getPrimeExponentQ(),
+                    rsaKey.getCrtCoefficient());
+            return result;
+        } else if (key instanceof RSAPrivateCrtKey
+                && RSAPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+            RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;
+            @SuppressWarnings("unchecked")
+            T result = (T) new RSAPrivateKeySpec(rsaKey.getModulus(), rsaKey.getPrivateExponent());
+            return result;
+        } else if (key instanceof RSAPrivateKey
+                && RSAPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+            RSAPrivateKey rsaKey = (RSAPrivateKey) key;
+            @SuppressWarnings("unchecked")
+            T result = (T) new RSAPrivateKeySpec(rsaKey.getModulus(), rsaKey.getPrivateExponent());
+            return result;
+        } else if (key instanceof PrivateKey
+                && RSAPrivateCrtKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"PKCS#8".equals(key.getFormat()) || encoded == null) {
+                throw new InvalidKeySpecException("Not a valid PKCS#8 encoding");
+            }
+            RSAPrivateKey privKey =
+                    (RSAPrivateKey) engineGeneratePrivate(new PKCS8EncodedKeySpec(encoded));
+            if (privKey instanceof RSAPrivateCrtKey) {
+                RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) privKey;
+                @SuppressWarnings("unchecked")
+                T result = (T) new RSAPrivateCrtKeySpec(rsaKey.getModulus(),
+                        rsaKey.getPublicExponent(), rsaKey.getPrivateExponent(), rsaKey.getPrimeP(),
+                        rsaKey.getPrimeQ(), rsaKey.getPrimeExponentP(), rsaKey.getPrimeExponentQ(),
+                        rsaKey.getCrtCoefficient());
+                return result;
+            } else {
+                throw new InvalidKeySpecException("Encoded key is not an RSAPrivateCrtKey");
+            }
+        } else if (key instanceof PrivateKey && RSAPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"PKCS#8".equals(key.getFormat()) || encoded == null) {
+                throw new InvalidKeySpecException("Not a valid PKCS#8 encoding");
+            }
+            RSAPrivateKey rsaKey =
+                    (RSAPrivateKey) engineGeneratePrivate(new PKCS8EncodedKeySpec(encoded));
+            @SuppressWarnings("unchecked")
+            T result = (T) new RSAPrivateKeySpec(rsaKey.getModulus(), rsaKey.getPrivateExponent());
+            return result;
+        } else if (key instanceof PrivateKey
+                && PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"PKCS#8".equals(key.getFormat())) {
+                throw new InvalidKeySpecException("Encoding type must be PKCS#8; was "
+                        + key.getFormat());
+            } else if (encoded == null) {
+                throw new InvalidKeySpecException("Key is not encodable");
+            }
+            @SuppressWarnings("unchecked") T result = (T) new PKCS8EncodedKeySpec(encoded);
+            return result;
+        } else if (key instanceof PublicKey && X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+            final byte[] encoded = key.getEncoded();
+            if (!"X.509".equals(key.getFormat())) {
+                throw new InvalidKeySpecException("Encoding type must be X.509; was "
+                        + key.getFormat());
+            } else if (encoded == null) {
+                throw new InvalidKeySpecException("Key is not encodable");
+            }
+            @SuppressWarnings("unchecked") T result = (T) new X509EncodedKeySpec(encoded);
+            return result;
+        } else {
+            throw new InvalidKeySpecException("Unsupported key type and key spec combination; key="
+                    + key.getClass().getName() + ", keySpec=" + keySpec.getName());
+        }
+    }
+
+    @Override
+    protected Key engineTranslateKey(Key key) throws InvalidKeyException {
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        }
+
+        if ((key instanceof OpenSSLRSAPublicKey) || (key instanceof OpenSSLRSAPrivateKey)) {
+            return key;
+        } else if (key instanceof RSAPublicKey) {
+            RSAPublicKey rsaKey = (RSAPublicKey) key;
+
+            try {
+                return engineGeneratePublic(new RSAPublicKeySpec(rsaKey.getModulus(),
+                        rsaKey.getPublicExponent()));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else if (key instanceof RSAPrivateCrtKey) {
+            RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;
+            BigInteger modulus = rsaKey.getModulus();
+            BigInteger publicExponent = rsaKey.getPublicExponent();
+            BigInteger privateExponent = rsaKey.getPrivateExponent();
+            BigInteger primeP = rsaKey.getPrimeP();
+            BigInteger primeQ = rsaKey.getPrimeQ();
+            BigInteger primeExponentP = rsaKey.getPrimeExponentP();
+            BigInteger primeExponentQ = rsaKey.getPrimeExponentQ();
+            BigInteger crtCoefficient = rsaKey.getCrtCoefficient();
+
+            try {
+                return engineGeneratePrivate(new RSAPrivateCrtKeySpec(modulus, publicExponent,
+                        privateExponent, primeP, primeQ, primeExponentP, primeExponentQ,
+                        crtCoefficient));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else if (key instanceof RSAPrivateKey) {
+            RSAPrivateKey rsaKey = (RSAPrivateKey) key;
+            BigInteger modulus = rsaKey.getModulus();
+            BigInteger privateExponent = rsaKey.getPrivateExponent();
+
+            try {
+                return engineGeneratePrivate(new RSAPrivateKeySpec(modulus, privateExponent));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else if ((key instanceof PrivateKey) && "PKCS#8".equals(key.getFormat())) {
+            byte[] encoded = key.getEncoded();
+            if (encoded == null) {
+                throw new InvalidKeyException("Key does not support encoding");
+            }
+            try {
+                return engineGeneratePrivate(new PKCS8EncodedKeySpec(encoded));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else if ((key instanceof PublicKey) && "X.509".equals(key.getFormat())) {
+            byte[] encoded = key.getEncoded();
+            if (encoded == null) {
+                throw new InvalidKeyException("Key does not support encoding");
+            }
+            try {
+                return engineGeneratePublic(new X509EncodedKeySpec(encoded));
+            } catch (InvalidKeySpecException e) {
+                throw new InvalidKeyException(e);
+            }
+        } else {
+            throw new InvalidKeyException("Key must be an RSA public or private key; was "
+                    + key.getClass().getName());
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRSAKeyPairGenerator.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRSAKeyPairGenerator.java
new file mode 100644
index 0000000..4501a49
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRSAKeyPairGenerator.java
@@ -0,0 +1,85 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPair;
+import java.security.KeyPairGeneratorSpi;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+
+/**
+ * An implementation of {@link java.security.KeyPairGenerator} which uses BoringSSL to perform all
+ * the operations.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public final class OpenSSLRSAKeyPairGenerator extends KeyPairGeneratorSpi {
+    /**
+     * Default modulus size is 0x10001 (65537)
+     */
+    private byte[] publicExponent = new byte[] {
+            0x01, 0x00, 0x01
+    };
+
+    /**
+     * Default RSA key size 2048 bits.
+     */
+    private int modulusBits = 2048;
+
+    @libcore.api.IntraCoreApi
+    public OpenSSLRSAKeyPairGenerator() {}
+
+    @Override
+    public KeyPair generateKeyPair() {
+        final OpenSSLKey key = new OpenSSLKey(NativeCrypto.RSA_generate_key_ex(modulusBits,
+                publicExponent));
+
+        PrivateKey privKey = OpenSSLRSAPrivateKey.getInstance(key);
+        PublicKey pubKey = new OpenSSLRSAPublicKey(key);
+
+        return new KeyPair(pubKey, privKey);
+    }
+
+    @Override
+    public void initialize(int keysize, SecureRandom random) {
+        this.modulusBits = keysize;
+    }
+
+    @Override
+    public void initialize(AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidAlgorithmParameterException {
+        if (!(params instanceof RSAKeyGenParameterSpec)) {
+            throw new InvalidAlgorithmParameterException("Only RSAKeyGenParameterSpec supported");
+        }
+
+        RSAKeyGenParameterSpec spec = (RSAKeyGenParameterSpec) params;
+
+        final BigInteger publicExponent = spec.getPublicExponent();
+        if (publicExponent != null) {
+            this.publicExponent = publicExponent.toByteArray();
+        }
+
+        this.modulusBits = spec.getKeysize();
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRSAPrivateCrtKey.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRSAPrivateCrtKey.java
new file mode 100644
index 0000000..22188bc
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRSAPrivateCrtKey.java
@@ -0,0 +1,285 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPrivateCrtKeySpec;
+
+/**
+ * An implementation of {@link java.security.PrivateKey} for RSA keys which uses BoringSSL to
+ * perform all the operations.
+ */
+final class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSAPrivateCrtKey {
+    private static final long serialVersionUID = 3785291944868707197L;
+
+    private BigInteger publicExponent;
+
+    private BigInteger primeP;
+
+    private BigInteger primeQ;
+
+    private BigInteger primeExponentP;
+
+    private BigInteger primeExponentQ;
+
+    private BigInteger crtCoefficient;
+
+    OpenSSLRSAPrivateCrtKey(OpenSSLKey key) {
+        super(key);
+    }
+
+    OpenSSLRSAPrivateCrtKey(OpenSSLKey key, byte[][] params) {
+        super(key, params);
+    }
+
+    OpenSSLRSAPrivateCrtKey(RSAPrivateCrtKeySpec rsaKeySpec) throws InvalidKeySpecException {
+        super(init(rsaKeySpec));
+    }
+
+    private static OpenSSLKey init(RSAPrivateCrtKeySpec rsaKeySpec) throws InvalidKeySpecException {
+        BigInteger modulus = rsaKeySpec.getModulus();
+        BigInteger privateExponent = rsaKeySpec.getPrivateExponent();
+
+        if (modulus == null) {
+            throw new InvalidKeySpecException("modulus == null");
+        } else if (privateExponent == null) {
+            throw new InvalidKeySpecException("privateExponent == null");
+        }
+
+        try {
+            /*
+             * OpenSSL uses the public modulus to do RSA blinding. If
+             * the public modulus is not available, the call to
+             * EVP_PKEY_new_RSA will turn off blinding for this key
+             * instance.
+             */
+            final BigInteger publicExponent = rsaKeySpec.getPublicExponent();
+            final BigInteger primeP = rsaKeySpec.getPrimeP();
+            final BigInteger primeQ = rsaKeySpec.getPrimeQ();
+            final BigInteger primeExponentP = rsaKeySpec.getPrimeExponentP();
+            final BigInteger primeExponentQ = rsaKeySpec.getPrimeExponentQ();
+            final BigInteger crtCoefficient = rsaKeySpec.getCrtCoefficient();
+
+            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
+                    modulus.toByteArray(),
+                    publicExponent == null ? null : publicExponent.toByteArray(),
+                    privateExponent.toByteArray(),
+                    primeP == null ? null : primeP.toByteArray(),
+                    primeQ == null ? null : primeQ.toByteArray(),
+                    primeExponentP == null ? null : primeExponentP.toByteArray(),
+                    primeExponentQ == null ? null : primeExponentQ.toByteArray(),
+                    crtCoefficient == null ? null : crtCoefficient.toByteArray()));
+        } catch (Exception e) {
+            throw new InvalidKeySpecException(e);
+        }
+    }
+
+    static OpenSSLKey getInstance(RSAPrivateCrtKey rsaPrivateKey) throws InvalidKeyException {
+        /**
+         * If the key is not encodable (PKCS11-like key), then wrap it and use
+         * JNI upcalls to satisfy requests.
+         */
+        if (rsaPrivateKey.getFormat() == null) {
+            return wrapPlatformKey(rsaPrivateKey);
+        }
+
+        BigInteger modulus = rsaPrivateKey.getModulus();
+        BigInteger privateExponent = rsaPrivateKey.getPrivateExponent();
+
+        if (modulus == null) {
+            throw new InvalidKeyException("modulus == null");
+        } else if (privateExponent == null) {
+            throw new InvalidKeyException("privateExponent == null");
+        }
+
+        try {
+            /*
+             * OpenSSL uses the public modulus to do RSA blinding. If
+             * the public modulus is not available, the call to
+             * EVP_PKEY_new_RSA will turn off blinding for this key
+             * instance.
+             */
+            final BigInteger publicExponent = rsaPrivateKey.getPublicExponent();
+            final BigInteger primeP = rsaPrivateKey.getPrimeP();
+            final BigInteger primeQ = rsaPrivateKey.getPrimeQ();
+            final BigInteger primeExponentP = rsaPrivateKey.getPrimeExponentP();
+            final BigInteger primeExponentQ = rsaPrivateKey.getPrimeExponentQ();
+            final BigInteger crtCoefficient = rsaPrivateKey.getCrtCoefficient();
+
+            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
+                    modulus.toByteArray(),
+                    publicExponent == null ? null : publicExponent.toByteArray(),
+                    privateExponent.toByteArray(),
+                    primeP == null ? null : primeP.toByteArray(),
+                    primeQ == null ? null : primeQ.toByteArray(),
+                    primeExponentP == null ? null : primeExponentP.toByteArray(),
+                    primeExponentQ == null ? null : primeExponentQ.toByteArray(),
+                    crtCoefficient == null ? null : crtCoefficient.toByteArray()));
+        } catch (Exception e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    @Override
+    synchronized void readParams(byte[][] params) {
+        super.readParams(params);
+        // params[0] read in super.readParams
+        if (params[1] != null) {
+            publicExponent = new BigInteger(params[1]);
+        }
+        // params[2] read in super.readParams
+        if (params[3] != null) {
+            primeP = new BigInteger(params[3]);
+        }
+        if (params[4] != null) {
+            primeQ = new BigInteger(params[4]);
+        }
+        if (params[5] != null) {
+            primeExponentP = new BigInteger(params[5]);
+        }
+        if (params[6] != null) {
+            primeExponentQ = new BigInteger(params[6]);
+        }
+        if (params[7] != null) {
+            crtCoefficient = new BigInteger(params[7]);
+        }
+    }
+
+    @Override
+    public BigInteger getPublicExponent() {
+        ensureReadParams();
+        return publicExponent;
+    }
+
+    @Override
+    public BigInteger getPrimeP() {
+        ensureReadParams();
+        return primeP;
+    }
+
+    @Override
+    public BigInteger getPrimeQ() {
+        ensureReadParams();
+        return primeQ;
+    }
+
+    @Override
+    public BigInteger getPrimeExponentP() {
+        ensureReadParams();
+        return primeExponentP;
+    }
+
+    @Override
+    public BigInteger getPrimeExponentQ() {
+        ensureReadParams();
+        return primeExponentQ;
+    }
+
+    @Override
+    public BigInteger getCrtCoefficient() {
+        ensureReadParams();
+        return crtCoefficient;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (o instanceof OpenSSLRSAPrivateKey) {
+            OpenSSLRSAPrivateKey other = (OpenSSLRSAPrivateKey) o;
+            return getOpenSSLKey().equals(other.getOpenSSLKey());
+        }
+
+        if (o instanceof RSAPrivateCrtKey) {
+            ensureReadParams();
+            RSAPrivateCrtKey other = (RSAPrivateCrtKey) o;
+
+            return getModulus().equals(other.getModulus())
+                    && publicExponent.equals(other.getPublicExponent())
+                    && getPrivateExponent().equals(other.getPrivateExponent())
+                    && primeP.equals(other.getPrimeP()) && primeQ.equals(other.getPrimeQ())
+                    && primeExponentP.equals(other.getPrimeExponentP())
+                    && primeExponentQ.equals(other.getPrimeExponentQ())
+                    && crtCoefficient.equals(other.getCrtCoefficient());
+        } else if (o instanceof RSAPrivateKey) {
+            ensureReadParams();
+            RSAPrivateKey other = (RSAPrivateKey) o;
+
+            return getModulus().equals(other.getModulus())
+                    && getPrivateExponent().equals(other.getPrivateExponent());
+        }
+
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        int hashCode = super.hashCode();
+        if (publicExponent != null) {
+            hashCode ^= publicExponent.hashCode();
+        }
+        return hashCode;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateCrtKey{");
+
+        ensureReadParams();
+        sb.append("modulus=");
+        sb.append(getModulus().toString(16));
+
+        if (publicExponent != null) {
+            sb.append(',');
+            sb.append("publicExponent=");
+            sb.append(publicExponent.toString(16));
+        }
+
+        sb.append('}');
+        return sb.toString();
+    }
+
+    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
+        stream.defaultReadObject();
+
+        key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
+                modulus.toByteArray(),
+                publicExponent == null ? null : publicExponent.toByteArray(),
+                privateExponent.toByteArray(),
+                primeP == null ? null : primeP.toByteArray(),
+                primeQ == null ? null : primeQ.toByteArray(),
+                primeExponentP == null ? null : primeExponentP.toByteArray(),
+                primeExponentQ == null ? null : primeExponentQ.toByteArray(),
+                crtCoefficient == null ? null : crtCoefficient.toByteArray()));
+        fetchedParams = true;
+    }
+
+    private void writeObject(ObjectOutputStream stream) throws IOException {
+        ensureReadParams();
+        stream.defaultWriteObject();
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRSAPrivateKey.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRSAPrivateKey.java
new file mode 100644
index 0000000..bb34171
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRSAPrivateKey.java
@@ -0,0 +1,278 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.RSAKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPrivateKeySpec;
+
+/**
+ * An implementation of {@link java.security.PrivateKey} for RSA keys which uses BoringSSL to
+ * perform all the operations.
+ */
+class OpenSSLRSAPrivateKey implements RSAPrivateKey, OpenSSLKeyHolder {
+    private static final long serialVersionUID = 4872170254439578735L;
+
+    transient OpenSSLKey key;
+
+    transient boolean fetchedParams;
+
+    BigInteger modulus;
+
+    BigInteger privateExponent;
+
+    OpenSSLRSAPrivateKey(OpenSSLKey key) {
+        this.key = key;
+    }
+
+    OpenSSLRSAPrivateKey(OpenSSLKey key, byte[][] params) {
+        this(key);
+        readParams(params);
+        fetchedParams = true;
+    }
+
+    @Override
+    public OpenSSLKey getOpenSSLKey() {
+        return key;
+    }
+
+    public OpenSSLRSAPrivateKey(RSAPrivateKeySpec rsaKeySpec) throws InvalidKeySpecException {
+        this(init(rsaKeySpec));
+    }
+
+    private static OpenSSLKey init(RSAPrivateKeySpec rsaKeySpec) throws InvalidKeySpecException {
+        final BigInteger modulus = rsaKeySpec.getModulus();
+        final BigInteger privateExponent = rsaKeySpec.getPrivateExponent();
+
+        if (modulus == null) {
+            throw new InvalidKeySpecException("modulus == null");
+        } else if (privateExponent == null) {
+            throw new InvalidKeySpecException("privateExponent == null");
+        }
+
+        try {
+            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
+                    modulus.toByteArray(),
+                    null,
+                    privateExponent.toByteArray(),
+                    null,
+                    null,
+                    null,
+                    null,
+                    null));
+        } catch (Exception e) {
+            throw new InvalidKeySpecException(e);
+        }
+    }
+
+    static OpenSSLRSAPrivateKey getInstance(OpenSSLKey key) {
+        byte[][] params = NativeCrypto.get_RSA_private_params(key.getNativeRef());
+        if (params[1] != null) {
+            return new OpenSSLRSAPrivateCrtKey(key, params);
+        }
+        return new OpenSSLRSAPrivateKey(key, params);
+    }
+
+    static OpenSSLKey wrapPlatformKey(RSAPrivateKey rsaPrivateKey)
+            throws InvalidKeyException {
+        OpenSSLKey wrapper = Platform.wrapRsaKey(rsaPrivateKey);
+        if (wrapper != null) {
+            return wrapper;
+        }
+        return new OpenSSLKey(NativeCrypto.getRSAPrivateKeyWrapper(rsaPrivateKey, rsaPrivateKey
+                .getModulus().toByteArray()), true);
+    }
+
+    /**
+     * Wraps the provided private key for use in the TLS/SSL stack only. Sign/decrypt operations
+     * using the key will be delegated to the {@code Signature}/{@code Cipher} implementation of the
+     * provider which accepts the key.
+     */
+    static OpenSSLKey wrapJCAPrivateKeyForTLSStackOnly(PrivateKey privateKey,
+            PublicKey publicKey) throws InvalidKeyException {
+        BigInteger modulus = null;
+        if (privateKey instanceof RSAKey) {
+            modulus = ((RSAKey) privateKey).getModulus();
+        } else if (publicKey instanceof RSAKey) {
+            modulus = ((RSAKey) publicKey).getModulus();
+        }
+        if (modulus == null) {
+            throw new InvalidKeyException("RSA modulus not available. Private: " + privateKey
+                    + ", public: " + publicKey);
+        }
+        return new OpenSSLKey(
+                NativeCrypto.getRSAPrivateKeyWrapper(privateKey, modulus.toByteArray()), true);
+    }
+
+    static OpenSSLKey getInstance(RSAPrivateKey rsaPrivateKey) throws InvalidKeyException {
+        /**
+         * If the key is not encodable (PKCS11-like key), then wrap it and use
+         * JNI upcalls to satisfy requests.
+         */
+        if (rsaPrivateKey.getFormat() == null) {
+            return wrapPlatformKey(rsaPrivateKey);
+        }
+
+        final BigInteger modulus = rsaPrivateKey.getModulus();
+        final BigInteger privateExponent = rsaPrivateKey.getPrivateExponent();
+
+        if (modulus == null) {
+            throw new InvalidKeyException("modulus == null");
+        } else if (privateExponent == null) {
+            throw new InvalidKeyException("privateExponent == null");
+        }
+
+        try {
+            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
+                    modulus.toByteArray(),
+                    null,
+                    privateExponent.toByteArray(),
+                    null,
+                    null,
+                    null,
+                    null,
+                    null));
+        } catch (Exception e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    synchronized final void ensureReadParams() {
+        if (fetchedParams) {
+            return;
+        }
+        readParams(NativeCrypto.get_RSA_private_params(key.getNativeRef()));
+        fetchedParams = true;
+    }
+
+    void readParams(byte[][] params) {
+        if (params[0] == null) {
+            throw new NullPointerException("modulus == null");
+        } else if (params[2] == null) {
+            throw new NullPointerException("privateExponent == null");
+        }
+
+        modulus = new BigInteger(params[0]);
+
+        // ENGINE-based keys are not guaranteed to have a private exponent.
+        if (params[2] != null) {
+            privateExponent = new BigInteger(params[2]);
+        }
+    }
+
+    @Override
+    public final BigInteger getPrivateExponent() {
+        ensureReadParams();
+        return privateExponent;
+    }
+
+    @Override
+    public final BigInteger getModulus() {
+        ensureReadParams();
+        return modulus;
+    }
+
+    @Override
+    public final byte[] getEncoded() {
+        return NativeCrypto.EVP_marshal_private_key(key.getNativeRef());
+    }
+
+    @Override
+    public final String getFormat() {
+        return "PKCS#8";
+    }
+
+    @Override
+    public final String getAlgorithm() {
+        return "RSA";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (o instanceof OpenSSLRSAPrivateKey) {
+            OpenSSLRSAPrivateKey other = (OpenSSLRSAPrivateKey) o;
+            return key.equals(other.getOpenSSLKey());
+        }
+
+        if (o instanceof RSAPrivateKey) {
+            ensureReadParams();
+            RSAPrivateKey other = (RSAPrivateKey) o;
+
+            return modulus.equals(other.getModulus())
+                    && privateExponent.equals(other.getPrivateExponent());
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        ensureReadParams();
+        int hash = 1;
+
+        hash = hash * 3 + modulus.hashCode();
+        if (privateExponent != null) {
+            hash = hash * 7 + privateExponent.hashCode();
+        }
+
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateKey{");
+
+        ensureReadParams();
+        sb.append("modulus=");
+        sb.append(modulus.toString(16));
+
+        return sb.toString();
+    }
+
+    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
+        stream.defaultReadObject();
+
+        key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
+                modulus.toByteArray(),
+                null,
+                privateExponent.toByteArray(),
+                null,
+                null,
+                null,
+                null,
+                null));
+        fetchedParams = true;
+    }
+
+    private void writeObject(ObjectOutputStream stream) throws IOException {
+        ensureReadParams();
+        stream.defaultWriteObject();
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRSAPublicKey.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRSAPublicKey.java
new file mode 100644
index 0000000..8821fd0
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRSAPublicKey.java
@@ -0,0 +1,196 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPublicKeySpec;
+
+/**
+ * An implementation of {@link java.security.PublicKey} for RSA keys which uses BoringSSL to
+ * perform all the operations.
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public class OpenSSLRSAPublicKey implements RSAPublicKey, OpenSSLKeyHolder {
+    private static final long serialVersionUID = 123125005824688292L;
+
+    private transient OpenSSLKey key;
+
+    private BigInteger publicExponent;
+
+    private BigInteger modulus;
+
+    private transient boolean fetchedParams;
+
+    OpenSSLRSAPublicKey(OpenSSLKey key) {
+        this.key = key;
+    }
+
+    @Override
+    public OpenSSLKey getOpenSSLKey() {
+        return key;
+    }
+
+    OpenSSLRSAPublicKey(RSAPublicKeySpec spec) throws InvalidKeySpecException {
+        try {
+            key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
+                    spec.getModulus().toByteArray(),
+                    spec.getPublicExponent().toByteArray(),
+                    null,
+                    null,
+                    null,
+                    null,
+                    null,
+                    null));
+        } catch (Exception e) {
+            throw new InvalidKeySpecException(e);
+        }
+    }
+
+    static OpenSSLKey getInstance(RSAPublicKey rsaPublicKey) throws InvalidKeyException {
+        try {
+            return new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
+                    rsaPublicKey.getModulus().toByteArray(),
+                    rsaPublicKey.getPublicExponent().toByteArray(),
+                    null,
+                    null,
+                    null,
+                    null,
+                    null,
+                    null));
+        } catch (Exception e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return "RSA";
+    }
+
+    @Override
+    public String getFormat() {
+        return "X.509";
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        return NativeCrypto.EVP_marshal_public_key(key.getNativeRef());
+    }
+
+    private synchronized void ensureReadParams() {
+        if (fetchedParams) {
+            return;
+        }
+
+        byte[][] params = NativeCrypto.get_RSA_public_params(key.getNativeRef());
+        modulus = new BigInteger(params[0]);
+        publicExponent = new BigInteger(params[1]);
+
+        fetchedParams = true;
+    }
+
+    @Override
+    public BigInteger getModulus() {
+        ensureReadParams();
+        return modulus;
+    }
+
+    @Override
+    public BigInteger getPublicExponent() {
+        ensureReadParams();
+        return publicExponent;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (o instanceof OpenSSLRSAPublicKey) {
+            OpenSSLRSAPublicKey other = (OpenSSLRSAPublicKey) o;
+
+            /*
+             * We can shortcut the true case, but it still may be equivalent but
+             * different copies.
+             */
+            if (key.equals(other.getOpenSSLKey())) {
+                return true;
+            }
+        }
+
+        if (!(o instanceof RSAPublicKey)) {
+            return false;
+        }
+
+        ensureReadParams();
+
+        RSAPublicKey other = (RSAPublicKey) o;
+        return modulus.equals(other.getModulus())
+                && publicExponent.equals(other.getPublicExponent());
+    }
+
+    @Override
+    public int hashCode() {
+        ensureReadParams();
+
+        return modulus.hashCode() ^ publicExponent.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        ensureReadParams();
+
+        final StringBuilder sb = new StringBuilder("OpenSSLRSAPublicKey{");
+        sb.append("modulus=");
+        sb.append(modulus.toString(16));
+        sb.append(',');
+        sb.append("publicExponent=");
+        sb.append(publicExponent.toString(16));
+        sb.append('}');
+
+        return sb.toString();
+    }
+
+    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
+        stream.defaultReadObject();
+
+        key = new OpenSSLKey(NativeCrypto.EVP_PKEY_new_RSA(
+                modulus.toByteArray(),
+                publicExponent.toByteArray(),
+                null,
+                null,
+                null,
+                null,
+                null,
+                null));
+        fetchedParams = true;
+    }
+
+    private void writeObject(ObjectOutputStream stream) throws IOException {
+        ensureReadParams();
+        stream.defaultWriteObject();
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRandom.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRandom.java
new file mode 100644
index 0000000..69d7309
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLRandom.java
@@ -0,0 +1,54 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.Serializable;
+import java.security.SecureRandomSpi;
+
+/**
+ * Implements {@link java.security.SecureRandom} using BoringSSL's RAND interface.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public final class OpenSSLRandom extends SecureRandomSpi implements Serializable {
+    private static final long serialVersionUID = 8506210602917522861L;
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.IntraCoreApi
+    public OpenSSLRandom() {}
+
+    @Override
+    protected void engineSetSeed(byte[] seed) {
+        if (seed == null) {
+            throw new NullPointerException("seed == null");
+        }
+    }
+
+    @Override
+    protected void engineNextBytes(byte[] bytes) {
+        NativeCrypto.RAND_bytes(bytes);
+    }
+
+    @Override
+    protected byte[] engineGenerateSeed(int numBytes) {
+        byte[] output = new byte[numBytes];
+        NativeCrypto.RAND_bytes(output);
+        return output;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLServerSocketFactoryImpl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLServerSocketFactoryImpl.java
new file mode 100644
index 0000000..540d7e4
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLServerSocketFactoryImpl.java
@@ -0,0 +1,104 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.security.KeyManagementException;
+import javax.net.ssl.SSLServerSocketFactory;
+
+/**
+ * An implementation of {@link SSLServerSocketFactory} using BoringSSL.
+ *
+ * <p/>This name of this class cannot change in order to maintain backward-compatibility with GMS
+ * core {@code ProviderInstallerImpl}
+ */
+final class OpenSSLServerSocketFactoryImpl extends SSLServerSocketFactory {
+    private static boolean useEngineSocketByDefault = SSLUtils.USE_ENGINE_SOCKET_BY_DEFAULT;
+
+    private SSLParametersImpl sslParameters;
+    private IOException instantiationException;
+    private boolean useEngineSocket = useEngineSocketByDefault;
+
+    OpenSSLServerSocketFactoryImpl() {
+        try {
+            this.sslParameters = SSLParametersImpl.getDefault();
+            this.sslParameters.setUseClientMode(false);
+        } catch (KeyManagementException e) {
+            instantiationException = new IOException("Delayed instantiation exception:");
+            instantiationException.initCause(e);
+        }
+    }
+
+    OpenSSLServerSocketFactoryImpl(SSLParametersImpl sslParameters) {
+        this.sslParameters = (SSLParametersImpl) sslParameters.clone();
+        this.sslParameters.setUseClientMode(false);
+    }
+
+    /**
+     * Configures the default socket to be created for all instances.
+     */
+    static void setUseEngineSocketByDefault(boolean useEngineSocket) {
+        useEngineSocketByDefault = useEngineSocket;
+    }
+
+    /**
+     * Configures the socket to be created for this instance. If not called,
+     * {@link #useEngineSocketByDefault} will be used.
+     */
+    void setUseEngineSocket(boolean useEngineSocket) {
+        this.useEngineSocket = useEngineSocket;
+    }
+
+    @Override
+    public String[] getDefaultCipherSuites() {
+        return sslParameters.getEnabledCipherSuites();
+    }
+
+    @Override
+    public String[] getSupportedCipherSuites() {
+        return NativeCrypto.getSupportedCipherSuites();
+    }
+
+    @Override
+    public ServerSocket createServerSocket() throws IOException {
+        return new ConscryptServerSocket((SSLParametersImpl) sslParameters.clone())
+                .setUseEngineSocket(useEngineSocket);
+    }
+
+    @Override
+    public ServerSocket createServerSocket(int port) throws IOException {
+        return new ConscryptServerSocket(port, (SSLParametersImpl) sslParameters.clone())
+                .setUseEngineSocket(useEngineSocket);
+    }
+
+    @Override
+    public ServerSocket createServerSocket(int port, int backlog) throws IOException {
+        return new ConscryptServerSocket(port, backlog, (SSLParametersImpl) sslParameters.clone())
+                .setUseEngineSocket(useEngineSocket);
+    }
+
+    @Override
+    public ServerSocket createServerSocket(int port, int backlog, InetAddress iAddress)
+            throws IOException {
+        return new ConscryptServerSocket(
+                port, backlog, iAddress, (SSLParametersImpl) sslParameters.clone())
+                .setUseEngineSocket(useEngineSocket);
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSignature.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSignature.java
new file mode 100644
index 0000000..f62600d
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSignature.java
@@ -0,0 +1,576 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.nio.ByteBuffer;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.SignatureSpi;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.MGF1ParameterSpec;
+import java.security.spec.PSSParameterSpec;
+
+/**
+ * Implements the subset of the JDK Signature interface needed for
+ * signature verification using OpenSSL.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public class OpenSSLSignature extends SignatureSpi {
+    private enum EngineType {
+        RSA, EC,
+    }
+
+    private NativeRef.EVP_MD_CTX ctx;
+
+    /**
+     * The current OpenSSL key we're operating on.
+     */
+    private OpenSSLKey key;
+
+    /**
+     * Holds the type of the Java algorithm.
+     */
+    private final EngineType engineType;
+
+    /**
+     * Digest algorithm (reference to {@code EVP_MD}).
+     */
+    private final long evpMdRef;
+
+    /**
+     * Holds a dummy buffer for writing single bytes to the digest.
+     */
+    private final byte[] singleByte = new byte[1];
+
+    /**
+     * True when engine is initialized to signing.
+     */
+    private boolean signing;
+
+    /**
+     * Public key algorithm context (reference to {@code EVP_PKEY_CTX}).
+     */
+    private long evpPkeyCtx;
+
+    /**
+     * Creates a new OpenSSLSignature instance for the given algorithm name.
+     *
+     * @param evpMdRef digest algorithm ({@code EVP_MD} reference).
+     */
+    private OpenSSLSignature(long evpMdRef, EngineType engineType) {
+        this.engineType = engineType;
+        this.evpMdRef = evpMdRef;
+    }
+
+    private void resetContext() throws InvalidAlgorithmParameterException {
+        NativeRef.EVP_MD_CTX ctxLocal = new NativeRef.EVP_MD_CTX(NativeCrypto.EVP_MD_CTX_create());
+        if (signing) {
+            evpPkeyCtx = NativeCrypto.EVP_DigestSignInit(ctxLocal, evpMdRef, key.getNativeRef());
+        } else {
+            evpPkeyCtx = NativeCrypto.EVP_DigestVerifyInit(ctxLocal, evpMdRef, key.getNativeRef());
+        }
+        configureEVP_PKEY_CTX(evpPkeyCtx);
+        this.ctx = ctxLocal;
+    }
+
+    /**
+     * Configures the public key algorithm context ({@code EVP_PKEY_CTX}) associated with this
+     * operation.
+     *
+     * <p>The default implementation does nothing.
+     *
+     * @param ctx reference to the context ({@code EVP_PKEY_CTX}).
+     */
+    protected void configureEVP_PKEY_CTX(long ctx) throws InvalidAlgorithmParameterException {}
+
+    @Override
+    protected void engineUpdate(byte input) {
+        singleByte[0] = input;
+        engineUpdate(singleByte, 0, 1);
+    }
+
+    @Override
+    protected void engineUpdate(byte[] input, int offset, int len) {
+        final NativeRef.EVP_MD_CTX ctxLocal = ctx;
+        if (signing) {
+            NativeCrypto.EVP_DigestSignUpdate(ctxLocal, input, offset, len);
+        } else {
+            NativeCrypto.EVP_DigestVerifyUpdate(ctxLocal, input, offset, len);
+        }
+    }
+
+    @Override
+    protected void engineUpdate(ByteBuffer input) {
+        // Optimization: Avoid copying/allocation for direct buffers because their contents are
+        // stored as a contiguous region in memory and thus can be efficiently accessed from native
+        // code.
+
+        if (!input.hasRemaining()) {
+            return;
+        }
+
+        if (!input.isDirect()) {
+            super.engineUpdate(input);
+            return;
+        }
+
+        long baseAddress = NativeCrypto.getDirectBufferAddress(input);
+        if (baseAddress == 0) {
+            // Direct buffer's contents can't be accessed from JNI  -- superclass's implementation
+            // is good enough to handle this.
+            super.engineUpdate(input);
+            return;
+        }
+
+        // Process the contents between Buffer's position and limit (remaining() number of bytes)
+        int position = input.position();
+        if (position < 0) {
+            throw new RuntimeException("Negative position");
+        }
+        long ptr = baseAddress + position;
+        int len = input.remaining();
+        if (len < 0) {
+            throw new RuntimeException("Negative remaining amount");
+        }
+
+        final NativeRef.EVP_MD_CTX ctxLocal = ctx;
+        if (signing) {
+            NativeCrypto.EVP_DigestSignUpdateDirect(ctxLocal, ptr, len);
+        } else {
+            NativeCrypto.EVP_DigestVerifyUpdateDirect(ctxLocal, ptr, len);
+        }
+        input.position(position + len);
+    }
+
+    @Deprecated
+    @Override
+    protected Object engineGetParameter(String param) throws InvalidParameterException {
+        return null;
+    }
+
+    private void checkEngineType(OpenSSLKey pkey) throws InvalidKeyException {
+        final int pkeyType = NativeCrypto.EVP_PKEY_type(pkey.getNativeRef());
+
+        switch (engineType) {
+            case RSA:
+                if (pkeyType != NativeConstants.EVP_PKEY_RSA) {
+                    throw new InvalidKeyException("Signature initialized as " + engineType
+                            + " (not RSA)");
+                }
+                break;
+            case EC:
+                if (pkeyType != NativeConstants.EVP_PKEY_EC) {
+                    throw new InvalidKeyException("Signature initialized as " + engineType
+                            + " (not EC)");
+                }
+                break;
+            default:
+                throw new InvalidKeyException("Key must be of type " + engineType);
+        }
+    }
+
+    private void initInternal(OpenSSLKey newKey, boolean signing) throws InvalidKeyException {
+        checkEngineType(newKey);
+        key = newKey;
+
+        this.signing = signing;
+        try {
+            resetContext();
+        } catch (InvalidAlgorithmParameterException e) {
+            throw new InvalidKeyException(e);
+        }
+    }
+
+    @Override
+    protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
+        initInternal(OpenSSLKey.fromPrivateKey(privateKey), true);
+    }
+
+    @Override
+    protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+        initInternal(OpenSSLKey.fromPublicKey(publicKey), false);
+    }
+
+    @Deprecated
+    @Override
+    protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
+    }
+
+    @Override
+    @SuppressWarnings("Finally")
+    protected byte[] engineSign() throws SignatureException {
+        final NativeRef.EVP_MD_CTX ctxLocal = ctx;
+        try {
+            return NativeCrypto.EVP_DigestSignFinal(ctxLocal);
+        } catch (Exception ex) {
+            throw new SignatureException(ex);
+        } finally {
+            /*
+             * Java expects the digest context to be reset completely after sign
+             * calls.
+             */
+            try {
+                resetContext();
+            } catch (InvalidAlgorithmParameterException e) {
+                throw new AssertionError("Reset of context failed after it was successful once");
+            }
+        }
+    }
+
+    @Override
+    @SuppressWarnings("Finally")
+    protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+        final NativeRef.EVP_MD_CTX ctxLocal = ctx;
+        try {
+            return NativeCrypto.EVP_DigestVerifyFinal(ctxLocal, sigBytes, 0, sigBytes.length);
+        } catch (Exception ex) {
+            throw new SignatureException(ex);
+        } finally {
+            /*
+             * Java expects the digest context to be reset completely after
+             * verify calls.
+             */
+            try {
+                resetContext();
+            } catch (InvalidAlgorithmParameterException e) {
+                throw new AssertionError("Reset of context failed after it was successful once");
+            }
+        }
+    }
+
+    /**
+     * Returns the public key algorithm context ({@code EVP_PKEY_CTX} reference) associated with
+     * this operation or {@code 0} if operation hasn't been initialized.
+     */
+    protected final long getEVP_PKEY_CTX() {
+        return evpPkeyCtx;
+    }
+
+    /**
+     * Base class for {@code RSASSA-PKCS1-v1_5} signatures.
+     */
+    abstract static class RSAPKCS1Padding extends OpenSSLSignature {
+        RSAPKCS1Padding(long evpMdRef) {
+            super(evpMdRef, EngineType.RSA);
+        }
+
+        @Override
+        protected final void configureEVP_PKEY_CTX(long ctx)
+                throws InvalidAlgorithmParameterException {
+            NativeCrypto.EVP_PKEY_CTX_set_rsa_padding(ctx, NativeConstants.RSA_PKCS1_PADDING);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class MD5RSA extends RSAPKCS1Padding {
+        @libcore.api.IntraCoreApi
+        public MD5RSA() {
+            super(EvpMdRef.MD5.EVP_MD);
+        }
+    }
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA1RSA extends RSAPKCS1Padding {
+        @libcore.api.IntraCoreApi
+        public SHA1RSA() {
+            super(EvpMdRef.SHA1.EVP_MD);
+        }
+    }
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA224RSA extends RSAPKCS1Padding {
+        @libcore.api.IntraCoreApi
+        public SHA224RSA() {
+            super(EvpMdRef.SHA224.EVP_MD);
+        }
+    }
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA256RSA extends RSAPKCS1Padding {
+        @libcore.api.IntraCoreApi
+        public SHA256RSA() {
+            super(EvpMdRef.SHA256.EVP_MD);
+        }
+    }
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA384RSA extends RSAPKCS1Padding {
+        @libcore.api.IntraCoreApi
+        public SHA384RSA() {
+            super(EvpMdRef.SHA384.EVP_MD);
+        }
+    }
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA512RSA extends RSAPKCS1Padding {
+        @libcore.api.IntraCoreApi
+        public SHA512RSA() {
+            super(EvpMdRef.SHA512.EVP_MD);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA1ECDSA extends OpenSSLSignature {
+        @libcore.api.IntraCoreApi
+        public SHA1ECDSA() {
+            super(EvpMdRef.SHA1.EVP_MD, EngineType.EC);
+        }
+    }
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA224ECDSA extends OpenSSLSignature {
+        @libcore.api.IntraCoreApi
+        public SHA224ECDSA() {
+            super(EvpMdRef.SHA224.EVP_MD, EngineType.EC);
+        }
+    }
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA256ECDSA extends OpenSSLSignature {
+        @libcore.api.IntraCoreApi
+        public SHA256ECDSA() {
+            super(EvpMdRef.SHA256.EVP_MD, EngineType.EC);
+        }
+    }
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA384ECDSA extends OpenSSLSignature {
+        @libcore.api.IntraCoreApi
+        public SHA384ECDSA() {
+            super(EvpMdRef.SHA384.EVP_MD, EngineType.EC);
+        }
+    }
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA512ECDSA extends OpenSSLSignature {
+        @libcore.api.IntraCoreApi
+        public SHA512ECDSA() {
+            super(EvpMdRef.SHA512.EVP_MD, EngineType.EC);
+        }
+    }
+
+    /**
+     * Base class for {@code RSASSA-PSS} signatures.
+     */
+    abstract static class RSAPSSPadding extends OpenSSLSignature {
+        private static final int TRAILER_FIELD_BC_ID = 1;
+
+        private final String contentDigestAlgorithm;
+
+        private String mgf1DigestAlgorithm;
+        private long mgf1EvpMdRef;
+        private int saltSizeBytes;
+
+        RSAPSSPadding(
+                long contentDigestEvpMdRef, String contentDigestAlgorithm, int saltSizeBytes) {
+            super(contentDigestEvpMdRef, EngineType.RSA);
+            this.contentDigestAlgorithm = contentDigestAlgorithm;
+            this.mgf1DigestAlgorithm = contentDigestAlgorithm;
+            this.mgf1EvpMdRef = contentDigestEvpMdRef;
+            this.saltSizeBytes = saltSizeBytes;
+        }
+
+        @Override
+        protected final void configureEVP_PKEY_CTX(long ctx)
+                throws InvalidAlgorithmParameterException {
+            NativeCrypto.EVP_PKEY_CTX_set_rsa_padding(ctx, NativeConstants.RSA_PKCS1_PSS_PADDING);
+            NativeCrypto.EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, mgf1EvpMdRef);
+            NativeCrypto.EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, saltSizeBytes);
+        }
+
+        @Override
+        protected final void engineSetParameter(AlgorithmParameterSpec params)
+                throws InvalidAlgorithmParameterException {
+            if (!(params instanceof PSSParameterSpec)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported parameter: " + params + ". Only "
+                                + PSSParameterSpec.class.getName() + " supported");
+            }
+            PSSParameterSpec spec = (PSSParameterSpec) params;
+            String specContentDigest = EvpMdRef
+                    .getJcaDigestAlgorithmStandardName(spec.getDigestAlgorithm());
+            if (specContentDigest == null) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported content digest algorithm: " + spec.getDigestAlgorithm());
+            } else if (!contentDigestAlgorithm.equalsIgnoreCase(specContentDigest)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Changing content digest algorithm not supported");
+            }
+
+            String specMgfAlgorithm = spec.getMGFAlgorithm();
+            if (!EvpMdRef.MGF1_ALGORITHM_NAME.equalsIgnoreCase(specMgfAlgorithm)
+                    && !EvpMdRef.MGF1_OID.equals(specMgfAlgorithm)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported MGF algorithm: " + specMgfAlgorithm + ". Only "
+                                + EvpMdRef.MGF1_ALGORITHM_NAME + " supported");
+            }
+
+            AlgorithmParameterSpec mgfSpec = spec.getMGFParameters();
+            if (!(mgfSpec instanceof MGF1ParameterSpec)) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported MGF parameters: " + mgfSpec + ". Only "
+                                + MGF1ParameterSpec.class.getName() + " supported");
+            }
+            MGF1ParameterSpec specMgf1Spec = (MGF1ParameterSpec) spec.getMGFParameters();
+
+            String specMgf1Digest = EvpMdRef
+                    .getJcaDigestAlgorithmStandardName(specMgf1Spec.getDigestAlgorithm());
+            if (specMgf1Digest == null) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported MGF1 digest algorithm: " + specMgf1Spec.getDigestAlgorithm());
+            }
+            long specMgf1EvpMdRef;
+            try {
+                specMgf1EvpMdRef = EvpMdRef
+                        .getEVP_MDByJcaDigestAlgorithmStandardName(specMgf1Digest);
+            } catch (NoSuchAlgorithmException e) {
+                throw new ProviderException("Failed to obtain EVP_MD for " + specMgf1Digest, e);
+            }
+
+            int specSaltSizeBytes = spec.getSaltLength();
+            if (specSaltSizeBytes < 0) {
+                throw new InvalidAlgorithmParameterException(
+                        "Salt length must be non-negative: " + specSaltSizeBytes);
+            }
+
+            int specTrailer = spec.getTrailerField();
+            if (specTrailer != TRAILER_FIELD_BC_ID) {
+                throw new InvalidAlgorithmParameterException(
+                        "Unsupported trailer field: " + specTrailer + ". Only "
+                                + TRAILER_FIELD_BC_ID + " supported");
+            }
+
+            this.mgf1DigestAlgorithm = specMgf1Digest;
+            this.mgf1EvpMdRef = specMgf1EvpMdRef;
+            this.saltSizeBytes = specSaltSizeBytes;
+
+            long ctx = getEVP_PKEY_CTX();
+            if (ctx != 0) {
+                configureEVP_PKEY_CTX(ctx);
+            }
+        }
+
+        @Override
+        protected final AlgorithmParameters engineGetParameters() {
+            try {
+                AlgorithmParameters result = AlgorithmParameters.getInstance("PSS");
+                result.init(
+                        new PSSParameterSpec(
+                                contentDigestAlgorithm,
+                                EvpMdRef.MGF1_ALGORITHM_NAME,
+                                new MGF1ParameterSpec(mgf1DigestAlgorithm),
+                                saltSizeBytes,
+                                TRAILER_FIELD_BC_ID));
+                return result;
+            } catch (NoSuchAlgorithmException e) {
+                throw new ProviderException("Failed to create PSS AlgorithmParameters", e);
+            } catch (InvalidParameterSpecException e) {
+                throw new ProviderException("Failed to create PSS AlgorithmParameters", e);
+            }
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA1RSAPSS extends RSAPSSPadding {
+        @libcore.api.IntraCoreApi
+        public SHA1RSAPSS() {
+            super(EvpMdRef.SHA1.EVP_MD, EvpMdRef.SHA1.JCA_NAME, EvpMdRef.SHA1.SIZE_BYTES);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA224RSAPSS extends RSAPSSPadding {
+        @libcore.api.IntraCoreApi
+        public SHA224RSAPSS() {
+            super(EvpMdRef.SHA224.EVP_MD, EvpMdRef.SHA224.JCA_NAME, EvpMdRef.SHA224.SIZE_BYTES);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA256RSAPSS extends RSAPSSPadding {
+        @libcore.api.IntraCoreApi
+        public SHA256RSAPSS() {
+            super(EvpMdRef.SHA256.EVP_MD, EvpMdRef.SHA256.JCA_NAME, EvpMdRef.SHA256.SIZE_BYTES);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA384RSAPSS extends RSAPSSPadding {
+        @libcore.api.IntraCoreApi
+        public SHA384RSAPSS() {
+            super(EvpMdRef.SHA384.EVP_MD, EvpMdRef.SHA384.JCA_NAME, EvpMdRef.SHA384.SIZE_BYTES);
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @libcore.api.IntraCoreApi
+    public static final class SHA512RSAPSS extends RSAPSSPadding {
+        @libcore.api.IntraCoreApi
+        public SHA512RSAPSS() {
+            super(EvpMdRef.SHA512.EVP_MD, EvpMdRef.SHA512.JCA_NAME, EvpMdRef.SHA512.SIZE_BYTES);
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSignatureRawECDSA.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSignatureRawECDSA.java
new file mode 100644
index 0000000..9710801
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSignatureRawECDSA.java
@@ -0,0 +1,138 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+import java.io.ByteArrayOutputStream;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.SignatureSpi;
+
+/**
+ * Implements the JDK Signature interface needed for RAW ECDSA signature
+ * generation and verification using BoringSSL.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public class OpenSSLSignatureRawECDSA extends SignatureSpi {
+    /**
+     * The current OpenSSL key we're operating on.
+     */
+    private OpenSSLKey key;
+
+    /**
+     * Buffer to hold value to be signed or verified.
+     */
+    private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+    @libcore.api.IntraCoreApi
+    public OpenSSLSignatureRawECDSA() {}
+
+    @Override
+    protected void engineUpdate(byte input) {
+        buffer.write(input);
+    }
+
+    @Override
+    protected void engineUpdate(byte[] input, int offset, int len) {
+        buffer.write(input, offset, len);
+    }
+
+    @Override
+    @SuppressWarnings("deprecation")
+    protected Object engineGetParameter(String param) throws InvalidParameterException {
+        return null;
+    }
+
+    private static OpenSSLKey verifyKey(OpenSSLKey key) throws InvalidKeyException {
+        int pkeyType = NativeCrypto.EVP_PKEY_type(key.getNativeRef());
+        if (pkeyType != NativeConstants.EVP_PKEY_EC) {
+            throw new InvalidKeyException("Non-EC key used to initialize EC signature.");
+        }
+        return key;
+    }
+
+    @Override
+    protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
+        key = verifyKey(OpenSSLKey.fromPrivateKey(privateKey));
+    }
+
+    @Override
+    protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+        key = verifyKey(OpenSSLKey.fromPublicKey(publicKey));
+    }
+
+    @Override
+    @SuppressWarnings("deprecation")
+    protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
+    }
+
+    @Override
+    protected byte[] engineSign() throws SignatureException {
+        if (key == null) {
+            // This can't actually happen, but you never know...
+            throw new SignatureException("No key provided");
+        }
+
+        int output_size = NativeCrypto.ECDSA_size(key.getNativeRef());
+        byte[] outputBuffer = new byte[output_size];
+        try {
+            int bytes_written =
+                    NativeCrypto.ECDSA_sign(buffer.toByteArray(), outputBuffer, key.getNativeRef());
+            if (bytes_written < 0) {
+                throw new SignatureException("Could not compute signature.");
+            }
+            // There's no guarantee that the signature will be ECDSA_size bytes long,
+            // that's just the maximum possible length of a signature.  Only return the bytes
+            // that were actually produced.
+            if (bytes_written != output_size) {
+                byte[] newBuffer = new byte[bytes_written];
+                System.arraycopy(outputBuffer, 0, newBuffer, 0, bytes_written);
+                outputBuffer = newBuffer;
+            }
+            return outputBuffer;
+        } catch (Exception ex) {
+            throw new SignatureException(ex);
+        } finally {
+            buffer.reset();
+        }
+    }
+
+    @Override
+    protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+        if (key == null) {
+            // This can't actually happen, but you never know...
+            throw new SignatureException("No key provided");
+        }
+
+        try {
+            int result =
+                    NativeCrypto.ECDSA_verify(buffer.toByteArray(), sigBytes, key.getNativeRef());
+            if (result == -1) {
+                throw new SignatureException("Could not verify signature.");
+            }
+            return result == 1;
+        } catch (Exception ex) {
+            throw new SignatureException(ex);
+        } finally {
+            buffer.reset();
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSignatureRawRSA.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSignatureRawRSA.java
new file mode 100644
index 0000000..5b7d2b8
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSignatureRawRSA.java
@@ -0,0 +1,203 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.SignatureSpi;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+
+/**
+ * Implements the JDK Signature interface needed for RAW RSA signature
+ * generation and verification using BoringSSL.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public final class OpenSSLSignatureRawRSA extends SignatureSpi {
+    /**
+     * The current OpenSSL key we're operating on.
+     */
+    private OpenSSLKey key;
+
+    /**
+     * Buffer to hold value to be signed or verified.
+     */
+    private byte[] inputBuffer;
+
+    /**
+     * Current offset in input buffer.
+     */
+    private int inputOffset;
+
+    /**
+     * Provides a flag to specify when the input is too long.
+     */
+    private boolean inputIsTooLong;
+
+    @libcore.api.IntraCoreApi
+    public OpenSSLSignatureRawRSA() {}
+
+    @Override
+    protected void engineUpdate(byte input) {
+        final int oldOffset = inputOffset++;
+
+        if (inputOffset > inputBuffer.length) {
+            inputIsTooLong = true;
+            return;
+        }
+
+        inputBuffer[oldOffset] = input;
+    }
+
+    @Override
+    protected void engineUpdate(byte[] input, int offset, int len) {
+        final int oldOffset = inputOffset;
+        inputOffset += len;
+
+        if (inputOffset > inputBuffer.length) {
+            inputIsTooLong = true;
+            return;
+        }
+
+        System.arraycopy(input, offset, inputBuffer, oldOffset, len);
+    }
+
+    @Override
+    @SuppressWarnings("deprecation")
+    protected Object engineGetParameter(String param) throws InvalidParameterException {
+        return null;
+    }
+
+    @Override
+    protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
+        if (privateKey instanceof OpenSSLRSAPrivateKey) {
+            OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) privateKey;
+            key = rsaPrivateKey.getOpenSSLKey();
+        } else if (privateKey instanceof RSAPrivateCrtKey) {
+            RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) privateKey;
+            key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey);
+        } else if (privateKey instanceof RSAPrivateKey) {
+            RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
+            key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey);
+        } else {
+            throw new InvalidKeyException("Need RSA private key");
+        }
+
+        // Allocate buffer according to RSA modulus size.
+        int maxSize = NativeCrypto.RSA_size(key.getNativeRef());
+        inputBuffer = new byte[maxSize];
+        inputOffset = 0;
+    }
+
+    @Override
+    protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+        if (publicKey instanceof OpenSSLRSAPublicKey) {
+            OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) publicKey;
+            key = rsaPublicKey.getOpenSSLKey();
+        } else if (publicKey instanceof RSAPublicKey) {
+            RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
+            key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey);
+        } else {
+            throw new InvalidKeyException("Need RSA public key");
+        }
+
+        // Allocate buffer according to RSA modulus size.
+        int maxSize = NativeCrypto.RSA_size(key.getNativeRef());
+        inputBuffer = new byte[maxSize];
+        inputOffset = 0;
+    }
+
+    @Override
+    @SuppressWarnings("deprecation")
+    protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
+    }
+
+    @Override
+    protected byte[] engineSign() throws SignatureException {
+        if (key == null) {
+            // This can't actually happen, but you never know...
+            throw new SignatureException("Need RSA private key");
+        }
+
+        if (inputIsTooLong) {
+            throw new SignatureException("input length " + inputOffset + " != "
+                    + inputBuffer.length + " (modulus size)");
+        }
+
+        byte[] outputBuffer = new byte[inputBuffer.length];
+        try {
+            NativeCrypto.RSA_private_encrypt(inputOffset, inputBuffer, outputBuffer,
+                    key.getNativeRef(), NativeConstants.RSA_PKCS1_PADDING);
+            return outputBuffer;
+        } catch (Exception ex) {
+            throw new SignatureException(ex);
+        } finally {
+            inputOffset = 0;
+        }
+    }
+
+    @Override
+    protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+        if (key == null) {
+            // This can't actually happen, but you never know...
+            throw new SignatureException("Need RSA public key");
+        }
+
+        if (inputIsTooLong) {
+            return false;
+        }
+
+        // We catch this case here instead of BoringSSL so we can throw an
+        // exception that matches other implementations.
+        if (sigBytes.length > inputBuffer.length) {
+            throw new SignatureException("Input signature length is too large: " + sigBytes.length
+                    + " > " + inputBuffer.length);
+        }
+
+        byte[] outputBuffer = new byte[inputBuffer.length];
+        try {
+            final int resultSize;
+            try {
+                resultSize = NativeCrypto.RSA_public_decrypt(sigBytes.length, sigBytes,
+                        outputBuffer, key.getNativeRef(), NativeConstants.RSA_PKCS1_PADDING);
+            } catch (SignatureException e) {
+                throw e;
+            } catch (Exception e) {
+                return false;
+            }
+            /* Make this constant time by comparing every byte. */
+            boolean matches = (resultSize == inputOffset);
+            for (int i = 0; i < resultSize; i++) {
+                if (inputBuffer[i] != outputBuffer[i]) {
+                    matches = false;
+                }
+            }
+            return matches;
+        } catch (Exception ex) {
+            throw new SignatureException(ex);
+        } finally {
+            inputOffset = 0;
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSocketFactoryImpl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSocketFactoryImpl.java
new file mode 100644
index 0000000..748c529
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSocketFactoryImpl.java
@@ -0,0 +1,173 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.Platform.createEngineSocket;
+import static com.android.org.conscrypt.Platform.createFileDescriptorSocket;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * An implementation of {@link SSLSocketFactory} based on BoringSSL.
+ *
+ * <p/>This name of this class cannot change in order to maintain backward-compatibility with GMS
+ * core {@code ProviderInstallerImpl}
+ */
+final class OpenSSLSocketFactoryImpl extends SSLSocketFactory {
+    private static boolean useEngineSocketByDefault = SSLUtils.USE_ENGINE_SOCKET_BY_DEFAULT;
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    private final SSLParametersImpl sslParameters;
+    private final IOException instantiationException;
+    private boolean useEngineSocket = useEngineSocketByDefault;
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    OpenSSLSocketFactoryImpl() {
+        SSLParametersImpl sslParametersLocal = null;
+        IOException instantiationExceptionLocal = null;
+        try {
+            sslParametersLocal = SSLParametersImpl.getDefault();
+        } catch (KeyManagementException e) {
+            instantiationExceptionLocal = new IOException("Delayed instantiation exception:", e);
+        }
+        this.sslParameters = sslParametersLocal;
+        this.instantiationException = instantiationExceptionLocal;
+    }
+
+    OpenSSLSocketFactoryImpl(SSLParametersImpl sslParameters) {
+        this.sslParameters = sslParameters;
+        this.instantiationException = null;
+    }
+
+    /**
+     * Configures the default socket to be created for all instances.
+     */
+    static void setUseEngineSocketByDefault(boolean useEngineSocket) {
+        useEngineSocketByDefault = useEngineSocket;
+    }
+
+    /**
+     * Configures the socket to be created for this instance. If not called,
+     * {@link #useEngineSocketByDefault} will be used.
+     */
+    void setUseEngineSocket(boolean useEngineSocket) {
+        this.useEngineSocket = useEngineSocket;
+    }
+
+    @Override
+    public String[] getDefaultCipherSuites() {
+        return sslParameters.getEnabledCipherSuites();
+    }
+
+    @Override
+    public String[] getSupportedCipherSuites() {
+        return NativeCrypto.getSupportedCipherSuites();
+    }
+
+    @Override
+    public Socket createSocket() throws IOException {
+        if (instantiationException != null) {
+            throw instantiationException;
+        }
+        if (useEngineSocket) {
+            return createEngineSocket((SSLParametersImpl) sslParameters.clone());
+        } else {
+            return createFileDescriptorSocket((SSLParametersImpl) sslParameters.clone());
+        }
+    }
+
+    @Override
+    public Socket createSocket(String hostname, int port) throws IOException, UnknownHostException {
+        if (useEngineSocket) {
+            return createEngineSocket(
+                    hostname, port, (SSLParametersImpl) sslParameters.clone());
+        } else {
+            return createFileDescriptorSocket(
+                    hostname, port, (SSLParametersImpl) sslParameters.clone());
+        }
+    }
+
+    @Override
+    public Socket createSocket(String hostname, int port, InetAddress localHost, int localPort)
+            throws IOException, UnknownHostException {
+        if (useEngineSocket) {
+            return createEngineSocket(hostname, port, localHost,
+                    localPort, (SSLParametersImpl) sslParameters.clone());
+        } else {
+            return createFileDescriptorSocket(hostname, port, localHost,
+                    localPort, (SSLParametersImpl) sslParameters.clone());
+        }
+    }
+
+    @Override
+    public Socket createSocket(InetAddress address, int port) throws IOException {
+        if (useEngineSocket) {
+            return createEngineSocket(
+                    address, port, (SSLParametersImpl) sslParameters.clone());
+        } else {
+            return createFileDescriptorSocket(
+                    address, port, (SSLParametersImpl) sslParameters.clone());
+        }
+    }
+
+    @Override
+    public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
+            int localPort) throws IOException {
+        if (useEngineSocket) {
+            return createEngineSocket(address, port, localAddress,
+                    localPort, (SSLParametersImpl) sslParameters.clone());
+        } else {
+            return createFileDescriptorSocket(address, port, localAddress,
+                    localPort, (SSLParametersImpl) sslParameters.clone());
+        }
+    }
+
+    @Override
+    public Socket createSocket(Socket socket, String hostname, int port, boolean autoClose)
+            throws IOException {
+        Preconditions.checkNotNull(socket, "socket");
+        if (!socket.isConnected()) {
+            throw new SocketException("Socket is not connected.");
+        }
+
+        if (!useEngineSocket && hasFileDescriptor(socket)) {
+            return createFileDescriptorSocket(
+                    socket, hostname, port, autoClose, (SSLParametersImpl) sslParameters.clone());
+        } else {
+            return createEngineSocket(
+                    socket, hostname, port, autoClose, (SSLParametersImpl) sslParameters.clone());
+        }
+    }
+
+    private boolean hasFileDescriptor(Socket s) {
+        try {
+            // If socket has a file descriptor we can use it directly
+            // otherwise we need to use the engine.
+            Platform.getFileDescriptor(s);
+            return true;
+        } catch (RuntimeException re) {
+            return false;
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSocketImpl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSocketImpl.java
new file mode 100644
index 0000000..42d0261
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLSocketImpl.java
@@ -0,0 +1,183 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.security.PrivateKey;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+
+/**
+ * Public shim allowing us to stay backward-compatible with legacy applications which were using
+ * Conscrypt's extended socket API before the introduction of the {@link Conscrypt} class.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.CorePlatformApi
+@Internal
+public abstract class OpenSSLSocketImpl extends AbstractConscryptSocket {
+    OpenSSLSocketImpl() throws IOException {
+    }
+
+    OpenSSLSocketImpl(String hostname, int port) throws IOException {
+        super(hostname, port);
+    }
+
+    OpenSSLSocketImpl(InetAddress address, int port) throws IOException {
+        super(address, port);
+    }
+
+    OpenSSLSocketImpl(String hostname, int port, InetAddress clientAddress, int clientPort)
+        throws IOException {
+        super(hostname, port, clientAddress, clientPort);
+    }
+
+    OpenSSLSocketImpl(InetAddress address, int port, InetAddress clientAddress,
+        int clientPort)
+        throws IOException {
+        super(address, port, clientAddress, clientPort);
+    }
+
+    OpenSSLSocketImpl(Socket socket, String hostname, int port, boolean autoClose)
+        throws IOException {
+        super(socket, hostname, port, autoClose);
+    }
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @Override
+    public String getHostname() {
+        return super.getHostname();
+    }
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    @Override
+    public void setHostname(String hostname) {
+        super.setHostname(hostname);
+    }
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @Override
+    public String getHostnameOrIP() {
+        return super.getHostnameOrIP();
+    }
+
+    @Override
+    public FileDescriptor getFileDescriptor$() {
+        return super.getFileDescriptor$();
+    }
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    @Override
+    public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
+        super.setSoWriteTimeout(writeTimeoutMilliseconds);
+    }
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @Override
+    public int getSoWriteTimeout() throws SocketException {
+        return super.getSoWriteTimeout();
+    }
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    @Override
+    public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
+        super.setHandshakeTimeout(handshakeTimeoutMilliseconds);
+    }
+
+    @Override
+    public abstract SSLSession getHandshakeSession();
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    @Override
+    public abstract void setUseSessionTickets(boolean useSessionTickets);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @Override
+    public abstract void setChannelIdEnabled(boolean enabled);
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @Override
+    public abstract byte[] getChannelId() throws SSLException;
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    @Override
+    public abstract void setChannelIdPrivateKey(PrivateKey privateKey);
+
+    /**
+     * @deprecated NPN is not supported
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    @Override
+    @Deprecated
+    public final byte[] getNpnSelectedProtocol() {
+        return super.getNpnSelectedProtocol();
+    }
+
+    /**
+     * @deprecated NPN is not supported
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    @Override
+    @Deprecated
+    public final void setNpnProtocols(byte[] npnProtocols) {
+        super.setNpnProtocols(npnProtocols);
+    }
+
+    /**
+     * @deprecated use {@link #setApplicationProtocols(String[])} instead.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @Override
+    @Deprecated
+    public final void setAlpnProtocols(String[] alpnProtocols) {
+        setApplicationProtocols(alpnProtocols == null ? EmptyArray.STRING : alpnProtocols);
+    }
+
+    /**
+     * @deprecated use {@link #getApplicationProtocol()} instead.
+     */
+    @dalvik.annotation.
+    compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    @Override
+    @Deprecated
+    public final byte[] getAlpnSelectedProtocol() {
+        return SSLUtils.toProtocolBytes(getApplicationProtocol());
+    }
+
+    /**
+     * @deprecated Use {@link #setAlpnProtocols(String[])} instead.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    @Override
+    @Deprecated
+    public final void setAlpnProtocols(byte[] protocols) {
+        setApplicationProtocols(SSLUtils.decodeProtocols(protocols == null ? EmptyArray.BYTE : protocols));
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CRL.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CRL.java
new file mode 100644
index 0000000..58f831d
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CRL.java
@@ -0,0 +1,420 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.CRLException;
+import java.security.cert.Certificate;
+import java.security.cert.X509CRL;
+import java.security.cert.X509CRLEntry;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TimeZone;
+import javax.security.auth.x500.X500Principal;
+import com.android.org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
+
+/**
+ * An implementation of {@link X509CRL} based on BoringSSL.
+ */
+final class OpenSSLX509CRL extends X509CRL {
+    private final long mContext;
+    private final Date thisUpdate;
+    private final Date nextUpdate;
+
+    private OpenSSLX509CRL(long ctx) throws ParsingException {
+        mContext = ctx;
+        // The legacy X509 OpenSSL APIs don't validate ASN1_TIME structures until access, so
+        // parse them here because this is the only time we're allowed to throw ParsingException
+        thisUpdate = toDate(NativeCrypto.X509_CRL_get_lastUpdate(mContext, this));
+        nextUpdate = toDate(NativeCrypto.X509_CRL_get_nextUpdate(mContext, this));
+    }
+
+    // Package-visible because it's also used by OpenSSLX509CRLEntry
+    static Date toDate(long asn1time) throws ParsingException {
+        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+        calendar.set(Calendar.MILLISECOND, 0);
+        NativeCrypto.ASN1_TIME_to_Calendar(asn1time, calendar);
+        return calendar.getTime();
+    }
+
+    static OpenSSLX509CRL fromX509DerInputStream(InputStream is) throws ParsingException {
+        final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
+
+        try {
+            final long crlCtx = NativeCrypto.d2i_X509_CRL_bio(bis.getBioContext());
+            if (crlCtx == 0) {
+                return null;
+            }
+            return new OpenSSLX509CRL(crlCtx);
+        } catch (Exception e) {
+            throw new ParsingException(e);
+        } finally {
+            bis.release();
+        }
+    }
+
+    static List<OpenSSLX509CRL> fromPkcs7DerInputStream(InputStream is)
+            throws ParsingException {
+        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
+
+        final long[] certRefs;
+        try {
+            certRefs = NativeCrypto.d2i_PKCS7_bio(bis.getBioContext(), NativeCrypto.PKCS7_CRLS);
+        } catch (Exception e) {
+            throw new ParsingException(e);
+        } finally {
+            bis.release();
+        }
+
+        final List<OpenSSLX509CRL> certs = new ArrayList<OpenSSLX509CRL>(certRefs.length);
+        for (int i = 0; i < certRefs.length; i++) {
+            if (certRefs[i] == 0) {
+                continue;
+            }
+            certs.add(new OpenSSLX509CRL(certRefs[i]));
+        }
+        return certs;
+    }
+
+    static OpenSSLX509CRL fromX509PemInputStream(InputStream is) throws ParsingException {
+        final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
+
+        try {
+            final long crlCtx = NativeCrypto.PEM_read_bio_X509_CRL(bis.getBioContext());
+            if (crlCtx == 0) {
+                return null;
+            }
+            return new OpenSSLX509CRL(crlCtx);
+        } catch (Exception e) {
+            throw new ParsingException(e);
+        } finally {
+            bis.release();
+        }
+    }
+
+    static List<OpenSSLX509CRL> fromPkcs7PemInputStream(InputStream is)
+            throws ParsingException {
+        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
+
+        final long[] certRefs;
+        try {
+            certRefs = NativeCrypto.PEM_read_bio_PKCS7(bis.getBioContext(),
+                    NativeCrypto.PKCS7_CRLS);
+        } catch (Exception e) {
+            throw new ParsingException(e);
+        } finally {
+            bis.release();
+        }
+
+        final List<OpenSSLX509CRL> certs = new ArrayList<OpenSSLX509CRL>(certRefs.length);
+        for (int i = 0; i < certRefs.length; i++) {
+            if (certRefs[i] == 0) {
+                continue;
+            }
+            certs.add(new OpenSSLX509CRL(certRefs[i]));
+        }
+        return certs;
+    }
+
+    @Override
+    public Set<String> getCriticalExtensionOIDs() {
+        String[] critOids =
+                NativeCrypto.get_X509_CRL_ext_oids(mContext, this, NativeCrypto.EXTENSION_TYPE_CRITICAL);
+
+        /*
+         * This API has a special case that if there are no extensions, we
+         * should return null. So if we have no critical extensions, we'll check
+         * non-critical extensions.
+         */
+        if ((critOids.length == 0)
+                && (NativeCrypto.get_X509_CRL_ext_oids(mContext, this,
+                        NativeCrypto.EXTENSION_TYPE_NON_CRITICAL).length == 0)) {
+            return null;
+        }
+
+        return new HashSet<String>(Arrays.asList(critOids));
+    }
+
+    @Override
+    public byte[] getExtensionValue(String oid) {
+        return NativeCrypto.X509_CRL_get_ext_oid(mContext, this, oid);
+    }
+
+    @Override
+    public Set<String> getNonCriticalExtensionOIDs() {
+        String[] nonCritOids =
+                NativeCrypto.get_X509_CRL_ext_oids(mContext, this,
+                        NativeCrypto.EXTENSION_TYPE_NON_CRITICAL);
+
+        /*
+         * This API has a special case that if there are no extensions, we
+         * should return null. So if we have no non-critical extensions, we'll
+         * check critical extensions.
+         */
+        if ((nonCritOids.length == 0)
+                && (NativeCrypto.get_X509_CRL_ext_oids(mContext, this,
+                        NativeCrypto.EXTENSION_TYPE_CRITICAL).length == 0)) {
+            return null;
+        }
+
+        return new HashSet<String>(Arrays.asList(nonCritOids));
+    }
+
+    @Override
+    public boolean hasUnsupportedCriticalExtension() {
+        final String[] criticalOids =
+                NativeCrypto.get_X509_CRL_ext_oids(mContext, this, NativeCrypto.EXTENSION_TYPE_CRITICAL);
+        for (String oid : criticalOids) {
+            final long extensionRef = NativeCrypto.X509_CRL_get_ext(mContext, this, oid);
+            if (NativeCrypto.X509_supported_extension(extensionRef) != 1) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public byte[] getEncoded() throws CRLException {
+        return NativeCrypto.i2d_X509_CRL(mContext, this);
+    }
+
+    private void verifyOpenSSL(OpenSSLKey pkey) throws CRLException, NoSuchAlgorithmException,
+            InvalidKeyException, NoSuchProviderException, SignatureException {
+        NativeCrypto.X509_CRL_verify(mContext, this, pkey.getNativeRef());
+    }
+
+    private void verifyInternal(PublicKey key, String sigProvider) throws CRLException,
+            NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException,
+            SignatureException {
+        String sigAlg = getSigAlgName();
+        if (sigAlg == null) {
+            sigAlg = getSigAlgOID();
+        }
+
+        final Signature sig;
+        if (sigProvider == null) {
+            sig = Signature.getInstance(sigAlg);
+        } else {
+            sig = Signature.getInstance(sigAlg, sigProvider);
+        }
+
+        sig.initVerify(key);
+        sig.update(getTBSCertList());
+        if (!sig.verify(getSignature())) {
+            throw new SignatureException("signature did not verify");
+        }
+    }
+
+    @Override
+    public void verify(PublicKey key) throws CRLException, NoSuchAlgorithmException,
+            InvalidKeyException, NoSuchProviderException, SignatureException {
+        if (key instanceof OpenSSLKeyHolder) {
+            OpenSSLKey pkey = ((OpenSSLKeyHolder) key).getOpenSSLKey();
+            verifyOpenSSL(pkey);
+            return;
+        }
+
+        verifyInternal(key, null);
+    }
+
+    @Override
+    public void verify(PublicKey key, String sigProvider) throws CRLException,
+            NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException,
+            SignatureException {
+        verifyInternal(key, sigProvider);
+    }
+
+    @Override
+    public int getVersion() {
+        return (int) NativeCrypto.X509_CRL_get_version(mContext, this) + 1;
+    }
+
+    @Override
+    public Principal getIssuerDN() {
+        return getIssuerX500Principal();
+    }
+
+    @Override
+    public X500Principal getIssuerX500Principal() {
+        final byte[] issuer = NativeCrypto.X509_CRL_get_issuer_name(mContext, this);
+        return new X500Principal(issuer);
+    }
+
+    @Override
+    public Date getThisUpdate() {
+        return (Date) thisUpdate.clone();
+    }
+
+    @Override
+    public Date getNextUpdate() {
+        return (Date) nextUpdate.clone();
+    }
+
+    @Override
+    public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) {
+        final long revokedRef = NativeCrypto.X509_CRL_get0_by_serial(mContext, this,
+                serialNumber.toByteArray());
+        if (revokedRef == 0) {
+            return null;
+        }
+        try {
+            return new OpenSSLX509CRLEntry(NativeCrypto.X509_REVOKED_dup(revokedRef));
+        } catch (ParsingException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public X509CRLEntry getRevokedCertificate(X509Certificate certificate) {
+        if (certificate instanceof OpenSSLX509Certificate) {
+            OpenSSLX509Certificate osslCert = (OpenSSLX509Certificate) certificate;
+            final long x509RevokedRef = NativeCrypto.X509_CRL_get0_by_cert(mContext, this,
+                    osslCert.getContext(), osslCert);
+
+            if (x509RevokedRef == 0) {
+                return null;
+            }
+
+            try {
+                return new OpenSSLX509CRLEntry(NativeCrypto.X509_REVOKED_dup(x509RevokedRef));
+            } catch (ParsingException e) {
+                return null;
+            }
+        }
+
+        return getRevokedCertificate(certificate.getSerialNumber());
+    }
+
+    @Override
+    public Set<? extends X509CRLEntry> getRevokedCertificates() {
+        final long[] entryRefs = NativeCrypto.X509_CRL_get_REVOKED(mContext, this);
+        if (entryRefs == null || entryRefs.length == 0) {
+            return null;
+        }
+
+        final Set<OpenSSLX509CRLEntry> crlSet = new HashSet<OpenSSLX509CRLEntry>();
+        for (long entryRef : entryRefs) {
+            try {
+                crlSet.add(new OpenSSLX509CRLEntry(entryRef));
+            } catch (ParsingException e) {
+                // Skip this entry
+            }
+        }
+
+        return crlSet;
+    }
+
+    @Override
+    public byte[] getTBSCertList() throws CRLException {
+        return NativeCrypto.get_X509_CRL_crl_enc(mContext, this);
+    }
+
+    @Override
+    public byte[] getSignature() {
+        return NativeCrypto.get_X509_CRL_signature(mContext, this);
+    }
+
+    @Override
+    public String getSigAlgName() {
+        String oid = getSigAlgOID();
+        String algName = OidData.oidToAlgorithmName(oid);
+        if (algName != null) {
+            return algName;
+        }
+        algName = Platform.oidToAlgorithmName(oid);
+        if (algName != null) {
+            return algName;
+        }
+        return oid;
+    }
+
+    @Override
+    public String getSigAlgOID() {
+        return NativeCrypto.get_X509_CRL_sig_alg_oid(mContext, this);
+    }
+
+    @Override
+    public byte[] getSigAlgParams() {
+        return NativeCrypto.get_X509_CRL_sig_alg_parameter(mContext, this);
+    }
+
+    @Override
+    public boolean isRevoked(Certificate cert) {
+        if (!(cert instanceof X509Certificate)) {
+            return false;
+        }
+
+        final OpenSSLX509Certificate osslCert;
+        if (cert instanceof OpenSSLX509Certificate) {
+            osslCert = (OpenSSLX509Certificate) cert;
+        } else {
+            try {
+                osslCert = OpenSSLX509Certificate.fromX509DerInputStream(new ByteArrayInputStream(
+                        cert.getEncoded()));
+            } catch (Exception e) {
+                throw new RuntimeException("cannot convert certificate", e);
+            }
+        }
+
+        final long x509RevokedRef = NativeCrypto.X509_CRL_get0_by_cert(mContext, this,
+                osslCert.getContext(), osslCert);
+
+        return x509RevokedRef != 0;
+    }
+
+    @Override
+    public String toString() {
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        final long bioCtx = NativeCrypto.create_BIO_OutputStream(os);
+        try {
+            NativeCrypto.X509_CRL_print(bioCtx, mContext, this);
+            return os.toString();
+        } finally {
+            NativeCrypto.BIO_free_all(bioCtx);
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mContext != 0) {
+                NativeCrypto.X509_CRL_free(mContext, this);
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CRLEntry.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CRLEntry.java
new file mode 100644
index 0000000..d8c5d9e
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CRLEntry.java
@@ -0,0 +1,138 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.ByteArrayOutputStream;
+import java.math.BigInteger;
+import java.security.cert.CRLException;
+import java.security.cert.X509CRLEntry;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+import com.android.org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
+
+/**
+ * An implementation of {@link X509CRLEntry} based on BoringSSL.
+ */
+final class OpenSSLX509CRLEntry extends X509CRLEntry {
+    private final long mContext;
+    private final Date revocationDate;
+
+    OpenSSLX509CRLEntry(long ctx) throws ParsingException {
+        mContext = ctx;
+        // The legacy X509 OpenSSL APIs don't validate ASN1_TIME structures until access, so
+        // parse them here because this is the only time we're allowed to throw ParsingException
+        revocationDate = OpenSSLX509CRL.toDate(NativeCrypto.get_X509_REVOKED_revocationDate(mContext));
+    }
+
+    @Override
+    public Set<String> getCriticalExtensionOIDs() {
+        String[] critOids =
+                NativeCrypto.get_X509_REVOKED_ext_oids(mContext,
+                        NativeCrypto.EXTENSION_TYPE_CRITICAL);
+
+        /*
+         * This API has a special case that if there are no extensions, we
+         * should return null. So if we have no critical extensions, we'll check
+         * non-critical extensions.
+         */
+        if ((critOids.length == 0)
+                && (NativeCrypto.get_X509_REVOKED_ext_oids(mContext,
+                        NativeCrypto.EXTENSION_TYPE_NON_CRITICAL).length == 0)) {
+            return null;
+        }
+
+        return new HashSet<String>(Arrays.asList(critOids));
+    }
+
+    @Override
+    public byte[] getExtensionValue(String oid) {
+        return NativeCrypto.X509_REVOKED_get_ext_oid(mContext, oid);
+    }
+
+    @Override
+    public Set<String> getNonCriticalExtensionOIDs() {
+        String[] critOids =
+                NativeCrypto.get_X509_REVOKED_ext_oids(mContext,
+                        NativeCrypto.EXTENSION_TYPE_NON_CRITICAL);
+
+        /*
+         * This API has a special case that if there are no extensions, we
+         * should return null. So if we have no non-critical extensions, we'll
+         * check critical extensions.
+         */
+        if ((critOids.length == 0)
+                && (NativeCrypto.get_X509_REVOKED_ext_oids(mContext,
+                        NativeCrypto.EXTENSION_TYPE_CRITICAL).length == 0)) {
+            return null;
+        }
+
+        return new HashSet<String>(Arrays.asList(critOids));
+    }
+
+    @Override
+    public boolean hasUnsupportedCriticalExtension() {
+        final String[] criticalOids =
+                NativeCrypto.get_X509_REVOKED_ext_oids(mContext,
+                        NativeCrypto.EXTENSION_TYPE_CRITICAL);
+        for (String oid : criticalOids) {
+            final long extensionRef = NativeCrypto.X509_REVOKED_get_ext(mContext, oid);
+            if (NativeCrypto.X509_supported_extension(extensionRef) != 1) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public byte[] getEncoded() throws CRLException {
+        return NativeCrypto.i2d_X509_REVOKED(mContext);
+    }
+
+    @Override
+    public BigInteger getSerialNumber() {
+        return new BigInteger(NativeCrypto.X509_REVOKED_get_serialNumber(mContext));
+    }
+
+    @Override
+    public Date getRevocationDate() {
+        return (Date) revocationDate.clone();
+    }
+
+    @Override
+    public boolean hasExtensions() {
+        return (NativeCrypto.get_X509_REVOKED_ext_oids(mContext,
+                NativeCrypto.EXTENSION_TYPE_NON_CRITICAL).length != 0)
+                || (NativeCrypto.get_X509_REVOKED_ext_oids(mContext,
+                        NativeCrypto.EXTENSION_TYPE_CRITICAL).length != 0);
+    }
+
+    @Override
+    public String toString() {
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        long bioCtx = NativeCrypto.create_BIO_OutputStream(os);
+        try {
+            NativeCrypto.X509_REVOKED_print(bioCtx, mContext);
+            return os.toString();
+        } finally {
+            NativeCrypto.BIO_free_all(bioCtx);
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CertPath.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CertPath.java
new file mode 100644
index 0000000..a76c1fc
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CertPath.java
@@ -0,0 +1,263 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.security.cert.CertPath;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import com.android.org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
+
+/**
+ * An implementation of {@link CertPath} based on BoringSSL.
+ */
+final class OpenSSLX509CertPath extends CertPath {
+    private static final long serialVersionUID = -3249106005255170761L;
+
+    private static final byte[] PKCS7_MARKER = new byte[] {
+            '-', '-', '-', '-', '-', 'B', 'E', 'G', 'I', 'N', ' ', 'P', 'K', 'C', 'S', '7'
+    };
+
+    private static final int PUSHBACK_SIZE = 64;
+
+    /**
+     * Supported encoding types for CerthPath. Used by the various APIs that
+     * encode this into bytes such as {@link #getEncoded()}.
+     */
+    private enum Encoding {
+        PKI_PATH("PkiPath"),
+        PKCS7("PKCS7");
+
+        private final String apiName;
+
+        Encoding(String apiName) {
+            this.apiName = apiName;
+        }
+
+        static Encoding findByApiName(String apiName) throws CertificateEncodingException {
+            for (Encoding element : values()) {
+                if (element.apiName.equals(apiName)) {
+                    return element;
+                }
+            }
+
+            return null;
+        }
+    }
+
+    /** Unmodifiable list of encodings for the API. */
+    private static final List<String> ALL_ENCODINGS = Collections.unmodifiableList(Arrays
+            .asList(new String[] {
+                    Encoding.PKI_PATH.apiName,
+                    Encoding.PKCS7.apiName,
+            }));
+
+    private static final Encoding DEFAULT_ENCODING = Encoding.PKI_PATH;
+
+    private final List<? extends X509Certificate> mCertificates;
+
+    static Iterator<String> getEncodingsIterator() {
+        return ALL_ENCODINGS.iterator();
+    }
+
+    OpenSSLX509CertPath(List<? extends X509Certificate> certificates) {
+        super("X.509");
+
+        mCertificates = certificates;
+    }
+
+    @Override
+    public List<? extends Certificate> getCertificates() {
+        return Collections.unmodifiableList(mCertificates);
+    }
+
+    private byte[] getEncoded(Encoding encoding) throws CertificateEncodingException {
+        final OpenSSLX509Certificate[] certs = new OpenSSLX509Certificate[mCertificates.size()];
+        final long[] certRefs = new long[certs.length];
+
+        for (int i = 0, j = certs.length - 1; j >= 0; i++, j--) {
+            final X509Certificate cert = mCertificates.get(i);
+
+            if (cert instanceof OpenSSLX509Certificate) {
+                certs[j] = (OpenSSLX509Certificate) cert;
+            } else {
+                certs[j] = OpenSSLX509Certificate.fromX509Der(cert.getEncoded());
+            }
+
+            certRefs[j] = certs[j].getContext();
+        }
+
+        switch (encoding) {
+            case PKI_PATH:
+                return NativeCrypto.ASN1_seq_pack_X509(certRefs);
+            case PKCS7:
+                return NativeCrypto.i2d_PKCS7(certRefs);
+            default:
+                throw new CertificateEncodingException("Unknown encoding");
+        }
+    }
+
+    @Override
+    public byte[] getEncoded() throws CertificateEncodingException {
+        return getEncoded(DEFAULT_ENCODING);
+    }
+
+    @Override
+    public byte[] getEncoded(String encoding) throws CertificateEncodingException {
+        Encoding enc = Encoding.findByApiName(encoding);
+        if (enc == null) {
+            throw new CertificateEncodingException("Invalid encoding: " + encoding);
+        }
+
+        return getEncoded(enc);
+    }
+
+    @Override
+    public Iterator<String> getEncodings() {
+        return getEncodingsIterator();
+    }
+
+    private static CertPath fromPkiPathEncoding(InputStream inStream) throws CertificateException {
+        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(inStream, true);
+
+        final boolean markable = inStream.markSupported();
+        if (markable) {
+            inStream.mark(PUSHBACK_SIZE);
+        }
+
+        final long[] certRefs;
+        try {
+            certRefs = NativeCrypto.ASN1_seq_unpack_X509_bio(bis.getBioContext());
+        } catch (Exception e) {
+            if (markable) {
+                try {
+                    inStream.reset();
+                } catch (IOException ignored) {
+                }
+            }
+            throw new CertificateException(e);
+        } finally {
+            bis.release();
+        }
+
+        if (certRefs == null) {
+            return new OpenSSLX509CertPath(Collections.<X509Certificate> emptyList());
+        }
+
+        final List<OpenSSLX509Certificate> certs =
+                new ArrayList<OpenSSLX509Certificate>(certRefs.length);
+        for (int i = certRefs.length - 1; i >= 0; i--) {
+            if (certRefs[i] == 0) {
+                continue;
+            }
+            try {
+                certs.add(new OpenSSLX509Certificate(certRefs[i]));
+            } catch (ParsingException e) {
+                throw new CertificateParsingException(e);
+            }
+        }
+
+        return new OpenSSLX509CertPath(certs);
+    }
+
+    private static CertPath fromPkcs7Encoding(InputStream inStream) throws CertificateException {
+        try {
+            if (inStream == null || inStream.available() == 0) {
+                return new OpenSSLX509CertPath(Collections.<X509Certificate> emptyList());
+            }
+        } catch (IOException e) {
+            throw new CertificateException("Problem reading input stream", e);
+        }
+
+        final boolean markable = inStream.markSupported();
+        if (markable) {
+            inStream.mark(PUSHBACK_SIZE);
+        }
+
+        /* Attempt to see if this is a PKCS#7 bag. */
+        final PushbackInputStream pbis = new PushbackInputStream(inStream, PUSHBACK_SIZE);
+        try {
+            final byte[] buffer = new byte[PKCS7_MARKER.length];
+
+            final int len = pbis.read(buffer);
+            if (len < 0) {
+                /* No need to reset here. The stream was empty or EOF. */
+                throw new ParsingException("inStream is empty");
+            }
+            pbis.unread(buffer, 0, len);
+
+            if (len == PKCS7_MARKER.length && Arrays.equals(PKCS7_MARKER, buffer)) {
+                return new OpenSSLX509CertPath(OpenSSLX509Certificate.fromPkcs7PemInputStream(pbis));
+            }
+
+            return new OpenSSLX509CertPath(OpenSSLX509Certificate.fromPkcs7DerInputStream(pbis));
+        } catch (Exception e) {
+            if (markable) {
+                try {
+                    inStream.reset();
+                } catch (IOException ignored) {
+                }
+            }
+            throw new CertificateException(e);
+        }
+    }
+
+    private static CertPath fromEncoding(InputStream inStream, Encoding encoding)
+            throws CertificateException {
+        switch (encoding) {
+            case PKI_PATH:
+                return fromPkiPathEncoding(inStream);
+            case PKCS7:
+                return fromPkcs7Encoding(inStream);
+            default:
+                throw new CertificateEncodingException("Unknown encoding");
+        }
+    }
+
+    static CertPath fromEncoding(InputStream inStream, String encoding)
+            throws CertificateException {
+        if (inStream == null) {
+            throw new CertificateException("inStream == null");
+        }
+
+        Encoding enc = Encoding.findByApiName(encoding);
+        if (enc == null) {
+            throw new CertificateException("Invalid encoding: " + encoding);
+        }
+
+        return fromEncoding(inStream, enc);
+    }
+
+    static CertPath fromEncoding(InputStream inStream) throws CertificateException {
+        if (inStream == null) {
+            throw new CertificateException("inStream == null");
+        }
+
+        return fromEncoding(inStream, DEFAULT_ENCODING);
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509Certificate.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509Certificate.java
new file mode 100644
index 0000000..e8e7d50
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509Certificate.java
@@ -0,0 +1,594 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TimeZone;
+import javax.crypto.BadPaddingException;
+import javax.security.auth.x500.X500Principal;
+import com.android.org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
+
+/**
+ * An implementation of {@link X509Certificate} based on BoringSSL.
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public final class OpenSSLX509Certificate extends X509Certificate {
+    private static final long serialVersionUID = 1992239142393372128L;
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    private transient final long mContext;
+    private transient Integer mHashCode;
+
+    private final Date notBefore;
+    private final Date notAfter;
+
+    OpenSSLX509Certificate(long ctx) throws ParsingException {
+        mContext = ctx;
+        // The legacy X509 OpenSSL APIs don't validate ASN1_TIME structures until access, so
+        // parse them here because this is the only time we're allowed to throw ParsingException
+        notBefore = toDate(NativeCrypto.X509_get_notBefore(mContext, this));
+        notAfter = toDate(NativeCrypto.X509_get_notAfter(mContext, this));
+    }
+
+    // A non-throwing constructor used when we have already parsed the dates
+    private OpenSSLX509Certificate(long ctx, Date notBefore, Date notAfter) {
+        mContext = ctx;
+        this.notBefore = notBefore;
+        this.notAfter = notAfter;
+    }
+
+    private static Date toDate(long asn1time) throws ParsingException {
+        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+        calendar.set(Calendar.MILLISECOND, 0);
+        NativeCrypto.ASN1_TIME_to_Calendar(asn1time, calendar);
+        return calendar.getTime();
+    }
+
+    public static OpenSSLX509Certificate fromX509DerInputStream(InputStream is)
+            throws ParsingException {
+        @SuppressWarnings("resource")
+        final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
+
+        try {
+            final long certCtx = NativeCrypto.d2i_X509_bio(bis.getBioContext());
+            if (certCtx == 0) {
+                return null;
+            }
+            return new OpenSSLX509Certificate(certCtx);
+        } catch (Exception e) {
+            throw new ParsingException(e);
+        } finally {
+            bis.release();
+        }
+    }
+
+    public static OpenSSLX509Certificate fromX509Der(byte[] encoded)
+            throws CertificateEncodingException {
+        try {
+            return new OpenSSLX509Certificate(NativeCrypto.d2i_X509(encoded));
+        } catch (ParsingException e) {
+            throw new CertificateEncodingException(e);
+        }
+    }
+
+    public static List<OpenSSLX509Certificate> fromPkcs7DerInputStream(InputStream is)
+            throws ParsingException {
+        @SuppressWarnings("resource")
+        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
+
+        final long[] certRefs;
+        try {
+            certRefs = NativeCrypto.d2i_PKCS7_bio(bis.getBioContext(), NativeCrypto.PKCS7_CERTS);
+        } catch (Exception e) {
+            throw new ParsingException(e);
+        } finally {
+            bis.release();
+        }
+
+        if (certRefs == null) {
+            return Collections.emptyList();
+        }
+
+        final List<OpenSSLX509Certificate> certs = new ArrayList<OpenSSLX509Certificate>(
+                certRefs.length);
+        for (int i = 0; i < certRefs.length; i++) {
+            if (certRefs[i] == 0) {
+                continue;
+            }
+            certs.add(new OpenSSLX509Certificate(certRefs[i]));
+        }
+        return certs;
+    }
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    public static OpenSSLX509Certificate fromX509PemInputStream(InputStream is)
+            throws ParsingException {
+        @SuppressWarnings("resource")
+        final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
+
+        try {
+            final long certCtx = NativeCrypto.PEM_read_bio_X509(bis.getBioContext());
+            if (certCtx == 0L) {
+                return null;
+            }
+            return new OpenSSLX509Certificate(certCtx);
+        } catch (Exception e) {
+            throw new ParsingException(e);
+        } finally {
+            bis.release();
+        }
+    }
+
+    public static List<OpenSSLX509Certificate> fromPkcs7PemInputStream(InputStream is)
+            throws ParsingException {
+        @SuppressWarnings("resource")
+        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
+
+        final long[] certRefs;
+        try {
+            certRefs = NativeCrypto.PEM_read_bio_PKCS7(bis.getBioContext(),
+                    NativeCrypto.PKCS7_CERTS);
+        } catch (Exception e) {
+            throw new ParsingException(e);
+        } finally {
+            bis.release();
+        }
+
+        final List<OpenSSLX509Certificate> certs = new ArrayList<OpenSSLX509Certificate>(
+                certRefs.length);
+        for (int i = 0; i < certRefs.length; i++) {
+            if (certRefs[i] == 0) {
+                continue;
+            }
+            certs.add(new OpenSSLX509Certificate(certRefs[i]));
+        }
+        return certs;
+    }
+
+    public static OpenSSLX509Certificate fromCertificate(Certificate cert)
+            throws CertificateEncodingException {
+        if (cert instanceof OpenSSLX509Certificate) {
+            return (OpenSSLX509Certificate) cert;
+        } else if (cert instanceof X509Certificate) {
+            return fromX509Der(cert.getEncoded());
+        } else {
+            throw new CertificateEncodingException("Only X.509 certificates are supported");
+        }
+    }
+
+    @Override
+    public Set<String> getCriticalExtensionOIDs() {
+        String[] critOids =
+                NativeCrypto.get_X509_ext_oids(mContext, this, NativeCrypto.EXTENSION_TYPE_CRITICAL);
+
+        /*
+         * This API has a special case that if there are no extensions, we
+         * should return null. So if we have no critical extensions, we'll check
+         * non-critical extensions.
+         */
+        if ((critOids.length == 0)
+                && (NativeCrypto.get_X509_ext_oids(mContext, this,
+                        NativeCrypto.EXTENSION_TYPE_NON_CRITICAL).length == 0)) {
+            return null;
+        }
+
+        return new HashSet<String>(Arrays.asList(critOids));
+    }
+
+    @Override
+    public byte[] getExtensionValue(String oid) {
+        return NativeCrypto.X509_get_ext_oid(mContext, this, oid);
+    }
+
+    @Override
+    public Set<String> getNonCriticalExtensionOIDs() {
+        String[] nonCritOids =
+                NativeCrypto.get_X509_ext_oids(mContext, this, NativeCrypto.EXTENSION_TYPE_NON_CRITICAL);
+
+        /*
+         * This API has a special case that if there are no extensions, we
+         * should return null. So if we have no non-critical extensions, we'll
+         * check critical extensions.
+         */
+        if ((nonCritOids.length == 0)
+                && (NativeCrypto.get_X509_ext_oids(mContext, this,
+                        NativeCrypto.EXTENSION_TYPE_CRITICAL).length == 0)) {
+            return null;
+        }
+
+        return new HashSet<String>(Arrays.asList(nonCritOids));
+    }
+
+    @Override
+    public boolean hasUnsupportedCriticalExtension() {
+        return (NativeCrypto.get_X509_ex_flags(mContext, this) & NativeConstants.EXFLAG_CRITICAL) != 0;
+    }
+
+    @Override
+    public void checkValidity() throws CertificateExpiredException,
+            CertificateNotYetValidException {
+        checkValidity(new Date());
+    }
+
+    @Override
+    public void checkValidity(Date date) throws CertificateExpiredException,
+            CertificateNotYetValidException {
+        if (getNotBefore().compareTo(date) > 0) {
+            throw new CertificateNotYetValidException("Certificate not valid until "
+                    + getNotBefore().toString() + " (compared to " + date.toString() + ")");
+        }
+
+        if (getNotAfter().compareTo(date) < 0) {
+            throw new CertificateExpiredException("Certificate expired at "
+                    + getNotAfter().toString() + " (compared to " + date.toString() + ")");
+        }
+    }
+
+    @Override
+    public int getVersion() {
+        return (int) NativeCrypto.X509_get_version(mContext, this) + 1;
+    }
+
+    @Override
+    public BigInteger getSerialNumber() {
+        return new BigInteger(NativeCrypto.X509_get_serialNumber(mContext, this));
+    }
+
+    @Override
+    public Principal getIssuerDN() {
+        return getIssuerX500Principal();
+    }
+
+    @Override
+    public Principal getSubjectDN() {
+        return getSubjectX500Principal();
+    }
+
+    @Override
+    public Date getNotBefore() {
+        return (Date) notBefore.clone();
+    }
+
+    @Override
+    public Date getNotAfter() {
+        return (Date) notAfter.clone();
+    }
+
+    @Override
+    public byte[] getTBSCertificate() throws CertificateEncodingException {
+        return NativeCrypto.get_X509_cert_info_enc(mContext, this);
+    }
+
+    @Override
+    public byte[] getSignature() {
+        return NativeCrypto.get_X509_signature(mContext, this);
+    }
+
+    @Override
+    public String getSigAlgName() {
+        String oid = getSigAlgOID();
+        String algName = OidData.oidToAlgorithmName(oid);
+        if (algName != null) {
+            return algName;
+        }
+        algName = Platform.oidToAlgorithmName(oid);
+        if (algName != null) {
+            return algName;
+        }
+        return oid;
+    }
+
+    @Override
+    public String getSigAlgOID() {
+        return NativeCrypto.get_X509_sig_alg_oid(mContext, this);
+    }
+
+    @Override
+    public byte[] getSigAlgParams() {
+        return NativeCrypto.get_X509_sig_alg_parameter(mContext, this);
+    }
+
+    @Override
+    public boolean[] getIssuerUniqueID() {
+        return NativeCrypto.get_X509_issuerUID(mContext, this);
+    }
+
+    @Override
+    public boolean[] getSubjectUniqueID() {
+        return NativeCrypto.get_X509_subjectUID(mContext, this);
+    }
+
+    @Override
+    public boolean[] getKeyUsage() {
+        final boolean[] kusage = NativeCrypto.get_X509_ex_kusage(mContext, this);
+        if (kusage == null) {
+            return null;
+        }
+
+        if (kusage.length >= 9) {
+            return kusage;
+        }
+
+        final boolean[] resized = new boolean[9];
+        System.arraycopy(kusage, 0, resized, 0, kusage.length);
+        return resized;
+    }
+
+    @Override
+    public int getBasicConstraints() {
+        if ((NativeCrypto.get_X509_ex_flags(mContext, this) & NativeConstants.EXFLAG_CA) == 0) {
+            return -1;
+        }
+
+        final int pathLen = NativeCrypto.get_X509_ex_pathlen(mContext, this);
+        if (pathLen == -1) {
+            return Integer.MAX_VALUE;
+        }
+
+        return pathLen;
+    }
+
+    @Override
+    public byte[] getEncoded() throws CertificateEncodingException {
+        return NativeCrypto.i2d_X509(mContext, this);
+    }
+
+    private void verifyOpenSSL(OpenSSLKey pkey) throws CertificateException,
+                                                       NoSuchAlgorithmException,
+                                                       InvalidKeyException, SignatureException {
+        try {
+            NativeCrypto.X509_verify(mContext, this, pkey.getNativeRef());
+        } catch (RuntimeException e) {
+            throw new CertificateException(e);
+        } catch (BadPaddingException e) {
+            throw new SignatureException();
+        }
+    }
+
+    private void verifyInternal(PublicKey key, String sigProvider) throws CertificateException,
+            NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException,
+            SignatureException {
+        final Signature sig;
+        if (sigProvider == null) {
+            sig = Signature.getInstance(getSigAlgName());
+        } else {
+            sig = Signature.getInstance(getSigAlgName(), sigProvider);
+        }
+
+        sig.initVerify(key);
+        sig.update(getTBSCertificate());
+        if (!sig.verify(getSignature())) {
+            throw new SignatureException("signature did not verify");
+        }
+    }
+
+    @Override
+    public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException,
+            InvalidKeyException, NoSuchProviderException, SignatureException {
+        if (key instanceof OpenSSLKeyHolder) {
+            OpenSSLKey pkey = ((OpenSSLKeyHolder) key).getOpenSSLKey();
+            verifyOpenSSL(pkey);
+            return;
+        }
+
+        verifyInternal(key, (String) null);
+    }
+
+    @Override
+    public void verify(PublicKey key, String sigProvider) throws CertificateException,
+            NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException,
+            SignatureException {
+        verifyInternal(key, sigProvider);
+    }
+
+    /* @Override */
+    @SuppressWarnings("MissingOverride")  // For compilation with Java 7.
+    // noinspection Override
+    public void verify(PublicKey key, Provider sigProvider)
+            throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
+                   SignatureException {
+        if (key instanceof OpenSSLKeyHolder && sigProvider instanceof OpenSSLProvider) {
+            OpenSSLKey pkey = ((OpenSSLKeyHolder) key).getOpenSSLKey();
+            verifyOpenSSL(pkey);
+            return;
+        }
+
+        final Signature sig;
+        if (sigProvider == null) {
+            sig = Signature.getInstance(getSigAlgName());
+        } else {
+            sig = Signature.getInstance(getSigAlgName(), sigProvider);
+        }
+
+        sig.initVerify(key);
+        sig.update(getTBSCertificate());
+        if (!sig.verify(getSignature())) {
+            throw new SignatureException("signature did not verify");
+        }
+    }
+
+    @Override
+    public String toString() {
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        long bioCtx = NativeCrypto.create_BIO_OutputStream(os);
+        try {
+            NativeCrypto.X509_print_ex(bioCtx, mContext, this, 0, 0);
+            return os.toString();
+        } finally {
+            NativeCrypto.BIO_free_all(bioCtx);
+        }
+    }
+
+    @Override
+    public PublicKey getPublicKey() {
+        /* First try to generate the key from supported OpenSSL key types. */
+        try {
+            OpenSSLKey pkey = new OpenSSLKey(NativeCrypto.X509_get_pubkey(mContext, this));
+            return pkey.getPublicKey();
+        } catch (NoSuchAlgorithmException ignored) {
+        } catch (InvalidKeyException ignored) {
+        }
+
+        /* Try generating the key using other Java providers. */
+        String oid = NativeCrypto.get_X509_pubkey_oid(mContext, this);
+        byte[] encoded = NativeCrypto.i2d_X509_PUBKEY(mContext, this);
+        try {
+            KeyFactory kf = KeyFactory.getInstance(oid);
+            return kf.generatePublic(new X509EncodedKeySpec(encoded));
+        } catch (NoSuchAlgorithmException ignored) {
+        } catch (InvalidKeySpecException ignored) {
+        }
+
+        /*
+         * We couldn't find anything else, so just return a nearly-unusable
+         * X.509-encoded key.
+         */
+        return new X509PublicKey(oid, encoded);
+    }
+
+    @Override
+    public X500Principal getIssuerX500Principal() {
+        final byte[] issuer = NativeCrypto.X509_get_issuer_name(mContext, this);
+        return new X500Principal(issuer);
+    }
+
+    @Override
+    public X500Principal getSubjectX500Principal() {
+        final byte[] subject = NativeCrypto.X509_get_subject_name(mContext, this);
+        return new X500Principal(subject);
+    }
+
+    @Override
+    public List<String> getExtendedKeyUsage() throws CertificateParsingException {
+        String[] extUsage = NativeCrypto.get_X509_ex_xkusage(mContext, this);
+        if (extUsage == null) {
+            return null;
+        }
+
+        return Arrays.asList(extUsage);
+    }
+
+    private static Collection<List<?>> alternativeNameArrayToList(Object[][] altNameArray) {
+        if (altNameArray == null) {
+            return null;
+        }
+
+        Collection<List<?>> coll = new ArrayList<List<?>>(altNameArray.length);
+        for (int i = 0; i < altNameArray.length; i++) {
+            coll.add(Collections.unmodifiableList(Arrays.asList(altNameArray[i])));
+        }
+
+        return Collections.unmodifiableCollection(coll);
+    }
+
+    @Override
+    public Collection<List<?>> getSubjectAlternativeNames() throws CertificateParsingException {
+        return alternativeNameArrayToList(NativeCrypto.get_X509_GENERAL_NAME_stack(mContext, this,
+                NativeCrypto.GN_STACK_SUBJECT_ALT_NAME));
+    }
+
+    @Override
+    public Collection<List<?>> getIssuerAlternativeNames() throws CertificateParsingException {
+        return alternativeNameArrayToList(NativeCrypto.get_X509_GENERAL_NAME_stack(mContext, this,
+                NativeCrypto.GN_STACK_ISSUER_ALT_NAME));
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof OpenSSLX509Certificate) {
+            OpenSSLX509Certificate o = (OpenSSLX509Certificate) other;
+
+            return NativeCrypto.X509_cmp(mContext, this, o.mContext, o) == 0;
+        }
+
+        return super.equals(other);
+    }
+
+    @Override
+    public int hashCode() {
+        if (mHashCode != null) {
+            return mHashCode;
+        }
+        mHashCode = super.hashCode();
+        return mHashCode;
+    }
+
+    /**
+     * Returns the raw pointer to the X509 context for use in JNI calls. The
+     * life cycle of this native pointer is managed by the
+     * {@code OpenSSLX509Certificate} instance and must not be destroyed or
+     * freed by users of this API.
+     */
+    public long getContext() {
+        return mContext;
+    }
+
+    /**
+     * Delete an extension.
+     *
+     * A modified copy of the certificate is returned. The original object
+     * is unchanged.
+     * If the extension is not present, an unmodified copy is returned.
+     */
+    public OpenSSLX509Certificate withDeletedExtension(String oid) {
+        OpenSSLX509Certificate copy = new OpenSSLX509Certificate(NativeCrypto.X509_dup(mContext, this), notBefore, notAfter);
+        NativeCrypto.X509_delete_ext(copy.getContext(), copy, oid);
+        return copy;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mContext != 0) {
+                NativeCrypto.X509_free(mContext, this);
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CertificateFactory.java b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CertificateFactory.java
new file mode 100644
index 0000000..6097a71
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/OpenSSLX509CertificateFactory.java
@@ -0,0 +1,352 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.security.cert.CRL;
+import java.security.cert.CRLException;
+import java.security.cert.CertPath;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactorySpi;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An implementation of {@link java.security.cert.CertificateFactory} based on BoringSSL.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public class OpenSSLX509CertificateFactory extends CertificateFactorySpi {
+    private static final byte[] PKCS7_MARKER = new byte[] {
+            '-', '-', '-', '-', '-', 'B', 'E', 'G', 'I', 'N', ' ', 'P', 'K', 'C', 'S', '7'
+    };
+
+    private static final int PUSHBACK_SIZE = 64;
+
+    static class ParsingException extends Exception {
+        private static final long serialVersionUID = 8390802697728301325L;
+
+        ParsingException(String message) {
+            super(message);
+        }
+
+        ParsingException(Exception cause) {
+            super(cause);
+        }
+
+        ParsingException(String message, Exception cause) {
+            super(message, cause);
+        }
+    }
+
+    /**
+     * The code for X509 Certificates and CRL is pretty much the same. We use
+     * this abstract class to share the code between them. This makes it ugly,
+     * but it's already written in this language anyway.
+     */
+    private static abstract class Parser<T> {
+        T generateItem(InputStream inStream) throws ParsingException {
+            if (inStream == null) {
+                throw new ParsingException("inStream == null");
+            }
+
+            final boolean markable = inStream.markSupported();
+            if (markable) {
+                inStream.mark(PKCS7_MARKER.length);
+            }
+
+            final PushbackInputStream pbis = new PushbackInputStream(inStream, PUSHBACK_SIZE);
+            try {
+                final byte[] buffer = new byte[PKCS7_MARKER.length];
+
+                final int len = pbis.read(buffer);
+                if (len < 0) {
+                    /* No need to reset here. The stream was empty or EOF. */
+                    throw new ParsingException("inStream is empty");
+                }
+                pbis.unread(buffer, 0, len);
+
+                if (buffer[0] == '-') {
+                    if (len == PKCS7_MARKER.length && Arrays.equals(PKCS7_MARKER, buffer)) {
+                        List<? extends T> items = fromPkcs7PemInputStream(pbis);
+                        if (items.size() == 0) {
+                            return null;
+                        }
+                        items.get(0);
+                    } else {
+                        return fromX509PemInputStream(pbis);
+                    }
+                }
+
+                /* PKCS#7 bags have a byte 0x06 at position 4 in the stream. */
+                if (buffer[4] == 0x06) {
+                    List<? extends T> certs = fromPkcs7DerInputStream(pbis);
+                    if (certs.size() == 0) {
+                        return null;
+                    }
+                    return certs.get(0);
+                } else {
+                    return fromX509DerInputStream(pbis);
+                }
+            } catch (Exception e) {
+                if (markable) {
+                    try {
+                        inStream.reset();
+                    } catch (IOException ignored) {
+                    }
+                }
+                throw new ParsingException(e);
+            }
+        }
+
+        Collection<? extends T> generateItems(InputStream inStream)
+                throws ParsingException {
+            if (inStream == null) {
+                throw new ParsingException("inStream == null");
+            }
+            try {
+                if (inStream.available() == 0) {
+                    return Collections.emptyList();
+                }
+            } catch (IOException e) {
+                throw new ParsingException("Problem reading input stream", e);
+            }
+
+            final boolean markable = inStream.markSupported();
+            if (markable) {
+                inStream.mark(PUSHBACK_SIZE);
+            }
+
+            /* Attempt to see if this is a PKCS#7 bag. */
+            final PushbackInputStream pbis = new PushbackInputStream(inStream, PUSHBACK_SIZE);
+            try {
+                final byte[] buffer = new byte[PKCS7_MARKER.length];
+
+                final int len = pbis.read(buffer);
+                if (len < 0) {
+                    /* No need to reset here. The stream was empty or EOF. */
+                    throw new ParsingException("inStream is empty");
+                }
+                pbis.unread(buffer, 0, len);
+
+                if (len == PKCS7_MARKER.length && Arrays.equals(PKCS7_MARKER, buffer)) {
+                    return fromPkcs7PemInputStream(pbis);
+                }
+
+                /* PKCS#7 bags have a byte 0x06 at position 4 in the stream. */
+                if (buffer[4] == 0x06) {
+                    return fromPkcs7DerInputStream(pbis);
+                }
+            } catch (Exception e) {
+                if (markable) {
+                    try {
+                        inStream.reset();
+                    } catch (IOException ignored) {
+                    }
+                }
+                throw new ParsingException(e);
+            }
+
+            /*
+             * It wasn't, so just try to keep grabbing certificates until we
+             * can't anymore.
+             */
+            final List<T> coll = new ArrayList<T>();
+            T c;
+            do {
+                /*
+                 * If this stream supports marking, try to mark here in case
+                 * there is an error during certificate generation.
+                 */
+                if (markable) {
+                    inStream.mark(PUSHBACK_SIZE);
+                }
+
+                try {
+                    c = generateItem(pbis);
+                    coll.add(c);
+                } catch (ParsingException e) {
+                    /*
+                     * If this stream supports marking, attempt to reset it to
+                     * the mark before the failure.
+                     */
+                    if (markable) {
+                        try {
+                            inStream.reset();
+                        } catch (IOException ignored) {
+                        }
+                    }
+
+                    c = null;
+                }
+            } while (c != null);
+
+            return coll;
+        }
+
+        protected abstract T fromX509PemInputStream(InputStream pbis) throws ParsingException;
+
+        protected abstract T fromX509DerInputStream(InputStream pbis) throws ParsingException;
+
+        protected abstract List<? extends T> fromPkcs7PemInputStream(InputStream is)
+                throws ParsingException;
+
+        protected abstract List<? extends T> fromPkcs7DerInputStream(InputStream is)
+                throws ParsingException;
+    }
+
+    private Parser<OpenSSLX509Certificate> certificateParser =
+            new Parser<OpenSSLX509Certificate>() {
+                @Override
+                public OpenSSLX509Certificate fromX509PemInputStream(InputStream is)
+                        throws ParsingException {
+                    return OpenSSLX509Certificate.fromX509PemInputStream(is);
+                }
+
+                @Override
+                public OpenSSLX509Certificate fromX509DerInputStream(InputStream is)
+                        throws ParsingException {
+                    return OpenSSLX509Certificate.fromX509DerInputStream(is);
+                }
+
+                @Override
+                public List<? extends OpenSSLX509Certificate>
+                        fromPkcs7PemInputStream(InputStream is) throws ParsingException {
+                    return OpenSSLX509Certificate.fromPkcs7PemInputStream(is);
+                }
+
+                @Override
+                public List<? extends OpenSSLX509Certificate>
+                        fromPkcs7DerInputStream(InputStream is) throws ParsingException {
+                    return OpenSSLX509Certificate.fromPkcs7DerInputStream(is);
+                }
+            };
+
+    private Parser<OpenSSLX509CRL> crlParser =
+            new Parser<OpenSSLX509CRL>() {
+                @Override
+                public OpenSSLX509CRL fromX509PemInputStream(InputStream is)
+                        throws ParsingException {
+                    return OpenSSLX509CRL.fromX509PemInputStream(is);
+                }
+
+                @Override
+                public OpenSSLX509CRL fromX509DerInputStream(InputStream is)
+                        throws ParsingException {
+                    return OpenSSLX509CRL.fromX509DerInputStream(is);
+                }
+
+                @Override
+                public List<? extends OpenSSLX509CRL> fromPkcs7PemInputStream(InputStream is)
+                        throws ParsingException {
+                    return OpenSSLX509CRL.fromPkcs7PemInputStream(is);
+                }
+
+                @Override
+                public List<? extends OpenSSLX509CRL> fromPkcs7DerInputStream(InputStream is)
+                        throws ParsingException {
+                    return OpenSSLX509CRL.fromPkcs7DerInputStream(is);
+                }
+            };
+
+    @libcore.api.IntraCoreApi
+    public OpenSSLX509CertificateFactory() {}
+
+    @Override
+    public Certificate engineGenerateCertificate(InputStream inStream) throws CertificateException {
+        try {
+            return certificateParser.generateItem(inStream);
+        } catch (ParsingException e) {
+            throw new CertificateException(e);
+        }
+    }
+
+    @Override
+    public Collection<? extends Certificate> engineGenerateCertificates(
+            InputStream inStream) throws CertificateException {
+        try {
+            return certificateParser.generateItems(inStream);
+        } catch (ParsingException e) {
+            throw new CertificateException(e);
+        }
+    }
+
+    @Override
+    public CRL engineGenerateCRL(InputStream inStream) throws CRLException {
+        try {
+            return crlParser.generateItem(inStream);
+        } catch (ParsingException e) {
+            throw new CRLException(e);
+        }
+    }
+
+    @Override
+    public Collection<? extends CRL> engineGenerateCRLs(InputStream inStream) throws CRLException {
+        if (inStream == null) {
+            return Collections.emptyList();
+        }
+
+        try {
+            return crlParser.generateItems(inStream);
+        } catch (ParsingException e) {
+            throw new CRLException(e);
+        }
+    }
+
+    @Override
+    public Iterator<String> engineGetCertPathEncodings() {
+        return OpenSSLX509CertPath.getEncodingsIterator();
+    }
+
+    @Override
+    public CertPath engineGenerateCertPath(InputStream inStream) throws CertificateException {
+        return OpenSSLX509CertPath.fromEncoding(inStream);
+    }
+
+    @Override
+    public CertPath engineGenerateCertPath(InputStream inStream, String encoding)
+            throws CertificateException {
+        return OpenSSLX509CertPath.fromEncoding(inStream, encoding);
+    }
+
+    @Override
+    public CertPath engineGenerateCertPath(List<? extends Certificate> certificates)
+            throws CertificateException {
+        final List<X509Certificate> filtered = new ArrayList<X509Certificate>(certificates.size());
+        for (int i = 0; i < certificates.size(); i++) {
+            final Certificate c = certificates.get(i);
+
+            if (!(c instanceof X509Certificate)) {
+                throw new CertificateException("Certificate not X.509 type at index " + i);
+            }
+
+            filtered.add((X509Certificate) c);
+        }
+
+        return new OpenSSLX509CertPath(filtered);
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/PSKKeyManager.java b/repackaged/common/src/main/java/com/android/org/conscrypt/PSKKeyManager.java
new file mode 100644
index 0000000..6bc09ad
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/PSKKeyManager.java
@@ -0,0 +1,177 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.net.Socket;
+import javax.crypto.SecretKey;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Provider of key material for pre-shared key (PSK) key exchange used in TLS-PSK cipher suites.
+ *
+ * <h3>Overview of TLS-PSK</h3>
+ *
+ * <p>TLS-PSK is a set of TLS/SSL cipher suites which rely on a symmetric pre-shared key (PSK) to
+ * secure the TLS/SSL connection and mutually authenticate its peers. These cipher suites may be
+ * a more natural fit compared to conventional public key based cipher suites in some scenarios
+ * where communication between peers is bootstrapped via a separate step (for example, a pairing
+ * step) and requires both peers to authenticate each other. In such scenarios a symmetric key (PSK)
+ * can be exchanged during the bootstrapping step, removing the need to generate and exchange public
+ * key pairs and X.509 certificates.</p>
+ *
+ * <p>When a TLS-PSK cipher suite is used, both peers have to use the same key for the TLS/SSL
+ * handshake to succeed. Thus, both peers are implicitly authenticated by a successful handshake.
+ * This removes the need to use a {@code TrustManager} in conjunction with this {@code KeyManager}.
+ * </p>
+ *
+ * <h3>Supporting multiple keys</h3>
+ *
+ * <p>A peer may have multiple keys to choose from. To help choose the right key, during the
+ * handshake the server can provide a <em>PSK identity hint</em> to the client, and the client can
+ * provide a <em>PSK identity</em> to the server. The contents of these two pieces of information
+ * are specific to application-level protocols.</p>
+ *
+ * <p><em>NOTE: Both the PSK identity hint and the PSK identity are transmitted in cleartext.
+ * Moreover, these data are received and processed prior to peer having been authenticated. Thus,
+ * they must not contain or leak key material or other sensitive information, and should be
+ * treated (e.g., parsed) with caution, as untrusted data.</em></p>
+ *
+ * <p>The high-level flow leading to peers choosing a key during TLS/SSL handshake is as follows:
+ * <ol>
+ * <li>Server receives a handshake request from client.
+ * <li>Server replies, optionally providing a PSK identity hint to client.</li>
+ * <li>Client chooses the key.</li>
+ * <li>Client provides a PSK identity of the chosen key to server.</li>
+ * <li>Server chooses the key.</li>
+ * </ol></p>
+ *
+ * <p>In the flow above, either peer can signal that they do not have a suitable key, in which case
+ * the the handshake will be aborted immediately. This may enable a network attacker who does not
+ * know the key to learn which PSK identity hints or PSK identities are supported. If this is a
+ * concern then a randomly generated key should be used in the scenario where no key is available.
+ * This will lead to the handshake aborting later, due to key mismatch -- same as in the scenario
+ * where a key is available -- making it appear to the attacker that all PSK identity hints and PSK
+ * identities are supported.</p>
+ *
+ * <h3>Maximum sizes</h3>
+ *
+ * <p>The maximum supported sizes are as follows:
+ * <ul>
+ * <li>256 bytes for keys (see {@link #MAX_KEY_LENGTH_BYTES}),</li>
+ * <li>128 bytes for PSK identity and PSK identity hint (in modified UTF-8 representation) (see
+ * {@link #MAX_IDENTITY_LENGTH_BYTES} and {@link #MAX_IDENTITY_HINT_LENGTH_BYTES}).</li>
+ * </ul></p>
+ *
+ * <h3>Example</h3>
+ * The following example illustrates how to create an {@code SSLContext} which enables the use of
+ * TLS-PSK in {@code SSLSocket}, {@code SSLServerSocket} and {@code SSLEngine} instances obtained
+ * from it.
+ * <pre> {@code
+ * PSKKeyManager myPskKeyManager = ...;
+ *
+ * SSLContext sslContext = SSLContext.getInstance("TLS");
+ * sslContext.init(
+ *         new KeyManager[] &#123;myPskKeyManager&#125;,
+ *         new TrustManager[0], // No TrustManagers needed for TLS-PSK
+ *         null // Use the default source of entropy
+ *         );
+ *
+ * SSLSocket sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(...);
+ * }</pre>
+ *
+ * @deprecated This abstraction is deprecated because it does not work with TLS 1.3.
+ * @hide This class is not part of the Android public SDK API
+ */
+@Deprecated
+@Internal
+public interface PSKKeyManager extends KeyManager {
+
+    /**
+     * Maximum supported length (in bytes) for PSK identity hint (in modified UTF-8 representation).
+     */
+    int MAX_IDENTITY_HINT_LENGTH_BYTES = 128;
+
+    /** Maximum supported length (in bytes) for PSK identity (in modified UTF-8 representation). */
+    int MAX_IDENTITY_LENGTH_BYTES = 128;
+
+    /** Maximum supported length (in bytes) for PSK key. */
+    int MAX_KEY_LENGTH_BYTES = 256;
+
+    /**
+     * Gets the PSK identity hint to report to the client to help agree on the PSK for the provided
+     * socket.
+     *
+     * @return PSK identity hint to be provided to the client or {@code null} to provide no hint.
+     */
+    String chooseServerKeyIdentityHint(Socket socket);
+
+    /**
+     * Gets the PSK identity hint to report to the client to help agree on the PSK for the provided
+     * engine.
+     *
+     * @return PSK identity hint to be provided to the client or {@code null} to provide no hint.
+     */
+    String chooseServerKeyIdentityHint(SSLEngine engine);
+
+    /**
+     * Gets the PSK identity to report to the server to help agree on the PSK for the provided
+     * socket.
+     *
+     * @param identityHint identity hint provided by the server or {@code null} if none provided.
+     *
+     * @return PSK identity to provide to the server. {@code null} is permitted but will be
+     *         converted into an empty string.
+     */
+    String chooseClientKeyIdentity(String identityHint, Socket socket);
+
+    /**
+     * Gets the PSK identity to report to the server to help agree on the PSK for the provided
+     * engine.
+     *
+     * @param identityHint identity hint provided by the server or {@code null} if none provided.
+     *
+     * @return PSK identity to provide to the server. {@code null} is permitted but will be
+     *         converted into an empty string.
+     */
+    String chooseClientKeyIdentity(String identityHint, SSLEngine engine);
+
+    /**
+     * Gets the PSK to use for the provided socket.
+     *
+     * @param identityHint identity hint provided by the server to help select the key or
+     *        {@code null} if none provided.
+     * @param identity identity provided by the client to help select the key.
+     *
+     * @return key or {@code null} to signal to peer that no suitable key is available and to abort
+     *         the handshake.
+     */
+    SecretKey getKey(String identityHint, String identity, Socket socket);
+
+    /**
+     * Gets the PSK to use for the provided engine.
+     *
+     * @param identityHint identity hint provided by the server to help select the key or
+     *        {@code null} if none provided.
+     * @param identity identity provided by the client to help select the key.
+     *
+     * @return key or {@code null} to signal to peer that no suitable key is available and to abort
+     *         the handshake.
+     */
+    SecretKey getKey(String identityHint, String identity, SSLEngine engine);
+}
\ No newline at end of file
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/PSSParameters.java b/repackaged/common/src/main/java/com/android/org/conscrypt/PSSParameters.java
new file mode 100644
index 0000000..29ddac7
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/PSSParameters.java
@@ -0,0 +1,160 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.IOException;
+import java.security.AlgorithmParametersSpi;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.MGF1ParameterSpec;
+import java.security.spec.PSSParameterSpec;
+
+/**
+ * AlgorithmParameters implementation for PSS.  The only supported encoding format is ASN.1
+ * (with X.509 accepted as an alias), as specified in RFC 4055 section 3.1.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.IntraCoreApi
+@Internal
+public class PSSParameters extends AlgorithmParametersSpi {
+
+    private PSSParameterSpec spec = PSSParameterSpec.DEFAULT;
+
+    @libcore.api.IntraCoreApi
+    public PSSParameters() {}
+
+    @Override
+    protected void engineInit(AlgorithmParameterSpec algorithmParameterSpec)
+            throws InvalidParameterSpecException {
+        if (algorithmParameterSpec instanceof PSSParameterSpec) {
+            this.spec = (PSSParameterSpec) algorithmParameterSpec;
+        } else {
+            throw new InvalidParameterSpecException("Only PSSParameterSpec is supported");
+        }
+    }
+
+    @Override
+    protected void engineInit(byte[] bytes) throws IOException {
+        long readRef = 0;
+        long seqRef = 0;
+        try {
+            readRef = NativeCrypto.asn1_read_init(bytes);
+            seqRef = NativeCrypto.asn1_read_sequence(readRef);
+            int saltLength = 20;
+            String hash = OAEPParameters.readHash(seqRef);
+            String mgfHash = OAEPParameters.readMgfHash(seqRef);
+            if (NativeCrypto.asn1_read_next_tag_is(seqRef, 2)) {
+                long saltRef = 0;
+                try {
+                    saltRef = NativeCrypto.asn1_read_tagged(seqRef);
+                    saltLength = (int) NativeCrypto.asn1_read_uint64(saltRef);
+                } finally {
+                    NativeCrypto.asn1_read_free(saltRef);
+                }
+            }
+            if (NativeCrypto.asn1_read_next_tag_is(seqRef, 3)) {
+                long trailerField;
+                long trailerRef = 0;
+                try {
+                    trailerRef = NativeCrypto.asn1_read_tagged(seqRef);
+                    trailerField = (int) NativeCrypto.asn1_read_uint64(trailerRef);
+                } finally {
+                    NativeCrypto.asn1_read_free(trailerRef);
+                }
+                // 1 is the only legal value for trailerField
+                if (trailerField != 1) {
+                    throw new IOException("Error reading ASN.1 encoding");
+                }
+            }
+
+            if (!NativeCrypto.asn1_read_is_empty(seqRef)
+                    || !NativeCrypto.asn1_read_is_empty(readRef)) {
+                throw new IOException("Error reading ASN.1 encoding");
+            }
+            this.spec = new PSSParameterSpec(hash, "MGF1", new MGF1ParameterSpec(mgfHash),
+                    saltLength, 1);
+        } finally {
+            NativeCrypto.asn1_read_free(seqRef);
+            NativeCrypto.asn1_read_free(readRef);
+        }
+    }
+
+    @Override
+    protected void engineInit(byte[] bytes, String format) throws IOException {
+        if ((format == null) || format.equals("ASN.1") || format.equals("X.509")) {
+            engineInit(bytes);
+        } else {
+            throw new IOException("Unsupported format: " + format);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(Class<T> aClass)
+            throws InvalidParameterSpecException {
+        if ((aClass != null) && aClass == PSSParameterSpec.class) {
+            return (T) spec;
+        } else {
+            throw new InvalidParameterSpecException("Unsupported class: " + aClass);
+        }
+    }
+
+    @Override
+    protected byte[] engineGetEncoded() throws IOException {
+        long cbbRef = 0;
+        long seqRef = 0;
+        try {
+            cbbRef = NativeCrypto.asn1_write_init();
+            seqRef = NativeCrypto.asn1_write_sequence(cbbRef);
+            OAEPParameters.writeHashAndMgfHash(seqRef, spec.getDigestAlgorithm(),
+                    (MGF1ParameterSpec) spec.getMGFParameters());
+            // Implementations are prohibited from writing the default value for any of the fields
+            if (spec.getSaltLength() != 20) {
+                long tagRef = 0;
+                try {
+                    tagRef = NativeCrypto.asn1_write_tag(seqRef, 2);
+                    NativeCrypto.asn1_write_uint64(tagRef, spec.getSaltLength());
+                } finally {
+                    NativeCrypto.asn1_write_flush(seqRef);
+                    NativeCrypto.asn1_write_free(tagRef);
+                }
+            }
+            // 1 is the only legal value for trailerField and the default, so ignore it
+            return NativeCrypto.asn1_write_finish(cbbRef);
+        } catch (IOException e) {
+            NativeCrypto.asn1_write_cleanup(cbbRef);
+            throw e;
+        } finally {
+            NativeCrypto.asn1_write_free(seqRef);
+            NativeCrypto.asn1_write_free(cbbRef);
+        }
+    }
+
+    @Override
+    protected byte[] engineGetEncoded(String format) throws IOException {
+        if ((format == null) || format.equals("ASN.1") || format.equals("X.509")) {
+            return engineGetEncoded();
+        }
+        throw new IOException("Unsupported format: " + format);
+    }
+
+    @Override
+    protected String engineToString() {
+        return "Conscrypt PSS AlgorithmParameters";
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/PeerInfoProvider.java b/repackaged/common/src/main/java/com/android/org/conscrypt/PeerInfoProvider.java
new file mode 100644
index 0000000..d180425
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/PeerInfoProvider.java
@@ -0,0 +1,79 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+/**
+ * A provider for the peer host and port information.
+ */
+abstract class PeerInfoProvider {
+    private static final PeerInfoProvider NULL_PEER_INFO_PROVIDER = new PeerInfoProvider() {
+        @Override
+        String getHostname() {
+            return null;
+        }
+
+        @Override
+        public String getHostnameOrIP() {
+            return null;
+        }
+
+        @Override
+        public int getPort() {
+            return -1;
+        }
+    };
+
+    /**
+     * Returns the hostname supplied during engine/socket creation. No DNS resolution is
+     * attempted before returning the hostname.
+     */
+    abstract String getHostname();
+
+    /**
+     * This method attempts to create a textual representation of the peer host or IP. Does
+     * not perform a reverse DNS lookup. This is typically used during session creation.
+     */
+    abstract String getHostnameOrIP();
+
+    /**
+     * Gets the port of the peer.
+     */
+    abstract int getPort();
+
+    static PeerInfoProvider nullProvider() {
+        return NULL_PEER_INFO_PROVIDER;
+    }
+
+    static PeerInfoProvider forHostAndPort(final String host, final int port) {
+        return new PeerInfoProvider() {
+            @Override
+            String getHostname() {
+                return host;
+            }
+
+            @Override
+            public String getHostnameOrIP() {
+                return host;
+            }
+
+            @Override
+            public int getPort() {
+                return port;
+            }
+        };
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/Preconditions.java b/repackaged/common/src/main/java/com/android/org/conscrypt/Preconditions.java
new file mode 100644
index 0000000..801cc0f
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/Preconditions.java
@@ -0,0 +1,109 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+/**
+ * Static convenience methods that help a method or constructor check whether it was invoked
+ * correctly (that is, whether its <i>preconditions</i> were met).
+ */
+final class Preconditions {
+    private Preconditions() {}
+
+    /**
+     * Ensures that an object reference passed as a parameter to the calling method is not null.
+     *
+     * @param reference an object reference
+     * @param errorMessage the exception message to use if the check fails.
+     * @return the non-null reference that was validated
+     * @throws NullPointerException if {@code reference} is null
+     */
+    static <T> T checkNotNull(T reference, String errorMessage) {
+        if (reference == null) {
+            throw new NullPointerException(errorMessage);
+        }
+        return reference;
+    }
+
+    /**
+     * Ensures the truth of an expression involving one or more parameters to the calling method.
+     *
+     * @param condition to condition to be tested
+     * @param errorMessage the exception message to use if the check fails.
+     * @throws IllegalArgumentException if the condition is {@code false}
+     */
+    static void checkArgument(boolean condition, String errorMessage) {
+        if (!condition) {
+            throw new IllegalArgumentException(errorMessage);
+        }
+    }
+
+    /**
+     * Ensures the truth of an expression involving one or more parameters to the calling method.
+     *
+     * @param condition to condition to be tested
+     * @param errorMessageTemplate the format string to be passed to {@link String#format(String,
+     * Object...)}
+     * @param arg the format argument to be passed to {@link String#format(String, Object...)}
+     * @throws IllegalArgumentException if the condition is {@code false}
+     */
+    static void checkArgument(boolean condition, String errorMessageTemplate, Object arg) {
+        if (!condition) {
+            throw new IllegalArgumentException(String.format(errorMessageTemplate, arg));
+        }
+    }
+
+    /**
+     * Ensures that {@code start} and {@code end} specify a valid <i>positions</i> in an array, list
+     * or string of size {@code size}, and are in order. A position index may range from zero to
+     * {@code size}, inclusive.
+     *
+     * @param start a user-supplied index identifying a starting position in an array, list or string
+     * @param end a user-supplied index identifying a ending position in an array, list or string
+     * @param size the size of that array, list or string
+     * @throws IndexOutOfBoundsException if either index is negative or is greater than {@code size},
+     *     or if {@code end} is less than {@code start}
+     * @throws IllegalArgumentException if {@code size} is negative
+     */
+    static void checkPositionIndexes(int start, int end, int size) {
+        // Carefully optimized for execution by hotspot (explanatory comment above)
+        if (start < 0 || end < start || end > size) {
+            throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size));
+        }
+    }
+
+    private static String badPositionIndexes(int start, int end, int size) {
+        if (start < 0 || start > size) {
+            return badPositionIndex(start, size, "start index");
+        }
+        if (end < 0 || end > size) {
+            return badPositionIndex(end, size, "end index");
+        }
+        // end < start
+        return String.format("end index (%s) must not be less than start index (%s)", end, start);
+    }
+
+    private static String badPositionIndex(int index, int size, String desc) {
+        if (index < 0) {
+            return String.format("%s (%s) must not be negative", desc, index);
+        } else if (size < 0) {
+            throw new IllegalArgumentException("negative size: " + size);
+        } else { // index > size
+            return String.format("%s (%s) must not be greater than size (%s)", desc, index, size);
+        }
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/SSLClientSessionCache.java b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLClientSessionCache.java
new file mode 100644
index 0000000..c63861d
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLClientSessionCache.java
@@ -0,0 +1,56 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import javax.net.ssl.SSLSession;
+
+/**
+ * A persistent {@link javax.net.ssl.SSLSession} cache used by
+ * {@link javax.net.ssl.SSLSessionContext} to share client-side SSL sessions
+ * across processes. For example, this cache enables applications to
+ * persist and reuse sessions across restarts.
+ *
+ * <p>The {@code SSLSessionContext} implementation converts
+ * {@code SSLSession}s into raw bytes and vice versa. The exact makeup of the
+ * session data is dependent upon the caller's implementation and is opaque to
+ * the {@code SSLClientSessionCache} implementation.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.CorePlatformApi
+@Internal
+public interface SSLClientSessionCache {
+    /**
+     * Gets data from a pre-existing session for a given server host and port.
+     *
+     * @param host from {@link javax.net.ssl.SSLSession#getPeerHost()}
+     * @param port from {@link javax.net.ssl.SSLSession#getPeerPort()}
+     * @return the session data or null if none is cached
+     * @throws NullPointerException if host is null
+     */
+    byte[] getSessionData(String host, int port);
+
+    /**
+     * Stores session data for the given session.
+     *
+     * @param session to cache data for
+     * @param sessionData to cache
+     * @throws NullPointerException if session, result of
+     *  {@code session.getPeerHost()} or data is null
+     */
+    void putSessionData(SSLSession session, byte[] sessionData);
+}
\ No newline at end of file
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/SSLNullSession.java b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLNullSession.java
new file mode 100644
index 0000000..428b27b
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLNullSession.java
@@ -0,0 +1,182 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.List;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+
+/**
+ * This is returned in the place of a {@link SSLSession} when no TLS connection could be negotiated,
+ * but one was requested from a method that can't throw an exception such as {@link
+ * javax.net.ssl.SSLSocket#getSession()} before {@link javax.net.ssl.SSLSocket#startHandshake()} is
+ * called.
+ */
+final class SSLNullSession implements ConscryptSession, Cloneable {
+    static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL";
+
+    /*
+     * Holds default instances so class preloading doesn't create an instance of
+     * it.
+     */
+    private static class DefaultHolder {
+        static final SSLNullSession NULL_SESSION = new SSLNullSession();
+    }
+
+    private long creationTime;
+    private long lastAccessedTime;
+
+    static ConscryptSession getNullSession() {
+        return DefaultHolder.NULL_SESSION;
+    }
+
+    private SSLNullSession() {
+        creationTime = System.currentTimeMillis();
+        lastAccessedTime = creationTime;
+    }
+
+    @Override
+    public String getRequestedServerName() {
+        return null;
+    }
+
+    @Override
+    public List<byte[]> getStatusResponses() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public byte[] getPeerSignedCertificateTimestamp() {
+        return EmptyArray.BYTE;
+    }
+
+    @Override
+    public int getApplicationBufferSize() {
+        return NativeConstants.SSL3_RT_MAX_PLAIN_LENGTH;
+    }
+
+    @Override
+    public String getCipherSuite() {
+        return INVALID_CIPHER;
+    }
+
+    @Override
+    public long getCreationTime() {
+        return creationTime;
+    }
+
+    @Override
+    public byte[] getId() {
+        return EmptyArray.BYTE;
+    }
+
+    @Override
+    public long getLastAccessedTime() {
+        return lastAccessedTime;
+    }
+
+    @Override
+    public Certificate[] getLocalCertificates() {
+        return null;
+    }
+
+    @Override
+    public Principal getLocalPrincipal() {
+        return null;
+    }
+
+    @Override
+    public int getPacketBufferSize() {
+        return NativeConstants.SSL3_RT_MAX_PACKET_SIZE;
+    }
+
+    @Override
+    public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+            throws SSLPeerUnverifiedException {
+        throw new SSLPeerUnverifiedException("No peer certificate");
+    }
+
+    @Override
+    public X509Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+        throw new SSLPeerUnverifiedException("No peer certificate");
+    }
+
+    @Override
+    public String getPeerHost() {
+        return null;
+    }
+
+    @Override
+    public int getPeerPort() {
+        return -1;
+    }
+
+    @Override
+    public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+        throw new SSLPeerUnverifiedException("No peer certificate");
+    }
+
+    @Override
+    public String getProtocol() {
+        return "NONE";
+    }
+
+    @Override
+    public SSLSessionContext getSessionContext() {
+        return null;
+    }
+
+    @Override
+    public Object getValue(String name) {
+        throw new UnsupportedOperationException(
+                "All calls to this method should be intercepted by ProvidedSessionDecorator.");
+    }
+
+    @Override
+    public String[] getValueNames() {
+        throw new UnsupportedOperationException(
+                "All calls to this method should be intercepted by ProvidedSessionDecorator.");
+    }
+
+    @Override
+    public void invalidate() {
+    }
+
+    @Override
+    public boolean isValid() {
+        return false;
+    }
+
+    @Override
+    public void putValue(String name, Object value) {
+        throw new UnsupportedOperationException(
+                "All calls to this method should be intercepted by ProvidedSessionDecorator.");
+    }
+
+    @Override
+    public void removeValue(String name) {
+        throw new UnsupportedOperationException(
+                "All calls to this method should be intercepted by ProvidedSessionDecorator.");
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/SSLParametersImpl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLParametersImpl.java
new file mode 100644
index 0000000..ff0e80b
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLParametersImpl.java
@@ -0,0 +1,691 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Set;
+import javax.crypto.SecretKey;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * The instances of this class encapsulate all the info
+ * about enabled cipher suites and protocols,
+ * as well as the information about client/server mode of
+ * ssl socket, whether it require/want client authentication or not,
+ * and controls whether new SSL sessions may be established by this
+ * socket or not.
+ */
+final class SSLParametersImpl implements Cloneable {
+
+    // default source of X.509 certificate based authentication keys
+    private static volatile X509KeyManager defaultX509KeyManager;
+    // default source of X.509 certificate based authentication trust decisions
+    private static volatile X509TrustManager defaultX509TrustManager;
+    // default SSL parameters
+    private static volatile SSLParametersImpl defaultParameters;
+
+    // client session context contains the set of reusable
+    // client-side SSL sessions
+    private final ClientSessionContext clientSessionContext;
+    // server session context contains the set of reusable
+    // server-side SSL sessions
+    private final ServerSessionContext serverSessionContext;
+    // source of X.509 certificate based authentication keys or null if not provided
+    private final X509KeyManager x509KeyManager;
+    // source of Pre-Shared Key (PSK) authentication keys or null if not provided.
+    @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+    private final PSKKeyManager pskKeyManager;
+    // source of X.509 certificate based authentication trust decisions or null if not provided
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    private final X509TrustManager x509TrustManager;
+
+    // protocols enabled for SSL connection
+    String[] enabledProtocols;
+    // set to indicate when obsolete protocols are filtered
+    boolean isEnabledProtocolsFiltered;
+    // The TLS 1.0-1.2 cipher suites enabled for the SSL connection.  TLS 1.3 cipher suites
+    // cannot be customized, so for simplicity this field never contains any TLS 1.3 suites.
+    String[] enabledCipherSuites;
+
+    // if the peer with this parameters tuned to work in client mode
+    private boolean client_mode = true;
+    // if the peer with this parameters tuned to require client authentication
+    private boolean need_client_auth = false;
+    // if the peer with this parameters tuned to request client authentication
+    private boolean want_client_auth = false;
+    // if the peer with this parameters allowed to cteate new SSL session
+    private boolean enable_session_creation = true;
+    // Endpoint identification algorithm (e.g., HTTPS)
+    private String endpointIdentificationAlgorithm;
+    // Whether to use the local cipher suites order
+    private boolean useCipherSuitesOrder;
+
+    // client-side only, bypasses the property based configuration, used for tests
+    private boolean ctVerificationEnabled;
+
+    // server-side only. SCT and OCSP data to send to clients which request it
+    byte[] sctExtension;
+    byte[] ocspResponse;
+
+    byte[] applicationProtocols = EmptyArray.BYTE;
+    ApplicationProtocolSelectorAdapter applicationProtocolSelector;
+    boolean useSessionTickets;
+    private Boolean useSni;
+
+    /**
+     * Whether the TLS Channel ID extension is enabled. This field is
+     * server-side only.
+     */
+    boolean channelIdEnabled;
+
+    /**
+     * Initializes the parameters. Naturally this constructor is used
+     * in SSLContextImpl.engineInit method which directly passes its
+     * parameters. In other words this constructor holds all
+     * the functionality provided by SSLContext.init method.
+     * See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[],
+     * SecureRandom)} for more information
+     */
+    SSLParametersImpl(KeyManager[] kms, TrustManager[] tms,
+            SecureRandom sr, ClientSessionContext clientSessionContext,
+            ServerSessionContext serverSessionContext, String[] protocols)
+            throws KeyManagementException {
+        this.serverSessionContext = serverSessionContext;
+        this.clientSessionContext = clientSessionContext;
+
+        // initialize key managers
+        if (kms == null) {
+            x509KeyManager = getDefaultX509KeyManager();
+            // There's no default PSK key manager
+            pskKeyManager = null;
+        } else {
+            x509KeyManager = findFirstX509KeyManager(kms);
+            pskKeyManager = findFirstPSKKeyManager(kms);
+        }
+
+        // initialize x509TrustManager
+        if (tms == null) {
+            x509TrustManager = getDefaultX509TrustManager();
+        } else {
+            x509TrustManager = findFirstX509TrustManager(tms);
+        }
+
+        // initialize the list of cipher suites and protocols enabled by default
+        enabledProtocols = NativeCrypto.checkEnabledProtocols(
+                protocols == null ? NativeCrypto.DEFAULT_PROTOCOLS : protocols).clone();
+        boolean x509CipherSuitesNeeded = (x509KeyManager != null) || (x509TrustManager != null);
+        boolean pskCipherSuitesNeeded = pskKeyManager != null;
+        enabledCipherSuites = getDefaultCipherSuites(
+                x509CipherSuitesNeeded, pskCipherSuitesNeeded);
+
+        // We ignore the SecureRandom passed in by the caller. The native code below
+        // directly accesses /dev/urandom, which makes it irrelevant.
+    }
+
+    // Copy constructor for the purposes of changing the final fields
+    private SSLParametersImpl(ClientSessionContext clientSessionContext,
+            ServerSessionContext serverSessionContext, X509KeyManager x509KeyManager,
+            PSKKeyManager pskKeyManager, X509TrustManager x509TrustManager,
+            SSLParametersImpl sslParams) {
+        this.clientSessionContext = clientSessionContext;
+        this.serverSessionContext = serverSessionContext;
+        this.x509KeyManager = x509KeyManager;
+        this.pskKeyManager = pskKeyManager;
+        this.x509TrustManager = x509TrustManager;
+
+        this.enabledProtocols =
+                (sslParams.enabledProtocols == null) ? null : sslParams.enabledProtocols.clone();
+        this.isEnabledProtocolsFiltered = sslParams.isEnabledProtocolsFiltered;
+        this.enabledCipherSuites = (sslParams.enabledCipherSuites == null)
+                ? null
+                : sslParams.enabledCipherSuites.clone();
+        this.client_mode = sslParams.client_mode;
+        this.need_client_auth = sslParams.need_client_auth;
+        this.want_client_auth = sslParams.want_client_auth;
+        this.enable_session_creation = sslParams.enable_session_creation;
+        this.endpointIdentificationAlgorithm = sslParams.endpointIdentificationAlgorithm;
+        this.useCipherSuitesOrder = sslParams.useCipherSuitesOrder;
+        this.ctVerificationEnabled = sslParams.ctVerificationEnabled;
+        this.sctExtension =
+                (sslParams.sctExtension == null) ? null : sslParams.sctExtension.clone();
+        this.ocspResponse =
+                (sslParams.ocspResponse == null) ? null : sslParams.ocspResponse.clone();
+        this.applicationProtocols = (sslParams.applicationProtocols == null)
+                ? null
+                : sslParams.applicationProtocols.clone();
+        this.applicationProtocolSelector = sslParams.applicationProtocolSelector;
+        this.useSessionTickets = sslParams.useSessionTickets;
+        this.useSni = sslParams.useSni;
+        this.channelIdEnabled = sslParams.channelIdEnabled;
+    }
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static SSLParametersImpl getDefault() throws KeyManagementException {
+        SSLParametersImpl result = defaultParameters;
+        if (result == null) {
+            // single-check idiom
+            defaultParameters = result = new SSLParametersImpl(null,
+                                                               null,
+                                                               null,
+                                                               new ClientSessionContext(),
+                                                               new ServerSessionContext(),
+                                                               null);
+        }
+        return (SSLParametersImpl) result.clone();
+    }
+
+    /**
+     * Returns the appropriate session context.
+     */
+    AbstractSessionContext getSessionContext() {
+        return client_mode ? clientSessionContext : serverSessionContext;
+    }
+
+    /**
+     * @return client session context
+     */
+    ClientSessionContext getClientSessionContext() {
+        return clientSessionContext;
+    }
+
+    /**
+     * @return X.509 key manager or {@code null} for none.
+     */
+    X509KeyManager getX509KeyManager() {
+        return x509KeyManager;
+    }
+
+    /**
+     * @return Pre-Shared Key (PSK) key manager or {@code null} for none.
+     */
+    @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+    PSKKeyManager getPSKKeyManager() {
+        return pskKeyManager;
+    }
+
+    /**
+     * @return X.509 trust manager or {@code null} for none.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    X509TrustManager getX509TrustManager() {
+        return x509TrustManager;
+    }
+
+    /**
+     * @return the names of enabled cipher suites
+     */
+    String[] getEnabledCipherSuites() {
+        if (Arrays.asList(enabledProtocols).contains(NativeCrypto.SUPPORTED_PROTOCOL_TLSV1_3)) {
+            return SSLUtils.concat(
+                    NativeCrypto.SUPPORTED_TLS_1_3_CIPHER_SUITES, enabledCipherSuites);
+        }
+        return enabledCipherSuites.clone();
+    }
+
+    /**
+     * Sets the enabled cipher suites after filtering through OpenSSL.
+     */
+    void setEnabledCipherSuites(String[] cipherSuites) {
+        // Filter out any TLS 1.3 cipher suites the user may have passed.  Our TLS 1.3 suites
+        // are always enabled, no matter what the user requests, so we only store the 1.0-1.2
+        // suites in enabledCipherSuites.
+        enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(
+                filterFromCipherSuites(cipherSuites,
+                        NativeCrypto.SUPPORTED_TLS_1_3_CIPHER_SUITES_SET));
+    }
+
+    /**
+     * @return the set of enabled protocols
+     */
+    String[] getEnabledProtocols() {
+        return enabledProtocols.clone();
+    }
+
+    /**
+     * Sets the list of available protocols for use in SSL connection.
+     * @throws IllegalArgumentException if {@code protocols == null}
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    void setEnabledProtocols(String[] protocols) {
+        if (protocols == null) {
+            throw new IllegalArgumentException("protocols == null");
+        }
+        String[] filteredProtocols =
+                filterFromProtocols(protocols, NativeCrypto.OBSOLETE_PROTOCOL_SSLV3);
+        isEnabledProtocolsFiltered = protocols.length != filteredProtocols.length;
+        enabledProtocols = NativeCrypto.checkEnabledProtocols(filteredProtocols).clone();
+    }
+
+    /**
+     * Sets the list of ALPN protocols.
+     *
+     * @param protocols the list of ALPN protocols
+     */
+    void setApplicationProtocols(String[] protocols) {
+        this.applicationProtocols = SSLUtils.encodeProtocols(protocols);
+    }
+
+    String[] getApplicationProtocols() {
+        return SSLUtils.decodeProtocols(applicationProtocols);
+    }
+
+    /**
+     * Used for server-mode only. Sets or clears the application-provided ALPN protocol selector.
+     * If set, will override the protocol list provided by {@link #setApplicationProtocols(String[])}.
+     */
+    void setApplicationProtocolSelector(ApplicationProtocolSelectorAdapter applicationProtocolSelector) {
+        this.applicationProtocolSelector = applicationProtocolSelector;
+    }
+
+    /**
+     * Tunes the peer holding this parameters to work in client mode.
+     * @param   mode if the peer is configured to work in client mode
+     */
+    void setUseClientMode(boolean mode) {
+        client_mode = mode;
+    }
+
+    /**
+     * Returns the value indicating if the parameters configured to work
+     * in client mode.
+     */
+    boolean getUseClientMode() {
+        return client_mode;
+    }
+
+    /**
+     * Tunes the peer holding this parameters to require client authentication
+     */
+    void setNeedClientAuth(boolean need) {
+        need_client_auth = need;
+        // reset the want_client_auth setting
+        want_client_auth = false;
+    }
+
+    /**
+     * Returns the value indicating if the peer with this parameters tuned
+     * to require client authentication
+     */
+    boolean getNeedClientAuth() {
+        return need_client_auth;
+    }
+
+    /**
+     * Tunes the peer holding this parameters to request client authentication
+     */
+    void setWantClientAuth(boolean want) {
+        want_client_auth = want;
+        // reset the need_client_auth setting
+        need_client_auth = false;
+    }
+
+    /**
+     * Returns the value indicating if the peer with this parameters
+     * tuned to request client authentication
+     */
+    boolean getWantClientAuth() {
+        return want_client_auth;
+    }
+
+    /**
+     * Allows/disallows the peer holding this parameters to
+     * create new SSL session
+     */
+    void setEnableSessionCreation(boolean flag) {
+        enable_session_creation = flag;
+    }
+
+    /**
+     * Returns the value indicating if the peer with this parameters
+     * allowed to cteate new SSL session
+     */
+    boolean getEnableSessionCreation() {
+        return enable_session_creation;
+    }
+
+    void setUseSessionTickets(boolean useSessionTickets) {
+        this.useSessionTickets = useSessionTickets;
+    }
+
+    /**
+     * Whether connections using this SSL connection should use the TLS
+     * extension Server Name Indication (SNI).
+     */
+    void setUseSni(boolean flag) {
+        useSni = flag;
+    }
+
+    /**
+     * Returns whether connections using this SSL connection should use the TLS
+     * extension Server Name Indication (SNI).
+     */
+    boolean getUseSni() {
+        return useSni != null ? useSni : isSniEnabledByDefault();
+    }
+
+    /**
+     * For testing only.
+     */
+    void setCTVerificationEnabled(boolean enabled) {
+        ctVerificationEnabled = enabled;
+    }
+
+    /**
+     * For testing only.
+     */
+    void setSCTExtension(byte[] extension) {
+        sctExtension = extension;
+    }
+
+    /**
+     * For testing only.
+     */
+    void setOCSPResponse(byte[] response) {
+        ocspResponse = response;
+    }
+
+    byte[] getOCSPResponse() {
+        return ocspResponse;
+    }
+
+    /**
+     * This filters {@code obsoleteProtocol} from the list of {@code protocols}
+     * down to help with app compatibility.
+     */
+    private static String[] filterFromProtocols(String[] protocols, String obsoleteProtocol) {
+        if (protocols.length == 1 && obsoleteProtocol.equals(protocols[0])) {
+            return EMPTY_STRING_ARRAY;
+        }
+
+        ArrayList<String> newProtocols = new ArrayList<String>();
+        for (String protocol : protocols) {
+            if (!obsoleteProtocol.equals(protocol)) {
+                newProtocols.add(protocol);
+            }
+        }
+        return newProtocols.toArray(EMPTY_STRING_ARRAY);
+    }
+
+    private static String[] filterFromCipherSuites(String[] cipherSuites, Set<String> toRemove) {
+        if (cipherSuites == null || cipherSuites.length == 0) {
+            return cipherSuites;
+        }
+        ArrayList<String> newCipherSuites = new ArrayList<String>(cipherSuites.length);
+        for (String cipherSuite : cipherSuites) {
+            if (!toRemove.contains(cipherSuite)) {
+                newCipherSuites.add(cipherSuite);
+            }
+        }
+        return newCipherSuites.toArray(EMPTY_STRING_ARRAY);
+    }
+
+    private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+    /**
+     * Returns whether Server Name Indication (SNI) is enabled by default for
+     * sockets. For more information on SNI, see RFC 6066 section 3.
+     */
+    private boolean isSniEnabledByDefault() {
+        try {
+            String enableSNI = System.getProperty("jsse.enableSNIExtension", "true");
+            if ("true".equalsIgnoreCase(enableSNI)) {
+                return true;
+            } else if ("false".equalsIgnoreCase(enableSNI)) {
+                return false;
+            } else {
+                throw new RuntimeException(
+                        "Can only set \"jsse.enableSNIExtension\" to \"true\" or \"false\"");
+            }
+        } catch (SecurityException e) {
+            return true;
+        }
+    }
+
+    /**
+     * For abstracting the X509KeyManager calls between
+     * {@link X509KeyManager#chooseClientAlias(String[], java.security.Principal[], java.net.Socket)}
+     * and
+     * {@link X509ExtendedKeyManager#chooseEngineClientAlias(String[], java.security.Principal[], javax.net.ssl.SSLEngine)}
+     */
+    interface AliasChooser {
+        String chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers,
+                String[] keyTypes);
+
+        String chooseServerAlias(X509KeyManager keyManager, String keyType);
+    }
+
+    /**
+     * For abstracting the {@code PSKKeyManager} calls between those taking an {@code SSLSocket} and
+     * those taking an {@code SSLEngine}.
+     */
+    @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+    interface PSKCallbacks {
+        String chooseServerPSKIdentityHint(PSKKeyManager keyManager);
+        String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint);
+        SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity);
+    }
+
+    /**
+     * Returns the clone of this object.
+     * @return the clone.
+     */
+    @Override
+    protected Object clone() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    SSLParametersImpl cloneWithTrustManager(X509TrustManager newTrustManager) {
+        return new SSLParametersImpl(clientSessionContext, serverSessionContext, x509KeyManager,
+                pskKeyManager, newTrustManager, this);
+    }
+
+    private static X509KeyManager getDefaultX509KeyManager() throws KeyManagementException {
+        X509KeyManager result = defaultX509KeyManager;
+        if (result == null) {
+            // single-check idiom
+            defaultX509KeyManager = result = createDefaultX509KeyManager();
+        }
+        return result;
+    }
+    private static X509KeyManager createDefaultX509KeyManager() throws KeyManagementException {
+        try {
+            String algorithm = KeyManagerFactory.getDefaultAlgorithm();
+            KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
+            kmf.init(null, null);
+            KeyManager[] kms = kmf.getKeyManagers();
+            X509KeyManager result = findFirstX509KeyManager(kms);
+            if (result == null) {
+                throw new KeyManagementException("No X509KeyManager among default KeyManagers: "
+                        + Arrays.toString(kms));
+            }
+            return result;
+        } catch (NoSuchAlgorithmException e) {
+            throw new KeyManagementException(e);
+        } catch (KeyStoreException e) {
+            throw new KeyManagementException(e);
+        } catch (UnrecoverableKeyException e) {
+            throw new KeyManagementException(e);
+        }
+    }
+
+    /**
+     * Finds the first {@link X509KeyManager} element in the provided array.
+     *
+     * @return the first {@code X509KeyManager} or {@code null} if not found.
+     */
+    private static X509KeyManager findFirstX509KeyManager(KeyManager[] kms) {
+        for (KeyManager km : kms) {
+            if (km instanceof X509KeyManager) {
+                return (X509KeyManager)km;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Finds the first {@link PSKKeyManager} element in the provided array.
+     *
+     * @return the first {@code PSKKeyManager} or {@code null} if not found.
+     */
+    @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
+    private static PSKKeyManager findFirstPSKKeyManager(KeyManager[] kms) {
+        for (KeyManager km : kms) {
+            if (km instanceof PSKKeyManager) {
+                return (PSKKeyManager)km;
+            } else if (km != null) {
+                try {
+                    return DuckTypedPSKKeyManager.getInstance(km);
+                } catch (NoSuchMethodException ignored) {}
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Gets the default X.509 trust manager.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    static X509TrustManager getDefaultX509TrustManager()
+            throws KeyManagementException {
+        X509TrustManager result = defaultX509TrustManager;
+        if (result == null) {
+            // single-check idiom
+            defaultX509TrustManager = result = createDefaultX509TrustManager();
+        }
+        return result;
+    }
+
+    private static X509TrustManager createDefaultX509TrustManager()
+            throws KeyManagementException {
+        try {
+            String algorithm = TrustManagerFactory.getDefaultAlgorithm();
+            TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
+            tmf.init((KeyStore) null);
+            TrustManager[] tms = tmf.getTrustManagers();
+            X509TrustManager trustManager = findFirstX509TrustManager(tms);
+            if (trustManager == null) {
+                throw new KeyManagementException(
+                        "No X509TrustManager in among default TrustManagers: "
+                                + Arrays.toString(tms));
+            }
+            return trustManager;
+        } catch (NoSuchAlgorithmException e) {
+            throw new KeyManagementException(e);
+        } catch (KeyStoreException e) {
+            throw new KeyManagementException(e);
+        }
+    }
+
+    /**
+     * Finds the first {@link X509TrustManager} element in the provided array.
+     *
+     * @return the first {@code X509ExtendedTrustManager} or
+     *         {@code X509TrustManager} or {@code null} if not found.
+     */
+    private static X509TrustManager findFirstX509TrustManager(TrustManager[] tms) {
+        for (TrustManager tm : tms) {
+            if (tm instanceof X509TrustManager) {
+                return (X509TrustManager) tm;
+            }
+        }
+        return null;
+    }
+
+    String getEndpointIdentificationAlgorithm() {
+        return endpointIdentificationAlgorithm;
+    }
+
+    void setEndpointIdentificationAlgorithm(String endpointIdentificationAlgorithm) {
+        this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
+    }
+
+    boolean getUseCipherSuitesOrder() {
+        return useCipherSuitesOrder;
+    }
+
+    void setUseCipherSuitesOrder(boolean useCipherSuitesOrder) {
+        this.useCipherSuitesOrder = useCipherSuitesOrder;
+    }
+
+    private static String[] getDefaultCipherSuites(
+            boolean x509CipherSuitesNeeded,
+            boolean pskCipherSuitesNeeded) {
+        if (x509CipherSuitesNeeded) {
+            // X.509 based cipher suites need to be listed.
+            if (pskCipherSuitesNeeded) {
+                // Both X.509 and PSK based cipher suites need to be listed. Because TLS-PSK is not
+                // normally used, we assume that when PSK cipher suites are requested here they
+                // should be preferred over other cipher suites. Thus, we give PSK cipher suites
+                // higher priority than X.509 cipher suites.
+                // NOTE: There are cipher suites that use both X.509 and PSK (e.g., those based on
+                // RSA_PSK key exchange). However, these cipher suites are not currently supported.
+                return SSLUtils.concat(
+                        NativeCrypto.DEFAULT_PSK_CIPHER_SUITES,
+                        NativeCrypto.DEFAULT_X509_CIPHER_SUITES,
+                        new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV});
+            } else {
+                // Only X.509 cipher suites need to be listed.
+                return SSLUtils.concat(
+                        NativeCrypto.DEFAULT_X509_CIPHER_SUITES,
+                        new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV});
+            }
+        } else if (pskCipherSuitesNeeded) {
+            // Only PSK cipher suites need to be listed.
+            return SSLUtils.concat(
+                    NativeCrypto.DEFAULT_PSK_CIPHER_SUITES,
+                    new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV});
+        } else {
+            // Neither X.509 nor PSK cipher suites need to be listed.
+            return new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV};
+        }
+    }
+
+    /**
+     * Check if SCT verification is enforced for a given hostname.
+     */
+    boolean isCTVerificationEnabled(String hostname) {
+        if (hostname == null) {
+            return false;
+        }
+
+        // Bypass the check. This is used for testing only
+        if (ctVerificationEnabled) {
+            return true;
+        }
+        return Platform.isCTVerificationRequired(hostname);
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/SSLServerSessionCache.java b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLServerSessionCache.java
new file mode 100644
index 0000000..8200fe8
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLServerSessionCache.java
@@ -0,0 +1,52 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import javax.net.ssl.SSLSession;
+
+/**
+ * A persistent {@link javax.net.ssl.SSLSession} cache used by
+ * {@link javax.net.ssl.SSLSessionContext} to share server-side SSL sessions
+ * across processes. For example, this cache enables one server to resume
+ * a session started by a different server based on a session ID provided
+ * by the client.
+ *
+ * <p>The {@code SSLSessionContext} implementation converts
+ * {@code SSLSession}s into raw bytes and vice versa. The exact makeup of the
+ * session data is dependent upon the caller's implementation and is opaque to
+ * the {@code SSLServerSessionCache} implementation.
+ */
+interface SSLServerSessionCache {
+    /**
+     * Gets the session data for given session ID.
+     *
+     * @param id from {@link javax.net.ssl.SSLSession#getId()}
+     * @return the session data or null if none is cached
+     * @throws NullPointerException if id is null
+     */
+    byte[] getSessionData(byte[] id);
+
+    /**
+     * Stores session data for the given session.
+     *
+     * @param session to cache data for
+     * @param sessionData to cache
+     * @throws NullPointerException if session or data is null
+     */
+    void putSessionData(SSLSession session, byte[] sessionData);
+}
\ No newline at end of file
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/SSLUtils.java b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLUtils.java
new file mode 100644
index 0000000..f3226e6
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/SSLUtils.java
@@ -0,0 +1,582 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2016 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static java.lang.Math.min;
+import static com.android.org.conscrypt.NativeConstants.SSL3_RT_ALERT;
+import static com.android.org.conscrypt.NativeConstants.SSL3_RT_APPLICATION_DATA;
+import static com.android.org.conscrypt.NativeConstants.SSL3_RT_CHANGE_CIPHER_SPEC;
+import static com.android.org.conscrypt.NativeConstants.SSL3_RT_HANDSHAKE;
+import static com.android.org.conscrypt.NativeConstants.SSL3_RT_HEADER_LENGTH;
+import static com.android.org.conscrypt.NativeConstants.SSL3_RT_MAX_PACKET_SIZE;
+
+import java.io.ByteArrayInputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.security.cert.CertificateException;
+
+/**
+ * Utility methods for SSL packet processing. Copied from the Netty project.
+ * <p>
+ * This is a public class to allow testing to occur on Android via CTS.
+ */
+final class SSLUtils {
+    static final boolean USE_ENGINE_SOCKET_BY_DEFAULT = Boolean.parseBoolean(
+            System.getProperty("com.android.org.conscrypt.useEngineSocketByDefault", "false"));
+    private static final int MAX_PROTOCOL_LENGTH = 255;
+
+    private static final Charset US_ASCII = Charset.forName("US-ASCII");
+
+    // TODO(nathanmittler): Should these be in NativeConstants?
+    enum SessionType {
+        /**
+         * Identifies OpenSSL sessions.
+         */
+        OPEN_SSL(1),
+
+        /**
+         * Identifies OpenSSL sessions with OCSP stapled data.
+         */
+        OPEN_SSL_WITH_OCSP(2),
+
+        /**
+         * Identifies OpenSSL sessions with TLS SCT data.
+         */
+        OPEN_SSL_WITH_TLS_SCT(3);
+
+        SessionType(int value) {
+            this.value = value;
+        }
+
+        static boolean isSupportedType(int type) {
+            return type == OPEN_SSL.value || type == OPEN_SSL_WITH_OCSP.value
+                    || type == OPEN_SSL_WITH_TLS_SCT.value;
+        }
+
+        final int value;
+    }
+
+    /**
+     * States for SSL engines.
+     */
+    static final class EngineStates {
+        private EngineStates() {}
+
+        /**
+         * The engine is constructed, but the initial handshake hasn't been started
+         */
+        static final int STATE_NEW = 0;
+
+        /**
+         * The client/server mode of the engine has been set.
+         */
+        static final int STATE_MODE_SET = 1;
+
+        /**
+         * The handshake has been started
+         */
+        static final int STATE_HANDSHAKE_STARTED = 2;
+
+        /**
+         * Listeners of the handshake have been notified of completion but the handshake call
+         * hasn't returned.
+         */
+        static final int STATE_HANDSHAKE_COMPLETED = 3;
+
+        /**
+         * The handshake call returned but the listeners have not yet been notified. This is expected
+         * behaviour in cut-through mode, where SSL_do_handshake returns before the handshake is
+         * complete. We can now start writing data to the socket.
+         */
+        static final int STATE_READY_HANDSHAKE_CUT_THROUGH = 4;
+
+        /**
+         * The handshake call has returned and the listeners have been notified. Ready to begin
+         * writing data.
+         */
+        static final int STATE_READY = 5;
+
+        /**
+         * The inbound direction of the engine has been closed.
+         */
+        static final int STATE_CLOSED_INBOUND = 6;
+
+        /**
+         * The outbound direction of the engine has been closed.
+         */
+        static final int STATE_CLOSED_OUTBOUND = 7;
+
+        /**
+         * The engine has been closed.
+         */
+        static final int STATE_CLOSED = 8;
+    }
+
+    /**
+     * This is the maximum overhead when encrypting plaintext as defined by
+     * <a href="https://www.ietf.org/rfc/rfc5246.txt">rfc5264</a>,
+     * <a href="https://www.ietf.org/rfc/rfc5289.txt">rfc5289</a>, and the BoringSSL
+     * implementation itself.
+     *
+     * Please note that we use a padding of 16 here as BoringSSL uses PKCS#5 which uses 16 bytes
+     * while the spec itself allow up to 255 bytes. 16 bytes is the max for PKCS#5 (which handles it
+     * the same way as PKCS#7) as we use a block size of 16. See <a
+     * href="https://tools.ietf.org/html/rfc5652#section-6.3">rfc5652#section-6.3</a>.
+     *
+     * 16 (IV) + 48 (MAC) + 1 (Padding_length field) + 15 (Padding)
+     * + 1 (ContentType in TLSCiphertext) + 2 (ProtocolVersion) + 2 (Length)
+     * + 1 (ContentType in TLSInnerPlaintext)
+     */
+    private static final int MAX_ENCRYPTION_OVERHEAD_LENGTH = 15 + 48 + 1 + 16 + 1 + 2 + 2 + 1;
+
+    private static final int MAX_ENCRYPTION_OVERHEAD_DIFF =
+            Integer.MAX_VALUE - MAX_ENCRYPTION_OVERHEAD_LENGTH;
+
+    /** Key type: RSA certificate. */
+    private static final String KEY_TYPE_RSA = "RSA";
+
+    /** Key type: Elliptic Curve certificate. */
+    private static final String KEY_TYPE_EC = "EC";
+
+    static X509Certificate[] decodeX509CertificateChain(byte[][] certChain)
+            throws java.security.cert.CertificateException {
+        CertificateFactory certificateFactory = getCertificateFactory();
+        int numCerts = certChain.length;
+        X509Certificate[] decodedCerts = new X509Certificate[numCerts];
+        for (int i = 0; i < numCerts; i++) {
+            decodedCerts[i] = decodeX509Certificate(certificateFactory, certChain[i]);
+        }
+        return decodedCerts;
+    }
+
+    private static CertificateFactory getCertificateFactory() {
+        try {
+            return CertificateFactory.getInstance("X.509");
+        } catch (java.security.cert.CertificateException e) {
+            return null;
+        }
+    }
+
+    private static X509Certificate decodeX509Certificate(CertificateFactory certificateFactory,
+            byte[] bytes) throws java.security.cert.CertificateException {
+        if (certificateFactory != null) {
+            return (X509Certificate) certificateFactory.generateCertificate(
+                    new ByteArrayInputStream(bytes));
+        }
+        return OpenSSLX509Certificate.fromX509Der(bytes);
+    }
+
+    /**
+     * Returns key type constant suitable for calling X509KeyManager.chooseServerAlias or
+     * X509ExtendedKeyManager.chooseEngineServerAlias. Returns {@code null} for key exchanges that
+     * do not use X.509 for server authentication.
+     */
+    static String getServerX509KeyType(long sslCipherNative) throws SSLException {
+        String kx_name = NativeCrypto.SSL_CIPHER_get_kx_name(sslCipherNative);
+        if (kx_name.equals("RSA") || kx_name.equals("DHE_RSA") || kx_name.equals("ECDHE_RSA")) {
+            return KEY_TYPE_RSA;
+        } else if (kx_name.equals("ECDHE_ECDSA")) {
+            return KEY_TYPE_EC;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Similar to getServerKeyType, but returns value given TLS
+     * ClientCertificateType byte values from a CertificateRequest
+     * message for use with X509KeyManager.chooseClientAlias or
+     * X509ExtendedKeyManager.chooseEngineClientAlias.
+     * <p>
+     * Visible for testing.
+     */
+    static String getClientKeyType(byte clientCertificateType) {
+        // See also
+        // https://www.ietf.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-2
+        switch (clientCertificateType) {
+            case NativeConstants.TLS_CT_RSA_SIGN:
+                return KEY_TYPE_RSA; // RFC rsa_sign
+            case NativeConstants.TLS_CT_ECDSA_SIGN:
+                return KEY_TYPE_EC; // RFC ecdsa_sign
+            default:
+                return null;
+        }
+    }
+
+    static String getClientKeyTypeFromSignatureAlg(int signatureAlg) {
+        // See also
+        // https://www.ietf.org/assignments/tls-parameters/tls-parameters.xml#tls-signaturescheme
+        switch (NativeCrypto.SSL_get_signature_algorithm_key_type(signatureAlg)) {
+            case NativeConstants.EVP_PKEY_RSA:
+                return KEY_TYPE_RSA;
+            case NativeConstants.EVP_PKEY_EC:
+                return KEY_TYPE_EC;
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Gets the supported key types for client certificates based on the
+     * {@code ClientCertificateType} values provided by the server.
+     *
+     * @param clientCertificateTypes
+     *         {@code ClientCertificateType} values provided by the server.
+     *         See https://www.ietf.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-2.
+     * @param signatureAlgs
+     *         {@code SignatureScheme} values provided by the server.
+     *         See https://www.ietf.org/assignments/tls-parameters/tls-parameters.xml#tls-signaturescheme
+     * @return supported key types that can be used in {@code X509KeyManager.chooseClientAlias} and
+     * {@code X509ExtendedKeyManager.chooseEngineClientAlias}.  If the inputs imply a preference
+     * order, the returned set will have an iteration order that respects that preference order,
+     * otherwise it will be in an arbitrary order.
+     *
+     * Visible for testing.
+     */
+    static Set<String> getSupportedClientKeyTypes(byte[] clientCertificateTypes,
+            int[] signatureAlgs) {
+        Set<String> fromClientCerts = new HashSet<String>(clientCertificateTypes.length);
+        for (byte keyTypeCode : clientCertificateTypes) {
+            String keyType = SSLUtils.getClientKeyType(keyTypeCode);
+            if (keyType == null) {
+                // Unsupported client key type -- ignore
+                continue;
+            }
+            fromClientCerts.add(keyType);
+        }
+        // Signature algorithms are listed in preference order
+        Set<String> fromSigAlgs = new LinkedHashSet<String>(signatureAlgs.length);
+        for (int signatureAlg : signatureAlgs) {
+            String keyType = SSLUtils.getClientKeyTypeFromSignatureAlg(signatureAlg);
+            if (keyType == null) {
+                // Unsupported client key type -- ignore
+                continue;
+            }
+            fromSigAlgs.add(keyType);
+        }
+        // If both are specified, the key needs to meet both sets of requirements.  Otherwise,
+        // just meet the set of requirements that were specified.  See RFC 5246, section 7.4.4.
+        // (In TLS 1.3, certificate_types is no longer used and is never present.)
+        if (clientCertificateTypes.length > 0 && signatureAlgs.length > 0) {
+            fromSigAlgs.retainAll(fromClientCerts);
+            return fromSigAlgs;
+        } else if (signatureAlgs.length > 0) {
+            return fromSigAlgs;
+        } else {
+            return fromClientCerts;
+        }
+    }
+
+    static byte[][] encodeSubjectX509Principals(X509Certificate[] certificates)
+            throws CertificateEncodingException {
+        byte[][] principalBytes = new byte[certificates.length][];
+        for (int i = 0; i < certificates.length; i++) {
+            principalBytes[i] = certificates[i].getSubjectX500Principal().getEncoded();
+        }
+        return principalBytes;
+    }
+
+    /**
+     * Converts the peer certificates into a cert chain.
+     */
+    static javax.security.cert.X509Certificate[] toCertificateChain(X509Certificate[] certificates)
+            throws SSLPeerUnverifiedException {
+        try {
+            javax.security.cert.X509Certificate[] chain =
+                    new javax.security.cert.X509Certificate[certificates.length];
+
+            for (int i = 0; i < certificates.length; i++) {
+                byte[] encoded = certificates[i].getEncoded();
+                chain[i] = javax.security.cert.X509Certificate.getInstance(encoded);
+            }
+            return chain;
+        } catch (CertificateEncodingException e) {
+            SSLPeerUnverifiedException exception = new SSLPeerUnverifiedException(e.getMessage());
+            exception.initCause(exception);
+            throw exception;
+        } catch (CertificateException e) {
+            SSLPeerUnverifiedException exception = new SSLPeerUnverifiedException(e.getMessage());
+            exception.initCause(exception);
+            throw exception;
+        }
+    }
+
+    /**
+     * Calculates the minimum bytes required in the encrypted output buffer for the given number of
+     * plaintext source bytes.
+     */
+    static int calculateOutNetBufSize(int pendingBytes) {
+        return min(SSL3_RT_MAX_PACKET_SIZE,
+                MAX_ENCRYPTION_OVERHEAD_LENGTH + min(MAX_ENCRYPTION_OVERHEAD_DIFF, pendingBytes));
+    }
+
+    /**
+     * Wraps the given exception if it's not already a {@link SSLHandshakeException}.
+     */
+    static SSLHandshakeException toSSLHandshakeException(Throwable e) {
+        if (e instanceof SSLHandshakeException) {
+            return (SSLHandshakeException) e;
+        }
+
+        return (SSLHandshakeException) new SSLHandshakeException(e.getMessage()).initCause(e);
+    }
+
+    /**
+     * Wraps the given exception if it's not already a {@link SSLException}.
+     */
+    static SSLException toSSLException(Throwable e) {
+        if (e instanceof SSLException) {
+            return (SSLException) e;
+        }
+        return new SSLException(e);
+    }
+
+    static String toProtocolString(byte[] bytes) {
+        if (bytes == null) {
+            return null;
+        }
+        return new String(bytes, US_ASCII);
+    }
+
+    static byte[] toProtocolBytes(String protocol) {
+        if (protocol == null) {
+            return null;
+        }
+        return protocol.getBytes(US_ASCII);
+    }
+
+    /**
+     * Decodes the given list of protocols into {@link String}s.
+     * @param protocols the encoded protocol list
+     * @return the decoded protocols or {@link EmptyArray#BYTE} if {@code protocols} is
+     * empty.
+     * @throws NullPointerException if protocols is {@code null}.
+     */
+    static String[] decodeProtocols(byte[] protocols) {
+        if (protocols.length == 0) {
+            return EmptyArray.STRING;
+        }
+
+        int numProtocols = 0;
+        for (int i = 0; i < protocols.length;) {
+            int protocolLength = protocols[i];
+            if (protocolLength < 0 || protocolLength > protocols.length - i) {
+                throw new IllegalArgumentException(
+                    "Protocol has invalid length (" + protocolLength + " at position " + i
+                        + "): " + (protocols.length < 50
+                        ? Arrays.toString(protocols) : protocols.length + " byte array"));
+            }
+
+            numProtocols++;
+            i += 1 + protocolLength;
+        }
+
+        String[] decoded = new String[numProtocols];
+        for (int i = 0, d = 0; i < protocols.length;) {
+            int protocolLength = protocols[i];
+            decoded[d++] = protocolLength > 0
+                    ? new String(protocols, i + 1, protocolLength, US_ASCII)
+                    : "";
+            i += 1 + protocolLength;
+        }
+
+        return decoded;
+    }
+
+    /**
+     * Encodes a list of protocols into the wire-format (length-prefixed 8-bit strings).
+     * Requires that all strings be encoded with US-ASCII.
+     *
+     * @param protocols the list of protocols to be encoded
+     * @return the encoded form of the protocol list.
+     * @throws IllegalArgumentException if protocols is {@code null}, or if any element is
+     * {@code null} or an empty string.
+     */
+    static byte[] encodeProtocols(String[] protocols) {
+        if (protocols == null) {
+            throw new IllegalArgumentException("protocols array must be non-null");
+        }
+
+        if (protocols.length == 0) {
+            return EmptyArray.BYTE;
+        }
+
+        // Calculate the encoded length.
+        int length = 0;
+        for (int i = 0; i < protocols.length; ++i) {
+            String protocol = protocols[i];
+            if (protocol == null) {
+                throw new IllegalArgumentException("protocol[" + i + "] is null");
+            }
+            int protocolLength = protocols[i].length();
+
+            // Verify that the length is valid here, so that we don't attempt to allocate an array
+            // below if the threshold is violated.
+            if (protocolLength == 0 || protocolLength > MAX_PROTOCOL_LENGTH) {
+                throw new IllegalArgumentException(
+                    "protocol[" + i + "] has invalid length: " + protocolLength);
+            }
+
+            // Include a 1-byte prefix for each protocol.
+            length += 1 + protocolLength;
+        }
+
+        byte[] data = new byte[length];
+        for (int dataIndex = 0, i = 0; i < protocols.length; ++i) {
+            String protocol = protocols[i];
+            int protocolLength = protocol.length();
+
+            // Add the length prefix.
+            data[dataIndex++] = (byte) protocolLength;
+            for (int ci = 0; ci < protocolLength; ++ci) {
+                char c = protocol.charAt(ci);
+                if (c > Byte.MAX_VALUE) {
+                    // Enforce US-ASCII
+                    throw new IllegalArgumentException("Protocol contains invalid character: "
+                        + c + "(protocol=" + protocol + ")");
+                }
+                data[dataIndex++] = (byte) c;
+            }
+        }
+        return data;
+    }
+
+    /**
+     * Return how much bytes can be read out of the encrypted data. Be aware that this method will
+     * not increase the readerIndex of the given {@link ByteBuffer}.
+     *
+     * @param buffers The {@link ByteBuffer}s to read from. Be aware that they must have at least
+     * {@link com.android.org.conscrypt.NativeConstants#SSL3_RT_HEADER_LENGTH} bytes to read, otherwise it will
+     * throw an {@link IllegalArgumentException}.
+     * @return length The length of the encrypted packet that is included in the buffer. This will
+     * return {@code -1} if the given {@link ByteBuffer} is not encrypted at all.
+     * @throws IllegalArgumentException Is thrown if the given {@link ByteBuffer} has not at least
+     * {@link com.android.org.conscrypt.NativeConstants#SSL3_RT_HEADER_LENGTH} bytes to read.
+     */
+    static int getEncryptedPacketLength(ByteBuffer[] buffers, int offset) {
+        ByteBuffer buffer = buffers[offset];
+
+        // Check if everything we need is in one ByteBuffer. If so we can make use of the fast-path.
+        if (buffer.remaining() >= SSL3_RT_HEADER_LENGTH) {
+            return getEncryptedPacketLength(buffer);
+        }
+
+        // We need to copy 5 bytes into a temporary buffer so we can parse out the packet length
+        // easily.
+        ByteBuffer tmp = ByteBuffer.allocate(SSL3_RT_HEADER_LENGTH);
+        do {
+            buffer = buffers[offset++];
+            int pos = buffer.position();
+            int limit = buffer.limit();
+            if (buffer.remaining() > tmp.remaining()) {
+                buffer.limit(pos + tmp.remaining());
+            }
+            try {
+                tmp.put(buffer);
+            } finally {
+                // Restore the original indices.
+                buffer.limit(limit);
+                buffer.position(pos);
+            }
+        } while (tmp.hasRemaining());
+
+        // Done, flip the buffer so we can read from it.
+        tmp.flip();
+        return getEncryptedPacketLength(tmp);
+    }
+
+    private static int getEncryptedPacketLength(ByteBuffer buffer) {
+        int pos = buffer.position();
+        // SSLv3 or TLS - Check ContentType
+        switch (unsignedByte(buffer.get(pos))) {
+            case SSL3_RT_CHANGE_CIPHER_SPEC:
+            case SSL3_RT_ALERT:
+            case SSL3_RT_HANDSHAKE:
+            case SSL3_RT_APPLICATION_DATA:
+                break;
+            default:
+                // SSLv2 or bad data
+                return -1;
+        }
+
+        // SSLv3 or TLS - Check ProtocolVersion
+        int majorVersion = unsignedByte(buffer.get(pos + 1));
+        if (majorVersion != 3) {
+            // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
+            return -1;
+        }
+
+        // SSLv3 or TLS
+        int packetLength = unsignedShort(buffer.getShort(pos + 3)) + SSL3_RT_HEADER_LENGTH;
+        if (packetLength <= SSL3_RT_HEADER_LENGTH) {
+            // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
+            return -1;
+        }
+        return packetLength;
+    }
+
+    private static short unsignedByte(byte b) {
+        return (short) (b & 0xFF);
+    }
+
+    private static int unsignedShort(short s) {
+        return s & 0xFFFF;
+    }
+
+    static String[] concat(String[]... arrays) {
+        int resultLength = 0;
+        for (String[] array : arrays) {
+            resultLength += array.length;
+        }
+        String[] result = new String[resultLength];
+        int resultOffset = 0;
+        for (String[] array : arrays) {
+            System.arraycopy(array, 0, result, resultOffset, array.length);
+            resultOffset += array.length;
+        }
+        return result;
+    }
+
+    private SSLUtils() {}
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ServerSessionContext.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ServerSessionContext.java
new file mode 100644
index 0000000..1be9883
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ServerSessionContext.java
@@ -0,0 +1,89 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import javax.net.ssl.SSLContext;
+
+/**
+ * Caches server sessions. Indexes by session ID. Users typically look up
+ * sessions using the ID provided by an SSL client.
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public final class ServerSessionContext extends AbstractSessionContext {
+    private SSLServerSessionCache persistentCache;
+
+    ServerSessionContext() {
+        super(100);
+
+        // TODO make sure SSL_CTX does not automaticaly clear sessions we want it to cache
+        // SSL_CTX_set_session_cache_mode(sslCtxNativePointer, SSL_SESS_CACHE_NO_AUTO_CLEAR);
+
+        // TODO remove SSL_CTX session cache limit so we can manage it
+        // SSL_CTX_sess_set_cache_size(sslCtxNativePointer, 0);
+
+        // TODO override trimToSize and removeEldestEntry to use
+        // SSL_CTX_sessions to remove from native cache
+
+        // Set a trivial session id context. OpenSSL uses this to make
+        // sure you don't reuse sessions externalized with i2d_SSL_SESSION
+        // between apps. However our sessions are either in memory or
+        // exported to a app's SSLServerSessionCache.
+        NativeCrypto.SSL_CTX_set_session_id_context(sslCtxNativePointer, this, new byte[] { ' ' });
+    }
+
+    /**
+     * Applications should not use this method. Instead use {@link
+     * Conscrypt#setServerSessionCache(SSLContext, SSLServerSessionCache)}.
+     */
+    public void setPersistentCache(SSLServerSessionCache persistentCache) {
+        this.persistentCache = persistentCache;
+    }
+
+    @Override
+    NativeSslSession getSessionFromPersistentCache(byte[] sessionId) {
+        if (persistentCache != null) {
+            byte[] data = persistentCache.getSessionData(sessionId);
+            if (data != null) {
+                NativeSslSession session = NativeSslSession.newInstance(this, data, null, -1);
+                if (session != null && session.isValid()) {
+                    cacheSession(session);
+                    return session;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    void onBeforeAddSession(NativeSslSession session) {
+        // TODO: Do this in background thread.
+        if (persistentCache != null) {
+            byte[] data = session.toBytes();
+            if (data != null) {
+                persistentCache.putSessionData(session.toSSLSession(), data);
+            }
+        }
+    }
+
+    @Override
+    void onBeforeRemoveSession(NativeSslSession session) {
+        // Do nothing.
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/SessionSnapshot.java b/repackaged/common/src/main/java/com/android/org/conscrypt/SessionSnapshot.java
new file mode 100644
index 0000000..4a9d781
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/SessionSnapshot.java
@@ -0,0 +1,187 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSessionContext;
+
+/**
+ * A snapshot of the content of another {@link ConscryptSession}. This copies everything over
+ * except for the certificates.
+ */
+final class SessionSnapshot implements ConscryptSession {
+    private final SSLSessionContext sessionContext;
+    private final byte[] id;
+    private final String requestedServerName;
+    private final List<byte[]> statusResponses;
+    private final byte[] peerTlsSctData;
+    private final long creationTime;
+    private final long lastAccessedTime;
+    private final String cipherSuite;
+    private final String protocol;
+    private final String peerHost;
+    private final int peerPort;
+
+    SessionSnapshot(ConscryptSession session) {
+        sessionContext = session.getSessionContext();
+        id = session.getId();
+        requestedServerName = session.getRequestedServerName();
+        statusResponses = session.getStatusResponses();
+        peerTlsSctData = session.getPeerSignedCertificateTimestamp();
+        creationTime = session.getCreationTime();
+        lastAccessedTime = session.getLastAccessedTime();
+        cipherSuite = session.getCipherSuite();
+        protocol = session.getProtocol();
+        peerHost = session.getPeerHost();
+        peerPort = session.getPeerPort();
+    }
+
+    @Override
+    public String getRequestedServerName() {
+        return requestedServerName;
+    }
+
+    @Override
+    public List<byte[]> getStatusResponses() {
+        List<byte[]> ret = new ArrayList<byte[]>(statusResponses.size());
+        for (byte[] resp : statusResponses) {
+            ret.add(resp.clone());
+        }
+        return ret;
+    }
+
+    @Override
+    public byte[] getPeerSignedCertificateTimestamp() {
+        return peerTlsSctData != null ? peerTlsSctData.clone() : null;
+    }
+
+    @Override
+    public byte[] getId() {
+        return id;
+    }
+
+    @Override
+    public SSLSessionContext getSessionContext() {
+        return sessionContext;
+    }
+
+    @Override
+    public long getCreationTime() {
+        return creationTime;
+    }
+
+    @Override
+    public long getLastAccessedTime() {
+        return lastAccessedTime;
+    }
+
+    @Override
+    public void invalidate() {
+        // Do nothing.
+    }
+
+    @Override
+    public boolean isValid() {
+        return false;
+    }
+
+    @Override
+    public void putValue(String s, Object o) {
+        throw new UnsupportedOperationException(
+                "All calls to this method should be intercepted by ProvidedSessionDecorator.");
+    }
+
+    @Override
+    public Object getValue(String s) {
+        throw new UnsupportedOperationException(
+                "All calls to this method should be intercepted by ProvidedSessionDecorator.");
+    }
+
+    @Override
+    public void removeValue(String s) {
+        throw new UnsupportedOperationException(
+                "All calls to this method should be intercepted by ProvidedSessionDecorator.");
+    }
+
+    @Override
+    public String[] getValueNames() {
+        throw new UnsupportedOperationException(
+                "All calls to this method should be intercepted by ProvidedSessionDecorator.");
+    }
+
+    @Override
+    public X509Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+        throw new SSLPeerUnverifiedException("No peer certificates");
+    }
+
+    @Override
+    public Certificate[] getLocalCertificates() {
+        return null;
+    }
+
+    @Override
+    public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+        throws SSLPeerUnverifiedException {
+        throw new SSLPeerUnverifiedException("No peer certificates");
+    }
+
+    @Override
+    public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+        throw new SSLPeerUnverifiedException("No peer certificates");
+    }
+
+    @Override
+    public Principal getLocalPrincipal() {
+        return null;
+    }
+
+    @Override
+    public String getCipherSuite() {
+        return cipherSuite;
+    }
+
+    @Override
+    public String getProtocol() {
+        return protocol;
+    }
+
+    @Override
+    public String getPeerHost() {
+        return peerHost;
+    }
+
+    @Override
+    public int getPeerPort() {
+        return peerPort;
+    }
+
+    @Override
+    public int getPacketBufferSize() {
+        return NativeConstants.SSL3_RT_MAX_PACKET_SIZE;
+    }
+
+    @Override
+    public int getApplicationBufferSize() {
+        return NativeConstants.SSL3_RT_MAX_PLAIN_LENGTH;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/TrustManagerFactoryImpl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/TrustManagerFactoryImpl.java
new file mode 100644
index 0000000..438b73f
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/TrustManagerFactoryImpl.java
@@ -0,0 +1,90 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// License from Apache Harmony:
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import javax.net.ssl.ManagerFactoryParameters;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactorySpi;
+
+/**
+ *
+ * TrustManagerFactory service provider interface implementation.
+ *
+ * @see javax.net.ssl.TrustManagerFactorySpi
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public class TrustManagerFactoryImpl extends TrustManagerFactorySpi {
+
+    private KeyStore keyStore;
+
+    /**
+     * @see javax.net.ssl.TrustManagerFactorySpi#engineInit(KeyStore)
+     */
+    @Override
+    public void engineInit(KeyStore ks) throws KeyStoreException {
+        if (ks != null) {
+            keyStore = ks;
+        } else {
+            keyStore = Platform.getDefaultCertKeyStore();
+        }
+    }
+
+    /**
+     * @see javax.net.ssl#engineInit(ManagerFactoryParameters)
+     */
+    @Override
+    public void engineInit(ManagerFactoryParameters spec)
+            throws InvalidAlgorithmParameterException {
+        throw new InvalidAlgorithmParameterException(
+                "ManagerFactoryParameters not supported");
+    }
+
+    /**
+     * @see javax.net.ssl#engineGetTrustManagers()
+     */
+    @Override
+    public TrustManager[] engineGetTrustManagers() {
+        if (keyStore == null) {
+            throw new IllegalStateException(
+                    "TrustManagerFactory is not initialized");
+        }
+        return new TrustManager[] { new TrustManagerImpl(keyStore) };
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/TrustManagerImpl.java b/repackaged/common/src/main/java/com/android/org/conscrypt/TrustManagerImpl.java
new file mode 100644
index 0000000..3e9c20a
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/TrustManagerImpl.java
@@ -0,0 +1,1051 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// License from Apache Harmony:
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.Socket;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.PKIXParameters;
+import java.security.cert.PKIXRevocationChecker;
+import java.security.cert.PKIXRevocationChecker.Option;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Logger;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509ExtendedTrustManager;
+import com.android.org.conscrypt.ct.CTLogStore;
+import com.android.org.conscrypt.ct.CTPolicy;
+import com.android.org.conscrypt.ct.CTVerificationResult;
+import com.android.org.conscrypt.ct.CTVerifier;
+
+/**
+ *
+ * TrustManager implementation. The implementation is based on CertPathValidator
+ * PKIX and CertificateFactory X509 implementations. This implementations should
+ * be provided by some certification provider.
+ *
+ * @see javax.net.ssl.X509ExtendedTrustManager
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.CorePlatformApi
+@Internal
+public final class TrustManagerImpl extends X509ExtendedTrustManager {
+
+    private static final Logger logger = Logger.getLogger(TrustManagerImpl.class.getName());
+
+    /**
+     * Comparator used for ordering trust anchors during certificate path building.
+     */
+    private static final TrustAnchorComparator TRUST_ANCHOR_COMPARATOR =
+            new TrustAnchorComparator();
+
+    private static ConscryptHostnameVerifier defaultHostnameVerifier;
+
+    /**
+     * The AndroidCAStore if non-null, null otherwise.
+     */
+    private final KeyStore rootKeyStore;
+
+    /**
+     * The CertPinManager, which validates the chain against a host-to-pin mapping
+     */
+    private CertPinManager pinManager;
+
+    /**
+     * The backing store for the AndroidCAStore if non-null. This will
+     * be null when the rootKeyStore is null, implying we are not
+     * using the AndroidCAStore.
+     */
+    private final ConscryptCertStore trustedCertificateStore;
+
+    private final CertPathValidator validator;
+
+    /**
+     * An index of TrustAnchor instances that we've seen.
+     */
+    private final TrustedCertificateIndex trustedCertificateIndex;
+
+    /**
+     * An index of intermediate certificates that we've seen. These certificates are NOT implicitly
+     * trusted and must still form a valid chain to an anchor.
+     */
+    private final TrustedCertificateIndex intermediateIndex;
+
+    /**
+     * This is lazily initialized in the AndroidCAStore case since it
+     * forces us to bring all the CAs into memory. In the
+     * non-AndroidCAStore, we initialize this as part of the
+     * constructor.
+     */
+    private final X509Certificate[] acceptedIssuers;
+
+    private final Exception err;
+    private final CertificateFactory factory;
+    private final CertBlacklist blacklist;
+    private CTVerifier ctVerifier;
+    private CTPolicy ctPolicy;
+
+    private ConscryptHostnameVerifier hostnameVerifier;
+
+    // Forces CT verification to always to done. For tests.
+    private boolean ctEnabledOverride;
+
+    /**
+     * Creates X509TrustManager based on a keystore
+     *
+     * @param keyStore
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    public TrustManagerImpl(KeyStore keyStore) {
+        this(keyStore, null);
+    }
+
+    public TrustManagerImpl(KeyStore keyStore, CertPinManager manager) {
+        this(keyStore, manager, null);
+    }
+
+    @libcore.api.CorePlatformApi
+    public TrustManagerImpl(KeyStore keyStore, CertPinManager manager,
+            ConscryptCertStore certStore) {
+        this(keyStore, manager, certStore, null);
+    }
+
+    public TrustManagerImpl(KeyStore keyStore, CertPinManager manager,
+            ConscryptCertStore certStore,
+                            CertBlacklist blacklist) {
+        this(keyStore, manager, certStore, blacklist, null, null, null);
+    }
+
+    /**
+     * For testing only.
+     */
+    public TrustManagerImpl(KeyStore keyStore, CertPinManager manager,
+            ConscryptCertStore certStore, CertBlacklist blacklist, CTLogStore ctLogStore,
+            CTVerifier ctVerifier, CTPolicy ctPolicy) {
+        CertPathValidator validatorLocal = null;
+        CertificateFactory factoryLocal = null;
+        KeyStore rootKeyStoreLocal = null;
+        ConscryptCertStore trustedCertificateStoreLocal = null;
+        TrustedCertificateIndex trustedCertificateIndexLocal = null;
+        X509Certificate[] acceptedIssuersLocal = null;
+        Exception errLocal = null;
+        try {
+            validatorLocal = CertPathValidator.getInstance("PKIX");
+            factoryLocal = CertificateFactory.getInstance("X509");
+
+            // if we have an AndroidCAStore, we will lazily load CAs
+            if ("AndroidCAStore".equals(keyStore.getType())
+                    && Platform.supportsConscryptCertStore()) {
+                rootKeyStoreLocal = keyStore;
+                trustedCertificateStoreLocal =
+                    (certStore != null) ? certStore : Platform.newDefaultCertStore();
+                acceptedIssuersLocal = null;
+                trustedCertificateIndexLocal = new TrustedCertificateIndex();
+            } else {
+                rootKeyStoreLocal = null;
+                trustedCertificateStoreLocal = certStore;
+                acceptedIssuersLocal = acceptedIssuers(keyStore);
+                trustedCertificateIndexLocal
+                        = new TrustedCertificateIndex(trustAnchors(acceptedIssuersLocal));
+            }
+
+        } catch (Exception e) {
+            errLocal = e;
+        }
+
+        if (blacklist == null) {
+            blacklist = Platform.newDefaultBlacklist();
+        }
+        if (ctLogStore == null) {
+            ctLogStore = Platform.newDefaultLogStore();
+        }
+
+        if (ctPolicy == null) {
+            ctPolicy = Platform.newDefaultPolicy(ctLogStore);
+        }
+
+        this.pinManager = manager;
+        this.rootKeyStore = rootKeyStoreLocal;
+        this.trustedCertificateStore = trustedCertificateStoreLocal;
+        this.validator = validatorLocal;
+        this.factory = factoryLocal;
+        this.trustedCertificateIndex = trustedCertificateIndexLocal;
+        this.intermediateIndex = new TrustedCertificateIndex();
+        this.acceptedIssuers = acceptedIssuersLocal;
+        this.err = errLocal;
+        this.blacklist = blacklist;
+        this.ctVerifier = new CTVerifier(ctLogStore);
+        this.ctPolicy = ctPolicy;
+    }
+
+    private static X509Certificate[] acceptedIssuers(KeyStore ks) {
+        try {
+            // Note that unlike the PKIXParameters code to create a Set of
+            // TrustAnchors from a KeyStore, this version takes from both
+            // TrustedCertificateEntry and PrivateKeyEntry, not just
+            // TrustedCertificateEntry, which is why TrustManagerImpl
+            // cannot just use an PKIXParameters(KeyStore)
+            // constructor.
+
+            // TODO remove duplicates if same cert is found in both a
+            // PrivateKeyEntry and TrustedCertificateEntry
+            List<X509Certificate> trusted = new ArrayList<X509Certificate>();
+            for (Enumeration<String> en = ks.aliases(); en.hasMoreElements();) {
+                final String alias = en.nextElement();
+                final X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
+                if (cert != null) {
+                    trusted.add(cert);
+                }
+            }
+            return trusted.toArray(new X509Certificate[trusted.size()]);
+        } catch (KeyStoreException e) {
+            return new X509Certificate[0];
+        }
+    }
+
+    private static Set<TrustAnchor> trustAnchors(X509Certificate[] certs) {
+        Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>(certs.length);
+        for (X509Certificate cert : certs) {
+            trustAnchors.add(new TrustAnchor(cert, null));
+        }
+        return trustAnchors;
+    }
+
+    @libcore.api.CorePlatformApi
+    @Override
+    public void checkClientTrusted(X509Certificate[] chain, String authType)
+            throws CertificateException {
+        checkTrusted(chain, authType, null, null, true /* client auth */);
+    }
+
+    /**
+     * For backward compatibility with older Android API that used String for the hostname only.
+     */
+    public List<X509Certificate> checkClientTrusted(X509Certificate[] chain, String authType,
+            String hostname) throws CertificateException {
+        return checkTrusted(chain, null /* ocspData */, null /* tlsSctData */, authType, hostname,
+                true);
+    }
+
+    private static SSLSession getHandshakeSessionOrThrow(SSLSocket sslSocket)
+            throws CertificateException {
+        SSLSession session = sslSocket.getHandshakeSession();
+        if (session == null) {
+            throw new CertificateException("Not in handshake; no session available");
+        }
+        return session;
+    }
+
+    @libcore.api.CorePlatformApi
+    @Override
+    public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
+            throws CertificateException {
+        SSLSession session = null;
+        SSLParameters parameters = null;
+        if (socket instanceof SSLSocket) {
+            SSLSocket sslSocket = (SSLSocket) socket;
+            session = getHandshakeSessionOrThrow(sslSocket);
+            parameters = sslSocket.getSSLParameters();
+        }
+        checkTrusted(chain, authType, session, parameters, true /* client auth */);
+    }
+
+    @libcore.api.CorePlatformApi
+    @Override
+    public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
+            throws CertificateException {
+        SSLSession session = engine.getHandshakeSession();
+        if (session == null) {
+            throw new CertificateException("Not in handshake; no session available");
+        }
+        checkTrusted(chain, authType, session, engine.getSSLParameters(), true /* client auth */);
+    }
+
+    @Override
+    public void checkServerTrusted(X509Certificate[] chain, String authType)
+            throws CertificateException {
+        checkTrusted(chain, authType, null, null, false /* client auth */);
+    }
+
+    /**
+     * For backward compatibility with older Android API that used String for the hostname only.
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType,
+            String hostname) throws CertificateException {
+        return checkTrusted(chain, null /* ocspData */, null /* tlsSctData */, authType, hostname,
+                false);
+    }
+
+    /**
+     * Returns the full trusted certificate chain found from {@code certs}.
+     *
+     * Throws {@link CertificateException} when no trusted chain can be found from {@code certs}.
+     */
+    @libcore.api.CorePlatformApi
+    public List<X509Certificate> getTrustedChainForServer(X509Certificate[] certs,
+            String authType, Socket socket) throws CertificateException {
+        SSLSession session = null;
+        SSLParameters parameters = null;
+        if (socket instanceof SSLSocket) {
+            SSLSocket sslSocket = (SSLSocket) socket;
+            session = getHandshakeSessionOrThrow(sslSocket);
+            parameters = sslSocket.getSSLParameters();
+        }
+        return checkTrusted(certs, authType, session, parameters, false /* client auth */);
+    }
+
+    /**
+     * Returns the full trusted certificate chain found from {@code certs}.
+     *
+     * Throws {@link CertificateException} when no trusted chain can be found from {@code certs}.
+     */
+    @libcore.api.CorePlatformApi
+    public List<X509Certificate> getTrustedChainForServer(X509Certificate[] certs,
+            String authType, SSLEngine engine) throws CertificateException {
+        SSLSession session = engine.getHandshakeSession();
+        if (session == null) {
+            throw new CertificateException("Not in handshake; no session available");
+        }
+        return checkTrusted(certs, authType, session, engine.getSSLParameters(),
+                false /* client auth */);
+    }
+
+    @Override
+    public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
+            throws CertificateException {
+        getTrustedChainForServer(chain, authType, socket);
+    }
+
+    @Override
+    public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
+            throws CertificateException {
+        getTrustedChainForServer(chain, authType, engine);
+    }
+
+    /**
+     * Validates whether a server is trusted. If session is given and non-null
+     * it also checks if chain is pinned appropriately for that peer host. If
+     * null, it does not check for pinned certs. The return value is a list of
+     * the certificates used for making the trust decision.
+     */
+    public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType,
+            SSLSession session) throws CertificateException {
+        return checkTrusted(chain, authType, session, null, false /* client auth */);
+    }
+
+    @libcore.api.CorePlatformApi
+    public void handleTrustStorageUpdate() {
+        if (acceptedIssuers == null) {
+            trustedCertificateIndex.reset();
+        } else {
+            trustedCertificateIndex.reset(trustAnchors(acceptedIssuers));
+        }
+    }
+
+    private List<X509Certificate> checkTrusted(X509Certificate[] certs, String authType,
+            SSLSession session, SSLParameters parameters, boolean clientAuth)
+                    throws CertificateException {
+        byte[] ocspData = null;
+        byte[] tlsSctData = null;
+        String hostname = null;
+        if (session != null) {
+            hostname = session.getPeerHost();
+            ocspData = getOcspDataFromSession(session);
+            tlsSctData = getTlsSctDataFromSession(session);
+        }
+
+        if (session != null && parameters != null) {
+            String identificationAlgorithm = parameters.getEndpointIdentificationAlgorithm();
+            if ("HTTPS".equalsIgnoreCase(identificationAlgorithm)) {
+                ConscryptHostnameVerifier verifier = getHttpsVerifier();
+                if (!verifier.verify(hostname, session)) {
+                    throw new CertificateException("No subjectAltNames on the certificate match");
+                }
+            }
+        }
+        return checkTrusted(certs, ocspData, tlsSctData, authType, hostname, clientAuth);
+    }
+
+    private byte[] getOcspDataFromSession(SSLSession session) {
+        List<byte[]> ocspResponses = null;
+        if (session instanceof ConscryptSession) {
+            ConscryptSession opensslSession = (ConscryptSession) session;
+            ocspResponses = opensslSession.getStatusResponses();
+        } else {
+            Method m_getResponses;
+            try {
+                m_getResponses = session.getClass().getDeclaredMethod("getStatusResponses");
+                m_getResponses.setAccessible(true);
+                Object rawResponses = m_getResponses.invoke(session);
+                if (rawResponses instanceof List) {
+                    ocspResponses = (List<byte[]>) rawResponses;
+                }
+            } catch (NoSuchMethodException ignored) {
+            } catch (SecurityException ignored) {
+            } catch (IllegalAccessException ignored) {
+            } catch (IllegalArgumentException ignored) {
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException(e.getCause());
+            }
+        }
+
+        if (ocspResponses == null || ocspResponses.isEmpty()) {
+            return null;
+        }
+
+        return ocspResponses.get(0);
+    }
+
+    private byte[] getTlsSctDataFromSession(SSLSession session) {
+        if (session instanceof ConscryptSession) {
+            ConscryptSession opensslSession = (ConscryptSession) session;
+            return opensslSession.getPeerSignedCertificateTimestamp();
+        }
+
+        byte[] data = null;
+        try {
+            Method m_getTlsSctData = session.getClass().getDeclaredMethod("getPeerSignedCertificateTimestamp");
+            m_getTlsSctData.setAccessible(true);
+            Object rawData = m_getTlsSctData.invoke(session);
+            if (rawData instanceof byte[]) {
+                data = (byte[]) rawData;
+            }
+        } catch (NoSuchMethodException ignored) {
+        } catch (SecurityException ignored) {
+        } catch (IllegalAccessException ignored) {
+        } catch (IllegalArgumentException ignored) {
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException(e.getCause());
+        }
+        return data;
+    }
+
+    private List<X509Certificate> checkTrusted(X509Certificate[] certs, byte[] ocspData,
+            byte[] tlsSctData, String authType, String host, boolean clientAuth)
+            throws CertificateException {
+        if (certs == null || certs.length == 0 || authType == null || authType.length() == 0) {
+            throw new IllegalArgumentException("null or zero-length parameter");
+        }
+        if (err != null) {
+            throw new CertificateException(err);
+        }
+        Set<X509Certificate> used = new HashSet<X509Certificate>();
+        ArrayList<X509Certificate> untrustedChain = new ArrayList<X509Certificate>();
+        ArrayList<TrustAnchor> trustedChain = new ArrayList<TrustAnchor>();
+        // Initialize the chain to contain the leaf certificate. This potentially could be a trust
+        // anchor. If the leaf is a trust anchor we still continue with path building to build the
+        // complete trusted chain for additional validation such as certificate pinning.
+        X509Certificate leaf = certs[0];
+        TrustAnchor leafAsAnchor = findTrustAnchorBySubjectAndPublicKey(leaf);
+        if (leafAsAnchor != null) {
+            trustedChain.add(leafAsAnchor);
+            used.add(leafAsAnchor.getTrustedCert());
+        } else {
+            untrustedChain.add(leaf);
+        }
+        used.add(leaf);
+        return checkTrustedRecursive(certs, ocspData, tlsSctData, host, clientAuth,
+                untrustedChain, trustedChain, used);
+    }
+
+    /**
+     * Recursively build certificate chains until a valid chain is found or all possible paths are
+     * exhausted.
+     *
+     * The chain is built in two sections, the complete trusted path is the the combination of
+     * {@code untrustedChain} and {@code trustAnchorChain}. The chain begins at the leaf
+     * certificate and ends in the final trusted root certificate.
+     *
+     * @param certs the bag of certs provided by the peer. No order is assumed.
+     * @param host the host being connected to.
+     * @param clientAuth if a client is being authorized instead of a server.
+     * @param untrustedChain the untrusted section of the chain built so far. Must be mutable.
+     * @param trustAnchorChain the trusted section of the chain built so far. Must be mutable.
+     * @param used the set certificates used so far in path building. Must be mutable.
+     *
+     * @return The entire valid chain starting with the leaf certificate. This is the
+     * concatenation of untrustedChain and trustAnchorChain.
+     *
+     * @throws CertificateException If no valid chain could be constructed. Note that there may be
+     * multiple reasons why no valid chain exists and there is no guarantee that the most severe is
+     * reported in this exception. As such applications MUST NOT use the specifics of this error
+     * for trust decisions (e.g. showing the user a click through page based on the specific error).
+     */
+    private List<X509Certificate> checkTrustedRecursive(X509Certificate[] certs, byte[] ocspData,
+            byte[] tlsSctData, String host, boolean clientAuth,
+            ArrayList<X509Certificate> untrustedChain, ArrayList<TrustAnchor> trustAnchorChain,
+            Set<X509Certificate> used) throws CertificateException {
+        CertificateException lastException = null;
+        X509Certificate current;
+        if (trustAnchorChain.isEmpty()) {
+            current = untrustedChain.get(untrustedChain.size() - 1);
+        } else {
+            current = trustAnchorChain.get(trustAnchorChain.size() - 1).getTrustedCert();
+        }
+
+        // Check that the certificate isn't blacklisted.
+        checkBlacklist(current);
+
+        // 1. If the current certificate in the chain is self-signed verify the chain as is.
+        if (current.getIssuerDN().equals(current.getSubjectDN())) {
+            return verifyChain(untrustedChain, trustAnchorChain, host, clientAuth, ocspData,
+                    tlsSctData);
+        }
+
+        // 2. Try building a chain via any trust anchors that issued the current certificate.
+        // Note that we do not stop at the first trust anchor since it is possible that the trust
+        // anchor is not self-signed and its issuer may be needed for additional validation such as
+        // certificate pinning. In the common case the first trust anchor will be self-signed or
+        // its issuer's certificate will be missing.
+        Set<TrustAnchor> anchors = findAllTrustAnchorsByIssuerAndSignature(current);
+        boolean seenIssuer = false;
+        for (TrustAnchor anchor : sortPotentialAnchors(anchors)) {
+            X509Certificate anchorCert = anchor.getTrustedCert();
+            // Avoid using certificates that have already been used.
+            if (used.contains(anchorCert)) {
+                continue;
+            }
+            seenIssuer = true;
+            used.add(anchorCert);
+            trustAnchorChain.add(anchor);
+            try {
+                return checkTrustedRecursive(certs, ocspData, tlsSctData, host, clientAuth,
+                        untrustedChain, trustAnchorChain, used);
+            } catch (CertificateException ex) {
+                lastException = ex;
+            }
+            // Could not form a valid chain via this certificate, remove it from this chain.
+            trustAnchorChain.remove(trustAnchorChain.size() - 1);
+            used.remove(anchorCert);
+        }
+
+        // 3. If we were unable to find additional trusted issuers, verify the current chain.
+        // This may happen if the root of trust is not self-signed and the issuer is not
+        // present in the trusted set.
+        if (!trustAnchorChain.isEmpty()) {
+            if (!seenIssuer) {
+                return verifyChain(untrustedChain, trustAnchorChain, host, clientAuth, ocspData,
+                        tlsSctData);
+            }
+
+            // Otherwise all chains based on the current trust anchor were rejected, fail.
+            throw lastException;
+        }
+
+        // 4. Use the certificates provided by the peer to grow the chain.
+        // Ignore the first certificate, as that is the leaf certificate.
+        for (int i = 1; i < certs.length; i++) {
+            X509Certificate candidateIssuer = certs[i];
+            // Avoid using certificates that have already been used.
+            if (used.contains(candidateIssuer)) {
+                continue;
+            }
+            if (current.getIssuerDN().equals(candidateIssuer.getSubjectDN())) {
+                // Check the strength and validity of the certificate to prune bad certificates
+                // early.
+                try {
+                    candidateIssuer.checkValidity();
+                    ChainStrengthAnalyzer.checkCert(candidateIssuer);
+                } catch (CertificateException ex) {
+                    lastException = new CertificateException("Unacceptable certificate: "
+                            + candidateIssuer.getSubjectX500Principal(), ex);
+                    continue;
+                }
+                used.add(candidateIssuer);
+                untrustedChain.add(candidateIssuer);
+                try {
+                    return checkTrustedRecursive(certs, ocspData, tlsSctData, host, clientAuth,
+                            untrustedChain, trustAnchorChain, used);
+                } catch (CertificateException ex) {
+                    lastException = ex;
+                }
+                // Could not form a valid chain via this certificate, remove it from this chain.
+                used.remove(candidateIssuer);
+                untrustedChain.remove(untrustedChain.size() - 1);
+            }
+        }
+
+        // 5. Finally try the cached intermediates to handle server that failed to send them.
+        Set<TrustAnchor> intermediateAnchors =
+                intermediateIndex.findAllByIssuerAndSignature(current);
+        for (TrustAnchor intermediate : sortPotentialAnchors(intermediateAnchors)) {
+            X509Certificate intermediateCert = intermediate.getTrustedCert();
+            // Avoid using certificates that have already been used.
+            if (used.contains(intermediateCert)) {
+                continue;
+            }
+            used.add(intermediateCert);
+            untrustedChain.add(intermediateCert);
+            try {
+                return checkTrustedRecursive(certs, ocspData, tlsSctData, host, clientAuth,
+                        untrustedChain, trustAnchorChain, used);
+            } catch (CertificateException ex) {
+                lastException = ex;
+            }
+            // Could not form a valid chain via this certificate, remove it from this chain.
+            untrustedChain.remove(untrustedChain.size() - 1);
+            used.remove(intermediateCert);
+        }
+
+        // 6. We were unable to build a valid chain, throw the last error encountered.
+        if (lastException != null) {
+            throw lastException;
+        }
+
+        // 7. If no errors were encountered above then verifyChain was never called because it was
+        // not possible to build a valid chain to a trusted certificate.
+        CertPath certPath = factory.generateCertPath(untrustedChain);
+        throw new CertificateException(new CertPathValidatorException(
+                "Trust anchor for certification path not found.", null, certPath, -1));
+    }
+
+    private List<X509Certificate> verifyChain(List<X509Certificate> untrustedChain,
+            List<TrustAnchor> trustAnchorChain, String host, boolean clientAuth, byte[] ocspData,
+            byte[] tlsSctData)
+            throws CertificateException {
+        try {
+            // build the cert path from the list of certs sans trust anchors
+            // TODO: check whether this is slow and should be replaced by a minimalistic CertPath impl
+            // since we already have built the path.
+            CertPath certPath = factory.generateCertPath(untrustedChain);
+
+            // Check that there are at least some trust anchors
+            if (trustAnchorChain.isEmpty()) {
+                throw new CertificateException(new CertPathValidatorException(
+                        "Trust anchor for certification path not found.", null, certPath, -1));
+            }
+
+            List<X509Certificate> wholeChain = new ArrayList<X509Certificate>();
+            wholeChain.addAll(untrustedChain);
+            for (TrustAnchor anchor : trustAnchorChain) {
+                wholeChain.add(anchor.getTrustedCert());
+            }
+
+            if (pinManager != null) {
+                pinManager.checkChainPinning(host, wholeChain);
+            }
+            // Check whole chain against the blacklist
+            for (X509Certificate cert : wholeChain) {
+                checkBlacklist(cert);
+            }
+
+            // Check CT (if required).
+            if (!clientAuth &&
+                    (ctEnabledOverride || (host != null && Platform
+                            .isCTVerificationRequired(host)))) {
+                checkCT(host, wholeChain, ocspData, tlsSctData);
+            }
+
+            if (untrustedChain.isEmpty()) {
+                // The chain consists of only trust anchors, skip the validator
+                return wholeChain;
+            }
+
+            ChainStrengthAnalyzer.check(untrustedChain);
+
+            // Validate the untrusted part of the chain
+            try {
+                Set<TrustAnchor> anchorSet = new HashSet<TrustAnchor>();
+                // We know that untrusted chains to the first trust anchor, only add that.
+                anchorSet.add(trustAnchorChain.get(0));
+                PKIXParameters params = new PKIXParameters(anchorSet);
+                params.setRevocationEnabled(false);
+                X509Certificate endPointCert = untrustedChain.get(0);
+                setOcspResponses(params, endPointCert, ocspData);
+                params.addCertPathChecker(
+                        new ExtendedKeyUsagePKIXCertPathChecker(clientAuth, endPointCert));
+                validator.validate(certPath, params);
+            } catch (InvalidAlgorithmParameterException e) {
+                throw new CertificateException("Chain validation failed", e);
+            } catch (CertPathValidatorException e) {
+                throw new CertificateException("Chain validation failed", e);
+            }
+            // Add intermediate CAs to the index to tolerate sites
+            // that assume that the browser will have cached these.
+            // http://b/3404902
+            for (int i = 1; i < untrustedChain.size(); i++) {
+                intermediateIndex.index(untrustedChain.get(i));
+            }
+            return wholeChain;
+        } catch (CertificateException e) {
+            logger.fine("Rejected candidate cert chain due to error: " + e.getMessage());
+            throw e;
+        }
+    }
+
+    private void checkBlacklist(X509Certificate cert) throws CertificateException {
+        if (blacklist != null && blacklist.isPublicKeyBlackListed(cert.getPublicKey())) {
+            throw new CertificateException("Certificate blacklisted by public key: " + cert);
+        }
+    }
+
+    private void checkCT(String host, List<X509Certificate> chain, byte[] ocspData, byte[] tlsData)
+            throws CertificateException {
+        CTVerificationResult result =
+                ctVerifier.verifySignedCertificateTimestamps(chain, tlsData, ocspData);
+
+        if (!ctPolicy.doesResultConformToPolicy(result, host,
+                    chain.toArray(new X509Certificate[chain.size()]))) {
+            throw new CertificateException(
+                    "Certificate chain does not conform to required transparency policy.");
+        }
+    }
+
+    /**
+     * Sets the OCSP response data that was possibly stapled to the TLS response.
+     */
+    private void setOcspResponses(PKIXParameters params, X509Certificate cert, byte[] ocspData) {
+        if (ocspData == null) {
+            return;
+        }
+
+        PKIXRevocationChecker revChecker = null;
+        List<PKIXCertPathChecker> checkers =
+                new ArrayList<PKIXCertPathChecker>(params.getCertPathCheckers());
+        for (PKIXCertPathChecker checker : checkers) {
+            if (checker instanceof PKIXRevocationChecker) {
+                revChecker = (PKIXRevocationChecker) checker;
+                break;
+            }
+        }
+
+        if (revChecker == null) {
+            // Only new CertPathValidatorSpi instances will support the
+            // revocation checker API.
+            try {
+                revChecker = (PKIXRevocationChecker) validator.getRevocationChecker();
+            } catch (UnsupportedOperationException e) {
+                return;
+            }
+
+            checkers.add(revChecker);
+
+            /*
+             * If we add a new revocation checker, we should set the option for
+             * end-entity verification only. Otherwise the CertPathValidator will
+             * throw an exception when it can't verify the entire chain.
+             */
+            revChecker.setOptions(Collections.singleton(Option.ONLY_END_ENTITY));
+        }
+
+        revChecker.setOcspResponses(Collections.singletonMap(cert, ocspData));
+        params.setCertPathCheckers(checkers);
+    }
+
+    /**
+     * Sort potential anchors so that the most preferred for use come first.
+     *
+     * @see CertificatePriorityComparator
+     */
+    private static Collection<TrustAnchor> sortPotentialAnchors(Set<TrustAnchor> anchors) {
+        if (anchors.size() <= 1) {
+            return anchors;
+        }
+        List<TrustAnchor> sortedAnchors = new ArrayList<TrustAnchor>(anchors);
+        Collections.sort(sortedAnchors, TRUST_ANCHOR_COMPARATOR);
+        return sortedAnchors;
+    }
+
+
+    /**
+     * Comparator for sorting {@link TrustAnchor}s using a {@link CertificatePriorityComparator}.
+     */
+    private static class TrustAnchorComparator implements Comparator<TrustAnchor> {
+        private static final CertificatePriorityComparator CERT_COMPARATOR =
+                new CertificatePriorityComparator();
+        @Override
+        public int compare(TrustAnchor lhs, TrustAnchor rhs) {
+            X509Certificate lhsCert = lhs.getTrustedCert();
+            X509Certificate rhsCert = rhs.getTrustedCert();
+            return CERT_COMPARATOR.compare(lhsCert, rhsCert);
+        }
+    }
+
+    /**
+     * If an EKU extension is present in the end-entity certificate,
+     * it MUST contain an appropriate key usage. For servers, this
+     * includes anyExtendedKeyUsage, serverAuth, or the historical
+     * Server Gated Cryptography options of nsSGC or msSGC.  For
+     * clients, this includes anyExtendedKeyUsage and clientAuth.
+     */
+    private static class ExtendedKeyUsagePKIXCertPathChecker extends PKIXCertPathChecker {
+
+        private static final String EKU_OID = "2.5.29.37";
+
+        private static final String EKU_anyExtendedKeyUsage = "2.5.29.37.0";
+        private static final String EKU_clientAuth = "1.3.6.1.5.5.7.3.2";
+        private static final String EKU_serverAuth = "1.3.6.1.5.5.7.3.1";
+        private static final String EKU_nsSGC = "2.16.840.1.113730.4.1";
+        private static final String EKU_msSGC = "1.3.6.1.4.1.311.10.3.3";
+
+        private static final Set<String> SUPPORTED_EXTENSIONS
+                = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(EKU_OID)));
+
+        private final boolean clientAuth;
+        private final X509Certificate leaf;
+
+        private ExtendedKeyUsagePKIXCertPathChecker(boolean clientAuth, X509Certificate leaf) {
+            this.clientAuth = clientAuth;
+            this.leaf = leaf;
+        }
+
+        @Override
+        public void init(boolean forward) throws CertPathValidatorException {
+        }
+
+        @Override
+        public boolean isForwardCheckingSupported() {
+            return true;
+        }
+
+        @Override
+        public Set<String> getSupportedExtensions() {
+            return SUPPORTED_EXTENSIONS;
+        }
+
+        @SuppressWarnings("ReferenceEquality")
+        @Override
+        public void check(Certificate c, Collection<String> unresolvedCritExts)
+                throws CertPathValidatorException {
+            // We only want to validate the EKU on the leaf certificate.
+            if (c != leaf) {
+                return;
+            }
+            List<String> ekuOids;
+            try {
+                ekuOids = leaf.getExtendedKeyUsage();
+            } catch (CertificateParsingException e) {
+                // A malformed EKU is bad news, consider it fatal.
+                throw new CertPathValidatorException(e);
+            }
+            // We are here to check EKU, but there is none.
+            if (ekuOids == null) {
+                return;
+            }
+
+            boolean goodExtendedKeyUsage = false;
+            for (String ekuOid : ekuOids) {
+                // anyExtendedKeyUsage for clients and servers
+                if (ekuOid.equals(EKU_anyExtendedKeyUsage)) {
+                    goodExtendedKeyUsage = true;
+                    break;
+                }
+
+                // clients
+                if (clientAuth) {
+                    if (ekuOid.equals(EKU_clientAuth)) {
+                        goodExtendedKeyUsage = true;
+                        break;
+                    }
+                    continue;
+                }
+
+                // servers
+                if (ekuOid.equals(EKU_serverAuth)) {
+                    goodExtendedKeyUsage = true;
+                    break;
+                }
+                if (ekuOid.equals(EKU_nsSGC)) {
+                    goodExtendedKeyUsage = true;
+                    break;
+                }
+                if (ekuOid.equals(EKU_msSGC)) {
+                    goodExtendedKeyUsage = true;
+                    break;
+                }
+            }
+            if (goodExtendedKeyUsage) {
+                // Mark extendedKeyUsage as resolved if present.
+                unresolvedCritExts.remove(EKU_OID);
+            } else {
+                throw new CertPathValidatorException("End-entity certificate does not have a valid "
+                                                     + "extendedKeyUsage.");
+            }
+        }
+    }
+
+    /**
+     * Find all possible issuing trust anchors of {@code cert}.
+     */
+    private Set<TrustAnchor> findAllTrustAnchorsByIssuerAndSignature(X509Certificate cert) {
+        Set<TrustAnchor> indexedAnchors =
+                trustedCertificateIndex.findAllByIssuerAndSignature(cert);
+        if (!indexedAnchors.isEmpty() || trustedCertificateStore == null) {
+            return indexedAnchors;
+        }
+        Set<X509Certificate> storeAnchors = trustedCertificateStore.findAllIssuers(cert);
+        if (storeAnchors.isEmpty()) {
+            return indexedAnchors;
+        }
+        Set<TrustAnchor> result = new HashSet<TrustAnchor>(storeAnchors.size());
+        for (X509Certificate storeCert : storeAnchors) {
+            result.add(trustedCertificateIndex.index(storeCert));
+        }
+        return result;
+    }
+
+    /**
+     * Check the trustedCertificateIndex for the cert to see if it is
+     * already trusted and failing that check the KeyStore if it is
+     * available.
+     */
+    private TrustAnchor findTrustAnchorBySubjectAndPublicKey(X509Certificate cert) {
+        TrustAnchor trustAnchor = trustedCertificateIndex.findBySubjectAndPublicKey(cert);
+        if (trustAnchor != null) {
+            return trustAnchor;
+        }
+        if (trustedCertificateStore == null) {
+            // not trusted and no TrustedCertificateStore to check
+            return null;
+        }
+        // probe KeyStore for a cert. AndroidCAStore stores its
+        // contents hashed by cert subject on the filesystem to make
+        // this faster than scanning all key store entries.
+        X509Certificate systemCert = trustedCertificateStore.getTrustAnchor(cert);
+        if (systemCert != null) {
+            // Don't index the system certificate here, that way the only place that adds anchors to
+            // the index are findAllTrustAnchorsByIssuerAndSignature.
+            // This allows findAllTrustAnchorsByIssuerAndSignature to avoid checking the
+            // TrustedCertificateStore if the TrustedCertificateIndex contains any issuers for the
+            // certificate because it will have cached all certificates contained in the
+            // TrustedCertificateStore.
+            return new TrustAnchor(systemCert, null);
+        }
+        return null;
+    }
+
+    @Override
+    public X509Certificate[] getAcceptedIssuers() {
+        return (acceptedIssuers != null) ? acceptedIssuers.clone() : acceptedIssuers(rootKeyStore);
+    }
+
+    /**
+     * Set the default hostname verifier that will be used for HTTPS endpoint identification.  If
+     * {@code null} (the default), endpoint identification will use the default hostname verifier
+     * set in {@link HttpsURLConnection#setDefaultHostnameVerifier(javax.net.ssl.HostnameVerifier)}.
+     */
+    synchronized static void setDefaultHostnameVerifier(ConscryptHostnameVerifier verifier) {
+        defaultHostnameVerifier = verifier;
+    }
+
+    /**
+     * Returns the currently-set default hostname verifier.
+     *
+     * @see #setDefaultHostnameVerifier(ConscryptHostnameVerifier)
+     */
+    synchronized static ConscryptHostnameVerifier getDefaultHostnameVerifier() {
+        return defaultHostnameVerifier;
+    }
+
+    /**
+     * Set the hostname verifier that will be used for HTTPS endpoint identification.  If
+     * {@code null} (the default), endpoint identification will use the default hostname verifier
+     * set in {@link #setDefaultHostnameVerifier(ConscryptHostnameVerifier)}.
+     */
+    void setHostnameVerifier(ConscryptHostnameVerifier verifier) {
+        this.hostnameVerifier = verifier;
+    }
+
+    /**
+     * Returns the currently-set hostname verifier for this instance.
+     *
+     * @see #setHostnameVerifier(ConscryptHostnameVerifier)
+     */
+    ConscryptHostnameVerifier getHostnameVerifier() {
+        return hostnameVerifier;
+    }
+
+    private enum GlobalHostnameVerifierAdapter implements ConscryptHostnameVerifier {
+        INSTANCE;
+
+        @Override
+        public boolean verify(String hostname, SSLSession session) {
+            return HttpsURLConnection.getDefaultHostnameVerifier().verify(hostname, session);
+        }
+    }
+
+    private ConscryptHostnameVerifier getHttpsVerifier() {
+        if (hostnameVerifier != null) {
+            return hostnameVerifier;
+        }
+        ConscryptHostnameVerifier defaultVerifier = getDefaultHostnameVerifier();
+        if (defaultVerifier != null) {
+            return defaultVerifier;
+        }
+        return GlobalHostnameVerifierAdapter.INSTANCE;
+    }
+
+    public void setCTEnabledOverride(boolean enabled) {
+        this.ctEnabledOverride = enabled;
+    }
+
+    // Replace the CTVerifier. For testing only.
+    public void setCTVerifier(CTVerifier verifier) {
+        this.ctVerifier = verifier;
+    }
+
+    // Replace the CTPolicy. For testing only.
+    public void setCTPolicy(CTPolicy policy) {
+        this.ctPolicy = policy;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/TrustedCertificateIndex.java b/repackaged/common/src/main/java/com/android/org/conscrypt/TrustedCertificateIndex.java
new file mode 100644
index 0000000..5df6c41
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/TrustedCertificateIndex.java
@@ -0,0 +1,212 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.PublicKey;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * Indexes {@code TrustAnchor} instances so they can be found in O(1)
+ * time instead of O(N).
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.CorePlatformApi
+@Internal
+public final class TrustedCertificateIndex {
+
+    private final Map<X500Principal, List<TrustAnchor>> subjectToTrustAnchors
+            = new HashMap<X500Principal, List<TrustAnchor>>();
+
+    @libcore.api.CorePlatformApi
+    public TrustedCertificateIndex() {}
+
+    public TrustedCertificateIndex(Set<TrustAnchor> anchors) {
+        index(anchors);
+    }
+
+    private void index(Set<TrustAnchor> anchors) {
+        for (TrustAnchor anchor : anchors) {
+            index(anchor);
+        }
+    }
+
+    @libcore.api.CorePlatformApi
+    public TrustAnchor index(X509Certificate cert) {
+        TrustAnchor anchor = new TrustAnchor(cert, null);
+        index(anchor);
+        return anchor;
+    }
+
+    public void index(TrustAnchor anchor) {
+        X500Principal subject;
+        X509Certificate cert = anchor.getTrustedCert();
+        if (cert != null) {
+            subject = cert.getSubjectX500Principal();
+        } else {
+            subject = anchor.getCA();
+        }
+
+        synchronized (subjectToTrustAnchors) {
+            List<TrustAnchor> anchors = subjectToTrustAnchors.get(subject);
+            if (anchors == null) {
+                anchors = new ArrayList<TrustAnchor>(1);
+                subjectToTrustAnchors.put(subject, anchors);
+            } else {
+                // Avoid indexing the same certificate multiple times
+                if (cert != null) {
+                    for (TrustAnchor entry : anchors) {
+                        if (cert.equals(entry.getTrustedCert())) {
+                            return;
+                        }
+                    }
+                }
+            }
+            anchors.add(anchor);
+        }
+    }
+
+    public void reset() {
+        synchronized (subjectToTrustAnchors) {
+            subjectToTrustAnchors.clear();
+        }
+    }
+
+    public void reset(Set<TrustAnchor> anchors) {
+        synchronized (subjectToTrustAnchors) {
+            reset();
+            index(anchors);
+        }
+    }
+
+    @libcore.api.CorePlatformApi
+    public TrustAnchor findByIssuerAndSignature(X509Certificate cert) {
+        X500Principal issuer = cert.getIssuerX500Principal();
+        synchronized (subjectToTrustAnchors) {
+            List<TrustAnchor> anchors = subjectToTrustAnchors.get(issuer);
+            if (anchors == null) {
+                return null;
+            }
+
+            for (TrustAnchor anchor : anchors) {
+                PublicKey publicKey;
+                try {
+                    X509Certificate caCert = anchor.getTrustedCert();
+                    if (caCert != null) {
+                        publicKey = caCert.getPublicKey();
+                    } else {
+                        publicKey = anchor.getCAPublicKey();
+                    }
+                    cert.verify(publicKey);
+                    return anchor;
+                } catch (Exception ignored) {
+                }
+            }
+        }
+        return null;
+    }
+
+    @libcore.api.CorePlatformApi
+    public TrustAnchor findBySubjectAndPublicKey(X509Certificate cert) {
+        X500Principal subject = cert.getSubjectX500Principal();
+        synchronized (subjectToTrustAnchors) {
+            List<TrustAnchor> anchors = subjectToTrustAnchors.get(subject);
+            if (anchors == null) {
+                return null;
+            }
+            return findBySubjectAndPublicKey(cert, anchors);
+        }
+    }
+
+    private static TrustAnchor findBySubjectAndPublicKey(X509Certificate cert,
+                                                         Collection<TrustAnchor> anchors) {
+        PublicKey certPublicKey = cert.getPublicKey();
+        for (TrustAnchor anchor : anchors) {
+            PublicKey caPublicKey;
+            try {
+                X509Certificate caCert = anchor.getTrustedCert();
+                if (caCert != null) {
+                    caPublicKey = caCert.getPublicKey();
+                } else {
+                    caPublicKey = anchor.getCAPublicKey();
+                }
+                if (caPublicKey.equals(certPublicKey)) {
+                    return anchor;
+                } else {
+                    // PublicKey.equals is not required to compare keys across providers. Fall back
+                    // to checking using the encoded form.
+                    if ("X.509".equals(caPublicKey.getFormat())
+                            && "X.509".equals(certPublicKey.getFormat())) {
+                        byte[] caPublicKeyEncoded = caPublicKey.getEncoded();
+                        byte[] certPublicKeyEncoded = certPublicKey.getEncoded();
+                        if (certPublicKeyEncoded != null
+                                && caPublicKeyEncoded != null
+                                && Arrays.equals(caPublicKeyEncoded, certPublicKeyEncoded)) {
+                            return anchor;
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                // can happen with unsupported public key types
+            }
+        }
+        return null;
+    }
+
+    @libcore.api.CorePlatformApi
+    public Set<TrustAnchor> findAllByIssuerAndSignature(X509Certificate cert) {
+        X500Principal issuer = cert.getIssuerX500Principal();
+        synchronized (subjectToTrustAnchors) {
+            List<TrustAnchor> anchors = subjectToTrustAnchors.get(issuer);
+            if (anchors == null) {
+                return Collections.<TrustAnchor>emptySet();
+            }
+
+            Set<TrustAnchor> result = new HashSet<TrustAnchor>();
+            for (TrustAnchor anchor : anchors) {
+                try {
+                    PublicKey publicKey;
+                    X509Certificate caCert = anchor.getTrustedCert();
+                    if (caCert != null) {
+                        publicKey = caCert.getPublicKey();
+                    } else {
+                        publicKey = anchor.getCAPublicKey();
+                    }
+                    if (publicKey == null) {
+                        continue;
+                    }
+                    cert.verify(publicKey);
+                    result.add(anchor);
+                } catch (Exception ignored) {
+                }
+            }
+            return result;
+        }
+    }
+
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/X509PublicKey.java b/repackaged/common/src/main/java/com/android/org/conscrypt/X509PublicKey.java
new file mode 100644
index 0000000..e1ea6e0
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/X509PublicKey.java
@@ -0,0 +1,88 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.PublicKey;
+import java.util.Arrays;
+
+/**
+ * A simple but useless key class that holds X.509 public key information when
+ * the appropriate KeyFactory for the key algorithm is not available.
+ */
+final class X509PublicKey implements PublicKey {
+    private static final long serialVersionUID = -8610156854731664298L;
+
+    private final String algorithm;
+
+    private final byte[] encoded;
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    X509PublicKey(String algorithm, byte[] encoded) {
+        this.algorithm = algorithm;
+        this.encoded = encoded;
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    @Override
+    public String getFormat() {
+        return "X.509";
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        return encoded;
+    }
+
+    @Override
+    public String toString() {
+        return "X509PublicKey [algorithm=" + algorithm + ", encoded=" + Arrays.toString(encoded)
+                + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((algorithm == null) ? 0 : algorithm.hashCode());
+        result = prime * result + Arrays.hashCode(encoded);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        X509PublicKey other = (X509PublicKey) obj;
+        if (algorithm == null) {
+            if (other.algorithm != null)
+                return false;
+        } else if (!algorithm.equals(other.algorithm))
+            return false;
+        if (!Arrays.equals(encoded, other.encoded))
+            return false;
+        return true;
+    }
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTConstants.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTConstants.java
new file mode 100644
index 0000000..f25a414
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTConstants.java
@@ -0,0 +1,48 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import com.android.org.conscrypt.Internal;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public class CTConstants {
+    public static final String X509_SCT_LIST_OID = "1.3.6.1.4.1.11129.2.4.2";
+    public static final String OCSP_SCT_LIST_OID = "1.3.6.1.4.1.11129.2.4.5";
+
+    public static final int VERSION_LENGTH = 1;
+    public static final int LOGID_LENGTH = 32;
+    public static final int TIMESTAMP_LENGTH = 8;
+    public static final int EXTENSIONS_LENGTH_BYTES = 2;
+
+    public static final int HASH_ALGORITHM_LENGTH = 1;
+    public static final int SIGNATURE_ALGORITHM_LENGTH = 1;
+    public static final int SIGNATURE_LENGTH_BYTES = 2;
+
+    public static final int SIGNATURE_TYPE_LENGTH = 1;
+    public static final int LOG_ENTRY_TYPE_LENGTH = 2;
+    public static final int CERTIFICATE_LENGTH_BYTES = 3;
+
+    public static final int SERIALIZED_SCT_LENGTH_BYTES = 2;
+    public static final int SCT_LIST_LENGTH_BYTES = 2;
+
+    public static final int ISSUER_KEY_HASH_LENGTH = 32;
+}
+
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTLogInfo.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTLogInfo.java
new file mode 100644
index 0000000..348f5cc
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTLogInfo.java
@@ -0,0 +1,147 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.util.Arrays;
+import com.android.org.conscrypt.Internal;
+
+/**
+ * Properties about a Certificate Transparency Log.
+ * This object stores information about a CT log, its public key, description and URL.
+ * It allows verification of SCTs against the log's public key.
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public class CTLogInfo {
+    private final byte[] logId;
+    private final PublicKey publicKey;
+    private final String description;
+    private final String url;
+
+    public CTLogInfo(PublicKey publicKey, String description, String url) {
+        try {
+            this.logId = MessageDigest.getInstance("SHA-256")
+                .digest(publicKey.getEncoded());
+        } catch (NoSuchAlgorithmException e) {
+            // SHA-256 is guaranteed to be available
+            throw new RuntimeException(e);
+        }
+
+        this.publicKey = publicKey;
+        this.description = description;
+        this.url = url;
+    }
+
+    /**
+     * Get the log's ID, that is the SHA-256 hash of it's public key
+     */
+    public byte[] getID() {
+        return logId;
+    }
+
+    public PublicKey getPublicKey() {
+        return publicKey;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof CTLogInfo)) {
+            return false;
+        }
+
+        CTLogInfo that = (CTLogInfo)other;
+        return
+            this.publicKey.equals(that.publicKey) &&
+            this.description.equals(that.description) &&
+            this.url.equals(that.url);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 1;
+        hash = hash * 31 + publicKey.hashCode();
+        hash = hash * 31 + description.hashCode();
+        hash = hash * 31 + url.hashCode();
+
+        return hash;
+    }
+
+    /**
+     * Verify the signature of a signed certificate timestamp for the given certificate entry
+     * against the log's public key.
+     *
+     * @return the result of the verification
+     */
+    public VerifiedSCT.Status verifySingleSCT(SignedCertificateTimestamp sct,
+                                              CertificateEntry entry) {
+        if (!Arrays.equals(sct.getLogID(), getID())) {
+            return VerifiedSCT.Status.UNKNOWN_LOG;
+        }
+
+        byte[] toVerify;
+        try {
+            toVerify = sct.encodeTBS(entry);
+        } catch (SerializationException e) {
+            return VerifiedSCT.Status.INVALID_SCT;
+        }
+
+        Signature signature;
+        try {
+            String algorithm = sct.getSignature().getAlgorithm();
+            signature = Signature.getInstance(algorithm);
+        } catch (NoSuchAlgorithmException e) {
+            return VerifiedSCT.Status.INVALID_SCT;
+        }
+
+        try {
+            signature.initVerify(publicKey);
+        } catch (InvalidKeyException e) {
+            return VerifiedSCT.Status.INVALID_SCT;
+        }
+
+        try {
+            signature.update(toVerify);
+            if (!signature.verify(sct.getSignature().getSignature())) {
+                return VerifiedSCT.Status.INVALID_SIGNATURE;
+            }
+            return VerifiedSCT.Status.VALID;
+        } catch (SignatureException e) {
+            // This only happens if the signature is not initialized,
+            // but we call initVerify just before, so it should never do
+            throw new RuntimeException(e);
+        }
+    }
+}
+
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTLogStore.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTLogStore.java
new file mode 100644
index 0000000..0f6b715
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTLogStore.java
@@ -0,0 +1,29 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import com.android.org.conscrypt.Internal;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public interface CTLogStore {
+    CTLogInfo getKnownLog(byte[] logId);
+}
+
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTPolicy.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTPolicy.java
new file mode 100644
index 0000000..9057e7a
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTPolicy.java
@@ -0,0 +1,30 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import java.security.cert.X509Certificate;
+import com.android.org.conscrypt.Internal;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public interface CTPolicy {
+    boolean doesResultConformToPolicy(CTVerificationResult result, String hostname,
+            X509Certificate[] chain);
+}
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTVerificationResult.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTVerificationResult.java
new file mode 100644
index 0000000..bfa1f59
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTVerificationResult.java
@@ -0,0 +1,49 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import com.android.org.conscrypt.Internal;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public class CTVerificationResult {
+    private final ArrayList<VerifiedSCT> validSCTs = new ArrayList<VerifiedSCT>();
+    private final ArrayList<VerifiedSCT> invalidSCTs = new ArrayList<VerifiedSCT>();
+
+    public void add(VerifiedSCT result) {
+        if (result.status == VerifiedSCT.Status.VALID) {
+            validSCTs.add(result);
+        } else {
+            invalidSCTs.add(result);
+        }
+    }
+
+    public List<VerifiedSCT> getValidSCTs() {
+        return Collections.unmodifiableList(validSCTs);
+    }
+
+    public List<VerifiedSCT> getInvalidSCTs() {
+        return Collections.unmodifiableList(invalidSCTs);
+    }
+}
+
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTVerifier.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTVerifier.java
new file mode 100644
index 0000000..4f2d9b8
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CTVerifier.java
@@ -0,0 +1,261 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import com.android.org.conscrypt.Internal;
+import com.android.org.conscrypt.NativeCrypto;
+import com.android.org.conscrypt.OpenSSLX509Certificate;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public class CTVerifier {
+    private final CTLogStore store;
+
+    public CTVerifier(CTLogStore store) {
+        this.store = store;
+    }
+
+    public CTVerificationResult verifySignedCertificateTimestamps(List<X509Certificate> chain,
+            byte[] tlsData, byte[] ocspData) throws CertificateEncodingException {
+        OpenSSLX509Certificate[] certs = new OpenSSLX509Certificate[chain.size()];
+        int i = 0;
+        for(X509Certificate cert : chain) {
+            certs[i++] = OpenSSLX509Certificate.fromCertificate(cert);
+        }
+        return verifySignedCertificateTimestamps(certs, tlsData, ocspData);
+    }
+
+    /**
+     * Verify a certificate chain for transparency.
+     * Signed timestamps are extracted from the leaf certificate, TLS extension, and stapled ocsp
+     * response, and verified against the list of known logs.
+     * @throws IllegalArgumentException if the chain is empty
+     */
+    public CTVerificationResult verifySignedCertificateTimestamps(OpenSSLX509Certificate[] chain,
+            byte[] tlsData, byte[] ocspData) throws CertificateEncodingException {
+        if (chain.length == 0) {
+            throw new IllegalArgumentException("Chain of certificates mustn't be empty.");
+        }
+
+        OpenSSLX509Certificate leaf = chain[0];
+
+        CTVerificationResult result = new CTVerificationResult();
+        List<SignedCertificateTimestamp> tlsScts = getSCTsFromTLSExtension(tlsData);
+        verifyExternalSCTs(tlsScts, leaf, result);
+
+        List<SignedCertificateTimestamp> ocspScts = getSCTsFromOCSPResponse(ocspData, chain);
+        verifyExternalSCTs(ocspScts, leaf, result);
+
+        List<SignedCertificateTimestamp> embeddedScts = getSCTsFromX509Extension(chain[0]);
+        verifyEmbeddedSCTs(embeddedScts, chain, result);
+        return result;
+    }
+
+    /**
+     * Verify a list of SCTs which were embedded from an X509 certificate.
+     * The result of the verification for each sct is added to {@code result}.
+     */
+    private void verifyEmbeddedSCTs(List<SignedCertificateTimestamp> scts,
+                                    OpenSSLX509Certificate[] chain,
+                                    CTVerificationResult result) {
+        // Avoid creating the cert entry if we don't need it
+        if (scts.isEmpty()) {
+            return;
+        }
+
+        CertificateEntry precertEntry = null;
+        if (chain.length >= 2) {
+            OpenSSLX509Certificate leaf = chain[0];
+            OpenSSLX509Certificate issuer = chain[1];
+
+            try {
+                precertEntry = CertificateEntry.createForPrecertificate(leaf, issuer);
+            } catch (CertificateException e) {
+                // Leave precertEntry as null, we handle it just below
+            }
+        }
+
+        if (precertEntry == null) {
+            markSCTsAsInvalid(scts, result);
+            return;
+        }
+
+        for (SignedCertificateTimestamp sct: scts) {
+            VerifiedSCT.Status status = verifySingleSCT(sct, precertEntry);
+            result.add(new VerifiedSCT(sct, status));
+        }
+    }
+
+    /**
+     * Verify a list of SCTs which were not embedded in an X509 certificate, that is received
+     * through the TLS or OCSP extensions.
+     * The result of the verification for each sct is added to {@code result}.
+     */
+    private void verifyExternalSCTs(List<SignedCertificateTimestamp> scts,
+                                    OpenSSLX509Certificate leaf,
+                                    CTVerificationResult result) {
+        // Avoid creating the cert entry if we don't need it
+        if (scts.isEmpty()) {
+            return;
+        }
+
+        CertificateEntry x509Entry;
+        try {
+            x509Entry = CertificateEntry.createForX509Certificate(leaf);
+        } catch (CertificateException e) {
+            markSCTsAsInvalid(scts, result);
+            return;
+        }
+
+        for (SignedCertificateTimestamp sct: scts) {
+            VerifiedSCT.Status status = verifySingleSCT(sct, x509Entry);
+            result.add(new VerifiedSCT(sct, status));
+        }
+    }
+
+    /**
+     * Verify a single SCT for the given Certificate Entry
+     */
+    private VerifiedSCT.Status verifySingleSCT(SignedCertificateTimestamp sct,
+                                                         CertificateEntry certEntry) {
+        CTLogInfo log = store.getKnownLog(sct.getLogID());
+        if (log == null) {
+            return VerifiedSCT.Status.UNKNOWN_LOG;
+        }
+
+        return log.verifySingleSCT(sct, certEntry);
+    }
+
+    /**
+     * Add every SCT in {@code scts} to {@code result} with INVALID_SCT as status
+     */
+    private void markSCTsAsInvalid(List<SignedCertificateTimestamp> scts,
+                                   CTVerificationResult result) {
+        for (SignedCertificateTimestamp sct: scts) {
+            result.add(new VerifiedSCT(sct, VerifiedSCT.Status.INVALID_SCT));
+        }
+    }
+
+    /**
+     * Parse an encoded SignedCertificateTimestampList into a list of SignedCertificateTimestamp
+     * instances, as described by RFC6962.
+     * Individual SCTs which fail to be parsed are skipped. If the data is null, or the encompassing
+     * list fails to be parsed, an empty list is returned.
+     * @param origin used to create the SignedCertificateTimestamp instances.
+     */
+    private List<SignedCertificateTimestamp> getSCTsFromSCTList(byte[] data,
+            SignedCertificateTimestamp.Origin origin) {
+        if (data == null) {
+            return Collections.emptyList();
+        }
+
+        byte[][] sctList;
+        try {
+            sctList = Serialization.readList(data, CTConstants.SCT_LIST_LENGTH_BYTES,
+                                             CTConstants.SERIALIZED_SCT_LENGTH_BYTES);
+        } catch (SerializationException e) {
+            return Collections.emptyList();
+        }
+
+        List<SignedCertificateTimestamp> scts = new ArrayList<SignedCertificateTimestamp>();
+        for (byte[] encodedSCT: sctList) {
+            try  {
+                SignedCertificateTimestamp sct = SignedCertificateTimestamp.decode(encodedSCT, origin);
+                scts.add(sct);
+            } catch (SerializationException e) {
+                // Ignore errors
+            }
+        }
+
+        return scts;
+    }
+
+    /**
+     * Extract a list of SignedCertificateTimestamp from a TLS "signed_certificate_timestamp"
+     * extension as described by RFC6962.
+     * Individual SCTs which fail to be parsed are skipped. If the data is null, or the encompassing
+     * list fails to be parsed, an empty list is returned.
+     * @param data contents of the TLS extension to be decoded
+     */
+    private List<SignedCertificateTimestamp> getSCTsFromTLSExtension(byte[] data) {
+        return getSCTsFromSCTList(data, SignedCertificateTimestamp.Origin.TLS_EXTENSION);
+    }
+
+    /**
+     * Extract a list of SignedCertificateTimestamp contained in an OCSP response.
+     * If the data is null, or parsing the OCSP response fails, an empty list is returned.
+     * Individual SCTs which fail to be parsed are skipped.
+     * @param data contents of the OCSP response
+     * @param chain certificate chain for which to get SCTs. Must contain at least the leaf and it's
+     *              issuer in order to identify the relevant SingleResponse from the OCSP response,
+     *              or an empty list is returned
+     */
+    private List<SignedCertificateTimestamp> getSCTsFromOCSPResponse(byte[] data,
+            OpenSSLX509Certificate[] chain) {
+        if (data == null || chain.length < 2) {
+            return Collections.emptyList();
+        }
+
+        byte[] extData = NativeCrypto.get_ocsp_single_extension(data, CTConstants.OCSP_SCT_LIST_OID,
+                chain[0].getContext(), chain[0],
+                chain[1].getContext(), chain[1]);
+        if (extData == null) {
+            return Collections.emptyList();
+        }
+
+        try {
+            return getSCTsFromSCTList(
+                    Serialization.readDEROctetString(
+                      Serialization.readDEROctetString(extData)),
+                    SignedCertificateTimestamp.Origin.OCSP_RESPONSE);
+        } catch (SerializationException e) {
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * Extract a list of SignedCertificateTimestamp embedded in an X509 certificate.
+     *
+     * If the certificate does not contain any SCT extension, or the encompassing encoded list fails
+     * to be parsed, an empty list is returned. Individual SCTs which fail to be parsed are ignored.
+     */
+    private List<SignedCertificateTimestamp> getSCTsFromX509Extension(OpenSSLX509Certificate leaf) {
+        byte[] extData = leaf.getExtensionValue(CTConstants.X509_SCT_LIST_OID);
+        if (extData == null) {
+            return Collections.emptyList();
+        }
+
+        try {
+            return getSCTsFromSCTList(
+                    Serialization.readDEROctetString(
+                      Serialization.readDEROctetString(extData)),
+                    SignedCertificateTimestamp.Origin.EMBEDDED);
+        } catch (SerializationException e) {
+            return Collections.emptyList();
+        }
+    }
+}
+
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CertificateEntry.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CertificateEntry.java
new file mode 100644
index 0000000..69c9367
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/CertificateEntry.java
@@ -0,0 +1,138 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import com.android.org.conscrypt.Internal;
+import com.android.org.conscrypt.OpenSSLX509Certificate;
+
+/**
+ * CertificateEntry structure.
+ * This structure describes part of the data which is signed over in SCTs.
+ * It is not defined by the RFC6962, but it is useful to have.
+ *
+ * It's definition would be :
+ * struct {
+ *     LogEntryType entry_type;
+ *     select(entry_type) {
+ *         case x509_entry: ASN.1Cert;
+ *         case precert_entry: PreCert;
+ *     } signed_entry;
+ * } CertificateEntry;
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public class CertificateEntry {
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum LogEntryType {
+        X509_ENTRY,
+        PRECERT_ENTRY
+    }
+
+    private final LogEntryType entryType;
+
+    // Only used when entryType is LOG_ENTRY_TYPE_PRECERT
+    private final byte[] issuerKeyHash;
+
+    /* If entryType == PRECERT_ENTRY, this is the encoded TBS of the precertificate.
+       If entryType == X509_ENTRY, this is the encoded leaf certificate. */
+    private final byte[] certificate;
+
+    private CertificateEntry(LogEntryType entryType, byte[] certificate, byte[] issuerKeyHash) {
+        if (entryType == LogEntryType.PRECERT_ENTRY && issuerKeyHash == null) {
+            throw new IllegalArgumentException("issuerKeyHash missing for precert entry.");
+        } else if (entryType == LogEntryType.X509_ENTRY && issuerKeyHash != null) {
+            throw new IllegalArgumentException("unexpected issuerKeyHash for X509 entry.");
+        }
+        
+        if (issuerKeyHash != null && issuerKeyHash.length != CTConstants.ISSUER_KEY_HASH_LENGTH) {
+            throw new IllegalArgumentException("issuerKeyHash must be 32 bytes long");
+        }
+
+        this.entryType = entryType;
+        this.issuerKeyHash = issuerKeyHash;
+        this.certificate = certificate;
+    }
+
+    /**
+     * @throws IllegalArgumentException if issuerKeyHash isn't 32 bytes
+     */
+    public static CertificateEntry createForPrecertificate(byte[] tbsCertificate, byte[] issuerKeyHash) {
+        return new CertificateEntry(LogEntryType.PRECERT_ENTRY, tbsCertificate, issuerKeyHash);
+    }
+
+    public static CertificateEntry createForPrecertificate(OpenSSLX509Certificate leaf,
+            OpenSSLX509Certificate issuer) throws CertificateException {
+        try {
+            if (!leaf.getNonCriticalExtensionOIDs().contains(CTConstants.X509_SCT_LIST_OID)) {
+                throw new CertificateException("Certificate does not contain embedded signed timestamps");
+            }
+
+            OpenSSLX509Certificate preCert = leaf.withDeletedExtension(CTConstants.X509_SCT_LIST_OID);
+            byte[] tbs = preCert.getTBSCertificate();
+
+            byte[] issuerKey = issuer.getPublicKey().getEncoded();
+            MessageDigest md = MessageDigest.getInstance("SHA-256");
+            md.update(issuerKey);
+            byte[] issuerKeyHash = md.digest();
+
+            return createForPrecertificate(tbs, issuerKeyHash);
+        } catch (NoSuchAlgorithmException e) {
+            // SHA-256 is guaranteed to be available
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static CertificateEntry createForX509Certificate(byte[] x509Certificate) {
+        return new CertificateEntry(LogEntryType.X509_ENTRY, x509Certificate, null);
+    }
+
+    public static CertificateEntry createForX509Certificate(X509Certificate cert)
+            throws CertificateEncodingException {
+        return createForX509Certificate(cert.getEncoded());
+    }
+
+    public LogEntryType getEntryType() {
+        return entryType;
+    }
+    public byte[] getCertificate() {
+        return certificate;
+    }
+    public byte[] getIssuerKeyHash() {
+        return issuerKeyHash;
+    }
+
+    /**
+     * TLS encode the CertificateEntry structure.
+     */
+    public void encode(OutputStream output) throws SerializationException {
+        Serialization.writeNumber(output, entryType.ordinal(), CTConstants.LOG_ENTRY_TYPE_LENGTH);
+        if (entryType == LogEntryType.PRECERT_ENTRY) {
+            Serialization.writeFixedBytes(output, issuerKeyHash);
+        }
+        Serialization.writeVariableBytes(output, certificate, CTConstants.CERTIFICATE_LENGTH_BYTES);
+    }
+}
+
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/DigitallySigned.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/DigitallySigned.java
new file mode 100644
index 0000000..ea62666
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/DigitallySigned.java
@@ -0,0 +1,136 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import com.android.org.conscrypt.Internal;
+
+/**
+ * DigitallySigned structure, as defined by RFC5246 Section 4.7.
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public class DigitallySigned {
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum HashAlgorithm {
+        NONE,
+        MD5,
+        SHA1,
+        SHA224,
+        SHA256,
+        SHA384,
+        SHA512;
+
+        private static HashAlgorithm[] values = values();
+        public static HashAlgorithm valueOf(int ord) {
+            try {
+                return values[ord];
+            } catch (IndexOutOfBoundsException e) {
+                throw new IllegalArgumentException("Invalid hash algorithm " + ord, e);
+            }
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum SignatureAlgorithm {
+        ANONYMOUS,
+        RSA,
+        DSA,
+        ECDSA;
+
+        private static SignatureAlgorithm[] values = values();
+        public static SignatureAlgorithm valueOf(int ord) {
+            try {
+                return values[ord];
+            } catch (IndexOutOfBoundsException e) {
+                throw new IllegalArgumentException("Invalid signature algorithm " + ord, e);
+            }
+        }
+    }
+
+    private final HashAlgorithm hashAlgorithm;
+    private final SignatureAlgorithm signatureAlgorithm;
+    private final byte[] signature;
+
+    public DigitallySigned(HashAlgorithm hashAlgorithm,
+                           SignatureAlgorithm signatureAlgorithm,
+                           byte[] signature) {
+        this.hashAlgorithm = hashAlgorithm;
+        this.signatureAlgorithm = signatureAlgorithm;
+        this.signature = signature;
+    }
+
+    public DigitallySigned(int hashAlgorithm,
+                           int signatureAlgorithm,
+                           byte[] signature) {
+        this(
+            HashAlgorithm.valueOf(hashAlgorithm),
+            SignatureAlgorithm.valueOf(signatureAlgorithm),
+            signature
+        );
+    }
+
+    public HashAlgorithm getHashAlgorithm() {
+        return hashAlgorithm;
+    }
+    public SignatureAlgorithm getSignatureAlgorithm() {
+        return signatureAlgorithm;
+    }
+    public byte[] getSignature() {
+        return signature;
+    }
+
+    /**
+     * Get the name of the hash and signature combination.
+     * The result can be used to as the argument to {@link java.security.Signature#getInstance}.
+     */
+    public String getAlgorithm() {
+        return String.format("%swith%s", hashAlgorithm, signatureAlgorithm);
+    }
+
+    /**
+     * Decode a TLS encoded DigitallySigned structure.
+     */
+    public static DigitallySigned decode(InputStream input)
+        throws SerializationException {
+        try {
+            return new DigitallySigned(
+                Serialization.readNumber(input, CTConstants.HASH_ALGORITHM_LENGTH),
+                Serialization.readNumber(input, CTConstants.SIGNATURE_ALGORITHM_LENGTH),
+                Serialization.readVariableBytes(input, CTConstants.SIGNATURE_LENGTH_BYTES)
+            );
+        } catch (IllegalArgumentException e) {
+            throw new SerializationException(e);
+        }
+    }
+
+    /**
+     * Decode a TLS encoded DigitallySigned structure.
+     */
+    public static DigitallySigned decode(byte[] input)
+            throws SerializationException {
+        return decode(new ByteArrayInputStream(input));
+    }
+}
+
+
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/Serialization.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/Serialization.java
new file mode 100644
index 0000000..4b54dbf
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/Serialization.java
@@ -0,0 +1,243 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import com.android.org.conscrypt.Internal;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public class Serialization {
+    private Serialization() {}
+
+    private static final int DER_TAG_MASK = 0x3f;
+    private static final int DER_TAG_OCTET_STRING = 0x4;
+    private static final int DER_LENGTH_LONG_FORM_FLAG = 0x80;
+
+    public static byte[] readDEROctetString(byte[] input)
+            throws SerializationException {
+        return readDEROctetString(new ByteArrayInputStream(input));
+    }
+
+    public static byte[] readDEROctetString(InputStream input)
+            throws SerializationException {
+        int tag = readByte(input) & DER_TAG_MASK;
+        if (tag != DER_TAG_OCTET_STRING) {
+            throw new SerializationException("Wrong DER tag, expected OCTET STRING, got " + tag);
+        }
+        int length;
+        int width = readNumber(input, 1);
+        if ((width & DER_LENGTH_LONG_FORM_FLAG) != 0) {
+            length = readNumber(input, width & ~DER_LENGTH_LONG_FORM_FLAG);
+        } else {
+            length = width;
+        }
+
+        return readFixedBytes(input, length);
+    }
+
+    public static byte[][] readList(byte[] input, int listWidth, int elemWidth)
+            throws SerializationException {
+        return readList(new ByteArrayInputStream(input), listWidth, elemWidth);
+    }
+
+    /**
+     * Read a variable length vector of variable sized elements as described by RFC5246 section 4.3.
+     * The vector is prefixed by its total length, in bytes and in big endian format,
+     * so is each element contained in the vector.
+     * @param listWidth the width of the vector's length field, in bytes.
+     * @param elemWidth the width of each element's length field, in bytes.
+     * @throws SerializationException if EOF is encountered.
+     */
+    public static byte[][] readList(InputStream input, int listWidth, int elemWidth)
+            throws SerializationException {
+        ArrayList<byte[]> result = new ArrayList<byte[]>();
+        byte[] data = readVariableBytes(input, listWidth);
+        input = new ByteArrayInputStream(data);
+        try {
+            while (input.available() > 0) {
+                result.add(readVariableBytes(input, elemWidth));
+            }
+        } catch (IOException e) {
+            throw new SerializationException(e);
+        }
+        return result.toArray(new byte[result.size()][]);
+    }
+
+    /**
+     * Read a length-prefixed sequence of bytes.
+     * The length must be encoded in big endian format.
+     * @param width the width of the length prefix, in bytes.
+     * @throws SerializationException if EOF is encountered, or if {@code width} is negative or
+     * greater than 4
+     */
+    public static byte[] readVariableBytes(InputStream input, int width)
+            throws SerializationException {
+        int length = readNumber(input, width);
+        return readFixedBytes(input, length);
+    }
+
+    /**
+     * Read a fixed number of bytes from the input stream.
+     * @param length the number of bytes to read.
+     * @throws SerializationException if EOF is encountered.
+     */
+    public static byte[] readFixedBytes(InputStream input, int length)
+            throws SerializationException {
+        try {
+            if (length < 0) {
+                throw new SerializationException("Negative length: " + length);
+            }
+
+            byte[] data = new byte[length];
+            int count = input.read(data);
+            if (count < length) {
+                throw new SerializationException("Premature end of input, expected " + length +
+                                                 " bytes, only read " + count);
+            }
+            return data;
+        } catch (IOException e) {
+            throw new SerializationException(e);
+        }
+    }
+
+    /**
+     * Read a number in big endian format from the input stream.
+     * This methods only supports a width of up to 4 bytes.
+     * @param width the width of the number, in bytes.
+     * @throws SerializationException if EOF is encountered, or if {@code width} is negative or
+     * greater than 4
+     */
+    public static int readNumber(InputStream input, int width) throws SerializationException {
+        if (width > 4 || width < 0) {
+            throw new SerializationException("Invalid width: " + width);
+        }
+
+        int result = 0;
+        for (int i = 0; i < width; i++) {
+            result = (result << 8) | (readByte(input) & 0xFF);
+        }
+
+        return result;
+    }
+
+    /**
+     * Read a number in big endian format from the input stream.
+     * This methods supports a width of up to 8 bytes.
+     * @param width the width of the number, in bytes.
+     * @throws SerializationException if EOF is encountered.
+     * @throws IllegalArgumentException if {@code width} is negative or greater than 8
+     */
+    public static long readLong(InputStream input, int width) throws SerializationException {
+        if (width > 8 || width < 0) {
+            throw new IllegalArgumentException("Invalid width: " + width);
+        }
+
+        long result = 0;
+        for (int i = 0; i < width; i++) {
+            result = (result << 8) | (readByte(input) & 0xFF);
+        }
+
+        return result;
+    }
+
+    /**
+     * Read a single byte from the input stream.
+     * @throws SerializationException if EOF is encountered.
+     */
+    public static byte readByte(InputStream input) throws SerializationException {
+        try {
+            int b = input.read();
+            if (b == -1) {
+                throw new SerializationException("Premature end of input, could not read byte.");
+            }
+            return (byte)b;
+        } catch (IOException e) {
+            throw new SerializationException(e);
+        }
+    }
+
+    /**
+     * Write length prefixed sequence of bytes to the ouput stream.
+     * The length prefix is encoded in big endian order.
+     * @param data the data to be written.
+     * @param width the width of the length prefix, in bytes.
+     * @throws SerializationException if the length of {@code data} is too large to fit in
+     * {@code width} bytes or {@code width} is negative.
+     */
+    public static void writeVariableBytes(OutputStream output, byte[] data, int width)
+            throws SerializationException {
+        writeNumber(output, data.length, width);
+        writeFixedBytes(output, data);
+    }
+
+    /**
+     * Write a fixed number sequence of bytes to the ouput stream.
+     * @param data the data to be written.
+     */
+    public static void writeFixedBytes(OutputStream output, byte[] data)
+            throws SerializationException {
+        try {
+            output.write(data);
+        } catch (IOException e) {
+            throw new SerializationException(e);
+        }
+    }
+
+    /**
+     * Write a number to the output stream.
+     * The number is encoded in big endian order.
+     * @param value the value to be written.
+     * @param width the width of the encoded number, in bytes
+     * @throws SerializationException if the number is too large to fit in {@code width} bytes or
+     * {@code width} is negative.
+     */
+    public static void writeNumber(OutputStream output, long value, int width)
+            throws SerializationException {
+        if (width < 0) {
+            throw new SerializationException("Negative width: " + width);
+        }
+        if (width < 8 && value >= (1L << (8 * width))) {
+            throw new SerializationException(
+                    "Number too large, " + value + " does not fit in " + width + " bytes");
+        }
+
+        try {
+            while (width > 0) {
+                long shift = (width - 1) * 8L;
+                // Java behaves weirdly if shifting by more than the variable's size
+                if (shift < Long.SIZE) {
+                    output.write((byte) ((value >> shift) & 0xFF));
+                } else {
+                    output.write(0);
+                }
+
+                width--;
+            }
+        } catch (IOException e) {
+            throw new SerializationException(e);
+        }
+    }
+}
+
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/SerializationException.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/SerializationException.java
new file mode 100644
index 0000000..092f166
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/SerializationException.java
@@ -0,0 +1,42 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import com.android.org.conscrypt.Internal;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public class SerializationException extends Exception {
+    public SerializationException() {
+    }
+
+    public SerializationException(String message) {
+        super(message);
+    }
+
+    public SerializationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public SerializationException(Throwable cause) {
+        super(cause);
+    }
+}
+
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/SignedCertificateTimestamp.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/SignedCertificateTimestamp.java
new file mode 100644
index 0000000..218603b
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/SignedCertificateTimestamp.java
@@ -0,0 +1,147 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import com.android.org.conscrypt.Internal;
+
+/**
+ * SignedCertificateTimestamp structure, as defined by RFC6962 Section 3.2.
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public class SignedCertificateTimestamp {
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum Version {
+        V1
+    };
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum SignatureType {
+        CERTIFICATE_TIMESTAMP,
+        TREE_HASH
+    };
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum Origin {
+        EMBEDDED,
+        TLS_EXTENSION,
+        OCSP_RESPONSE
+    };
+
+    private final Version version;
+    private final byte[] logId;
+    private final long timestamp;
+    private final byte[] extensions;
+    private final DigitallySigned signature;
+
+    // origin is implied from the SCT's source and is not encoded in it,
+    // and affects the verification process.
+    private final Origin origin;
+
+    public SignedCertificateTimestamp(Version version, byte[] logId,
+                                      long timestamp, byte[] extensions,
+                                      DigitallySigned signature, Origin origin) {
+        this.version = version;
+        this.logId = logId;
+        this.timestamp = timestamp;
+        this.extensions = extensions;
+        this.signature = signature;
+        this.origin = origin;
+    }
+
+    public Version getVersion() {
+        return version;
+    }
+    public byte[] getLogID() {
+        return logId;
+    }
+    public long getTimestamp() {
+        return timestamp;
+    }
+    public byte[] getExtensions() {
+        return extensions;
+    }
+    public DigitallySigned getSignature() {
+        return signature;
+    }
+    public Origin getOrigin() {
+        return origin;
+    }
+
+    /**
+     * Decode a TLS encoded SignedCertificateTimestamp structure.
+     */
+    public static SignedCertificateTimestamp decode(InputStream input, Origin origin)
+            throws SerializationException {
+        int version = Serialization.readNumber(input, CTConstants.VERSION_LENGTH);
+        if (version != Version.V1.ordinal()) {
+            throw new SerializationException("Unsupported SCT version " + version);
+        }
+
+        return new SignedCertificateTimestamp(
+            Version.V1,
+            Serialization.readFixedBytes(input, CTConstants.LOGID_LENGTH),
+            Serialization.readLong(input, CTConstants.TIMESTAMP_LENGTH),
+            Serialization.readVariableBytes(input, CTConstants.EXTENSIONS_LENGTH_BYTES),
+            DigitallySigned.decode(input),
+            origin
+        );
+    }
+
+    /**
+     * Decode a TLS encoded SignedCertificateTimestamp structure.
+     */
+    public static SignedCertificateTimestamp decode(byte[] input, Origin origin)
+            throws SerializationException {
+        return decode(new ByteArrayInputStream(input), origin);
+    }
+
+    /**
+     * TLS encode the signed part of the SCT, as described by RFC6962 section 3.2.
+     */
+    public void encodeTBS(OutputStream output, CertificateEntry certEntry)
+            throws SerializationException {
+        Serialization.writeNumber(output, version.ordinal(), CTConstants.VERSION_LENGTH);
+        Serialization.writeNumber(output, SignatureType.CERTIFICATE_TIMESTAMP.ordinal(),
+                                          CTConstants.SIGNATURE_TYPE_LENGTH);
+        Serialization.writeNumber(output, timestamp, CTConstants.TIMESTAMP_LENGTH);
+        certEntry.encode(output);
+        Serialization.writeVariableBytes(output, extensions, CTConstants.EXTENSIONS_LENGTH_BYTES);
+    }
+
+    /**
+     * TLS encode the signed part of the SCT, as described by RFC6962 section 3.2.
+     */
+    public byte[] encodeTBS(CertificateEntry certEntry)
+            throws SerializationException {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        encodeTBS(output, certEntry);
+        return output.toByteArray();
+    }
+}
+
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/ct/VerifiedSCT.java b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/VerifiedSCT.java
new file mode 100644
index 0000000..e68a98c
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/ct/VerifiedSCT.java
@@ -0,0 +1,46 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import com.android.org.conscrypt.Internal;
+
+/**
+ * Verification result for a single SCT.
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public final class VerifiedSCT {
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum Status {
+        VALID,
+        INVALID_SIGNATURE,
+        UNKNOWN_LOG,
+        INVALID_SCT
+    }
+
+    public final SignedCertificateTimestamp sct;
+    public final Status status;
+
+    public VerifiedSCT(SignedCertificateTimestamp sct, Status status) {
+        this.sct = sct;
+        this.status = status;
+    }
+}
+
diff --git a/repackaged/common/src/main/java/com/android/org/conscrypt/io/IoUtils.java b/repackaged/common/src/main/java/com/android/org/conscrypt/io/IoUtils.java
new file mode 100644
index 0000000..84c3926
--- /dev/null
+++ b/repackaged/common/src/main/java/com/android/org/conscrypt/io/IoUtils.java
@@ -0,0 +1,66 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.io;
+
+import java.io.Closeable;
+import java.io.InterruptedIOException;
+import java.net.Socket;
+import com.android.org.conscrypt.Internal;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public final class IoUtils {
+    private IoUtils() {}
+
+    /**
+     * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null.
+     */
+    public static void closeQuietly(Closeable closeable) {
+        if (closeable != null) {
+            try {
+                closeable.close();
+            } catch (RuntimeException rethrown) {
+                throw rethrown;
+            } catch (Exception ignored) {
+            }
+        }
+    }
+
+    /**
+     * Closes 'socket', ignoring any exceptions. Does nothing if 'socket' is null.
+     */
+    public static void closeQuietly(Socket socket) {
+        if (socket != null) {
+            try {
+                socket.close();
+            } catch (Exception ignored) {
+            }
+        }
+    }
+
+    public static void throwInterruptedIoException() throws InterruptedIOException {
+        // This is typically thrown in response to an
+        // InterruptedException which does not leave the thread in an
+        // interrupted state, so explicitly interrupt here.
+        Thread.currentThread().interrupt();
+        // TODO: set InterruptedIOException.bytesTransferred
+        throw new InterruptedIOException();
+    }
+}
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/CertPinManagerTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/CertPinManagerTest.java
new file mode 100644
index 0000000..3317765
--- /dev/null
+++ b/repackaged/common/src/test/java/com/android/org/conscrypt/CertPinManagerTest.java
@@ -0,0 +1,143 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.security.KeyStore;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.List;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class CertPinManagerTest {
+    private static boolean installedProvider;
+    private List<X509Certificate> expectedFullChain;
+    private X509Certificate[] chain;
+
+    @BeforeClass
+    public static void installConscrypt() {
+        installedProvider = TestUtils.installConscryptIfNotPresent();
+    }
+
+    @AfterClass
+    public static void removeConscrypt() {
+        if (installedProvider) {
+            Security.removeProvider(TestUtils.getConscryptProvider().getName());
+            installedProvider = false;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
+        X509Certificate[] certs = (X509Certificate[]) pke.getCertificateChain();
+        expectedFullChain = Arrays.asList(certs);
+        // Leave the root out of the chain
+        chain = new X509Certificate[2];
+        chain[0] = certs[0];
+        chain[1] = certs[1];
+    }
+
+    @Test
+    public void testCertPinManagerCalled() throws Exception {
+        class TestCertPinManager implements CertPinManager {
+            public boolean called = false;
+            @Override
+            public void checkChainPinning(String hostname, List<X509Certificate> chain) {
+                called = true;
+            }
+        }
+        TestCertPinManager manager = new TestCertPinManager();
+        callCheckServerTrusted(null, manager);
+        assertTrue(manager.called);
+    }
+
+    @Test
+    public void testNullPinManager() throws Exception {
+        callCheckServerTrusted(null, null);
+    }
+
+    @Test
+    public void testFailure() throws Exception {
+        CertPinManager manager = new CertPinManager() {
+            @Override
+            public void checkChainPinning(String hostname, List<X509Certificate> chain)
+                    throws CertificateException {
+                throw new CertificateException("pin failure");
+            }
+        };
+        try {
+            callCheckServerTrusted(null, manager);
+            fail("Invalid chain was trusted");
+        } catch (CertificateException expected) {
+            assertEquals("pin failure", expected.getMessage());
+        }
+    }
+
+    @Test
+    public void testHostnameProvided() throws Exception {
+        final String expectedHostname = "example.com";
+        class TestCertPinManager implements CertPinManager {
+            public boolean hostnameMatched = false;
+            @Override
+            public void checkChainPinning(String hostname, List<X509Certificate> chain) {
+                hostnameMatched = expectedHostname.equals(hostname);
+            }
+        }
+        TestCertPinManager manager = new TestCertPinManager();
+        callCheckServerTrusted(expectedHostname, manager);
+        assertTrue(manager.hostnameMatched);
+    }
+
+    @Test
+    public void testFullChainProvided() throws Exception {
+        class TestCertPinManager implements CertPinManager {
+            public boolean fullChainProvided = false;
+            @Override
+            public void checkChainPinning(String hostname, List<X509Certificate> chain)
+                    throws CertificateException {
+                fullChainProvided = expectedFullChain.equals(chain);
+            }
+        }
+        TestCertPinManager manager = new TestCertPinManager();
+        callCheckServerTrusted(null, manager);
+        assertTrue(manager.fullChainProvided);
+    }
+
+    private void callCheckServerTrusted(String hostname, CertPinManager manager)
+            throws CertificateException {
+        TestUtils.assumeExtendedTrustManagerAvailable();
+        TrustManagerImpl tm = new TrustManagerImpl(TestKeyStore.getClient().keyStore, manager);
+        tm.checkServerTrusted(chain, "RSA", hostname);
+    }
+}
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/ChainStrengthAnalyzerTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/ChainStrengthAnalyzerTest.java
new file mode 100644
index 0000000..fe968b9
--- /dev/null
+++ b/repackaged/common/src/test/java/com/android/org/conscrypt/ChainStrengthAnalyzerTest.java
@@ -0,0 +1,371 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class ChainStrengthAnalyzerTest {
+
+    //openssl req -x509 -nodes -days 365 -subj '/C=US/ST=Testsota/L=Testville/CN=test.com' \
+    //-newkey rsa:2048 -sha256 -keyout k.pem -out good.pem
+    private static final String GOOD_RSA_PEM = "" +
+                            "-----BEGIN CERTIFICATE-----\n" +
+                            "MIIDYTCCAkmgAwIBAgIJAPFX8KGuEZcgMA0GCSqGSIb3DQEBCwUAMEcxCzAJBgNV\n" +
+                            "BAYTAlVTMREwDwYDVQQIDAhUZXN0c290YTESMBAGA1UEBwwJVGVzdHZpbGxlMREw\n" +
+                            "DwYDVQQDDAh0ZXN0LmNvbTAeFw0xMjEwMTUyMTQ0MTBaFw0xMzEwMTUyMTQ0MTBa\n" +
+                            "MEcxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhUZXN0c290YTESMBAGA1UEBwwJVGVz\n" +
+                            "dHZpbGxlMREwDwYDVQQDDAh0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP\n" +
+                            "ADCCAQoCggEBAM44hz3eTINuAIS9OYmg6DkUIj3MItn5dgbcMEdbXrhNpeWY93ho\n" +
+                            "WQFfsqcSSx28NzqKJmnX+cyinzIUfVde/qciP9P7fxRDokRsf34DJ6gXQplz6P2t\n" +
+                            "s4CWjYM+WXJrvEUgLUQ3CBV0CCrtYvG1B9wYsBdAdWkVaMxTvEt7aVxcvJYzp+KU\n" +
+                            "ME7HDg0PVxptvUExIskcqKVmW7i748AgBLhd0r1nFWLuH20d42Aowja0Wi19fWl2\n" +
+                            "SEMErDRjG8jIPUdSoOLPVLGTktEpex51xnAaZ+I7hy6zs55dq8ua/hE/v2cXIkiQ\n" +
+                            "ZXpWyvI/MaKEfeydLnNpa7J3GpH3KW93HQcCAwEAAaNQME4wHQYDVR0OBBYEFA0M\n" +
+                            "RI+3hIPCSpVVArisr3Y3/sheMB8GA1UdIwQYMBaAFA0MRI+3hIPCSpVVArisr3Y3\n" +
+                            "/sheMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFgUNyuy2qaJvgDO\n" +
+                            "plYudTrJR38O3id1B5oKOzgTEgRrfmHHfyloY4fL5gjAGNp7vdlDKSHC2Ebo23/X\n" +
+                            "Wg535MJ2296R855jaTMdkSE0+4ASpdmon1D007H0FhLyojlKVta3pqMAF1zsp0YF\n" +
+                            "Mf3V/rVMDxCOnbSnqAX0+1nW8Qm4Jgrr3AAMafZk6ypq0xuNQn+sUWuIWw3Xv5Jl\n" +
+                            "KehjnuKtMgVYkn2ItRNnUdhm2dQK+Phdb5Yg8WHXN/r9sZQdORg8FQS9TfQJmimB\n" +
+                            "CVYuqA9Dt0JJZPuO/Pd1yAxWP4NpxX1xr3lNQ5jrTO702QA3gOrscluULLzrYR50\n" +
+                            "FoAjeos=\n" +
+                            "-----END CERTIFICATE-----";
+
+    //openssl ecparam -genkey -name prime256v1 -out eckey.pem && \
+    //openssl req -x509 -nodes -days 365 -subj '/C=US/ST=Testsota/L=Testville/CN=test.com' \
+    //-newkey ec:eckey.pem -sha256 -keyout k.pem -out good.pem
+    private static final String GOOD_ECDSA_PEM = "" +
+                            "-----BEGIN CERTIFICATE-----\n" +
+                            "MIIB1jCCAXugAwIBAgIJALhpH2C1lYeaMAoGCCqGSM49BAMCMEcxCzAJBgNVBAYT\n" +
+                            "AlVTMREwDwYDVQQIDAhUZXN0c290YTESMBAGA1UEBwwJVGVzdHZpbGxlMREwDwYD\n" +
+                            "VQQDDAh0ZXN0LmNvbTAeFw0xNDEwMjAyMjUyNDZaFw0xNTEwMjAyMjUyNDZaMEcx\n" +
+                            "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhUZXN0c290YTESMBAGA1UEBwwJVGVzdHZp\n" +
+                            "bGxlMREwDwYDVQQDDAh0ZXN0LmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA\n" +
+                            "BNR++2RWKGFUm+1KTLz7qxrJclPhVNM6gqInvAz2bLo7ENsD5KqN9BbmNvT4eg3y\n" +
+                            "u5i+00kiroKcm/35zhNFYamjUDBOMB0GA1UdDgQWBBRJmq9/dKkDW8n8mPzGzuo5\n" +
+                            "LcYUKjAfBgNVHSMEGDAWgBRJmq9/dKkDW8n8mPzGzuo5LcYUKjAMBgNVHRMEBTAD\n" +
+                            "AQH/MAoGCCqGSM49BAMCA0kAMEYCIQDgq5qudvY9zp3ZhVKEfMLbmwybiM15+wrC\n" +
+                            "xp6ipl+GZgIhAKbN/YfYoYlvr6z/xPrZfCZNLEaY/E01PqvD/d91Psa8\n" +
+                            "-----END CERTIFICATE-----\n";
+
+    //openssl dsaparam -genkey 1024 -out dsakey.pem && \
+    //openssl req -x509 -nodes -days 365 -subj '/C=US/ST=Testsota/L=Testville/CN=test.com' \
+    //-newkey dsa:dsakey.pem -sha256 -keyout k.pem -out good.pem
+    private static final String GOOD_DSA_PEM = "" +
+                            "-----BEGIN CERTIFICATE-----\n" +
+                            "MIIDHTCCAtugAwIBAgIJAI4X+OBX9ap9MAsGCWCGSAFlAwQDAjBHMQswCQYDVQQG\n" +
+                            "EwJVUzERMA8GA1UECAwIVGVzdHNvdGExEjAQBgNVBAcMCVRlc3R2aWxsZTERMA8G\n" +
+                            "A1UEAwwIdGVzdC5jb20wHhcNMTQwOTAyMjA1MjUwWhcNMTUwOTAyMjA1MjUwWjBH\n" +
+                            "MQswCQYDVQQGEwJVUzERMA8GA1UECAwIVGVzdHNvdGExEjAQBgNVBAcMCVRlc3R2\n" +
+                            "aWxsZTERMA8GA1UEAwwIdGVzdC5jb20wggG2MIIBKwYHKoZIzjgEATCCAR4CgYEA\n" +
+                            "2QAjoImNX+oSkLdHPDdAzRrbdGdp665OyVBORfdnQeUHbi4WDElqUefTvIWYoDpC\n" +
+                            "Dvio284lhTSwXs8H2LKW3xV3AChzaNmPbGwWd4x8zxrE0OSQ+nXgbnBdhlUNUHpa\n" +
+                            "AnuuD31eMIDRN6o9WJ7DgksL8aEDO9DRuKUI4TNJKtECFQCB4+ccG9JUCoRh/bnb\n" +
+                            "X3cw3BV55wKBgHTmAcAt9Yu6vPdxX6NyzBMwb11kdt/3f0111WCI8nJl/+9mpRDd\n" +
+                            "snuPJUzsT00/JMH+puEN2fgOq7QxlCHtgNhX+WUtRE+QFjgvqilM+o+YEWEzeLfp\n" +
+                            "kWu/VfM6fV1B3jjmMsie1VNuitVVV1WOE7Pw0rq8m/yXQ5xft0ylhmLSA4GEAAKB\n" +
+                            "gH2Q6/2aSPh2b+ePFTLQc20EI6oU6xcyDPKfTsSYH0nUGpr4/k02spVOpHvtUe8e\n" +
+                            "1TVS0U30bzdC3bIz2fSUmeU4Kqde4IoZZ3SKjxD0jUKU4/hGuPSAMDEZfPKQIcpj\n" +
+                            "UEiqYo+r1ER2u3LdSOqu5ZkYNgT4/C7tr6+NIg1Y4sNuo1AwTjAdBgNVHQ4EFgQU\n" +
+                            "PfxTb9tJ6gh4KgFCR6q4Hng1P1AwHwYDVR0jBBgwFoAUPfxTb9tJ6gh4KgFCR6q4\n" +
+                            "Hng1P1AwDAYDVR0TBAUwAwEB/zALBglghkgBZQMEAwIDLwAwLAIUNgv+keqfh+sd\n" +
+                            "6xqIy6O1QFmjCsMCFB+MYu4K4+BrgPrrMVOnHB4MFHHo\n" +
+                            "-----END CERTIFICATE-----";
+
+    //openssl req -x509 -nodes -days 365 -subj '/C=US/ST=Testsota/L=Testville/CN=test.com' \
+    //-newkey rsa:2048 -md2 -keyout k.pem -out md2.pem
+    private static final String MD2_RSA_PEM = "" +
+                            "-----BEGIN CERTIFICATE-----\n" +
+                            "MIIDuzCCAqOgAwIBAgIJAPgJ74B13cElMA0GCSqGSIb3DQEBAgUAMEcxCzAJBgNV\n" +
+                            "BAYTAlVTMREwDwYDVQQIEwhUZXN0c290YTESMBAGA1UEBxMJVGVzdHZpbGxlMREw\n" +
+                            "DwYDVQQDEwh0ZXN0LmNvbTAeFw0xNDA5MDUwMTMwMDZaFw0xNTA5MDUwMTMwMDZa\n" +
+                            "MEcxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhUZXN0c290YTESMBAGA1UEBxMJVGVz\n" +
+                            "dHZpbGxlMREwDwYDVQQDEwh0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP\n" +
+                            "ADCCAQoCggEBAMHoaqm+IagQsnbI5fg1shbV4o4RMuxdOdqq35+FUuyGHRm2iUwu\n" +
+                            "0KVIX35ZGpzzfbpsOMFSy5XoRdgdG/6zEpYXTNzjGWtZQ/51cwMAVxDFAsrL7bZz\n" +
+                            "9mMEbccXOBS6P4mCAVBQmPfjf6YEP9XUFSY4FeD/sfoIwvutQDbkiUKjhUnQzkSl\n" +
+                            "JwnIURUqJOonzBVQV+slypYC9GMrXBT+gVq3QaQSkBwQHHr3SAhZfr8nKoxWlPUy\n" +
+                            "l/uliZw9LlctlqRegzGo9m1JHHft9E4mqN4DsVfHl/43XE9DVzZwFZlJ2iJ0X2yL\n" +
+                            "VXvKPTwZucdXkhl3oW6NHT/u02P9EnSTbEUCAwEAAaOBqTCBpjAdBgNVHQ4EFgQU\n" +
+                            "q1g42h7XKGGPlPbgAmmWvlAC2kMwdwYDVR0jBHAwboAUq1g42h7XKGGPlPbgAmmW\n" +
+                            "vlAC2kOhS6RJMEcxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhUZXN0c290YTESMBAG\n" +
+                            "A1UEBxMJVGVzdHZpbGxlMREwDwYDVQQDEwh0ZXN0LmNvbYIJAPgJ74B13cElMAwG\n" +
+                            "A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQECBQADggEBAIz1S5LVYRrmRAKfEaXf0Ja8\n" +
+                            "XyxGoE8BlM2WWHQoUO6HX+ixJBFueJT6kFJCH4NPKIZdTmhtKKOKBqJeHKiRom2L\n" +
+                            "a+p7GEGondaO/Q+8dqx+S7LUI22CaOss72DHoGFqES37KCs9P8G1gu/5GrQVgfV/\n" +
+                            "/UjESMF5/fQuFncgWfn5c6E5z7PRuYOLw3Clym1GbLUwldGeAeVqT4kcIgIKA3Rd\n" +
+                            "NqMum8A2TrJlrmtxG4OlkKdpKKjPRhYPYLtPXi/g0p8heJ8/YZSwXGQHrqqOND1F\n" +
+                            "fkc4rWxUev50cXXJ4qI8EM0zi3HpBqsqV6JgR8+VMA6MMxPQAWmGbBoztKv1r8U=\n" +
+                            "-----END CERTIFICATE-----";
+
+    //openssl req -x509 -nodes -days 365 -subj '/C=US/ST=Testsota/L=Testville/CN=test.com' \
+    //-newkey rsa:2048 -md4 -keyout k.pem -out md4.pem
+    private static final String MD4_RSA_PEM = "" +
+                            "-----BEGIN CERTIFICATE-----\n" +
+                            "MIIDYTCCAkmgAwIBAgIJAO2CvPpNFLqwMA0GCSqGSIb3DQEBAwUAMEcxCzAJBgNV\n" +
+                            "BAYTAlVTMREwDwYDVQQIDAhUZXN0c290YTESMBAGA1UEBwwJVGVzdHZpbGxlMREw\n" +
+                            "DwYDVQQDDAh0ZXN0LmNvbTAeFw0xNDA5MDQyMjI1MzNaFw0xNTA5MDQyMjI1MzNa\n" +
+                            "MEcxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhUZXN0c290YTESMBAGA1UEBwwJVGVz\n" +
+                            "dHZpbGxlMREwDwYDVQQDDAh0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP\n" +
+                            "ADCCAQoCggEBAOQHeENDnuCN08gW/CgIcIYZlD8qgHIc/QgUaHkxbMNBomiOgD8Z\n" +
+                            "D1JGtrW6ucbdD66L3Zd5gAfqgGbJ8ySrVFpgXbSpVb6C0wulPZRrm9ll4sZ5BYvg\n" +
+                            "zgFhY0TlrizaupZMV+XM3dce/EOYGnrqxWr6jOS7cX3D5Vb9NVE6g+GIW6XKw51Z\n" +
+                            "qD+GxxZ2As0lYaZ3vc/+EbiTs/UuIUTsSQvctRkvc83e2vAPtWHX+9ztOLmpSRUP\n" +
+                            "8xpganKg5JrfKlXlMXdhJipnOPcYLRMf+UD/7s13TyiQ8Qgt1/h8nirkP8mHYreM\n" +
+                            "WenY9Sqrp0FPgGTZbkSnL127mUcWiq+CyasCAwEAAaNQME4wHQYDVR0OBBYEFPSg\n" +
+                            "PNT/OJ5IrgrbA7Y0kNgqMp2uMB8GA1UdIwQYMBaAFPSgPNT/OJ5IrgrbA7Y0kNgq\n" +
+                            "Mp2uMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEDBQADggEBADg6acU5eqHUDjvG\n" +
+                            "M6L+2gMVNiTczlYItLqoibYZW88wzgxpptGKFlWzdl11TIjUaIYqZktfLAWC3Oun\n" +
+                            "C564mYPZfaIJEDKNMqcVPiZa9g/8dbctmOxAAvOGdXl+5uk5xOrAsmab7/NH+ksA\n" +
+                            "YRpcZntUzbqH33GcMP3CG2i8TM0xM3ZjKch+79asBD/vZmNK1BhsHy3LAE2H2HeA\n" +
+                            "k+YDvaBU2yKb0RuZvUmfiySiIjyLtX9JagtHVpcnCZ6pXgCuBy60nGSeP5GQ024x\n" +
+                            "GdyN37tmX7gvcazx1+uBlGtw07Uydua4868v/kgu/Ll2zY37CIY6OFi1G0mdk2Xs\n" +
+                            "28zzK8s=\n" +
+                            "-----END CERTIFICATE-----";
+
+    //openssl req -x509 -nodes -days 365 -subj '/C=US/ST=Testsota/L=Testville/CN=test.com' \
+    //-newkey rsa:2048 -md5 -keyout k.pem -out md5.pem
+    private static final String MD5_RSA_PEM = "" +
+                            "-----BEGIN CERTIFICATE-----\n" +
+                            "MIIDYTCCAkmgAwIBAgIJAJsffMf2cyx0MA0GCSqGSIb3DQEBBAUAMEcxCzAJBgNV\n" +
+                            "BAYTAlVTMREwDwYDVQQIDAhUZXN0c290YTESMBAGA1UEBwwJVGVzdHZpbGxlMREw\n" +
+                            "DwYDVQQDDAh0ZXN0LmNvbTAeFw0xMjEwMTUyMTQzMzZaFw0xMzEwMTUyMTQzMzZa\n" +
+                            "MEcxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhUZXN0c290YTESMBAGA1UEBwwJVGVz\n" +
+                            "dHZpbGxlMREwDwYDVQQDDAh0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP\n" +
+                            "ADCCAQoCggEBAOJyiUwgf/VsdbTTdx6dsb742adeBFBY1FpSWCeQW/JVtdMephbK\n" +
+                            "AA00nu8Xq3dNx9bp8AqvzeyHi/RBsZOtb2eAsOXE3RbFy28ehDTHdG34fRQNT6kp\n" +
+                            "RUHw8wrUGovMVqS8j+iW8HfAy3sjArje0ygz2NIETlNQbEOifAJtY+AEfZwZE0/0\n" +
+                            "IMVP4hwTmIgyReJBDmAx31clwsWZSPar9x+WQfeJ3rfy5LBCtf3RUbdgnvynBHFk\n" +
+                            "FjucwoqgOOXviCWxIa0F+ZAmZJBj5+pLN/V92RXOu0c2fR3Mf68J67OJ+K4ueo1N\n" +
+                            "nBhRsulWMmGqIVjYOZQxiNzWYcOVXj3DTRMCAwEAAaNQME4wHQYDVR0OBBYEFJbY\n" +
+                            "TU06RuJaiMBs2vzx5y0MbaQOMB8GA1UdIwQYMBaAFJbYTU06RuJaiMBs2vzx5y0M\n" +
+                            "baQOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBAFEky0jLTmKefDVX\n" +
+                            "8O84KoupmQ2qQQBaQF3F5GEuhi0qJRwnmsWkCmsxPP55S67WDFp3JH+LX14UxL4T\n" +
+                            "fbG2CXHt/BF1yU3Z8JBwx3bDmfUnUOAFkO3nmByb11FyZTHMzq4jp03DexWREv4q\n" +
+                            "Ai5+5Xb56VECgCH/hnGqhQeFGhlZUcSXobVhAU+39L6azWELXxk1K4bpVxYFGn1N\n" +
+                            "uZ+dWmb6snPKDzG6J5IIX8QIs6G8H6ptj+QNoU/qTcZEnuzMJxpqMsyq10AA+bY/\n" +
+                            "VAYyXeZm3XZrtqYosDeiUdmcL0jjmyQtyOcAoVUQWj1EJuRjXg4BvI6xxRAIPWYT\n" +
+                            "EDeWHJE=\n" +
+                            "-----END CERTIFICATE-----";
+
+    //openssl req -x509 -nodes -days 365 -subj '/C=US/ST=Testsota/L=Testville/CN=test.com' \
+    //-newkey rsa:2048 -sha1 -keyout k.pem -out md5.pem
+    private static final String SHA1_RSA_PEM = "" +
+                            "-----BEGIN CERTIFICATE-----\n" +
+                            "MIIDZDCCAkygAwIBAgIJALW5K4gErucTMA0GCSqGSIb3DQEBBQUAMEcxCzAJBgNV\n" +
+                            "BAYTAlVTMREwDwYDVQQIDAhUZXN0c290YTESMBAGA1UEBwwJVGVzdHZpbGxlMREw\n" +
+                            "DwYDVQQDDAh0ZXN0LmNvbTAeFw0xODA0MTIxOTM1MzlaFw0xOTA0MTIxOTM1Mzla\n" +
+                            "MEcxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhUZXN0c290YTESMBAGA1UEBwwJVGVz\n" +
+                            "dHZpbGxlMREwDwYDVQQDDAh0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP\n" +
+                            "ADCCAQoCggEBAMAphayEftP2twO/FpfUoERx9Y2DyaSMqvLND5Ay6wDXuLMN6qWX\n" +
+                            "3ljtEJW3ZVYM2gEhRIXKKUYt0lyx5EuE0VxrNOVyncr8/SQUY2tYlCSB1LLeOzGB\n" +
+                            "sYvVzEon/FUeKlRmcgae9FdqDP/t1pCwVdSxIhYxGoPt+znsbrT2UFO7yBw2WDZa\n" +
+                            "P8pLP8VeryXWLyAjX2ezxBNVpxwPBsdssrMRqX2BvsZt9pVx87weBH8Mj1lnGJL2\n" +
+                            "4ekfUonSEgT6hhCJv8G6PPvXvV2XWmGzjh+CyaEncoODa5a16JHVmq/BNtK6o/OB\n" +
+                            "YNrne86kDCzpruA69JtSYAf9YM2TU8vy6GECAwEAAaNTMFEwHQYDVR0OBBYEFHFu\n" +
+                            "2+j9+gNDXIlvtDq7P7A6JYnZMB8GA1UdIwQYMBaAFHFu2+j9+gNDXIlvtDq7P7A6\n" +
+                            "JYnZMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBALnB+IOCAuWU\n" +
+                            "BEC8AtPzQaBQh2MJhzIg+0HHOGldkMX6jRGRnySf31okZMr9FjLkUMEwyylZvFI1\n" +
+                            "fFIdq7a070XAH1u4k/Xx7xi7R0+sfnceaLrt1nvOyhEjitLzLT/+zblMrvY+PvpF\n" +
+                            "JkUNSKbd8XkSSMvV3U4bmkAZfP/LIJ8juSrNwzsfIu7IPBq+3yPFZpBR/UNH/NhP\n" +
+                            "/9OmD8bLwSer9xAcWFT3JVljtaHmL3D+mP/Q1n2lsb7VhrZ4XESLN8thWxWddRC7\n" +
+                            "/72ObwvnJIPGB4Knybv8qee02ZDZRKcjFp872FeIkpHMfG/G/kwQiNzvA6cmwTYQ\n" +
+                            "QeVc5iP8Lqo=\n" +
+                            "-----END CERTIFICATE-----";
+
+
+    //openssl ecparam -genkey -name prime256v1 -out eckey.pem && \
+    //openssl req -x509 -nodes -days 365 -subj '/C=US/ST=Testsota/L=Testville/CN=test.com' \
+    //-newkey ec:eckey.pem -sha1 -keyout k.pem -out sha1.pem
+    private static final String SHA1_ECDSA_PEM = "" +
+                            "-----BEGIN CERTIFICATE-----\n" +
+                            "MIIB1zCCAX2gAwIBAgIJAKS+GaTWit91MAkGByqGSM49BAEwRzELMAkGA1UEBhMC\n" +
+                            "VVMxETAPBgNVBAgMCFRlc3Rzb3RhMRIwEAYDVQQHDAlUZXN0dmlsbGUxETAPBgNV\n" +
+                            "BAMMCHRlc3QuY29tMB4XDTE4MDQxMjE5NDAyMloXDTE5MDQxMjE5NDAyMlowRzEL\n" +
+                            "MAkGA1UEBhMCVVMxETAPBgNVBAgMCFRlc3Rzb3RhMRIwEAYDVQQHDAlUZXN0dmls\n" +
+                            "bGUxETAPBgNVBAMMCHRlc3QuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\n" +
+                            "VYHDIpvFu7UBWsfF9G8L5V5Cj+wIGHXIUIYp/GVri9bCTZBkLMqcoNyYKWDKDQb5\n" +
+                            "sKuo/CCSo5+1dPSjy8gm8KNTMFEwHQYDVR0OBBYEFI8coJOBd83LFcwx7ypFc7F0\n" +
+                            "B7clMB8GA1UdIwQYMBaAFI8coJOBd83LFcwx7ypFc7F0B7clMA8GA1UdEwEB/wQF\n" +
+                            "MAMBAf8wCQYHKoZIzj0EAQNJADBGAiEAjXa+FcLuU4jRVf93c4vY8EmATcjFrb4h\n" +
+                            "bKrvFxXMUpkCIQCllGWVU3j8Np8DxX0MK2Af/5h8O4zlr9DvPUpCsggaQw==\n" +
+                            "-----END CERTIFICATE-----";
+
+    //openssl dsaparam -out dsakey.pem -genkey 1024 && \
+    //openssl req -x509 -nodes -days 365 -subj '/C=US/ST=Testsota/L=Testville/CN=test.com' \
+    //-newkey dsa:dsakey.pem -sha1 -keyout k.pem -out sha1.pem
+    private static final String SHA1_DSA_PEM = "" +
+                            "-----BEGIN CERTIFICATE-----\n" +
+                            "MIIDHzCCAt2gAwIBAgIJAPO9edaSntPLMAkGByqGSM44BAMwRzELMAkGA1UEBhMC\n" +
+                            "VVMxETAPBgNVBAgMCFRlc3Rzb3RhMRIwEAYDVQQHDAlUZXN0dmlsbGUxETAPBgNV\n" +
+                            "BAMMCHRlc3QuY29tMB4XDTE4MDQxMjE5NTAyN1oXDTE5MDQxMjE5NTAyN1owRzEL\n" +
+                            "MAkGA1UEBhMCVVMxETAPBgNVBAgMCFRlc3Rzb3RhMRIwEAYDVQQHDAlUZXN0dmls\n" +
+                            "bGUxETAPBgNVBAMMCHRlc3QuY29tMIIBtzCCASwGByqGSM44BAEwggEfAoGBAMZy\n" +
+                            "BYuw9s+UFLnrErRwysU2dfcY0tv4b8FIi63JtF12kTborQkyxilNtDDtBVEA0mKE\n" +
+                            "13dvd8JQx2+d6LwHSiaaS2n2/XofVn61HmDNPns1zV8m9XvUX8Cqmz0+1dgyZx0Y\n" +
+                            "dP+eg2BjfhfX/6tXWXMd2t2+y3sJalLh9KeC/LftAhUA2RmeKHbNMj9pC9wOj8Yj\n" +
+                            "u239Q1ECgYEAhnfB/Z2S/lYc2c78PU2DcChXsj+Mp8ITUwTVg+G4+WvqGzX6FFzr\n" +
+                            "9/eTrn+rPLkKDJonHW/OZyVFK2mVQ/s5xE8Wn9YDUYkNPlJ/dFB+okmhZE8hDRwF\n" +
+                            "LsgtrLgJqpOEw54b37hyqdvk2vtHI+ANU+jZONRdsmWT9HZ0ryJGqY8DgYQAAoGA\n" +
+                            "U8tXEXYh4oCAGLG+S7aNI73LN+a/n0r1aSJM8XuNExZus/eaXCHqEreUi/SBXVEm\n" +
+                            "UJEXnsRwzLyErE24yBlQzLBoMbHqJnIOJRmxjrQ7xo9vivo53woIbxHSRdWlzfwW\n" +
+                            "14yR5dSVDEVI30TTT/zAoNIWvegHXO2LCeEZ/ilLPxCjUzBRMB0GA1UdDgQWBBQB\n" +
+                            "cKP86kuQ/GEG+n0NdJK7A9uBOTAfBgNVHSMEGDAWgBQBcKP86kuQ/GEG+n0NdJK7\n" +
+                            "A9uBOTAPBgNVHRMBAf8EBTADAQH/MAkGByqGSM44BAMDMQAwLgIVAIIMd1qgBuGf\n" +
+                            "zY7SmaNFYmeQV2qpAhUAkPFti47uD7JjdAEqJ/nFMhYcolQ=\n" +
+                            "-----END CERTIFICATE-----";
+
+
+    //openssl req -x509 -nodes -days 365 -subj '/C=US/ST=Testsota/L=Testville/CN=test.com' \
+    //-newkey rsa:512 -sha256 -keyout k.pem -out short.pem
+    private static final String SHORT_RSA_PEM = "" +
+                            "-----BEGIN CERTIFICATE-----\n" +
+                            "MIIB1zCCAYGgAwIBAgIJAOxaz9TreDNIMA0GCSqGSIb3DQEBCwUAMEcxCzAJBgNV\n" +
+                            "BAYTAlVTMREwDwYDVQQIDAhUZXN0c290YTESMBAGA1UEBwwJVGVzdHZpbGxlMREw\n" +
+                            "DwYDVQQDDAh0ZXN0LmNvbTAeFw0xMjEwMTUyMTQzMjNaFw0xMzEwMTUyMTQzMjNa\n" +
+                            "MEcxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhUZXN0c290YTESMBAGA1UEBwwJVGVz\n" +
+                            "dHZpbGxlMREwDwYDVQQDDAh0ZXN0LmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgC\n" +
+                            "QQCoMgxK9HG0L+hXEht1mKq6ApN3+3lmIEVUcWQKL7EMmn9+L6rVSJyOAGwpTVG7\n" +
+                            "eZ5uulC0Lkm5/bzKFSrCf1jlAgMBAAGjUDBOMB0GA1UdDgQWBBTda66RZsgUvR4e\n" +
+                            "2RSsq65K1xcz0jAfBgNVHSMEGDAWgBTda66RZsgUvR4e2RSsq65K1xcz0jAMBgNV\n" +
+                            "HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA0EAZWYgoNDn6yEzcmWgsYnG3w2BT6fL\n" +
+                            "Npi0+APKWkwxnEJk1kgpdeSTMgaHAphQ8qksHnSgeBAJSs2ZCQMinVPgOg==\n" +
+                            "-----END CERTIFICATE-----";
+
+    //openssl dsaparam -genkey 768 -out dsakey.pem && \
+    //openssl req -x509 -nodes -days 365 -subj '/C=US/ST=Testsota/L=Testville/CN=test.com' \
+    //-newkey dsa:dsakey.pem -sha256 -keyout k.pem -out short.pem
+    private static final String SHORT_DSA_PEM = "" +
+                            "-----BEGIN CERTIFICATE-----\n" +
+                            "MIICuDCCAnWgAwIBAgIJAMQeQVxVNTKRMAsGCWCGSAFlAwQDAjBHMQswCQYDVQQG\n" +
+                            "EwJVUzERMA8GA1UECAwIVGVzdHNvdGExEjAQBgNVBAcMCVRlc3R2aWxsZTERMA8G\n" +
+                            "A1UEAwwIdGVzdC5jb20wHhcNMTQwOTAyMjAzNjQ4WhcNMTUwOTAyMjAzNjQ4WjBH\n" +
+                            "MQswCQYDVQQGEwJVUzERMA8GA1UECAwIVGVzdHNvdGExEjAQBgNVBAcMCVRlc3R2\n" +
+                            "aWxsZTERMA8GA1UEAwwIdGVzdC5jb20wggFQMIHoBgcqhkjOOAQBMIHcAmEApVZC\n" +
+                            "vx5pcu5CjEv0n5M0PVxnX/4ZkJn8EAnkgn5P37KxDm7dIHcMw71Epd+l7hP4TLUV\n" +
+                            "etW9VOu1ybo+hOMr3IGqlaMVHxL5VWk6DGFjo5ZplF5QGQt+hqFYX8agruoFAhUA\n" +
+                            "xsTsmLlEe97rZm2UfNt51tXoQgECYA1dMDAfVUqfC06LJ0O5Q2RmjbkqCLfwiXvq\n" +
+                            "q0LVqxQJBVzmjbWoNRdmZpzhjOfMQ2bpQwTj+M4t2YPGifQTgumUolutWGEs7jxU\n" +
+                            "HcybdA8/3fqubZ/pEKrz1FhjIReuJgNjAAJgEWAocKA/8Q7pFQ7tkJDUTctU7ZUN\n" +
+                            "O9eUqghBkJAaHhjq8GJ/UIoPuS8PCz19/xDZICMhbKpobi+z/sy3atZLtcrrUhN1\n" +
+                            "XBgEPD6aWSP3qEBzz2a6MqL6RegDL3ldrRMjo1AwTjAdBgNVHQ4EFgQUk7IR6KN+\n" +
+                            "Lb8ZlDs4v1pKtmQans0wHwYDVR0jBBgwFoAUk7IR6KN+Lb8ZlDs4v1pKtmQans0w\n" +
+                            "DAYDVR0TBAUwAwEB/zALBglghkgBZQMEAwIDMAAwLQIUG9is/MhJ0qXggCtPiOdH\n" +
+                            "UZSNrCgCFQDBb443MntlcWrx5gV7YRd52k0Yug==\n" +
+                            "-----END CERTIFICATE-----";
+
+    //ecparam -genkey -name secp128r1 -out eckey.pem && \
+    //openssl req -x509 -nodes -days 365 -subj '/C=US/ST=Testsota/L=Testville/CN=test.com' \
+    //-newkey ec:eckey.pem -sha256 -keyout k.pem -out short.pem
+    private static final String SHORT_ECDSA_PEM = "" +
+                            "-----BEGIN CERTIFICATE-----\n" +
+                            "MIIBkTCCAVigAwIBAgIJAKogErAsYuahMAoGCCqGSM49BAMCMEcxCzAJBgNVBAYT\n" +
+                            "AlVTMREwDwYDVQQIDAhUZXN0c290YTESMBAGA1UEBwwJVGVzdHZpbGxlMREwDwYD\n" +
+                            "VQQDDAh0ZXN0LmNvbTAeFw0xNDA5MDIyMDQ1MjdaFw0xNTA5MDIyMDQ1MjdaMEcx\n" +
+                            "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhUZXN0c290YTESMBAGA1UEBwwJVGVzdHZp\n" +
+                            "bGxlMREwDwYDVQQDDAh0ZXN0LmNvbTA2MBAGByqGSM49AgEGBSuBBAAcAyIABE9Z\n" +
+                            "bL28dyGE/sRmSUB0kqdsmkaKaC7gu+9A4CLDO5kJo1AwTjAdBgNVHQ4EFgQU7f+b\n" +
+                            "vrGRimukkorDkERufEFRaj0wHwYDVR0jBBgwFoAU7f+bvrGRimukkorDkERufEFR\n" +
+                            "aj0wDAYDVR0TBAUwAwEB/zAKBggqhkjOPQQDAgMnADAkAhBXRMkfHNexPXaqzJwT\n" +
+                            "9eAwAhAzX+1NE+FY0kk74wH83Cz0\n" +
+                            "-----END CERTIFICATE-----";
+
+
+    @Test
+    public void testMD2() throws Exception {
+        assertBad(MD2_RSA_PEM, "Weak hash check did not fail as expected");
+    }
+
+    @Test
+    public void testMD4() throws Exception {
+        assertBad(MD4_RSA_PEM, "Weak hash check did not fail as expected");
+    }
+
+    @Test
+    public void testMD5() throws Exception {
+        assertBad(MD5_RSA_PEM, "Weak hash check did not fail as expected");
+    }
+
+    @Test
+    public void testSHA1() throws Exception {
+        assertBad(SHA1_RSA_PEM, "Weak SHA1 RSA signature did not fail as expected");
+        assertBad(SHA1_ECDSA_PEM, "Weak SHA1 ECDSA signature did not fail as expected");
+        assertBad(SHA1_DSA_PEM, "Weak SHA1 DSA signature did not fail as expected");
+    }
+
+    @Test
+    public void testRsa512() throws Exception {
+        assertBad(SHORT_RSA_PEM, "Short RSA modulus check did not fail as expected");
+    }
+
+    @Test
+    public void testDsa768() throws Exception {
+        assertBad(SHORT_DSA_PEM, "Short DSA key check did not fail as expected");
+    }
+
+    @Test
+    public void testEcdsa128() throws Exception {
+        assertBad(SHORT_ECDSA_PEM, "Short EC key check did not fail as expected");
+    }
+
+    @Test
+    public void testGoodChain() throws Exception {
+        assertGood(GOOD_RSA_PEM);
+        assertGood(GOOD_DSA_PEM);
+        assertGood(GOOD_ECDSA_PEM);
+    }
+
+    private static void assertBad(String pem, String msg) throws Exception {
+        try {
+            check(createCert(pem));
+            fail(msg);
+        } catch (CertificateException expected) {
+        } catch (NoSuchAlgorithmException expected) {
+            // Some weak EC groups can no longer be parsed.
+        }
+    }
+
+    private static void assertGood(String pem) throws Exception {
+        check(createCert(pem));
+    }
+
+    private static void check(X509Certificate cert) throws Exception {
+        X509Certificate[] chain = {cert};
+        ChainStrengthAnalyzer.check(chain);
+    }
+
+    private static X509Certificate createCert(String pem) throws Exception {
+        CertificateFactory cf = CertificateFactory.getInstance("X509");
+        InputStream pemInput = new ByteArrayInputStream(pem.getBytes("UTF-8"));
+        return (X509Certificate) cf.generateCertificate(pemInput);
+    }
+}
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/TrustManagerImplTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/TrustManagerImplTest.java
new file mode 100644
index 0000000..40ca21e
--- /dev/null
+++ b/repackaged/common/src/test/java/com/android/org/conscrypt/TrustManagerImplTest.java
@@ -0,0 +1,486 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.List;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509TrustManager;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class TrustManagerImplTest {
+
+    /**
+     * Ensure that our non-standard behavior of learning to trust new
+     * intermediate CAs does not regress. http://b/3404902
+     */
+    @Test
+    public void testLearnIntermediate() throws Exception {
+        TestUtils.assumeExtendedTrustManagerAvailable();
+        // chain3 should be server/intermediate/root
+        KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
+        X509Certificate[] chain3 = (X509Certificate[])pke.getCertificateChain();
+        X509Certificate root = chain3[2];
+        X509Certificate intermediate = chain3[1];
+        X509Certificate server = chain3[0];
+        X509Certificate[] chain2 =  new X509Certificate[] { server, intermediate };
+        X509Certificate[] chain1 =  new X509Certificate[] { server };
+
+        // Normal behavior
+        assertValid(chain3,   trustManager(root));
+        assertValid(chain2,   trustManager(root));
+        assertInvalid(chain1, trustManager(root));
+        assertValid(chain3,   trustManager(intermediate));
+        assertValid(chain2,   trustManager(intermediate));
+        assertValid(chain1,   trustManager(intermediate));
+        assertValid(chain3,   trustManager(server));
+        assertValid(chain2,   trustManager(server));
+        assertValid(chain1,   trustManager(server));
+
+        // non-standard behavior
+        X509TrustManager tm = trustManager(root);
+        // fail on short chain with only root trusted
+        assertInvalid(chain1, tm);
+        // succeed on longer chain, learn intermediate
+        assertValid(chain2, tm);
+        // now we can validate the short chain
+        assertValid(chain1, tm);
+    }
+
+    // We should ignore duplicate cruft in the certificate chain
+    // See https://code.google.com/p/android/issues/detail?id=52295 http://b/8313312
+    @Test
+    public void testDuplicateInChain() throws Exception {
+        TestUtils.assumeExtendedTrustManagerAvailable();
+        // chain3 should be server/intermediate/root
+        KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
+        X509Certificate[] chain3 = (X509Certificate[])pke.getCertificateChain();
+        X509Certificate root = chain3[2];
+        X509Certificate intermediate = chain3[1];
+        X509Certificate server = chain3[0];
+
+        X509Certificate[] chain4 = new X509Certificate[] { server, intermediate,
+                                                           server, intermediate
+        };
+        assertValid(chain4, trustManager(root));
+    }
+
+    @Test
+    public void testGetFullChain() throws Exception {
+        TestUtils.assumeExtendedTrustManagerAvailable();
+        // build the trust manager
+        KeyStore.PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
+        X509Certificate[] chain3 = (X509Certificate[]) pke.getCertificateChain();
+        X509Certificate root = chain3[2];
+        X509TrustManager tm = trustManager(root);
+
+        // build the chains we'll use for testing
+        X509Certificate intermediate = chain3[1];
+        X509Certificate server = chain3[0];
+        X509Certificate[] chain2 =  new X509Certificate[] { server, intermediate };
+        X509Certificate[] chain1 =  new X509Certificate[] { server };
+
+        assertTrue(tm instanceof TrustManagerImpl);
+        TrustManagerImpl tmi = (TrustManagerImpl) tm;
+        List<X509Certificate> certs =
+                tmi.checkServerTrusted(chain2, "RSA", new FakeSSLSession("purple.com"));
+        assertEquals(Arrays.asList(chain3), certs);
+        certs = tmi.checkServerTrusted(chain1, "RSA", new FakeSSLSession("purple.com"));
+        assertEquals(Arrays.asList(chain3), certs);
+    }
+
+    @Test
+    public void testHttpsEndpointIdentification() throws Exception {
+        TestUtils.assumeExtendedTrustManagerAvailable();
+
+        KeyStore.PrivateKeyEntry pke = TestKeyStore.getServerHostname().getPrivateKey("RSA", "RSA");
+        X509Certificate[] chain = (X509Certificate[]) pke.getCertificateChain();
+        X509Certificate root = chain[2];
+        TrustManagerImpl tmi = (TrustManagerImpl) trustManager(root);
+
+        String goodHostname = TestKeyStore.CERT_HOSTNAME;
+        String badHostname = "definitelywrong.nopenopenope";
+
+        // The default hostname verifier on OpenJDK rejects all hostnames, so use our own
+        javax.net.ssl.HostnameVerifier oldDefault = HttpsURLConnection.getDefaultHostnameVerifier();
+        try {
+            HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier());
+
+            SSLParameters params = new SSLParameters();
+
+            // Without endpoint identification this should pass despite the mismatched hostname
+            params.setEndpointIdentificationAlgorithm(null);
+
+            List<X509Certificate> certs = tmi.getTrustedChainForServer(chain, "RSA",
+                    new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params));
+            assertEquals(Arrays.asList(chain), certs);
+
+            // Turn on endpoint identification
+            params.setEndpointIdentificationAlgorithm("HTTPS");
+
+            try {
+                tmi.getTrustedChainForServer(chain, "RSA",
+                        new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params));
+            } catch (CertificateException expected) {
+            }
+
+            certs = tmi.getTrustedChainForServer(chain, "RSA",
+                    new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params));
+            assertEquals(Arrays.asList(chain), certs);
+
+            // Override the global default hostname verifier with a Conscrypt-specific one that
+            // always passes.  Both scenarios should pass.
+            Conscrypt.setDefaultHostnameVerifier(new ConscryptHostnameVerifier() {
+                @Override
+                public boolean verify(String s, SSLSession sslSession) {
+                    return true;
+                }
+            });
+
+            certs = tmi.getTrustedChainForServer(chain, "RSA",
+                    new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params));
+            assertEquals(Arrays.asList(chain), certs);
+
+            certs = tmi.getTrustedChainForServer(chain, "RSA",
+                    new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params));
+            assertEquals(Arrays.asList(chain), certs);
+
+            // Now set an instance-specific verifier on the trust manager.  The bad hostname should
+            // fail again.
+            Conscrypt.setHostnameVerifier(tmi, new TestHostnameVerifier());
+
+            try {
+                tmi.getTrustedChainForServer(chain, "RSA",
+                        new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params));
+            } catch (CertificateException expected) {
+            }
+
+            certs = tmi.getTrustedChainForServer(chain, "RSA",
+                    new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params));
+            assertEquals(Arrays.asList(chain), certs);
+
+            // Remove the instance-specific verifier, and both should pass again.
+            Conscrypt.setHostnameVerifier(tmi, null);
+
+            certs = tmi.getTrustedChainForServer(chain, "RSA",
+                    new FakeSSLSocket(new FakeSSLSession(badHostname, chain), params));
+            assertEquals(Arrays.asList(chain), certs);
+
+            certs = tmi.getTrustedChainForServer(chain, "RSA",
+                    new FakeSSLSocket(new FakeSSLSession(goodHostname, chain), params));
+            assertEquals(Arrays.asList(chain), certs);
+        } finally {
+            Conscrypt.setDefaultHostnameVerifier(null);
+            HttpsURLConnection.setDefaultHostnameVerifier(oldDefault);
+        }
+    }
+
+    private X509TrustManager trustManager(X509Certificate ca) throws Exception {
+        KeyStore keyStore = TestKeyStore.createKeyStore();
+        keyStore.setCertificateEntry("alias", ca);
+
+        return new TrustManagerImpl(keyStore);
+    }
+
+    private void assertValid(X509Certificate[] chain, X509TrustManager tm) throws Exception {
+        if (tm instanceof TrustManagerImpl) {
+            TrustManagerImpl tmi = (TrustManagerImpl) tm;
+            tmi.checkServerTrusted(chain, "RSA");
+        }
+        tm.checkServerTrusted(chain, "RSA");
+    }
+
+    private void assertInvalid(X509Certificate[] chain, X509TrustManager tm) {
+        try {
+            tm.checkClientTrusted(chain, "RSA");
+            fail();
+        } catch (CertificateException expected) {
+            // Expected.
+        }
+        try {
+            tm.checkServerTrusted(chain, "RSA");
+            fail();
+        } catch (CertificateException expected) {
+            // Expected.
+        }
+    }
+
+    private static class FakeSSLSession implements SSLSession {
+        private final String hostname;
+        private final X509Certificate[] peerCerts;
+
+        FakeSSLSession(String hostname) {
+            this.hostname = hostname;
+            peerCerts = null;
+        }
+
+        FakeSSLSession(String hostname, X509Certificate[] peerCerts) {
+            this.hostname = hostname;
+            this.peerCerts = peerCerts.clone();
+        }
+
+        @Override
+        public int getApplicationBufferSize() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String getCipherSuite() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public long getCreationTime() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public byte[] getId() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public long getLastAccessedTime() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Certificate[] getLocalCertificates() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Principal getLocalPrincipal() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int getPacketBufferSize() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+                throws SSLPeerUnverifiedException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+            if (peerCerts == null) {
+                throw new SSLPeerUnverifiedException("Null peerCerts");
+            } else {
+                return peerCerts.clone();
+            }
+        }
+
+        @Override
+        public String getPeerHost() {
+            return hostname;
+        }
+
+        @Override
+        public int getPeerPort() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String getProtocol() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public SSLSessionContext getSessionContext() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Object getValue(String name) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String[] getValueNames() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void invalidate() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isValid() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void putValue(String name, Object value) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void removeValue(String name) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    private static class FakeSSLSocket extends SSLSocket {
+        private final SSLSession session;
+        private final SSLParameters parameters;
+
+        public FakeSSLSocket(SSLSession session, SSLParameters parameters) {
+            this.session = session;
+            this.parameters = parameters;
+        }
+
+        @Override
+        public SSLParameters getSSLParameters() {
+            return parameters;
+        }
+
+        @Override
+        public String[] getSupportedCipherSuites() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String[] getEnabledCipherSuites() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void setEnabledCipherSuites(String[] strings) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String[] getSupportedProtocols() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String[] getEnabledProtocols() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void setEnabledProtocols(String[] strings) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public SSLSession getSession() {
+            return session;
+        }
+
+        @Override
+        public SSLSession getHandshakeSession() {
+            return session;
+        }
+
+        @Override
+        public void addHandshakeCompletedListener(
+                HandshakeCompletedListener handshakeCompletedListener) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void removeHandshakeCompletedListener(
+                HandshakeCompletedListener handshakeCompletedListener) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void startHandshake() throws IOException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void setUseClientMode(boolean b) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean getUseClientMode() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void setNeedClientAuth(boolean b) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean getNeedClientAuth() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void setWantClientAuth(boolean b) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean getWantClientAuth() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void setEnableSessionCreation(boolean b) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean getEnableSessionCreation() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    private static class TestHostnameVerifier
+            extends com.android.org.conscrypt.javax.net.ssl.TestHostnameVerifier
+            implements ConscryptHostnameVerifier {}
+}
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/ct/CTVerifierTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/ct/CTVerifierTest.java
new file mode 100644
index 0000000..ac153fb
--- /dev/null
+++ b/repackaged/common/src/test/java/com/android/org/conscrypt/ct/CTVerifierTest.java
@@ -0,0 +1,197 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import static com.android.org.conscrypt.TestUtils.openTestFile;
+import static com.android.org.conscrypt.TestUtils.readTestFile;
+import static org.junit.Assert.assertEquals;
+
+import java.security.PublicKey;
+import java.security.Security;
+import java.util.Arrays;
+import com.android.org.conscrypt.OpenSSLX509Certificate;
+import com.android.org.conscrypt.TestUtils;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class CTVerifierTest {
+    private static boolean installedProvider;
+    private OpenSSLX509Certificate ca;
+    private OpenSSLX509Certificate cert;
+    private OpenSSLX509Certificate certEmbedded;
+    private CTVerifier ctVerifier;
+
+    @BeforeClass
+    public static void installConscrypt() {
+        installedProvider = TestUtils.installConscryptIfNotPresent();
+    }
+
+    @AfterClass
+    public static void removeConscrypt() {
+        if (installedProvider) {
+            Security.removeProvider(TestUtils.getConscryptProvider().getName());
+            installedProvider = false;
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        ca = OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("ca-cert.pem"));
+        cert = OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("cert.pem"));
+        certEmbedded = OpenSSLX509Certificate.fromX509PemInputStream(
+                openTestFile("cert-ct-embedded.pem"));
+
+        PublicKey key = TestUtils.readPublicKeyPemFile("ct-server-key-public.pem");
+
+        final CTLogInfo log = new CTLogInfo(key, "Test Log", "foo");
+        CTLogStore store = new CTLogStore() {
+            @Override
+            public CTLogInfo getKnownLog(byte[] logId) {
+                if (Arrays.equals(logId, log.getID())) {
+                    return log;
+                } else {
+                    return null;
+                }
+            }
+        };
+
+        ctVerifier = new CTVerifier(store);
+    }
+
+    @Test
+    public void test_verifySignedCertificateTimestamps_withOCSPResponse() throws Exception {
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
+
+        byte[] ocspResponse = readTestFile("ocsp-response.der");
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, null, ocspResponse);
+        assertEquals(1, result.getValidSCTs().size());
+        assertEquals(0, result.getInvalidSCTs().size());
+    }
+
+    @Test
+    public void test_verifySignedCertificateTimestamps_withTLSExtension() throws Exception {
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
+
+        byte[] tlsExtension = readTestFile("ct-signed-timestamp-list");
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, tlsExtension, null);
+        assertEquals(1, result.getValidSCTs().size());
+        assertEquals(0, result.getInvalidSCTs().size());
+    }
+
+    @Test
+    public void test_verifySignedCertificateTimestamps_withEmbeddedExtension() throws Exception {
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { certEmbedded, ca };
+
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, null, null);
+        assertEquals(1, result.getValidSCTs().size());
+        assertEquals(0, result.getInvalidSCTs().size());
+    }
+
+    @Test
+    public void test_verifySignedCertificateTimestamps_withoutTimestamp() throws Exception {
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
+
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, null, null);
+        assertEquals(0, result.getValidSCTs().size());
+        assertEquals(0, result.getInvalidSCTs().size());
+    }
+
+    @Test
+    public void test_verifySignedCertificateTimestamps_withInvalidSignature() throws Exception {
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
+
+        byte[] tlsExtension = readTestFile("ct-signed-timestamp-list-invalid");
+
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, tlsExtension, null);
+        assertEquals(0, result.getValidSCTs().size());
+        assertEquals(1, result.getInvalidSCTs().size());
+        assertEquals(VerifiedSCT.Status.INVALID_SIGNATURE,
+                     result.getInvalidSCTs().get(0).status);
+    }
+
+    @Test
+    public void test_verifySignedCertificateTimestamps_withUnknownLog() throws Exception {
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
+
+        byte[] tlsExtension = readTestFile("ct-signed-timestamp-list-unknown");
+
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, tlsExtension, null);
+        assertEquals(0, result.getValidSCTs().size());
+        assertEquals(1, result.getInvalidSCTs().size());
+        assertEquals(VerifiedSCT.Status.UNKNOWN_LOG,
+                     result.getInvalidSCTs().get(0).status);
+    }
+
+    @Test
+    public void test_verifySignedCertificateTimestamps_withInvalidEncoding() throws Exception {
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
+
+        // Just some garbage data which will fail to deserialize
+        byte[] tlsExtension = new byte[] { 1, 2, 3, 4 };
+
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, tlsExtension, null);
+        assertEquals(0, result.getValidSCTs().size());
+        assertEquals(0, result.getInvalidSCTs().size());
+    }
+
+    @Test
+    public void test_verifySignedCertificateTimestamps_withInvalidOCSPResponse() throws Exception {
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
+
+        // Just some garbage data which will fail to deserialize
+        byte[] ocspResponse = new byte[] { 1, 2, 3, 4 };
+
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, null, ocspResponse);
+        assertEquals(0, result.getValidSCTs().size());
+        assertEquals(0, result.getInvalidSCTs().size());
+    }
+
+    @Test
+    public void test_verifySignedCertificateTimestamps_withMultipleTimestamps() throws Exception {
+        OpenSSLX509Certificate[] chain = new OpenSSLX509Certificate[] { cert, ca };
+
+        byte[] tlsExtension = readTestFile("ct-signed-timestamp-list-invalid");
+        byte[] ocspResponse = readTestFile("ocsp-response.der");
+
+        CTVerificationResult result =
+            ctVerifier.verifySignedCertificateTimestamps(chain, tlsExtension, ocspResponse);
+        assertEquals(1, result.getValidSCTs().size());
+        assertEquals(1, result.getInvalidSCTs().size());
+        assertEquals(SignedCertificateTimestamp.Origin.OCSP_RESPONSE,
+                     result.getValidSCTs().get(0).sct.getOrigin());
+        assertEquals(SignedCertificateTimestamp.Origin.TLS_EXTENSION,
+                     result.getInvalidSCTs().get(0).sct.getOrigin());
+    }
+}
+
diff --git a/repackaged/common/src/test/java/com/android/org/conscrypt/ct/SerializationTest.java b/repackaged/common/src/test/java/com/android/org/conscrypt/ct/SerializationTest.java
new file mode 100644
index 0000000..7dabf84
--- /dev/null
+++ b/repackaged/common/src/test/java/com/android/org/conscrypt/ct/SerializationTest.java
@@ -0,0 +1,229 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class SerializationTest {
+
+    @Test
+    public void test_decode_SignedCertificateTimestamp() throws Exception {
+        byte[] in = new byte[] {
+            0x00,                            // version
+            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // log id
+            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+            0x01, 0x02, 0x03, 0x04,          // timestamp
+            0x05, 0x06, 0x07, 0x08,
+            0x00, 0x00,                      // extensions length
+            0x04, 0x03,                      // hash & signature algorithm
+            0x00, 0x04,                      // signature length
+            0x12, 0x34, 0x56, 0x78           // signature
+        };
+
+        SignedCertificateTimestamp sct
+            = SignedCertificateTimestamp.decode(in, SignedCertificateTimestamp.Origin.EMBEDDED);
+
+        assertEquals(SignedCertificateTimestamp.Version.V1, sct.getVersion());
+        assertEquals(0x0102030405060708L, sct.getTimestamp());
+        assertEquals(0, sct.getExtensions().length);
+        assertEquals(DigitallySigned.HashAlgorithm.SHA256,
+                     sct.getSignature().getHashAlgorithm());
+        assertEquals(DigitallySigned.SignatureAlgorithm.ECDSA,
+                     sct.getSignature().getSignatureAlgorithm());
+        assertTrue(Arrays.equals(new byte[] { 0x12, 0x34, 0x56, 0x78},
+                     sct.getSignature().getSignature()));
+        assertEquals(SignedCertificateTimestamp.Origin.EMBEDDED, sct.getOrigin());
+    }
+
+    @Test
+    public void test_decode_invalid_SignedCertificateTimestamp() throws Exception {
+        byte[] sct = new byte[] {
+            0x00,                            // version
+            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // log id
+            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+            0x01, 0x02, 0x03, 0x04,          // timestamp
+            0x05, 0x06, 0x07, 0x08,
+            0x00, 0x00,                      // extensions length
+            0x04, 0x03,                      // hash & signature algorithm
+            0x00, 0x04,                      // signature length
+            0x12, 0x34, 0x56, 0x78           // signature
+        };
+
+        // Make sure the original decodes fine
+        SignedCertificateTimestamp.decode(sct, SignedCertificateTimestamp.Origin.EMBEDDED);
+
+        // Perform various modification to it, and make sure it throws an exception on decoding
+        try {
+            byte[] in = sct.clone();
+            in[0] = 1; // Modify version field
+            SignedCertificateTimestamp.decode(in, SignedCertificateTimestamp.Origin.EMBEDDED);
+            fail("SerializationException not thrown on unsupported version");
+        } catch (SerializationException e) {}
+
+        try {
+            byte[] in = sct.clone();
+            in[41] = 1; // Modify extensions lemgth
+            SignedCertificateTimestamp.decode(in, SignedCertificateTimestamp.Origin.EMBEDDED);
+            fail("SerializationException not thrown on invalid extensions length");
+        } catch (SerializationException e) {}
+    }
+
+    @Test
+    public void test_decode_DigitallySigned() throws Exception {
+        byte[] in = new byte[] {
+            0x04, 0x03,            // hash & signature algorithm
+            0x00, 0x04,            // signature length
+            0x12, 0x34, 0x56, 0x78 // signature
+        };
+
+        DigitallySigned dst = DigitallySigned.decode(in);
+        assertEquals(DigitallySigned.HashAlgorithm.SHA256, dst.getHashAlgorithm());
+        assertEquals(DigitallySigned.SignatureAlgorithm.ECDSA, dst.getSignatureAlgorithm());
+        assertEqualByteArrays(new byte[] { 0x12, 0x34, 0x56, 0x78}, dst.getSignature());
+    }
+
+    @Test
+    public void test_decode_invalid_DigitallySigned() throws Exception {
+        try {
+            DigitallySigned.decode(new byte[] {
+                0x07, 0x03,            // hash & signature algorithm
+                0x00, 0x04,            // signature length
+                0x12, 0x34, 0x56, 0x78 // signature
+            });
+            fail("SerializationException not thrown on invalid hash type");
+        } catch (SerializationException e) {}
+
+        try {
+            DigitallySigned.decode(new byte[] {
+                0x04, 0x04,            // hash & signature algorithm
+                0x00, 0x04,            // signature length
+                0x12, 0x34, 0x56, 0x78 // signature
+            });
+            fail("SerializationException not thrown on invalid signature type");
+        } catch (SerializationException e) {}
+
+        try {
+            DigitallySigned.decode(new byte[] {
+                0x07, 0x03,            // hash & signature algorithm
+                0x64, 0x35,            // signature length
+                0x12, 0x34, 0x56, 0x78 // signature
+            });
+            fail("SerializationException not thrown on invalid signature length");
+        } catch (SerializationException e) {}
+
+        try {
+            DigitallySigned.decode(new byte[] {
+                0x07, 0x03,            // hash & signature algorithm
+            });
+            fail("SerializationException not thrown on missing signature");
+        } catch (SerializationException e) {}
+    }
+
+    @Test
+    public void test_encode_CertificateEntry_X509Certificate() throws Exception {
+        // Use a dummy certificate. It doesn't matter, CertificateEntry doesn't care about the contents.
+        CertificateEntry entry = CertificateEntry.createForX509Certificate(new byte[] { 0x12, 0x34, 0x56, 0x78 });
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        entry.encode(output);
+
+        assertEqualByteArrays(new byte[] {
+            0x00, 0x00,            // entry_type
+            0x00, 0x00, 0x04,      // x509_entry length
+            0x12, 0x34, 0x56, 0x78 // x509_entry
+        }, output.toByteArray());
+    }
+
+    @Test
+    public void test_encode_CertificateEntry_PreCertificate() throws Exception {
+        // Use a dummy certificate and issuer key hash. It doesn't matter,
+        // CertificateEntry doesn't care about the contents.
+        CertificateEntry entry = CertificateEntry.createForPrecertificate(new byte[] { 0x12, 0x34, 0x56, 0x78 },
+                                                          new byte[32]);
+
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        entry.encode(output);
+
+        assertEqualByteArrays(new byte[] {
+            0x00, 0x01,                      // entry_type
+            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // issuer key hash
+            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+            0x00, 0x00, 0x04,                // precert_entry length
+            0x12, 0x34, 0x56, 0x78           // precert_entry
+        }, output.toByteArray());
+    }
+
+    @Test
+    public void test_readDEROctetString() throws Exception {
+        byte[] in, expected;
+
+        in = new byte[] {
+            0x04, // TAG
+            0x06, // length
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x05 // data
+        };
+        expected = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x05 };
+        assertEqualByteArrays(expected, Serialization.readDEROctetString(in));
+
+        in = new byte[203];
+        in[0] = 0x04; // TAG
+        in[1] = (byte)0x81; // long length flag
+        in[2] = (byte)200; // length
+        in[3] = 0x45; // data, the rest is just zeros
+
+        expected = new byte[200];
+        expected[0] = 0x45;
+        assertEqualByteArrays(expected, Serialization.readDEROctetString(in));
+
+        try {
+            in = new byte[] {
+                0x12, // wrong tag
+                0x06, // length
+                0x01, 0x02, 0x03, 0x04, 0x05, 0x05 // data
+            };
+            Serialization.readDEROctetString(in);
+            fail("SerializationException not thrown on invalid tag.");
+        } catch (SerializationException e) {}
+
+        try {
+            in = new byte[] {
+                0x04, // wrong tag
+                0x06, // length
+                0x01, 0x02 // data
+            };
+            Serialization.readDEROctetString(in);
+            fail("SerializationException not thrown on invalid length.");
+        } catch (SerializationException e) {}
+    }
+
+    public static void assertEqualByteArrays(byte[] expected, byte[] actual) {
+        assertEquals(Arrays.toString(expected), Arrays.toString(actual));
+    }
+}
+
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParameterGeneratorTestDH.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParameterGeneratorTestDH.java
new file mode 100644
index 0000000..8ec1867
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParameterGeneratorTestDH.java
@@ -0,0 +1,32 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class AlgorithmParameterGeneratorTestDH extends
+        AbstractAlgorithmParameterGeneratorTest {
+
+    public AlgorithmParameterGeneratorTestDH() {
+        super("DH", new AlgorithmParameterKeyAgreementHelper("DH"));
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParameterGeneratorTestDSA.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParameterGeneratorTestDSA.java
new file mode 100644
index 0000000..bd192f3
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParameterGeneratorTestDSA.java
@@ -0,0 +1,33 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import java.security.spec.DSAParameterSpec;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class AlgorithmParameterGeneratorTestDSA extends
+        AbstractAlgorithmParameterGeneratorTest {
+
+    public AlgorithmParameterGeneratorTestDSA() {
+        super("DSA", new AlgorithmParameterSignatureHelper<DSAParameterSpec>("DSA", DSAParameterSpec.class));
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersPSSTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersPSSTest.java
new file mode 100644
index 0000000..2c47180
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersPSSTest.java
@@ -0,0 +1,263 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.security.AlgorithmParameters;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.MGF1ParameterSpec;
+import java.security.spec.PSSParameterSpec;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+import com.android.org.conscrypt.TestUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class AlgorithmParametersPSSTest {
+
+    // ASN.1 DER-encoded forms of DEFAULT_SPEC and WEIRD_SPEC were generated using
+    // Bouncy Castle 1.52 AlgorithmParameters of type "PSS" and checked for correctness
+    // using ASN.1 DER decoder.
+    private static final PSSParameterSpec DEFAULT_SPEC = PSSParameterSpec.DEFAULT;
+    private static final byte[] DEFAULT_SPEC_DER_ENCODED = TestUtils.decodeHex("3000");
+
+    private static final PSSParameterSpec WEIRD_SPEC =
+            new PSSParameterSpec("SHA-224", "MGF1", MGF1ParameterSpec.SHA256, 32, 1);
+    private static final byte[] WEIRD_SPEC_DER_ENCODED =
+            TestUtils.decodeHex(
+                    "3034a00f300d06096086480165030402040500a11c301a06092a864886f70d010108300d060960"
+                    + "86480165030402010500a203020120");
+
+    /** Truncated SEQUENCE (one more byte needed at the end) */
+    private static final byte[] BROKEN_SPEC1_DER_ENCODED =
+            TestUtils.decodeHex(
+                    "303aa00f300d06096086480165030402030500a11c301a06092a864886f70d010108300d060960"
+                    + "86480165030402020500a20302011ba303020103");
+
+    /** Payload of SEQUENCE extends beyond the SEQUENCE. */
+    private static final byte[] BROKEN_SPEC2_DER_ENCODED =
+            TestUtils.decodeHex(
+                    "3037a00f300d06096086480165030402030500a11c301a06092a864886f70d010108300d060960"
+                    + "86480165030402020500a20302011ba303020103");
+
+    @Test
+    public void testGetInstance() throws Exception {
+        AlgorithmParameters params = AlgorithmParameters.getInstance("PSS");
+        assertNotNull(params);
+    }
+
+    @Test
+    public void testGetAlgorithm() throws Exception {
+        AlgorithmParameters params = AlgorithmParameters.getInstance("PSS");
+        assertEquals("PSS", params.getAlgorithm());
+    }
+
+    @Test
+    public void testGetProvider() throws Exception {
+        AlgorithmParameters params = AlgorithmParameters.getInstance("PSS");
+        params.init(DEFAULT_SPEC);
+        assertNotNull(params.getProvider());
+    }
+
+    @Test
+    public void testInitFailsWhenAlreadyInitialized() throws Exception {
+        AlgorithmParameters params = AlgorithmParameters.getInstance("PSS");
+        params.init(DEFAULT_SPEC);
+        try {
+            params.init(DEFAULT_SPEC);
+            fail();
+        } catch (InvalidParameterSpecException expected) {}
+        try {
+            params.init(DEFAULT_SPEC_DER_ENCODED);
+            fail();
+        } catch (IOException expected) {}
+        try {
+            params.init(DEFAULT_SPEC_DER_ENCODED, "ASN.1");
+            fail();
+        } catch (IOException expected) {}
+    }
+
+    @Test
+    public void testInitWithPSSParameterSpec() throws Exception {
+        AlgorithmParameters params = AlgorithmParameters.getInstance("PSS");
+        params.init(WEIRD_SPEC);
+        assertPSSParameterSpecEquals(WEIRD_SPEC, params.getParameterSpec(PSSParameterSpec.class));
+    }
+
+    @Test
+    public void testInitWithDerEncoded() throws Exception {
+        assertInitWithDerEncoded(WEIRD_SPEC_DER_ENCODED, WEIRD_SPEC);
+        assertInitWithDerEncoded(DEFAULT_SPEC_DER_ENCODED, DEFAULT_SPEC);
+    }
+
+    private void assertInitWithDerEncoded(
+            byte[] encoded, PSSParameterSpec expected) throws Exception {
+        AlgorithmParameters params = AlgorithmParameters.getInstance("PSS");
+        params.init(encoded);
+        assertPSSParameterSpecEquals(expected, params.getParameterSpec(PSSParameterSpec.class));
+
+        params = AlgorithmParameters.getInstance("PSS");
+        params.init(encoded, "ASN.1");
+        assertPSSParameterSpecEquals(expected, params.getParameterSpec(PSSParameterSpec.class));
+    }
+
+    @Test
+    public void testGetEncodedThrowsWhenNotInitialized() throws Exception {
+        AlgorithmParameters params = AlgorithmParameters.getInstance("PSS");
+        try {
+            params.getEncoded();
+            fail();
+        } catch (IOException expected) {}
+        try {
+            params.getEncoded("ASN.1");
+            fail();
+        } catch (IOException expected) {}
+    }
+
+    @Test
+    public void testGetEncoded() throws Exception {
+        assertGetEncoded(WEIRD_SPEC, WEIRD_SPEC_DER_ENCODED);
+        assertGetEncoded(DEFAULT_SPEC, DEFAULT_SPEC_DER_ENCODED);
+    }
+
+    private void assertGetEncoded(PSSParameterSpec spec, byte[] expectedEncoded) throws Exception {
+        AlgorithmParameters params = AlgorithmParameters.getInstance("PSS");
+        params.init(spec);
+        byte[] encoded = params.getEncoded("ASN.1");
+        assertTrue(Arrays.equals(expectedEncoded, encoded));
+        // Assert that getEncoded() returns ASN.1 form.
+        assertTrue(Arrays.equals(encoded, params.getEncoded()));
+    }
+
+    @Test
+    public void testGetEncodedWithBrokenInput() throws Exception {
+        AlgorithmParameters params = AlgorithmParameters.getInstance("PSS");
+        try {
+            params.init(BROKEN_SPEC1_DER_ENCODED);
+            fail();
+        } catch (IOException expected) {
+        } catch (IllegalArgumentException expected) {
+            // Bouncy Castle incorrectly throws an IllegalArgumentException instead of IOException.
+            if (!"BC".equals(params.getProvider().getName())) {
+                throw new RuntimeException(
+                        "Unexpected exception. Provider: " + params.getProvider(),
+                        expected);
+            }
+        }
+
+        params = AlgorithmParameters.getInstance("PSS");
+        try {
+            params.init(BROKEN_SPEC2_DER_ENCODED);
+            fail();
+        } catch (IOException expected) {
+        } catch (IllegalArgumentException expected) {
+            // Bouncy Castle incorrectly throws an IllegalArgumentException instead of IOException.
+            if (!"BC".equals(params.getProvider().getName())) {
+                throw new RuntimeException(
+                        "Unexpected exception. Provider: " + params.getProvider(),
+                        expected);
+            }
+        }
+    }
+
+    private static void assertPSSParameterSpecEquals(
+        PSSParameterSpec spec1, PSSParameterSpec spec2) {
+        assertEquals(
+                getDigestAlgorithmCanonicalName(spec1.getDigestAlgorithm()),
+                getDigestAlgorithmCanonicalName(spec2.getDigestAlgorithm()));
+        assertEquals(
+                getMGFAlgorithmCanonicalName(spec1.getMGFAlgorithm()),
+                getMGFAlgorithmCanonicalName(spec2.getMGFAlgorithm()));
+
+        AlgorithmParameterSpec spec1MgfParams = spec1.getMGFParameters();
+        assertNotNull(spec1MgfParams);
+        if (!(spec1MgfParams instanceof MGF1ParameterSpec)) {
+            fail("Unexpected type of MGF parameters: " + spec1MgfParams.getClass().getName());
+        }
+        MGF1ParameterSpec spec1Mgf1Params = (MGF1ParameterSpec) spec1MgfParams;
+        AlgorithmParameterSpec spec2MgfParams = spec2.getMGFParameters();
+        assertNotNull(spec2MgfParams);
+        if (!(spec2MgfParams instanceof MGF1ParameterSpec)) {
+            fail("Unexpected type of MGF parameters: " + spec2MgfParams.getClass().getName());
+        }
+        MGF1ParameterSpec spec2Mgf1Params = (MGF1ParameterSpec) spec2MgfParams;
+
+        assertEquals(
+                getDigestAlgorithmCanonicalName(spec1Mgf1Params.getDigestAlgorithm()),
+                getDigestAlgorithmCanonicalName(spec2Mgf1Params.getDigestAlgorithm()));
+        assertEquals(spec1.getSaltLength(), spec2.getSaltLength());
+        assertEquals(spec1.getTrailerField(), spec2.getTrailerField());
+    }
+
+    // All the craziness with supporting OIDs is needed because Bouncy Castle, when parsing from
+    // ASN.1 form, returns PSSParameterSpec instances which use OIDs instead of JCA standard names
+    // for digest algorithms and MGF algorithms.
+    private static final Map<String, String> DIGEST_OID_TO_NAME = new TreeMap<String, String>(
+            String.CASE_INSENSITIVE_ORDER);
+    private static final Map<String, String> DIGEST_NAME_TO_OID = new TreeMap<String, String>(
+            String.CASE_INSENSITIVE_ORDER);
+
+    private static void addDigestOid(String algorithm, String oid) {
+        DIGEST_OID_TO_NAME.put(oid, algorithm);
+        DIGEST_NAME_TO_OID.put(algorithm, oid);
+    }
+
+    static {
+        addDigestOid("SHA-1", "1.3.14.3.2.26");
+        addDigestOid("SHA-224", "2.16.840.1.101.3.4.2.4");
+        addDigestOid("SHA-256", "2.16.840.1.101.3.4.2.1");
+        addDigestOid("SHA-384", "2.16.840.1.101.3.4.2.2");
+        addDigestOid("SHA-512", "2.16.840.1.101.3.4.2.3");
+    }
+
+    private static String getDigestAlgorithmCanonicalName(String algorithm) {
+        if (DIGEST_NAME_TO_OID.containsKey(algorithm)) {
+            return algorithm.toUpperCase(Locale.US);
+        }
+
+        String nameByOid = DIGEST_OID_TO_NAME.get(algorithm);
+        if (nameByOid != null) {
+            return nameByOid;
+        }
+
+        return algorithm;
+    }
+
+    private static String getMGFAlgorithmCanonicalName(String name) {
+        if ("MGF1".equalsIgnoreCase(name)) {
+            return name.toUpperCase(Locale.US);
+        } else if ("1.2.840.113549.1.1.8".equals(name)) {
+            return "MGF1";
+        } else {
+            return name;
+        }
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestAES.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestAES.java
new file mode 100644
index 0000000..df55013
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestAES.java
@@ -0,0 +1,91 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import com.android.org.conscrypt.TestUtils;
+import dalvik.system.VMRuntime;
+import java.security.AlgorithmParameters;
+import java.security.Provider;
+import javax.crypto.spec.IvParameterSpec;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
+import tests.util.ServiceTester;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class AlgorithmParametersTestAES extends AbstractAlgorithmParametersTest {
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
+    private static final byte[] parameterData = new byte[] {
+        (byte) 0x04, (byte) 0x08, (byte) 0x68, (byte) 0xC8,
+        (byte) 0xFF, (byte) 0x64, (byte) 0x72, (byte) 0xF5,
+        (byte) 0x04, (byte) 0x08, (byte) 0x68, (byte) 0xC8,
+        (byte) 0xFF, (byte) 0x64, (byte) 0x72, (byte) 0xF5 };
+
+    // See README.ASN1 for how to understand and reproduce this data
+
+    // asn1=FORMAT:HEX,OCTETSTRING:040868C8FF6472F5040868C8
+    private static final String ENCODED_DATA = "BBAECGjI/2Ry9QQIaMj/ZHL1";
+
+    public AlgorithmParametersTestAES() {
+        super("AES", new AlgorithmParameterSymmetricHelper("AES", "CBC/PKCS5PADDING", 128), new IvParameterSpec(parameterData));
+    }
+
+    @Test
+    public void testEncoding() throws Exception {
+        ServiceTester.test("AlgorithmParameters")
+                .withAlgorithm("AES")
+                .run(new ServiceTester.Test() {
+                    @Override
+                    public void test(Provider p, String algorithm) throws Exception {
+                        AlgorithmParameters params = AlgorithmParameters.getInstance("AES", p);
+
+                        params.init(new IvParameterSpec(parameterData));
+                        assertEquals(ENCODED_DATA, TestUtils.encodeBase64(params.getEncoded()));
+
+                        params = AlgorithmParameters.getInstance("AES", p);
+                        params.init(TestUtils.decodeBase64(ENCODED_DATA));
+                        assertArrayEquals(parameterData,
+                                params.getParameterSpec(IvParameterSpec.class).getIV());
+                    }
+                });
+    }
+
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestDES.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestDES.java
new file mode 100644
index 0000000..603981c
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestDES.java
@@ -0,0 +1,70 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import com.android.org.conscrypt.TestUtils;
+import java.security.AlgorithmParameters;
+import java.security.Provider;
+import javax.crypto.spec.IvParameterSpec;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import tests.util.ServiceTester;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class AlgorithmParametersTestDES extends AbstractAlgorithmParametersTest {
+
+    private static final byte[] parameterData = new byte[] {
+        (byte) 0x04, (byte) 0x08, (byte) 0x68, (byte) 0xC8,
+        (byte) 0xFF, (byte) 0x64, (byte) 0x72, (byte) 0xF5 };
+
+    // See README.ASN1 for how to understand and reproduce this data
+
+    // asn1=FORMAT:HEX,OCTETSTRING:040868C8FF6472F5
+    private static final String ENCODED_DATA = "BAgECGjI/2Ry9Q==";
+
+    public AlgorithmParametersTestDES() {
+        super("DES", new AlgorithmParameterSymmetricHelper("DES", "CBC/PKCS5PADDING", 56), new IvParameterSpec(parameterData));
+    }
+
+    @Test
+    public void testEncoding() throws Exception {
+        ServiceTester.test("AlgorithmParameters")
+                .withAlgorithm("DES")
+                .run(new ServiceTester.Test() {
+                    @Override
+                    public void test(Provider p, String algorithm) throws Exception {
+                        AlgorithmParameters params = AlgorithmParameters.getInstance("DES", p);
+
+                        params.init(new IvParameterSpec(parameterData));
+                        assertEquals(ENCODED_DATA, TestUtils.encodeBase64(params.getEncoded()));
+
+                        params = AlgorithmParameters.getInstance("DES", p);
+                        params.init(TestUtils.decodeBase64(ENCODED_DATA));
+                        assertArrayEquals(parameterData,
+                                params.getParameterSpec(IvParameterSpec.class).getIV());
+                    }
+                });
+    }
+
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestDESede.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestDESede.java
new file mode 100644
index 0000000..37ab2be
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestDESede.java
@@ -0,0 +1,89 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import com.android.org.conscrypt.TestUtils;
+import dalvik.system.VMRuntime;
+import java.security.AlgorithmParameters;
+import java.security.Provider;
+import javax.crypto.spec.IvParameterSpec;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
+import tests.util.ServiceTester;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class AlgorithmParametersTestDESede extends AbstractAlgorithmParametersTest {
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
+    private static final byte[] parameterData = new byte[] {
+        (byte) 0x04, (byte) 0x08, (byte) 0x68, (byte) 0xC8,
+        (byte) 0xFF, (byte) 0x64, (byte) 0x72, (byte) 0xF5 };
+
+    // See README.ASN1 for how to understand and reproduce this data
+
+    // asn1=FORMAT:HEX,OCTETSTRING:040868C8FF6472F5
+    private static final String ENCODED_DATA = "BAgECGjI/2Ry9Q==";
+
+    public AlgorithmParametersTestDESede() {
+        super("DESede", new AlgorithmParameterSymmetricHelper("DESede", "CBC/PKCS5PADDING", 112), new IvParameterSpec(parameterData));
+    }
+
+    @Test
+    public void testEncoding() throws Exception {
+        ServiceTester.test("AlgorithmParameters")
+                .withAlgorithm("DESEDE")
+                .run(new ServiceTester.Test() {
+                    @Override
+                    public void test(Provider p, String algorithm) throws Exception {
+                        AlgorithmParameters params = AlgorithmParameters.getInstance("DESede", p);
+
+                        params.init(new IvParameterSpec(parameterData));
+                        assertEquals(ENCODED_DATA, TestUtils.encodeBase64(params.getEncoded()));
+
+                        params = AlgorithmParameters.getInstance("DESede", p);
+                        params.init(TestUtils.decodeBase64(ENCODED_DATA));
+                        assertArrayEquals(parameterData,
+                                params.getParameterSpec(IvParameterSpec.class).getIV());
+                    }
+                });
+    }
+
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestDH.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestDH.java
new file mode 100644
index 0000000..b6429d0
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestDH.java
@@ -0,0 +1,67 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import java.math.BigInteger;
+import javax.crypto.spec.DHParameterSpec;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class AlgorithmParametersTestDH extends AbstractAlgorithmParametersTest {
+
+    private static final byte[] P = new byte[] {
+            (byte) 0x00, (byte) 0xB8, (byte) 0xA4, (byte) 0x06, (byte) 0x10,
+            (byte) 0xA2, (byte) 0x8B, (byte) 0xD2, (byte) 0xC0, (byte) 0xB6,
+            (byte) 0x87, (byte) 0xFF, (byte) 0x15, (byte) 0xBA, (byte) 0x18,
+            (byte) 0xE9, (byte) 0x7D, (byte) 0x77, (byte) 0x9F, (byte) 0xAF,
+            (byte) 0x6F, (byte) 0x0B, (byte) 0xA4, (byte) 0xB6, (byte) 0x2B,
+            (byte) 0x35, (byte) 0xE2, (byte) 0x01, (byte) 0x66, (byte) 0x41,
+            (byte) 0x05, (byte) 0xE7, (byte) 0x6A, (byte) 0x62, (byte) 0x19,
+            (byte) 0x94, (byte) 0x18, (byte) 0x46, (byte) 0xBA, (byte) 0x60,
+            (byte) 0x2E, (byte) 0x5A, (byte) 0x48, (byte) 0x6C, (byte) 0x4B,
+            (byte) 0xBF, (byte) 0x8C, (byte) 0xBF, (byte) 0xB9, (byte) 0xEE,
+            (byte) 0xCC, (byte) 0x35, (byte) 0x89, (byte) 0x18, (byte) 0x02,
+            (byte) 0x18, (byte) 0xFE, (byte) 0xF4, (byte) 0x02, (byte) 0x3B,
+            (byte) 0x5E, (byte) 0x8A, (byte) 0x42, (byte) 0xB3, (byte) 0x39};
+
+    private static final byte[] Q = new byte[] {
+            (byte) 0x00, (byte) 0x87, (byte) 0xE2, (byte) 0xD1, (byte) 0x8A,
+            (byte) 0x23, (byte) 0x90, (byte) 0x3A, (byte) 0x0F, (byte) 0xC8,
+            (byte) 0x38, (byte) 0xA8, (byte) 0x65, (byte) 0x35, (byte) 0x89,
+            (byte) 0x4F, (byte) 0x4B, (byte) 0xB3, (byte) 0xBF, (byte) 0x18,
+            (byte) 0x3C, (byte) 0x3B, (byte) 0xD8, (byte) 0x72, (byte) 0xC3,
+            (byte) 0xCF, (byte) 0xC9, (byte) 0xA7, (byte) 0x39, (byte) 0x7E,
+            (byte) 0x9C, (byte) 0x69, (byte) 0xDA, (byte) 0xDE, (byte) 0x8E,
+            (byte) 0x96, (byte) 0x9D, (byte) 0x44, (byte) 0xC1, (byte) 0x1E,
+            (byte) 0x58, (byte) 0xC7, (byte) 0xFC, (byte) 0x40, (byte) 0x1B,
+            (byte) 0xE8, (byte) 0x23, (byte) 0xF3, (byte) 0x6B, (byte) 0x95,
+            (byte) 0x68, (byte) 0x29, (byte) 0x93, (byte) 0x35, (byte) 0x05,
+            (byte) 0xC5, (byte) 0xCB, (byte) 0xB8, (byte) 0x57, (byte) 0x8F,
+            (byte) 0xB9, (byte) 0xC3, (byte) 0x36, (byte) 0x09, (byte) 0x51};
+
+    private static final int l = 511;
+
+    public AlgorithmParametersTestDH() {
+        super("DH", new AlgorithmParameterKeyAgreementHelper("DH"),
+                new DHParameterSpec(new BigInteger(P), new BigInteger(Q), l));
+    }
+
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestDSA.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestDSA.java
new file mode 100644
index 0000000..f6585fc
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestDSA.java
@@ -0,0 +1,152 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.org.conscrypt.TestUtils;
+import java.math.BigInteger;
+import java.security.AlgorithmParameters;
+import java.security.Provider;
+import java.security.spec.DSAParameterSpec;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import tests.util.ServiceTester;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class AlgorithmParametersTestDSA extends AbstractAlgorithmParametersTest {
+
+    /*
+     * Parameters generated with OpenSSL:
+     * openssl dsaparam -genkey 1024 -C
+     */
+    private static final byte[] P = new byte[] {
+            (byte) 0xE6, (byte) 0x41, (byte) 0x58, (byte) 0x77, (byte) 0x76,
+            (byte) 0x5A, (byte) 0x4A, (byte) 0x53, (byte) 0xF1, (byte) 0xD6,
+            (byte) 0xC8, (byte) 0x7D, (byte) 0x67, (byte) 0x1F, (byte) 0x2F,
+            (byte) 0xFA, (byte) 0xDE, (byte) 0xB7, (byte) 0xAA, (byte) 0xCD,
+            (byte) 0xD7, (byte) 0x5D, (byte) 0xD0, (byte) 0xE9, (byte) 0xB1,
+            (byte) 0xDA, (byte) 0xFE, (byte) 0x42, (byte) 0xBE, (byte) 0xCC,
+            (byte) 0x42, (byte) 0x52, (byte) 0x2E, (byte) 0x01, (byte) 0xD2,
+            (byte) 0x16, (byte) 0xB1, (byte) 0x5B, (byte) 0xC4, (byte) 0x42,
+            (byte) 0xF9, (byte) 0x55, (byte) 0x0F, (byte) 0xE2, (byte) 0xD5,
+            (byte) 0x01, (byte) 0xD2, (byte) 0x7E, (byte) 0x22, (byte) 0xF6,
+            (byte) 0xC1, (byte) 0xFE, (byte) 0x5C, (byte) 0x6A, (byte) 0xCF,
+            (byte) 0x82, (byte) 0x1B, (byte) 0x5C, (byte) 0x46, (byte) 0x66,
+            (byte) 0x8B, (byte) 0xAF, (byte) 0xDF, (byte) 0x44, (byte) 0xE2,
+            (byte) 0x0E, (byte) 0xA3, (byte) 0x58, (byte) 0xF7, (byte) 0xA3,
+            (byte) 0x24, (byte) 0xE3, (byte) 0x84, (byte) 0xA6, (byte) 0x16,
+            (byte) 0xE0, (byte) 0xCA, (byte) 0x72, (byte) 0x55, (byte) 0x07,
+            (byte) 0xA0, (byte) 0x99, (byte) 0x7B, (byte) 0xF8, (byte) 0xB1,
+            (byte) 0x5A, (byte) 0x84, (byte) 0x36, (byte) 0x5A, (byte) 0xC8,
+            (byte) 0x6A, (byte) 0xFE, (byte) 0xA6, (byte) 0xB4, (byte) 0x1B,
+            (byte) 0x3A, (byte) 0x0A, (byte) 0x00, (byte) 0x6B, (byte) 0x72,
+            (byte) 0xDC, (byte) 0x0C, (byte) 0xD1, (byte) 0x09, (byte) 0x25,
+            (byte) 0x11, (byte) 0x68, (byte) 0x6B, (byte) 0x75, (byte) 0xDE,
+            (byte) 0x2C, (byte) 0x1A, (byte) 0xC2, (byte) 0x3A, (byte) 0xCB,
+            (byte) 0xA0, (byte) 0x17, (byte) 0xCA, (byte) 0x2D, (byte) 0xEE,
+            (byte) 0xA2, (byte) 0x5A, (byte) 0x9D, (byte) 0x1F, (byte) 0x33,
+            (byte) 0x1B, (byte) 0x07, (byte) 0x6D,
+    };
+    private static final byte[] Q = new byte[] {
+            (byte) 0x9B, (byte) 0x39, (byte) 0xD0, (byte) 0x02, (byte) 0x0F,
+            (byte) 0xE9, (byte) 0x96, (byte) 0x16, (byte) 0xC5, (byte) 0x25,
+            (byte) 0xF7, (byte) 0x94, (byte) 0xA9, (byte) 0x2C, (byte) 0xD0,
+            (byte) 0x25, (byte) 0x5B, (byte) 0x6E, (byte) 0xE0, (byte) 0x8F,
+    };
+    private static final byte[] G = new byte[] {
+            (byte) 0x5E, (byte) 0x9C, (byte) 0x95, (byte) 0x5F, (byte) 0x7E,
+            (byte) 0x91, (byte) 0x47, (byte) 0x4D, (byte) 0x68, (byte) 0xA4,
+            (byte) 0x1C, (byte) 0x44, (byte) 0x3B, (byte) 0xEC, (byte) 0x0A,
+            (byte) 0x7E, (byte) 0x59, (byte) 0x54, (byte) 0xF7, (byte) 0xEF,
+            (byte) 0x42, (byte) 0xFB, (byte) 0x63, (byte) 0x95, (byte) 0x08,
+            (byte) 0x2F, (byte) 0x4A, (byte) 0xD3, (byte) 0xBC, (byte) 0x79,
+            (byte) 0x9D, (byte) 0xBA, (byte) 0xD8, (byte) 0x8A, (byte) 0x83,
+            (byte) 0x84, (byte) 0xAE, (byte) 0x5B, (byte) 0x26, (byte) 0x80,
+            (byte) 0xB3, (byte) 0xFB, (byte) 0x9C, (byte) 0xA3, (byte) 0xCF,
+            (byte) 0xF4, (byte) 0x0A, (byte) 0xD5, (byte) 0xB6, (byte) 0x65,
+            (byte) 0x65, (byte) 0x1A, (byte) 0x4F, (byte) 0xC0, (byte) 0x86,
+            (byte) 0x3B, (byte) 0xE6, (byte) 0xFB, (byte) 0x4E, (byte) 0x9E,
+            (byte) 0x49, (byte) 0x0A, (byte) 0x8C, (byte) 0x77, (byte) 0x2D,
+            (byte) 0x93, (byte) 0x0B, (byte) 0xCA, (byte) 0x81, (byte) 0x07,
+            (byte) 0x09, (byte) 0xC4, (byte) 0x71, (byte) 0xFD, (byte) 0xC8,
+            (byte) 0xC7, (byte) 0xD1, (byte) 0xA3, (byte) 0xD0, (byte) 0xBB,
+            (byte) 0x7D, (byte) 0x92, (byte) 0x74, (byte) 0x8B, (byte) 0x3B,
+            (byte) 0x2A, (byte) 0x45, (byte) 0x1F, (byte) 0x5D, (byte) 0x85,
+            (byte) 0x90, (byte) 0xE3, (byte) 0xFB, (byte) 0x0E, (byte) 0x16,
+            (byte) 0xBA, (byte) 0x8A, (byte) 0xDE, (byte) 0x10, (byte) 0x0F,
+            (byte) 0xE0, (byte) 0x0F, (byte) 0x37, (byte) 0xA7, (byte) 0xC1,
+            (byte) 0xDC, (byte) 0xBC, (byte) 0x00, (byte) 0xB8, (byte) 0x24,
+            (byte) 0x0F, (byte) 0xF6, (byte) 0x5F, (byte) 0xB1, (byte) 0xA8,
+            (byte) 0x9A, (byte) 0xDB, (byte) 0x9F, (byte) 0x36, (byte) 0x54,
+            (byte) 0x45, (byte) 0xBD, (byte) 0xC0, (byte) 0xE8, (byte) 0x27,
+            (byte) 0x82, (byte) 0xC9, (byte) 0x75,
+    };
+
+    // The ASN.1 module for DSA is defined in RFC 3279 section 3. See README.ASN1 for how
+    // to understand and reproduce this data.
+
+    // asn1=SEQUENCE:dsa
+    // [dsa]
+    // p=INT:0xE6415877765A4A53F1D6C87D671F2FFADEB7AACDD75DD0E9B1DAFE42BECC42522E01D216B15BC442F9550FE2D501D27E22F6C1FE5C6ACF821B5C46668BAFDF44E20EA358F7A324E384A616E0CA725507A0997BF8B15A84365AC86AFEA6B41B3A0A006B72DC0CD1092511686B75DE2C1AC23ACBA017CA2DEEA25A9D1F331B076D
+    // q=INT:0x9B39D0020FE99616C525F794A92CD0255B6EE08F
+    // g=INT:0x5E9C955F7E91474D68A41C443BEC0A7E5954F7EF42FB6395082F4AD3BC799DBAD88A8384AE5B2680B3FB9CA3CFF40AD5B665651A4FC0863BE6FB4E9E490A8C772D930BCA810709C471FDC8C7D1A3D0BB7D92748B3B2A451F5D8590E3FB0E16BA8ADE100FE00F37A7C1DCBC00B8240FF65FB1A89ADB9F365445BDC0E82782C975
+    private static final String ENCODED_DATA = "MIIBHgKBgQDmQVh3dlpKU/HWyH1nHy/63reqzddd0Omx2v5"
+            + "CvsxCUi4B0haxW8RC+VUP4tUB0n4i9sH+XGrPghtcRmaLr99E4g6jWPejJOOEphbgynJVB6CZe/ixWoQ"
+            + "2Wshq/qa0GzoKAGty3AzRCSURaGt13iwawjrLoBfKLe6iWp0fMxsHbQIVAJs50AIP6ZYWxSX3lKks0CV"
+            + "bbuCPAoGAXpyVX36RR01opBxEO+wKfllU9+9C+2OVCC9K07x5nbrYioOErlsmgLP7nKPP9ArVtmVlGk/"
+            + "Ahjvm+06eSQqMdy2TC8qBBwnEcf3Ix9Gj0Lt9knSLOypFH12FkOP7Dha6it4QD+APN6fB3LwAuCQP9l+"
+            + "xqJrbnzZURb3A6CeCyXU=";
+
+    public AlgorithmParametersTestDSA() {
+        super("DSA", new AlgorithmParameterSignatureHelper<DSAParameterSpec>(
+                "DSA", DSAParameterSpec.class), new DSAParameterSpec(
+                new BigInteger(1, P), new BigInteger(1, Q), new BigInteger(1, G)));
+    }
+
+    @Test
+    public void testEncoding() throws Exception {
+        ServiceTester.test("AlgorithmParameters")
+                .withAlgorithm("DSA")
+                .run(new ServiceTester.Test() {
+                    @Override
+                    public void test(Provider p, String algorithm) throws Exception {
+                        AlgorithmParameters params = AlgorithmParameters.getInstance("DSA", p);
+
+                        DSAParameterSpec spec = new DSAParameterSpec(
+                                new BigInteger(1, P), new BigInteger(1, Q), new BigInteger(1, G));
+
+                        params.init(spec);
+                        assertEquals(ENCODED_DATA, TestUtils.encodeBase64(params.getEncoded()));
+
+                        params = AlgorithmParameters.getInstance("DSA", p);
+                        params.init(TestUtils.decodeBase64(ENCODED_DATA));
+                        DSAParameterSpec derivedSpec =
+                                params.getParameterSpec(DSAParameterSpec.class);
+
+                        assertEquals(new BigInteger(1, P), derivedSpec.getP());
+                        assertEquals(new BigInteger(1, Q), derivedSpec.getQ());
+                        assertEquals(new BigInteger(1, G), derivedSpec.getG());
+                    }
+                });
+    }
+
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestGCM.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestGCM.java
new file mode 100644
index 0000000..7ab64bc
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestGCM.java
@@ -0,0 +1,120 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.org.conscrypt.TestUtils;
+import dalvik.system.VMRuntime;
+import java.security.AlgorithmParameters;
+import java.security.Provider;
+import javax.crypto.spec.GCMParameterSpec;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
+import tests.util.ServiceTester;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class AlgorithmParametersTestGCM extends AbstractAlgorithmParametersTest {
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
+    private static final byte[] IV = new byte[] {
+        (byte) 0x04, (byte) 0x08, (byte) 0x68, (byte) 0xC8,
+        (byte) 0xFF, (byte) 0x64, (byte) 0x72, (byte) 0xF5,
+        (byte) 0x04, (byte) 0x08, (byte) 0x68, (byte) 0xC8 };
+
+    private static final int TLEN = 96;
+    private static final int SUN_ALT_TLEN = 128;
+
+    // The ASN.1 encoding for GCM params (specified in RFC 5084 section 3.2) specifies
+    // a default value of 12 for TLEN, so both values with and without TLEN should work.
+    // See README.ASN1 for how to understand and reproduce this data.
+
+    // asn1=SEQUENCE:gcm
+    // [gcm]
+    // iv=FORMAT:HEX,OCTETSTRING:040868C8FF6472F5040868C8
+    private static final String ENCODED_DATA_NO_TLEN = "MA4EDAQIaMj/ZHL1BAhoyA==";
+
+    // asn1=SEQUENCE:gcm
+    // [gcm]
+    // iv=FORMAT:HEX,OCTETSTRING:040868C8FF6472F5040868C8
+    // tlen=INT:12
+    private static final String ENCODED_DATA_TLEN = "MBEEDAQIaMj/ZHL1BAhoyAIBDA==";
+
+    public AlgorithmParametersTestGCM() {
+        super("GCM", new AlgorithmParameterSymmetricHelper("AES", "GCM/NOPADDING", 128), new GCMParameterSpec(TLEN, IV));
+    }
+
+    @Test
+    public void testEncoding() throws Exception {
+        ServiceTester.test("AlgorithmParameters")
+                .withAlgorithm("GCM")
+                .run(new ServiceTester.Test() {
+                    @Override
+                    public void test(Provider p, String algorithm) throws Exception {
+                        AlgorithmParameters params = AlgorithmParameters.getInstance("GCM", p);
+
+                        params.init(new GCMParameterSpec(TLEN, IV));
+                        String encoded = TestUtils.encodeBase64(params.getEncoded());
+                        assertTrue("Encoded: " + encoded,
+                                encoded.equals(ENCODED_DATA_TLEN)
+                                        || encoded.equals(ENCODED_DATA_NO_TLEN));
+
+                        params = AlgorithmParameters.getInstance("GCM", p);
+                        params.init(TestUtils.decodeBase64(ENCODED_DATA_NO_TLEN));
+                        GCMParameterSpec spec = params.getParameterSpec(GCMParameterSpec.class);
+                        if (!p.getName().equals("SunJCE")) {
+                            assertEquals(TLEN, spec.getTLen());
+                        } else {
+                            // In some cases the SunJCE provider uses 128 as the default instead of
+                            // 96
+                            assertTrue(spec.getTLen() == TLEN || spec.getTLen() == SUN_ALT_TLEN);
+                        }
+                        assertArrayEquals(IV, spec.getIV());
+
+                        params = AlgorithmParameters.getInstance("GCM", p);
+                        params.init(TestUtils.decodeBase64(ENCODED_DATA_TLEN));
+                        spec = params.getParameterSpec(GCMParameterSpec.class);
+                        assertEquals(TLEN, spec.getTLen());
+                        assertArrayEquals(IV, spec.getIV());
+                    }
+                });
+    }
+
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestOAEP.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestOAEP.java
new file mode 100644
index 0000000..2176981
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/AlgorithmParametersTestOAEP.java
@@ -0,0 +1,259 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import com.android.org.conscrypt.TestUtils;
+import dalvik.system.VMRuntime;
+import java.security.AlgorithmParameters;
+import java.security.Provider;
+import java.security.spec.MGF1ParameterSpec;
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.crypto.spec.PSource;
+import javax.crypto.spec.PSource.PSpecified;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
+import tests.util.ServiceTester;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class AlgorithmParametersTestOAEP extends AbstractAlgorithmParametersTest {
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
+    // The ASN.1 encoding for OAEP params (specified in RFC 4055 section 4.1) specifies
+    // default values for all parameters, so we need to consider encodings with those
+    // values both explicitly specified and unspecified.  When encoding values, it is required
+    // that default values are left empty, but implementations must be able to parse explicitly-
+    // specified defaults as well.
+    //
+    // See README.ASN1 for how to understand and reproduce this data.
+
+    // asn1=SEQUENCE
+    private static final String ENCODED_DATA_ALL_DEFAULTS = "MAA=";
+
+    // asn1=SEQUENCE:oaep
+    // [oaep]
+    // hashFunc=EXP:0,SEQUENCE:sha1
+    // maskGenFunc=EXP:1,SEQUENCE:mgf1
+    // pSourceFunc=EXP:2,SEQUENCE:pSpecified
+    // [mgf1]
+    // oid=OID:1.2.840.113549.1.1.8
+    // params=SEQUENCE:sha1
+    // [pSpecified]
+    // oid=OID:1.2.840.113549.1.1.9
+    // val=OCTETSTRING:
+    // [sha1]
+    // oid=OID:sha1
+    // params=NULL
+    private static final String ENCODED_DATA_EXPLICIT_DEFAULTS =
+            "MDigCzAJBgUrDgMCGgUAoRgwFgYJKoZIhvcNAQEIMAkGBSsOAwIaBQCiDzANBgkqhkiG9w0BAQkEAA==";
+
+    // Base64 version of ASN.1-encoded data with none of the default values.  Specifically:
+    // SHA-256 hashFunc, MGF1-SHA-384 maskGenFunc, and [1, 2, 3, 4] pSourceFunc
+
+    // asn1=SEQUENCE:oaep
+    // [oaep]
+    // hashFunc=EXP:0,SEQUENCE:sha256
+    // maskGenFunc=EXP:1,SEQUENCE:mgf1
+    // pSourceFunc=EXP:2,SEQUENCE:pSpecified
+    // [sha256]
+    // oid=OID:sha256
+    // params=NULL
+    // [mgf1]
+    // oid=OID:1.2.840.113549.1.1.8
+    // params=SEQUENCE:sha384
+    // [sha384]
+    // oid=OID:sha384
+    // params=NULL
+    // [pSpecified]
+    // oid=OID:1.2.840.113549.1.1.9
+    // val=FORMAT:HEX,OCTETSTRING:01020304
+    private static final String ENCODED_DATA_NON_DEFAULTS = "MESgDzANBglghkgBZQMEAgEFAKEc"
+            + "MBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgIFAKITMBEGCSqGSIb3DQEBCQQEAQIDBA==";
+
+    // Base64 version of ASN.1-encoded data with some default and some non-default values.
+    // Specifically, SHA-1 hashFunc (default), MGF1-SHA-512 maskGenFunc (non-default),
+    // empty pSourceFunc (default)
+
+    // asn1=SEQUENCE:oaep
+    // [oaep]
+    // maskGenFunc=EXP:1,SEQUENCE:mgf1
+    // [mgf1]
+    // oid=OID:1.2.840.113549.1.1.8
+    // params=SEQUENCE:sha512
+    // [sha512]
+    // oid=OID:sha512
+    // params=NULL
+    private static final String ENCODED_DATA_MIXED = "MB6hHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAIDBQA=";
+
+    // Base64 version of the same ASN.1-encoded data as ENCODED_DATA_MIXED, but with the
+    // default values explicitly specified.
+
+    // asn1=SEQUENCE:oaep
+    // [oaep]
+    // hashFunc=EXP:0,SEQUENCE:sha1
+    // maskGenFunc=EXP:1,SEQUENCE:mgf1
+    // pSourceFunc=EXP:2,SEQUENCE:pSpecified
+    // [sha1]
+    // oid=OID:sha1
+    // params=NULL
+    // [mgf1]
+    // oid=OID:1.2.840.113549.1.1.8
+    // params=SEQUENCE:sha512
+    // [pSpecified]
+    // oid=OID:1.2.840.113549.1.1.9
+    // val=OCTETSTRING:
+    // [sha512]
+    // oid=OID:sha512
+    // params=NULL
+    private static final String ENCODED_DATA_MIXED_EXPLICIT_DEFAULTS = "MDygCzAJBgUrDgMCGgUAoRww"
+            + "GgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAwUAog8wDQYJKoZIhvcNAQEJBAA=";
+
+    public AlgorithmParametersTestOAEP() {
+        super("OAEP", new AlgorithmParameterAsymmetricHelper("RSA/ECB/OAEPPadding"), new OAEPParameterSpec("SHA-1", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT));
+    }
+
+    @Test
+    public void testEncoding() throws Exception {
+        ServiceTester.test("AlgorithmParameters")
+                .withAlgorithm("OAEP")
+                .run(new ServiceTester.Test() {
+                    @Override
+                    public void test(Provider p, String algorithm) throws Exception {
+                        AlgorithmParameters params = AlgorithmParameters.getInstance("OAEP", p);
+
+                        OAEPParameterSpec spec = new OAEPParameterSpec("SHA-1", "MGF1",
+                                MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
+                        params.init(spec);
+                        if (!p.getName().equals("SunJCE")) {
+                            assertEquals(ENCODED_DATA_ALL_DEFAULTS,
+                                    TestUtils.encodeBase64(params.getEncoded()));
+                        } else {
+                            // SunJCE encodes the defaults explicitly, which is not allowed by RFC
+                            // 4055.
+                            assertEquals(ENCODED_DATA_EXPLICIT_DEFAULTS,
+                                    TestUtils.encodeBase64(params.getEncoded()));
+                        }
+
+                        params = AlgorithmParameters.getInstance("OAEP", p);
+                        spec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA384,
+                                new PSource.PSpecified(new byte[] {1, 2, 3, 4}));
+                        params.init(spec);
+                        assertEquals(ENCODED_DATA_NON_DEFAULTS,
+                                TestUtils.encodeBase64(params.getEncoded()));
+
+                        params = AlgorithmParameters.getInstance("OAEP", p);
+                        spec = new OAEPParameterSpec("SHA-1", "MGF1", MGF1ParameterSpec.SHA512,
+                                PSource.PSpecified.DEFAULT);
+                        params.init(spec);
+                        if (!p.getName().equals("SunJCE")) {
+                            assertEquals(ENCODED_DATA_MIXED,
+                                    TestUtils.encodeBase64(params.getEncoded()));
+                        } else {
+                            // SunJCE encodes the defaults explicitly, which is not allowed by RFC
+                            // 4055.
+                            assertEquals(ENCODED_DATA_MIXED_EXPLICIT_DEFAULTS,
+                                    TestUtils.encodeBase64(params.getEncoded()));
+                        }
+
+                        params = AlgorithmParameters.getInstance("OAEP", p);
+                        params.init(TestUtils.decodeBase64(ENCODED_DATA_ALL_DEFAULTS));
+                        OAEPParameterSpec producedSpec =
+                                params.getParameterSpec(OAEPParameterSpec.class);
+
+                        assertEquals("SHA-1", producedSpec.getDigestAlgorithm());
+                        assertEquals("MGF1", producedSpec.getMGFAlgorithm());
+                        assertEquals(MGF1ParameterSpec.SHA1.getDigestAlgorithm(),
+                                ((MGF1ParameterSpec) producedSpec.getMGFParameters())
+                                        .getDigestAlgorithm());
+                        assertArrayEquals(PSpecified.DEFAULT.getValue(),
+                                ((PSpecified) producedSpec.getPSource()).getValue());
+
+                        params = AlgorithmParameters.getInstance("OAEP", p);
+                        params.init(TestUtils.decodeBase64(ENCODED_DATA_EXPLICIT_DEFAULTS));
+                        producedSpec = params.getParameterSpec(OAEPParameterSpec.class);
+
+                        assertEquals("SHA-1", producedSpec.getDigestAlgorithm());
+                        assertEquals("MGF1", producedSpec.getMGFAlgorithm());
+                        assertEquals(MGF1ParameterSpec.SHA1.getDigestAlgorithm(),
+                                ((MGF1ParameterSpec) producedSpec.getMGFParameters())
+                                        .getDigestAlgorithm());
+                        assertArrayEquals(PSpecified.DEFAULT.getValue(),
+                                ((PSpecified) producedSpec.getPSource()).getValue());
+
+                        params = AlgorithmParameters.getInstance("OAEP", p);
+                        params.init(TestUtils.decodeBase64(ENCODED_DATA_NON_DEFAULTS));
+                        producedSpec = params.getParameterSpec(OAEPParameterSpec.class);
+
+                        assertEquals("SHA-256", producedSpec.getDigestAlgorithm());
+                        assertEquals("MGF1", producedSpec.getMGFAlgorithm());
+                        assertEquals(MGF1ParameterSpec.SHA384.getDigestAlgorithm(),
+                                ((MGF1ParameterSpec) producedSpec.getMGFParameters())
+                                        .getDigestAlgorithm());
+                        assertArrayEquals(new byte[] {1, 2, 3, 4},
+                                ((PSpecified) producedSpec.getPSource()).getValue());
+
+                        params = AlgorithmParameters.getInstance("OAEP", p);
+                        params.init(TestUtils.decodeBase64(ENCODED_DATA_MIXED));
+                        producedSpec = params.getParameterSpec(OAEPParameterSpec.class);
+
+                        assertEquals("SHA-1", producedSpec.getDigestAlgorithm());
+                        assertEquals("MGF1", producedSpec.getMGFAlgorithm());
+                        assertEquals(MGF1ParameterSpec.SHA512.getDigestAlgorithm(),
+                                ((MGF1ParameterSpec) producedSpec.getMGFParameters())
+                                        .getDigestAlgorithm());
+                        assertArrayEquals(PSpecified.DEFAULT.getValue(),
+                                ((PSpecified) producedSpec.getPSource()).getValue());
+
+                        params = AlgorithmParameters.getInstance("OAEP", p);
+                        params.init(TestUtils.decodeBase64(ENCODED_DATA_MIXED_EXPLICIT_DEFAULTS));
+                        producedSpec = params.getParameterSpec(OAEPParameterSpec.class);
+
+                        assertEquals("SHA-1", producedSpec.getDigestAlgorithm());
+                        assertEquals("MGF1", producedSpec.getMGFAlgorithm());
+                        assertEquals(MGF1ParameterSpec.SHA512.getDigestAlgorithm(),
+                                ((MGF1ParameterSpec) producedSpec.getMGFParameters())
+                                        .getDigestAlgorithm());
+                        assertArrayEquals(PSpecified.DEFAULT.getValue(),
+                                ((PSpecified) producedSpec.getPSource()).getValue());
+                    }
+                });
+    }
+
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestDH.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestDH.java
new file mode 100644
index 0000000..c316d94
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestDH.java
@@ -0,0 +1,39 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import java.security.KeyPair;
+import javax.crypto.spec.DHPrivateKeySpec;
+import javax.crypto.spec.DHPublicKeySpec;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class KeyFactoryTestDH extends AbstractKeyFactoryTest<DHPublicKeySpec, DHPrivateKeySpec> {
+
+    public KeyFactoryTestDH() {
+        super("DH", DHPublicKeySpec.class, DHPrivateKeySpec.class);
+    }
+
+    @Override
+    protected void check(KeyPair keyPair) throws Exception {
+        new KeyAgreementHelper("DH").test(keyPair);
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestDSA.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestDSA.java
new file mode 100644
index 0000000..d4e6984
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestDSA.java
@@ -0,0 +1,40 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import java.security.KeyPair;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class KeyFactoryTestDSA extends
+        AbstractKeyFactoryTest<DSAPublicKeySpec, DSAPrivateKeySpec> {
+
+    public KeyFactoryTestDSA() {
+        super("DSA", DSAPublicKeySpec.class, DSAPrivateKeySpec.class);
+    }
+
+    @Override
+    protected void check(KeyPair keyPair) throws Exception {
+        new SignatureHelper("DSA").test(keyPair);
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestRSA.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestRSA.java
new file mode 100644
index 0000000..15593ca
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyFactoryTestRSA.java
@@ -0,0 +1,65 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class KeyFactoryTestRSA extends
+        AbstractKeyFactoryTest<RSAPublicKeySpec, RSAPrivateKeySpec> {
+
+    @SuppressWarnings("unchecked")
+    public KeyFactoryTestRSA() {
+        super("RSA", RSAPublicKeySpec.class, RSAPrivateKeySpec.class);
+    }
+
+    @Override
+    protected void check(KeyPair keyPair) throws Exception {
+        new CipherAsymmetricCryptHelper("RSA").test(keyPair);
+    }
+
+    @Test
+    public void testExtraBufferSpace_Private() throws Exception {
+        PrivateKey privateKey = DefaultKeys.getPrivateKey("RSA");
+        byte[] encoded = privateKey.getEncoded();
+        byte[] longBuffer = new byte[encoded.length + 147];
+        System.arraycopy(encoded, 0, longBuffer, 0, encoded.length);
+        KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(longBuffer));
+    }
+
+    @Test
+    public void testExtraBufferSpace_Public() throws Exception {
+        PublicKey publicKey = DefaultKeys.getPublicKey("RSA");
+        byte[] encoded = publicKey.getEncoded();
+        byte[] longBuffer = new byte[encoded.length + 147];
+        System.arraycopy(encoded, 0, longBuffer, 0, encoded.length);
+        KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(longBuffer));
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTest.java
new file mode 100644
index 0000000..5687b44
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTest.java
@@ -0,0 +1,468 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import com.android.org.conscrypt.TestUtils;
+import dalvik.system.VMRuntime;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.DSAParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
+import tests.util.ServiceTester;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class KeyPairGeneratorTest {
+
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
+    @Test
+    public void test_getInstance() throws Exception {
+        ServiceTester
+                .test("KeyPairGenerator")
+                // Do not test AndroidKeyStore Provider. It does not accept vanilla public keys for
+                // signature verification. It's OKish not to test here because it's tested by
+                // cts/tests/tests/keystore.
+                .skipProvider("AndroidKeyStore")
+                // The SunEC provider tries to pass a sun-only AlgorithmParameterSpec to the default
+                // AlgorithmParameters:EC when its KeyPairGenerator is initialized.  Since Conscrypt
+                // is the highest-ranked provider when running our tests, its implementation of
+                // AlgorithmParameters:EC is returned, and it doesn't understand the special
+                // AlgorithmParameterSpec, so the KeyPairGenerator can't be initialized.
+                .skipProvider("SunEC")
+                // The SunPKCS11-NSS provider on OpenJDK 7 attempts to delegate to the SunEC
+                // provider, which doesn't exist on OpenJDK 7, and thus totally fails.  This appears
+                // to be a bug introduced into later revisions of OpenJDK 7.
+                .skipProvider("SunPKCS11-NSS")
+                .run(new ServiceTester.Test() {
+                    @Override
+                    public void test(Provider provider, String algorithm) throws Exception {
+                        AlgorithmParameterSpec params = null;
+
+                        if ("DH".equals(algorithm) || "DiffieHellman".equalsIgnoreCase(algorithm)) {
+                            params = getDHParams();
+                        }
+                        // KeyPairGenerator.getInstance(String)
+                        KeyPairGenerator kpg1 = KeyPairGenerator.getInstance(algorithm);
+                        assertEquals(algorithm, kpg1.getAlgorithm());
+                        if (params != null) {
+                            kpg1.initialize(params);
+                        }
+                        test_KeyPairGenerator(kpg1);
+
+                        // KeyPairGenerator.getInstance(String, Provider)
+                        KeyPairGenerator kpg2 = KeyPairGenerator.getInstance(algorithm, provider);
+                        assertEquals(algorithm, kpg2.getAlgorithm());
+                        assertEquals(provider, kpg2.getProvider());
+                        if (params != null) {
+                            kpg2.initialize(params);
+                        }
+                        test_KeyPairGenerator(kpg2);
+
+                        // KeyPairGenerator.getInstance(String, String)
+                        KeyPairGenerator kpg3 =
+                                KeyPairGenerator.getInstance(algorithm, provider.getName());
+                        assertEquals(algorithm, kpg3.getAlgorithm());
+                        assertEquals(provider, kpg3.getProvider());
+                        if (params != null) {
+                            kpg3.initialize(params);
+                        }
+                        test_KeyPairGenerator(kpg3);
+                    }
+                });
+    }
+
+    private static final Map<String, List<Integer>> KEY_SIZES
+            = new HashMap<String, List<Integer>>();
+    private static void putKeySize(String algorithm, int keySize) {
+        algorithm = algorithm.toUpperCase();
+        List<Integer> keySizes = KEY_SIZES.get(algorithm);
+        if (keySizes == null) {
+            keySizes = new ArrayList<Integer>();
+            KEY_SIZES.put(algorithm, keySizes);
+        }
+        keySizes.add(keySize);
+    }
+    private static List<Integer> getKeySizes(String algorithm) throws Exception {
+        algorithm = algorithm.toUpperCase();
+        List<Integer> keySizes = KEY_SIZES.get(algorithm);
+        if (keySizes == null) {
+            throw new Exception("Unknown key sizes for KeyPairGenerator." + algorithm);
+        }
+        return keySizes;
+    }
+    static {
+        putKeySize("DSA", 512);
+        putKeySize("DSA", 512+64);
+        putKeySize("DSA", 1024);
+        putKeySize("RSA", 512);
+        putKeySize("RSASSA-PSS", 512);
+        putKeySize("DH", 512);
+        putKeySize("DH", 512+64);
+        putKeySize("DH", 1024);
+        putKeySize("DiffieHellman", 512);
+        putKeySize("DiffieHellman", 512+64);
+        putKeySize("DiffieHellman", 1024);
+        putKeySize("EC", 224);
+        putKeySize("EC", 256);
+        putKeySize("EC", 384);
+        putKeySize("EC", 521);
+    }
+
+    /** Elliptic Curve Crypto named curves that should be supported. */
+    private static final String[] EC_NAMED_CURVES = {
+        // NIST P-256 aka SECG secp256r1 aka ANSI X9.62 prime256v1
+        "secp256r1", "prime256v1",
+        // NIST P-521 aka SECG secp521r1
+        "secp521r1",
+    };
+
+    private void test_KeyPairGenerator(KeyPairGenerator kpg) throws Exception {
+        // without a call to initialize
+        test_KeyPair(kpg, kpg.genKeyPair());
+        test_KeyPair(kpg, kpg.generateKeyPair());
+
+        String algorithm = kpg.getAlgorithm();
+
+        // TODO: detect if we're running in vogar and run the full test
+        if ("DH".equals(algorithm) || "DiffieHellman".equalsIgnoreCase(algorithm)) {
+            // Disabled because this takes too long on devices.
+            // TODO: Re-enable DH test. http://b/5513723.
+            return;
+        }
+
+        List<Integer> keySizes = getKeySizes(algorithm);
+        for (int keySize : keySizes) {
+            // TODO(flooey): Remove when we don't support Java 6 anymore
+            if ("DSA".equals(algorithm)
+                    && ("SUN".equalsIgnoreCase(kpg.getProvider().getName())
+                        || "SunPKCS11-NSS".equalsIgnoreCase(kpg.getProvider().getName()))
+                    && keySize != 512 && keySize != 1024) {
+                // The Sun provider doesn't support DSA in all the key sizes, so ignore
+                // the uncommon ones.
+                continue;
+            }
+            if ("EC".equals(algorithm)
+                    && "SunPKCS11-NSS".equalsIgnoreCase(kpg.getProvider().getName())
+                    && keySize == 224) {
+                // TODO(flooey): Remove when we stop supporting Java 6
+                // This Sun provider doesn't support 224-bit EC keys
+                continue;
+            }
+            kpg.initialize(keySize);
+            test_KeyPair(kpg, kpg.genKeyPair());
+            test_KeyPair(kpg, kpg.generateKeyPair());
+
+            kpg.initialize(keySize, (SecureRandom) null);
+            test_KeyPair(kpg, kpg.genKeyPair());
+            test_KeyPair(kpg, kpg.generateKeyPair());
+
+            kpg.initialize(keySize, new SecureRandom());
+            test_KeyPair(kpg, kpg.genKeyPair());
+            test_KeyPair(kpg, kpg.generateKeyPair());
+        }
+
+        if ("EC".equals(algorithm) || "ECDH".equals(algorithm) || "ECDSA".equals(algorithm)) {
+            if ("SunPKCS11-NSS".equalsIgnoreCase(kpg.getProvider().getName())) {
+                // SunPKCS11 doesn't support some of the named curves that we expect, so it
+                // fails.  Skip it.
+                return;
+            }
+            for (String curveName : EC_NAMED_CURVES) {
+                kpg.initialize(new ECGenParameterSpec(curveName));
+                test_KeyPair(kpg, kpg.genKeyPair());
+                test_KeyPair(kpg, kpg.generateKeyPair());
+
+                kpg.initialize(new ECGenParameterSpec(curveName), (SecureRandom) null);
+                test_KeyPair(kpg, kpg.genKeyPair());
+                test_KeyPair(kpg, kpg.generateKeyPair());
+
+                kpg.initialize(new ECGenParameterSpec(curveName), new SecureRandom());
+                test_KeyPair(kpg, kpg.genKeyPair());
+                test_KeyPair(kpg, kpg.generateKeyPair());
+            }
+        }
+    }
+
+    private void test_KeyPair(KeyPairGenerator kpg, KeyPair kp) throws Exception {
+        assertNotNull(kp);
+        test_Key(kpg, kp.getPrivate());
+        test_Key(kpg, kp.getPublic());
+    }
+
+    private void test_Key(KeyPairGenerator kpg, Key k) throws Exception {
+        String expectedAlgorithm = kpg.getAlgorithm().toUpperCase(Locale.ROOT);
+        if (StandardNames.IS_RI && expectedAlgorithm.equals("DIFFIEHELLMAN")) {
+            expectedAlgorithm = "DH";
+        }
+        assertEquals(expectedAlgorithm, k.getAlgorithm().toUpperCase());
+        if (expectedAlgorithm.equals("DH")) {
+            if (k instanceof DHPublicKey) {
+                DHPublicKey dhPub = (DHPublicKey) k;
+                assertEquals(dhPub.getParams().getP(), getDHParams().getP());
+            } else if (k instanceof DHPrivateKey) {
+                DHPrivateKey dhPriv = (DHPrivateKey) k;
+                assertEquals(dhPriv.getParams().getP(), getDHParams().getP());
+            } else {
+                fail("not a public or private key!?");
+            }
+        }
+        if (kpg.getProvider().getName().equalsIgnoreCase("SunMSCAPI")
+                && expectedAlgorithm.equals("RSA")) {
+            // The SunMSCAPI RSA keys don't provide encoded versions at all, nor are they
+            // serializable, so just skip them.
+            return;
+        }
+        assertNotNull(k.getEncoded());
+        assertNotNull(k.getFormat());
+
+        // Test serialization
+        // SunPKCS11-NSS on Java 6 replaces serialized keys with one from the default provider,
+        // so the deserialized key doesn't match the original.
+        if (!kpg.getProvider().getName().equalsIgnoreCase("SunPKCS11-NSS")) {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream(16384);
+            ObjectOutputStream oos = new ObjectOutputStream(baos);
+            oos.writeObject(k);
+
+            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+            ObjectInputStream ois = new ObjectInputStream(bais);
+            Key inflatedKey = (Key) ois.readObject();
+
+            assertEquals("Provider: " + kpg.getProvider(), k, inflatedKey);
+        }
+
+        test_KeyWithAllKeyFactories(k);
+    }
+
+    private void test_KeyWithAllKeyFactories(Key k) throws Exception {
+        byte[] encoded = k.getEncoded();
+
+        String keyAlgo = k.getAlgorithm();
+        for (Provider p : Security.getProviders()) {
+            Set<Provider.Service> services = p.getServices();
+            for (Provider.Service service : services) {
+                if (!"KeyFactory".equals(service.getType())) {
+                    continue;
+                }
+                if (!service.getAlgorithm().equals(keyAlgo)) {
+                    continue;
+                }
+                if (p.getName().equalsIgnoreCase("AndroidKeyStore")) {
+                    // AndroidKeyStore only works with its own keys
+                    continue;
+                }
+                if ("EC".equals(k.getAlgorithm())
+                        && "SunPKCS11-NSS".equalsIgnoreCase(p.getName())) {
+                    // SunPKCS11 doesn't support some of the named curves that we expect, so it
+                    // fails.  Skip testing that combination.
+                    continue;
+                }
+                if ("DH".equals(k.getAlgorithm())
+                        && "SunPKCS11-NSS".equalsIgnoreCase(p.getName())) {
+                    // SunPKCS11 omits the privateValueLength field from the DHParameter
+                    // structure in its encoded keys, unlike every other provider seen,
+                    // so ignore it.
+                    continue;
+                }
+
+                if ("PKCS#8".equals(k.getFormat())) {
+                    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(encoded);
+                    KeyFactory kf = KeyFactory.getInstance(k.getAlgorithm(), p);
+                    PrivateKey privKey = kf.generatePrivate(spec);
+                    assertNotNull(k.getAlgorithm() + ", provider=" + p.getName(), privKey);
+
+                    /*
+                     * EC keys are unique because they can have explicit parameters or a curve
+                     * name. Check them specially so this test can continue to function.
+                     */
+                    if (k instanceof ECPrivateKey) {
+                        assertECPrivateKeyEquals((ECPrivateKey) k, (ECPrivateKey) privKey);
+                    } else {
+                        assertEquals(k.getAlgorithm() + ", provider=" + p.getName(),
+                                TestUtils.encodeBase64(encoded),
+                                TestUtils.encodeBase64(privKey.getEncoded()));
+                    }
+                } else if ("X.509".equals(k.getFormat())) {
+                    X509EncodedKeySpec spec = new X509EncodedKeySpec(encoded);
+                    KeyFactory kf = KeyFactory.getInstance(k.getAlgorithm(), p);
+                    PublicKey pubKey = kf.generatePublic(spec);
+                    assertNotNull(pubKey);
+                    assertEquals(k.getAlgorithm() + ", provider=" + p.getName(),
+                            TestUtils.encodeBase64(encoded),
+                            TestUtils.encodeBase64(pubKey.getEncoded()));
+                }
+            }
+        }
+    }
+
+    private static void assertECPrivateKeyEquals(ECPrivateKey expected, ECPrivateKey actual) {
+        assertEquals(expected.getS(), actual.getS());
+        assertECParametersEquals(expected.getParams(), actual.getParams());
+    }
+
+    private static void assertECParametersEquals(ECParameterSpec expected, ECParameterSpec actual) {
+        assertEquals(expected.getCurve(), actual.getCurve());
+        assertEquals(expected.getGenerator(), actual.getGenerator());
+        assertEquals(expected.getOrder(), actual.getOrder());
+        assertEquals(expected.getCofactor(), actual.getCofactor());
+    }
+
+    /**
+     * DH parameters pre-generated so that the test doesn't take too long.
+     * These parameters were generated with:
+     *
+     * openssl gendh 512 | openssl dhparams -C
+     */
+    private static DHParameterSpec getDHParams() {
+        BigInteger p = new BigInteger("E7AB1768BD75CD24700960FFA32D3F1557344E587101237532CC641646ED7A7C104743377F6D46251698B665CE2A6CBAB6714C2569A7D2CA22C0CF03FA40AC93", 16);
+        BigInteger g = new BigInteger("02", 16);
+        return new DHParameterSpec(p, g, 512);
+    }
+
+    private static final BigInteger DSA_P = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0x9e, (byte) 0x61, (byte) 0xc2, (byte) 0x89, (byte) 0xef, (byte) 0x77, (byte) 0xa9,
+        (byte) 0x4e, (byte) 0x13, (byte) 0x67, (byte) 0x64, (byte) 0x1f, (byte) 0x09, (byte) 0x01, (byte) 0xfe,
+        (byte) 0x24, (byte) 0x13, (byte) 0x53, (byte) 0xe0, (byte) 0xb7, (byte) 0x90, (byte) 0xa8, (byte) 0x4e,
+        (byte) 0x76, (byte) 0xfe, (byte) 0x89, (byte) 0x82, (byte) 0x7f, (byte) 0x7a, (byte) 0xc5, (byte) 0x3c,
+        (byte) 0x4e, (byte) 0x0c, (byte) 0x20, (byte) 0x55, (byte) 0x30, (byte) 0x95, (byte) 0x42, (byte) 0x85,
+        (byte) 0xe1, (byte) 0x40, (byte) 0x7d, (byte) 0x27, (byte) 0x8f, (byte) 0x07, (byte) 0x0d, (byte) 0xe8,
+        (byte) 0xdc, (byte) 0x99, (byte) 0xef, (byte) 0xb3, (byte) 0x07, (byte) 0x94, (byte) 0x34, (byte) 0xd6,
+        (byte) 0x7c, (byte) 0xff, (byte) 0x9c, (byte) 0xbe, (byte) 0x69, (byte) 0xd3, (byte) 0xeb, (byte) 0x44,
+        (byte) 0x37, (byte) 0x50, (byte) 0xef, (byte) 0x49, (byte) 0xf8, (byte) 0xe2, (byte) 0x5b, (byte) 0xd8,
+        (byte) 0xd1, (byte) 0x10, (byte) 0x84, (byte) 0x97, (byte) 0xea, (byte) 0xe3, (byte) 0xa5, (byte) 0x1c,
+        (byte) 0xc0, (byte) 0x4e, (byte) 0x69, (byte) 0xca, (byte) 0x70, (byte) 0x3d, (byte) 0x78, (byte) 0xb9,
+        (byte) 0x16, (byte) 0xe5, (byte) 0xfe, (byte) 0x61, (byte) 0x5d, (byte) 0x8a, (byte) 0x5a, (byte) 0xb3,
+        (byte) 0x2c, (byte) 0x61, (byte) 0xb6, (byte) 0x01, (byte) 0x3b, (byte) 0xd0, (byte) 0x01, (byte) 0x7c,
+        (byte) 0x32, (byte) 0x8d, (byte) 0xe1, (byte) 0xf3, (byte) 0x69, (byte) 0x0e, (byte) 0x8b, (byte) 0x58,
+        (byte) 0xc6, (byte) 0xcf, (byte) 0x00, (byte) 0x94, (byte) 0xf8, (byte) 0x49, (byte) 0x2a, (byte) 0x4b,
+        (byte) 0xea, (byte) 0xda, (byte) 0x00, (byte) 0xff, (byte) 0x4b, (byte) 0xd0, (byte) 0xbe, (byte) 0x40,
+        (byte) 0x23,
+    });
+
+    private static final BigInteger DSA_Q = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0xbf, (byte) 0xee, (byte) 0xaa, (byte) 0x0f, (byte) 0x12, (byte) 0x34, (byte) 0x50,
+        (byte) 0x72, (byte) 0xf8, (byte) 0x60, (byte) 0x13, (byte) 0xd8, (byte) 0xf1, (byte) 0x41, (byte) 0x01,
+        (byte) 0x10, (byte) 0xa5, (byte) 0x2f, (byte) 0x57, (byte) 0x5f,
+    });
+
+    private static final BigInteger DSA_G = new BigInteger(new byte[] {
+        (byte) 0x77, (byte) 0xd4, (byte) 0x7a, (byte) 0x12, (byte) 0xcc, (byte) 0x81, (byte) 0x7e, (byte) 0x7e,
+        (byte) 0xeb, (byte) 0x3a, (byte) 0xfb, (byte) 0xe6, (byte) 0x86, (byte) 0x6d, (byte) 0x5a, (byte) 0x10,
+        (byte) 0x1d, (byte) 0xad, (byte) 0xa9, (byte) 0x4f, (byte) 0xb9, (byte) 0x03, (byte) 0x5d, (byte) 0x21,
+        (byte) 0x1a, (byte) 0xe4, (byte) 0x30, (byte) 0x95, (byte) 0x75, (byte) 0x8e, (byte) 0xcd, (byte) 0x5e,
+        (byte) 0xd1, (byte) 0xbd, (byte) 0x0a, (byte) 0x45, (byte) 0xee, (byte) 0xe7, (byte) 0xf7, (byte) 0x6b,
+        (byte) 0x65, (byte) 0x02, (byte) 0x60, (byte) 0xd0, (byte) 0x2e, (byte) 0xaf, (byte) 0x3d, (byte) 0xbc,
+        (byte) 0x07, (byte) 0xdd, (byte) 0x2b, (byte) 0x8e, (byte) 0x33, (byte) 0xc0, (byte) 0x93, (byte) 0x80,
+        (byte) 0xd9, (byte) 0x2b, (byte) 0xa7, (byte) 0x71, (byte) 0x57, (byte) 0x76, (byte) 0xbc, (byte) 0x8e,
+        (byte) 0xb9, (byte) 0xe0, (byte) 0xd7, (byte) 0xf4, (byte) 0x23, (byte) 0x8d, (byte) 0x41, (byte) 0x1a,
+        (byte) 0x97, (byte) 0x4f, (byte) 0x2c, (byte) 0x1b, (byte) 0xd5, (byte) 0x4b, (byte) 0x66, (byte) 0xe8,
+        (byte) 0xfa, (byte) 0xd2, (byte) 0x50, (byte) 0x0d, (byte) 0x17, (byte) 0xab, (byte) 0x34, (byte) 0x31,
+        (byte) 0x3d, (byte) 0xa4, (byte) 0x88, (byte) 0xd8, (byte) 0x8e, (byte) 0xa8, (byte) 0xa7, (byte) 0x6e,
+        (byte) 0x17, (byte) 0x03, (byte) 0xb7, (byte) 0x0f, (byte) 0x68, (byte) 0x7c, (byte) 0x64, (byte) 0x7b,
+        (byte) 0x92, (byte) 0xb8, (byte) 0x63, (byte) 0xe4, (byte) 0x9a, (byte) 0x67, (byte) 0x18, (byte) 0x81,
+        (byte) 0x27, (byte) 0xd4, (byte) 0x0b, (byte) 0x13, (byte) 0x48, (byte) 0xd3, (byte) 0x7d, (byte) 0x4e,
+        (byte) 0xf6, (byte) 0xa8, (byte) 0x8f, (byte) 0x56, (byte) 0x17, (byte) 0x2d, (byte) 0x08, (byte) 0x51,
+    });
+
+    @Test
+    public void testDSAGeneratorWithParams() throws Exception {
+        final DSAParameterSpec dsaSpec = new DSAParameterSpec(DSA_P, DSA_Q, DSA_G);
+
+        ServiceTester.test("KeyPairGenerator").withAlgorithm("DSA").run(new ServiceTester.Test() {
+            @Override
+            public void test(Provider p, String algorithm) throws Exception {
+                final KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA", p);
+                kpg.initialize(dsaSpec);
+                KeyPair pair = kpg.generateKeyPair();
+                DSAPrivateKey privKey = (DSAPrivateKey) pair.getPrivate();
+                DSAPublicKey pubKey = (DSAPublicKey) pair.getPublic();
+
+                DSAParams actualParams = privKey.getParams();
+                assertNotNull("DSA params should not be null", actualParams);
+
+                assertEquals("DSA P should be the same as supplied", DSA_P, actualParams.getP());
+                assertEquals("DSA Q should be the same as supplied", DSA_Q, actualParams.getQ());
+                assertEquals("DSA G should be the same as supplied", DSA_G, actualParams.getG());
+
+                actualParams = pubKey.getParams();
+                assertNotNull("DSA params should not be null", actualParams);
+
+                assertEquals("DSA P should be the same as supplied", DSA_P, actualParams.getP());
+                assertEquals("DSA Q should be the same as supplied", DSA_Q, actualParams.getQ());
+                assertEquals("DSA G should be the same as supplied", DSA_G, actualParams.getG());
+            }
+        });
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestDH.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestDH.java
new file mode 100644
index 0000000..16b23f7
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestDH.java
@@ -0,0 +1,37 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class KeyPairGeneratorTestDH extends AbstractKeyPairGeneratorTest {
+
+    public KeyPairGeneratorTestDH() {
+        super("DH", new KeyAgreementHelper("DH"));
+    }
+
+    // Broken Test: Takes ages due to DH computations. Disabling for now.
+    @Override
+    public void testKeyPairGenerator() throws Exception {
+        super.testKeyPairGenerator();
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestDSA.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestDSA.java
new file mode 100644
index 0000000..b97adce
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestDSA.java
@@ -0,0 +1,32 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class KeyPairGeneratorTestDSA extends AbstractKeyPairGeneratorTest {
+
+    public KeyPairGeneratorTestDSA() {
+        super("DSA", new SignatureHelper("DSA"));
+    }
+
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestRSA.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestRSA.java
new file mode 100644
index 0000000..2e9d074
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/KeyPairGeneratorTestRSA.java
@@ -0,0 +1,34 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class KeyPairGeneratorTestRSA extends AbstractKeyPairGeneratorTest {
+
+    @SuppressWarnings("unchecked")
+    public KeyPairGeneratorTestRSA() {
+        super("RSA", new CipherAsymmetricCryptHelper("RSA"));
+    }
+
+}
+
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/MessageDigestTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/MessageDigestTest.java
new file mode 100644
index 0000000..83590e9
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/MessageDigestTest.java
@@ -0,0 +1,282 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.org.conscrypt.TestUtils;
+import dalvik.system.VMRuntime;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
+import tests.util.ServiceTester;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public final class MessageDigestTest {
+
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
+    private final byte[] sha_456 = {
+            -24,   9, -59, -47,  -50,  -92, 123, 69, -29,  71,
+              1, -46,  63,  96, -118, -102,  88,  3,  77, -55
+    };
+
+    @Test
+    public void testShaReset() throws NoSuchAlgorithmException {
+        MessageDigest sha = MessageDigest.getInstance("SHA");
+        sha.update(new byte[] { 1, 2, 3 });
+        sha.reset();
+        sha.update(new byte[] { 4, 5, 6 });
+        assertEquals(Arrays.toString(sha_456), Arrays.toString(sha.digest()));
+    }
+
+    @Test
+    public void test_getInstance() throws Exception {
+        ServiceTester.test("MessageDigest").run(new ServiceTester.Test() {
+            @Override
+            public void test(Provider provider, String algorithm) throws Exception {
+                // MessageDigest.getInstance(String)
+                MessageDigest md1 = MessageDigest.getInstance(algorithm);
+                assertEquals(algorithm, md1.getAlgorithm());
+                test_MessageDigest(md1);
+
+                // MessageDigest.getInstance(String, Provider)
+                MessageDigest md2 = MessageDigest.getInstance(algorithm, provider);
+                assertEquals(algorithm, md2.getAlgorithm());
+                assertEquals(provider, md2.getProvider());
+                test_MessageDigest(md2);
+
+                // MessageDigest.getInstance(String, String)
+                MessageDigest md3 = MessageDigest.getInstance(algorithm, provider.getName());
+                assertEquals(algorithm, md3.getAlgorithm());
+                assertEquals(provider, md3.getProvider());
+                test_MessageDigest(md3);
+            }
+        });
+    }
+
+    private static final Map<String, Map<String, byte[]>> EXPECTATIONS
+            = new HashMap<String, Map<String, byte[]>>();
+    private static void putExpectation(String algorithm, String inputName, byte[] expected) {
+        algorithm = algorithm.toUpperCase();
+        Map<String, byte[]> expectations = EXPECTATIONS.get(algorithm);
+        if (expectations == null) {
+            expectations = new HashMap<String, byte[]>();
+            EXPECTATIONS.put(algorithm, expectations);
+        }
+        expectations.put(inputName, expected);
+    }
+    private static Map<String, byte[]> getExpectations(String algorithm) throws Exception {
+        algorithm = algorithm.toUpperCase();
+        Map<String, byte[]> expectations = EXPECTATIONS.get(algorithm);
+        if (expectations == null) {
+            throw new Exception("No expectations for MessageDigest." + algorithm);
+        }
+        return expectations;
+    }
+    private static final String INPUT_EMPTY = "empty";
+    private static final String INPUT_256MB = "256mb";
+    static {
+        // INPUT_EMPTY
+        putExpectation("MD2",
+                       INPUT_EMPTY,
+                       new byte[] { -125, 80, -27, -93, -30, 76, 21, 61,
+                                    -14, 39, 92, -97, -128, 105, 39, 115 });
+        putExpectation("MD5",
+                       INPUT_EMPTY,
+                       new byte[] { -44, 29, -116, -39, -113, 0, -78, 4,
+                                    -23, -128, 9, -104, -20, -8, 66, 126 });
+        putExpectation("SHA",
+                       INPUT_EMPTY,
+                       new byte[] { -38, 57, -93, -18, 94, 107, 75, 13,
+                                    50, 85, -65, -17, -107, 96, 24, -112,
+                                    -81, -40, 7, 9});
+        putExpectation("SHA1",
+                       INPUT_EMPTY,
+                       new byte[] { -38, 57, -93, -18, 94, 107, 75, 13,
+                                    50, 85, -65, -17, -107, 96, 24, -112,
+                                    -81, -40, 7, 9});
+        putExpectation("SHA-1",
+                       INPUT_EMPTY,
+                       new byte[] { -38, 57, -93, -18, 94, 107, 75, 13,
+                                    50, 85, -65, -17, -107, 96, 24, -112,
+                                    -81, -40, 7, 9});
+        putExpectation("SHA-224",
+                       INPUT_EMPTY,
+                       new byte[] { -47, 74, 2, -116, 42, 58, 43, -55, 71,
+                                    97, 2, -69, 40, -126, 52, -60, 21,
+                                    -94, -80, 31, -126, -114, -90, 42,
+                                    -59, -77, -28, 47});
+        putExpectation("SHA-256",
+                       INPUT_EMPTY,
+                       new byte[] { -29, -80, -60, 66, -104, -4, 28, 20,
+                                    -102, -5, -12, -56, -103, 111, -71, 36,
+                                    39, -82, 65, -28, 100, -101, -109, 76,
+                                    -92, -107, -103, 27, 120, 82, -72, 85 });
+        putExpectation("SHA-384",
+                       INPUT_EMPTY,
+                       new byte[] { 56, -80, 96, -89, 81, -84, -106, 56,
+                                    76, -39, 50, 126, -79, -79, -29, 106,
+                                    33, -3, -73, 17, 20, -66, 7, 67,
+                                    76, 12, -57, -65, 99, -10, -31, -38,
+                                    39, 78, -34, -65, -25, 111, 101, -5,
+                                    -43, 26, -46, -15, 72, -104, -71, 91 });
+        putExpectation("SHA-512",
+                       INPUT_EMPTY,
+                       new byte[] { -49, -125, -31, 53, 126, -17, -72, -67,
+                                    -15, 84, 40, 80, -42, 109, -128, 7,
+                                    -42, 32, -28, 5, 11, 87, 21, -36,
+                                    -125, -12, -87, 33, -45, 108, -23, -50,
+                                    71, -48, -47, 60, 93, -123, -14, -80,
+                                    -1, -125, 24, -46, -121, 126, -20, 47,
+                                    99, -71, 49, -67, 71, 65, 122, -127,
+                                    -91, 56, 50, 122, -7, 39, -38, 62 });
+        putExpectation("SHA-512/224", INPUT_EMPTY,
+                TestUtils.decodeHex("6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"));
+        putExpectation("SHA-512/256", INPUT_EMPTY,
+                TestUtils.decodeHex(
+                        "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"));
+        putExpectation("SHA3-224", INPUT_EMPTY,
+                TestUtils.decodeHex("6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7"));
+        putExpectation("SHA3-256", INPUT_EMPTY,
+                TestUtils.decodeHex(
+                        "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"));
+        putExpectation("SHA3-384", INPUT_EMPTY,
+                TestUtils.decodeHex(
+                        "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2a"
+                        + "c3713831264adb47fb6bd1e058d5f004"));
+        putExpectation("SHA3-512", INPUT_EMPTY,
+                TestUtils.decodeHex(
+                        "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a6"
+                        + "15b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26"));
+
+        // Regression test for a SHA-1 problem with inputs larger than 256 MiB. http://b/4501620
+        // In mid-2013 this takes 3 minutes even on the host, so let's not run it on devices.
+        if (System.getenv("ANDROID_BUILD_TOP") != null) {
+            // INPUT_256MB
+            putExpectation("MD2",
+                           INPUT_256MB,
+                           new byte[] { -63, -120, 6, 67, 12, -87, -39, -11,
+                                        -67, -3, -31, -41, -91, 16, -35, 91 });
+            putExpectation("MD5",
+                           INPUT_256MB,
+                           new byte[] { 31, 80, 57, -27, 11, -42, 107, 41,
+                                        12, 86, 104, 77, -123, 80, -58, -62 });
+            putExpectation("SHA",
+                           INPUT_256MB,
+                           new byte[] { 123, -111, -37, -36, 86, -59, 120, 30,
+                                        -33, 108, -120, 71, -76, -86, 105, 101,
+                                        86, 108, 92, 117 });
+            putExpectation("SHA-1",
+                           INPUT_256MB,
+                           new byte[] { 123, -111, -37, -36, 86, -59, 120, 30,
+                                        -33, 108, -120, 71, -76, -86, 105, 101,
+                                        86, 108, 92, 117 });
+            putExpectation("SHA-224",
+                           INPUT_256MB,
+                           new byte[] { -78, 82, 5, -71, 57, 119, 77, -32,
+                                        -62, -74, -40, 64, -57, 79, 40, 116,
+                                        -18, 48, -69, 45, 18, -94, 111, 114,
+                                        -45, -93, 43, -11 });
+            putExpectation("SHA-256",
+                           INPUT_256MB,
+                           new byte[] { -90, -41, 42, -57, 105, 15, 83, -66,
+                                        106, -28, 107, -88, -123, 6, -67, -105,
+                                        48, 42, 9, 63, 113, 8, 71, 43,
+                                        -39, -17, -61, -50, -3, -96, 100, -124 });
+            putExpectation("SHA-384",
+                           INPUT_256MB,
+                           new byte[] { 71, 72, 77, -83, -110, 22, -118, -18,
+                                        -58, 119, 115, 74, -67, -36, 84, 122,
+                                        -105, -67, -75, 15, -33, 37, 78, -95,
+                                        4, 118, -53, 106, 65, -115, -19, 121,
+                                        -59, -94, -45, -111, -124, 35, 35, 60,
+                                        67, -34, 62, 106, -16, 122, -110, -14 });
+            putExpectation("SHA-512",
+                           INPUT_256MB,
+                           new byte[] { 36, 7, -120, 39, -87, -87, 84, -40,
+                                        -66, 114, 62, -73, 107, 101, -117, -12,
+                                        -124, 20, 109, 103, -92, 125, 111, 102,
+                                        12, 114, -68, 100, 30, 25, -88, 62,
+                                        108, 56, 9, -107, 89, -25, -50, 118,
+                                        -87, 100, 13, 37, -14, 66, -40, -97,
+                                        105, -27, 79, -62, 53, -31, 83, 40,
+                                        4, 57, 90, -81, 63, -77, -42, 113 });
+        }
+    }
+
+    private void test_MessageDigest(MessageDigest md) throws Exception {
+        String algorithm = md.getAlgorithm();
+        for (Map.Entry<String, byte[]> expectation : getExpectations(algorithm).entrySet()) {
+            String inputName = expectation.getKey();
+            byte[] expected = expectation.getValue();
+            byte[] actual;
+            if (inputName.equals(INPUT_EMPTY)) {
+                actual = md.digest();
+            } else if (inputName.equals(INPUT_256MB)) {
+                byte[] mb = new byte[1 * 1024 * 1024];
+                for (int i = 0; i < 256; i++) {
+                    md.update(mb);
+                }
+                actual = md.digest();
+            } else {
+                throw new AssertionError(inputName);
+            }
+            assertEquals(algorithm, javaBytes(expected), javaBytes(actual));
+            assertEquals(algorithm, expected.length, md.getDigestLength());
+        }
+    }
+
+    private String javaBytes(byte[] bytes) {
+        StringBuilder buf = new StringBuilder();
+        buf.append("new byte[] { ");
+        for (byte b : bytes) {
+            buf.append(b);
+            buf.append(", ");
+        }
+        buf.append(" }");
+        return buf.toString();
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/SignatureTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/SignatureTest.java
new file mode 100644
index 0000000..ada0405
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/SignatureTest.java
@@ -0,0 +1,3224 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.org.conscrypt.Conscrypt;
+import com.android.org.conscrypt.TestUtils;
+import com.android.org.conscrypt.testing.BrokenProvider;
+import com.android.org.conscrypt.testing.OpaqueProvider;
+import dalvik.system.VMRuntime;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.security.AlgorithmParameters;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.EllipticCurve;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.MGF1ParameterSpec;
+import java.security.spec.PSSParameterSpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
+import tests.util.ServiceTester;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class SignatureTest {
+
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
+    // 20 bytes for DSA
+    private final byte[] DATA = new byte[20];
+
+    @Test
+    public void test_getInstance() throws Exception {
+        ServiceTester
+                .test("Signature")
+                // Do not test AndroidKeyStore's Signature. It needs an AndroidKeyStore-specific
+                // key. It's OKish not to test AndroidKeyStore's Signature here because it's tested
+                // by cts/tests/test/keystore.
+                .skipProvider("AndroidKeyStore")
+                .skipProvider("AndroidKeyStoreBCWorkaround")
+                // The SunMSCAPI is very strange, including only supporting its own keys,
+                // so don't test it.
+                .skipProvider("SunMSCAPI")
+                // SunPKCS11-NSS has a problem where failed verifications can leave the
+                // operation open, which results in future init() calls to throw an exception.
+                // This appears to be a problem in the underlying library (see
+                // https://bugs.openjdk.java.net/browse/JDK-8044554), but skip verifying it all
+                // the same.
+                .skipProvider("SunPKCS11-NSS")
+                .run(new ServiceTester.Test() {
+                    @Override
+                    public void test(Provider provider, String algorithm) throws Exception {
+                        KeyPair kp = keyPair(algorithm);
+                        // Signature.getInstance(String)
+                        Signature sig1 = Signature.getInstance(algorithm);
+                        assertEquals(algorithm, sig1.getAlgorithm());
+                        test_Signature(sig1, kp);
+
+                        // Signature.getInstance(String, Provider)
+                        Signature sig2 = Signature.getInstance(algorithm, provider);
+                        assertEquals(algorithm, sig2.getAlgorithm());
+                        assertEquals(provider, sig2.getProvider());
+                        test_Signature(sig2, kp);
+
+                        // Signature.getInstance(String, String)
+                        Signature sig3 = Signature.getInstance(algorithm, provider.getName());
+                        assertEquals(algorithm, sig3.getAlgorithm());
+                        assertEquals(provider, sig3.getProvider());
+                        test_Signature(sig3, kp);
+                    }
+                });
+    }
+
+    private final Map<String, KeyPair> keypairAlgorithmToInstance
+            = new HashMap<String, KeyPair>();
+
+    private KeyPair keyPair(String sigAlgorithm) throws Exception {
+        String sigAlgorithmUpperCase = sigAlgorithm.toUpperCase(Locale.US);
+        if (sigAlgorithmUpperCase.endsWith("ENCRYPTION")) {
+            sigAlgorithm = sigAlgorithm.substring(0, sigAlgorithm.length()-"ENCRYPTION".length());
+            sigAlgorithmUpperCase = sigAlgorithm.toUpperCase(Locale.US);
+        }
+
+        String kpAlgorithm;
+        // note ECDSA must be before DSA
+        if (sigAlgorithmUpperCase.endsWith("ECDSA")
+                || sigAlgorithmUpperCase.endsWith("ECDSAINP1363FORMAT")) {
+            kpAlgorithm = "EC";
+        } else if (sigAlgorithmUpperCase.endsWith("DSA")
+                || sigAlgorithmUpperCase.endsWith("DSAINP1363FORMAT")) {
+            kpAlgorithm = "DSA";
+        } else if (sigAlgorithmUpperCase.endsWith("RSA")
+                || sigAlgorithmUpperCase.endsWith("RSA/PSS")
+                || sigAlgorithmUpperCase.endsWith("RSASSA-PSS")) {
+            kpAlgorithm = "RSA";
+        } else {
+            throw new Exception("Unknown KeyPair algorithm for Signature algorithm "
+                                + sigAlgorithm);
+        }
+
+        KeyPair kp = keypairAlgorithmToInstance.get(kpAlgorithm);
+        if (kp == null) {
+            KeyPairGenerator kpg = KeyPairGenerator.getInstance(kpAlgorithm);
+            if (kpAlgorithm.equals("DSA")) {
+                kpg.initialize(1024);
+            }
+            kp = kpg.generateKeyPair();
+            keypairAlgorithmToInstance.put(kpAlgorithm, kp);
+        }
+        return kp;
+    }
+
+    private AlgorithmParameterSpec getAlgParamSpec(String algorithm) {
+        if (algorithm.equalsIgnoreCase("RSASSA-PSS")) {
+            return PSSParameterSpec.DEFAULT;
+        }
+        return null;
+    }
+
+    private void test_Signature(Signature sig, KeyPair keyPair) throws Exception {
+        AlgorithmParameterSpec params = getAlgParamSpec(sig.getAlgorithm());
+        sig.initSign(keyPair.getPrivate());
+        if (params != null) {
+            sig.setParameter(params);
+        }
+        sig.update(DATA);
+        byte[] signature = sig.sign();
+        assertNotNull(sig.getAlgorithm(), signature);
+        assertTrue(sig.getAlgorithm(), signature.length > 0);
+
+        sig.initVerify(keyPair.getPublic());
+        if (params != null) {
+            sig.setParameter(params);
+        }
+        sig.update(DATA);
+        assertTrue(sig.getAlgorithm(), sig.verify(signature));
+
+        // After verify, should be reusable as if we are after initVerify
+        sig.update(DATA);
+        assertTrue(sig.getAlgorithm(), sig.verify(signature));
+
+        /*
+         * The RI appears to clear out the input data in RawDSA while calling
+         * verify a second time.
+         */
+        if (StandardNames.IS_RI
+                && ("NONEwithDSA".equalsIgnoreCase(sig.getAlgorithm())
+                        || "NONEwithDSAinP1363Format".equalsIgnoreCase(sig.getAlgorithm())
+                        || "RawDSA".equalsIgnoreCase(sig.getAlgorithm()))) {
+            try {
+                sig.verify(signature);
+                fail("Expected RI to have a NONEwithDSA bug");
+            } catch (SignatureException bug) {
+            }
+        } else if (StandardNames.IS_RI && "NONEwithECDSA".equalsIgnoreCase(sig.getAlgorithm())
+                && "SunPKCS11-NSS".equalsIgnoreCase(sig.getProvider().getName())) {
+            // This provider doesn't work properly
+            try {
+                sig.verify(signature);
+                fail("Expected RI to have a NONEwithECDSA bug");
+            } catch (ProviderException bug) {
+            }
+        } else {
+            // Calling Signature.verify a second time should not throw
+            // http://code.google.com/p/android/issues/detail?id=34933
+            sig.verify(signature);
+        }
+
+        if (Conscrypt.isConscrypt(sig.getProvider())) {
+            testSignature_MultipleThreads_Misuse(sig, keyPair.getPrivate());
+        }
+    }
+
+    private static final byte[] PK_BYTES = TestUtils.decodeHex(
+            "30819f300d06092a864886f70d010101050003818d0030818902818100cd769d178f61475fce3001"
+            + "2604218320c77a427121d3b41dd76756c8fc0c428cd15cb754adc85466f47547b1c85623d9c17fc6"
+            + "4f202fca21099caf99460c824ad657caa8c2db34996838d32623c4f23c8b6a4e6698603901262619"
+            + "4840e0896b1a6ec4f6652484aad04569bb6a885b822a10d700224359c632dc7324520cbb3d020301"
+            + "0001");
+    private static final byte[] CONTENT = TestUtils.decodeHex(
+            "f2fa9d73656e00fa01edc12e73656e2e7670632e6432004867268c46dd95030b93ce7260423e5c00"
+            + "fabd4d656d6265727300fa018dc12e73656e2e7670632e643100d7c258dc00fabd44657669636573"
+            + "00faa54b65797300fa02b5c12e4d2e4b009471968cc68835f8a68dde10f53d19693d480de767e5fb"
+            + "976f3562324006372300fabdfd04e1f51ef3aa00fa8d00000001a203e202859471968cc68835f8a6"
+            + "8dde10f53d19693d480de767e5fb976f356232400637230002bab504e1f51ef5810002c29d28463f"
+            + "0003da8d000001e201eaf2fa9d73656e00fa01edc12e73656e2e7670632e6432004867268c46dd95"
+            + "030b93ce7260423e5c00fabd4d656d6265727300fa018dc12e73656e2e7670632e643100d7c258dc"
+            + "00fabd4465766963657300faa54b65797300fa02b5c12e4d2e4b009471968cc68835f8a68dde10f5"
+            + "3d19693d480de767e5fb976f3562324006372300fabdfd04e1f51ef3aa000003e202859471968cc6"
+            + "8835f8a68dde10f53d19693d480de767e5fb976f3562324006372300000000019a0a9530819f300d"
+            + "06092a864886f70d010101050003818d0030818902818100cd769d178f61475fce30012604218320"
+            + "c77a427121d3b41dd76756c8fc0c428cd15cb754adc85466f47547b1c85623d9c17fc64f202fca21"
+            + "099caf99460c824ad657caa8c2db34996838d32623c4f23c8b6a4e66986039012626194840e0896b"
+            + "1a6ec4f6652484aad04569bb6a885b822a10d700224359c632dc7324520cbb3d020301000100");
+    private static final byte[] SIGNATURE = TestUtils.decodeHex(
+            "b4016456148cd2e9f580470aad63d19c1fee52b38c9dcb5b4d61a7ca369a7277497775d106d86394"
+            + "a69229184333b5a3e6261d5bcebdb02530ca9909f4d790199eae7c140f7db39dee2232191bdf0bfb"
+            + "34fdadc44326b9b3f3fa828652bab07f0362ac141c8c3784ebdec44e0b156a5e7bccdc81a56fe954"
+            + "56ac8c0e4ae12d97");
+
+
+    /**
+     * This should actually fail because the ASN.1 encoding is incorrect. It is
+     * missing the NULL in the AlgorithmIdentifier field.
+     * <p>
+     * http://code.google.com/p/android/issues/detail?id=18566 <br/>
+     * http://b/5038554
+     */
+    @Test
+    public void test18566_AlgorithmOid_MissingNull_Failure() throws Exception {
+        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(PK_BYTES);
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        PublicKey pk = keyFactory.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA256withRSA");
+        sig.initVerify(pk);
+        sig.update(CONTENT);
+        assertFalse(sig.verify(SIGNATURE));
+    }
+
+    /*
+     * Test vectors generated with this private key:
+     *
+     * -----BEGIN RSA PRIVATE KEY-----
+     * MIIEpAIBAAKCAQEA4Ec+irjyKE/rnnQv+XSPoRjtmGM8kvUq63ouvg075gMpvnZq
+     * 0Q62pRXQ0s/ZvqeTDwwwZTeJn3lYzT6FsB+IGFJNMSWEqUslHjYltUFB7b/uGYgI
+     * 4buX/Hy0m56qr2jpyY19DtxTu8D6ADQ1bWMF+7zDxwAUBThqu8hzyw8+90JfPTPf
+     * ezFa4DbSoLZq/UdQOxab8247UWJRW3Ff2oPeryxYrrmr+zCXw8yd2dvl7ylsF2E5
+     * Ao6KZx5jBW1F9AGI0sQTNJCEXeUsJTTpxrJHjAe9rpKII7YtBmx3cPn2Pz26JH9T
+     * CER0e+eqqF2FO4vSRKzsPePImrRkU6tNJMOsaQIDAQABAoIBADd4R3al8XaY9ayW
+     * DfuDobZ1ZOZIvQWXz4q4CHGG8macJ6nsvdSA8Bl6gNBzCebGqW+SUzHlf4tKxvTU
+     * XtpFojJpwJ/EKMB6Tm7fc4oV3sl/q9Lyu0ehTyDqcvz+TDbgGtp3vRN82NTaELsW
+     * LpSkZilx8XX5hfoYjwVsuX7igW9Dq503R2Ekhs2owWGWwwgYqZXshdOEZ3kSZ7O/
+     * IfJzcQppJYYldoQcW2cSwS1L0govMpmtt8E12l6VFavadufK8qO+gFUdBzt4vxFi
+     * xIrSt/R0OgI47k0lL31efmUzzK5kzLOTYAdaL9HgNOw65c6cQIzL8OJeQRQCFoez
+     * 3UdUroECgYEA9UGIS8Nzeyki1BGe9F4t7izUy7dfRVBaFXqlAJ+Zxzot8HJKxGAk
+     * MGMy6omBd2NFRl3G3x4KbxQK/ztzluaomUrF2qloc0cv43dJ0U6z4HXmKdvrNYMz
+     * im82SdCiZUp6Qv2atr+krE1IHTkLsimwZL3DEcwb4bYxidp8QM3s8rECgYEA6hp0
+     * LduIHO23KIyH442GjdekCdFaQ/RF1Td6C1cx3b/KLa8oqOE81cCvzsM0fXSjniNa
+     * PNljPydN4rlPkt9DgzkR2enxz1jyfeLgj/RZZMcg0+whOdx8r8kSlTzeyy81Wi4s
+     * NaUPrXVMs7IxZkJLo7bjESoriYw4xcFe2yOGkzkCgYBRgo8exv2ZYCmQG68dfjN7
+     * pfCvJ+mE6tiVrOYr199O5FoiQInyzBUa880XP84EdLywTzhqLNzA4ANrokGfVFeS
+     * YtRxAL6TGYSj76Bb7PFBV03AebOpXEqD5sQ/MhTW3zLVEt4ZgIXlMeYWuD/X3Z0f
+     * TiYHwzM9B8VdEH0dOJNYcQKBgQDbT7UPUN6O21P/NMgJMYigUShn2izKBIl3WeWH
+     * wkQBDa+GZNWegIPRbBZHiTAfZ6nweAYNg0oq29NnV1toqKhCwrAqibPzH8zsiiL+
+     * OVeVxcbHQitOXXSh6ajzDndZufwtY5wfFWc+hOk6XvFQb0MVODw41Fy9GxQEj0ch
+     * 3IIyYQKBgQDYEUWTr0FfthLb8ZI3ENVNB0hiBadqO0MZSWjA3/HxHvD2GkozfV/T
+     * dBu8lkDkR7i2tsR8OsEgQ1fTsMVbqShr2nP2KSlvX6kUbYl2NX08dR51FIaWpAt0
+     * aFyCzjCQLWOdck/yTV4ulAfuNO3tLjtN9lqpvP623yjQe6aQPxZXaA==
+     * -----END RSA PRIVATE KEY-----
+     *
+     */
+
+    private static final BigInteger RSA_2048_modulus = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0xe0, (byte) 0x47, (byte) 0x3e, (byte) 0x8a, (byte) 0xb8, (byte) 0xf2, (byte) 0x28,
+        (byte) 0x4f, (byte) 0xeb, (byte) 0x9e, (byte) 0x74, (byte) 0x2f, (byte) 0xf9, (byte) 0x74, (byte) 0x8f,
+        (byte) 0xa1, (byte) 0x18, (byte) 0xed, (byte) 0x98, (byte) 0x63, (byte) 0x3c, (byte) 0x92, (byte) 0xf5,
+        (byte) 0x2a, (byte) 0xeb, (byte) 0x7a, (byte) 0x2e, (byte) 0xbe, (byte) 0x0d, (byte) 0x3b, (byte) 0xe6,
+        (byte) 0x03, (byte) 0x29, (byte) 0xbe, (byte) 0x76, (byte) 0x6a, (byte) 0xd1, (byte) 0x0e, (byte) 0xb6,
+        (byte) 0xa5, (byte) 0x15, (byte) 0xd0, (byte) 0xd2, (byte) 0xcf, (byte) 0xd9, (byte) 0xbe, (byte) 0xa7,
+        (byte) 0x93, (byte) 0x0f, (byte) 0x0c, (byte) 0x30, (byte) 0x65, (byte) 0x37, (byte) 0x89, (byte) 0x9f,
+        (byte) 0x79, (byte) 0x58, (byte) 0xcd, (byte) 0x3e, (byte) 0x85, (byte) 0xb0, (byte) 0x1f, (byte) 0x88,
+        (byte) 0x18, (byte) 0x52, (byte) 0x4d, (byte) 0x31, (byte) 0x25, (byte) 0x84, (byte) 0xa9, (byte) 0x4b,
+        (byte) 0x25, (byte) 0x1e, (byte) 0x36, (byte) 0x25, (byte) 0xb5, (byte) 0x41, (byte) 0x41, (byte) 0xed,
+        (byte) 0xbf, (byte) 0xee, (byte) 0x19, (byte) 0x88, (byte) 0x08, (byte) 0xe1, (byte) 0xbb, (byte) 0x97,
+        (byte) 0xfc, (byte) 0x7c, (byte) 0xb4, (byte) 0x9b, (byte) 0x9e, (byte) 0xaa, (byte) 0xaf, (byte) 0x68,
+        (byte) 0xe9, (byte) 0xc9, (byte) 0x8d, (byte) 0x7d, (byte) 0x0e, (byte) 0xdc, (byte) 0x53, (byte) 0xbb,
+        (byte) 0xc0, (byte) 0xfa, (byte) 0x00, (byte) 0x34, (byte) 0x35, (byte) 0x6d, (byte) 0x63, (byte) 0x05,
+        (byte) 0xfb, (byte) 0xbc, (byte) 0xc3, (byte) 0xc7, (byte) 0x00, (byte) 0x14, (byte) 0x05, (byte) 0x38,
+        (byte) 0x6a, (byte) 0xbb, (byte) 0xc8, (byte) 0x73, (byte) 0xcb, (byte) 0x0f, (byte) 0x3e, (byte) 0xf7,
+        (byte) 0x42, (byte) 0x5f, (byte) 0x3d, (byte) 0x33, (byte) 0xdf, (byte) 0x7b, (byte) 0x31, (byte) 0x5a,
+        (byte) 0xe0, (byte) 0x36, (byte) 0xd2, (byte) 0xa0, (byte) 0xb6, (byte) 0x6a, (byte) 0xfd, (byte) 0x47,
+        (byte) 0x50, (byte) 0x3b, (byte) 0x16, (byte) 0x9b, (byte) 0xf3, (byte) 0x6e, (byte) 0x3b, (byte) 0x51,
+        (byte) 0x62, (byte) 0x51, (byte) 0x5b, (byte) 0x71, (byte) 0x5f, (byte) 0xda, (byte) 0x83, (byte) 0xde,
+        (byte) 0xaf, (byte) 0x2c, (byte) 0x58, (byte) 0xae, (byte) 0xb9, (byte) 0xab, (byte) 0xfb, (byte) 0x30,
+        (byte) 0x97, (byte) 0xc3, (byte) 0xcc, (byte) 0x9d, (byte) 0xd9, (byte) 0xdb, (byte) 0xe5, (byte) 0xef,
+        (byte) 0x29, (byte) 0x6c, (byte) 0x17, (byte) 0x61, (byte) 0x39, (byte) 0x02, (byte) 0x8e, (byte) 0x8a,
+        (byte) 0x67, (byte) 0x1e, (byte) 0x63, (byte) 0x05, (byte) 0x6d, (byte) 0x45, (byte) 0xf4, (byte) 0x01,
+        (byte) 0x88, (byte) 0xd2, (byte) 0xc4, (byte) 0x13, (byte) 0x34, (byte) 0x90, (byte) 0x84, (byte) 0x5d,
+        (byte) 0xe5, (byte) 0x2c, (byte) 0x25, (byte) 0x34, (byte) 0xe9, (byte) 0xc6, (byte) 0xb2, (byte) 0x47,
+        (byte) 0x8c, (byte) 0x07, (byte) 0xbd, (byte) 0xae, (byte) 0x92, (byte) 0x88, (byte) 0x23, (byte) 0xb6,
+        (byte) 0x2d, (byte) 0x06, (byte) 0x6c, (byte) 0x77, (byte) 0x70, (byte) 0xf9, (byte) 0xf6, (byte) 0x3f,
+        (byte) 0x3d, (byte) 0xba, (byte) 0x24, (byte) 0x7f, (byte) 0x53, (byte) 0x08, (byte) 0x44, (byte) 0x74,
+        (byte) 0x7b, (byte) 0xe7, (byte) 0xaa, (byte) 0xa8, (byte) 0x5d, (byte) 0x85, (byte) 0x3b, (byte) 0x8b,
+        (byte) 0xd2, (byte) 0x44, (byte) 0xac, (byte) 0xec, (byte) 0x3d, (byte) 0xe3, (byte) 0xc8, (byte) 0x9a,
+        (byte) 0xb4, (byte) 0x64, (byte) 0x53, (byte) 0xab, (byte) 0x4d, (byte) 0x24, (byte) 0xc3, (byte) 0xac,
+        (byte) 0x69,
+    });
+
+    private static final BigInteger RSA_2048_privateExponent = new BigInteger(new byte[] {
+        (byte) 0x37, (byte) 0x78, (byte) 0x47, (byte) 0x76, (byte) 0xa5, (byte) 0xf1, (byte) 0x76, (byte) 0x98,
+        (byte) 0xf5, (byte) 0xac, (byte) 0x96, (byte) 0x0d, (byte) 0xfb, (byte) 0x83, (byte) 0xa1, (byte) 0xb6,
+        (byte) 0x75, (byte) 0x64, (byte) 0xe6, (byte) 0x48, (byte) 0xbd, (byte) 0x05, (byte) 0x97, (byte) 0xcf,
+        (byte) 0x8a, (byte) 0xb8, (byte) 0x08, (byte) 0x71, (byte) 0x86, (byte) 0xf2, (byte) 0x66, (byte) 0x9c,
+        (byte) 0x27, (byte) 0xa9, (byte) 0xec, (byte) 0xbd, (byte) 0xd4, (byte) 0x80, (byte) 0xf0, (byte) 0x19,
+        (byte) 0x7a, (byte) 0x80, (byte) 0xd0, (byte) 0x73, (byte) 0x09, (byte) 0xe6, (byte) 0xc6, (byte) 0xa9,
+        (byte) 0x6f, (byte) 0x92, (byte) 0x53, (byte) 0x31, (byte) 0xe5, (byte) 0x7f, (byte) 0x8b, (byte) 0x4a,
+        (byte) 0xc6, (byte) 0xf4, (byte) 0xd4, (byte) 0x5e, (byte) 0xda, (byte) 0x45, (byte) 0xa2, (byte) 0x32,
+        (byte) 0x69, (byte) 0xc0, (byte) 0x9f, (byte) 0xc4, (byte) 0x28, (byte) 0xc0, (byte) 0x7a, (byte) 0x4e,
+        (byte) 0x6e, (byte) 0xdf, (byte) 0x73, (byte) 0x8a, (byte) 0x15, (byte) 0xde, (byte) 0xc9, (byte) 0x7f,
+        (byte) 0xab, (byte) 0xd2, (byte) 0xf2, (byte) 0xbb, (byte) 0x47, (byte) 0xa1, (byte) 0x4f, (byte) 0x20,
+        (byte) 0xea, (byte) 0x72, (byte) 0xfc, (byte) 0xfe, (byte) 0x4c, (byte) 0x36, (byte) 0xe0, (byte) 0x1a,
+        (byte) 0xda, (byte) 0x77, (byte) 0xbd, (byte) 0x13, (byte) 0x7c, (byte) 0xd8, (byte) 0xd4, (byte) 0xda,
+        (byte) 0x10, (byte) 0xbb, (byte) 0x16, (byte) 0x2e, (byte) 0x94, (byte) 0xa4, (byte) 0x66, (byte) 0x29,
+        (byte) 0x71, (byte) 0xf1, (byte) 0x75, (byte) 0xf9, (byte) 0x85, (byte) 0xfa, (byte) 0x18, (byte) 0x8f,
+        (byte) 0x05, (byte) 0x6c, (byte) 0xb9, (byte) 0x7e, (byte) 0xe2, (byte) 0x81, (byte) 0x6f, (byte) 0x43,
+        (byte) 0xab, (byte) 0x9d, (byte) 0x37, (byte) 0x47, (byte) 0x61, (byte) 0x24, (byte) 0x86, (byte) 0xcd,
+        (byte) 0xa8, (byte) 0xc1, (byte) 0x61, (byte) 0x96, (byte) 0xc3, (byte) 0x08, (byte) 0x18, (byte) 0xa9,
+        (byte) 0x95, (byte) 0xec, (byte) 0x85, (byte) 0xd3, (byte) 0x84, (byte) 0x67, (byte) 0x79, (byte) 0x12,
+        (byte) 0x67, (byte) 0xb3, (byte) 0xbf, (byte) 0x21, (byte) 0xf2, (byte) 0x73, (byte) 0x71, (byte) 0x0a,
+        (byte) 0x69, (byte) 0x25, (byte) 0x86, (byte) 0x25, (byte) 0x76, (byte) 0x84, (byte) 0x1c, (byte) 0x5b,
+        (byte) 0x67, (byte) 0x12, (byte) 0xc1, (byte) 0x2d, (byte) 0x4b, (byte) 0xd2, (byte) 0x0a, (byte) 0x2f,
+        (byte) 0x32, (byte) 0x99, (byte) 0xad, (byte) 0xb7, (byte) 0xc1, (byte) 0x35, (byte) 0xda, (byte) 0x5e,
+        (byte) 0x95, (byte) 0x15, (byte) 0xab, (byte) 0xda, (byte) 0x76, (byte) 0xe7, (byte) 0xca, (byte) 0xf2,
+        (byte) 0xa3, (byte) 0xbe, (byte) 0x80, (byte) 0x55, (byte) 0x1d, (byte) 0x07, (byte) 0x3b, (byte) 0x78,
+        (byte) 0xbf, (byte) 0x11, (byte) 0x62, (byte) 0xc4, (byte) 0x8a, (byte) 0xd2, (byte) 0xb7, (byte) 0xf4,
+        (byte) 0x74, (byte) 0x3a, (byte) 0x02, (byte) 0x38, (byte) 0xee, (byte) 0x4d, (byte) 0x25, (byte) 0x2f,
+        (byte) 0x7d, (byte) 0x5e, (byte) 0x7e, (byte) 0x65, (byte) 0x33, (byte) 0xcc, (byte) 0xae, (byte) 0x64,
+        (byte) 0xcc, (byte) 0xb3, (byte) 0x93, (byte) 0x60, (byte) 0x07, (byte) 0x5a, (byte) 0x2f, (byte) 0xd1,
+        (byte) 0xe0, (byte) 0x34, (byte) 0xec, (byte) 0x3a, (byte) 0xe5, (byte) 0xce, (byte) 0x9c, (byte) 0x40,
+        (byte) 0x8c, (byte) 0xcb, (byte) 0xf0, (byte) 0xe2, (byte) 0x5e, (byte) 0x41, (byte) 0x14, (byte) 0x02,
+        (byte) 0x16, (byte) 0x87, (byte) 0xb3, (byte) 0xdd, (byte) 0x47, (byte) 0x54, (byte) 0xae, (byte) 0x81,
+    });
+
+    private static final BigInteger RSA_2048_publicExponent = new BigInteger(new byte[] {
+        (byte) 0x01, (byte) 0x00, (byte) 0x01,
+    });
+
+    private static final BigInteger RSA_2048_primeP = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0xf5, (byte) 0x41, (byte) 0x88, (byte) 0x4b, (byte) 0xc3, (byte) 0x73, (byte) 0x7b,
+        (byte) 0x29, (byte) 0x22, (byte) 0xd4, (byte) 0x11, (byte) 0x9e, (byte) 0xf4, (byte) 0x5e, (byte) 0x2d,
+        (byte) 0xee, (byte) 0x2c, (byte) 0xd4, (byte) 0xcb, (byte) 0xb7, (byte) 0x5f, (byte) 0x45, (byte) 0x50,
+        (byte) 0x5a, (byte) 0x15, (byte) 0x7a, (byte) 0xa5, (byte) 0x00, (byte) 0x9f, (byte) 0x99, (byte) 0xc7,
+        (byte) 0x3a, (byte) 0x2d, (byte) 0xf0, (byte) 0x72, (byte) 0x4a, (byte) 0xc4, (byte) 0x60, (byte) 0x24,
+        (byte) 0x30, (byte) 0x63, (byte) 0x32, (byte) 0xea, (byte) 0x89, (byte) 0x81, (byte) 0x77, (byte) 0x63,
+        (byte) 0x45, (byte) 0x46, (byte) 0x5d, (byte) 0xc6, (byte) 0xdf, (byte) 0x1e, (byte) 0x0a, (byte) 0x6f,
+        (byte) 0x14, (byte) 0x0a, (byte) 0xff, (byte) 0x3b, (byte) 0x73, (byte) 0x96, (byte) 0xe6, (byte) 0xa8,
+        (byte) 0x99, (byte) 0x4a, (byte) 0xc5, (byte) 0xda, (byte) 0xa9, (byte) 0x68, (byte) 0x73, (byte) 0x47,
+        (byte) 0x2f, (byte) 0xe3, (byte) 0x77, (byte) 0x49, (byte) 0xd1, (byte) 0x4e, (byte) 0xb3, (byte) 0xe0,
+        (byte) 0x75, (byte) 0xe6, (byte) 0x29, (byte) 0xdb, (byte) 0xeb, (byte) 0x35, (byte) 0x83, (byte) 0x33,
+        (byte) 0x8a, (byte) 0x6f, (byte) 0x36, (byte) 0x49, (byte) 0xd0, (byte) 0xa2, (byte) 0x65, (byte) 0x4a,
+        (byte) 0x7a, (byte) 0x42, (byte) 0xfd, (byte) 0x9a, (byte) 0xb6, (byte) 0xbf, (byte) 0xa4, (byte) 0xac,
+        (byte) 0x4d, (byte) 0x48, (byte) 0x1d, (byte) 0x39, (byte) 0x0b, (byte) 0xb2, (byte) 0x29, (byte) 0xb0,
+        (byte) 0x64, (byte) 0xbd, (byte) 0xc3, (byte) 0x11, (byte) 0xcc, (byte) 0x1b, (byte) 0xe1, (byte) 0xb6,
+        (byte) 0x31, (byte) 0x89, (byte) 0xda, (byte) 0x7c, (byte) 0x40, (byte) 0xcd, (byte) 0xec, (byte) 0xf2,
+        (byte) 0xb1,
+    });
+
+    private static final BigInteger RSA_2048_primeQ = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0xea, (byte) 0x1a, (byte) 0x74, (byte) 0x2d, (byte) 0xdb, (byte) 0x88, (byte) 0x1c,
+        (byte) 0xed, (byte) 0xb7, (byte) 0x28, (byte) 0x8c, (byte) 0x87, (byte) 0xe3, (byte) 0x8d, (byte) 0x86,
+        (byte) 0x8d, (byte) 0xd7, (byte) 0xa4, (byte) 0x09, (byte) 0xd1, (byte) 0x5a, (byte) 0x43, (byte) 0xf4,
+        (byte) 0x45, (byte) 0xd5, (byte) 0x37, (byte) 0x7a, (byte) 0x0b, (byte) 0x57, (byte) 0x31, (byte) 0xdd,
+        (byte) 0xbf, (byte) 0xca, (byte) 0x2d, (byte) 0xaf, (byte) 0x28, (byte) 0xa8, (byte) 0xe1, (byte) 0x3c,
+        (byte) 0xd5, (byte) 0xc0, (byte) 0xaf, (byte) 0xce, (byte) 0xc3, (byte) 0x34, (byte) 0x7d, (byte) 0x74,
+        (byte) 0xa3, (byte) 0x9e, (byte) 0x23, (byte) 0x5a, (byte) 0x3c, (byte) 0xd9, (byte) 0x63, (byte) 0x3f,
+        (byte) 0x27, (byte) 0x4d, (byte) 0xe2, (byte) 0xb9, (byte) 0x4f, (byte) 0x92, (byte) 0xdf, (byte) 0x43,
+        (byte) 0x83, (byte) 0x39, (byte) 0x11, (byte) 0xd9, (byte) 0xe9, (byte) 0xf1, (byte) 0xcf, (byte) 0x58,
+        (byte) 0xf2, (byte) 0x7d, (byte) 0xe2, (byte) 0xe0, (byte) 0x8f, (byte) 0xf4, (byte) 0x59, (byte) 0x64,
+        (byte) 0xc7, (byte) 0x20, (byte) 0xd3, (byte) 0xec, (byte) 0x21, (byte) 0x39, (byte) 0xdc, (byte) 0x7c,
+        (byte) 0xaf, (byte) 0xc9, (byte) 0x12, (byte) 0x95, (byte) 0x3c, (byte) 0xde, (byte) 0xcb, (byte) 0x2f,
+        (byte) 0x35, (byte) 0x5a, (byte) 0x2e, (byte) 0x2c, (byte) 0x35, (byte) 0xa5, (byte) 0x0f, (byte) 0xad,
+        (byte) 0x75, (byte) 0x4c, (byte) 0xb3, (byte) 0xb2, (byte) 0x31, (byte) 0x66, (byte) 0x42, (byte) 0x4b,
+        (byte) 0xa3, (byte) 0xb6, (byte) 0xe3, (byte) 0x11, (byte) 0x2a, (byte) 0x2b, (byte) 0x89, (byte) 0x8c,
+        (byte) 0x38, (byte) 0xc5, (byte) 0xc1, (byte) 0x5e, (byte) 0xdb, (byte) 0x23, (byte) 0x86, (byte) 0x93,
+        (byte) 0x39,
+    });
+
+    /* Test data is: "Android.\n" */
+    private static final byte[] Vector1Data = new byte[] {
+        (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x2e,
+        (byte) 0x0a,
+    };
+
+    private static final byte[] SHA1withRSA_Vector1Signature = {
+        (byte) 0x6d, (byte) 0x5b, (byte) 0xff, (byte) 0x68, (byte) 0xda, (byte) 0x18, (byte) 0x98, (byte) 0x72,
+        (byte) 0x5c, (byte) 0x1f, (byte) 0x46, (byte) 0x51, (byte) 0x77, (byte) 0x15, (byte) 0x11, (byte) 0xcb,
+        (byte) 0xe0, (byte) 0xb9, (byte) 0x3b, (byte) 0x7d, (byte) 0xf5, (byte) 0x96, (byte) 0x98, (byte) 0x24,
+        (byte) 0x85, (byte) 0x9d, (byte) 0x3e, (byte) 0xed, (byte) 0x9b, (byte) 0xb2, (byte) 0x8a, (byte) 0x91,
+        (byte) 0xfb, (byte) 0xf6, (byte) 0x85, (byte) 0x64, (byte) 0x74, (byte) 0x18, (byte) 0xb5, (byte) 0x1c,
+        (byte) 0xb3, (byte) 0x8d, (byte) 0x99, (byte) 0x0d, (byte) 0xdf, (byte) 0xaa, (byte) 0xa6, (byte) 0xa1,
+        (byte) 0xc3, (byte) 0xb6, (byte) 0x25, (byte) 0xb3, (byte) 0x06, (byte) 0xe0, (byte) 0xef, (byte) 0x28,
+        (byte) 0xb0, (byte) 0x4d, (byte) 0x50, (byte) 0xc7, (byte) 0x75, (byte) 0x39, (byte) 0xb9, (byte) 0x2c,
+        (byte) 0x47, (byte) 0xb5, (byte) 0xe2, (byte) 0x96, (byte) 0xf8, (byte) 0xf6, (byte) 0xcb, (byte) 0xa0,
+        (byte) 0x58, (byte) 0xc9, (byte) 0x3e, (byte) 0xd5, (byte) 0xfc, (byte) 0x26, (byte) 0xd9, (byte) 0x55,
+        (byte) 0x73, (byte) 0x39, (byte) 0x75, (byte) 0xb3, (byte) 0xb0, (byte) 0x0a, (byte) 0x5f, (byte) 0x5e,
+        (byte) 0x3b, (byte) 0x4a, (byte) 0x2e, (byte) 0xb1, (byte) 0x0e, (byte) 0x7d, (byte) 0xe5, (byte) 0xcc,
+        (byte) 0x04, (byte) 0x2c, (byte) 0xd1, (byte) 0x0a, (byte) 0x32, (byte) 0xaa, (byte) 0xd9, (byte) 0x8d,
+        (byte) 0x1f, (byte) 0xcb, (byte) 0xe3, (byte) 0x7f, (byte) 0x63, (byte) 0x12, (byte) 0xb1, (byte) 0x98,
+        (byte) 0x46, (byte) 0x46, (byte) 0x07, (byte) 0xd9, (byte) 0x49, (byte) 0xd2, (byte) 0xbf, (byte) 0xb5,
+        (byte) 0xbc, (byte) 0xbb, (byte) 0xfd, (byte) 0x1c, (byte) 0xd7, (byte) 0x11, (byte) 0x94, (byte) 0xaa,
+        (byte) 0x5f, (byte) 0x7b, (byte) 0xb2, (byte) 0x0c, (byte) 0x5d, (byte) 0x94, (byte) 0x53, (byte) 0x5e,
+        (byte) 0x81, (byte) 0x5c, (byte) 0xbb, (byte) 0x1d, (byte) 0x4f, (byte) 0x30, (byte) 0xcd, (byte) 0xf8,
+        (byte) 0xd7, (byte) 0xa5, (byte) 0xfa, (byte) 0x5e, (byte) 0xe0, (byte) 0x19, (byte) 0x3f, (byte) 0xa4,
+        (byte) 0xaa, (byte) 0x56, (byte) 0x4e, (byte) 0xec, (byte) 0xeb, (byte) 0xee, (byte) 0xa2, (byte) 0x6c,
+        (byte) 0xc9, (byte) 0x4f, (byte) 0xc2, (byte) 0xcc, (byte) 0x2a, (byte) 0xbc, (byte) 0x5b, (byte) 0x09,
+        (byte) 0x10, (byte) 0x73, (byte) 0x61, (byte) 0x0c, (byte) 0x04, (byte) 0xb6, (byte) 0xb7, (byte) 0x2c,
+        (byte) 0x37, (byte) 0xd2, (byte) 0xca, (byte) 0x2d, (byte) 0x54, (byte) 0xf2, (byte) 0xf7, (byte) 0x77,
+        (byte) 0xe1, (byte) 0xba, (byte) 0x9f, (byte) 0x29, (byte) 0x07, (byte) 0xa2, (byte) 0x74, (byte) 0xc6,
+        (byte) 0xe9, (byte) 0x1e, (byte) 0xde, (byte) 0xd7, (byte) 0x9c, (byte) 0x4b, (byte) 0xb7, (byte) 0x66,
+        (byte) 0x52, (byte) 0xe8, (byte) 0xac, (byte) 0xf6, (byte) 0x76, (byte) 0xab, (byte) 0x16, (byte) 0x82,
+        (byte) 0x96, (byte) 0x87, (byte) 0x40, (byte) 0x0f, (byte) 0xad, (byte) 0x2d, (byte) 0x46, (byte) 0xa6,
+        (byte) 0x28, (byte) 0x04, (byte) 0x13, (byte) 0xc2, (byte) 0xce, (byte) 0x50, (byte) 0x56, (byte) 0x6d,
+        (byte) 0xbe, (byte) 0x0c, (byte) 0x91, (byte) 0xd0, (byte) 0x8e, (byte) 0x80, (byte) 0x9e, (byte) 0x91,
+        (byte) 0x8f, (byte) 0x62, (byte) 0xb3, (byte) 0x57, (byte) 0xd6, (byte) 0xae, (byte) 0x53, (byte) 0x91,
+        (byte) 0x83, (byte) 0xe9, (byte) 0x38, (byte) 0x77, (byte) 0x8f, (byte) 0x20, (byte) 0xdd, (byte) 0x13,
+        (byte) 0x7d, (byte) 0x15, (byte) 0x44, (byte) 0x7e, (byte) 0xb5, (byte) 0x00, (byte) 0xd6, (byte) 0x45,
+    };
+
+    private static final byte[] Vector2Data = new byte[] {
+        (byte) 0x54, (byte) 0x68, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x69, (byte) 0x73, (byte) 0x20,
+        (byte) 0x61, (byte) 0x20, (byte) 0x73, (byte) 0x69, (byte) 0x67, (byte) 0x6e, (byte) 0x65, (byte) 0x64,
+        (byte) 0x20, (byte) 0x6d, (byte) 0x65, (byte) 0x73, (byte) 0x73, (byte) 0x61, (byte) 0x67, (byte) 0x65,
+        (byte) 0x20, (byte) 0x66, (byte) 0x72, (byte) 0x6f, (byte) 0x6d, (byte) 0x20, (byte) 0x4b, (byte) 0x65,
+        (byte) 0x6e, (byte) 0x6e, (byte) 0x79, (byte) 0x20, (byte) 0x52, (byte) 0x6f, (byte) 0x6f, (byte) 0x74,
+        (byte) 0x2e, (byte) 0x0a,
+    };
+
+    private static final byte[] SHA1withRSA_Vector2Signature = new byte[] {
+        (byte) 0x2e, (byte) 0xa6, (byte) 0x33, (byte) 0xd1, (byte) 0x9d, (byte) 0xfc, (byte) 0x4e, (byte) 0x27,
+        (byte) 0xb3, (byte) 0xa8, (byte) 0x9a, (byte) 0xf2, (byte) 0x48, (byte) 0x62, (byte) 0x15, (byte) 0xa2,
+        (byte) 0xce, (byte) 0x5f, (byte) 0x2b, (byte) 0x0e, (byte) 0xc5, (byte) 0x26, (byte) 0xba, (byte) 0xd9,
+        (byte) 0x0f, (byte) 0x60, (byte) 0xeb, (byte) 0xf0, (byte) 0xd5, (byte) 0x5c, (byte) 0x6b, (byte) 0x23,
+        (byte) 0x11, (byte) 0x95, (byte) 0xa4, (byte) 0xbd, (byte) 0x11, (byte) 0x68, (byte) 0xe7, (byte) 0x3a,
+        (byte) 0x37, (byte) 0x3d, (byte) 0x79, (byte) 0xb8, (byte) 0x4f, (byte) 0xe9, (byte) 0xa1, (byte) 0x88,
+        (byte) 0xfb, (byte) 0xa9, (byte) 0x8b, (byte) 0x34, (byte) 0xa1, (byte) 0xe0, (byte) 0xca, (byte) 0x11,
+        (byte) 0xdd, (byte) 0xd0, (byte) 0x83, (byte) 0x7f, (byte) 0xc1, (byte) 0x0b, (byte) 0x16, (byte) 0x61,
+        (byte) 0xac, (byte) 0x09, (byte) 0xa2, (byte) 0xdd, (byte) 0x40, (byte) 0x5b, (byte) 0x8c, (byte) 0x7a,
+        (byte) 0xb2, (byte) 0xb4, (byte) 0x02, (byte) 0x7c, (byte) 0xd4, (byte) 0x9a, (byte) 0xe6, (byte) 0xa5,
+        (byte) 0x1a, (byte) 0x27, (byte) 0x77, (byte) 0x70, (byte) 0xe3, (byte) 0xe3, (byte) 0x71, (byte) 0xc7,
+        (byte) 0x59, (byte) 0xc7, (byte) 0x9f, (byte) 0xb8, (byte) 0xef, (byte) 0xe7, (byte) 0x15, (byte) 0x02,
+        (byte) 0x0d, (byte) 0x70, (byte) 0xdc, (byte) 0x2c, (byte) 0xe9, (byte) 0xf7, (byte) 0x63, (byte) 0x2a,
+        (byte) 0xb5, (byte) 0xee, (byte) 0x9f, (byte) 0x29, (byte) 0x56, (byte) 0x86, (byte) 0x99, (byte) 0xb3,
+        (byte) 0x0f, (byte) 0xe5, (byte) 0x1f, (byte) 0x76, (byte) 0x22, (byte) 0x3b, (byte) 0x7f, (byte) 0xa9,
+        (byte) 0x9e, (byte) 0xd4, (byte) 0xc4, (byte) 0x83, (byte) 0x5d, (byte) 0x57, (byte) 0xcc, (byte) 0x37,
+        (byte) 0xcb, (byte) 0x9a, (byte) 0x9e, (byte) 0x73, (byte) 0x44, (byte) 0x93, (byte) 0xb4, (byte) 0xf1,
+        (byte) 0x6b, (byte) 0x98, (byte) 0xa0, (byte) 0x57, (byte) 0xbb, (byte) 0x5e, (byte) 0x8f, (byte) 0x89,
+        (byte) 0x5b, (byte) 0x97, (byte) 0x26, (byte) 0xe4, (byte) 0xd0, (byte) 0x51, (byte) 0x0a, (byte) 0x5a,
+        (byte) 0xb7, (byte) 0x12, (byte) 0x1a, (byte) 0x6d, (byte) 0xb0, (byte) 0x79, (byte) 0x30, (byte) 0x51,
+        (byte) 0x83, (byte) 0x2e, (byte) 0xe2, (byte) 0x7a, (byte) 0x67, (byte) 0x66, (byte) 0xd3, (byte) 0x95,
+        (byte) 0xca, (byte) 0xfc, (byte) 0xcb, (byte) 0x92, (byte) 0x79, (byte) 0x32, (byte) 0x26, (byte) 0x86,
+        (byte) 0xe1, (byte) 0x0d, (byte) 0xd8, (byte) 0x19, (byte) 0xfa, (byte) 0x65, (byte) 0x37, (byte) 0xc9,
+        (byte) 0x4c, (byte) 0x2a, (byte) 0xe1, (byte) 0x42, (byte) 0xc7, (byte) 0xd4, (byte) 0xb7, (byte) 0xeb,
+        (byte) 0x1f, (byte) 0xc3, (byte) 0x53, (byte) 0x64, (byte) 0x6f, (byte) 0x2b, (byte) 0x78, (byte) 0x18,
+        (byte) 0x03, (byte) 0xda, (byte) 0x8d, (byte) 0x62, (byte) 0x24, (byte) 0x70, (byte) 0xab, (byte) 0xe6,
+        (byte) 0x16, (byte) 0x13, (byte) 0x24, (byte) 0x6b, (byte) 0x5f, (byte) 0xd3, (byte) 0xec, (byte) 0xc1,
+        (byte) 0x58, (byte) 0x64, (byte) 0xbd, (byte) 0x30, (byte) 0x98, (byte) 0x5e, (byte) 0x33, (byte) 0xce,
+        (byte) 0x87, (byte) 0x64, (byte) 0x14, (byte) 0x07, (byte) 0x85, (byte) 0x43, (byte) 0x3e, (byte) 0x9f,
+        (byte) 0x27, (byte) 0x9f, (byte) 0x63, (byte) 0x66, (byte) 0x9d, (byte) 0x26, (byte) 0x19, (byte) 0xc0,
+        (byte) 0x02, (byte) 0x08, (byte) 0x15, (byte) 0xcb, (byte) 0xb4, (byte) 0xaa, (byte) 0x4a, (byte) 0xc8,
+        (byte) 0xc0, (byte) 0x09, (byte) 0x15, (byte) 0x7d, (byte) 0x8a, (byte) 0x21, (byte) 0xbc, (byte) 0xa3,
+    };
+
+    /*
+     * echo 'Android.' | openssl dgst -sha224 -binary -sign privkey.pem  | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA224withRSA_Vector2Signature = new byte[] {
+        (byte) 0xBD, (byte) 0x3F, (byte) 0xD4, (byte) 0x20, (byte) 0x5B, (byte) 0xC0, (byte) 0x89, (byte) 0x4F,
+        (byte) 0x99, (byte) 0x6C, (byte) 0xF4, (byte) 0xA4, (byte) 0x70, (byte) 0xE3, (byte) 0x5B, (byte) 0x33,
+        (byte) 0xB3, (byte) 0xCA, (byte) 0xFE, (byte) 0x1F, (byte) 0xB9, (byte) 0x3A, (byte) 0xD6, (byte) 0x9B,
+        (byte) 0x1E, (byte) 0xDA, (byte) 0x65, (byte) 0x06, (byte) 0xBD, (byte) 0xC3, (byte) 0x2B, (byte) 0xF8,
+        (byte) 0x0E, (byte) 0xA0, (byte) 0xB5, (byte) 0x33, (byte) 0x7F, (byte) 0x15, (byte) 0xDC, (byte) 0xBB,
+        (byte) 0xDC, (byte) 0x98, (byte) 0x96, (byte) 0xF5, (byte) 0xF8, (byte) 0xE5, (byte) 0x55, (byte) 0x7D,
+        (byte) 0x48, (byte) 0x51, (byte) 0xC5, (byte) 0xAE, (byte) 0x12, (byte) 0xA2, (byte) 0x61, (byte) 0xC7,
+        (byte) 0xA2, (byte) 0x00, (byte) 0x0F, (byte) 0x35, (byte) 0x54, (byte) 0x3C, (byte) 0x7E, (byte) 0x97,
+        (byte) 0x19, (byte) 0x2D, (byte) 0x8F, (byte) 0xFD, (byte) 0x51, (byte) 0x04, (byte) 0x72, (byte) 0x23,
+        (byte) 0x65, (byte) 0x16, (byte) 0x41, (byte) 0x12, (byte) 0x46, (byte) 0xD6, (byte) 0x20, (byte) 0xB6,
+        (byte) 0x4E, (byte) 0xD6, (byte) 0xE8, (byte) 0x60, (byte) 0x91, (byte) 0x05, (byte) 0xCA, (byte) 0x57,
+        (byte) 0x6F, (byte) 0x53, (byte) 0xA4, (byte) 0x05, (byte) 0x2A, (byte) 0x37, (byte) 0xDD, (byte) 0x2E,
+        (byte) 0xA4, (byte) 0xC7, (byte) 0xBF, (byte) 0x9E, (byte) 0xF6, (byte) 0xD5, (byte) 0xD4, (byte) 0x34,
+        (byte) 0xB8, (byte) 0xB3, (byte) 0x8B, (byte) 0x66, (byte) 0x2C, (byte) 0xB6, (byte) 0x5F, (byte) 0xA4,
+        (byte) 0xB7, (byte) 0x77, (byte) 0xF8, (byte) 0x9A, (byte) 0x9C, (byte) 0x44, (byte) 0x9F, (byte) 0xF0,
+        (byte) 0xCA, (byte) 0x53, (byte) 0x56, (byte) 0x2F, (byte) 0x99, (byte) 0x2E, (byte) 0x4B, (byte) 0xA2,
+        (byte) 0x26, (byte) 0x50, (byte) 0x30, (byte) 0x97, (byte) 0x2B, (byte) 0x4B, (byte) 0x0C, (byte) 0x3E,
+        (byte) 0x28, (byte) 0x0B, (byte) 0x88, (byte) 0x87, (byte) 0x9E, (byte) 0xCE, (byte) 0xCB, (byte) 0x57,
+        (byte) 0x72, (byte) 0x6B, (byte) 0xF6, (byte) 0xD6, (byte) 0xAA, (byte) 0x4D, (byte) 0x5F, (byte) 0x19,
+        (byte) 0x7A, (byte) 0xAD, (byte) 0x44, (byte) 0x09, (byte) 0x33, (byte) 0x62, (byte) 0xC8, (byte) 0x56,
+        (byte) 0x82, (byte) 0x84, (byte) 0xBF, (byte) 0x52, (byte) 0xC6, (byte) 0xA2, (byte) 0x2B, (byte) 0xE3,
+        (byte) 0xC2, (byte) 0x7F, (byte) 0xE3, (byte) 0x06, (byte) 0xC3, (byte) 0x30, (byte) 0xB8, (byte) 0xD4,
+        (byte) 0x01, (byte) 0xE6, (byte) 0x3D, (byte) 0xDB, (byte) 0xCA, (byte) 0xE4, (byte) 0xFB, (byte) 0xA8,
+        (byte) 0x7B, (byte) 0x2D, (byte) 0x8F, (byte) 0x39, (byte) 0x7A, (byte) 0x63, (byte) 0x9F, (byte) 0x02,
+        (byte) 0xE8, (byte) 0x91, (byte) 0xD1, (byte) 0xEE, (byte) 0x60, (byte) 0xEE, (byte) 0xCA, (byte) 0xF2,
+        (byte) 0x33, (byte) 0x7D, (byte) 0xF2, (byte) 0x41, (byte) 0x52, (byte) 0x0B, (byte) 0x9B, (byte) 0x1B,
+        (byte) 0x2D, (byte) 0x89, (byte) 0x38, (byte) 0xEC, (byte) 0x24, (byte) 0x60, (byte) 0x40, (byte) 0x40,
+        (byte) 0x6F, (byte) 0xB6, (byte) 0x6F, (byte) 0x86, (byte) 0xB5, (byte) 0x0A, (byte) 0x3D, (byte) 0x98,
+        (byte) 0x77, (byte) 0x3F, (byte) 0x59, (byte) 0x41, (byte) 0x3E, (byte) 0x4D, (byte) 0xE4, (byte) 0x4E,
+        (byte) 0x91, (byte) 0xCD, (byte) 0x8E, (byte) 0x33, (byte) 0x60, (byte) 0x16, (byte) 0x8D, (byte) 0xAB,
+        (byte) 0x04, (byte) 0x14, (byte) 0xE8, (byte) 0x76, (byte) 0xF1, (byte) 0x06, (byte) 0xCD, (byte) 0x4A,
+        (byte) 0x88, (byte) 0xC7, (byte) 0x69, (byte) 0x6B, (byte) 0xC6, (byte) 0xDA, (byte) 0x9E, (byte) 0x09
+    };
+
+    private static final byte[] SHA256withRSA_Vector2Signature = new byte[] {
+        (byte) 0x18, (byte) 0x6e, (byte) 0x31, (byte) 0x1f, (byte) 0x1d, (byte) 0x44, (byte) 0x09, (byte) 0x3e,
+        (byte) 0xa0, (byte) 0xc4, (byte) 0x3d, (byte) 0xb4, (byte) 0x1b, (byte) 0xf2, (byte) 0xd8, (byte) 0xa4,
+        (byte) 0x59, (byte) 0xab, (byte) 0xb5, (byte) 0x37, (byte) 0x28, (byte) 0xb8, (byte) 0x94, (byte) 0x6b,
+        (byte) 0x6f, (byte) 0x13, (byte) 0x54, (byte) 0xff, (byte) 0xac, (byte) 0x15, (byte) 0x84, (byte) 0xd0,
+        (byte) 0xc9, (byte) 0x15, (byte) 0x5b, (byte) 0x69, (byte) 0x05, (byte) 0xf1, (byte) 0x44, (byte) 0xfd,
+        (byte) 0xde, (byte) 0xe8, (byte) 0xb4, (byte) 0x12, (byte) 0x59, (byte) 0x9e, (byte) 0x4c, (byte) 0x0b,
+        (byte) 0xd5, (byte) 0x49, (byte) 0x33, (byte) 0x28, (byte) 0xe0, (byte) 0xcb, (byte) 0x87, (byte) 0x85,
+        (byte) 0xd8, (byte) 0x18, (byte) 0x6f, (byte) 0xfe, (byte) 0xa2, (byte) 0x23, (byte) 0x82, (byte) 0xf0,
+        (byte) 0xe5, (byte) 0x39, (byte) 0x1b, (byte) 0x8c, (byte) 0x93, (byte) 0x11, (byte) 0x49, (byte) 0x72,
+        (byte) 0x2a, (byte) 0x5b, (byte) 0x25, (byte) 0xff, (byte) 0x4e, (byte) 0x88, (byte) 0x70, (byte) 0x9d,
+        (byte) 0x9d, (byte) 0xff, (byte) 0xe2, (byte) 0xc0, (byte) 0x7e, (byte) 0xc8, (byte) 0x03, (byte) 0x40,
+        (byte) 0xbe, (byte) 0x44, (byte) 0x09, (byte) 0xeb, (byte) 0x9e, (byte) 0x8e, (byte) 0x88, (byte) 0xe4,
+        (byte) 0x98, (byte) 0x82, (byte) 0x06, (byte) 0xa4, (byte) 0x9d, (byte) 0x63, (byte) 0x88, (byte) 0x65,
+        (byte) 0xa3, (byte) 0x8e, (byte) 0x0d, (byte) 0x22, (byte) 0xf3, (byte) 0x33, (byte) 0xf2, (byte) 0x40,
+        (byte) 0xe8, (byte) 0x91, (byte) 0x67, (byte) 0x72, (byte) 0x29, (byte) 0x1c, (byte) 0x08, (byte) 0xff,
+        (byte) 0x54, (byte) 0xa0, (byte) 0xcc, (byte) 0xad, (byte) 0x84, (byte) 0x88, (byte) 0x4b, (byte) 0x3b,
+        (byte) 0xef, (byte) 0xf9, (byte) 0x5e, (byte) 0xb3, (byte) 0x41, (byte) 0x6a, (byte) 0xbd, (byte) 0x94,
+        (byte) 0x16, (byte) 0x7d, (byte) 0x9d, (byte) 0x53, (byte) 0x77, (byte) 0xf1, (byte) 0x6a, (byte) 0x95,
+        (byte) 0x57, (byte) 0xad, (byte) 0x65, (byte) 0x9d, (byte) 0x75, (byte) 0x95, (byte) 0xf6, (byte) 0x6a,
+        (byte) 0xd2, (byte) 0x88, (byte) 0xea, (byte) 0x5b, (byte) 0xa2, (byte) 0x94, (byte) 0x8f, (byte) 0x5e,
+        (byte) 0x84, (byte) 0x18, (byte) 0x19, (byte) 0x46, (byte) 0x83, (byte) 0x0b, (byte) 0x6d, (byte) 0x5b,
+        (byte) 0xb9, (byte) 0xdb, (byte) 0xa4, (byte) 0xe5, (byte) 0x17, (byte) 0x02, (byte) 0x9e, (byte) 0x11,
+        (byte) 0xed, (byte) 0xd9, (byte) 0x7b, (byte) 0x83, (byte) 0x87, (byte) 0x89, (byte) 0xf3, (byte) 0xe4,
+        (byte) 0xbf, (byte) 0x0e, (byte) 0xe8, (byte) 0xdc, (byte) 0x55, (byte) 0x9c, (byte) 0xf7, (byte) 0xc9,
+        (byte) 0xc3, (byte) 0xe2, (byte) 0x2c, (byte) 0xf7, (byte) 0x8c, (byte) 0xaa, (byte) 0x17, (byte) 0x1f,
+        (byte) 0xd1, (byte) 0xc7, (byte) 0x74, (byte) 0xc7, (byte) 0x8e, (byte) 0x1c, (byte) 0x5b, (byte) 0xd2,
+        (byte) 0x31, (byte) 0x74, (byte) 0x43, (byte) 0x9a, (byte) 0x52, (byte) 0xbf, (byte) 0x89, (byte) 0xc5,
+        (byte) 0xb4, (byte) 0x80, (byte) 0x6a, (byte) 0x9e, (byte) 0x05, (byte) 0xdb, (byte) 0xbb, (byte) 0x07,
+        (byte) 0x8c, (byte) 0x08, (byte) 0x61, (byte) 0xba, (byte) 0xa4, (byte) 0xbc, (byte) 0x80, (byte) 0x3a,
+        (byte) 0xdd, (byte) 0x3b, (byte) 0x1a, (byte) 0x8c, (byte) 0x21, (byte) 0xd8, (byte) 0xa3, (byte) 0xc0,
+        (byte) 0xc7, (byte) 0xd1, (byte) 0x08, (byte) 0xe1, (byte) 0x34, (byte) 0x99, (byte) 0xc0, (byte) 0xcf,
+        (byte) 0x80, (byte) 0xff, (byte) 0xfa, (byte) 0x07, (byte) 0xef, (byte) 0x5c, (byte) 0x45, (byte) 0xe5,
+    };
+
+    private static final byte[] SHA384withRSA_Vector2Signature = new byte[] {
+        (byte) 0xaf, (byte) 0xf7, (byte) 0x7a, (byte) 0xc2, (byte) 0xbb, (byte) 0xb8, (byte) 0xbd, (byte) 0xe3,
+        (byte) 0x42, (byte) 0xaa, (byte) 0x16, (byte) 0x8a, (byte) 0x52, (byte) 0x6c, (byte) 0x99, (byte) 0x66,
+        (byte) 0x08, (byte) 0xbe, (byte) 0x15, (byte) 0xd9, (byte) 0x7c, (byte) 0x60, (byte) 0x2c, (byte) 0xac,
+        (byte) 0x4d, (byte) 0x4c, (byte) 0xf4, (byte) 0xdf, (byte) 0xbc, (byte) 0x16, (byte) 0x58, (byte) 0x0a,
+        (byte) 0x4e, (byte) 0xde, (byte) 0x8d, (byte) 0xb3, (byte) 0xbd, (byte) 0x03, (byte) 0x4e, (byte) 0x23,
+        (byte) 0x40, (byte) 0xa5, (byte) 0x80, (byte) 0xae, (byte) 0x83, (byte) 0xb4, (byte) 0x0f, (byte) 0x99,
+        (byte) 0x44, (byte) 0xc3, (byte) 0x5e, (byte) 0xdb, (byte) 0x59, (byte) 0x1d, (byte) 0xea, (byte) 0x7b,
+        (byte) 0x4d, (byte) 0xf3, (byte) 0xd2, (byte) 0xad, (byte) 0xbd, (byte) 0x21, (byte) 0x9f, (byte) 0x8e,
+        (byte) 0x87, (byte) 0x8f, (byte) 0x12, (byte) 0x13, (byte) 0x33, (byte) 0xf1, (byte) 0xc0, (byte) 0x9d,
+        (byte) 0xe7, (byte) 0xec, (byte) 0x6e, (byte) 0xad, (byte) 0xea, (byte) 0x5d, (byte) 0x69, (byte) 0xbb,
+        (byte) 0xab, (byte) 0x5b, (byte) 0xd8, (byte) 0x55, (byte) 0x56, (byte) 0xc8, (byte) 0xda, (byte) 0x81,
+        (byte) 0x41, (byte) 0xfb, (byte) 0xd3, (byte) 0x11, (byte) 0x6c, (byte) 0x97, (byte) 0xa7, (byte) 0xc3,
+        (byte) 0xf1, (byte) 0x31, (byte) 0xbf, (byte) 0xbe, (byte) 0x3f, (byte) 0xdb, (byte) 0x35, (byte) 0x85,
+        (byte) 0xb7, (byte) 0xb0, (byte) 0x75, (byte) 0x7f, (byte) 0xaf, (byte) 0xfb, (byte) 0x65, (byte) 0x61,
+        (byte) 0xc7, (byte) 0x0e, (byte) 0x63, (byte) 0xb5, (byte) 0x7d, (byte) 0x95, (byte) 0xe9, (byte) 0x16,
+        (byte) 0x9d, (byte) 0x6a, (byte) 0x00, (byte) 0x9f, (byte) 0x5e, (byte) 0xcd, (byte) 0xff, (byte) 0xa6,
+        (byte) 0xbc, (byte) 0x71, (byte) 0xf2, (byte) 0x2c, (byte) 0xd3, (byte) 0x68, (byte) 0xb9, (byte) 0x3f,
+        (byte) 0xaa, (byte) 0x06, (byte) 0xf1, (byte) 0x9c, (byte) 0x7e, (byte) 0xca, (byte) 0x4a, (byte) 0xfe,
+        (byte) 0xb1, (byte) 0x73, (byte) 0x19, (byte) 0x80, (byte) 0x05, (byte) 0xa6, (byte) 0x85, (byte) 0x14,
+        (byte) 0xda, (byte) 0x7a, (byte) 0x16, (byte) 0x7a, (byte) 0xc2, (byte) 0x46, (byte) 0x57, (byte) 0xa7,
+        (byte) 0xc0, (byte) 0xbf, (byte) 0xcd, (byte) 0xdc, (byte) 0x2f, (byte) 0x64, (byte) 0xf6, (byte) 0x6d,
+        (byte) 0xdc, (byte) 0xcb, (byte) 0x5a, (byte) 0x29, (byte) 0x95, (byte) 0x1c, (byte) 0xfe, (byte) 0xf2,
+        (byte) 0xda, (byte) 0x7e, (byte) 0xcb, (byte) 0x26, (byte) 0x12, (byte) 0xc6, (byte) 0xb0, (byte) 0xba,
+        (byte) 0x84, (byte) 0x9b, (byte) 0x4f, (byte) 0xba, (byte) 0x1b, (byte) 0x78, (byte) 0x25, (byte) 0xb8,
+        (byte) 0x8f, (byte) 0x2e, (byte) 0x51, (byte) 0x5f, (byte) 0x9e, (byte) 0xfc, (byte) 0x40, (byte) 0xbc,
+        (byte) 0x85, (byte) 0xcd, (byte) 0x86, (byte) 0x7f, (byte) 0x88, (byte) 0xc5, (byte) 0xaa, (byte) 0x2b,
+        (byte) 0x78, (byte) 0xb1, (byte) 0x9c, (byte) 0x51, (byte) 0x9a, (byte) 0xe1, (byte) 0xe1, (byte) 0xc0,
+        (byte) 0x40, (byte) 0x47, (byte) 0xcb, (byte) 0xa4, (byte) 0xb7, (byte) 0x6c, (byte) 0x31, (byte) 0xf2,
+        (byte) 0xc8, (byte) 0x9a, (byte) 0xad, (byte) 0x0b, (byte) 0xd3, (byte) 0xf6, (byte) 0x85, (byte) 0x9a,
+        (byte) 0x8f, (byte) 0x4f, (byte) 0xc9, (byte) 0xd8, (byte) 0x33, (byte) 0x7c, (byte) 0x45, (byte) 0x30,
+        (byte) 0xea, (byte) 0x17, (byte) 0xd3, (byte) 0xe3, (byte) 0x90, (byte) 0x2c, (byte) 0xda, (byte) 0xde,
+        (byte) 0x41, (byte) 0x17, (byte) 0x3f, (byte) 0x08, (byte) 0xb9, (byte) 0x34, (byte) 0xc0, (byte) 0xd1,
+    };
+
+    private static final byte[] SHA512withRSA_Vector2Signature = new byte[] {
+        (byte) 0x19, (byte) 0xe2, (byte) 0xe5, (byte) 0xf3, (byte) 0x18, (byte) 0x83, (byte) 0xec, (byte) 0xf0,
+        (byte) 0xab, (byte) 0x50, (byte) 0x05, (byte) 0x4b, (byte) 0x5f, (byte) 0x22, (byte) 0xfc, (byte) 0x82,
+        (byte) 0x6d, (byte) 0xca, (byte) 0xe7, (byte) 0xbe, (byte) 0x23, (byte) 0x94, (byte) 0xfa, (byte) 0xf9,
+        (byte) 0xa4, (byte) 0x8a, (byte) 0x95, (byte) 0x4d, (byte) 0x14, (byte) 0x08, (byte) 0x8b, (byte) 0x5e,
+        (byte) 0x03, (byte) 0x1b, (byte) 0x74, (byte) 0xde, (byte) 0xc1, (byte) 0x45, (byte) 0x9c, (byte) 0xce,
+        (byte) 0x1d, (byte) 0xac, (byte) 0xab, (byte) 0xd3, (byte) 0xa8, (byte) 0xc3, (byte) 0xca, (byte) 0x67,
+        (byte) 0x80, (byte) 0xf6, (byte) 0x03, (byte) 0x46, (byte) 0x65, (byte) 0x77, (byte) 0x59, (byte) 0xbb,
+        (byte) 0xb8, (byte) 0x83, (byte) 0xee, (byte) 0xc2, (byte) 0x3e, (byte) 0x78, (byte) 0xdd, (byte) 0x89,
+        (byte) 0xcd, (byte) 0x9b, (byte) 0x78, (byte) 0x35, (byte) 0xa9, (byte) 0x09, (byte) 0xc8, (byte) 0x77,
+        (byte) 0xdd, (byte) 0xd3, (byte) 0xa0, (byte) 0x64, (byte) 0xb0, (byte) 0x74, (byte) 0x48, (byte) 0x51,
+        (byte) 0x4f, (byte) 0xa0, (byte) 0xae, (byte) 0x33, (byte) 0xb3, (byte) 0x28, (byte) 0xb0, (byte) 0xa8,
+        (byte) 0x78, (byte) 0x8f, (byte) 0xa2, (byte) 0x32, (byte) 0xa6, (byte) 0x0a, (byte) 0xaa, (byte) 0x09,
+        (byte) 0xb5, (byte) 0x8d, (byte) 0x4c, (byte) 0x44, (byte) 0x46, (byte) 0xb4, (byte) 0xd2, (byte) 0x06,
+        (byte) 0x6b, (byte) 0x8c, (byte) 0x51, (byte) 0x6e, (byte) 0x9c, (byte) 0xfa, (byte) 0x1f, (byte) 0x94,
+        (byte) 0x3e, (byte) 0x19, (byte) 0x9c, (byte) 0x63, (byte) 0xfe, (byte) 0xa9, (byte) 0x9a, (byte) 0xe3,
+        (byte) 0x6c, (byte) 0x82, (byte) 0x64, (byte) 0x5f, (byte) 0xca, (byte) 0xc2, (byte) 0x8d, (byte) 0x66,
+        (byte) 0xbe, (byte) 0x12, (byte) 0x6e, (byte) 0xb6, (byte) 0x35, (byte) 0x6d, (byte) 0xaa, (byte) 0xed,
+        (byte) 0x4b, (byte) 0x50, (byte) 0x08, (byte) 0x1c, (byte) 0xbf, (byte) 0x07, (byte) 0x70, (byte) 0x78,
+        (byte) 0xc0, (byte) 0xbb, (byte) 0xc5, (byte) 0x8d, (byte) 0x6c, (byte) 0x8d, (byte) 0x35, (byte) 0xff,
+        (byte) 0x04, (byte) 0x81, (byte) 0xd8, (byte) 0xf4, (byte) 0xd2, (byte) 0x4a, (byte) 0xc3, (byte) 0x05,
+        (byte) 0x23, (byte) 0xcb, (byte) 0xeb, (byte) 0x20, (byte) 0xb1, (byte) 0xd4, (byte) 0x2d, (byte) 0xd8,
+        (byte) 0x7a, (byte) 0xd4, (byte) 0x7e, (byte) 0xf6, (byte) 0xa9, (byte) 0xe8, (byte) 0x72, (byte) 0x69,
+        (byte) 0xfe, (byte) 0xab, (byte) 0x54, (byte) 0x4d, (byte) 0xd1, (byte) 0xf4, (byte) 0x6b, (byte) 0x83,
+        (byte) 0x31, (byte) 0x17, (byte) 0xed, (byte) 0x26, (byte) 0xe9, (byte) 0xd2, (byte) 0x5b, (byte) 0xad,
+        (byte) 0x42, (byte) 0x42, (byte) 0xa5, (byte) 0x8f, (byte) 0x98, (byte) 0x7c, (byte) 0x1b, (byte) 0x5c,
+        (byte) 0x8e, (byte) 0x88, (byte) 0x56, (byte) 0x20, (byte) 0x8e, (byte) 0x48, (byte) 0xf9, (byte) 0x4d,
+        (byte) 0x82, (byte) 0x91, (byte) 0xcb, (byte) 0xc8, (byte) 0x1c, (byte) 0x7c, (byte) 0xa5, (byte) 0x69,
+        (byte) 0x1b, (byte) 0x40, (byte) 0xc2, (byte) 0x4c, (byte) 0x25, (byte) 0x16, (byte) 0x4f, (byte) 0xfa,
+        (byte) 0x09, (byte) 0xeb, (byte) 0xf5, (byte) 0x6c, (byte) 0x55, (byte) 0x3c, (byte) 0x6e, (byte) 0xf7,
+        (byte) 0xc0, (byte) 0xc1, (byte) 0x34, (byte) 0xd1, (byte) 0x53, (byte) 0xa3, (byte) 0x69, (byte) 0x64,
+        (byte) 0xee, (byte) 0xf4, (byte) 0xf9, (byte) 0xc7, (byte) 0x96, (byte) 0x60, (byte) 0x84, (byte) 0x87,
+        (byte) 0xb4, (byte) 0xc7, (byte) 0x3c, (byte) 0x26, (byte) 0xa7, (byte) 0x3a, (byte) 0xbf, (byte) 0x95,
+    };
+
+    private static final byte[] MD5withRSA_Vector2Signature = new byte[] {
+        (byte) 0x04, (byte) 0x17, (byte) 0x83, (byte) 0x10, (byte) 0xe2, (byte) 0x6e, (byte) 0xdf, (byte) 0xa9,
+        (byte) 0xae, (byte) 0xd2, (byte) 0xdc, (byte) 0x5f, (byte) 0x70, (byte) 0x1d, (byte) 0xaf, (byte) 0x54,
+        (byte) 0xc0, (byte) 0x5f, (byte) 0x0b, (byte) 0x2c, (byte) 0xe6, (byte) 0xd0, (byte) 0x00, (byte) 0x18,
+        (byte) 0x4c, (byte) 0xf6, (byte) 0x8f, (byte) 0x18, (byte) 0x10, (byte) 0x74, (byte) 0x90, (byte) 0x99,
+        (byte) 0xa9, (byte) 0x90, (byte) 0x3c, (byte) 0x5a, (byte) 0x38, (byte) 0xd3, (byte) 0x3d, (byte) 0x48,
+        (byte) 0xcf, (byte) 0x31, (byte) 0xaf, (byte) 0x12, (byte) 0x98, (byte) 0xfb, (byte) 0x66, (byte) 0xe8,
+        (byte) 0x58, (byte) 0xec, (byte) 0xca, (byte) 0xe1, (byte) 0x42, (byte) 0xf9, (byte) 0x84, (byte) 0x17,
+        (byte) 0x6f, (byte) 0x4c, (byte) 0x3e, (byte) 0xc4, (byte) 0x40, (byte) 0xc6, (byte) 0x70, (byte) 0xb0,
+        (byte) 0x38, (byte) 0xf3, (byte) 0x47, (byte) 0xeb, (byte) 0x6f, (byte) 0xcb, (byte) 0xea, (byte) 0x21,
+        (byte) 0x41, (byte) 0xf3, (byte) 0xa0, (byte) 0x3e, (byte) 0x42, (byte) 0xad, (byte) 0xa5, (byte) 0xad,
+        (byte) 0x5d, (byte) 0x2c, (byte) 0x1a, (byte) 0x8e, (byte) 0x3e, (byte) 0xb3, (byte) 0xa5, (byte) 0x78,
+        (byte) 0x3d, (byte) 0x56, (byte) 0x09, (byte) 0x93, (byte) 0xc9, (byte) 0x93, (byte) 0xd3, (byte) 0xd2,
+        (byte) 0x9a, (byte) 0xc5, (byte) 0xa5, (byte) 0x2e, (byte) 0xb2, (byte) 0xd8, (byte) 0x37, (byte) 0xc7,
+        (byte) 0x13, (byte) 0x1a, (byte) 0x0b, (byte) 0xda, (byte) 0x50, (byte) 0x28, (byte) 0x6d, (byte) 0x47,
+        (byte) 0x65, (byte) 0x52, (byte) 0xcd, (byte) 0xe7, (byte) 0xec, (byte) 0x57, (byte) 0x00, (byte) 0x41,
+        (byte) 0x34, (byte) 0x28, (byte) 0xb9, (byte) 0x8b, (byte) 0x03, (byte) 0x41, (byte) 0xb6, (byte) 0xd5,
+        (byte) 0xa8, (byte) 0xef, (byte) 0xd3, (byte) 0xdd, (byte) 0x80, (byte) 0xd5, (byte) 0x69, (byte) 0xe4,
+        (byte) 0xf0, (byte) 0x4d, (byte) 0xa4, (byte) 0x7d, (byte) 0x60, (byte) 0x2f, (byte) 0xef, (byte) 0x79,
+        (byte) 0x07, (byte) 0x75, (byte) 0xeb, (byte) 0xf7, (byte) 0x4b, (byte) 0x43, (byte) 0x41, (byte) 0xdb,
+        (byte) 0x33, (byte) 0xad, (byte) 0x9c, (byte) 0x7b, (byte) 0x78, (byte) 0x83, (byte) 0x34, (byte) 0x77,
+        (byte) 0xe4, (byte) 0x80, (byte) 0xbe, (byte) 0xe6, (byte) 0x6f, (byte) 0xdd, (byte) 0xac, (byte) 0xa5,
+        (byte) 0x37, (byte) 0xcf, (byte) 0xb5, (byte) 0x44, (byte) 0x11, (byte) 0x77, (byte) 0x96, (byte) 0x45,
+        (byte) 0xf9, (byte) 0xae, (byte) 0x48, (byte) 0xa6, (byte) 0xbe, (byte) 0x30, (byte) 0x32, (byte) 0xeb,
+        (byte) 0x43, (byte) 0x6f, (byte) 0x66, (byte) 0x39, (byte) 0x57, (byte) 0xf8, (byte) 0xe6, (byte) 0x60,
+        (byte) 0x31, (byte) 0xd0, (byte) 0xfc, (byte) 0xcf, (byte) 0x9f, (byte) 0xe5, (byte) 0x3d, (byte) 0xcf,
+        (byte) 0xbd, (byte) 0x7b, (byte) 0x13, (byte) 0x20, (byte) 0xce, (byte) 0x11, (byte) 0xfd, (byte) 0xe5,
+        (byte) 0xff, (byte) 0x90, (byte) 0x85, (byte) 0xdf, (byte) 0xca, (byte) 0x3d, (byte) 0xd9, (byte) 0x44,
+        (byte) 0x16, (byte) 0xc2, (byte) 0x32, (byte) 0x28, (byte) 0xc7, (byte) 0x01, (byte) 0x6d, (byte) 0xea,
+        (byte) 0xcb, (byte) 0x0d, (byte) 0x85, (byte) 0x08, (byte) 0x6f, (byte) 0xcb, (byte) 0x41, (byte) 0x6a,
+        (byte) 0x3c, (byte) 0x0f, (byte) 0x3d, (byte) 0x38, (byte) 0xb5, (byte) 0x61, (byte) 0xc5, (byte) 0x64,
+        (byte) 0x64, (byte) 0x81, (byte) 0x4c, (byte) 0xcd, (byte) 0xd1, (byte) 0x6a, (byte) 0x87, (byte) 0x28,
+        (byte) 0x02, (byte) 0xaf, (byte) 0x8f, (byte) 0x59, (byte) 0xe5, (byte) 0x67, (byte) 0x25, (byte) 0x00,
+    };
+
+    /*
+     * openssl rsautl -raw -sign -inkey rsa.key | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] NONEwithRSA_Vector1Signature = new byte[] {
+        (byte) 0x35, (byte) 0x43, (byte) 0x38, (byte) 0x44, (byte) 0xAD, (byte) 0x3F,
+        (byte) 0x97, (byte) 0x02, (byte) 0xFB, (byte) 0x59, (byte) 0x1F, (byte) 0x4A,
+        (byte) 0x2B, (byte) 0xB9, (byte) 0x06, (byte) 0xEC, (byte) 0x66, (byte) 0xE6,
+        (byte) 0xD2, (byte) 0xC5, (byte) 0x8B, (byte) 0x7B, (byte) 0xE3, (byte) 0x18,
+        (byte) 0xBF, (byte) 0x07, (byte) 0xD6, (byte) 0x01, (byte) 0xF9, (byte) 0xD9,
+        (byte) 0x89, (byte) 0xC4, (byte) 0xDB, (byte) 0x00, (byte) 0x68, (byte) 0xFF,
+        (byte) 0x9B, (byte) 0x43, (byte) 0x90, (byte) 0xF2, (byte) 0xDB, (byte) 0x83,
+        (byte) 0xF4, (byte) 0x7E, (byte) 0xC6, (byte) 0x81, (byte) 0x01, (byte) 0x3A,
+        (byte) 0x0B, (byte) 0xE5, (byte) 0xED, (byte) 0x08, (byte) 0x73, (byte) 0x3E,
+        (byte) 0xE1, (byte) 0x3F, (byte) 0xDF, (byte) 0x1F, (byte) 0x07, (byte) 0x6D,
+        (byte) 0x22, (byte) 0x8D, (byte) 0xCC, (byte) 0x4E, (byte) 0xE3, (byte) 0x9A,
+        (byte) 0xBC, (byte) 0xCC, (byte) 0x8F, (byte) 0x9E, (byte) 0x9B, (byte) 0x02,
+        (byte) 0x48, (byte) 0x00, (byte) 0xAC, (byte) 0x9F, (byte) 0xA4, (byte) 0x8F,
+        (byte) 0x87, (byte) 0xA1, (byte) 0xA8, (byte) 0xE6, (byte) 0x9D, (byte) 0xCD,
+        (byte) 0x8B, (byte) 0x05, (byte) 0xE9, (byte) 0xD2, (byte) 0x05, (byte) 0x8D,
+        (byte) 0xC9, (byte) 0x95, (byte) 0x16, (byte) 0xD0, (byte) 0xCD, (byte) 0x43,
+        (byte) 0x25, (byte) 0x8A, (byte) 0x11, (byte) 0x46, (byte) 0xD7, (byte) 0x74,
+        (byte) 0x4C, (byte) 0xCF, (byte) 0x58, (byte) 0xF9, (byte) 0xA1, (byte) 0x30,
+        (byte) 0x84, (byte) 0x52, (byte) 0xC9, (byte) 0x01, (byte) 0x5F, (byte) 0x24,
+        (byte) 0x4C, (byte) 0xB1, (byte) 0x9F, (byte) 0x7D, (byte) 0x12, (byte) 0x38,
+        (byte) 0x27, (byte) 0x0F, (byte) 0x5E, (byte) 0xFF, (byte) 0xE0, (byte) 0x55,
+        (byte) 0x8B, (byte) 0xA3, (byte) 0xAD, (byte) 0x60, (byte) 0x35, (byte) 0x83,
+        (byte) 0x58, (byte) 0xAF, (byte) 0x99, (byte) 0xDE, (byte) 0x3F, (byte) 0x5D,
+        (byte) 0x80, (byte) 0x80, (byte) 0xFF, (byte) 0x9B, (byte) 0xDE, (byte) 0x5C,
+        (byte) 0xAB, (byte) 0x97, (byte) 0x43, (byte) 0x64, (byte) 0xD9, (byte) 0x9F,
+        (byte) 0xFB, (byte) 0x67, (byte) 0x65, (byte) 0xA5, (byte) 0x99, (byte) 0xE7,
+        (byte) 0xE6, (byte) 0xEB, (byte) 0x05, (byte) 0x95, (byte) 0xFC, (byte) 0x46,
+        (byte) 0x28, (byte) 0x4B, (byte) 0xD8, (byte) 0x8C, (byte) 0xF5, (byte) 0x0A,
+        (byte) 0xEB, (byte) 0x1F, (byte) 0x30, (byte) 0xEA, (byte) 0xE7, (byte) 0x67,
+        (byte) 0x11, (byte) 0x25, (byte) 0xF0, (byte) 0x44, (byte) 0x75, (byte) 0x74,
+        (byte) 0x94, (byte) 0x06, (byte) 0x78, (byte) 0xD0, (byte) 0x21, (byte) 0xF4,
+        (byte) 0x3F, (byte) 0xC8, (byte) 0xC4, (byte) 0x4A, (byte) 0x57, (byte) 0xBE,
+        (byte) 0x02, (byte) 0x3C, (byte) 0x93, (byte) 0xF6, (byte) 0x95, (byte) 0xFB,
+        (byte) 0xD1, (byte) 0x77, (byte) 0x8B, (byte) 0x43, (byte) 0xF0, (byte) 0xB9,
+        (byte) 0x7D, (byte) 0xE0, (byte) 0x32, (byte) 0xE1, (byte) 0x72, (byte) 0xB5,
+        (byte) 0x62, (byte) 0x3F, (byte) 0x86, (byte) 0xC3, (byte) 0xD4, (byte) 0x5F,
+        (byte) 0x5E, (byte) 0x54, (byte) 0x1B, (byte) 0x5B, (byte) 0xE6, (byte) 0x74,
+        (byte) 0xA1, (byte) 0x0B, (byte) 0xE5, (byte) 0x18, (byte) 0xD2, (byte) 0x4F,
+        (byte) 0x93, (byte) 0xF3, (byte) 0x09, (byte) 0x58, (byte) 0xCE, (byte) 0xF0,
+        (byte) 0xA3, (byte) 0x61, (byte) 0xE4, (byte) 0x6E, (byte) 0x46, (byte) 0x45,
+        (byte) 0x89, (byte) 0x50, (byte) 0xBD, (byte) 0x03, (byte) 0x3F, (byte) 0x38,
+        (byte) 0xDA, (byte) 0x5D, (byte) 0xD0, (byte) 0x1B, (byte) 0x1F, (byte) 0xB1,
+        (byte) 0xEE, (byte) 0x89, (byte) 0x59, (byte) 0xC5,
+    };
+
+    /*
+     * echo "This is a signed message from Kenny Root." | openssl sha1 -binary -out digest.bin \
+     *     && openssl pkeyutl -sign -in digest.bin -inkey privkey.pem \
+     *         -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha1 -pkeyopt rsa_pss_saltlen:20 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA1withRSAPSS_Vector2Signature = new byte[] {
+        (byte) 0x66, (byte) 0xE3, (byte) 0xA5, (byte) 0x20, (byte) 0xE9, (byte) 0x5D,
+        (byte) 0xDF, (byte) 0x99, (byte) 0xA6, (byte) 0x04, (byte) 0x77, (byte) 0xF8,
+        (byte) 0x39, (byte) 0x78, (byte) 0x74, (byte) 0xF5, (byte) 0xC2, (byte) 0x4E,
+        (byte) 0x9E, (byte) 0xEB, (byte) 0x24, (byte) 0xDE, (byte) 0xB4, (byte) 0x36,
+        (byte) 0x69, (byte) 0x1F, (byte) 0xAC, (byte) 0x01, (byte) 0xFF, (byte) 0x5A,
+        (byte) 0xE3, (byte) 0x89, (byte) 0x8A, (byte) 0xE9, (byte) 0x92, (byte) 0x32,
+        (byte) 0xA7, (byte) 0xA4, (byte) 0xC0, (byte) 0x25, (byte) 0x00, (byte) 0x14,
+        (byte) 0xFF, (byte) 0x38, (byte) 0x19, (byte) 0x37, (byte) 0x84, (byte) 0x1A,
+        (byte) 0x3D, (byte) 0xCA, (byte) 0xEE, (byte) 0xF3, (byte) 0xC6, (byte) 0x91,
+        (byte) 0xED, (byte) 0x02, (byte) 0xE6, (byte) 0x1D, (byte) 0x73, (byte) 0xDA,
+        (byte) 0xD4, (byte) 0x55, (byte) 0x93, (byte) 0x54, (byte) 0x9A, (byte) 0xE6,
+        (byte) 0x2E, (byte) 0x7D, (byte) 0x5C, (byte) 0x41, (byte) 0xAF, (byte) 0xED,
+        (byte) 0xAD, (byte) 0x8E, (byte) 0x7F, (byte) 0x47, (byte) 0x3B, (byte) 0x23,
+        (byte) 0xC3, (byte) 0xB8, (byte) 0xBB, (byte) 0xCD, (byte) 0x87, (byte) 0xC4,
+        (byte) 0xA3, (byte) 0x32, (byte) 0x16, (byte) 0x57, (byte) 0xCC, (byte) 0xB8,
+        (byte) 0xB6, (byte) 0x96, (byte) 0x84, (byte) 0x1A, (byte) 0xBC, (byte) 0xF8,
+        (byte) 0x09, (byte) 0x53, (byte) 0xB0, (byte) 0x9D, (byte) 0xE1, (byte) 0x6F,
+        (byte) 0xB2, (byte) 0xEB, (byte) 0x83, (byte) 0xDC, (byte) 0x61, (byte) 0x31,
+        (byte) 0xD7, (byte) 0x02, (byte) 0xB4, (byte) 0xD1, (byte) 0xBA, (byte) 0xBD,
+        (byte) 0xF0, (byte) 0x78, (byte) 0xC6, (byte) 0xBE, (byte) 0x1F, (byte) 0xB0,
+        (byte) 0xE1, (byte) 0xCA, (byte) 0x32, (byte) 0x57, (byte) 0x9F, (byte) 0x8C,
+        (byte) 0xD3, (byte) 0xBB, (byte) 0x04, (byte) 0x1B, (byte) 0x30, (byte) 0x74,
+        (byte) 0x5D, (byte) 0xEA, (byte) 0xD3, (byte) 0x6B, (byte) 0x74, (byte) 0x31,
+        (byte) 0x6F, (byte) 0x33, (byte) 0x5A, (byte) 0x70, (byte) 0x96, (byte) 0x8B,
+        (byte) 0xCB, (byte) 0x22, (byte) 0xF3, (byte) 0xAA, (byte) 0x74, (byte) 0x82,
+        (byte) 0xB2, (byte) 0x82, (byte) 0x71, (byte) 0x4D, (byte) 0x42, (byte) 0x13,
+        (byte) 0x3F, (byte) 0xEA, (byte) 0xE3, (byte) 0x39, (byte) 0xC5, (byte) 0x03,
+        (byte) 0x27, (byte) 0xFF, (byte) 0x78, (byte) 0xB2, (byte) 0xA6, (byte) 0x71,
+        (byte) 0x07, (byte) 0x1C, (byte) 0xB3, (byte) 0x97, (byte) 0xFB, (byte) 0xE8,
+        (byte) 0x85, (byte) 0x6D, (byte) 0x14, (byte) 0xDF, (byte) 0xF9, (byte) 0x7D,
+        (byte) 0x0D, (byte) 0x0C, (byte) 0x9F, (byte) 0xC3, (byte) 0xE2, (byte) 0xDB,
+        (byte) 0xE0, (byte) 0xA5, (byte) 0x05, (byte) 0xBC, (byte) 0x47, (byte) 0x36,
+        (byte) 0xEB, (byte) 0x1E, (byte) 0xBA, (byte) 0x60, (byte) 0x12, (byte) 0x19,
+        (byte) 0xA5, (byte) 0x7E, (byte) 0x55, (byte) 0x0C, (byte) 0x9B, (byte) 0xD4,
+        (byte) 0x9A, (byte) 0xE9, (byte) 0x72, (byte) 0x5C, (byte) 0x5B, (byte) 0xF4,
+        (byte) 0xAA, (byte) 0x4A, (byte) 0x12, (byte) 0x8B, (byte) 0xC2, (byte) 0x8E,
+        (byte) 0xC2, (byte) 0x9A, (byte) 0x3E, (byte) 0x0C, (byte) 0x40, (byte) 0xA4,
+        (byte) 0x0A, (byte) 0xFF, (byte) 0xF8, (byte) 0xC1, (byte) 0x85, (byte) 0x59,
+        (byte) 0xDA, (byte) 0xC6, (byte) 0x8C, (byte) 0x83, (byte) 0x2A, (byte) 0x68,
+        (byte) 0x84, (byte) 0x53, (byte) 0x17, (byte) 0x28, (byte) 0x78, (byte) 0x3F,
+        (byte) 0x5A, (byte) 0xA4, (byte) 0x04, (byte) 0xE6, (byte) 0x23, (byte) 0x8D,
+        (byte) 0x2A, (byte) 0x71, (byte) 0xC1, (byte) 0xBC, (byte) 0x1C, (byte) 0xFD,
+        (byte) 0x75, (byte) 0x16, (byte) 0x6E, (byte) 0x85,
+    };
+    private static final PSSParameterSpec SHA1withRSAPSS_Vector2Signature_ParameterSpec =
+            new PSSParameterSpec("SHA-1", "MGF1", MGF1ParameterSpec.SHA1, 20, 1);
+
+    /*
+     * echo "This is a signed message from Kenny Root." | openssl sha1 -binary -out digest.bin \
+     *     && openssl pkeyutl -sign -in digest.bin -inkey privkey.pem \
+     *         -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha1 -pkeyopt rsa_pss_saltlen:0 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA1withRSAPSS_NoSalt_Vector2Signature = new byte[] {
+        (byte) 0x31, (byte) 0x61, (byte) 0xA5, (byte) 0x47, (byte) 0x28, (byte) 0x44,
+        (byte) 0x48, (byte) 0x5A, (byte) 0xDA, (byte) 0x78, (byte) 0xA7, (byte) 0x85,
+        (byte) 0xE9, (byte) 0x64, (byte) 0x69, (byte) 0xCF, (byte) 0x14, (byte) 0x07,
+        (byte) 0x3F, (byte) 0xA8, (byte) 0xDB, (byte) 0xFC, (byte) 0xB7, (byte) 0x89,
+        (byte) 0x87, (byte) 0x74, (byte) 0xB9, (byte) 0x81, (byte) 0x37, (byte) 0x62,
+        (byte) 0xD1, (byte) 0x07, (byte) 0x0F, (byte) 0x3D, (byte) 0xDF, (byte) 0xA8,
+        (byte) 0x84, (byte) 0x38, (byte) 0x31, (byte) 0xEB, (byte) 0x17, (byte) 0x3F,
+        (byte) 0xE0, (byte) 0x28, (byte) 0x75, (byte) 0x1F, (byte) 0xE9, (byte) 0x4D,
+        (byte) 0xD3, (byte) 0x62, (byte) 0xFA, (byte) 0xCF, (byte) 0xCC, (byte) 0x2E,
+        (byte) 0xC7, (byte) 0x81, (byte) 0xE1, (byte) 0xEA, (byte) 0xEC, (byte) 0x78,
+        (byte) 0xFE, (byte) 0x19, (byte) 0x59, (byte) 0x54, (byte) 0x1D, (byte) 0x27,
+        (byte) 0xED, (byte) 0x0C, (byte) 0x54, (byte) 0xDF, (byte) 0xE3, (byte) 0x44,
+        (byte) 0x31, (byte) 0x21, (byte) 0x31, (byte) 0xA7, (byte) 0x23, (byte) 0xC4,
+        (byte) 0xE2, (byte) 0x69, (byte) 0x8A, (byte) 0xB3, (byte) 0x1A, (byte) 0x72,
+        (byte) 0x4F, (byte) 0x4E, (byte) 0x82, (byte) 0x86, (byte) 0x2D, (byte) 0x2B,
+        (byte) 0x85, (byte) 0xFE, (byte) 0x4A, (byte) 0x28, (byte) 0x90, (byte) 0xF7,
+        (byte) 0xDF, (byte) 0xD6, (byte) 0xB1, (byte) 0x3E, (byte) 0xC6, (byte) 0xFB,
+        (byte) 0x76, (byte) 0x7B, (byte) 0x3D, (byte) 0x12, (byte) 0x81, (byte) 0x6E,
+        (byte) 0xFD, (byte) 0x00, (byte) 0x7D, (byte) 0xD0, (byte) 0xDC, (byte) 0x25,
+        (byte) 0xD0, (byte) 0x86, (byte) 0x6C, (byte) 0xE8, (byte) 0x0F, (byte) 0x09,
+        (byte) 0x82, (byte) 0x74, (byte) 0x89, (byte) 0x79, (byte) 0x69, (byte) 0x73,
+        (byte) 0x37, (byte) 0x64, (byte) 0xEE, (byte) 0x53, (byte) 0x57, (byte) 0x20,
+        (byte) 0xFA, (byte) 0x0B, (byte) 0x4A, (byte) 0x5A, (byte) 0x4D, (byte) 0x33,
+        (byte) 0xAC, (byte) 0x8B, (byte) 0x04, (byte) 0xA5, (byte) 0x4A, (byte) 0x1A,
+        (byte) 0x9B, (byte) 0x66, (byte) 0xAA, (byte) 0x0B, (byte) 0x3D, (byte) 0x15,
+        (byte) 0xD9, (byte) 0x3E, (byte) 0x2F, (byte) 0xD2, (byte) 0xA1, (byte) 0x28,
+        (byte) 0x13, (byte) 0x59, (byte) 0x98, (byte) 0xC3, (byte) 0x45, (byte) 0x7C,
+        (byte) 0xEE, (byte) 0x60, (byte) 0xD0, (byte) 0xBD, (byte) 0x42, (byte) 0x16,
+        (byte) 0x84, (byte) 0x19, (byte) 0xF6, (byte) 0xD9, (byte) 0xF7, (byte) 0x7D,
+        (byte) 0x77, (byte) 0xAD, (byte) 0x60, (byte) 0xE2, (byte) 0xE3, (byte) 0x22,
+        (byte) 0xB9, (byte) 0xFA, (byte) 0xD5, (byte) 0xFA, (byte) 0x6E, (byte) 0x1F,
+        (byte) 0x69, (byte) 0x3F, (byte) 0xB1, (byte) 0xA7, (byte) 0x1A, (byte) 0x22,
+        (byte) 0xF7, (byte) 0x31, (byte) 0x97, (byte) 0x68, (byte) 0x62, (byte) 0x0F,
+        (byte) 0x39, (byte) 0xB0, (byte) 0xE7, (byte) 0x63, (byte) 0xAE, (byte) 0x65,
+        (byte) 0x69, (byte) 0xD0, (byte) 0xD3, (byte) 0x56, (byte) 0xC9, (byte) 0xA6,
+        (byte) 0xA4, (byte) 0xA5, (byte) 0xA4, (byte) 0x61, (byte) 0xA9, (byte) 0xC4,
+        (byte) 0x45, (byte) 0xCD, (byte) 0x49, (byte) 0x76, (byte) 0xC8, (byte) 0x53,
+        (byte) 0x46, (byte) 0xD0, (byte) 0x63, (byte) 0x35, (byte) 0x89, (byte) 0x04,
+        (byte) 0x22, (byte) 0xD7, (byte) 0xB6, (byte) 0x63, (byte) 0xAF, (byte) 0xC2,
+        (byte) 0x97, (byte) 0x10, (byte) 0xDF, (byte) 0xDE, (byte) 0xE6, (byte) 0x39,
+        (byte) 0x25, (byte) 0x2F, (byte) 0xEA, (byte) 0xD8, (byte) 0x56, (byte) 0x5A,
+        (byte) 0xC1, (byte) 0xB8, (byte) 0xCA, (byte) 0xC1, (byte) 0x8A, (byte) 0xB8,
+        (byte) 0x87, (byte) 0x2F, (byte) 0xCD, (byte) 0x21,
+    };
+    private static final PSSParameterSpec SHA1withRSAPSS_NoSalt_Vector2Signature_ParameterSpec =
+            new PSSParameterSpec("SHA-1", "MGF1", MGF1ParameterSpec.SHA1, 0, 1);
+
+    /*
+     * echo "This is a signed message from Kenny Root." | openssl sha1 -binary -out digest.bin \
+     *     && openssl pkeyutl -sign -in digest.bin -inkey privkey.pem \
+     *         -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha1 -pkeyopt rsa_pss_saltlen:234 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA1withRSAPSS_MaxSalt_Vector2Signature = new byte[] {
+        (byte) 0x49, (byte) 0xDB, (byte) 0xAD, (byte) 0x48, (byte) 0x7C, (byte) 0x06,
+        (byte) 0x03, (byte) 0x7C, (byte) 0x58, (byte) 0xE1, (byte) 0x38, (byte) 0x20,
+        (byte) 0x46, (byte) 0x28, (byte) 0x60, (byte) 0x64, (byte) 0x94, (byte) 0x51,
+        (byte) 0xA3, (byte) 0xD1, (byte) 0xC9, (byte) 0x52, (byte) 0xC6, (byte) 0x2A,
+        (byte) 0xB3, (byte) 0xCC, (byte) 0xD6, (byte) 0x19, (byte) 0x50, (byte) 0x99,
+        (byte) 0x60, (byte) 0x58, (byte) 0xA2, (byte) 0x86, (byte) 0xA8, (byte) 0x74,
+        (byte) 0x50, (byte) 0x8C, (byte) 0x0E, (byte) 0x32, (byte) 0x58, (byte) 0x56,
+        (byte) 0x6D, (byte) 0x30, (byte) 0x38, (byte) 0xFB, (byte) 0x26, (byte) 0xC3,
+        (byte) 0xFD, (byte) 0x8E, (byte) 0x36, (byte) 0x73, (byte) 0x82, (byte) 0x9A,
+        (byte) 0xB4, (byte) 0xE5, (byte) 0x22, (byte) 0x96, (byte) 0x55, (byte) 0x3C,
+        (byte) 0x18, (byte) 0xD7, (byte) 0x46, (byte) 0xF1, (byte) 0x7C, (byte) 0xE6,
+        (byte) 0x8E, (byte) 0x0A, (byte) 0x18, (byte) 0xA7, (byte) 0x29, (byte) 0x96,
+        (byte) 0x8D, (byte) 0xFC, (byte) 0x0E, (byte) 0xBE, (byte) 0x91, (byte) 0xA0,
+        (byte) 0xF8, (byte) 0xE2, (byte) 0x70, (byte) 0x5A, (byte) 0xE3, (byte) 0x76,
+        (byte) 0xAC, (byte) 0x18, (byte) 0x10, (byte) 0xB4, (byte) 0xB1, (byte) 0xFF,
+        (byte) 0x58, (byte) 0xBC, (byte) 0x10, (byte) 0xF5, (byte) 0x88, (byte) 0x2F,
+        (byte) 0x0B, (byte) 0x10, (byte) 0x9D, (byte) 0x52, (byte) 0x2D, (byte) 0x42,
+        (byte) 0xDB, (byte) 0xFD, (byte) 0xA7, (byte) 0x23, (byte) 0x3C, (byte) 0x4B,
+        (byte) 0xB3, (byte) 0xD2, (byte) 0x96, (byte) 0x1B, (byte) 0xCE, (byte) 0xB3,
+        (byte) 0xA3, (byte) 0xC3, (byte) 0x42, (byte) 0xA4, (byte) 0x0E, (byte) 0x35,
+        (byte) 0x5C, (byte) 0xC2, (byte) 0x32, (byte) 0xC7, (byte) 0x8C, (byte) 0xFC,
+        (byte) 0x7F, (byte) 0xE0, (byte) 0xF7, (byte) 0x1D, (byte) 0x38, (byte) 0x21,
+        (byte) 0x3C, (byte) 0xDF, (byte) 0x82, (byte) 0x1A, (byte) 0xBD, (byte) 0x83,
+        (byte) 0xE9, (byte) 0x56, (byte) 0xF0, (byte) 0xF1, (byte) 0x54, (byte) 0x76,
+        (byte) 0xE3, (byte) 0xCE, (byte) 0x86, (byte) 0x69, (byte) 0xC2, (byte) 0x61,
+        (byte) 0x6D, (byte) 0x8E, (byte) 0xF5, (byte) 0xA3, (byte) 0x61, (byte) 0xCA,
+        (byte) 0x16, (byte) 0xCB, (byte) 0x7A, (byte) 0xF5, (byte) 0xBF, (byte) 0x36,
+        (byte) 0xCB, (byte) 0x7D, (byte) 0xB1, (byte) 0xE9, (byte) 0x70, (byte) 0x41,
+        (byte) 0xCF, (byte) 0x89, (byte) 0x51, (byte) 0x13, (byte) 0xCC, (byte) 0x95,
+        (byte) 0x50, (byte) 0xC8, (byte) 0xB6, (byte) 0x30, (byte) 0x35, (byte) 0xE3,
+        (byte) 0x13, (byte) 0x08, (byte) 0xF6, (byte) 0xBE, (byte) 0x20, (byte) 0xF1,
+        (byte) 0x48, (byte) 0x4D, (byte) 0x46, (byte) 0x95, (byte) 0xFE, (byte) 0x9E,
+        (byte) 0xD2, (byte) 0xD5, (byte) 0x29, (byte) 0x81, (byte) 0x2E, (byte) 0x0F,
+        (byte) 0x6F, (byte) 0xA7, (byte) 0x02, (byte) 0x15, (byte) 0xCA, (byte) 0x75,
+        (byte) 0x77, (byte) 0x29, (byte) 0x7C, (byte) 0x3A, (byte) 0xE3, (byte) 0x2B,
+        (byte) 0xD7, (byte) 0x3D, (byte) 0x5C, (byte) 0x94, (byte) 0x3B, (byte) 0x2A,
+        (byte) 0x91, (byte) 0xDB, (byte) 0xFA, (byte) 0x69, (byte) 0x47, (byte) 0x1C,
+        (byte) 0x2C, (byte) 0x46, (byte) 0x49, (byte) 0xE6, (byte) 0x37, (byte) 0x5D,
+        (byte) 0x78, (byte) 0x71, (byte) 0x76, (byte) 0xC1, (byte) 0xB6, (byte) 0x2E,
+        (byte) 0x4E, (byte) 0x3C, (byte) 0x83, (byte) 0x6F, (byte) 0x82, (byte) 0xC3,
+        (byte) 0xD8, (byte) 0x50, (byte) 0xD7, (byte) 0x1B, (byte) 0xAF, (byte) 0xF9,
+        (byte) 0xE3, (byte) 0xF1, (byte) 0x47, (byte) 0xC8, (byte) 0x12, (byte) 0x86,
+        (byte) 0x82, (byte) 0x9D, (byte) 0x3F, (byte) 0xCE,
+    };
+    private static final PSSParameterSpec SHA1withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec =
+            new PSSParameterSpec("SHA-1", "MGF1", MGF1ParameterSpec.SHA1, 234, 1);
+
+    /*
+     * echo "This is a signed message from Kenny Root." | openssl sha224 -binary -out digest.bin \
+     *     && openssl pkeyutl -sign -in digest.bin -inkey privkey.pem \
+     *         -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha224 -pkeyopt rsa_pss_saltlen:28 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA224withRSAPSS_Vector2Signature = new byte[] {
+        (byte) 0x86, (byte) 0x41, (byte) 0xCC, (byte) 0x4B, (byte) 0x82, (byte) 0x74,
+        (byte) 0x04, (byte) 0x43, (byte) 0x8C, (byte) 0xAB, (byte) 0xF6, (byte) 0x3B,
+        (byte) 0xFB, (byte) 0x94, (byte) 0xBC, (byte) 0x4C, (byte) 0x0A, (byte) 0xFE,
+        (byte) 0x0F, (byte) 0x4F, (byte) 0x0F, (byte) 0x9F, (byte) 0x84, (byte) 0x35,
+        (byte) 0x57, (byte) 0x8B, (byte) 0x8D, (byte) 0xC3, (byte) 0x58, (byte) 0xA6,
+        (byte) 0x70, (byte) 0xAC, (byte) 0x40, (byte) 0x6D, (byte) 0xBC, (byte) 0xC1,
+        (byte) 0x6A, (byte) 0xFA, (byte) 0x31, (byte) 0x3B, (byte) 0x7A, (byte) 0x23,
+        (byte) 0xCA, (byte) 0x1F, (byte) 0xCD, (byte) 0xA7, (byte) 0xE3, (byte) 0xD6,
+        (byte) 0x7C, (byte) 0x2C, (byte) 0xF3, (byte) 0x6F, (byte) 0xF5, (byte) 0x82,
+        (byte) 0x9E, (byte) 0x18, (byte) 0x70, (byte) 0x90, (byte) 0xE6, (byte) 0xA3,
+        (byte) 0x44, (byte) 0x61, (byte) 0xB6, (byte) 0x46, (byte) 0x9B, (byte) 0x0D,
+        (byte) 0xE5, (byte) 0x3C, (byte) 0xAE, (byte) 0x22, (byte) 0xF4, (byte) 0x87,
+        (byte) 0xB7, (byte) 0x03, (byte) 0xD8, (byte) 0x42, (byte) 0x33, (byte) 0x4E,
+        (byte) 0xCC, (byte) 0x7A, (byte) 0xDF, (byte) 0xD7, (byte) 0x57, (byte) 0xEB,
+        (byte) 0x51, (byte) 0x6C, (byte) 0xB1, (byte) 0x99, (byte) 0x4D, (byte) 0x94,
+        (byte) 0x82, (byte) 0xA7, (byte) 0x69, (byte) 0x85, (byte) 0x8D, (byte) 0x82,
+        (byte) 0x18, (byte) 0xE4, (byte) 0x53, (byte) 0xF5, (byte) 0x9F, (byte) 0x82,
+        (byte) 0x1C, (byte) 0xE1, (byte) 0x25, (byte) 0x1C, (byte) 0x8E, (byte) 0xE7,
+        (byte) 0xC1, (byte) 0xEC, (byte) 0xBE, (byte) 0x3F, (byte) 0xC3, (byte) 0xED,
+        (byte) 0x41, (byte) 0x89, (byte) 0x94, (byte) 0x13, (byte) 0x11, (byte) 0x75,
+        (byte) 0x3F, (byte) 0x38, (byte) 0x52, (byte) 0x58, (byte) 0xAB, (byte) 0x88,
+        (byte) 0x01, (byte) 0x30, (byte) 0xB4, (byte) 0xCD, (byte) 0x45, (byte) 0x3E,
+        (byte) 0x1A, (byte) 0x5F, (byte) 0x36, (byte) 0xF8, (byte) 0x51, (byte) 0x90,
+        (byte) 0x6E, (byte) 0x6F, (byte) 0x31, (byte) 0x9D, (byte) 0x40, (byte) 0x90,
+        (byte) 0x1A, (byte) 0xA8, (byte) 0x10, (byte) 0xEF, (byte) 0x9D, (byte) 0xF8,
+        (byte) 0xB0, (byte) 0x03, (byte) 0x01, (byte) 0xFB, (byte) 0xD8, (byte) 0x3D,
+        (byte) 0x83, (byte) 0x79, (byte) 0x01, (byte) 0xA7, (byte) 0x82, (byte) 0xC2,
+        (byte) 0x46, (byte) 0x35, (byte) 0x68, (byte) 0xD2, (byte) 0x08, (byte) 0x81,
+        (byte) 0x31, (byte) 0x14, (byte) 0xE8, (byte) 0x13, (byte) 0x8C, (byte) 0xD4,
+        (byte) 0xC4, (byte) 0xCB, (byte) 0xB9, (byte) 0x85, (byte) 0x25, (byte) 0x93,
+        (byte) 0x40, (byte) 0x88, (byte) 0x34, (byte) 0x11, (byte) 0xDA, (byte) 0xFF,
+        (byte) 0xEF, (byte) 0x4D, (byte) 0xDC, (byte) 0x31, (byte) 0x74, (byte) 0x7B,
+        (byte) 0x5E, (byte) 0xA7, (byte) 0x51, (byte) 0x15, (byte) 0x13, (byte) 0xB1,
+        (byte) 0x9E, (byte) 0x06, (byte) 0x51, (byte) 0xBA, (byte) 0x11, (byte) 0xDA,
+        (byte) 0x64, (byte) 0x1B, (byte) 0x78, (byte) 0x76, (byte) 0x57, (byte) 0x96,
+        (byte) 0xF3, (byte) 0x1B, (byte) 0x86, (byte) 0xB2, (byte) 0xF3, (byte) 0x66,
+        (byte) 0x64, (byte) 0x2B, (byte) 0x04, (byte) 0x81, (byte) 0x8C, (byte) 0xDC,
+        (byte) 0xE0, (byte) 0xEA, (byte) 0x66, (byte) 0x62, (byte) 0x44, (byte) 0x31,
+        (byte) 0xA2, (byte) 0x19, (byte) 0xF1, (byte) 0x77, (byte) 0x67, (byte) 0x58,
+        (byte) 0x18, (byte) 0x5B, (byte) 0xCB, (byte) 0xBA, (byte) 0x28, (byte) 0x91,
+        (byte) 0x47, (byte) 0x5B, (byte) 0x4F, (byte) 0x17, (byte) 0x23, (byte) 0x2A,
+        (byte) 0xE4, (byte) 0xB0, (byte) 0xAE, (byte) 0x82, (byte) 0x4E, (byte) 0xCA,
+        (byte) 0xA6, (byte) 0x12, (byte) 0xCA, (byte) 0x70,
+    };
+    private static final PSSParameterSpec SHA224withRSAPSS_Vector2Signature_ParameterSpec =
+            new PSSParameterSpec("SHA-224", "MGF1", new MGF1ParameterSpec("SHA-224"), 28, 1);
+
+    /*
+     * echo "This is a signed message from Kenny Root." | openssl sha224 -binary -out digest.bin \
+     *     && openssl pkeyutl -sign -in digest.bin -inkey privkey.pem \
+     *         -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha224 -pkeyopt rsa_pss_saltlen:0 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA224withRSAPSS_NoSalt_Vector2Signature = new byte[] {
+        (byte) 0xD8, (byte) 0x38, (byte) 0x48, (byte) 0xCD, (byte) 0xA4, (byte) 0x09,
+        (byte) 0x36, (byte) 0x35, (byte) 0x47, (byte) 0x55, (byte) 0xDB, (byte) 0x6C,
+        (byte) 0x2D, (byte) 0x83, (byte) 0x17, (byte) 0x10, (byte) 0x3E, (byte) 0xCE,
+        (byte) 0x95, (byte) 0x02, (byte) 0x58, (byte) 0xCE, (byte) 0xA8, (byte) 0x02,
+        (byte) 0x44, (byte) 0xB7, (byte) 0xE4, (byte) 0x32, (byte) 0x3D, (byte) 0x50,
+        (byte) 0xE1, (byte) 0x8C, (byte) 0xF3, (byte) 0x24, (byte) 0x6F, (byte) 0xA4,
+        (byte) 0x2D, (byte) 0xD7, (byte) 0xFB, (byte) 0x70, (byte) 0x97, (byte) 0xBE,
+        (byte) 0xED, (byte) 0x27, (byte) 0x2D, (byte) 0x22, (byte) 0xDC, (byte) 0x62,
+        (byte) 0x97, (byte) 0x66, (byte) 0x39, (byte) 0xE0, (byte) 0x36, (byte) 0x5F,
+        (byte) 0x07, (byte) 0x78, (byte) 0xAF, (byte) 0x5E, (byte) 0xDC, (byte) 0xFD,
+        (byte) 0x21, (byte) 0xA8, (byte) 0xD5, (byte) 0xA7, (byte) 0xD1, (byte) 0xBA,
+        (byte) 0x1C, (byte) 0xDA, (byte) 0xCA, (byte) 0x80, (byte) 0x72, (byte) 0x8A,
+        (byte) 0xDD, (byte) 0x5C, (byte) 0x16, (byte) 0x6A, (byte) 0x47, (byte) 0xFC,
+        (byte) 0x11, (byte) 0x42, (byte) 0x7E, (byte) 0x4E, (byte) 0x3F, (byte) 0x49,
+        (byte) 0xCF, (byte) 0x2F, (byte) 0x54, (byte) 0xD7, (byte) 0x13, (byte) 0x76,
+        (byte) 0x5D, (byte) 0xE9, (byte) 0x2A, (byte) 0x29, (byte) 0xCC, (byte) 0x73,
+        (byte) 0xDB, (byte) 0xE5, (byte) 0xDE, (byte) 0x48, (byte) 0xA2, (byte) 0xE9,
+        (byte) 0xD1, (byte) 0xD0, (byte) 0x35, (byte) 0xFE, (byte) 0xA1, (byte) 0x1C,
+        (byte) 0x13, (byte) 0x04, (byte) 0x75, (byte) 0x77, (byte) 0xF4, (byte) 0x55,
+        (byte) 0x03, (byte) 0xC4, (byte) 0x6D, (byte) 0xAC, (byte) 0x25, (byte) 0x1D,
+        (byte) 0x57, (byte) 0xFF, (byte) 0x0D, (byte) 0xE0, (byte) 0x91, (byte) 0xEA,
+        (byte) 0xF6, (byte) 0x1F, (byte) 0x3F, (byte) 0x69, (byte) 0xD6, (byte) 0x00,
+        (byte) 0xBD, (byte) 0x89, (byte) 0xEA, (byte) 0xD3, (byte) 0x31, (byte) 0x80,
+        (byte) 0x5E, (byte) 0x04, (byte) 0x4C, (byte) 0x59, (byte) 0xDE, (byte) 0xD0,
+        (byte) 0x62, (byte) 0x93, (byte) 0x3B, (byte) 0xC9, (byte) 0x9F, (byte) 0xE7,
+        (byte) 0x69, (byte) 0xC0, (byte) 0xB8, (byte) 0xED, (byte) 0xBF, (byte) 0x0D,
+        (byte) 0x60, (byte) 0x28, (byte) 0x55, (byte) 0x20, (byte) 0x0C, (byte) 0x9F,
+        (byte) 0xA2, (byte) 0x42, (byte) 0x34, (byte) 0x95, (byte) 0xAE, (byte) 0xF8,
+        (byte) 0x67, (byte) 0x7C, (byte) 0xF1, (byte) 0xA0, (byte) 0xC0, (byte) 0x74,
+        (byte) 0xF2, (byte) 0xDF, (byte) 0x75, (byte) 0x5B, (byte) 0x6E, (byte) 0x2F,
+        (byte) 0xFB, (byte) 0x1F, (byte) 0xDD, (byte) 0xC3, (byte) 0xD3, (byte) 0x90,
+        (byte) 0x0A, (byte) 0x33, (byte) 0xF6, (byte) 0x03, (byte) 0x16, (byte) 0xC4,
+        (byte) 0xF8, (byte) 0xED, (byte) 0xB7, (byte) 0x45, (byte) 0x39, (byte) 0x5D,
+        (byte) 0x7C, (byte) 0xF8, (byte) 0x82, (byte) 0xCE, (byte) 0x7D, (byte) 0xFB,
+        (byte) 0x02, (byte) 0x2D, (byte) 0xE0, (byte) 0x96, (byte) 0x35, (byte) 0x60,
+        (byte) 0x5D, (byte) 0xBC, (byte) 0x35, (byte) 0x80, (byte) 0x4C, (byte) 0x39,
+        (byte) 0x7C, (byte) 0xE7, (byte) 0xD4, (byte) 0xB4, (byte) 0x19, (byte) 0xD1,
+        (byte) 0xE5, (byte) 0x8E, (byte) 0x6D, (byte) 0x25, (byte) 0x0C, (byte) 0xB9,
+        (byte) 0x0C, (byte) 0x8D, (byte) 0x45, (byte) 0xE4, (byte) 0x67, (byte) 0x73,
+        (byte) 0xCF, (byte) 0x87, (byte) 0x7C, (byte) 0x78, (byte) 0xAA, (byte) 0xB9,
+        (byte) 0x42, (byte) 0xAE, (byte) 0x7F, (byte) 0xB8, (byte) 0xEC, (byte) 0x4F,
+        (byte) 0xD2, (byte) 0x85, (byte) 0x01, (byte) 0x80, (byte) 0x00, (byte) 0xBD,
+        (byte) 0xF5, (byte) 0xEA, (byte) 0x44, (byte) 0x6D,
+    };
+    private static final PSSParameterSpec SHA224withRSAPSS_NoSalt_Vector2Signature_ParameterSpec =
+            new PSSParameterSpec("SHA-224", "MGF1", new MGF1ParameterSpec("SHA-224"), 0, 1);
+
+    /*
+     * echo "This is a signed message from Kenny Root." | openssl sha224 -binary -out digest.bin \
+     *     && openssl pkeyutl -sign -in digest.bin -inkey privkey.pem \
+     *         -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha224 -pkeyopt rsa_pss_saltlen:226 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA224withRSAPSS_MaxSalt_Vector2Signature = new byte[] {
+        (byte) 0x2C, (byte) 0x19, (byte) 0x5E, (byte) 0x63, (byte) 0xC5, (byte) 0x32,
+        (byte) 0xC3, (byte) 0xC7, (byte) 0x52, (byte) 0xE9, (byte) 0x69, (byte) 0x4C,
+        (byte) 0x04, (byte) 0xE5, (byte) 0x4A, (byte) 0xF2, (byte) 0x72, (byte) 0x78,
+        (byte) 0xBF, (byte) 0xC5, (byte) 0x8D, (byte) 0x5A, (byte) 0x71, (byte) 0xEF,
+        (byte) 0xA9, (byte) 0x58, (byte) 0x77, (byte) 0x94, (byte) 0x49, (byte) 0x71,
+        (byte) 0xBF, (byte) 0x45, (byte) 0x3E, (byte) 0xA4, (byte) 0x2E, (byte) 0x33,
+        (byte) 0x9B, (byte) 0x4E, (byte) 0xA4, (byte) 0x95, (byte) 0x07, (byte) 0x9C,
+        (byte) 0xAA, (byte) 0xC4, (byte) 0xA8, (byte) 0x60, (byte) 0xBC, (byte) 0xCD,
+        (byte) 0xC3, (byte) 0x45, (byte) 0xE6, (byte) 0xBC, (byte) 0xAD, (byte) 0xB6,
+        (byte) 0xF3, (byte) 0x0E, (byte) 0xF6, (byte) 0xD5, (byte) 0xCF, (byte) 0x33,
+        (byte) 0xA3, (byte) 0x82, (byte) 0x62, (byte) 0x52, (byte) 0x95, (byte) 0xA8,
+        (byte) 0x0E, (byte) 0xD4, (byte) 0xAC, (byte) 0x1F, (byte) 0x9A, (byte) 0xDC,
+        (byte) 0x00, (byte) 0xD6, (byte) 0x78, (byte) 0xEA, (byte) 0x53, (byte) 0x00,
+        (byte) 0x19, (byte) 0xE3, (byte) 0x81, (byte) 0x7C, (byte) 0x7A, (byte) 0x8E,
+        (byte) 0x30, (byte) 0x57, (byte) 0xB7, (byte) 0x81, (byte) 0xD7, (byte) 0x4D,
+        (byte) 0x1D, (byte) 0xCB, (byte) 0x99, (byte) 0x8D, (byte) 0xE4, (byte) 0xFA,
+        (byte) 0x6E, (byte) 0x4E, (byte) 0xA6, (byte) 0xDA, (byte) 0x13, (byte) 0x92,
+        (byte) 0x31, (byte) 0x7C, (byte) 0x2B, (byte) 0x3A, (byte) 0xA0, (byte) 0xF1,
+        (byte) 0x03, (byte) 0x83, (byte) 0x12, (byte) 0xD1, (byte) 0x23, (byte) 0xED,
+        (byte) 0xC4, (byte) 0x01, (byte) 0x57, (byte) 0x63, (byte) 0xAF, (byte) 0x40,
+        (byte) 0x15, (byte) 0xEC, (byte) 0xB8, (byte) 0x5A, (byte) 0xCE, (byte) 0x3D,
+        (byte) 0x3E, (byte) 0xCD, (byte) 0xD8, (byte) 0xF3, (byte) 0x76, (byte) 0xCA,
+        (byte) 0x23, (byte) 0x20, (byte) 0x68, (byte) 0x17, (byte) 0x5B, (byte) 0x7F,
+        (byte) 0xBC, (byte) 0x22, (byte) 0x67, (byte) 0x2A, (byte) 0x91, (byte) 0x05,
+        (byte) 0xB3, (byte) 0x85, (byte) 0x60, (byte) 0xD8, (byte) 0x76, (byte) 0xD5,
+        (byte) 0x2B, (byte) 0x9C, (byte) 0x80, (byte) 0xB6, (byte) 0xEA, (byte) 0x1E,
+        (byte) 0x05, (byte) 0xC7, (byte) 0x95, (byte) 0x2C, (byte) 0x4F, (byte) 0x14,
+        (byte) 0x5F, (byte) 0xEE, (byte) 0x08, (byte) 0x32, (byte) 0xF7, (byte) 0x12,
+        (byte) 0x2B, (byte) 0xCD, (byte) 0xF3, (byte) 0x83, (byte) 0x7C, (byte) 0xCE,
+        (byte) 0x04, (byte) 0x8A, (byte) 0x36, (byte) 0x3D, (byte) 0xB2, (byte) 0x97,
+        (byte) 0x15, (byte) 0xDB, (byte) 0xD6, (byte) 0xFA, (byte) 0x53, (byte) 0x29,
+        (byte) 0xD1, (byte) 0x43, (byte) 0x55, (byte) 0xDD, (byte) 0xAE, (byte) 0xA7,
+        (byte) 0xB4, (byte) 0x2C, (byte) 0xD9, (byte) 0xA7, (byte) 0x74, (byte) 0xA8,
+        (byte) 0x08, (byte) 0xD6, (byte) 0xC2, (byte) 0x05, (byte) 0xBF, (byte) 0x67,
+        (byte) 0x3B, (byte) 0xBA, (byte) 0x8D, (byte) 0x99, (byte) 0xC1, (byte) 0x14,
+        (byte) 0x1A, (byte) 0x32, (byte) 0xCA, (byte) 0xD5, (byte) 0xCC, (byte) 0xF9,
+        (byte) 0x64, (byte) 0x07, (byte) 0x5B, (byte) 0xB8, (byte) 0xA9, (byte) 0x69,
+        (byte) 0xED, (byte) 0x01, (byte) 0xCD, (byte) 0xD2, (byte) 0x88, (byte) 0x67,
+        (byte) 0xFF, (byte) 0x92, (byte) 0xA3, (byte) 0xC6, (byte) 0x97, (byte) 0x97,
+        (byte) 0xA1, (byte) 0xC5, (byte) 0x15, (byte) 0xC8, (byte) 0xB6, (byte) 0xFE,
+        (byte) 0x4A, (byte) 0x07, (byte) 0x2E, (byte) 0x46, (byte) 0x3F, (byte) 0x27,
+        (byte) 0xB8, (byte) 0xEE, (byte) 0x69, (byte) 0xCB, (byte) 0xDC, (byte) 0x30,
+        (byte) 0x19, (byte) 0x77, (byte) 0xC5, (byte) 0xEF,
+    };
+    private static final PSSParameterSpec SHA224withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec =
+            new PSSParameterSpec("SHA-224", "MGF1", new MGF1ParameterSpec("SHA-224"), 226, 1);
+
+    /*
+     * echo "This is a signed message from Kenny Root." | openssl sha256 -binary -out digest.bin \
+     *     && openssl pkeyutl -sign -in digest.bin -inkey privkey.pem \
+     *         -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha256 -pkeyopt rsa_pss_saltlen:32 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA256withRSAPSS_Vector2Signature = new byte[] {
+        (byte) 0x94, (byte) 0x33, (byte) 0xCB, (byte) 0x9E, (byte) 0x2C, (byte) 0x17,
+        (byte) 0x46, (byte) 0xB3, (byte) 0x8F, (byte) 0xB7, (byte) 0x93, (byte) 0x98,
+        (byte) 0xA3, (byte) 0x45, (byte) 0xEA, (byte) 0xD4, (byte) 0x51, (byte) 0x60,
+        (byte) 0x3E, (byte) 0x00, (byte) 0xA3, (byte) 0x93, (byte) 0x05, (byte) 0x0F,
+        (byte) 0xCB, (byte) 0x6E, (byte) 0xFF, (byte) 0xA5, (byte) 0x97, (byte) 0x18,
+        (byte) 0xF6, (byte) 0xED, (byte) 0x6B, (byte) 0x6C, (byte) 0xAD, (byte) 0x9C,
+        (byte) 0x73, (byte) 0x63, (byte) 0x9C, (byte) 0x5B, (byte) 0xA5, (byte) 0xA1,
+        (byte) 0x42, (byte) 0xA3, (byte) 0x0E, (byte) 0x32, (byte) 0xF5, (byte) 0xF0,
+        (byte) 0x55, (byte) 0xEE, (byte) 0x58, (byte) 0xC1, (byte) 0xBD, (byte) 0x99,
+        (byte) 0x0A, (byte) 0x2B, (byte) 0xFD, (byte) 0xBD, (byte) 0x1E, (byte) 0x23,
+        (byte) 0xEF, (byte) 0x99, (byte) 0x7D, (byte) 0xC1, (byte) 0xE2, (byte) 0xD5,
+        (byte) 0x71, (byte) 0x6C, (byte) 0x96, (byte) 0x70, (byte) 0xC3, (byte) 0x75,
+        (byte) 0x48, (byte) 0x83, (byte) 0x85, (byte) 0x5E, (byte) 0xC6, (byte) 0x3A,
+        (byte) 0xFF, (byte) 0xE5, (byte) 0xF1, (byte) 0x6B, (byte) 0x85, (byte) 0x7B,
+        (byte) 0x61, (byte) 0xA6, (byte) 0xB1, (byte) 0xCF, (byte) 0x60, (byte) 0x09,
+        (byte) 0x32, (byte) 0xAF, (byte) 0xEF, (byte) 0x95, (byte) 0xA4, (byte) 0x1B,
+        (byte) 0xD6, (byte) 0xFA, (byte) 0xD0, (byte) 0xD7, (byte) 0x17, (byte) 0xCA,
+        (byte) 0xB0, (byte) 0x19, (byte) 0x21, (byte) 0x7F, (byte) 0x5E, (byte) 0x9B,
+        (byte) 0xBB, (byte) 0xB8, (byte) 0xE0, (byte) 0xB1, (byte) 0x95, (byte) 0xB3,
+        (byte) 0xDA, (byte) 0x0B, (byte) 0xB8, (byte) 0xFA, (byte) 0x15, (byte) 0x75,
+        (byte) 0x73, (byte) 0x88, (byte) 0xC8, (byte) 0x45, (byte) 0x33, (byte) 0xD1,
+        (byte) 0x5C, (byte) 0xB7, (byte) 0xFB, (byte) 0x38, (byte) 0x05, (byte) 0xA0,
+        (byte) 0x85, (byte) 0x99, (byte) 0x2C, (byte) 0xB1, (byte) 0xC2, (byte) 0xFE,
+        (byte) 0xAC, (byte) 0x5D, (byte) 0x2C, (byte) 0x1B, (byte) 0xD3, (byte) 0x42,
+        (byte) 0x81, (byte) 0xC8, (byte) 0x1C, (byte) 0xB7, (byte) 0x53, (byte) 0x7E,
+        (byte) 0xC5, (byte) 0x9F, (byte) 0x84, (byte) 0x97, (byte) 0x6F, (byte) 0x00,
+        (byte) 0xC3, (byte) 0x5E, (byte) 0x8B, (byte) 0x67, (byte) 0x3D, (byte) 0x9A,
+        (byte) 0xD0, (byte) 0xE2, (byte) 0x9B, (byte) 0x2D, (byte) 0xC6, (byte) 0xD8,
+        (byte) 0xEF, (byte) 0x19, (byte) 0x14, (byte) 0x49, (byte) 0x88, (byte) 0x52,
+        (byte) 0xF7, (byte) 0x93, (byte) 0xEB, (byte) 0xDB, (byte) 0xB6, (byte) 0x55,
+        (byte) 0x05, (byte) 0xB6, (byte) 0xE7, (byte) 0x70, (byte) 0xE4, (byte) 0x5A,
+        (byte) 0x9E, (byte) 0x80, (byte) 0x78, (byte) 0x48, (byte) 0xA8, (byte) 0xE5,
+        (byte) 0x59, (byte) 0x8D, (byte) 0x1C, (byte) 0x5D, (byte) 0x95, (byte) 0x38,
+        (byte) 0x25, (byte) 0xFC, (byte) 0x38, (byte) 0xC3, (byte) 0xFF, (byte) 0xE2,
+        (byte) 0x6F, (byte) 0xE4, (byte) 0xFC, (byte) 0x64, (byte) 0x8B, (byte) 0xCA,
+        (byte) 0x91, (byte) 0x5F, (byte) 0x0B, (byte) 0x4E, (byte) 0x9A, (byte) 0xB5,
+        (byte) 0x22, (byte) 0x5D, (byte) 0xC5, (byte) 0x5A, (byte) 0x77, (byte) 0xED,
+        (byte) 0x23, (byte) 0xE0, (byte) 0x13, (byte) 0x8F, (byte) 0xAC, (byte) 0x13,
+        (byte) 0xE5, (byte) 0x81, (byte) 0xEE, (byte) 0xD1, (byte) 0xAD, (byte) 0x8A,
+        (byte) 0x0F, (byte) 0x2B, (byte) 0x4C, (byte) 0xB2, (byte) 0x13, (byte) 0x54,
+        (byte) 0x44, (byte) 0x8E, (byte) 0x53, (byte) 0xE2, (byte) 0x33, (byte) 0x14,
+        (byte) 0x7F, (byte) 0x9B, (byte) 0xA9, (byte) 0xD3, (byte) 0xBB, (byte) 0xFC,
+        (byte) 0xAC, (byte) 0xC9, (byte) 0x31, (byte) 0xB6,
+    };
+    private static final PSSParameterSpec SHA256withRSAPSS_Vector2Signature_ParameterSpec =
+            new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1);
+
+    /*
+     * echo "This is a signed message from Kenny Root." | openssl sha256 -binary -out digest.bin \
+     *     && openssl pkeyutl -sign -in digest.bin -inkey privkey.pem \
+     *         -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha256 -pkeyopt rsa_pss_saltlen:0 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA256withRSAPSS_NoSalt_Vector2Signature = new byte[] {
+        (byte) 0x4C, (byte) 0xB7, (byte) 0x33, (byte) 0x78, (byte) 0x0A, (byte) 0xA7,
+        (byte) 0xEB, (byte) 0x35, (byte) 0x5E, (byte) 0x99, (byte) 0x8F, (byte) 0xE9,
+        (byte) 0x2A, (byte) 0x3D, (byte) 0x8C, (byte) 0x9B, (byte) 0x19, (byte) 0xC7,
+        (byte) 0xC8, (byte) 0xB8, (byte) 0x10, (byte) 0xC5, (byte) 0x6D, (byte) 0xA4,
+        (byte) 0x44, (byte) 0x3E, (byte) 0xAB, (byte) 0x90, (byte) 0x82, (byte) 0x70,
+        (byte) 0xFA, (byte) 0x7B, (byte) 0xE6, (byte) 0x06, (byte) 0x36, (byte) 0x06,
+        (byte) 0x93, (byte) 0x54, (byte) 0x50, (byte) 0xCD, (byte) 0x5F, (byte) 0xAA,
+        (byte) 0x01, (byte) 0x42, (byte) 0xAD, (byte) 0xB9, (byte) 0x02, (byte) 0x6E,
+        (byte) 0xAE, (byte) 0x60, (byte) 0x00, (byte) 0x60, (byte) 0x55, (byte) 0x1B,
+        (byte) 0xBB, (byte) 0x9E, (byte) 0x03, (byte) 0xB7, (byte) 0x86, (byte) 0x3D,
+        (byte) 0xCC, (byte) 0xFA, (byte) 0x6E, (byte) 0x20, (byte) 0x07, (byte) 0x61,
+        (byte) 0x8F, (byte) 0x53, (byte) 0xC6, (byte) 0x2B, (byte) 0xEF, (byte) 0x8F,
+        (byte) 0x0F, (byte) 0x8B, (byte) 0x80, (byte) 0x22, (byte) 0xDC, (byte) 0x9E,
+        (byte) 0x20, (byte) 0x4A, (byte) 0x57, (byte) 0xA1, (byte) 0x15, (byte) 0xE0,
+        (byte) 0x01, (byte) 0x95, (byte) 0xDB, (byte) 0x46, (byte) 0x85, (byte) 0x6D,
+        (byte) 0x27, (byte) 0x9F, (byte) 0x44, (byte) 0x3B, (byte) 0xB1, (byte) 0x35,
+        (byte) 0x04, (byte) 0x9D, (byte) 0xF8, (byte) 0xC6, (byte) 0xD7, (byte) 0xD7,
+        (byte) 0xEF, (byte) 0x9A, (byte) 0x53, (byte) 0x5A, (byte) 0x73, (byte) 0xB3,
+        (byte) 0xD0, (byte) 0x32, (byte) 0x39, (byte) 0xE1, (byte) 0x28, (byte) 0x3A,
+        (byte) 0x9D, (byte) 0x69, (byte) 0x4E, (byte) 0x57, (byte) 0xC1, (byte) 0xDF,
+        (byte) 0xFE, (byte) 0x5F, (byte) 0xA8, (byte) 0xFF, (byte) 0xE8, (byte) 0x75,
+        (byte) 0x85, (byte) 0x33, (byte) 0x90, (byte) 0x83, (byte) 0x3D, (byte) 0x8F,
+        (byte) 0x15, (byte) 0x47, (byte) 0x16, (byte) 0xF2, (byte) 0x32, (byte) 0xF9,
+        (byte) 0x46, (byte) 0x96, (byte) 0xCC, (byte) 0x2E, (byte) 0x8F, (byte) 0x27,
+        (byte) 0x3F, (byte) 0xCF, (byte) 0x91, (byte) 0xA6, (byte) 0x9E, (byte) 0xBF,
+        (byte) 0x42, (byte) 0x2F, (byte) 0xD6, (byte) 0x52, (byte) 0xD7, (byte) 0x3B,
+        (byte) 0xCD, (byte) 0xFE, (byte) 0x0B, (byte) 0x4A, (byte) 0x3B, (byte) 0x19,
+        (byte) 0x57, (byte) 0x47, (byte) 0x65, (byte) 0x33, (byte) 0xD9, (byte) 0xF7,
+        (byte) 0xE4, (byte) 0xC3, (byte) 0x05, (byte) 0x49, (byte) 0x3C, (byte) 0xC0,
+        (byte) 0xDF, (byte) 0xC1, (byte) 0x54, (byte) 0x18, (byte) 0x8D, (byte) 0xDA,
+        (byte) 0xE4, (byte) 0x59, (byte) 0xE9, (byte) 0x3B, (byte) 0xD6, (byte) 0x89,
+        (byte) 0x07, (byte) 0x99, (byte) 0xB0, (byte) 0xF4, (byte) 0x09, (byte) 0x0A,
+        (byte) 0x2C, (byte) 0xBA, (byte) 0x0B, (byte) 0xE4, (byte) 0x79, (byte) 0xB1,
+        (byte) 0xDB, (byte) 0xAD, (byte) 0xAB, (byte) 0x5D, (byte) 0xA2, (byte) 0x1E,
+        (byte) 0x76, (byte) 0x7F, (byte) 0x74, (byte) 0x62, (byte) 0x49, (byte) 0x07,
+        (byte) 0x7A, (byte) 0x5B, (byte) 0xD7, (byte) 0x0F, (byte) 0xA4, (byte) 0x2C,
+        (byte) 0x36, (byte) 0x13, (byte) 0x42, (byte) 0xBA, (byte) 0xCF, (byte) 0x0A,
+        (byte) 0xFC, (byte) 0xC3, (byte) 0x31, (byte) 0x5E, (byte) 0x06, (byte) 0x84,
+        (byte) 0x8A, (byte) 0x8A, (byte) 0x84, (byte) 0x0D, (byte) 0x48, (byte) 0xBD,
+        (byte) 0x67, (byte) 0xCF, (byte) 0x04, (byte) 0xB4, (byte) 0xFB, (byte) 0xBB,
+        (byte) 0x04, (byte) 0x91, (byte) 0xB1, (byte) 0x0A, (byte) 0xA4, (byte) 0x70,
+        (byte) 0x58, (byte) 0x1A, (byte) 0x9B, (byte) 0x02, (byte) 0x86, (byte) 0xBD,
+        (byte) 0xAE, (byte) 0x77, (byte) 0x97, (byte) 0x1C,
+    };
+    private static final PSSParameterSpec SHA256withRSAPSS_NoSalt_Vector2Signature_ParameterSpec =
+            new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 0, 1);
+
+    /*
+     * echo "This is a signed message from Kenny Root." | openssl sha256 -binary -out digest.bin \
+     *     && openssl pkeyutl -sign -in digest.bin -inkey privkey.pem \
+     *         -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha256 -pkeyopt rsa_pss_saltlen:222 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA256withRSAPSS_MaxSalt_Vector2Signature = new byte[] {
+        (byte) 0x3B, (byte) 0x43, (byte) 0xA8, (byte) 0xB5, (byte) 0x34, (byte) 0xD8,
+        (byte) 0xF9, (byte) 0xAD, (byte) 0xDD, (byte) 0x1F, (byte) 0x7A, (byte) 0x73,
+        (byte) 0xBF, (byte) 0xFA, (byte) 0xED, (byte) 0x10, (byte) 0xF3, (byte) 0x16,
+        (byte) 0xCC, (byte) 0xE5, (byte) 0x09, (byte) 0x0F, (byte) 0x68, (byte) 0x02,
+        (byte) 0xE7, (byte) 0x55, (byte) 0x0D, (byte) 0xCF, (byte) 0x1B, (byte) 0x83,
+        (byte) 0xCD, (byte) 0xA2, (byte) 0xD6, (byte) 0x02, (byte) 0xDD, (byte) 0x72,
+        (byte) 0xA6, (byte) 0x5F, (byte) 0x05, (byte) 0x8A, (byte) 0x1E, (byte) 0xA1,
+        (byte) 0x4F, (byte) 0x92, (byte) 0xD9, (byte) 0x09, (byte) 0x19, (byte) 0x6E,
+        (byte) 0x80, (byte) 0xA0, (byte) 0x47, (byte) 0x98, (byte) 0x5C, (byte) 0xF7,
+        (byte) 0x34, (byte) 0x52, (byte) 0x7D, (byte) 0x85, (byte) 0xCF, (byte) 0x9F,
+        (byte) 0xEB, (byte) 0xAF, (byte) 0xB4, (byte) 0x53, (byte) 0xF0, (byte) 0x5D,
+        (byte) 0x28, (byte) 0x87, (byte) 0xAC, (byte) 0xA7, (byte) 0xB4, (byte) 0xCF,
+        (byte) 0xDD, (byte) 0x8B, (byte) 0xA4, (byte) 0xC9, (byte) 0xCA, (byte) 0xAA,
+        (byte) 0xF4, (byte) 0xA8, (byte) 0x25, (byte) 0x26, (byte) 0x34, (byte) 0x11,
+        (byte) 0x14, (byte) 0x24, (byte) 0x1C, (byte) 0x1C, (byte) 0x50, (byte) 0xC8,
+        (byte) 0xFF, (byte) 0x7E, (byte) 0xFF, (byte) 0x6F, (byte) 0x4F, (byte) 0x14,
+        (byte) 0xB3, (byte) 0x57, (byte) 0x48, (byte) 0x0A, (byte) 0x5A, (byte) 0x95,
+        (byte) 0x5D, (byte) 0xEB, (byte) 0x71, (byte) 0x4E, (byte) 0x86, (byte) 0xFC,
+        (byte) 0x38, (byte) 0x1B, (byte) 0x93, (byte) 0x45, (byte) 0x09, (byte) 0x15,
+        (byte) 0xD3, (byte) 0x06, (byte) 0x6B, (byte) 0x9D, (byte) 0x05, (byte) 0x5C,
+        (byte) 0x4A, (byte) 0xB3, (byte) 0x93, (byte) 0xD1, (byte) 0x01, (byte) 0x54,
+        (byte) 0xCC, (byte) 0xED, (byte) 0xBF, (byte) 0x0E, (byte) 0x7E, (byte) 0x33,
+        (byte) 0x32, (byte) 0xA6, (byte) 0xA5, (byte) 0xF7, (byte) 0x3D, (byte) 0x2E,
+        (byte) 0xCB, (byte) 0x76, (byte) 0xA7, (byte) 0x22, (byte) 0x64, (byte) 0xB8,
+        (byte) 0x19, (byte) 0x53, (byte) 0xFE, (byte) 0x8C, (byte) 0xC8, (byte) 0x1E,
+        (byte) 0x6C, (byte) 0xEE, (byte) 0x08, (byte) 0x07, (byte) 0x7E, (byte) 0x93,
+        (byte) 0x43, (byte) 0x1B, (byte) 0xCF, (byte) 0x37, (byte) 0xE4, (byte) 0xAB,
+        (byte) 0xE7, (byte) 0xD7, (byte) 0x83, (byte) 0x8E, (byte) 0x19, (byte) 0xAE,
+        (byte) 0x05, (byte) 0x51, (byte) 0x91, (byte) 0x10, (byte) 0x7B, (byte) 0x70,
+        (byte) 0xFC, (byte) 0x73, (byte) 0x12, (byte) 0x96, (byte) 0xFA, (byte) 0xD0,
+        (byte) 0xCA, (byte) 0xA3, (byte) 0x59, (byte) 0xA7, (byte) 0xDD, (byte) 0xC3,
+        (byte) 0x1D, (byte) 0x9C, (byte) 0x7B, (byte) 0x50, (byte) 0xBB, (byte) 0x57,
+        (byte) 0xB8, (byte) 0x86, (byte) 0xF2, (byte) 0xCA, (byte) 0xC4, (byte) 0x86,
+        (byte) 0x7A, (byte) 0x96, (byte) 0x90, (byte) 0x02, (byte) 0xDF, (byte) 0xA0,
+        (byte) 0x88, (byte) 0x0E, (byte) 0x89, (byte) 0x45, (byte) 0x27, (byte) 0x52,
+        (byte) 0xDA, (byte) 0x86, (byte) 0x42, (byte) 0x4B, (byte) 0x90, (byte) 0xC3,
+        (byte) 0xC1, (byte) 0x41, (byte) 0x60, (byte) 0x5C, (byte) 0x29, (byte) 0x15,
+        (byte) 0xE5, (byte) 0x5C, (byte) 0x43, (byte) 0x9B, (byte) 0x40, (byte) 0xE5,
+        (byte) 0x04, (byte) 0x1B, (byte) 0x4A, (byte) 0x93, (byte) 0xDD, (byte) 0x55,
+        (byte) 0xC4, (byte) 0xFC, (byte) 0xFE, (byte) 0x0C, (byte) 0x65, (byte) 0x96,
+        (byte) 0x98, (byte) 0xDE, (byte) 0xC5, (byte) 0x05, (byte) 0xC5, (byte) 0x3E,
+        (byte) 0xB0, (byte) 0x25, (byte) 0x4E, (byte) 0x65, (byte) 0x24, (byte) 0x8D,
+        (byte) 0x4E, (byte) 0x9D, (byte) 0x94, (byte) 0x01,
+    };
+    private static final PSSParameterSpec SHA256withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec =
+            new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 222, 1);
+
+    /*
+     * echo "This is a signed message from Kenny Root." | openssl sha384 -binary -out digest.bin \
+     *     && openssl pkeyutl -sign -in digest.bin -inkey privkey.pem \
+     *         -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha384 -pkeyopt rsa_pss_saltlen:48 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA384withRSAPSS_Vector2Signature = new byte[] {
+        (byte) 0x20, (byte) 0xCB, (byte) 0x97, (byte) 0x9C, (byte) 0x2E, (byte) 0x51,
+        (byte) 0x59, (byte) 0x56, (byte) 0x9F, (byte) 0x04, (byte) 0x47, (byte) 0x7C,
+        (byte) 0x5C, (byte) 0x57, (byte) 0x59, (byte) 0xBC, (byte) 0x43, (byte) 0xD9,
+        (byte) 0x4B, (byte) 0xEC, (byte) 0xAC, (byte) 0xB9, (byte) 0x88, (byte) 0xA2,
+        (byte) 0x30, (byte) 0x8B, (byte) 0xEE, (byte) 0x2F, (byte) 0xC1, (byte) 0x73,
+        (byte) 0xF1, (byte) 0x13, (byte) 0xB2, (byte) 0x5E, (byte) 0x1A, (byte) 0xC8,
+        (byte) 0xD2, (byte) 0xAA, (byte) 0x27, (byte) 0x16, (byte) 0xA1, (byte) 0x14,
+        (byte) 0xAB, (byte) 0x45, (byte) 0x8A, (byte) 0x7E, (byte) 0x22, (byte) 0x22,
+        (byte) 0x2A, (byte) 0x2E, (byte) 0xDA, (byte) 0x6A, (byte) 0x7E, (byte) 0x3F,
+        (byte) 0x66, (byte) 0x99, (byte) 0x55, (byte) 0xAF, (byte) 0x2B, (byte) 0x94,
+        (byte) 0xD8, (byte) 0x6B, (byte) 0xC2, (byte) 0x60, (byte) 0xB5, (byte) 0x55,
+        (byte) 0xA9, (byte) 0x26, (byte) 0x29, (byte) 0xFC, (byte) 0x17, (byte) 0x56,
+        (byte) 0x05, (byte) 0xB7, (byte) 0x48, (byte) 0x2F, (byte) 0xAB, (byte) 0x68,
+        (byte) 0xCF, (byte) 0x37, (byte) 0x62, (byte) 0x79, (byte) 0x4F, (byte) 0x32,
+        (byte) 0x04, (byte) 0xF6, (byte) 0xEA, (byte) 0xBE, (byte) 0x79, (byte) 0x84,
+        (byte) 0x73, (byte) 0xEE, (byte) 0x1C, (byte) 0xEE, (byte) 0x9F, (byte) 0x72,
+        (byte) 0x7A, (byte) 0xC6, (byte) 0x64, (byte) 0xB4, (byte) 0x4F, (byte) 0xDE,
+        (byte) 0x0B, (byte) 0x38, (byte) 0x47, (byte) 0x62, (byte) 0xA9, (byte) 0xFD,
+        (byte) 0x1B, (byte) 0x75, (byte) 0xEC, (byte) 0xFE, (byte) 0x2D, (byte) 0x04,
+        (byte) 0x2D, (byte) 0x0A, (byte) 0xCE, (byte) 0x13, (byte) 0xFA, (byte) 0xDA,
+        (byte) 0x3F, (byte) 0x4C, (byte) 0x11, (byte) 0xEA, (byte) 0x02, (byte) 0x00,
+        (byte) 0x0A, (byte) 0x93, (byte) 0x12, (byte) 0xDC, (byte) 0x60, (byte) 0xE7,
+        (byte) 0x52, (byte) 0x90, (byte) 0x8A, (byte) 0xA3, (byte) 0xAE, (byte) 0xC5,
+        (byte) 0x9A, (byte) 0xD7, (byte) 0xD5, (byte) 0x0D, (byte) 0xBC, (byte) 0x7A,
+        (byte) 0xDB, (byte) 0xF4, (byte) 0x10, (byte) 0xE0, (byte) 0xDB, (byte) 0xC0,
+        (byte) 0x97, (byte) 0xF1, (byte) 0x84, (byte) 0xCF, (byte) 0x66, (byte) 0xB2,
+        (byte) 0x04, (byte) 0x58, (byte) 0x81, (byte) 0xB5, (byte) 0x9B, (byte) 0x4A,
+        (byte) 0xF9, (byte) 0xD7, (byte) 0xCA, (byte) 0x51, (byte) 0x09, (byte) 0x67,
+        (byte) 0x48, (byte) 0x7B, (byte) 0xE5, (byte) 0xE9, (byte) 0x07, (byte) 0x4E,
+        (byte) 0x6A, (byte) 0xC1, (byte) 0xA6, (byte) 0x68, (byte) 0x90, (byte) 0x17,
+        (byte) 0xAB, (byte) 0x0E, (byte) 0xFB, (byte) 0x3E, (byte) 0x39, (byte) 0x74,
+        (byte) 0x85, (byte) 0x04, (byte) 0x42, (byte) 0x0A, (byte) 0x9E, (byte) 0x02,
+        (byte) 0xA9, (byte) 0x50, (byte) 0xFF, (byte) 0x23, (byte) 0x2D, (byte) 0x30,
+        (byte) 0xDD, (byte) 0x17, (byte) 0xC0, (byte) 0x82, (byte) 0xF7, (byte) 0xBB,
+        (byte) 0x3B, (byte) 0x03, (byte) 0xBD, (byte) 0xB1, (byte) 0x96, (byte) 0xCD,
+        (byte) 0x71, (byte) 0x3F, (byte) 0x67, (byte) 0x59, (byte) 0x5E, (byte) 0x45,
+        (byte) 0xE0, (byte) 0x1C, (byte) 0x80, (byte) 0x52, (byte) 0xD7, (byte) 0xF0,
+        (byte) 0xC1, (byte) 0xE6, (byte) 0xCF, (byte) 0x59, (byte) 0x13, (byte) 0x25,
+        (byte) 0x6F, (byte) 0x9F, (byte) 0xBB, (byte) 0xB9, (byte) 0x7F, (byte) 0x7E,
+        (byte) 0x7D, (byte) 0x93, (byte) 0xD9, (byte) 0x3F, (byte) 0x95, (byte) 0xB7,
+        (byte) 0x9A, (byte) 0xDB, (byte) 0xE2, (byte) 0x2C, (byte) 0x53, (byte) 0x83,
+        (byte) 0x9A, (byte) 0x06, (byte) 0x6D, (byte) 0x22, (byte) 0x81, (byte) 0xB5,
+        (byte) 0x63, (byte) 0xAE, (byte) 0x4A, (byte) 0xEE,
+    };
+    private static final PSSParameterSpec SHA384withRSAPSS_Vector2Signature_ParameterSpec =
+            new PSSParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, 48, 1);
+
+    /*
+     * echo "This is a signed message from Kenny Root." | openssl sha384 -binary -out digest.bin \
+     *     && openssl pkeyutl -sign -in digest.bin -inkey privkey.pem \
+     *         -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha384 -pkeyopt rsa_pss_saltlen:0 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA384withRSAPSS_NoSalt_Vector2Signature = new byte[] {
+        (byte) 0x41, (byte) 0x0C, (byte) 0x3A, (byte) 0xEC, (byte) 0xF6, (byte) 0xD9,
+        (byte) 0x8F, (byte) 0xA3, (byte) 0x61, (byte) 0xBB, (byte) 0x03, (byte) 0xED,
+        (byte) 0xD9, (byte) 0x69, (byte) 0x7D, (byte) 0xE1, (byte) 0xE1, (byte) 0x4E,
+        (byte) 0x5E, (byte) 0x71, (byte) 0x4E, (byte) 0x88, (byte) 0x9C, (byte) 0x79,
+        (byte) 0xD3, (byte) 0x71, (byte) 0x28, (byte) 0x07, (byte) 0x28, (byte) 0x19,
+        (byte) 0x96, (byte) 0x55, (byte) 0x30, (byte) 0x81, (byte) 0x29, (byte) 0x5C,
+        (byte) 0x4A, (byte) 0x18, (byte) 0x69, (byte) 0x36, (byte) 0x74, (byte) 0xAC,
+        (byte) 0x99, (byte) 0xB1, (byte) 0xBC, (byte) 0xA0, (byte) 0xFC, (byte) 0x17,
+        (byte) 0xA4, (byte) 0xD1, (byte) 0xAE, (byte) 0x84, (byte) 0xA6, (byte) 0x09,
+        (byte) 0x6B, (byte) 0xB3, (byte) 0x02, (byte) 0xB2, (byte) 0x81, (byte) 0x04,
+        (byte) 0x59, (byte) 0x8C, (byte) 0xCF, (byte) 0xAD, (byte) 0xFB, (byte) 0x76,
+        (byte) 0x6F, (byte) 0xE2, (byte) 0x5E, (byte) 0x09, (byte) 0xE5, (byte) 0xBC,
+        (byte) 0x54, (byte) 0xBD, (byte) 0x08, (byte) 0xA8, (byte) 0x18, (byte) 0x60,
+        (byte) 0xAF, (byte) 0x09, (byte) 0x67, (byte) 0x15, (byte) 0x03, (byte) 0xA8,
+        (byte) 0x8B, (byte) 0x3F, (byte) 0x31, (byte) 0xB7, (byte) 0x76, (byte) 0xFD,
+        (byte) 0xF6, (byte) 0x82, (byte) 0xC7, (byte) 0x89, (byte) 0xC2, (byte) 0x47,
+        (byte) 0x80, (byte) 0x06, (byte) 0x4F, (byte) 0x8C, (byte) 0x9C, (byte) 0xD7,
+        (byte) 0x4F, (byte) 0x63, (byte) 0x1E, (byte) 0xF0, (byte) 0x34, (byte) 0xD7,
+        (byte) 0x91, (byte) 0xD2, (byte) 0x96, (byte) 0x62, (byte) 0xFD, (byte) 0x68,
+        (byte) 0xE3, (byte) 0xE0, (byte) 0xFB, (byte) 0x7D, (byte) 0x0A, (byte) 0xD7,
+        (byte) 0x52, (byte) 0xFE, (byte) 0xD1, (byte) 0x95, (byte) 0x9E, (byte) 0xD2,
+        (byte) 0x84, (byte) 0xBE, (byte) 0x3D, (byte) 0x1F, (byte) 0x8C, (byte) 0xC4,
+        (byte) 0xD6, (byte) 0xE3, (byte) 0xCF, (byte) 0xE8, (byte) 0xB3, (byte) 0x82,
+        (byte) 0x2E, (byte) 0xFA, (byte) 0x39, (byte) 0xA3, (byte) 0x20, (byte) 0x3C,
+        (byte) 0xBE, (byte) 0x6A, (byte) 0xFA, (byte) 0x04, (byte) 0xD2, (byte) 0x74,
+        (byte) 0x41, (byte) 0xDC, (byte) 0xE8, (byte) 0x0E, (byte) 0xE7, (byte) 0xF2,
+        (byte) 0x36, (byte) 0xD4, (byte) 0x2E, (byte) 0x6A, (byte) 0xCF, (byte) 0xDF,
+        (byte) 0x8B, (byte) 0x4B, (byte) 0x77, (byte) 0xE8, (byte) 0x0A, (byte) 0x64,
+        (byte) 0x86, (byte) 0x2C, (byte) 0xCA, (byte) 0x92, (byte) 0x01, (byte) 0xB2,
+        (byte) 0x8A, (byte) 0xB8, (byte) 0xB2, (byte) 0x6C, (byte) 0x0B, (byte) 0x18,
+        (byte) 0x90, (byte) 0x31, (byte) 0x93, (byte) 0x29, (byte) 0xBA, (byte) 0xB1,
+        (byte) 0x88, (byte) 0x94, (byte) 0x44, (byte) 0x0B, (byte) 0x38, (byte) 0x64,
+        (byte) 0xC1, (byte) 0xDE, (byte) 0x0B, (byte) 0xD8, (byte) 0xE4, (byte) 0xBA,
+        (byte) 0x0A, (byte) 0x41, (byte) 0x24, (byte) 0x35, (byte) 0xAA, (byte) 0xE3,
+        (byte) 0x59, (byte) 0x8E, (byte) 0x57, (byte) 0x51, (byte) 0x43, (byte) 0xE1,
+        (byte) 0x9C, (byte) 0xF6, (byte) 0xF8, (byte) 0x16, (byte) 0x68, (byte) 0x83,
+        (byte) 0x08, (byte) 0x8C, (byte) 0x2D, (byte) 0x40, (byte) 0xD2, (byte) 0xEF,
+        (byte) 0xD6, (byte) 0xAE, (byte) 0x98, (byte) 0x77, (byte) 0xE8, (byte) 0xF2,
+        (byte) 0xC7, (byte) 0x19, (byte) 0x61, (byte) 0xD6, (byte) 0x43, (byte) 0xCD,
+        (byte) 0x76, (byte) 0x2E, (byte) 0x7A, (byte) 0xCB, (byte) 0x1A, (byte) 0x5D,
+        (byte) 0x73, (byte) 0x45, (byte) 0xF2, (byte) 0x7C, (byte) 0xD0, (byte) 0x88,
+        (byte) 0x83, (byte) 0x51, (byte) 0xF3, (byte) 0x19, (byte) 0x0F, (byte) 0xD5,
+        (byte) 0x40, (byte) 0x3F, (byte) 0xD9, (byte) 0xBF,
+    };
+    private static final PSSParameterSpec SHA384withRSAPSS_NoSalt_Vector2Signature_ParameterSpec =
+            new PSSParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, 0, 1);
+
+    /*
+     * echo "This is a signed message from Kenny Root." | openssl sha384 -binary -out digest.bin \
+     *     && openssl pkeyutl -sign -in digest.bin -inkey privkey.pem \
+     *         -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha384 -pkeyopt rsa_pss_saltlen:206 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA384withRSAPSS_MaxSalt_Vector2Signature = new byte[] {
+        (byte) 0xDE, (byte) 0xF7, (byte) 0xC3, (byte) 0x21, (byte) 0x79, (byte) 0x0F,
+        (byte) 0x55, (byte) 0xD1, (byte) 0x56, (byte) 0x9A, (byte) 0xB0, (byte) 0x08,
+        (byte) 0xA1, (byte) 0x27, (byte) 0xC9, (byte) 0x5E, (byte) 0x64, (byte) 0xF4,
+        (byte) 0xC7, (byte) 0x83, (byte) 0x94, (byte) 0xCA, (byte) 0xBD, (byte) 0x50,
+        (byte) 0xD6, (byte) 0xC5, (byte) 0x56, (byte) 0x94, (byte) 0xBD, (byte) 0x0B,
+        (byte) 0x55, (byte) 0xE6, (byte) 0x04, (byte) 0xAD, (byte) 0xAF, (byte) 0xAF,
+        (byte) 0x4F, (byte) 0x2D, (byte) 0x91, (byte) 0x7F, (byte) 0xF1, (byte) 0x60,
+        (byte) 0x0C, (byte) 0xEE, (byte) 0xE8, (byte) 0x44, (byte) 0xFC, (byte) 0x69,
+        (byte) 0x80, (byte) 0x43, (byte) 0xBC, (byte) 0xAB, (byte) 0x83, (byte) 0x35,
+        (byte) 0xB0, (byte) 0xC6, (byte) 0xCB, (byte) 0xE6, (byte) 0x92, (byte) 0x29,
+        (byte) 0x09, (byte) 0xCF, (byte) 0xDB, (byte) 0xAD, (byte) 0x16, (byte) 0x93,
+        (byte) 0xC7, (byte) 0xBE, (byte) 0x81, (byte) 0x68, (byte) 0x0F, (byte) 0x7B,
+        (byte) 0xC1, (byte) 0xC2, (byte) 0x8C, (byte) 0xBA, (byte) 0x59, (byte) 0x80,
+        (byte) 0xAE, (byte) 0xFB, (byte) 0x60, (byte) 0x22, (byte) 0x28, (byte) 0x36,
+        (byte) 0xBE, (byte) 0x37, (byte) 0x72, (byte) 0x86, (byte) 0x02, (byte) 0x4B,
+        (byte) 0xF9, (byte) 0x14, (byte) 0x5A, (byte) 0x6B, (byte) 0x32, (byte) 0x44,
+        (byte) 0x72, (byte) 0x33, (byte) 0x2E, (byte) 0x7F, (byte) 0xA1, (byte) 0xFD,
+        (byte) 0x07, (byte) 0xF2, (byte) 0xD9, (byte) 0x9D, (byte) 0x03, (byte) 0x77,
+        (byte) 0x17, (byte) 0xFB, (byte) 0x0E, (byte) 0xFF, (byte) 0xF7, (byte) 0x37,
+        (byte) 0x68, (byte) 0xF6, (byte) 0x8F, (byte) 0x9B, (byte) 0x2C, (byte) 0xEB,
+        (byte) 0xAF, (byte) 0x6C, (byte) 0x50, (byte) 0x9F, (byte) 0x34, (byte) 0xB2,
+        (byte) 0x52, (byte) 0x3B, (byte) 0x94, (byte) 0x6F, (byte) 0x60, (byte) 0x16,
+        (byte) 0x52, (byte) 0x0A, (byte) 0xBF, (byte) 0x95, (byte) 0x41, (byte) 0x44,
+        (byte) 0x83, (byte) 0x91, (byte) 0x85, (byte) 0xA1, (byte) 0xF7, (byte) 0xF9,
+        (byte) 0x17, (byte) 0x4A, (byte) 0xF7, (byte) 0xF1, (byte) 0xE8, (byte) 0x9C,
+        (byte) 0x75, (byte) 0x86, (byte) 0x12, (byte) 0x44, (byte) 0x19, (byte) 0x5C,
+        (byte) 0x65, (byte) 0x31, (byte) 0x89, (byte) 0x2A, (byte) 0xFC, (byte) 0xBE,
+        (byte) 0xE8, (byte) 0xEC, (byte) 0xC9, (byte) 0xD7, (byte) 0x41, (byte) 0xDA,
+        (byte) 0xD9, (byte) 0xC9, (byte) 0x8B, (byte) 0x90, (byte) 0x60, (byte) 0xCC,
+        (byte) 0xB2, (byte) 0x7A, (byte) 0xBA, (byte) 0xA0, (byte) 0xEE, (byte) 0xBE,
+        (byte) 0x9C, (byte) 0xE7, (byte) 0xF2, (byte) 0x27, (byte) 0x92, (byte) 0x9C,
+        (byte) 0x3C, (byte) 0x0F, (byte) 0x5C, (byte) 0xEE, (byte) 0x38, (byte) 0x48,
+        (byte) 0xCF, (byte) 0xFF, (byte) 0x33, (byte) 0x35, (byte) 0x80, (byte) 0x99,
+        (byte) 0x5D, (byte) 0xA7, (byte) 0x5A, (byte) 0x7A, (byte) 0xEA, (byte) 0x96,
+        (byte) 0x74, (byte) 0x28, (byte) 0x36, (byte) 0x7B, (byte) 0xE1, (byte) 0x33,
+        (byte) 0x7C, (byte) 0x78, (byte) 0xEC, (byte) 0x05, (byte) 0x72, (byte) 0x0E,
+        (byte) 0x5D, (byte) 0x16, (byte) 0x5C, (byte) 0x77, (byte) 0x58, (byte) 0xA7,
+        (byte) 0x31, (byte) 0x3F, (byte) 0xBA, (byte) 0x91, (byte) 0xA7, (byte) 0x16,
+        (byte) 0xFC, (byte) 0x31, (byte) 0xCA, (byte) 0x30, (byte) 0xE0, (byte) 0xF4,
+        (byte) 0x5D, (byte) 0x07, (byte) 0x4A, (byte) 0x9C, (byte) 0x1D, (byte) 0x2B,
+        (byte) 0x4E, (byte) 0xB8, (byte) 0x7C, (byte) 0x67, (byte) 0xCB, (byte) 0x34,
+        (byte) 0x69, (byte) 0x85, (byte) 0x4E, (byte) 0x99, (byte) 0x41, (byte) 0x8A,
+        (byte) 0x35, (byte) 0x85, (byte) 0xF2, (byte) 0x1A,
+    };
+    private static final PSSParameterSpec SHA384withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec =
+            new PSSParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, 206, 1);
+
+    /*
+     * echo "This is a signed message from Kenny Root." | openssl sha512 -binary -out digest.bin \
+     *     && openssl pkeyutl -sign -in digest.bin -inkey privkey.pem \
+     *         -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha512 -pkeyopt rsa_pss_saltlen:64 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA512withRSAPSS_Vector2Signature = new byte[] {
+        (byte) 0x9F, (byte) 0xED, (byte) 0xF8, (byte) 0xEE, (byte) 0x30, (byte) 0x5F,
+        (byte) 0x30, (byte) 0x63, (byte) 0x1D, (byte) 0x86, (byte) 0xD3, (byte) 0xAD,
+        (byte) 0x1D, (byte) 0xD8, (byte) 0xD2, (byte) 0x67, (byte) 0xE2, (byte) 0x43,
+        (byte) 0x64, (byte) 0x71, (byte) 0x98, (byte) 0x82, (byte) 0x00, (byte) 0x84,
+        (byte) 0x2C, (byte) 0x88, (byte) 0x1A, (byte) 0x28, (byte) 0xCD, (byte) 0xA2,
+        (byte) 0x34, (byte) 0x17, (byte) 0x0F, (byte) 0x34, (byte) 0x8A, (byte) 0x10,
+        (byte) 0x79, (byte) 0x6C, (byte) 0xCB, (byte) 0xDA, (byte) 0x2F, (byte) 0xDF,
+        (byte) 0x4D, (byte) 0x98, (byte) 0x01, (byte) 0xE8, (byte) 0xB3, (byte) 0xF5,
+        (byte) 0xCD, (byte) 0x60, (byte) 0xEA, (byte) 0xDE, (byte) 0xA5, (byte) 0x0C,
+        (byte) 0x09, (byte) 0xA1, (byte) 0x4A, (byte) 0xC4, (byte) 0x6B, (byte) 0x09,
+        (byte) 0xB3, (byte) 0x37, (byte) 0x1F, (byte) 0x8A, (byte) 0x64, (byte) 0x81,
+        (byte) 0x2E, (byte) 0x22, (byte) 0x75, (byte) 0x24, (byte) 0x3B, (byte) 0xC0,
+        (byte) 0x0E, (byte) 0x1F, (byte) 0x37, (byte) 0xC9, (byte) 0x1E, (byte) 0x6F,
+        (byte) 0xAF, (byte) 0x3E, (byte) 0x9B, (byte) 0x3F, (byte) 0xA3, (byte) 0xC3,
+        (byte) 0x0B, (byte) 0xB9, (byte) 0x83, (byte) 0x60, (byte) 0x02, (byte) 0xC6,
+        (byte) 0x29, (byte) 0x83, (byte) 0x09, (byte) 0x16, (byte) 0xD9, (byte) 0x3D,
+        (byte) 0x84, (byte) 0x02, (byte) 0x81, (byte) 0x20, (byte) 0xE9, (byte) 0x01,
+        (byte) 0x5B, (byte) 0x85, (byte) 0xC8, (byte) 0x81, (byte) 0x25, (byte) 0x6B,
+        (byte) 0xCB, (byte) 0x78, (byte) 0x48, (byte) 0x65, (byte) 0x3A, (byte) 0xD6,
+        (byte) 0x95, (byte) 0x9B, (byte) 0x62, (byte) 0x2D, (byte) 0x84, (byte) 0x54,
+        (byte) 0x12, (byte) 0x94, (byte) 0xB7, (byte) 0xF0, (byte) 0x1C, (byte) 0xB6,
+        (byte) 0x59, (byte) 0xCD, (byte) 0xC3, (byte) 0x86, (byte) 0xE6, (byte) 0x63,
+        (byte) 0xD7, (byte) 0x99, (byte) 0x9A, (byte) 0xC4, (byte) 0xBF, (byte) 0x8E,
+        (byte) 0xDD, (byte) 0x46, (byte) 0x10, (byte) 0xBE, (byte) 0xAB, (byte) 0x78,
+        (byte) 0xC6, (byte) 0x30, (byte) 0x47, (byte) 0x23, (byte) 0xB6, (byte) 0x2C,
+        (byte) 0x02, (byte) 0x5E, (byte) 0x1F, (byte) 0x07, (byte) 0x96, (byte) 0x54,
+        (byte) 0xEE, (byte) 0x28, (byte) 0xC7, (byte) 0xEC, (byte) 0x57, (byte) 0xDB,
+        (byte) 0x9E, (byte) 0xEF, (byte) 0xE4, (byte) 0x11, (byte) 0xF8, (byte) 0x04,
+        (byte) 0xA9, (byte) 0x26, (byte) 0xC2, (byte) 0x61, (byte) 0xF1, (byte) 0x84,
+        (byte) 0xEB, (byte) 0x94, (byte) 0xBD, (byte) 0x48, (byte) 0xCA, (byte) 0xD1,
+        (byte) 0x84, (byte) 0xCE, (byte) 0x82, (byte) 0x2E, (byte) 0xF6, (byte) 0x4E,
+        (byte) 0x17, (byte) 0x6F, (byte) 0x78, (byte) 0xB9, (byte) 0x0B, (byte) 0xA9,
+        (byte) 0x7D, (byte) 0xBC, (byte) 0xE5, (byte) 0xF8, (byte) 0x7D, (byte) 0xA8,
+        (byte) 0x76, (byte) 0x7A, (byte) 0x8B, (byte) 0xB5, (byte) 0x05, (byte) 0x42,
+        (byte) 0x37, (byte) 0xDA, (byte) 0x15, (byte) 0xE2, (byte) 0xC4, (byte) 0x70,
+        (byte) 0x6E, (byte) 0x95, (byte) 0x60, (byte) 0x47, (byte) 0xF9, (byte) 0x0F,
+        (byte) 0xF4, (byte) 0xA2, (byte) 0x73, (byte) 0xF1, (byte) 0x73, (byte) 0xBD,
+        (byte) 0x0B, (byte) 0x9B, (byte) 0x44, (byte) 0xB6, (byte) 0xA9, (byte) 0xAF,
+        (byte) 0x50, (byte) 0x2D, (byte) 0x5C, (byte) 0xA3, (byte) 0x72, (byte) 0x6F,
+        (byte) 0x85, (byte) 0xE8, (byte) 0x0C, (byte) 0xF9, (byte) 0xE1, (byte) 0xE8,
+        (byte) 0xF7, (byte) 0xC0, (byte) 0x85, (byte) 0x14, (byte) 0x53, (byte) 0x95,
+        (byte) 0xF9, (byte) 0x9E, (byte) 0x65, (byte) 0x05, (byte) 0xF0, (byte) 0x22,
+        (byte) 0x7F, (byte) 0x4F, (byte) 0x40, (byte) 0x45,
+    };
+    private static final PSSParameterSpec SHA512withRSAPSS_Vector2Signature_ParameterSpec =
+            new PSSParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 64, 1);
+
+    /*
+     * echo "This is a signed message from Kenny Root." | openssl sha512 -binary -out digest.bin \
+     *     && openssl pkeyutl -sign -in digest.bin -inkey privkey.pem \
+     *         -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha512 -pkeyopt rsa_pss_saltlen:64 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA512withRSAPSS_NoSalt_Vector2Signature = new byte[] {
+        (byte) 0x49, (byte) 0xA3, (byte) 0xBC, (byte) 0x2E, (byte) 0x67, (byte) 0x96,
+        (byte) 0xA5, (byte) 0x3E, (byte) 0x39, (byte) 0x46, (byte) 0xD6, (byte) 0xA1,
+        (byte) 0xA0, (byte) 0x4F, (byte) 0x3A, (byte) 0x03, (byte) 0x8F, (byte) 0x62,
+        (byte) 0xF2, (byte) 0xD8, (byte) 0x90, (byte) 0xAD, (byte) 0xE2, (byte) 0x3B,
+        (byte) 0x4F, (byte) 0x98, (byte) 0x88, (byte) 0x51, (byte) 0x41, (byte) 0x09,
+        (byte) 0x23, (byte) 0xEB, (byte) 0xF4, (byte) 0x5D, (byte) 0x6A, (byte) 0x22,
+        (byte) 0x12, (byte) 0x12, (byte) 0xDC, (byte) 0x27, (byte) 0xE9, (byte) 0xF7,
+        (byte) 0x64, (byte) 0xA3, (byte) 0xDE, (byte) 0x3A, (byte) 0xB0, (byte) 0xD6,
+        (byte) 0xF2, (byte) 0xC6, (byte) 0xBC, (byte) 0x0B, (byte) 0xA2, (byte) 0xA1,
+        (byte) 0xAA, (byte) 0xB0, (byte) 0x51, (byte) 0xDA, (byte) 0x4F, (byte) 0x28,
+        (byte) 0xA8, (byte) 0xEB, (byte) 0x34, (byte) 0x60, (byte) 0x37, (byte) 0xF7,
+        (byte) 0x50, (byte) 0x7D, (byte) 0xB8, (byte) 0xE7, (byte) 0x24, (byte) 0x8E,
+        (byte) 0xAC, (byte) 0x03, (byte) 0x31, (byte) 0xB8, (byte) 0xE0, (byte) 0xDB,
+        (byte) 0x97, (byte) 0xE9, (byte) 0x1B, (byte) 0x7E, (byte) 0x27, (byte) 0x99,
+        (byte) 0x93, (byte) 0x4D, (byte) 0x46, (byte) 0xB3, (byte) 0xFE, (byte) 0xD6,
+        (byte) 0x23, (byte) 0xB3, (byte) 0xAB, (byte) 0x3E, (byte) 0x33, (byte) 0xA1,
+        (byte) 0x10, (byte) 0x4E, (byte) 0x34, (byte) 0x27, (byte) 0x58, (byte) 0x25,
+        (byte) 0xB7, (byte) 0xBA, (byte) 0xEE, (byte) 0xBE, (byte) 0xE0, (byte) 0x6E,
+        (byte) 0x54, (byte) 0xF7, (byte) 0x73, (byte) 0x7B, (byte) 0x5A, (byte) 0x9C,
+        (byte) 0x74, (byte) 0xEA, (byte) 0xC7, (byte) 0x7E, (byte) 0xC6, (byte) 0xF7,
+        (byte) 0xD5, (byte) 0x32, (byte) 0x0E, (byte) 0x28, (byte) 0x99, (byte) 0xD8,
+        (byte) 0xEF, (byte) 0x97, (byte) 0x62, (byte) 0x8A, (byte) 0xE3, (byte) 0x16,
+        (byte) 0xAD, (byte) 0xE2, (byte) 0xF4, (byte) 0x11, (byte) 0x91, (byte) 0x17,
+        (byte) 0xF3, (byte) 0x32, (byte) 0x90, (byte) 0xCB, (byte) 0x3C, (byte) 0x89,
+        (byte) 0xF4, (byte) 0x20, (byte) 0xF1, (byte) 0x2D, (byte) 0x74, (byte) 0x22,
+        (byte) 0x50, (byte) 0x64, (byte) 0xC2, (byte) 0xF4, (byte) 0xC4, (byte) 0x0D,
+        (byte) 0x18, (byte) 0x6A, (byte) 0x02, (byte) 0x52, (byte) 0x14, (byte) 0x85,
+        (byte) 0x67, (byte) 0xA4, (byte) 0x08, (byte) 0xE5, (byte) 0xBF, (byte) 0x65,
+        (byte) 0x15, (byte) 0xB3, (byte) 0x5A, (byte) 0x88, (byte) 0xEB, (byte) 0xD4,
+        (byte) 0x75, (byte) 0xF9, (byte) 0x52, (byte) 0x73, (byte) 0xA0, (byte) 0x5E,
+        (byte) 0xBA, (byte) 0x37, (byte) 0x6A, (byte) 0x61, (byte) 0x2B, (byte) 0x16,
+        (byte) 0x8A, (byte) 0xA8, (byte) 0x00, (byte) 0xBB, (byte) 0x4D, (byte) 0xFA,
+        (byte) 0x04, (byte) 0xB8, (byte) 0xAB, (byte) 0x4D, (byte) 0xA4, (byte) 0xFC,
+        (byte) 0x9D, (byte) 0xCF, (byte) 0x63, (byte) 0x83, (byte) 0x34, (byte) 0xAE,
+        (byte) 0xAE, (byte) 0xA6, (byte) 0x77, (byte) 0x73, (byte) 0xA2, (byte) 0xB5,
+        (byte) 0x77, (byte) 0xAC, (byte) 0x00, (byte) 0x03, (byte) 0x06, (byte) 0xD4,
+        (byte) 0xDF, (byte) 0x81, (byte) 0x61, (byte) 0xCE, (byte) 0x8E, (byte) 0xC1,
+        (byte) 0xD5, (byte) 0x99, (byte) 0xD5, (byte) 0x2F, (byte) 0xE8, (byte) 0x27,
+        (byte) 0xFA, (byte) 0x84, (byte) 0x7E, (byte) 0x57, (byte) 0xF1, (byte) 0xC9,
+        (byte) 0xEB, (byte) 0x4F, (byte) 0xF9, (byte) 0x92, (byte) 0xC6, (byte) 0xD0,
+        (byte) 0x25, (byte) 0x8A, (byte) 0x16, (byte) 0xD0, (byte) 0xEC, (byte) 0xE5,
+        (byte) 0x33, (byte) 0xA6, (byte) 0xF9, (byte) 0xD5, (byte) 0x0C, (byte) 0x7B,
+        (byte) 0xEC, (byte) 0xC6, (byte) 0x58, (byte) 0x45,
+    };
+    private static final PSSParameterSpec SHA512withRSAPSS_NoSalt_Vector2Signature_ParameterSpec =
+            new PSSParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 0, 1);
+
+    /*
+     * echo "This is a signed message from Kenny Root." | openssl sha512 -binary -out digest.bin \
+     *     && openssl pkeyutl -sign -in digest.bin -inkey privkey.pem \
+     *         -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha512 -pkeyopt rsa_pss_saltlen:190 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] SHA512withRSAPSS_MaxSalt_Vector2Signature = new byte[] {
+        (byte) 0x90, (byte) 0x92, (byte) 0x45, (byte) 0xA1, (byte) 0x1E, (byte) 0x0F,
+        (byte) 0x5F, (byte) 0xF6, (byte) 0x8F, (byte) 0xA0, (byte) 0xBE, (byte) 0x34,
+        (byte) 0x29, (byte) 0x62, (byte) 0xBE, (byte) 0x41, (byte) 0x80, (byte) 0xF0,
+        (byte) 0xB8, (byte) 0x9F, (byte) 0x29, (byte) 0x63, (byte) 0x89, (byte) 0x26,
+        (byte) 0xC2, (byte) 0x22, (byte) 0x1F, (byte) 0x60, (byte) 0xB6, (byte) 0xFC,
+        (byte) 0x5A, (byte) 0x3E, (byte) 0x99, (byte) 0xB8, (byte) 0xC6, (byte) 0x3B,
+        (byte) 0x67, (byte) 0x33, (byte) 0x97, (byte) 0x19, (byte) 0xC6, (byte) 0xFF,
+        (byte) 0x0C, (byte) 0xA9, (byte) 0x04, (byte) 0x5A, (byte) 0xF0, (byte) 0x02,
+        (byte) 0x9A, (byte) 0x19, (byte) 0x0F, (byte) 0xEA, (byte) 0x77, (byte) 0x0D,
+        (byte) 0x56, (byte) 0x38, (byte) 0x0A, (byte) 0xED, (byte) 0x4E, (byte) 0xB7,
+        (byte) 0x57, (byte) 0xBD, (byte) 0xC9, (byte) 0xA3, (byte) 0xE8, (byte) 0xC0,
+        (byte) 0x7D, (byte) 0xF6, (byte) 0xA3, (byte) 0x4B, (byte) 0x61, (byte) 0x45,
+        (byte) 0x06, (byte) 0x5E, (byte) 0x56, (byte) 0xF5, (byte) 0xEF, (byte) 0x76,
+        (byte) 0x6B, (byte) 0xB7, (byte) 0xD4, (byte) 0xBB, (byte) 0xA4, (byte) 0x3C,
+        (byte) 0x52, (byte) 0xF8, (byte) 0x06, (byte) 0x67, (byte) 0xF7, (byte) 0xC3,
+        (byte) 0x8C, (byte) 0x5E, (byte) 0xDF, (byte) 0xFE, (byte) 0x30, (byte) 0x2E,
+        (byte) 0xF8, (byte) 0x59, (byte) 0x3C, (byte) 0x3B, (byte) 0xEA, (byte) 0xA0,
+        (byte) 0x5D, (byte) 0x8F, (byte) 0x18, (byte) 0x73, (byte) 0x1A, (byte) 0x2D,
+        (byte) 0xB1, (byte) 0x55, (byte) 0x07, (byte) 0xC8, (byte) 0x33, (byte) 0xED,
+        (byte) 0x8A, (byte) 0x5E, (byte) 0xC3, (byte) 0xAE, (byte) 0x51, (byte) 0x31,
+        (byte) 0xC4, (byte) 0xFA, (byte) 0xE8, (byte) 0xE9, (byte) 0xBE, (byte) 0x2E,
+        (byte) 0x28, (byte) 0xAA, (byte) 0xED, (byte) 0xA8, (byte) 0x4B, (byte) 0xA3,
+        (byte) 0x13, (byte) 0xB9, (byte) 0x82, (byte) 0x57, (byte) 0xD1, (byte) 0x72,
+        (byte) 0x0D, (byte) 0xA7, (byte) 0xF8, (byte) 0x67, (byte) 0xB8, (byte) 0x55,
+        (byte) 0xF3, (byte) 0x06, (byte) 0xAE, (byte) 0xA7, (byte) 0x69, (byte) 0x66,
+        (byte) 0x0B, (byte) 0x80, (byte) 0x56, (byte) 0x65, (byte) 0xC7, (byte) 0xE9,
+        (byte) 0x60, (byte) 0xDC, (byte) 0x2D, (byte) 0x4B, (byte) 0x26, (byte) 0xA9,
+        (byte) 0xED, (byte) 0x54, (byte) 0x79, (byte) 0x9E, (byte) 0x55, (byte) 0x1D,
+        (byte) 0xEE, (byte) 0x78, (byte) 0x49, (byte) 0xA1, (byte) 0x1F, (byte) 0x9B,
+        (byte) 0x37, (byte) 0xC0, (byte) 0xBA, (byte) 0xE6, (byte) 0x4B, (byte) 0x3B,
+        (byte) 0xAF, (byte) 0x12, (byte) 0x99, (byte) 0x32, (byte) 0x14, (byte) 0x8C,
+        (byte) 0x4D, (byte) 0xEB, (byte) 0x08, (byte) 0xA4, (byte) 0xE3, (byte) 0xC6,
+        (byte) 0x37, (byte) 0x8B, (byte) 0x6E, (byte) 0x7C, (byte) 0xEC, (byte) 0xA3,
+        (byte) 0x78, (byte) 0xED, (byte) 0x4E, (byte) 0x36, (byte) 0xBC, (byte) 0xA2,
+        (byte) 0x7D, (byte) 0x11, (byte) 0x0E, (byte) 0xD0, (byte) 0x53, (byte) 0x14,
+        (byte) 0x93, (byte) 0x16, (byte) 0x54, (byte) 0x45, (byte) 0x79, (byte) 0x7A,
+        (byte) 0x1A, (byte) 0xA1, (byte) 0xEC, (byte) 0xF3, (byte) 0x12, (byte) 0x3F,
+        (byte) 0xFE, (byte) 0x68, (byte) 0xFF, (byte) 0x5A, (byte) 0x3F, (byte) 0xE7,
+        (byte) 0x13, (byte) 0x37, (byte) 0xEB, (byte) 0x60, (byte) 0x0A, (byte) 0x8E,
+        (byte) 0x4F, (byte) 0x54, (byte) 0x46, (byte) 0x19, (byte) 0x82, (byte) 0xBF,
+        (byte) 0xB7, (byte) 0xD2, (byte) 0x19, (byte) 0x71, (byte) 0x78, (byte) 0x38,
+        (byte) 0x4C, (byte) 0xE3, (byte) 0xC4, (byte) 0xEA, (byte) 0x8F, (byte) 0x9B,
+        (byte) 0xE5, (byte) 0xBA, (byte) 0x06, (byte) 0xFC,
+    };
+    private static final PSSParameterSpec SHA512withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec =
+            new PSSParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 190, 1);
+
+    @Test
+    public void testGetCommonInstances_Success() throws Exception {
+        assertNotNull(Signature.getInstance("SHA1withRSA"));
+        assertNotNull(Signature.getInstance("SHA256withRSA"));
+        assertNotNull(Signature.getInstance("SHA384withRSA"));
+        assertNotNull(Signature.getInstance("SHA512withRSA"));
+        assertNotNull(Signature.getInstance("NONEwithRSA"));
+        assertNotNull(Signature.getInstance("MD5withRSA"));
+        assertNotNull(Signature.getInstance("SHA1withDSA"));
+    }
+
+    @Test
+    public void testVerify_SHA1withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector1Data);
+
+        assertTrue("Signature must match expected signature",
+                sig.verify(SHA1withRSA_Vector1Signature));
+    }
+
+    @Test
+    public void testVerify_SHA256withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA256withRSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must match expected signature",
+                sig.verify(SHA256withRSA_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA384withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA384withRSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must match expected signature",
+                sig.verify(SHA384withRSA_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA512withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA512withRSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must match expected signature",
+                sig.verify(SHA512withRSA_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_MD5withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("MD5withRSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must match expected signature",
+                sig.verify(MD5withRSA_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA1withRSAPSS_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA/PSS");
+        sig.initVerify(pubKey);
+        assertPSSAlgorithmParametersEquals(
+                SHA1withRSAPSS_Vector2Signature_ParameterSpec, sig.getParameters());
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must verify",
+                sig.verify(SHA1withRSAPSS_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA1withRSAPSS_NoSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA/PSS");
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA1withRSAPSS_NoSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must verify",
+                sig.verify(SHA1withRSAPSS_NoSalt_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA1withRSAPSS_MaxSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA/PSS");
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA1withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must verify",
+                sig.verify(SHA1withRSAPSS_MaxSalt_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA224withRSAPSS_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA224withRSA/PSS");
+        sig.initVerify(pubKey);
+        assertPSSAlgorithmParametersEquals(
+                SHA224withRSAPSS_Vector2Signature_ParameterSpec, sig.getParameters());
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must verify",
+                sig.verify(SHA224withRSAPSS_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA224withRSAPSS_NoSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA224withRSA/PSS");
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA224withRSAPSS_NoSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must verify",
+                sig.verify(SHA224withRSAPSS_NoSalt_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA224withRSAPSS_MaxSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA224withRSA/PSS");
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA224withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must verify",
+                sig.verify(SHA224withRSAPSS_MaxSalt_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA256withRSAPSS_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA256withRSA/PSS");
+        sig.initVerify(pubKey);
+        assertPSSAlgorithmParametersEquals(
+                SHA256withRSAPSS_Vector2Signature_ParameterSpec, sig.getParameters());
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must verify",
+                sig.verify(SHA256withRSAPSS_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA256withRSAPSS_NoSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA256withRSA/PSS");
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA256withRSAPSS_NoSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must verify",
+                sig.verify(SHA256withRSAPSS_NoSalt_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA256withRSAPSS_MaxSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA256withRSA/PSS");
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA256withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must verify",
+                sig.verify(SHA256withRSAPSS_MaxSalt_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA384withRSAPSS_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA384withRSA/PSS");
+        sig.initVerify(pubKey);
+        assertPSSAlgorithmParametersEquals(
+                SHA384withRSAPSS_Vector2Signature_ParameterSpec, sig.getParameters());
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must verify",
+                sig.verify(SHA384withRSAPSS_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA384withRSAPSS_NoSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA384withRSA/PSS");
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA384withRSAPSS_NoSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must verify",
+                sig.verify(SHA384withRSAPSS_NoSalt_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA384withRSAPSS_MaxSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA384withRSA/PSS");
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA384withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must verify",
+                sig.verify(SHA384withRSAPSS_MaxSalt_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA512withRSAPSS_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA512withRSA/PSS");
+        sig.initVerify(pubKey);
+        assertPSSAlgorithmParametersEquals(
+                SHA512withRSAPSS_Vector2Signature_ParameterSpec, sig.getParameters());
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must verify",
+                sig.verify(SHA512withRSAPSS_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA512withRSAPSS_NoSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA512withRSA/PSS");
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA512withRSAPSS_NoSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must verify",
+                sig.verify(SHA512withRSAPSS_NoSalt_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA512withRSAPSS_MaxSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA512withRSA/PSS");
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA512withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        assertTrue("Signature must verify",
+                sig.verify(SHA512withRSAPSS_MaxSalt_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA1withRSA_Key_InitSignThenInitVerify_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+        RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(privKeySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+
+        // Start a signing operation
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        // Switch to verify
+        sig.initVerify(pubKey);
+        sig.update(Vector1Data);
+
+        assertTrue("Signature must match expected signature",
+                sig.verify(SHA1withRSA_Vector1Signature));
+    }
+
+    @Test
+    public void testVerify_SHA1withRSA_Key_TwoMessages_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+        sig.initVerify(pubKey);
+
+        sig.update(Vector1Data);
+        assertTrue("First signature must match expected signature",
+                sig.verify(SHA1withRSA_Vector1Signature));
+
+        sig.update(Vector2Data);
+        assertTrue("Second signature must match expected signature",
+                sig.verify(SHA1withRSA_Vector2Signature));
+    }
+
+    @Test
+    public void testVerify_SHA1withRSA_Key_WrongExpectedSignature_Failure() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus, RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector1Data);
+
+        assertFalse("Signature should fail to verify", sig.verify(SHA1withRSA_Vector2Signature));
+    }
+
+    @Test
+    public void testSign_SHA1withRSA_CrtKeyWithPublicExponent_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent, RSA_2048_privateExponent, null, null, null, null, null);
+
+        // The RI fails on this key which is totally unreasonable.
+        final PrivateKey privKey;
+        try {
+            privKey = kf.generatePrivate(keySpec);
+        } catch (NullPointerException e) {
+            if (StandardNames.IS_RI) {
+                return;
+            } else {
+                fail("Private key should be created");
+                return;
+            }
+        }
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+        sig.initSign(privKey);
+        sig.update(Vector1Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA1withRSA_Vector1Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector1Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA1withRSA_CrtKey_NoPrivateExponent_Failure() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent, null, RSA_2048_primeP, RSA_2048_primeQ, null, null, null);
+
+        // Failing on this key early is okay.
+        final PrivateKey privKey;
+        try {
+            privKey = kf.generatePrivate(keySpec);
+        } catch (NullPointerException e) {
+            return;
+        } catch (InvalidKeySpecException e) {
+            return;
+        }
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+
+        try {
+            sig.initSign(privKey);
+            fail("Should throw error when private exponent is not available");
+        } catch (InvalidKeyException expected) {
+        }
+    }
+
+    @Test
+    public void testSign_SHA1withRSA_CrtKey_NoModulus_Failure() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(null, RSA_2048_publicExponent,
+                RSA_2048_privateExponent, RSA_2048_primeP, RSA_2048_primeQ, null, null, null);
+
+        // Failing on this key early is okay.
+        final PrivateKey privKey;
+        try {
+            privKey = kf.generatePrivate(keySpec);
+        } catch (NullPointerException e) {
+            return;
+        } catch (InvalidKeySpecException e) {
+            return;
+        }
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+
+        try {
+            sig.initSign(privKey);
+            fail("Should throw error when modulus is not available");
+        } catch (InvalidKeyException expected) {
+        }
+    }
+
+    @Test
+    public void testSign_SHA1withRSA_Key_EmptyKey_Failure() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(null, null);
+
+        // Failing on this key early is okay.
+        final PrivateKey privKey;
+        try {
+            privKey = kf.generatePrivate(keySpec);
+        } catch (NullPointerException e) {
+            return;
+        } catch (InvalidKeySpecException e) {
+            return;
+        }
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+
+        try {
+            sig.initSign(privKey);
+            fail("Should throw error when key is empty");
+        } catch (InvalidKeyException expected) {
+        }
+    }
+
+    @Test
+    public void testSign_SHA1withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA");
+        sig.initSign(privKey);
+        sig.update(Vector1Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA1withRSA_Vector1Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector1Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA224withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+
+        final PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA224withRSA");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA224withRSA_Vector2Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA256withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+
+        final PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA256withRSA");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA256withRSA_Vector2Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA384withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA384withRSA");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA384withRSA_Vector2Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA512withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA512withRSA");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA512withRSA_Vector2Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_MD5withRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("MD5withRSA");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, MD5withRSA_Vector2Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA1withRSAPSS_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA/PSS");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertPSSAlgorithmParametersEquals(
+                SHA1withRSAPSS_Vector2Signature_ParameterSpec, sig.getParameters());
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA1withRSAPSS_NoSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA/PSS");
+        sig.initSign(privKey);
+        sig.setParameter(SHA1withRSAPSS_NoSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertPSSAlgorithmParametersEquals(
+                SHA1withRSAPSS_NoSalt_Vector2Signature_ParameterSpec, sig.getParameters());
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA1withRSAPSS_NoSalt_Vector2Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA1withRSAPSS_NoSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA1withRSAPSS_MaxSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withRSA/PSS");
+        sig.initSign(privKey);
+        sig.setParameter(SHA1withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertPSSAlgorithmParametersEquals(
+                SHA1withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec, sig.getParameters());
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig = Signature.getInstance("SHA1withRSA/PSS");
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA1withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA224withRSAPSS_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA224withRSA/PSS");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertPSSAlgorithmParametersEquals(
+                SHA224withRSAPSS_Vector2Signature_ParameterSpec, sig.getParameters());
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA224withRSAPSS_NoSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA224withRSA/PSS");
+        sig.initSign(privKey);
+        sig.setParameter(SHA224withRSAPSS_NoSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertPSSAlgorithmParametersEquals(
+                SHA224withRSAPSS_NoSalt_Vector2Signature_ParameterSpec, sig.getParameters());
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA224withRSAPSS_NoSalt_Vector2Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA224withRSAPSS_NoSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA224withRSAPSS_MaxSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA224withRSA/PSS");
+        sig.initSign(privKey);
+        sig.setParameter(SHA224withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertPSSAlgorithmParametersEquals(
+                SHA224withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec, sig.getParameters());
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig = Signature.getInstance("SHA224withRSA/PSS");
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA224withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA256withRSAPSS_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA256withRSA/PSS");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertPSSAlgorithmParametersEquals(
+                SHA256withRSAPSS_Vector2Signature_ParameterSpec, sig.getParameters());
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA256withRSAPSS_NoSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA256withRSA/PSS");
+        sig.initSign(privKey);
+        sig.setParameter(SHA256withRSAPSS_NoSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertPSSAlgorithmParametersEquals(
+                SHA256withRSAPSS_NoSalt_Vector2Signature_ParameterSpec, sig.getParameters());
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA256withRSAPSS_NoSalt_Vector2Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA256withRSAPSS_NoSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA256withRSAPSS_MaxSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA256withRSA/PSS");
+        sig.initSign(privKey);
+        sig.setParameter(SHA256withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertPSSAlgorithmParametersEquals(
+                SHA256withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec, sig.getParameters());
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig = Signature.getInstance("SHA256withRSA/PSS");
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA256withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA384withRSAPSS_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA384withRSA/PSS");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertPSSAlgorithmParametersEquals(
+                SHA384withRSAPSS_Vector2Signature_ParameterSpec, sig.getParameters());
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA384withRSAPSS_NoSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA384withRSA/PSS");
+        sig.initSign(privKey);
+        sig.setParameter(SHA384withRSAPSS_NoSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertPSSAlgorithmParametersEquals(
+                SHA384withRSAPSS_NoSalt_Vector2Signature_ParameterSpec, sig.getParameters());
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA384withRSAPSS_NoSalt_Vector2Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA384withRSAPSS_NoSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA384withRSAPSS_MaxSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA384withRSA/PSS");
+        sig.initSign(privKey);
+        sig.setParameter(SHA384withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertPSSAlgorithmParametersEquals(
+                SHA384withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec, sig.getParameters());
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig = Signature.getInstance("SHA384withRSA/PSS");
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA384withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA512withRSAPSS_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA512withRSA/PSS");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertPSSAlgorithmParametersEquals(
+                SHA512withRSAPSS_Vector2Signature_ParameterSpec, sig.getParameters());
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA512withRSAPSS_NoSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA512withRSA/PSS");
+        sig.initSign(privKey);
+        sig.setParameter(SHA512withRSAPSS_NoSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertPSSAlgorithmParametersEquals(
+                SHA512withRSAPSS_NoSalt_Vector2Signature_ParameterSpec, sig.getParameters());
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, SHA512withRSAPSS_NoSalt_Vector2Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA512withRSAPSS_NoSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_SHA512withRSAPSS_MaxSalt_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA512withRSA/PSS");
+        sig.initSign(privKey);
+        sig.setParameter(SHA512withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertPSSAlgorithmParametersEquals(
+                SHA512withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec, sig.getParameters());
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig = Signature.getInstance("SHA512withRSA/PSS");
+        sig.initVerify(pubKey);
+        sig.setParameter(SHA512withRSAPSS_MaxSalt_Vector2Signature_ParameterSpec);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testSign_NONEwithRSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("NONEwithRSA");
+        sig.initSign(privKey);
+        sig.update(Vector1Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertTrue("Signature should match expected",
+                Arrays.equals(signature, NONEwithRSA_Vector1Signature));
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector1Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testVerify_NONEwithRSA_Key_WrongSignature_Failure() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+        Signature sig = Signature.getInstance("NONEwithRSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector1Data);
+        assertFalse("Invalid signature must not verify",
+                sig.verify("Invalid".getBytes("UTF-8")));
+    }
+
+    @Test
+    public void testSign_NONEwithRSA_Key_DataTooLarge_Failure() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("NONEwithRSA");
+        sig.initSign(privKey);
+
+        final int oneTooBig = RSA_2048_modulus.bitLength() - 10;
+        for (int i = 0; i < oneTooBig; i++) {
+            sig.update((byte) i);
+        }
+
+        try {
+            sig.sign();
+            fail("Should throw exception when data is too large");
+        } catch (SignatureException expected) {
+        }
+    }
+
+    @Test
+    public void testSign_NONEwithRSA_Key_DataTooLarge_SingleByte_Failure() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(RSA_2048_modulus,
+                RSA_2048_privateExponent);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("NONEwithRSA");
+        sig.initSign(privKey);
+
+        // This should make it two bytes too big.
+        final int oneTooBig = RSA_2048_modulus.bitLength() - 10;
+        for (int i = 0; i < oneTooBig; i++) {
+            sig.update((byte) i);
+        }
+
+        try {
+            sig.sign();
+            fail("Should throw exception when data is too large");
+        } catch (SignatureException expected) {
+        }
+    }
+
+    @Test
+    public void testVerify_NONEwithRSA_Key_DataTooLarge_Failure() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+        Signature sig = Signature.getInstance("NONEwithRSA");
+        sig.initVerify(pubKey);
+
+        // This should make it one bytes too big.
+        final int oneTooBig = RSA_2048_modulus.bitLength() + 1;
+        final byte[] vector = new byte[oneTooBig];
+        for (int i = 0; i < oneTooBig; i++) {
+            vector[i] = Vector1Data[i % Vector1Data.length];
+        }
+        sig.update(vector);
+
+        assertFalse("Should not verify when signature is too large",
+                sig.verify(NONEwithRSA_Vector1Signature));
+    }
+
+    @Test
+    public void testVerify_NONEwithRSA_Key_DataTooLarge_SingleByte_Failure() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+        Signature sig = Signature.getInstance("NONEwithRSA");
+        sig.initVerify(pubKey);
+
+        // This should make it twice as big as it should be.
+        final int tooBig = RSA_2048_modulus.bitLength() * 2;
+        for (int i = 0; i < tooBig; i++) {
+            sig.update(Vector1Data[i % Vector1Data.length]);
+        }
+
+        assertFalse("Should not verify when signature is too large",
+                sig.verify(NONEwithRSA_Vector1Signature));
+    }
+
+    @Test
+    public void testVerify_NONEwithRSA_Key_SignatureTooSmall_Failure() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+        Signature sig = Signature.getInstance("NONEwithRSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector1Data);
+
+        assertFalse("Invalid signature should not verify",
+                sig.verify("Invalid sig".getBytes("UTF-8")));
+    }
+
+    @Test
+    public void testVerify_NONEwithRSA_Key_SignatureTooLarge_Failure() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+
+        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                RSA_2048_publicExponent);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+        Signature sig = Signature.getInstance("NONEwithRSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector1Data);
+
+        byte[] invalidSignature = new byte[NONEwithRSA_Vector1Signature.length * 2];
+        System.arraycopy(NONEwithRSA_Vector1Signature, 0, invalidSignature, 0,
+                NONEwithRSA_Vector1Signature.length);
+        System.arraycopy(NONEwithRSA_Vector1Signature, 0, invalidSignature,
+                NONEwithRSA_Vector1Signature.length, NONEwithRSA_Vector1Signature.length);
+
+        try {
+            sig.verify(invalidSignature);
+            fail("Should throw exception when signature is too large");
+        } catch (SignatureException expected) {
+        }
+    }
+
+    @Test
+    public void testSign_NONEwithECDSA_Key_Success() throws Exception {
+        KeyPair keys = keyPair("NONEwithECDSA");
+        Signature sig = Signature.getInstance("NONEwithECDSA");
+
+        sig.initSign(keys.getPrivate());
+        sig.update(Vector1Data);
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+        assertTrue("Signature must not be empty", signature.length > 0);
+
+        sig.initVerify(keys.getPublic());
+        sig.update(Vector1Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testVerify_NONEwithECDSA_Key_Success() throws Exception {
+        PublicKey pub = getNamedCurveEcPublicKey();
+        MessageDigest sha1 = MessageDigest.getInstance("SHA1");
+        Signature sig = Signature.getInstance("NONEwithECDSA");
+
+        // NAMED_CURVE_SIGNATURE was signed using SHA1withECDSA, so NONEwithECDSA should
+        // verify the digest
+        sig.initVerify(pub);
+        sig.update(sha1.digest(NAMED_CURVE_VECTOR));
+        assertTrue(sig.verify(NAMED_CURVE_SIGNATURE));
+    }
+
+    @Test
+    public void testVerify_NONEwithECDSA_Key_WrongData_Failure() throws Exception {
+        PublicKey pub = getNamedCurveEcPublicKey();
+        Signature sig = Signature.getInstance("NONEwithECDSA");
+
+        sig.initVerify(pub);
+        sig.update(NAMED_CURVE_VECTOR);
+        assertFalse(sig.verify(NAMED_CURVE_SIGNATURE));
+    }
+
+    // Suppress ErrorProne's warning about the try block that doesn't call fail() but
+    // expects an exception, it's intentional
+    @SuppressWarnings("MissingFail")
+    @Test
+    public void testVerify_NONEwithECDSA_Key_SingleByte_Failure() throws Exception {
+        PublicKey pub = getNamedCurveEcPublicKey();
+        MessageDigest sha1 = MessageDigest.getInstance("SHA1");
+        Signature sig = Signature.getInstance("NONEwithECDSA");
+
+        byte[] corrupted = new byte[NAMED_CURVE_SIGNATURE.length];
+        corrupted[0] = (byte) (corrupted[0] ^ 1);
+
+        sig.initVerify(pub);
+        sig.update(sha1.digest(NAMED_CURVE_VECTOR));
+        try {
+            assertFalse(sig.verify(corrupted));
+        } catch (SignatureException expected) {
+            // It's valid to either return false or throw an exception, accept either
+        }
+    }
+
+    // Tests that an opaque key will be accepted by the ECDSA signature and will delegate to a
+    // functioning alternative provider
+    @Test
+    public void test_NONEwithECDSA_OpaqueKey() throws Exception {
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
+        keyGen.initialize(256);
+        KeyPair kp = keyGen.generateKeyPair();
+
+        // Insert this at #2 so that Conscrypt is still the first provider and CryptoUpcalls
+        // has to drop to manual provider selection rather than relying on Signature's internals
+        Security.insertProviderAt(new OpaqueProvider(), 2);
+        try {
+            Signature sig =
+                    Signature.getInstance("NONEwithECDSA", TestUtils.getConscryptProvider());
+            sig.initSign(OpaqueProvider.wrapKeyMarked(kp.getPrivate()));
+            sig.update(new byte[] {1, 2, 3, 4, 5, 6, 7, 8});
+            byte[] data = sig.sign();
+
+            sig.initVerify(kp.getPublic());
+            sig.update(new byte[] {1, 2, 3, 4, 5, 6, 7, 8});
+            assertTrue(sig.verify(data));
+        } finally {
+            Security.removeProvider(OpaqueProvider.NAME);
+        }
+    }
+
+    // Tests that an opaque key will be accepted by the ECDSA signature and that a broken
+    // alternative provider that throws UnsupportedOperationException will be skipped and
+    // a functioning provider that follows will work.
+    @Test
+    public void test_NONEwithECDSA_OpaqueKey_BrokenProvider() throws Exception {
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
+        keyGen.initialize(256);
+        KeyPair kp = keyGen.generateKeyPair();
+
+        // Insert these at #2 so that Conscrypt is still the first provider and CryptoUpcalls
+        // has to drop to manual provider selection rather than relying on Signature's internals
+        Security.insertProviderAt(new OpaqueProvider(), 2);
+        Security.insertProviderAt(new BrokenProvider(), 2);
+        try {
+            Signature sig =
+                    Signature.getInstance("NONEwithECDSA", TestUtils.getConscryptProvider());
+            sig.initSign(OpaqueProvider.wrapKeyMarked(kp.getPrivate()));
+            sig.update(new byte[] {1, 2, 3, 4, 5, 6, 7, 8});
+            byte[] data = sig.sign();
+
+            sig.initVerify(kp.getPublic());
+            sig.update(new byte[] {1, 2, 3, 4, 5, 6, 7, 8});
+            assertTrue(sig.verify(data));
+        } finally {
+            Security.removeProvider(OpaqueProvider.NAME);
+            Security.removeProvider(BrokenProvider.NAME);
+        }
+    }
+
+    /*
+     * These tests were generated with this DSA private key:
+     *
+     * -----BEGIN DSA PRIVATE KEY-----
+     * MIIBugIBAAKBgQCeYcKJ73epThNnZB8JAf4kE1Pgt5CoTnb+iYJ/esU8TgwgVTCV
+     * QoXhQH0njwcN6NyZ77MHlDTWfP+cvmnT60Q3UO9J+OJb2NEQhJfq46UcwE5pynA9
+     * eLkW5f5hXYpasyxhtgE70AF8Mo3h82kOi1jGzwCU+EkqS+raAP9L0L5AIwIVAL/u
+     * qg8SNFBy+GAT2PFBARClL1dfAoGAd9R6EsyBfn7rOvvmhm1aEB2tqU+5A10hGuQw
+     * lXWOzV7RvQpF7uf3a2UCYNAurz28B90rjjPAk4DZK6dxV3a8jrng1/QjjUEal08s
+     * G9VLZuj60lANF6s0MT2kiNiOqKduFwO3D2h8ZHuSuGPkmmcYgSfUCxNI031O9qiP
+     * VhctCFECgYAz7i1DhjRGUkCdYQd5tVaI42lhXOV71MTYPbuFOIxTL/hny7Z0PZWR
+     * A1blmYE6vrArDEhzpmRvDJZSIMzMfJjUIGu1KO73zpo9siK0xY0/sw5r3QC9txP2
+     * 2Mv3BUIl5TLrs9outQJ0VMwldY2fElgCLWcSVkH44qZwWir1cq+cIwIUEGPDardb
+     * pNvWlWgTDD6a6ZTby+M=
+     * -----END DSA PRIVATE KEY-----
+     *
+     */
+
+    private static final BigInteger DSA_priv = new BigInteger(new byte[] {
+        (byte) 0x10, (byte) 0x63, (byte) 0xc3, (byte) 0x6a, (byte) 0xb7, (byte) 0x5b, (byte) 0xa4, (byte) 0xdb,
+        (byte) 0xd6, (byte) 0x95, (byte) 0x68, (byte) 0x13, (byte) 0x0c, (byte) 0x3e, (byte) 0x9a, (byte) 0xe9,
+        (byte) 0x94, (byte) 0xdb, (byte) 0xcb, (byte) 0xe3,
+    });
+
+    private static final BigInteger DSA_pub = new BigInteger(new byte[] {
+        (byte) 0x33, (byte) 0xee, (byte) 0x2d, (byte) 0x43, (byte) 0x86, (byte) 0x34, (byte) 0x46, (byte) 0x52,
+        (byte) 0x40, (byte) 0x9d, (byte) 0x61, (byte) 0x07, (byte) 0x79, (byte) 0xb5, (byte) 0x56, (byte) 0x88,
+        (byte) 0xe3, (byte) 0x69, (byte) 0x61, (byte) 0x5c, (byte) 0xe5, (byte) 0x7b, (byte) 0xd4, (byte) 0xc4,
+        (byte) 0xd8, (byte) 0x3d, (byte) 0xbb, (byte) 0x85, (byte) 0x38, (byte) 0x8c, (byte) 0x53, (byte) 0x2f,
+        (byte) 0xf8, (byte) 0x67, (byte) 0xcb, (byte) 0xb6, (byte) 0x74, (byte) 0x3d, (byte) 0x95, (byte) 0x91,
+        (byte) 0x03, (byte) 0x56, (byte) 0xe5, (byte) 0x99, (byte) 0x81, (byte) 0x3a, (byte) 0xbe, (byte) 0xb0,
+        (byte) 0x2b, (byte) 0x0c, (byte) 0x48, (byte) 0x73, (byte) 0xa6, (byte) 0x64, (byte) 0x6f, (byte) 0x0c,
+        (byte) 0x96, (byte) 0x52, (byte) 0x20, (byte) 0xcc, (byte) 0xcc, (byte) 0x7c, (byte) 0x98, (byte) 0xd4,
+        (byte) 0x20, (byte) 0x6b, (byte) 0xb5, (byte) 0x28, (byte) 0xee, (byte) 0xf7, (byte) 0xce, (byte) 0x9a,
+        (byte) 0x3d, (byte) 0xb2, (byte) 0x22, (byte) 0xb4, (byte) 0xc5, (byte) 0x8d, (byte) 0x3f, (byte) 0xb3,
+        (byte) 0x0e, (byte) 0x6b, (byte) 0xdd, (byte) 0x00, (byte) 0xbd, (byte) 0xb7, (byte) 0x13, (byte) 0xf6,
+        (byte) 0xd8, (byte) 0xcb, (byte) 0xf7, (byte) 0x05, (byte) 0x42, (byte) 0x25, (byte) 0xe5, (byte) 0x32,
+        (byte) 0xeb, (byte) 0xb3, (byte) 0xda, (byte) 0x2e, (byte) 0xb5, (byte) 0x02, (byte) 0x74, (byte) 0x54,
+        (byte) 0xcc, (byte) 0x25, (byte) 0x75, (byte) 0x8d, (byte) 0x9f, (byte) 0x12, (byte) 0x58, (byte) 0x02,
+        (byte) 0x2d, (byte) 0x67, (byte) 0x12, (byte) 0x56, (byte) 0x41, (byte) 0xf8, (byte) 0xe2, (byte) 0xa6,
+        (byte) 0x70, (byte) 0x5a, (byte) 0x2a, (byte) 0xf5, (byte) 0x72, (byte) 0xaf, (byte) 0x9c, (byte) 0x23,
+    });
+
+    private static final BigInteger DSA_P = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0x9e, (byte) 0x61, (byte) 0xc2, (byte) 0x89, (byte) 0xef, (byte) 0x77, (byte) 0xa9,
+        (byte) 0x4e, (byte) 0x13, (byte) 0x67, (byte) 0x64, (byte) 0x1f, (byte) 0x09, (byte) 0x01, (byte) 0xfe,
+        (byte) 0x24, (byte) 0x13, (byte) 0x53, (byte) 0xe0, (byte) 0xb7, (byte) 0x90, (byte) 0xa8, (byte) 0x4e,
+        (byte) 0x76, (byte) 0xfe, (byte) 0x89, (byte) 0x82, (byte) 0x7f, (byte) 0x7a, (byte) 0xc5, (byte) 0x3c,
+        (byte) 0x4e, (byte) 0x0c, (byte) 0x20, (byte) 0x55, (byte) 0x30, (byte) 0x95, (byte) 0x42, (byte) 0x85,
+        (byte) 0xe1, (byte) 0x40, (byte) 0x7d, (byte) 0x27, (byte) 0x8f, (byte) 0x07, (byte) 0x0d, (byte) 0xe8,
+        (byte) 0xdc, (byte) 0x99, (byte) 0xef, (byte) 0xb3, (byte) 0x07, (byte) 0x94, (byte) 0x34, (byte) 0xd6,
+        (byte) 0x7c, (byte) 0xff, (byte) 0x9c, (byte) 0xbe, (byte) 0x69, (byte) 0xd3, (byte) 0xeb, (byte) 0x44,
+        (byte) 0x37, (byte) 0x50, (byte) 0xef, (byte) 0x49, (byte) 0xf8, (byte) 0xe2, (byte) 0x5b, (byte) 0xd8,
+        (byte) 0xd1, (byte) 0x10, (byte) 0x84, (byte) 0x97, (byte) 0xea, (byte) 0xe3, (byte) 0xa5, (byte) 0x1c,
+        (byte) 0xc0, (byte) 0x4e, (byte) 0x69, (byte) 0xca, (byte) 0x70, (byte) 0x3d, (byte) 0x78, (byte) 0xb9,
+        (byte) 0x16, (byte) 0xe5, (byte) 0xfe, (byte) 0x61, (byte) 0x5d, (byte) 0x8a, (byte) 0x5a, (byte) 0xb3,
+        (byte) 0x2c, (byte) 0x61, (byte) 0xb6, (byte) 0x01, (byte) 0x3b, (byte) 0xd0, (byte) 0x01, (byte) 0x7c,
+        (byte) 0x32, (byte) 0x8d, (byte) 0xe1, (byte) 0xf3, (byte) 0x69, (byte) 0x0e, (byte) 0x8b, (byte) 0x58,
+        (byte) 0xc6, (byte) 0xcf, (byte) 0x00, (byte) 0x94, (byte) 0xf8, (byte) 0x49, (byte) 0x2a, (byte) 0x4b,
+        (byte) 0xea, (byte) 0xda, (byte) 0x00, (byte) 0xff, (byte) 0x4b, (byte) 0xd0, (byte) 0xbe, (byte) 0x40,
+        (byte) 0x23,
+    });
+
+    private static final BigInteger DSA_Q = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0xbf, (byte) 0xee, (byte) 0xaa, (byte) 0x0f, (byte) 0x12, (byte) 0x34, (byte) 0x50,
+        (byte) 0x72, (byte) 0xf8, (byte) 0x60, (byte) 0x13, (byte) 0xd8, (byte) 0xf1, (byte) 0x41, (byte) 0x01,
+        (byte) 0x10, (byte) 0xa5, (byte) 0x2f, (byte) 0x57, (byte) 0x5f,
+    });
+
+    private static final BigInteger DSA_G = new BigInteger(new byte[] {
+        (byte) 0x77, (byte) 0xd4, (byte) 0x7a, (byte) 0x12, (byte) 0xcc, (byte) 0x81, (byte) 0x7e, (byte) 0x7e,
+        (byte) 0xeb, (byte) 0x3a, (byte) 0xfb, (byte) 0xe6, (byte) 0x86, (byte) 0x6d, (byte) 0x5a, (byte) 0x10,
+        (byte) 0x1d, (byte) 0xad, (byte) 0xa9, (byte) 0x4f, (byte) 0xb9, (byte) 0x03, (byte) 0x5d, (byte) 0x21,
+        (byte) 0x1a, (byte) 0xe4, (byte) 0x30, (byte) 0x95, (byte) 0x75, (byte) 0x8e, (byte) 0xcd, (byte) 0x5e,
+        (byte) 0xd1, (byte) 0xbd, (byte) 0x0a, (byte) 0x45, (byte) 0xee, (byte) 0xe7, (byte) 0xf7, (byte) 0x6b,
+        (byte) 0x65, (byte) 0x02, (byte) 0x60, (byte) 0xd0, (byte) 0x2e, (byte) 0xaf, (byte) 0x3d, (byte) 0xbc,
+        (byte) 0x07, (byte) 0xdd, (byte) 0x2b, (byte) 0x8e, (byte) 0x33, (byte) 0xc0, (byte) 0x93, (byte) 0x80,
+        (byte) 0xd9, (byte) 0x2b, (byte) 0xa7, (byte) 0x71, (byte) 0x57, (byte) 0x76, (byte) 0xbc, (byte) 0x8e,
+        (byte) 0xb9, (byte) 0xe0, (byte) 0xd7, (byte) 0xf4, (byte) 0x23, (byte) 0x8d, (byte) 0x41, (byte) 0x1a,
+        (byte) 0x97, (byte) 0x4f, (byte) 0x2c, (byte) 0x1b, (byte) 0xd5, (byte) 0x4b, (byte) 0x66, (byte) 0xe8,
+        (byte) 0xfa, (byte) 0xd2, (byte) 0x50, (byte) 0x0d, (byte) 0x17, (byte) 0xab, (byte) 0x34, (byte) 0x31,
+        (byte) 0x3d, (byte) 0xa4, (byte) 0x88, (byte) 0xd8, (byte) 0x8e, (byte) 0xa8, (byte) 0xa7, (byte) 0x6e,
+        (byte) 0x17, (byte) 0x03, (byte) 0xb7, (byte) 0x0f, (byte) 0x68, (byte) 0x7c, (byte) 0x64, (byte) 0x7b,
+        (byte) 0x92, (byte) 0xb8, (byte) 0x63, (byte) 0xe4, (byte) 0x9a, (byte) 0x67, (byte) 0x18, (byte) 0x81,
+        (byte) 0x27, (byte) 0xd4, (byte) 0x0b, (byte) 0x13, (byte) 0x48, (byte) 0xd3, (byte) 0x7d, (byte) 0x4e,
+        (byte) 0xf6, (byte) 0xa8, (byte) 0x8f, (byte) 0x56, (byte) 0x17, (byte) 0x2d, (byte) 0x08, (byte) 0x51,
+    });
+
+    /**
+     * A possible signature using SHA1withDSA of Vector2Data. Note that DSS is
+     * randomized, so this won't be the exact signature you'll get out of
+     * another signing operation unless you use a fixed RNG.
+     */
+    public static final byte[] SHA1withDSA_Vector2Signature = new byte[] {
+        (byte) 0x30, (byte) 0x2d, (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0x88, (byte) 0xef, (byte) 0xac,
+        (byte) 0x2b, (byte) 0x8b, (byte) 0xe2, (byte) 0x61, (byte) 0xc6, (byte) 0x2b, (byte) 0xea, (byte) 0xd5,
+        (byte) 0x96, (byte) 0xbc, (byte) 0xb0, (byte) 0xa1, (byte) 0x30, (byte) 0x0c, (byte) 0x1f, (byte) 0xed,
+        (byte) 0x11, (byte) 0x02, (byte) 0x14, (byte) 0x15, (byte) 0xc4, (byte) 0xfc, (byte) 0x82, (byte) 0x6f,
+        (byte) 0x17, (byte) 0xdc, (byte) 0x87, (byte) 0x82, (byte) 0x75, (byte) 0x23, (byte) 0xd4, (byte) 0x58,
+        (byte) 0xdc, (byte) 0x73, (byte) 0x3d, (byte) 0xf3, (byte) 0x51, (byte) 0xc0, (byte) 0x57,
+    };
+
+    /**
+     * A possible signature using SHA224withDSA of Vector2Data. Note that DSS is
+     * randomized, so this won't be the exact signature you'll get out of
+     * another signing operation unless you use a fixed RNG.
+     */
+    public static final byte[] SHA224withDSA_Vector2Signature = new byte[] {
+        (byte) 0x30, (byte) 0x2D, (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0xAD, (byte) 0xE5, (byte) 0x6D,
+        (byte) 0xF5, (byte) 0x11, (byte) 0x8D, (byte) 0x2E, (byte) 0x62, (byte) 0x5D, (byte) 0x98, (byte) 0x8A,
+        (byte) 0xC4, (byte) 0x88, (byte) 0x7E, (byte) 0xE6, (byte) 0xA3, (byte) 0x44, (byte) 0x99, (byte) 0xEF,
+        (byte) 0x49, (byte) 0x02, (byte) 0x14, (byte) 0x15, (byte) 0x3E, (byte) 0x32, (byte) 0xD6, (byte) 0xF9,
+        (byte) 0x79, (byte) 0x2C, (byte) 0x60, (byte) 0x6E, (byte) 0xF9, (byte) 0xA9, (byte) 0x78, (byte) 0xE7,
+        (byte) 0x4B, (byte) 0x87, (byte) 0x08, (byte) 0x96, (byte) 0x60, (byte) 0xDE, (byte) 0xB5
+    };
+
+    /**
+     * A possible signature using SHA256withDSA of Vector2Data. Note that DSS is
+     * randomized, so this won't be the exact signature you'll get out of
+     * another signing operation unless you use a fixed RNG.
+     */
+    public static final byte[] SHA256withDSA_Vector2Signature = new byte[] {
+        (byte) 0x30, (byte) 0x2D, (byte) 0x02, (byte) 0x14, (byte) 0x0A, (byte) 0xB1, (byte) 0x74, (byte) 0x45,
+        (byte) 0xE1, (byte) 0x63, (byte) 0x43, (byte) 0x68, (byte) 0x65, (byte) 0xBC, (byte) 0xCA, (byte) 0x45,
+        (byte) 0x27, (byte) 0x11, (byte) 0x4D, (byte) 0x52, (byte) 0xFB, (byte) 0x22, (byte) 0x93, (byte) 0xDD,
+        (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0x98, (byte) 0x32, (byte) 0x1A, (byte) 0x16, (byte) 0x77,
+        (byte) 0x49, (byte) 0xA7, (byte) 0x78, (byte) 0xFD, (byte) 0xE0, (byte) 0xF7, (byte) 0x71, (byte) 0xD4,
+        (byte) 0x80, (byte) 0x50, (byte) 0xA7, (byte) 0xDD, (byte) 0x94, (byte) 0xD1, (byte) 0x6C
+    };
+
+    @Test
+    public void testSign_SHA1withDSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("DSA");
+        DSAPrivateKeySpec keySpec = new DSAPrivateKeySpec(DSA_priv, DSA_P, DSA_Q, DSA_G);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA1withDSA");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+
+        DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(DSA_pub, DSA_P, DSA_Q, DSA_G);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testVerify_SHA1withDSA_Key_Success() throws Exception {
+        KeyFactory kf = KeyFactory.getInstance("DSA");
+        DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(DSA_pub, DSA_P, DSA_Q, DSA_G);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+        Signature sig = Signature.getInstance("SHA1withDSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(SHA1withDSA_Vector2Signature));
+    }
+
+    @Test
+    public void testSign_SHA224withDSA_Key_Success() throws Exception {
+        TestUtils.assumeSHA2WithDSAAvailable();
+        KeyFactory kf = KeyFactory.getInstance("DSA");
+        DSAPrivateKeySpec keySpec = new DSAPrivateKeySpec(DSA_priv, DSA_P, DSA_Q, DSA_G);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA224withDSA");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+
+        DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(DSA_pub, DSA_P, DSA_Q, DSA_G);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testVerify_SHA224withDSA_Key_Success() throws Exception {
+        TestUtils.assumeSHA2WithDSAAvailable();
+        KeyFactory kf = KeyFactory.getInstance("DSA");
+        DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(DSA_pub, DSA_P, DSA_Q, DSA_G);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+        Signature sig = Signature.getInstance("SHA224withDSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(SHA224withDSA_Vector2Signature));
+    }
+
+    @Test
+    public void testSign_SHA256withDSA_Key_Success() throws Exception {
+        TestUtils.assumeSHA2WithDSAAvailable();
+        KeyFactory kf = KeyFactory.getInstance("DSA");
+        DSAPrivateKeySpec keySpec = new DSAPrivateKeySpec(DSA_priv, DSA_P, DSA_Q, DSA_G);
+        PrivateKey privKey = kf.generatePrivate(keySpec);
+
+        Signature sig = Signature.getInstance("SHA256withDSA");
+        sig.initSign(privKey);
+        sig.update(Vector2Data);
+
+        byte[] signature = sig.sign();
+        assertNotNull("Signature must not be null", signature);
+
+        DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(DSA_pub, DSA_P, DSA_Q, DSA_G);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(signature));
+    }
+
+    @Test
+    public void testVerify_SHA256withDSA_Key_Success() throws Exception {
+        TestUtils.assumeSHA2WithDSAAvailable();
+        KeyFactory kf = KeyFactory.getInstance("DSA");
+        DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(DSA_pub, DSA_P, DSA_Q, DSA_G);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+
+        Signature sig = Signature.getInstance("SHA256withDSA");
+        sig.initVerify(pubKey);
+        sig.update(Vector2Data);
+        assertTrue("Signature must verify correctly", sig.verify(SHA256withDSA_Vector2Signature));
+    }
+
+    private final int THREAD_COUNT = 10;
+
+    private void testSignature_MultipleThreads_Misuse(final Signature s, final PrivateKey p)
+            throws Exception {
+        ExecutorService es = Executors.newFixedThreadPool(THREAD_COUNT);
+
+        final CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
+        final byte[] message = new byte[64];
+        List<Future<Void>> futures = new ArrayList<Future<Void>>();
+
+        for (int i = 0; i < THREAD_COUNT; i++) {
+            futures.add(es.submit(new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    // Try to make sure all the threads are ready first.
+                    latch.countDown();
+                    latch.await();
+
+                    for (int j = 0; j < 100; j++) {
+                        s.initSign(p);
+                        s.update(message);
+                        s.sign();
+                    }
+
+                    return null;
+                }
+            }));
+        }
+        es.shutdown();
+        assertTrue("Test should not timeout", es.awaitTermination(1, TimeUnit.MINUTES));
+
+        for (Future<Void> f : futures) {
+            try {
+                f.get();
+            } catch (ExecutionException expected) {
+                // We expect concurrent execution to cause instances to eventually throw, though
+                // if they happen to get lucky and execute completely, that's fine.
+            }
+        }
+    }
+
+    private static final byte[] NAMED_CURVE_VECTOR = "Satoshi Nakamoto".getBytes(
+            Charset.defaultCharset());
+    // $ echo -n "Satoshi Nakamoto" > signed
+    // $ openssl dgst -ecdsa-with-SHA1 -sign key.pem -out sig signed
+    private static final byte[] NAMED_CURVE_SIGNATURE = TestUtils.decodeHex("304402205b41ece6dcc1c5bfcfdae74658d99c08c5e783f3926c11ecc1a8bea5d95cdf27022061a7d5fc687287e2e02dd7c6723e2e27fe0555f789590a37e96b1bb0355b4df0");
+
+    private static PublicKey getNamedCurveEcPublicKey() throws Exception {
+        // These are the parameters for the BitCoin curve (secp256k1). See
+        // https://en.bitcoin.it/wiki/Secp256k1.
+        final BigInteger p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16);
+        final BigInteger a = BigInteger.valueOf(0);
+        final BigInteger b = BigInteger.valueOf(7);
+        final BigInteger x = new BigInteger("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16);
+        final BigInteger y = new BigInteger("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16);
+        final BigInteger order = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16);
+        final int cofactor = 1;
+
+        final ECParameterSpec spec = new ECParameterSpec(new EllipticCurve(new ECFieldFp(p), a, b), new ECPoint(x, y), order, cofactor);
+
+        // $ openssl ecparam -name secp256k1 -genkey > key.pem
+        // $ openssl ec -text -noout < key.pem
+        final BigInteger Px = new BigInteger("2d45572747a625db5fd23b30f97044a682f2d42d31959295043c1fa0034c8ed3", 16);
+        final BigInteger Py = new BigInteger("4d330f52e4bba00145a331041c8bbcf300c4fbfdf3d63d8de7608155b2793808", 16);
+
+        final KeyFactory factory = KeyFactory.getInstance("EC");
+        ECPublicKeySpec keySpec = new ECPublicKeySpec(new ECPoint(Px, Py), spec);
+        return factory.generatePublic(keySpec);
+    }
+
+    @Test
+    public void testArbitraryCurve() throws Exception {
+        final PublicKey pub = getNamedCurveEcPublicKey();
+
+        Signature ecdsaVerify = Signature.getInstance("SHA1withECDSA");
+        ecdsaVerify.initVerify(pub);
+        ecdsaVerify.update(NAMED_CURVE_VECTOR);
+        boolean result = ecdsaVerify.verify(NAMED_CURVE_SIGNATURE);
+        assertEquals(true, result);
+
+        ecdsaVerify = Signature.getInstance("SHA1withECDSA");
+        ecdsaVerify.initVerify(pub);
+        ecdsaVerify.update("Not Satoshi Nakamoto".getBytes("UTF-8"));
+        result = ecdsaVerify.verify(NAMED_CURVE_SIGNATURE);
+        assertEquals(false, result);
+    }
+
+    private static void assertPSSAlgorithmParametersEquals(
+            PSSParameterSpec expectedSpec, AlgorithmParameters actual)
+                    throws InvalidParameterSpecException {
+        assertNotNull(actual);
+        assertEqualsIgnoreCase("PSS", actual.getAlgorithm());
+        PSSParameterSpec actualSpec = actual.getParameterSpec(PSSParameterSpec.class);
+        assertPSSParameterSpecEquals(expectedSpec, actualSpec);
+    }
+
+    private static void assertPSSParameterSpecEquals(
+            PSSParameterSpec expected, PSSParameterSpec actual) {
+        assertEqualsIgnoreCase(expected.getDigestAlgorithm(), actual.getDigestAlgorithm());
+        assertEqualsIgnoreCase(expected.getMGFAlgorithm(), actual.getMGFAlgorithm());
+        if (!"MGF1".equalsIgnoreCase(expected.getMGFAlgorithm())) {
+            fail("Unsupported MGF algorithm: " + expected.getMGFAlgorithm());
+        }
+        MGF1ParameterSpec expectedMgfParams = (MGF1ParameterSpec) expected.getMGFParameters();
+        MGF1ParameterSpec actualMgfParams = (MGF1ParameterSpec) actual.getMGFParameters();
+        assertEqualsIgnoreCase(
+                expectedMgfParams.getDigestAlgorithm(), actualMgfParams.getDigestAlgorithm());
+        assertEquals(expected.getSaltLength(), actual.getSaltLength());
+        assertEquals(expected.getTrailerField(), actual.getTrailerField());
+    }
+
+    private static void assertEqualsIgnoreCase(String expected, String actual) {
+        if (expected == null) {
+            if (actual == null) {
+                return;
+            }
+            fail("Expected null, actual: <" + actual + ">");
+        } else if (actual == null) {
+            fail("Expected: <" + expected + ">, actual: null");
+        } else {
+            if (!expected.equalsIgnoreCase(actual)) {
+                fail("Expected: <" + expected + ">, actual: <" + actual + ">");
+            }
+        }
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/cert/CertificateFactoryTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/cert/CertificateFactoryTest.java
new file mode 100644
index 0000000..54dcee2
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/cert/CertificateFactoryTest.java
@@ -0,0 +1,717 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security.cert;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.org.conscrypt.Conscrypt;
+import com.android.org.conscrypt.TestUtils;
+import com.android.org.conscrypt.java.security.StandardNames;
+import dalvik.system.VMRuntime;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.cert.CertPath;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TimeZone;
+import javax.security.auth.x500.X500Principal;
+import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.x509.X509V3CertificateGenerator;
+import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
+import tests.util.ServiceTester;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class CertificateFactoryTest {
+
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
+    private static final String VALID_CERTIFICATE_PEM =
+            "-----BEGIN CERTIFICATE-----\n"
+            + "MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM\n"
+            + "MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg\n"
+            + "THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x\n"
+            + "MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh\n"
+            + "MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw\n"
+            + "FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC\n"
+            + "gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN\n"
+            + "gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L\n"
+            + "05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM\n"
+            + "BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl\n"
+            + "LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF\n"
+            + "BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw\n"
+            + "Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0\n"
+            + "ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUF\n"
+            + "AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5\n"
+            + "u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6\n"
+            + "z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==\n"
+            + "-----END CERTIFICATE-----\n";
+
+    private static final String VALID_CERTIFICATE_PEM_CRLF =
+            "-----BEGIN CERTIFICATE-----\r\n"
+            + "MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM\r\n"
+            + "MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg\r\n"
+            + "THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x\r\n"
+            + "MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh\r\n"
+            + "MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw\r\n"
+            + "FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC\r\n"
+            + "gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN\r\n"
+            + "gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L\r\n"
+            + "05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM\r\n"
+            + "BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl\r\n"
+            + "LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF\r\n"
+            + "BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw\r\n"
+            + "Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0\r\n"
+            + "ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUF\r\n"
+            + "AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5\r\n"
+            + "u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6\r\n"
+            + "z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==\r\n"
+            + "-----END CERTIFICATE-----\r\n";
+
+    private static final byte[] VALID_CERTIFICATE_PEM_HEADER = "-----BEGIN CERTIFICATE-----\n"
+            .getBytes(Charset.defaultCharset());
+
+    private static final byte[] VALID_CERTIFICATE_PEM_DATA =
+             ("MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM"
+            + "MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg"
+            + "THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x"
+            + "MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh"
+            + "MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw"
+            + "FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC"
+            + "gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN"
+            + "gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L"
+            + "05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM"
+            + "BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl"
+            + "LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF"
+            + "BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw"
+            + "Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0"
+            + "ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUF"
+            + "AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5"
+            + "u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6"
+            + "z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==")
+                     .getBytes(Charset.defaultCharset());
+
+    private static final byte[] VALID_CERTIFICATE_PEM_FOOTER = "\n-----END CERTIFICATE-----\n"
+            .getBytes(Charset.defaultCharset());
+
+    private static final String INVALID_CERTIFICATE_PEM =
+            "-----BEGIN CERTIFICATE-----\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+            + "AAAAAAAA\n"
+            + "-----END CERTIFICATE-----";
+
+    private static final String VALID_CERTIFICATE_DER_BASE64 =
+            "MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBMMQswCQYDVQQG"
+            + "EwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkgTHRkLjEWMBQGA1UEAxMNVGhh"
+            + "d3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0xMTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVT"
+            + "MRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApH"
+            + "b29nbGUgSW5jMRcwFQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw"
+            + "gYkCgYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jNgtXj9xVo"
+            + "RaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L05vuuWciKh0R73mkszeK"
+            + "9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAMBgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0w"
+            + "K6ApoCeGJWh0dHA6Ly9jcmwudGhhd3RlLmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYI"
+            + "KwYBBQUHAwEGCCsGAQUFBwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzAB"
+            + "hhZodHRwOi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0ZS5j"
+            + "b20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUFAAOBgQCfQ89bxFAp"
+            + "sb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5u2ONgJd8IyAPkU0Wueru9G2Jysa9"
+            + "zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqA"
+            + "ibAxWEEHXw==";
+
+    @Test
+    public void test_generateCertificate() throws Exception {
+        ServiceTester.test("CertificateFactory")
+                .withAlgorithm("X509")
+                .run(new ServiceTester.Test() {
+                    @Override
+                    public void test(Provider p, String algorithm) throws Exception {
+                        CertificateFactory cf = CertificateFactory.getInstance("X509", p);
+                        test_generateCertificate(cf);
+                        test_generateCertificate_InputStream_Offset_Correct(cf);
+                        test_generateCertificate_InputStream_Empty(cf);
+                        test_generateCertificate_InputStream_InvalidStart_Failure(cf);
+                        test_generateCertificate_AnyLineLength_Success(cf);
+                        test_generateCertificate_PartialInput(cf);
+                    }
+                });
+    }
+
+    private void test_generateCertificate(CertificateFactory cf) throws Exception {
+        {
+            byte[] valid = VALID_CERTIFICATE_PEM.getBytes(Charset.defaultCharset());
+            Certificate c = cf.generateCertificate(new ByteArrayInputStream(valid));
+            assertNotNull(c);
+        }
+
+        {
+            byte[] valid = VALID_CERTIFICATE_PEM_CRLF.getBytes(Charset.defaultCharset());
+            Certificate c = cf.generateCertificate(new ByteArrayInputStream(valid));
+            assertNotNull(c);
+        }
+
+        {
+            byte[] valid = TestUtils.decodeBase64(VALID_CERTIFICATE_DER_BASE64);
+            Certificate c = cf.generateCertificate(new ByteArrayInputStream(valid));
+            assertNotNull(c);
+        }
+
+        try {
+            byte[] invalid = INVALID_CERTIFICATE_PEM.getBytes(Charset.defaultCharset());
+            cf.generateCertificate(new ByteArrayInputStream(invalid));
+            fail();
+        } catch (CertificateException expected) {
+        }
+
+        try {
+            Certificate c = cf.generateCertificate(new ByteArrayInputStream(new byte[0]));
+            // Bouncy Castle returns null on empty inputs rather than throwing an exception,
+            // which technically doesn't satisfy the method contract, but we'll accept it
+            assertTrue((c == null) && cf.getProvider().getName().equals("BC"));
+        } catch (CertificateException expected) {
+        }
+
+        try {
+            Certificate c = cf.generateCertificate(new ByteArrayInputStream(new byte[] { 0x00 }));
+            // Bouncy Castle returns null on short inputs rather than throwing an exception,
+            // which technically doesn't satisfy the method contract, but we'll accept it
+            assertTrue((c == null) && cf.getProvider().getName().equals("BC"));
+        } catch (CertificateException expected) {
+        }
+    }
+
+    /*
+     * Checks all possible line lengths for PEM input data.
+     */
+    private void test_generateCertificate_AnyLineLength_Success(CertificateFactory cf)
+            throws Exception {
+        // RI barfs on this
+        if (StandardNames.IS_RI) {
+            return;
+        }
+
+        int lineLength = 1;
+        int maxLineLength = VALID_CERTIFICATE_PEM_DATA.length;
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        baos.write(VALID_CERTIFICATE_PEM_HEADER);
+        int offset = 0;
+        while (lineLength < (maxLineLength - 4)) {
+            int end = offset + lineLength;
+            if (end > VALID_CERTIFICATE_PEM_DATA.length) {
+                end = VALID_CERTIFICATE_PEM_DATA.length;
+            }
+            baos.write(Arrays.copyOfRange(VALID_CERTIFICATE_PEM_DATA, offset, end));
+            baos.write('\n');
+            offset += lineLength;
+            if (offset >= maxLineLength) {
+                baos.write(VALID_CERTIFICATE_PEM_FOOTER);
+                try {
+                    Certificate c =
+                            cf.generateCertificate(new ByteArrayInputStream(baos.toByteArray()));
+                    assertNotNull(c);
+                } catch (Exception e) {
+                    throw new Exception("Fail at line length " + lineLength, e);
+                }
+                baos.reset();
+                baos.write(VALID_CERTIFICATE_PEM_HEADER);
+                offset = 0;
+            } else {
+                lineLength++;
+            }
+        }
+
+    }
+
+    private void test_generateCertificate_InputStream_Empty(CertificateFactory cf) throws Exception {
+        try {
+            Certificate c = cf.generateCertificate(new ByteArrayInputStream(new byte[0]));
+            if (!"BC".equals(cf.getProvider().getName())) {
+                fail("should throw CertificateException: " + cf.getProvider().getName());
+            }
+            assertNull(c);
+        } catch (CertificateException e) {
+            if ("BC".equals(cf.getProvider().getName())) {
+                fail("should return null: " + cf.getProvider().getName());
+            }
+        }
+    }
+
+    private void test_generateCertificate_InputStream_InvalidStart_Failure(CertificateFactory cf)
+            throws Exception {
+        try {
+            Certificate c = cf.generateCertificate(new ByteArrayInputStream(
+                    "-----BEGIN CERTIFICATE-----".getBytes(Charset.defaultCharset())));
+            if (!"BC".equals(cf.getProvider().getName())) {
+                fail("should throw CertificateException: " + cf.getProvider().getName());
+            }
+            assertNull(c);
+        } catch (CertificateException expected) {
+            if ("BC".equals(cf.getProvider().getName())) {
+                fail("should return null: " + cf.getProvider().getName());
+            }
+        }
+    }
+
+    private void test_generateCertificate_InputStream_Offset_Correct(CertificateFactory cf)
+            throws Exception {
+        byte[] valid = VALID_CERTIFICATE_PEM.getBytes(Charset.defaultCharset());
+
+        byte[] doubleCertificateData = new byte[valid.length * 2];
+        System.arraycopy(valid, 0, doubleCertificateData, 0, valid.length);
+        System.arraycopy(valid, 0, doubleCertificateData, valid.length, valid.length);
+        MeasuredInputStream certStream = new MeasuredInputStream(new ByteArrayInputStream(
+                doubleCertificateData));
+        Certificate certificate = cf.generateCertificate(certStream);
+        assertNotNull(certificate);
+        assertEquals(valid.length, certStream.getCount());
+    }
+
+    /**
+     * Proxy that counts the number of bytes read from an InputStream.
+     */
+    private static class MeasuredInputStream extends InputStream {
+        private long mCount = 0;
+
+        private long mMarked = 0;
+
+        private InputStream mStream;
+
+        public MeasuredInputStream(InputStream is) {
+            mStream = is;
+        }
+
+        public long getCount() {
+            return mCount;
+        }
+
+        @Override
+        public int read() throws IOException {
+            int nextByte = mStream.read();
+            mCount++;
+            return nextByte;
+        }
+
+        @Override
+        public int read(byte[] buffer) throws IOException {
+            int count = mStream.read(buffer);
+            mCount += count;
+            return count;
+        }
+
+        @Override
+        public int read(byte[] buffer, int offset, int length) throws IOException {
+            int count = mStream.read(buffer, offset, length);
+            mCount += count;
+            return count;
+        }
+
+        @Override
+        public long skip(long byteCount) throws IOException {
+            long count = mStream.skip(byteCount);
+            mCount += count;
+            return count;
+        }
+
+        @Override
+        public int available() throws IOException {
+            return mStream.available();
+        }
+
+        @Override
+        public void close() throws IOException {
+            mStream.close();
+        }
+
+        @Override
+        public void mark(int readlimit) {
+            mMarked = mCount;
+            mStream.mark(readlimit);
+        }
+
+        @Override
+        public boolean markSupported() {
+            return mStream.markSupported();
+        }
+
+        @Override
+        public synchronized void reset() throws IOException {
+            mCount = mMarked;
+            mStream.reset();
+        }
+    }
+
+    /**
+     * An InputStream that only returns two bytes at a time, no matter how many were requested.
+     */
+    private static class SlowInputStream extends FilterInputStream {
+        protected SlowInputStream(InputStream inputStream) {
+            super(inputStream);
+        }
+
+        @Override
+        public int read(byte[] buffer) throws IOException {
+            if (buffer.length < 2) {
+                return super.read(buffer);
+            }
+            return super.read(buffer, 0, 2);
+        }
+
+        @Override
+        public int read(byte[] buffer, int offset, int len) throws IOException {
+            if (len < 2) {
+                return super.read(buffer, offset, len);
+            }
+            return super.read(buffer, offset, 2);
+        }
+    }
+
+    // Test that certificates are decoded properly even if the InputStream is unhelpful and only
+    // returns partial inputs on basically every request.
+    private void test_generateCertificate_PartialInput(CertificateFactory cf) throws Exception {
+        byte[] valid = VALID_CERTIFICATE_PEM.getBytes(Charset.defaultCharset());
+        Certificate c =
+                cf.generateCertificate(new SlowInputStream(new ByteArrayInputStream(valid)));
+        assertNotNull(c);
+
+        valid = TestUtils.decodeBase64(VALID_CERTIFICATE_DER_BASE64);
+        c = cf.generateCertificate(new SlowInputStream(new ByteArrayInputStream(valid)));
+        assertNotNull(c);
+    }
+
+    /* CertPath tests */
+    @Test
+    public void testGenerateCertPath() throws Exception {
+        KeyHolder ca = generateCertificate(true, null);
+        KeyHolder cert1 = generateCertificate(true, ca);
+        KeyHolder cert2 = generateCertificate(false, cert1);
+        KeyHolder cert3 = generateCertificate(false, cert2);
+
+        List<X509Certificate> certs = new ArrayList<X509Certificate>();
+        certs.add(cert3.certificate);
+        certs.add(cert2.certificate);
+        certs.add(cert1.certificate);
+
+        List<X509Certificate> duplicatedCerts = new ArrayList<X509Certificate>(certs);
+        duplicatedCerts.add(cert2.certificate);
+
+        Provider[] providers = Security.getProviders("CertificateFactory.X509");
+        for (Provider p : providers) {
+            final CertificateFactory cf = CertificateFactory.getInstance("X.509", p);
+
+            if (Conscrypt.isConscrypt(p)) {
+                // It's not specified whether duplicated certs should work, but we want Conscrypt
+                // to accept them
+                {
+                    final CertPath duplicatedPath = cf.generateCertPath(duplicatedCerts);
+                    // This shouldn't cause an exception
+                    duplicatedPath.getEncoded();
+                }
+            }
+
+            testCertPathEncoding(cf, certs, null);
+
+            /* Make sure all encoding entries are the same. */
+            final Iterator<String> it1 = cf.getCertPathEncodings();
+            final Iterator<String> it2 = cf.generateCertPath(certs).getEncodings();
+            for (;;) {
+                assertEquals(p.getName(), it1.hasNext(), it2.hasNext());
+                if (!it1.hasNext()) {
+                    break;
+                }
+
+                String encoding = it1.next();
+                assertEquals(p.getName(), encoding, it2.next());
+
+                try {
+                    it1.remove();
+                    fail("Should not be able to remove from iterator");
+                } catch (UnsupportedOperationException expected) {
+                }
+
+                try {
+                    it2.remove();
+                    fail("Should not be able to remove from iterator");
+                } catch (UnsupportedOperationException expected) {
+                }
+
+                /* Now test using this encoding. */
+                testCertPathEncoding(cf, certs, encoding);
+            }
+        }
+    }
+
+    private void testCertPathEncoding(CertificateFactory cf, List<X509Certificate> expectedCerts,
+            String encoding) throws Exception {
+        final String providerName = cf.getProvider().getName() + "[" + encoding + "]";
+
+        final CertPath pathFromList = cf.generateCertPath(expectedCerts);
+
+        // Create a copy we can modify and discard.
+        final byte[] encodedCopy;
+        if (encoding == null) {
+            encodedCopy = pathFromList.getEncoded();
+            assertNotNull(providerName, encodedCopy);
+
+            // check idempotence
+            assertEquals(providerName, Arrays.toString(pathFromList.getEncoded()),
+                    Arrays.toString(encodedCopy));
+        } else {
+            encodedCopy = pathFromList.getEncoded(encoding);
+            assertNotNull(providerName, encodedCopy);
+
+            // check idempotence
+            assertEquals(providerName, Arrays.toString(pathFromList.getEncoded(encoding)),
+                    Arrays.toString(encodedCopy));
+        }
+
+        // Try to modify byte array.
+        encodedCopy[0] ^= (byte) 0xFF;
+
+        // Get a real copy we will use if the test proceeds.
+        final byte[] encoded;
+        if (encoding == null) {
+            encoded = pathFromList.getEncoded();
+            assertNotNull(providerName, encodedCopy);
+
+            // check idempotence
+            assertEquals(providerName, Arrays.toString(pathFromList.getEncoded()),
+                    Arrays.toString(encoded));
+        } else {
+            encoded = pathFromList.getEncoded(encoding);
+            assertNotNull(providerName, encodedCopy);
+
+            // check idempotence
+            assertEquals(providerName, Arrays.toString(pathFromList.getEncoded(encoding)),
+                    Arrays.toString(encoded));
+        }
+        assertFalse(providerName, Arrays.toString(encoded).equals(Arrays.toString(encodedCopy)));
+
+        encodedCopy[0] ^= (byte) 0xFF;
+        assertEquals(providerName, Arrays.toString(encoded), Arrays.toString(encodedCopy));
+
+        final CertPath actualPath;
+        if (encoding == null) {
+            actualPath = cf.generateCertPath(new ByteArrayInputStream(encoded));
+        } else {
+            actualPath = cf.generateCertPath(new ByteArrayInputStream(encoded), encoding);
+        }
+
+        // PKCS7 certificate bags are not guaranteed to be in order.
+        final List<? extends Certificate> actualCerts;
+        if (!"PKCS7".equals(encoding)) {
+            actualCerts = actualPath.getCertificates();
+            assertEquals(providerName, expectedCerts, actualCerts);
+        } else {
+            actualCerts = pathFromList.getCertificates();
+        }
+
+        try {
+            actualCerts.remove(0);
+            fail("List of certificate should be immutable");
+        } catch (UnsupportedOperationException expected) {
+        }
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(baos);
+        oos.writeObject(actualPath);
+        oos.close();
+
+        byte[] serialized = baos.toByteArray();
+        ByteArrayInputStream bais = new ByteArrayInputStream(serialized);
+        ObjectInputStream ois = new ObjectInputStream(bais);
+        Object output = ois.readObject();
+        assertTrue(providerName, output instanceof CertPath);
+
+        assertEquals(providerName, actualPath, (CertPath) output);
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class KeyHolder {
+        public X509Certificate certificate;
+
+        public PrivateKey privateKey;
+    }
+
+    @SuppressWarnings("deprecation")
+    private static KeyHolder generateCertificate(boolean isCa, KeyHolder issuer) throws Exception {
+        Date startDate = new Date();
+
+        GregorianCalendar cal = new GregorianCalendar();
+        cal.setTimeZone(TimeZone.getTimeZone("UTC"));
+        cal.set(2100, 0, 1, 0, 0, 0); // Jan 1, 2100 UTC
+        Date expiryDate = cal.getTime();
+
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+        KeyPair keyPair = kpg.generateKeyPair();
+
+        BigInteger serial;
+        X500Principal issuerPrincipal;
+        X500Principal subjectPrincipal;
+        PrivateKey caKey;
+        if (issuer != null) {
+            serial = issuer.certificate.getSerialNumber().add(BigInteger.ONE);
+            subjectPrincipal = new X500Principal("CN=Test Certificate Serial #" + serial.toString());
+            issuerPrincipal = issuer.certificate.getSubjectX500Principal();
+            caKey = issuer.privateKey;
+        } else {
+            serial = BigInteger.ONE;
+            subjectPrincipal = new X500Principal("CN=Test CA, O=Tests, C=US");
+            issuerPrincipal = subjectPrincipal;
+            caKey = keyPair.getPrivate();
+        }
+
+        BasicConstraints basicConstraints;
+        if (isCa) {
+            basicConstraints = new BasicConstraints(10 - serial.intValue());
+        } else {
+            basicConstraints = new BasicConstraints(false);
+        }
+
+        X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
+
+        PublicKey pubKey = keyPair.getPublic();
+        certGen.setSerialNumber(serial);
+        certGen.setIssuerDN(issuerPrincipal);
+        certGen.setNotBefore(startDate);
+        certGen.setNotAfter(expiryDate);
+        certGen.setSubjectDN(subjectPrincipal);
+        certGen.setPublicKey(pubKey);
+        certGen.setSignatureAlgorithm("SHA1withRSA");
+
+        if (issuer != null) {
+            certGen.addExtension(Extension.authorityKeyIdentifier, false,
+                    new AuthorityKeyIdentifierStructure(issuer.certificate));
+        } else {
+            certGen.addExtension(Extension.authorityKeyIdentifier, false,
+                    new AuthorityKeyIdentifier(generatePublicKeyDigest(pubKey)));
+        }
+
+        certGen.addExtension(Extension.subjectKeyIdentifier, false,
+                new SubjectKeyIdentifier(generatePublicKeyDigest(pubKey)));
+        certGen.addExtension(Extension.basicConstraints, true, basicConstraints);
+
+        X509Certificate cert = certGen.generate(caKey);
+
+        KeyHolder holder = new KeyHolder();
+        holder.certificate = cert;
+        holder.privateKey = keyPair.getPrivate();
+
+        return holder;
+    }
+
+    /**
+     * Generates a type 1 key identifier according to RFC 3280 4.2.1.2.
+     */
+    private static byte[] generatePublicKeyDigest(PublicKey pubKey) {
+        SubjectPublicKeyInfo spki = SubjectPublicKeyInfo.getInstance(pubKey.getEncoded());
+        MessageDigest sha1digest;
+        try {
+            sha1digest = MessageDigest.getInstance("SHA-1");
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("SHA-1 not available");
+        }
+        return sha1digest.digest(spki.getPublicKeyData().getBytes());
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/cert/X509CertificateTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/cert/X509CertificateTest.java
new file mode 100644
index 0000000..6456e70
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/java/security/cert/X509CertificateTest.java
@@ -0,0 +1,214 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security.cert;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import dalvik.system.VMRuntime;
+import java.io.ByteArrayInputStream;
+import java.nio.charset.Charset;
+import java.security.InvalidKeyException;
+import java.security.Provider;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
+import tests.util.ServiceTester;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class X509CertificateTest {
+
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
+    private static final String VALID_CERT = "-----BEGIN CERTIFICATE-----\n"
+            + "MIIFMjCCAxqgAwIBAgIJAL0mG5fOeJ7xMA0GCSqGSIb3DQEBCwUAMC0xCzAJBgNV\n"
+            + "BAYTAkdCMQ8wDQYDVQQHDAZMb25kb24xDTALBgNVBAoMBFRlc3QwIBcNMTgwOTE3\n"
+            + "MTIxNzU3WhgPMjExODA4MjQxMjE3NTdaMC0xCzAJBgNVBAYTAkdCMQ8wDQYDVQQH\n"
+            + "DAZMb25kb24xDTALBgNVBAoMBFRlc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\n"
+            + "ggIKAoICAQDCMhBrRAGGw+n2GdctBr/cEK4FZA6ajiHjihgpCHoSBdyL4R2jGKLS\n"
+            + "g0WgaMXa1HpkKN7LcIySosEBPlmcRkr1RqbEvQStOSvoFCXYvtx3alM6HTbXMcDR\n"
+            + "mqoKoABP6LXsPSoMWIgqMtP2X9EOppzHVIK1yFYFfbIlvYUV2Ka+MuMe0Vh5wvD1\n"
+            + "4GanPb+cWSKgdRSVQovCCMY3yWtZKVEaxRpCsk/mYYIFWz0tcgMjIKwDx1XXgiAV\n"
+            + "nU6NK43xbaw3XhtnaD/pv9lhTTbNrlcln9LjTD097BaK4R+1AEPHnpfxA9Ui3upn\n"
+            + "kbsNUdGdOB0ksZi/vd7lh833YgquQUIAhYrbfvq/HFCpVV1gljzlS3sqULYpLE//\n"
+            + "i3OsuL2mE+CYIJGpIi2GeJJWXciNMTJDOqTn+fRDtVb4RPp4Y70DJirp7XzaBi3q\n"
+            + "H0edANCzPSRCDbZsOhzIXhXshldiXVRX666DDlbMQgLTEnNKrkwv6DmU8o15XQsb\n"
+            + "8k1Os2YwXmkEOxUQ7AJZXVTZSf6UK9Znmdq1ZrHjybMfRUkHVxJcnKvrxfryralv\n"
+            + "gzfvu+D6HuxrCo3Ojqa+nDgIbxKEBtdrcsMhq1jWPFhjwo1fSadAkKOfdCAuXJRD\n"
+            + "THg3b4Sf+W7Cpc570YHrIpBf7WFl2XsPcEM0mJZ5+yATASCubNozQwIDAQABo1Mw\n"
+            + "UTAdBgNVHQ4EFgQUES0hupZSqY21JOba10QyZuxm91EwHwYDVR0jBBgwFoAUES0h\n"
+            + "upZSqY21JOba10QyZuxm91EwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsF\n"
+            + "AAOCAgEABTN5S30ng/RMpBweDm2N561PdpaCdiRXtAFCRVWR2mkDYC/Xj9Vqe6be\n"
+            + "PyM7L/5OKYVjzF1yJu67z/dx+ja5o+41g17jdqla7hyPx+9B4uRyDh+1KJTa+duj\n"
+            + "mw/aA1LCr6O6W4WizDOsChJ6FaB2Y1+GlFnKWb5nUdhVJqXQE1WOX9dZnw8Y4Npd\n"
+            + "VmAsjWot0BZorJrt3fwfcv3QfA896twkbo7Llv/8qzg4sXZXZ4ZtgAOqnPngiSn+\n"
+            + "JT/vYCXZ406VvAFpFqMcVz2dO/VGuL8lGIMHRKNyafrsV81EzH1W/XmRWOgvgj6r\n"
+            + "yQI63ln/AMY72HQ97xLkE1xKunGz6bK5Ug5+O43Uftc4Mb6MUgzo+ZqEQ3Ob+cAV\n"
+            + "cvjmtwDaPO/O39O5Xq0tLTlkn2/cKf4OQ6S++GDxzyRVHh5JXgP4j9+jfZY57Woy\n"
+            + "R1bE7N50JjY4cDermBJKdlBIjL7UPhqmLyaG7V0hBitFlgGBUCcJtJOV0xYd5aF3\n"
+            + "pxNkvMXhBmh95fjxJ0cJjpO7tN1RAwtMMNgsl7OUbuVRQCHOPW5DgP5qY21jDeRn\n"
+            + "BY82382l+9QzykmJLI5MZnmj4BA9uIDCwMtoTTvP++SsvhUAbuvh7MOOUQL0EY4m\n"
+            + "KStYq7X9PKseN+PvmfeoffIKc5R/Ha39oi7cGMVHCr8aiEhsf94=\n"
+            + "-----END CERTIFICATE-----";
+
+    /*
+     This certificate is a modified version of the above self-signed cert. The cert has
+     been modified to change the certificate data's signature algorithm
+     declaration from sha256withRSAEncryption to sha512withRSAEncryption.  This causes
+     the signature block's algorithm (which is unmodified) to not match the cert info.
+     */
+    private static final String MISMATCHED_ALGORITHM_CERT =
+            "-----BEGIN CERTIFICATE-----\n"
+            + "MIIFMjCCAxqgAwIBAgIJAL0mG5fOeJ7xMA0GCSqGSIb3DQEBDQUAMC0xCzAJBgNV\n"
+            + "BAYTAkdCMQ8wDQYDVQQHDAZMb25kb24xDTALBgNVBAoMBFRlc3QwIBcNMTgwOTE3\n"
+            + "MTIxNzU3WhgPMjExODA4MjQxMjE3NTdaMC0xCzAJBgNVBAYTAkdCMQ8wDQYDVQQH\n"
+            + "DAZMb25kb24xDTALBgNVBAoMBFRlc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\n"
+            + "ggIKAoICAQDCMhBrRAGGw+n2GdctBr/cEK4FZA6ajiHjihgpCHoSBdyL4R2jGKLS\n"
+            + "g0WgaMXa1HpkKN7LcIySosEBPlmcRkr1RqbEvQStOSvoFCXYvtx3alM6HTbXMcDR\n"
+            + "mqoKoABP6LXsPSoMWIgqMtP2X9EOppzHVIK1yFYFfbIlvYUV2Ka+MuMe0Vh5wvD1\n"
+            + "4GanPb+cWSKgdRSVQovCCMY3yWtZKVEaxRpCsk/mYYIFWz0tcgMjIKwDx1XXgiAV\n"
+            + "nU6NK43xbaw3XhtnaD/pv9lhTTbNrlcln9LjTD097BaK4R+1AEPHnpfxA9Ui3upn\n"
+            + "kbsNUdGdOB0ksZi/vd7lh833YgquQUIAhYrbfvq/HFCpVV1gljzlS3sqULYpLE//\n"
+            + "i3OsuL2mE+CYIJGpIi2GeJJWXciNMTJDOqTn+fRDtVb4RPp4Y70DJirp7XzaBi3q\n"
+            + "H0edANCzPSRCDbZsOhzIXhXshldiXVRX666DDlbMQgLTEnNKrkwv6DmU8o15XQsb\n"
+            + "8k1Os2YwXmkEOxUQ7AJZXVTZSf6UK9Znmdq1ZrHjybMfRUkHVxJcnKvrxfryralv\n"
+            + "gzfvu+D6HuxrCo3Ojqa+nDgIbxKEBtdrcsMhq1jWPFhjwo1fSadAkKOfdCAuXJRD\n"
+            + "THg3b4Sf+W7Cpc570YHrIpBf7WFl2XsPcEM0mJZ5+yATASCubNozQwIDAQABo1Mw\n"
+            + "UTAdBgNVHQ4EFgQUES0hupZSqY21JOba10QyZuxm91EwHwYDVR0jBBgwFoAUES0h\n"
+            + "upZSqY21JOba10QyZuxm91EwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsF\n"
+            + "AAOCAgEABTN5S30ng/RMpBweDm2N561PdpaCdiRXtAFCRVWR2mkDYC/Xj9Vqe6be\n"
+            + "PyM7L/5OKYVjzF1yJu67z/dx+ja5o+41g17jdqla7hyPx+9B4uRyDh+1KJTa+duj\n"
+            + "mw/aA1LCr6O6W4WizDOsChJ6FaB2Y1+GlFnKWb5nUdhVJqXQE1WOX9dZnw8Y4Npd\n"
+            + "VmAsjWot0BZorJrt3fwfcv3QfA896twkbo7Llv/8qzg4sXZXZ4ZtgAOqnPngiSn+\n"
+            + "JT/vYCXZ406VvAFpFqMcVz2dO/VGuL8lGIMHRKNyafrsV81EzH1W/XmRWOgvgj6r\n"
+            + "yQI63ln/AMY72HQ97xLkE1xKunGz6bK5Ug5+O43Uftc4Mb6MUgzo+ZqEQ3Ob+cAV\n"
+            + "cvjmtwDaPO/O39O5Xq0tLTlkn2/cKf4OQ6S++GDxzyRVHh5JXgP4j9+jfZY57Woy\n"
+            + "R1bE7N50JjY4cDermBJKdlBIjL7UPhqmLyaG7V0hBitFlgGBUCcJtJOV0xYd5aF3\n"
+            + "pxNkvMXhBmh95fjxJ0cJjpO7tN1RAwtMMNgsl7OUbuVRQCHOPW5DgP5qY21jDeRn\n"
+            + "BY82382l+9QzykmJLI5MZnmj4BA9uIDCwMtoTTvP++SsvhUAbuvh7MOOUQL0EY4m\n"
+            + "KStYq7X9PKseN+PvmfeoffIKc5R/Ha39oi7cGMVHCr8aiEhsf94=\n"
+            + "-----END CERTIFICATE-----\n";
+
+    /**
+     * This cert has an EC key with curve prime256v1 encoded using explicit params.
+     */
+    private static final String EC_EXPLICIT_KEY_CERT =
+            "-----BEGIN CERTIFICATE-----\n"
+            + "MIICAjCCAagCCQCrIzClvU58azAKBggqhkjOPQQDAjAPMQ0wCwYDVQQDDARUZXN0\n"
+            + "MB4XDTE4MTAwMjEyNDQzMloXDTE4MTEwMTEyNDQzMlowDzENMAsGA1UEAwwEVGVz\n"
+            + "dDCCAUswggEDBgcqhkjOPQIBMIH3AgEBMCwGByqGSM49AQECIQD/////AAAAAQAA\n"
+            + "AAAAAAAAAAAAAP///////////////zBbBCD/////AAAAAQAAAAAAAAAAAAAAAP//\n"
+            + "/////////////AQgWsY12Ko6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsDFQDE\n"
+            + "nTYIhucEk2pmeOETnSa3gZ9+kARBBGsX0fLhLEJH+Lzm5WOkQPJ3A32BLeszoPSh\n"
+            + "OUXYmMKWT+NC4v4af5uO5+tKfA+eFivOM1drMV7Oy7ZAaDe/UfUCIQD/////AAAA\n"
+            + "AP//////////vOb6racXnoTzucrC/GMlUQIBAQNCAAQXU+GFdLabcY/RvzoNjLhC\n"
+            + "6uN1Yt1baN2NYyKYEhwR9nb8nLa/m7f30OOi/8OrxQhnUl5qW0I0IbHflGnsqQ6s\n"
+            + "MAoGCCqGSM49BAMCA0gAMEUCIQDRXoZwmnsIJfg4mTemkM+heMS1iXRYUO0Dar5u\n"
+            + "Qhy0YgIgYWr0qSCLqxUQv3oQHMUpSmfHtP0Pwvb3DbbH6lY7TkI=\n"
+            + "-----END CERTIFICATE-----\n";
+
+    // See issue #539.
+    @Test
+    public void testMismatchedAlgorithm() throws Exception {
+        ServiceTester.test("CertificateFactory")
+                .withAlgorithm("X509")
+                .run(new ServiceTester.Test() {
+                    @Override
+                    public void test(Provider p, String algorithm) throws Exception {
+                        CertificateFactory cf = CertificateFactory.getInstance("X509", p);
+                        try {
+                            Certificate c = cf.generateCertificate(
+                                    new ByteArrayInputStream(MISMATCHED_ALGORITHM_CERT.getBytes(
+                                            Charset.forName("US-ASCII"))));
+                            c.verify(c.getPublicKey());
+                            fail();
+                        } catch (CertificateException expected) {
+                        }
+                    }
+                });
+    }
+
+    /**
+     * Confirm that explicit EC params aren't accepted in certificates.
+     */
+    @Test
+    public void testExplicitEcParams() throws Exception {
+        ServiceTester.test("CertificateFactory")
+                .withAlgorithm("X509")
+                // Bouncy Castle allows explicit EC params in certificates, even though they're
+                // barred by RFC 5480
+                .skipProvider("BC")
+                .run(new ServiceTester.Test() {
+                    @Override
+                    public void test(Provider p, String algorithm) throws Exception {
+                        try {
+                            CertificateFactory cf = CertificateFactory.getInstance("X509", p);
+                            Certificate c = cf.generateCertificate(new ByteArrayInputStream(
+                                    EC_EXPLICIT_KEY_CERT.getBytes(Charset.forName("US-ASCII"))));
+                            c.verify(c.getPublicKey());
+                            fail();
+                        } catch (InvalidKeyException expected) {
+                            // TODO: Should we throw CertificateParsingException at parse time
+                            // instead of waiting for when the user accesses the key?
+                        } catch (CertificateParsingException expected) {
+                        }
+                    }
+                });
+    }
+
+    @Test
+    public void testSigAlgName() throws Exception {
+        ServiceTester.test("CertificateFactory")
+                .withAlgorithm("X509")
+                .run(new ServiceTester.Test() {
+                    @Override
+                    public void test(Provider p, String algorithm) throws Exception {
+                        CertificateFactory cf = CertificateFactory.getInstance("X509", p);
+                        Certificate c = cf.generateCertificate(new ByteArrayInputStream(
+                                VALID_CERT.getBytes(Charset.forName("US-ASCII"))));
+                        assertEquals("SHA256WITHRSA",
+                                ((X509Certificate) c).getSigAlgName().toUpperCase());
+                    }
+                });
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/crypto/AeadCipherTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/crypto/AeadCipherTest.java
new file mode 100644
index 0000000..fd70f86
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/crypto/AeadCipherTest.java
@@ -0,0 +1,330 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.conscrypt.javax.crypto;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import java.nio.ByteBuffer;
+import java.security.Key;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Arrays;
+import javax.crypto.Cipher;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import com.android.org.conscrypt.TestUtils;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(Parameterized.class)
+public class AeadCipherTest {
+
+  @BeforeClass
+  public static void setUp() {
+    TestUtils.assumeAllowsUnsignedCrypto();
+  }
+
+  @Parameterized.Parameters(name = "{0}")
+  public static Iterable<String> ciphers() {
+    return Arrays.asList(
+      "AES/GCM/NoPadding",
+      "AES/GCM-SIV/NoPadding",
+      "ChaCha20/Poly1305/NoPadding");
+  }
+
+  private final String cipher;
+  private byte counter;
+
+  public AeadCipherTest(String cipher) {
+    this.cipher = cipher;
+  }
+
+  private Key newKey() {
+    if (cipher.startsWith("AES/")) {
+      byte[] keyData = new byte[16];
+      keyData[0] = counter++;
+      return new SecretKeySpec(keyData, "AES");
+    } else if (cipher.startsWith("ChaCha20/")) {
+      byte[] keyData = new byte[32];
+      keyData[0] = counter++;
+      return new SecretKeySpec(keyData, "ChaCha20");
+    } else {
+      throw new IllegalStateException("Couldn't generate key for " + cipher);
+    }
+  }
+
+  private AlgorithmParameterSpec newParamSpec() {
+    if (cipher.startsWith("AES/GCM")) {
+      byte[] nonce = new byte[12];
+      nonce[0] = counter++;
+      return new GCMParameterSpec(128, nonce);
+    } else if (cipher.startsWith("ChaCha20/")) {
+      byte[] nonce = new byte[12];
+      nonce[0] = counter++;
+      return new IvParameterSpec(nonce);
+    } else {
+      throw new IllegalStateException("Couldn't generate algorithm parameter spec for " + cipher);
+    }
+  }
+
+  @Test
+  public void testUpdateAAD_AfterInit() throws Exception {
+    Cipher c = Cipher.getInstance(cipher);
+    c.init(Cipher.ENCRYPT_MODE, newKey());
+    c.updateAAD(new byte[8]);
+    c.updateAAD(ByteBuffer.wrap(new byte[8]));
+  }
+
+  @Test
+  public void testUpdateAAD_AfterUpdate() throws Exception {
+    Cipher c = Cipher.getInstance(cipher);
+    c.init(Cipher.ENCRYPT_MODE, newKey());
+    c.updateAAD(new byte[8]);
+    c.update(new byte[8]);
+    c.updateAAD(ByteBuffer.wrap(new byte[8]));
+  }
+
+  /*
+   * Check that two AAD updates are equivalent to one.
+   * http://b/27371173
+   */
+  @Test
+  public void testUpdateAAD_Twice() throws Exception {
+    Key key = newKey();
+    AlgorithmParameterSpec spec = newParamSpec();
+    Cipher c1 = Cipher.getInstance(cipher);
+    Cipher c2 = Cipher.getInstance(cipher);
+
+    c1.init(Cipher.ENCRYPT_MODE, key, spec);
+    c1.updateAAD(new byte[] {
+        0x01, 0x02, 0x03, 0x04, 0x05,
+    });
+    c1.updateAAD(new byte[] {
+        0x06, 0x07, 0x08, 0x09, 0x10,
+    });
+
+    c2.init(Cipher.ENCRYPT_MODE, key, spec);
+    c2.updateAAD(new byte[] {
+        0x01, 0x02, 0x03, 0x04, 0x05,
+        0x06, 0x07, 0x08, 0x09, 0x10,
+    });
+
+    assertEquals(Arrays.toString(c1.doFinal()), Arrays.toString(c2.doFinal()));
+  }
+
+  @Test
+  public void testUpdateAAD_ByteBuffer() throws Exception {
+    Key key = newKey();
+    AlgorithmParameterSpec spec = newParamSpec();
+    Cipher c1 = Cipher.getInstance(cipher);
+    Cipher c2 = Cipher.getInstance(cipher);
+    Cipher c3 = Cipher.getInstance(cipher);
+
+    c1.init(Cipher.ENCRYPT_MODE, key, spec);
+    c1.updateAAD(new byte[] {
+        0x01, 0x02, 0x03, 0x04, 0x05,
+        0x06, 0x07, 0x08, 0x09, 0x10,
+    });
+
+    c2.init(Cipher.ENCRYPT_MODE, key, spec);
+    c2.updateAAD(ByteBuffer.wrap(new byte[] {
+        0x01, 0x02, 0x03, 0x04, 0x05,
+        0x06, 0x07, 0x08, 0x09, 0x10,
+    }));
+
+    c3.init(Cipher.ENCRYPT_MODE, key, spec);
+    ByteBuffer buf = ByteBuffer.allocateDirect(10);
+    buf.put(new byte[] {
+        0x01, 0x02, 0x03, 0x04, 0x05,
+        0x06, 0x07, 0x08, 0x09, 0x10,
+    });
+    buf.flip();
+    c3.updateAAD(buf);
+
+    byte[] c1Final = c1.doFinal();
+    byte[] c2Final = c2.doFinal();
+    byte[] c3Final = c3.doFinal();
+    assertEquals(Arrays.toString(c1Final), Arrays.toString(c2Final));
+    assertEquals(Arrays.toString(c1Final), Arrays.toString(c3Final));
+  }
+
+  @Test
+  public void testUpdateAAD_ByteBuffer_MultipleUpdates() throws Exception {
+    Key key = newKey();
+    AlgorithmParameterSpec spec = newParamSpec();
+    Cipher c1 = Cipher.getInstance(cipher);
+    Cipher c2 = Cipher.getInstance(cipher);
+    Cipher c3 = Cipher.getInstance(cipher);
+
+    c1.init(Cipher.ENCRYPT_MODE, key, spec);
+    c1.updateAAD(new byte[] {
+        0x01, 0x02, 0x03, 0x04, 0x05,
+    });
+    c1.updateAAD(new byte[] {
+        0x06, 0x07, 0x08, 0x09, 0x10,
+    });
+
+    c2.init(Cipher.ENCRYPT_MODE, key, spec);
+    c2.updateAAD(ByteBuffer.wrap(new byte[] {
+        0x01, 0x02, 0x03, 0x04, 0x05,
+    }));
+    c2.updateAAD(ByteBuffer.wrap(new byte[] {
+        0x06, 0x07, 0x08, 0x09, 0x10,
+    }));
+
+    c3.init(Cipher.ENCRYPT_MODE, key, spec);
+    ByteBuffer buf = ByteBuffer.allocateDirect(10);
+    buf.put(new byte[] {
+        0x01, 0x02, 0x03, 0x04, 0x05,
+        0x06, 0x07, 0x08, 0x09, 0x10,
+    });
+    buf.flip();
+    buf.limit(5);
+    c3.updateAAD(buf);
+    buf.limit(10);
+    c3.updateAAD(buf);
+
+    byte[] c1Final = c1.doFinal();
+    byte[] c2Final = c2.doFinal();
+    byte[] c3Final = c3.doFinal();
+    assertEquals(Arrays.toString(c1Final), Arrays.toString(c2Final));
+    assertEquals(Arrays.toString(c1Final), Arrays.toString(c3Final));
+  }
+
+  @Test
+  public void testUpdateAAD_ByteBuffer_MixedCalls() throws Exception {
+    Key key = newKey();
+    AlgorithmParameterSpec spec = newParamSpec();
+    Cipher c1 = Cipher.getInstance(cipher);
+    Cipher c2 = Cipher.getInstance(cipher);
+    Cipher c3 = Cipher.getInstance(cipher);
+
+    c1.init(Cipher.ENCRYPT_MODE, key, spec);
+    c1.updateAAD(new byte[] {
+        0x01, 0x02, 0x03, 0x04, 0x05,
+        0x06, 0x07, 0x08, 0x09, 0x10,
+    });
+
+    c2.init(Cipher.ENCRYPT_MODE, key, spec);
+    c2.updateAAD(new byte[] {
+        0x01, 0x02, 0x03, 0x04, 0x05,
+    });
+    c2.updateAAD(ByteBuffer.wrap(new byte[] {
+        0x06, 0x07, 0x08, 0x09, 0x10,
+    }));
+
+    c3.init(Cipher.ENCRYPT_MODE, key, spec);
+    ByteBuffer buf = ByteBuffer.allocateDirect(10);
+    buf.put(new byte[] {
+        0x01, 0x02, 0x03, 0x04, 0x05,
+        0x06, 0x07, 0x08, 0x09, 0x10,
+    });
+    buf.flip();
+    buf.limit(5);
+    c3.updateAAD(buf);
+    c3.updateAAD(new byte[] {
+        0x06, 0x07, 0x08, 0x09, 0x10,
+    });
+
+    byte[] c1Final = c1.doFinal();
+    byte[] c2Final = c2.doFinal();
+    byte[] c3Final = c3.doFinal();
+    assertEquals(Arrays.toString(c1Final), Arrays.toString(c2Final));
+    assertEquals(Arrays.toString(c1Final), Arrays.toString(c3Final));
+  }
+
+  @Test
+  public void testUpdateAAD_ByteBuffer_Unequal() throws Exception {
+    Key key = newKey();
+    AlgorithmParameterSpec spec = newParamSpec();
+    Cipher c1 = Cipher.getInstance(cipher);
+    Cipher c2 = Cipher.getInstance(cipher);
+    Cipher c3 = Cipher.getInstance(cipher);
+
+    c1.init(Cipher.ENCRYPT_MODE, key, spec);
+    c1.updateAAD(ByteBuffer.wrap(new byte[] {
+        0x01, 0x02, 0x03, 0x04, 0x05,
+    }));
+
+    c2.init(Cipher.ENCRYPT_MODE, key, spec);
+    c2.updateAAD(new byte[] {
+        0x06, 0x07, 0x08, 0x09, 0x10,
+    });
+
+    c3.init(Cipher.ENCRYPT_MODE, key, spec);
+    ByteBuffer buf = ByteBuffer.allocateDirect(10);
+    buf.put(new byte[] {
+        0x11, 0x12, 0x13, 0x14, 0x15,
+    });
+    buf.flip();
+    c3.updateAAD(buf);
+
+    byte[] c1Final = c1.doFinal();
+    byte[] c2Final = c2.doFinal();
+    byte[] c3Final = c3.doFinal();
+    assertFalse(Arrays.equals(c1Final, c2Final));
+    assertFalse(Arrays.equals(c2Final, c3Final));
+    assertFalse(Arrays.equals(c1Final, c3Final));
+  }
+
+  /*
+   * Check that encryption with old and new instances update correctly.
+   * http://b/27324690
+   */
+  @Test
+  public void testReuse() throws Exception {
+    Key key = newKey();
+    Key key2 = newKey();
+    AlgorithmParameterSpec spec = newParamSpec();
+    Cipher c1 = Cipher.getInstance(cipher);
+    Cipher c2 = Cipher.getInstance(cipher);
+
+    // Pollute the c1 cipher with AAD
+    c1.init(Cipher.ENCRYPT_MODE, key, spec);
+    c1.updateAAD(new byte[] {
+        0x01, 0x02, 0x03, 0x04, 0x05,
+    });
+
+    // Now init each again and make sure the outputs are the same.  We have to use a
+    // different key because reiniting an AEAD cipher with the same key and IV should fail.
+    c1.init(Cipher.ENCRYPT_MODE, key2, spec);
+    c2.init(Cipher.ENCRYPT_MODE, key2, spec);
+
+    byte[] aad = new byte[] {
+        0x10, 0x20, 0x30, 0x40, 0x50, 0x60,
+    };
+    c1.updateAAD(aad);
+    c2.updateAAD(aad);
+
+    assertEquals(Arrays.toString(c1.doFinal()), Arrays.toString(c2.doFinal()));
+
+    // .doFinal should also not allow reuse without re-initialization
+    byte[] aad2 = new byte[] {
+        0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
+    };
+    try {
+      c1.updateAAD(aad2);
+      fail("Should not allow updateAAD without re-initialization");
+    } catch (IllegalStateException expected) {
+    }
+
+    try {
+      c1.update(new byte[8]);
+      fail("Should not allow update without re-initialization");
+    } catch (IllegalStateException expected) {
+    }
+
+    try {
+      c1.doFinal();
+      fail("Should not allow doFinal without re-initialization");
+    } catch (IllegalStateException expected) {
+    }
+  }
+
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/crypto/CipherBasicsTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/crypto/CipherBasicsTest.java
new file mode 100644
index 0000000..0ae78f4
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/crypto/CipherBasicsTest.java
@@ -0,0 +1,280 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.crypto;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.Security;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.crypto.Cipher;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import com.android.org.conscrypt.TestUtils;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for basic compliance for ciphers.  This test uses reference vectors produced by
+ * standards bodies and confirms that all implementations produce the correct answer
+ * for the given inputs.
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public final class CipherBasicsTest {
+
+    private static final Map<String, String> BASIC_CIPHER_TO_TEST_DATA = new HashMap<String, String>();
+    static {
+        BASIC_CIPHER_TO_TEST_DATA.put("AES/ECB/NoPadding", "/crypto/aes-ecb.csv");
+        BASIC_CIPHER_TO_TEST_DATA.put("AES/CBC/NoPadding", "/crypto/aes-cbc.csv");
+        BASIC_CIPHER_TO_TEST_DATA.put("AES/CFB8/NoPadding", "/crypto/aes-cfb8.csv");
+        BASIC_CIPHER_TO_TEST_DATA.put("AES/CFB128/NoPadding", "/crypto/aes-cfb128.csv");
+        BASIC_CIPHER_TO_TEST_DATA.put("AES/OFB/NoPadding", "/crypto/aes-ofb.csv");
+        BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/ECB/NoPadding", "/crypto/desede-ecb.csv");
+        BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/CBC/NoPadding", "/crypto/desede-cbc.csv");
+        BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/CFB8/NoPadding", "/crypto/desede-cfb8.csv");
+        BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/CFB64/NoPadding", "/crypto/desede-cfb64.csv");
+        BASIC_CIPHER_TO_TEST_DATA.put("DESEDE/OFB/NoPadding", "/crypto/desede-ofb.csv");
+        BASIC_CIPHER_TO_TEST_DATA.put("ChaCha20", "/crypto/chacha20.csv");
+    }
+
+    private static final Map<String, String> AEAD_CIPHER_TO_TEST_DATA = new HashMap<String, String>();
+    static {
+        AEAD_CIPHER_TO_TEST_DATA.put("AES/GCM/NoPadding", "/crypto/aes-gcm.csv");
+        AEAD_CIPHER_TO_TEST_DATA.put("AES/GCM-SIV/NoPadding", "/crypto/aes-gcm-siv.csv");
+        AEAD_CIPHER_TO_TEST_DATA.put("ChaCha20/Poly1305/NoPadding", "/crypto/chacha20-poly1305.csv");
+    }
+
+    private static final int KEY_INDEX = 0;
+    private static final int IV_INDEX = 1;
+    private static final int PLAINTEXT_INDEX = 2;
+    private static final int CIPHERTEXT_INDEX = 3;
+    private static final int TAG_INDEX = 4;
+    private static final int AAD_INDEX = 5;
+
+    @BeforeClass
+    public static void setUp() {
+        TestUtils.assumeAllowsUnsignedCrypto();
+    }
+
+    @Test
+    public void testBasicEncryption() throws Exception {
+        for (Provider p : Security.getProviders()) {
+            for (Map.Entry<String, String> entry : BASIC_CIPHER_TO_TEST_DATA.entrySet()) {
+                String transformation = entry.getKey();
+
+                // In OpenJDK 6, the SunPKCS11-NSS implementation of AES/ECB/NoPadding thinks
+                // that it's AES/CTR/NoPadding during init() for some reason, which causes it
+                // to throw an exception due to a lack of IV (required for CTR, prohibited for ECB).
+                // We don't strongly care about checking this implementation, so just skip it.
+                if (p.getName().equals("SunPKCS11-NSS")
+                        && transformation.equals("AES/ECB/NoPadding")) {
+                    continue;
+                }
+
+                // The SunJCE implementation of ChaCha20 only supports initializing with
+                // ChaCha20ParameterSpec, introduced in Java 11.  For now, just skip testing it.
+                if (transformation.equals("ChaCha20") && p.getName().equals("SunJCE")) {
+                    continue;
+                }
+
+                Cipher cipher;
+                try {
+                    cipher = Cipher.getInstance(transformation, p);
+                } catch (NoSuchAlgorithmException e) {
+                    // This provider doesn't provide this algorithm, ignore it
+                    continue;
+                }
+
+                List<String[]> data = readCsvResource(entry.getValue());
+                for (String[] line : data) {
+                    Key key = new SecretKeySpec(toBytes(line[KEY_INDEX]),
+                            getBaseAlgorithm(transformation));
+                    byte[] iv = toBytes(line[IV_INDEX]);
+                    byte[] plaintext = toBytes(line[PLAINTEXT_INDEX]);
+                    byte[] ciphertext = toBytes(line[CIPHERTEXT_INDEX]);
+
+                    // Initialize the IV, if there is one
+                    AlgorithmParameters params;
+                    if (iv.length > 0) {
+                        params = AlgorithmParameters.getInstance(getBaseAlgorithm(transformation));
+                        params.init(iv, "RAW");
+                    } else {
+                        params = null;
+                    }
+
+                    try {
+                        cipher.init(Cipher.ENCRYPT_MODE, key, params);
+                        assertEquals("Provider " + p.getName()
+                                        + ", algorithm " + transformation
+                                        + " reported the wrong output size",
+                                ciphertext.length, cipher.getOutputSize(plaintext.length));
+                        assertTrue("Provider " + p.getName()
+                                        + ", algorithm " + transformation
+                                        + " failed on encryption, data is " + Arrays.toString(line),
+                                Arrays.equals(ciphertext, cipher.doFinal(plaintext)));
+
+                        cipher.init(Cipher.DECRYPT_MODE, key, params);
+                        assertEquals("Provider " + p.getName()
+                                        + ", algorithm " + transformation
+                                        + " reported the wrong output size",
+                                plaintext.length, cipher.getOutputSize(ciphertext.length));
+                        assertTrue("Provider " + p.getName()
+                                        + ", algorithm " + transformation
+                                        + " failed on decryption, data is " + Arrays.toString(line),
+                                Arrays.equals(plaintext, cipher.doFinal(ciphertext)));
+                    } catch (InvalidKeyException e) {
+                        // Some providers may not support raw SecretKeySpec keys, that's allowed
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testAeadEncryption() throws Exception {
+        TestUtils.assumeAEADAvailable();
+        for (Provider p : Security.getProviders()) {
+            for (Map.Entry<String, String> entry : AEAD_CIPHER_TO_TEST_DATA.entrySet()) {
+                String transformation = entry.getKey();
+
+                Cipher cipher;
+                try {
+                    cipher = Cipher.getInstance(transformation, p);
+                } catch (NoSuchAlgorithmException e) {
+                    // This provider doesn't provide this algorithm, ignore it
+                    continue;
+                }
+
+                List<String[]> data = readCsvResource(entry.getValue());
+                for (String[] line : data) {
+                    Key key = new SecretKeySpec(toBytes(line[KEY_INDEX]),
+                            getBaseAlgorithm(transformation));
+                    byte[] iv = toBytes(line[IV_INDEX]);
+                    byte[] plaintext = toBytes(line[PLAINTEXT_INDEX]);
+                    byte[] ciphertext = toBytes(line[CIPHERTEXT_INDEX]);
+                    byte[] tag = toBytes(line[TAG_INDEX]);
+                    byte[] aad = toBytes(line[AAD_INDEX]);
+
+                    // Some ChaCha20 tests include truncated tags, which the Java API doesn't
+                    // support.  Skip those tests.
+                    if (transformation.startsWith("ChaCha20") && tag.length < 16) {
+                        continue;
+                    }
+
+                    AlgorithmParameterSpec params;
+                    if (transformation.contains("GCM")) {
+                        params = new GCMParameterSpec(8 * tag.length, iv);
+                    } else {
+                        params = new IvParameterSpec(iv);
+                    }
+
+                    try {
+                        cipher.init(Cipher.ENCRYPT_MODE, key, params);
+                        if (aad.length > 0) {
+                            cipher.updateAAD(aad);
+                        }
+                        byte[] combinedOutput = new byte[ciphertext.length + tag.length];
+                        assertEquals("Provider " + p.getName()
+                                        + ", algorithm " + transformation
+                                        + " reported the wrong output size",
+                                combinedOutput.length, cipher.getOutputSize(plaintext.length));
+                        System.arraycopy(ciphertext, 0, combinedOutput, 0, ciphertext.length);
+                        System.arraycopy(tag, 0, combinedOutput, ciphertext.length, tag.length);
+                        assertTrue("Provider " + p.getName()
+                                        + ", algorithm " + transformation
+                                        + " failed on encryption, data is " + Arrays.toString(line),
+                                Arrays.equals(combinedOutput, cipher.doFinal(plaintext)));
+
+                        cipher.init(Cipher.DECRYPT_MODE, key, params);
+                        if (aad.length > 0) {
+                            cipher.updateAAD(aad);
+                        }
+                        assertEquals("Provider " + p.getName()
+                                        + ", algorithm " + transformation
+                                        + " reported the wrong output size",
+                                plaintext.length, cipher.getOutputSize(combinedOutput.length));
+                        assertTrue("Provider " + p.getName()
+                                        + ", algorithm " + transformation
+                                        + " failed on decryption, data is " + Arrays.toString(line),
+                                Arrays.equals(plaintext, cipher.doFinal(combinedOutput)));
+                    } catch (InvalidKeyException e) {
+                        // Some providers may not support raw SecretKeySpec keys, that's allowed
+                    } catch (InvalidAlgorithmParameterException e) {
+                        // Some providers may not support all tag lengths or nonce lengths,
+                        // that's allowed
+                    }
+                }
+            }
+        }
+    }
+
+    private static List<String[]> readCsvResource(String resourceName) throws IOException {
+        InputStream stream = CipherBasicsTest.class.getResourceAsStream(resourceName);
+        List<String[]> lines = new ArrayList<String[]>();
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
+            String line;
+            while ((line = reader.readLine()) != null) {
+                if (line.isEmpty() || line.startsWith("#")) {
+                    continue;
+                }
+                lines.add(line.split(",", -1));
+            }
+        } finally {
+            if (reader != null) {
+                reader.close();
+            }
+        }
+        return lines;
+    }
+
+    /**
+     * Returns the underlying cipher name given a cipher transformation.  For example,
+     * passing {@code AES/CBC/NoPadding} returns {@code AES}.
+     */
+    private static String getBaseAlgorithm(String transformation) {
+        if (transformation.contains("/")) {
+            return transformation.substring(0, transformation.indexOf('/'));
+        }
+        return transformation;
+    }
+
+    private static byte[] toBytes(String hex) {
+        return TestUtils.decodeHex(hex, /* allowSingleChar= */ true);
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/crypto/CipherTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/crypto/CipherTest.java
new file mode 100644
index 0000000..0e0ab48
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/crypto/CipherTest.java
@@ -0,0 +1,4754 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.crypto;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.MGF1ParameterSpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import javax.crypto.AEADBadTagException;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+import javax.crypto.spec.PSource;
+import javax.crypto.spec.SecretKeySpec;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import com.android.org.conscrypt.Conscrypt;
+import com.android.org.conscrypt.TestUtils;
+import com.android.org.conscrypt.java.security.StandardNames;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import org.junit.AfterClass;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import dalvik.system.VMRuntime;
+import sun.security.jca.Providers;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public final class CipherTest {
+
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
+    @BeforeClass
+    public static void setUp() {
+        TestUtils.assumeAllowsUnsignedCrypto();
+    }
+
+    /** GCM tag size used for tests. */
+    private static final int GCM_TAG_SIZE_BITS = 96;
+    private static final int GCM_SIV_TAG_SIZE_BITS = 128;
+
+    private static final String[] RSA_PROVIDERS = StandardNames.IS_RI
+            ? new String[] {"SunJCE", StandardNames.JSSE_PROVIDER_NAME}
+            : new String[] {"BC", StandardNames.JSSE_PROVIDER_NAME};
+
+    private static final String[] AES_PROVIDERS = StandardNames.IS_RI
+            ? new String[] {"SunJCE", StandardNames.JSSE_PROVIDER_NAME}
+            : new String[] {"BC", StandardNames.JSSE_PROVIDER_NAME};
+
+    private static boolean isSupported(String algorithm, String provider) {
+        if (algorithm.equals("RC2")) {
+            return false;
+        }
+        if (algorithm.equals("PBEWITHMD5ANDRC2")) {
+            return false;
+        }
+        if (algorithm.startsWith("PBEWITHSHA1ANDRC2")) {
+            return false;
+        }
+        if (algorithm.equals("PBEWITHSHAAND40BITRC2-CBC")) {
+            return false;
+        }
+        if (algorithm.equals("PBEWITHSHAAND128BITRC2-CBC")) {
+            return false;
+        }
+        if (algorithm.equals("PBEWITHSHAANDTWOFISH-CBC")) {
+            return false;
+        }
+        if (!IS_UNLIMITED) {
+            if (algorithm.equals("PBEWITHMD5ANDTRIPLEDES")) {
+                return false;
+            }
+        }
+        // stream modes CFB, CTR, CTS, OFB with PKCS5Padding or PKCS7Padding don't really make sense
+        if (!provider.equals("AndroidOpenSSL") &&
+            (algorithm.equals("AES/CFB/PKCS5PADDING")
+             || algorithm.equals("AES/CFB/PKCS7PADDING")
+             || algorithm.equals("AES/CTR/PKCS5PADDING")
+             || algorithm.equals("AES/CTR/PKCS7PADDING")
+             || algorithm.equals("AES/CTS/PKCS5PADDING")
+             || algorithm.equals("AES/CTS/PKCS7PADDING")
+             || algorithm.equals("AES/OFB/PKCS5PADDING")
+             || algorithm.equals("AES/OFB/PKCS7PADDING"))) {
+            return false;
+        }
+        return true;
+    }
+
+    private static boolean isSupportedForWrapping(String algorithm) {
+        if (isOnlyWrappingAlgorithm(algorithm)) {
+            return true;
+        }
+        // http://b/9097343 RSA with NoPadding won't work since
+        // leading zeroes in the underlying key material are lost.
+        if (algorithm.equals("RSA/ECB/NOPADDING")) {
+            return false;
+        }
+        // AESWRAP should be used instead, fails with BC and SunJCE otherwise.
+        if (algorithm.startsWith("AES") || algorithm.startsWith("DESEDE")) {
+            return false;
+        }
+        return true;
+    }
+
+    private synchronized static int getEncryptMode(String algorithm) throws Exception {
+        if (isOnlyWrappingAlgorithm(algorithm)) {
+            return Cipher.WRAP_MODE;
+        }
+        return Cipher.ENCRYPT_MODE;
+    }
+
+    private synchronized static int getDecryptMode(String algorithm) throws Exception {
+        if (isOnlyWrappingAlgorithm(algorithm)) {
+            return Cipher.UNWRAP_MODE;
+        }
+        return Cipher.DECRYPT_MODE;
+    }
+
+    private static String getBaseAlgorithm(String algorithm) {
+        if (algorithm.equals("AESWRAP")) {
+            return "AES";
+        }
+        if (algorithm.startsWith("AES/")) {
+            return "AES";
+        }
+        if (algorithm.startsWith("AES_128/") || algorithm.startsWith("AES_256/")) {
+            return "AES";
+        }
+        if (algorithm.equals("GCM")) {
+            return "AES";
+        }
+        if (algorithm.startsWith("CHACHA20/")) {
+            return "CHACHA20";
+        }
+        if (algorithm.startsWith("DESEDE/")) {
+            return "DESEDE";
+        }
+        if (algorithm.equals("PBEWITHMD5AND128BITAES-CBC-OPENSSL")) {
+            return "AES";
+        }
+        if (algorithm.equals("PBEWITHMD5AND192BITAES-CBC-OPENSSL")) {
+            return "AES";
+        }
+        if (algorithm.equals("PBEWITHMD5AND256BITAES-CBC-OPENSSL")) {
+            return "AES";
+        }
+        if (algorithm.equals("PBEWITHSHA256AND128BITAES-CBC-BC")) {
+            return "AES";
+        }
+        if (algorithm.equals("PBEWITHSHA256AND192BITAES-CBC-BC")) {
+            return "AES";
+        }
+        if (algorithm.equals("PBEWITHSHA256AND256BITAES-CBC-BC")) {
+            return "AES";
+        }
+        if (algorithm.equals("PBEWITHSHAAND128BITAES-CBC-BC")) {
+            return "AES";
+        }
+        if (algorithm.equals("PBEWITHSHAAND192BITAES-CBC-BC")) {
+            return "AES";
+        }
+        if (algorithm.equals("PBEWITHSHAAND256BITAES-CBC-BC")) {
+            return "AES";
+        }
+        if (algorithm.equals("PBEWITHMD5ANDDES")) {
+            return "DES";
+        }
+        if (algorithm.equals("PBEWITHSHA1ANDDES")) {
+            return "DES";
+        }
+        if (algorithm.equals("DESEDEWRAP")) {
+            return "DESEDE";
+        }
+        if (algorithm.equals("PBEWITHSHAAND2-KEYTRIPLEDES-CBC")) {
+            return "DESEDE";
+        }
+        if (algorithm.equals("PBEWITHSHAAND3-KEYTRIPLEDES-CBC")) {
+            return "DESEDE";
+        }
+        if (algorithm.equals("PBEWITHMD5ANDTRIPLEDES")) {
+            return "DESEDE";
+        }
+        if (algorithm.equals("PBEWITHSHA1ANDDESEDE")) {
+            return "DESEDE";
+        }
+        if (algorithm.equals("RSA/ECB/NOPADDING")) {
+            return "RSA";
+        }
+        if (algorithm.equals("RSA/ECB/PKCS1PADDING")) {
+            return "RSA";
+        }
+        if (algorithm.equals("PBEWITHSHAAND40BITRC4")) {
+            return "ARC4";
+        }
+        if (algorithm.equals("PBEWITHSHAAND128BITRC4")) {
+            return "ARC4";
+        }
+        return algorithm;
+    }
+
+    private static boolean isAsymmetric(String algorithm) {
+        return getBaseAlgorithm(algorithm).equals("RSA");
+    }
+
+    private static boolean isOnlyWrappingAlgorithm(String algorithm) {
+        return algorithm.endsWith("WRAP");
+    }
+
+    private static boolean isPBE(String algorithm) {
+        return algorithm.startsWith("PBE");
+    }
+
+    private static boolean isAEAD(String algorithm) {
+        return "GCM".equals(algorithm) || algorithm.contains("/GCM/")
+                || algorithm.contains("/GCM-SIV/")
+                || algorithm.equals("CHACHA20/POLY1305/NOPADDING");
+    }
+
+    private static boolean isStreamMode(String algorithm) {
+        return algorithm.contains("/CTR/") || algorithm.contains("/OFB")
+                || algorithm.contains("/CFB");
+    }
+
+    private static boolean isRandomizedEncryption(String algorithm) {
+        return algorithm.endsWith("/PKCS1PADDING") || algorithm.endsWith("/OAEPPADDING")
+                || algorithm.contains("/OAEPWITH");
+    }
+
+    private static Map<String, Key> ENCRYPT_KEYS = new HashMap<String, Key>();
+
+    /**
+     * Returns the key meant for enciphering for {@code algorithm}.
+     */
+    private synchronized static Key getEncryptKey(String algorithm) {
+        Key key = ENCRYPT_KEYS.get(algorithm);
+        if (key != null) {
+            return key;
+        }
+        try {
+            if (algorithm.startsWith("RSA")) {
+                KeyFactory kf = KeyFactory.getInstance("RSA");
+                RSAPublicKeySpec keySpec = new RSAPublicKeySpec(RSA_2048_modulus,
+                        RSA_2048_publicExponent);
+                key = kf.generatePublic(keySpec);
+            } else if (isPBE(algorithm)) {
+                SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm);
+                key = skf.generateSecret(new PBEKeySpec("secret".toCharArray()));
+            } else {
+                KeyGenerator kg = KeyGenerator.getInstance(getBaseAlgorithm(algorithm));
+                if (algorithm.startsWith("AES_256/")) {
+                    // This is the 256-bit constrained version, so we have to switch from the
+                    // default of 128-bit keys.
+                    kg.init(256);
+                }
+                key = kg.generateKey();
+            }
+        } catch (Exception e) {
+            throw new AssertionError("Error generating keys for test setup", e);
+        }
+        ENCRYPT_KEYS.put(algorithm, key);
+        return key;
+    }
+
+    private static Map<String, Key> DECRYPT_KEYS = new HashMap<String, Key>();
+
+    /**
+     * Returns the key meant for deciphering for {@code algorithm}.
+     */
+    private synchronized static Key getDecryptKey(String algorithm) {
+        Key key = DECRYPT_KEYS.get(algorithm);
+        if (key != null) {
+            return key;
+        }
+        try {
+            if (algorithm.startsWith("RSA")) {
+                KeyFactory kf = KeyFactory.getInstance("RSA");
+                RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(RSA_2048_modulus,
+                        RSA_2048_publicExponent, RSA_2048_privateExponent, RSA_2048_primeP,
+                        RSA_2048_primeQ, RSA_2048_primeExponentP, RSA_2048_primeExponentQ,
+                        RSA_2048_crtCoefficient);
+                key = kf.generatePrivate(keySpec);
+            } else {
+                assertFalse(algorithm, isAsymmetric(algorithm));
+                key = getEncryptKey(algorithm);
+            }
+        } catch (Exception e) {
+            throw new AssertionError("Error generating keys for test setup", e);
+        }
+        DECRYPT_KEYS.put(algorithm, key);
+        return key;
+    }
+
+    private static Map<String, Integer> EXPECTED_BLOCK_SIZE = new HashMap<String, Integer>();
+    static {
+        setExpectedBlockSize("AES", 16);
+        setExpectedBlockSize("AES/CBC/PKCS5PADDING", 16);
+        setExpectedBlockSize("AES/CBC/PKCS7PADDING", 16);
+        setExpectedBlockSize("AES/CBC/NOPADDING", 16);
+        setExpectedBlockSize("AES/CFB/PKCS5PADDING", 16);
+        setExpectedBlockSize("AES/CFB/PKCS7PADDING", 16);
+        setExpectedBlockSize("AES/CFB/NOPADDING", 16);
+        setExpectedBlockSize("AES/CTR/PKCS5PADDING", 16);
+        setExpectedBlockSize("AES/CTR/PKCS7PADDING", 16);
+        setExpectedBlockSize("AES/CTR/NOPADDING", 16);
+        setExpectedBlockSize("AES/CTS/PKCS5PADDING", 16);
+        setExpectedBlockSize("AES/CTS/PKCS7PADDING", 16);
+        setExpectedBlockSize("AES/CTS/NOPADDING", 16);
+        setExpectedBlockSize("AES/ECB/PKCS5PADDING", 16);
+        setExpectedBlockSize("AES/ECB/PKCS7PADDING", 16);
+        setExpectedBlockSize("AES/ECB/NOPADDING", 16);
+        setExpectedBlockSize("AES/GCM/NOPADDING", 16);
+        setExpectedBlockSize("AES/GCM-SIV/NOPADDING", 16);
+        setExpectedBlockSize("AES/OFB/PKCS5PADDING", 16);
+        setExpectedBlockSize("AES/OFB/PKCS7PADDING", 16);
+        setExpectedBlockSize("AES/OFB/NOPADDING", 16);
+        setExpectedBlockSize("AES_128/CBC/PKCS5PADDING", 16);
+        setExpectedBlockSize("AES_128/CBC/PKCS7PADDING", 16);
+        setExpectedBlockSize("AES_128/CBC/NOPADDING", 16);
+        setExpectedBlockSize("AES_128/ECB/PKCS5PADDING", 16);
+        setExpectedBlockSize("AES_128/ECB/PKCS7PADDING", 16);
+        setExpectedBlockSize("AES_128/ECB/NOPADDING", 16);
+        setExpectedBlockSize("AES_128/GCM/NOPADDING", 16);
+        setExpectedBlockSize("AES_128/GCM-SIV/NOPADDING", 16);
+        setExpectedBlockSize("AES_256/CBC/PKCS5PADDING", 16);
+        setExpectedBlockSize("AES_256/CBC/PKCS7PADDING", 16);
+        setExpectedBlockSize("AES_256/CBC/NOPADDING", 16);
+        setExpectedBlockSize("AES_256/ECB/PKCS5PADDING", 16);
+        setExpectedBlockSize("AES_256/ECB/PKCS7PADDING", 16);
+        setExpectedBlockSize("AES_256/ECB/NOPADDING", 16);
+        setExpectedBlockSize("AES_256/GCM/NOPADDING", 16);
+        setExpectedBlockSize("AES_256/GCM-SIV/NOPADDING", 16);
+        setExpectedBlockSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16);
+        setExpectedBlockSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16);
+        setExpectedBlockSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16);
+        setExpectedBlockSize("PBEWITHSHA256AND128BITAES-CBC-BC", 16);
+        setExpectedBlockSize("PBEWITHSHA256AND192BITAES-CBC-BC", 16);
+        setExpectedBlockSize("PBEWITHSHA256AND256BITAES-CBC-BC", 16);
+        setExpectedBlockSize("PBEWITHSHAAND128BITAES-CBC-BC", 16);
+        setExpectedBlockSize("PBEWITHSHAAND192BITAES-CBC-BC", 16);
+        setExpectedBlockSize("PBEWITHSHAAND256BITAES-CBC-BC", 16);
+
+        if (StandardNames.IS_RI) {
+            setExpectedBlockSize("AESWRAP", 16);
+        } else {
+            setExpectedBlockSize("AESWRAP", 0);
+        }
+
+        setExpectedBlockSize("ARC4", 0);
+        setExpectedBlockSize("ARCFOUR", 0);
+        setExpectedBlockSize("CHACHA20", 0);
+        setExpectedBlockSize("CHACHA20/POLY1305/NOPADDING", 0);
+        setExpectedBlockSize("PBEWITHSHAAND40BITRC4", 0);
+        setExpectedBlockSize("PBEWITHSHAAND128BITRC4", 0);
+
+        setExpectedBlockSize("BLOWFISH", 8);
+
+        setExpectedBlockSize("DES", 8);
+        setExpectedBlockSize("PBEWITHMD5ANDDES", 8);
+        setExpectedBlockSize("PBEWITHSHA1ANDDES", 8);
+
+        setExpectedBlockSize("DESEDE", 8);
+        setExpectedBlockSize("DESEDE/CBC/PKCS5PADDING", 8);
+        setExpectedBlockSize("DESEDE/CBC/PKCS7PADDING", 8);
+        setExpectedBlockSize("DESEDE/CBC/NOPADDING", 8);
+        setExpectedBlockSize("DESEDE/CFB/PKCS5PADDING", 8);
+        setExpectedBlockSize("DESEDE/CFB/PKCS7PADDING", 8);
+        setExpectedBlockSize("DESEDE/CFB/NOPADDING", 8);
+        setExpectedBlockSize("DESEDE/CTR/PKCS5PADDING", 8);
+        setExpectedBlockSize("DESEDE/CTR/PKCS7PADDING", 8);
+        setExpectedBlockSize("DESEDE/CTR/NOPADDING", 8);
+        setExpectedBlockSize("DESEDE/CTS/PKCS5PADDING", 8);
+        setExpectedBlockSize("DESEDE/CTS/PKCS7PADDING", 8);
+        setExpectedBlockSize("DESEDE/CTS/NOPADDING", 8);
+        setExpectedBlockSize("DESEDE/ECB/PKCS5PADDING", 8);
+        setExpectedBlockSize("DESEDE/ECB/PKCS7PADDING", 8);
+        setExpectedBlockSize("DESEDE/ECB/NOPADDING", 8);
+        setExpectedBlockSize("DESEDE/OFB/PKCS5PADDING", 8);
+        setExpectedBlockSize("DESEDE/OFB/PKCS7PADDING", 8);
+        setExpectedBlockSize("DESEDE/OFB/NOPADDING", 8);
+        setExpectedBlockSize("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", 8);
+        setExpectedBlockSize("PBEWITHSHAAND3-KEYTRIPLEDES-CBC", 8);
+        setExpectedBlockSize("PBEWITHMD5ANDTRIPLEDES", 8);
+        setExpectedBlockSize("PBEWITHSHA1ANDDESEDE", 8);
+
+
+        if (StandardNames.IS_RI) {
+            setExpectedBlockSize("DESEDEWRAP", 8);
+        } else {
+            setExpectedBlockSize("DESEDEWRAP", 0);
+        }
+
+        setExpectedBlockSize("RSA", "SunJCE",0);
+        setExpectedBlockSize("RSA/ECB/NoPadding", "SunJCE", 0);
+        setExpectedBlockSize("RSA/ECB/PKCS1Padding", "SunJCE", 0);
+        setExpectedBlockSize("RSA/ECB/OAEPPadding", "SunJCE", 0);
+        setExpectedBlockSize("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", "SunJCE", 0);
+        setExpectedBlockSize("RSA/ECB/OAEPWithSHA-224AndMGF1Padding", "SunJCE", 0);
+        setExpectedBlockSize("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "SunJCE", 0);
+        setExpectedBlockSize("RSA/ECB/OAEPWithSHA-384AndMGF1Padding", "SunJCE", 0);
+        setExpectedBlockSize("RSA/ECB/OAEPWithSHA-512AndMGF1Padding", "SunJCE", 0);
+
+        setExpectedBlockSize("RSA", Cipher.ENCRYPT_MODE, 256);
+        setExpectedBlockSize("RSA/ECB/NoPadding", Cipher.ENCRYPT_MODE, 256);
+        setExpectedBlockSize("RSA/ECB/PKCS1Padding", Cipher.ENCRYPT_MODE, 245);
+
+        // BC strips the leading 0 for us even when NoPadding is specified
+        setExpectedBlockSize("RSA", Cipher.ENCRYPT_MODE, "BC", 255);
+        setExpectedBlockSize("RSA/ECB/NoPadding", Cipher.ENCRYPT_MODE, "BC", 255);
+
+        setExpectedBlockSize("RSA", Cipher.DECRYPT_MODE, 256);
+        setExpectedBlockSize("RSA/ECB/NoPadding", Cipher.DECRYPT_MODE, 256);
+        setExpectedBlockSize("RSA/ECB/PKCS1Padding", Cipher.DECRYPT_MODE, 256);
+
+        // OAEP padding modes change the output and block size. SHA-1 is the default.
+        setExpectedBlockSize("RSA/ECB/OAEPPadding", Cipher.ENCRYPT_MODE, 214);
+        setExpectedBlockSize("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", Cipher.ENCRYPT_MODE, 214);
+        setExpectedBlockSize("RSA/ECB/OAEPWithSHA-224AndMGF1Padding", Cipher.ENCRYPT_MODE, 198);
+        setExpectedBlockSize("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", Cipher.ENCRYPT_MODE, 190);
+        setExpectedBlockSize("RSA/ECB/OAEPWithSHA-384AndMGF1Padding", Cipher.ENCRYPT_MODE, 158);
+        setExpectedBlockSize("RSA/ECB/OAEPWithSHA-512AndMGF1Padding", Cipher.ENCRYPT_MODE, 126);
+
+        setExpectedBlockSize("RSA/ECB/OAEPPadding", Cipher.DECRYPT_MODE, 256);
+        setExpectedBlockSize("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", Cipher.DECRYPT_MODE, 256);
+        setExpectedBlockSize("RSA/ECB/OAEPWithSHA-224AndMGF1Padding", Cipher.DECRYPT_MODE, 256);
+        setExpectedBlockSize("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", Cipher.DECRYPT_MODE, 256);
+        setExpectedBlockSize("RSA/ECB/OAEPWithSHA-384AndMGF1Padding", Cipher.DECRYPT_MODE, 256);
+        setExpectedBlockSize("RSA/ECB/OAEPWithSHA-512AndMGF1Padding", Cipher.DECRYPT_MODE, 256);
+    }
+
+    private static String modeKey(String algorithm, int mode) {
+        return algorithm + ":" + mode;
+    }
+
+    private static String modeProviderKey(String algorithm, int mode, String provider) {
+        return algorithm + ":" + mode + ":" + provider;
+    }
+
+    private static String providerKey(String algorithm, String provider) {
+        return algorithm + ":" + provider;
+    }
+
+    private static void setExpectedSize(Map<String, Integer> map,
+                                        String algorithm, int value) {
+        algorithm = algorithm.toUpperCase(Locale.US);
+        map.put(algorithm, value);
+    }
+
+    private static void setExpectedSize(Map<String, Integer> map,
+                                        String algorithm, int mode, int value) {
+        setExpectedSize(map, modeKey(algorithm, mode), value);
+    }
+
+    private static void setExpectedSize(Map<String, Integer> map,
+                                        String algorithm, int mode, String provider, int value) {
+        setExpectedSize(map, modeProviderKey(algorithm, mode, provider), value);
+    }
+
+    private static void setExpectedSize(Map<String, Integer> map,
+            String algorithm, String provider, int value) {
+        setExpectedSize(map, providerKey(algorithm, provider), value);
+    }
+
+    private static int getExpectedSize(Map<String, Integer> map, String algorithm, int mode, String provider) {
+        algorithm = algorithm.toUpperCase(Locale.US);
+        provider = provider.toUpperCase(Locale.US);
+        Integer expected = map.get(modeProviderKey(algorithm, mode, provider));
+        if (expected != null) {
+            return expected;
+        }
+        expected = map.get(providerKey(algorithm, provider));
+        if (expected != null) {
+            return expected;
+        }
+        expected = map.get(modeKey(algorithm, mode));
+        if (expected != null) {
+            return expected;
+        }
+        expected = map.get(algorithm);
+        assertNotNull("Algorithm " + algorithm + " with mode " + mode + " and provider " + provider
+                      + " not found in " + map, expected);
+        return expected;
+    }
+
+    private static void setExpectedBlockSize(String algorithm, int value) {
+        setExpectedSize(EXPECTED_BLOCK_SIZE, algorithm, value);
+    }
+
+    private static void setExpectedBlockSize(String algorithm, int mode, int value) {
+        setExpectedSize(EXPECTED_BLOCK_SIZE, algorithm, mode, value);
+    }
+
+    private static void setExpectedBlockSize(String algorithm, String provider, int value) {
+        setExpectedSize(EXPECTED_BLOCK_SIZE, algorithm, provider, value);
+    }
+
+    private static void setExpectedBlockSize(String algorithm, int mode, String provider, int value) {
+        setExpectedSize(EXPECTED_BLOCK_SIZE, algorithm, mode, provider, value);
+    }
+
+    private static int getExpectedBlockSize(String algorithm, int mode, String provider) {
+        return getExpectedSize(EXPECTED_BLOCK_SIZE, algorithm, mode, provider);
+    }
+
+    private static Map<String, Integer> EXPECTED_OUTPUT_SIZE = new HashMap<String, Integer>();
+    static {
+        setExpectedOutputSize("AES/CBC/NOPADDING", 0);
+        setExpectedOutputSize("AES/CFB/NOPADDING", 0);
+        setExpectedOutputSize("AES/CTR/NOPADDING", 0);
+        setExpectedOutputSize("AES/CTS/NOPADDING", 0);
+        setExpectedOutputSize("AES/ECB/NOPADDING", 0);
+        setExpectedOutputSize("AES/OFB/NOPADDING", 0);
+        setExpectedOutputSize("AES_128/CBC/NOPADDING", 0);
+        setExpectedOutputSize("AES_128/ECB/NOPADDING", 0);
+        setExpectedOutputSize("AES_256/CBC/NOPADDING", 0);
+        setExpectedOutputSize("AES_256/ECB/NOPADDING", 0);
+
+        setExpectedOutputSize("AES", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES/CBC/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES/CFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES/CFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES/CTR/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES/CTR/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES/CTS/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES/CTS/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES/ECB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES/ECB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES/GCM/NOPADDING", Cipher.ENCRYPT_MODE, GCM_TAG_SIZE_BITS / 8);
+        setExpectedOutputSize(
+                "AES/GCM-SIV/NOPADDING", Cipher.ENCRYPT_MODE, GCM_SIV_TAG_SIZE_BITS / 8);
+        setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES/OFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_128/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_128/CBC/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_128/ECB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_128/ECB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_128/GCM/NOPADDING", Cipher.ENCRYPT_MODE, GCM_TAG_SIZE_BITS / 8);
+        setExpectedOutputSize(
+                "AES_128/GCM-SIV/NOPADDING", Cipher.ENCRYPT_MODE, GCM_SIV_TAG_SIZE_BITS / 8);
+        setExpectedOutputSize("AES_256/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_256/CBC/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_256/ECB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_256/ECB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 16);
+        setExpectedOutputSize("AES_256/GCM/NOPADDING", Cipher.ENCRYPT_MODE, GCM_TAG_SIZE_BITS / 8);
+        setExpectedOutputSize(
+                "AES_256/GCM-SIV/NOPADDING", Cipher.ENCRYPT_MODE, GCM_SIV_TAG_SIZE_BITS / 8);
+        setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16);
+        setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16);
+        setExpectedOutputSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16);
+        setExpectedOutputSize("PBEWITHSHA256AND128BITAES-CBC-BC", 16);
+        setExpectedOutputSize("PBEWITHSHA256AND192BITAES-CBC-BC", 16);
+        setExpectedOutputSize("PBEWITHSHA256AND256BITAES-CBC-BC", 16);
+        setExpectedOutputSize("PBEWITHSHAAND128BITAES-CBC-BC", 16);
+        setExpectedOutputSize("PBEWITHSHAAND192BITAES-CBC-BC", 16);
+        setExpectedOutputSize("PBEWITHSHAAND256BITAES-CBC-BC", 16);
+        // AndroidOpenSSL returns zero for the non-block ciphers
+        setExpectedOutputSize("AES/CFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
+        setExpectedOutputSize("AES/CFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
+        setExpectedOutputSize("AES/CTR/PKCS5PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
+        setExpectedOutputSize("AES/CTR/PKCS7PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
+        setExpectedOutputSize("AES/CTS/PKCS5PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
+        setExpectedOutputSize("AES/CTS/PKCS7PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
+        setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
+        setExpectedOutputSize("AES/OFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
+
+        setExpectedOutputSize("AES", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES/CFB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES/CFB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES/CTR/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES/CTR/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES/CTS/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES/CTS/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES/GCM/NOPADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES/GCM-SIV/NOPADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES/OFB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_128/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_128/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_128/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_128/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_128/GCM/NOPADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_128/GCM-SIV/NOPADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_256/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_256/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_256/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_256/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_256/GCM/NOPADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("AES_256/GCM-SIV/NOPADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("PBEWITHSHA256AND128BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("PBEWITHSHA256AND192BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("PBEWITHSHA256AND256BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("PBEWITHSHAAND128BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("PBEWITHSHAAND192BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("PBEWITHSHAAND256BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("DESEDE/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 0);
+        setExpectedOutputSize("DESEDE/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 0);
+
+        if (StandardNames.IS_RI) {
+            setExpectedOutputSize("AESWRAP", Cipher.WRAP_MODE, 8);
+            setExpectedOutputSize("AESWRAP", Cipher.UNWRAP_MODE, 0);
+        } else {
+            setExpectedOutputSize("AESWRAP", -1);
+        }
+
+        setExpectedOutputSize("ARC4", 0);
+        setExpectedOutputSize("ARCFOUR", 0);
+        setExpectedOutputSize("CHACHA20", 0);
+        setExpectedOutputSize("CHACHA20/POLY1305/NOPADDING", 0);
+        setExpectedOutputSize("PBEWITHSHAAND40BITRC4", 0);
+        setExpectedOutputSize("PBEWITHSHAAND128BITRC4", 0);
+
+        setExpectedOutputSize("BLOWFISH", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("BLOWFISH", Cipher.DECRYPT_MODE, 0);
+
+        setExpectedOutputSize("DES", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("PBEWITHMD5ANDDES", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("PBEWITHSHA1ANDDES", Cipher.ENCRYPT_MODE, 8);
+
+        setExpectedOutputSize("DES", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("PBEWITHMD5ANDDES", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("PBEWITHSHA1ANDDES", Cipher.DECRYPT_MODE, 0);
+
+        setExpectedOutputSize("DESEDE/CBC/NOPADDING", 0);
+        setExpectedOutputSize("DESEDE/CFB/NOPADDING", 0);
+        setExpectedOutputSize("DESEDE/CTR/NOPADDING", 0);
+        setExpectedOutputSize("DESEDE/CTS/NOPADDING", 0);
+        setExpectedOutputSize("DESEDE/ECB/NOPADDING", 0);
+        setExpectedOutputSize("DESEDE/OFB/NOPADDING", 0);
+
+        setExpectedOutputSize("DESEDE", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("DESEDE/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("DESEDE/CBC/PKCS7PADDING", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("DESEDE/CFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("DESEDE/CFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("DESEDE/CTR/PKCS5PADDING", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("DESEDE/CTR/PKCS7PADDING", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("DESEDE/CTS/PKCS5PADDING", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("DESEDE/CTS/PKCS7PADDING", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("DESEDE/ECB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("DESEDE/ECB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("DESEDE/OFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("DESEDE/OFB/PKCS7PADDING", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("PBEWITHSHAAND3-KEYTRIPLEDES-CBC", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("PBEWITHMD5ANDTRIPLEDES", Cipher.ENCRYPT_MODE, 8);
+        setExpectedOutputSize("PBEWITHSHA1ANDDESEDE", Cipher.ENCRYPT_MODE, 8);
+
+        setExpectedOutputSize("DESEDE", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("DESEDE/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("DESEDE/CBC/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("DESEDE/CFB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("DESEDE/CFB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("DESEDE/CTR/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("DESEDE/CTR/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("DESEDE/CTS/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("DESEDE/CTS/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("DESEDE/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("DESEDE/ECB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("DESEDE/OFB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("DESEDE/OFB/PKCS7PADDING", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("PBEWITHSHAAND3-KEYTRIPLEDES-CBC", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("PBEWITHMD5ANDTRIPLEDES", Cipher.DECRYPT_MODE, 0);
+        setExpectedOutputSize("PBEWITHSHA1ANDDESEDE", Cipher.DECRYPT_MODE, 0);
+
+        if (StandardNames.IS_RI) {
+            setExpectedOutputSize("DESEDEWRAP", Cipher.WRAP_MODE, 16);
+            setExpectedOutputSize("DESEDEWRAP", Cipher.UNWRAP_MODE, 0);
+        } else {
+            setExpectedOutputSize("DESEDEWRAP", -1);
+        }
+
+        setExpectedOutputSize("RSA", Cipher.ENCRYPT_MODE, 256);
+        setExpectedOutputSize("RSA/ECB/NoPadding", Cipher.ENCRYPT_MODE, 256);
+        setExpectedOutputSize("RSA/ECB/PKCS1Padding", Cipher.ENCRYPT_MODE, 256);
+
+        setExpectedOutputSize("RSA", Cipher.DECRYPT_MODE, 256);
+        setExpectedOutputSize("RSA/ECB/NoPadding", Cipher.DECRYPT_MODE, 256);
+        setExpectedOutputSize("RSA/ECB/PKCS1Padding", Cipher.DECRYPT_MODE, 245);
+        setExpectedOutputSize("RSA/ECB/OAEPPadding", Cipher.DECRYPT_MODE, 256);
+
+        // SunJCE returns the full for size even when PKCS1Padding is specified
+        setExpectedOutputSize("RSA/ECB/PKCS1Padding", Cipher.DECRYPT_MODE, "SunJCE", 256);
+
+        // BC strips the leading 0 for us even when NoPadding is specified
+        setExpectedOutputSize("RSA", Cipher.DECRYPT_MODE, "BC", 255);
+        setExpectedOutputSize("RSA/ECB/NoPadding", Cipher.DECRYPT_MODE, "BC", 255);
+
+        // OAEP padding modes change the output and block size. SHA-1 is the default.
+        setExpectedOutputSize("RSA/ECB/OAEPPadding", Cipher.DECRYPT_MODE, 214);
+        setExpectedOutputSize("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", Cipher.DECRYPT_MODE, 214);
+        setExpectedOutputSize("RSA/ECB/OAEPWithSHA-224AndMGF1Padding", Cipher.DECRYPT_MODE, 198);
+        setExpectedOutputSize("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", Cipher.DECRYPT_MODE, 190);
+        setExpectedOutputSize("RSA/ECB/OAEPWithSHA-384AndMGF1Padding", Cipher.DECRYPT_MODE, 158);
+        setExpectedOutputSize("RSA/ECB/OAEPWithSHA-512AndMGF1Padding", Cipher.DECRYPT_MODE, 126);
+
+        setExpectedOutputSize("RSA/ECB/OAEPPadding", Cipher.ENCRYPT_MODE, 256);
+        setExpectedOutputSize("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", Cipher.ENCRYPT_MODE, 256);
+        setExpectedOutputSize("RSA/ECB/OAEPWithSHA-224AndMGF1Padding", Cipher.ENCRYPT_MODE, 256);
+        setExpectedOutputSize("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", Cipher.ENCRYPT_MODE, 256);
+        setExpectedOutputSize("RSA/ECB/OAEPWithSHA-384AndMGF1Padding", Cipher.ENCRYPT_MODE, 256);
+        setExpectedOutputSize("RSA/ECB/OAEPWithSHA-512AndMGF1Padding", Cipher.ENCRYPT_MODE, 256);
+    }
+
+    private static void setExpectedOutputSize(String algorithm, int value) {
+        setExpectedSize(EXPECTED_OUTPUT_SIZE, algorithm, value);
+    }
+
+    private static void setExpectedOutputSize(String algorithm, int mode, int value) {
+        setExpectedSize(EXPECTED_OUTPUT_SIZE, algorithm, mode, value);
+    }
+
+    private static void setExpectedOutputSize(String algorithm, int mode, String provider, int value) {
+        setExpectedSize(EXPECTED_OUTPUT_SIZE, algorithm, mode, provider, value);
+    }
+
+    private static int getExpectedOutputSize(String algorithm, int mode, String provider) {
+        return getExpectedSize(EXPECTED_OUTPUT_SIZE, algorithm, mode, provider);
+    }
+
+    private static byte[] ORIGINAL_PLAIN_TEXT = new byte[] { 0x0a, 0x0b, 0x0c };
+    private static byte[] SIXTEEN_BYTE_BLOCK_PLAIN_TEXT = new byte[] { 0x0a, 0x0b, 0x0c, 0x00,
+                                                                       0x00, 0x00, 0x00, 0x00,
+                                                                       0x00, 0x00, 0x00, 0x00,
+                                                                       0x00, 0x00, 0x00, 0x00 };
+    private static byte[] EIGHT_BYTE_BLOCK_PLAIN_TEXT = new byte[] { 0x0a, 0x0b, 0x0c, 0x00,
+                                                                     0x00, 0x00, 0x00, 0x00 };
+    private static byte[] PKCS1_BLOCK_TYPE_00_PADDED_PLAIN_TEXT = new byte[] {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0x0b, 0x0c
+    };
+    private static byte[] PKCS1_BLOCK_TYPE_01_PADDED_PLAIN_TEXT = new byte[] {
+        (byte) 0x00, (byte) 0x01, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c
+    };
+    private static byte[] PKCS1_BLOCK_TYPE_02_PADDED_PLAIN_TEXT = new byte[] {
+        (byte) 0x00, (byte) 0x02, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c
+    };
+
+
+    private static byte[] getActualPlainText(String algorithm) {
+        // Block mode AES with NoPadding needs to match underlying block size
+        if (algorithm.equals("AES")
+            || algorithm.equals("AES/CBC/NOPADDING")
+            || algorithm.equals("AES/CTS/NOPADDING")
+            || algorithm.equals("AES/ECB/NOPADDING")
+            || algorithm.equals("AES_128/CBC/NOPADDING")
+            || algorithm.equals("AES_128/ECB/NOPADDING")
+            || algorithm.equals("AES_256/CBC/NOPADDING")
+            || algorithm.equals("AES_256/ECB/NOPADDING")) {
+            return SIXTEEN_BYTE_BLOCK_PLAIN_TEXT;
+        }
+        if (algorithm.equals("DESEDE")
+            || algorithm.equals("DESEDE/CBC/NOPADDING")
+            || algorithm.equals("DESEDE/ECB/NOPADDING")) {
+            return EIGHT_BYTE_BLOCK_PLAIN_TEXT;
+        }
+        return ORIGINAL_PLAIN_TEXT;
+    }
+
+    private static byte[] getExpectedPlainText(String algorithm, String provider) {
+        // Block mode AES with NoPadding needs to match underlying block size
+        if (algorithm.equals("AES")
+            || algorithm.equals("AES/CBC/NOPADDING")
+            || algorithm.equals("AES/CTS/NOPADDING")
+            || algorithm.equals("AES/ECB/NOPADDING")
+            || algorithm.equals("AES_128/CBC/NOPADDING")
+            || algorithm.equals("AES_128/ECB/NOPADDING")
+            || algorithm.equals("AES_256/CBC/NOPADDING")
+            || algorithm.equals("AES_256/ECB/NOPADDING")) {
+            return SIXTEEN_BYTE_BLOCK_PLAIN_TEXT;
+        }
+        if (algorithm.equals("DESEDE")
+            || algorithm.equals("DESEDE/CBC/NOPADDING")
+            || algorithm.equals("DESEDE/ECB/NOPADDING")) {
+            return EIGHT_BYTE_BLOCK_PLAIN_TEXT;
+        }
+        // BC strips the leading 0 for us even when NoPadding is specified
+        if (!provider.equals("BC") && algorithm.equals("RSA/ECB/NOPADDING")) {
+            return PKCS1_BLOCK_TYPE_00_PADDED_PLAIN_TEXT;
+        }
+        return ORIGINAL_PLAIN_TEXT;
+    }
+
+    private static AlgorithmParameterSpec getEncryptAlgorithmParameterSpec(String algorithm) {
+        if (isPBE(algorithm)) {
+            final byte[] salt = new byte[8];
+            new SecureRandom().nextBytes(salt);
+            return new PBEParameterSpec(salt, 1024);
+        }
+        if (algorithm.equals("AES/GCM/NOPADDING")
+            || algorithm.equals("AES_128/GCM/NOPADDING")
+            || algorithm.equals("AES_256/GCM/NOPADDING")) {
+            final byte[] iv = new byte[12];
+            new SecureRandom().nextBytes(iv);
+            return new GCMParameterSpec(GCM_TAG_SIZE_BITS, iv);
+        }
+        if (algorithm.equals("AES/GCM-SIV/NOPADDING")
+                || algorithm.equals("AES_128/GCM-SIV/NOPADDING")
+                || algorithm.equals("AES_256/GCM-SIV/NOPADDING")) {
+            final byte[] iv = new byte[12];
+            new SecureRandom().nextBytes(iv);
+            return new GCMParameterSpec(GCM_SIV_TAG_SIZE_BITS, iv);
+        }
+        if (algorithm.equals("AES/CBC/NOPADDING")
+            || algorithm.equals("AES/CBC/PKCS5PADDING")
+            || algorithm.equals("AES/CBC/PKCS7PADDING")
+            || algorithm.equals("AES/CFB/NOPADDING")
+            || algorithm.equals("AES/CTR/NOPADDING")
+            || algorithm.equals("AES/CTS/NOPADDING")
+            || algorithm.equals("AES/OFB/NOPADDING")
+            || algorithm.equals("AES_128/CBC/NOPADDING")
+            || algorithm.equals("AES_128/CBC/PKCS5PADDING")
+            || algorithm.equals("AES_128/CBC/PKCS7PADDING")
+            || algorithm.equals("AES_256/CBC/NOPADDING")
+            || algorithm.equals("AES_256/CBC/PKCS5PADDING")
+            || algorithm.equals("AES_256/CBC/PKCS7PADDING")) {
+            final byte[] iv = new byte[16];
+            new SecureRandom().nextBytes(iv);
+            return new IvParameterSpec(iv);
+        }
+        if (algorithm.equals("DESEDE/CBC/NOPADDING")
+            || algorithm.equals("DESEDE/CBC/PKCS5PADDING")
+            || algorithm.equals("DESEDE/CBC/PKCS7PADDING")
+            || algorithm.equals("DESEDE/CFB/NOPADDING")
+            || algorithm.equals("DESEDE/CTR/NOPADDING")
+            || algorithm.equals("DESEDE/CTS/NOPADDING")
+            || algorithm.equals("DESEDE/OFB/NOPADDING")) {
+            final byte[] iv = new byte[8];
+            new SecureRandom().nextBytes(iv);
+            return new IvParameterSpec(iv);
+        }
+        if (algorithm.equals("CHACHA20")
+            || algorithm.equals("CHACHA20/POLY1305/NOPADDING")) {
+            final byte[] iv = new byte[12];
+            new SecureRandom().nextBytes(iv);
+            return new IvParameterSpec(iv);
+        }
+        return null;
+    }
+
+    private static AlgorithmParameterSpec getDecryptAlgorithmParameterSpec(AlgorithmParameterSpec encryptSpec,
+                                                                           Cipher encryptCipher) {
+        String algorithm = encryptCipher.getAlgorithm().toUpperCase(Locale.US);
+        if (isPBE(algorithm)) {
+            return encryptSpec;
+        }
+        if (isOnlyWrappingAlgorithm(algorithm)) {
+            return null;
+        }
+        byte[] iv = encryptCipher.getIV();
+        if (iv != null) {
+            if ("AES/GCM/NOPADDING".equals(algorithm)
+                    || "AES_128/GCM/NOPADDING".equals(algorithm)
+                    || "AES_256/GCM/NOPADDING".equals(algorithm)) {
+                return new GCMParameterSpec(GCM_TAG_SIZE_BITS, iv);
+            }
+            if ("AES/GCM-SIV/NOPADDING".equals(algorithm)
+                    || "AES_128/GCM-SIV/NOPADDING".equals(algorithm)
+                    || "AES_256/GCM-SIV/NOPADDING".equals(algorithm)) {
+                return new GCMParameterSpec(GCM_SIV_TAG_SIZE_BITS, iv);
+            }
+            return new IvParameterSpec(iv);
+        }
+        return null;
+    }
+
+    /*
+     * This must be below everything else to make sure the other static blocks
+     * have run first.
+     */
+    private static final boolean IS_UNLIMITED;
+    static {
+        boolean is_unlimited;
+        if (StandardNames.IS_RI) {
+            try {
+                String algorithm = "PBEWITHMD5ANDTRIPLEDES";
+                Cipher.getInstance(algorithm).init(getEncryptMode(algorithm),
+                                                   getEncryptKey(algorithm),
+                                                   getEncryptAlgorithmParameterSpec(algorithm));
+                is_unlimited = true;
+            } catch (Exception e) {
+                is_unlimited = false;
+                System.out.println("WARNING: Some tests disabled due to lack of "
+                                   + "'Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files'");
+            }
+        } else {
+            is_unlimited = true;
+        }
+        IS_UNLIMITED = is_unlimited;
+    }
+
+    @Test
+    public void test_getInstance() throws Exception {
+        final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
+        PrintStream out = new PrintStream(errBuffer);
+
+        Set<String> seenBaseCipherNames = new HashSet<String>();
+        Set<String> seenCiphersWithModeAndPadding = new HashSet<String>();
+
+        Provider[] providers = Security.getProviders();
+        for (Provider provider : providers) {
+            Set<Provider.Service> services = provider.getServices();
+            for (Provider.Service service : services) {
+                String type = service.getType();
+                if (!type.equals("Cipher")) {
+                    continue;
+                }
+
+                String algorithm = service.getAlgorithm().toUpperCase(Locale.US);
+
+                /*
+                 * Any specific modes and paddings aren't tested directly here,
+                 * but we need to make sure we see the bare algorithm from some
+                 * provider. We will test each mode specifically when we get the
+                 * base cipher.
+                 */
+                final int firstSlash = algorithm.indexOf('/');
+                if (firstSlash == -1) {
+                    seenBaseCipherNames.add(algorithm);
+                } else {
+                    final String baseCipherName = algorithm.substring(0, firstSlash);
+                    if (!seenBaseCipherNames.contains(baseCipherName)
+                            && !(baseCipherName.equals("AES_128")
+                                || baseCipherName.equals("AES_192")
+                                || baseCipherName.equals("AES_256"))) {
+                        seenCiphersWithModeAndPadding.add(baseCipherName);
+                    }
+                    if (!Conscrypt.isConscrypt(provider)) {
+                        continue;
+                    }
+                }
+
+                if (provider.getName().equals("SunJCE")) {
+                    // The SunJCE provider acts in numerous idiosyncratic ways that don't
+                    // match any other provider.  Examples include returning non-null IVs
+                    // when no IV was provided on init, NullPointerExceptions when null
+                    // SecureRandoms are supplied (but only to PBE ciphers), and not
+                    // supplying KeyGenerators for some algorithms.  We aren't sufficiently
+                    // interested in verifying this provider's behavior to adapt the
+                    // tests and Oracle presumably tests them well anyway, so just skip
+                    // verifying them.
+                    continue;
+                }
+
+                try {
+                    test_Cipher_Algorithm(provider, algorithm);
+                } catch (Throwable e) {
+                    out.append("Error encountered checking " + algorithm
+                               + " with provider " + provider.getName() + "\n");
+                    e.printStackTrace(out);
+                }
+
+                Set<String> modes = StandardNames.getModesForCipher(algorithm);
+                if (modes != null) {
+                    for (String mode : modes) {
+                        Set<String> paddings = StandardNames.getPaddingsForCipher(algorithm);
+                        if (paddings != null) {
+                            for (String padding : paddings) {
+                                final String algorithmName = algorithm + "/" + mode + "/" + padding;
+                                try {
+                                    test_Cipher_Algorithm(provider, algorithmName);
+                                } catch (Throwable e) {
+                                    out.append("Error encountered checking " + algorithmName
+                                               + " with provider " + provider.getName() + "\n");
+                                    e.printStackTrace(out);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        seenCiphersWithModeAndPadding.removeAll(seenBaseCipherNames);
+        assertEquals("Ciphers seen with mode and padding but not base cipher",
+                Collections.EMPTY_SET, seenCiphersWithModeAndPadding);
+
+        out.flush();
+        if (errBuffer.size() > 0) {
+            throw new Exception("Errors encountered:\n\n" + errBuffer.toString() + "\n\n");
+        }
+    }
+
+    private void test_Cipher_Algorithm(Provider provider, String algorithm) throws Exception {
+        if (algorithm.equals("RSA") && provider.getName().equals("BC")) {
+            // http://b/9097343 BC's Cipher.RSA defaults to NoPadding
+            // which makes it fail the key wrapping test if the
+            // generated AES key to wrap starts with a leading
+            // zero. For the purposes of the test, use the same
+            // default behavior as the RI. Real code really should
+            // specify the exact mode and padding they need and not
+            // rely on defaults. http://b/9097343
+            algorithm = "RSA/ECB/PKCS1Padding";
+        }
+
+        // Cipher.getInstance(String)
+        Cipher c1 = Cipher.getInstance(algorithm);
+        if (provider.equals(c1.getProvider())) {
+            assertEquals(algorithm, c1.getAlgorithm());
+            test_Cipher(c1);
+        }
+
+        // Cipher.getInstance(String, Provider)
+        Cipher c2 = Cipher.getInstance(algorithm, provider);
+        assertEquals(algorithm, c2.getAlgorithm());
+        assertEquals(provider, c2.getProvider());
+        test_Cipher(c2);
+
+        // Cipher.getInstance(String, String)
+        Cipher c3 = Cipher.getInstance(algorithm, provider.getName());
+        assertEquals(algorithm, c3.getAlgorithm());
+        assertEquals(provider, c3.getProvider());
+        test_Cipher(c3);
+    }
+
+    private void test_Cipher(Cipher c) throws Exception {
+        String algorithm = c.getAlgorithm().toUpperCase(Locale.US);
+        String providerName = c.getProvider().getName();
+        if (!isSupported(algorithm, providerName)) {
+            return;
+        }
+        String cipherID = algorithm + ":" + providerName;
+
+        try {
+            c.getOutputSize(0);
+            fail("getOutputSize() should throw if called before Cipher initialization");
+        } catch (IllegalStateException expected) {
+        }
+
+        // TODO: test keys from different factories (e.g. OpenSSLRSAPrivateKey vs BCRSAPrivateKey)
+        Key encryptKey = getEncryptKey(algorithm);
+
+        AlgorithmParameterSpec encryptSpec = getEncryptAlgorithmParameterSpec(algorithm);
+        int encryptMode = getEncryptMode(algorithm);
+
+        // Bouncycastle doesn't return a default PBEParameterSpec
+        if (isPBE(algorithm) && !"BC".equals(providerName)) {
+            assertNotNull(cipherID + " getParameters()", c.getParameters());
+            assertNotNull(c.getParameters().getParameterSpec(PBEParameterSpec.class));
+        } else {
+            assertNull(cipherID + " getParameters()", c.getParameters());
+        }
+        try {
+            assertNull(cipherID + " getIV()", c.getIV());
+        } catch (NullPointerException e) {
+            // Bouncycastle apparently has a bug here with AESWRAP, et al.
+            if (!("BC".equals(providerName) && isOnlyWrappingAlgorithm(algorithm))) {
+                throw e;
+            }
+        }
+
+        test_Cipher_init_NullParameters(c, encryptMode, encryptKey);
+
+        c.init(encryptMode, encryptKey, encryptSpec);
+        assertEquals(cipherID + " getBlockSize() encryptMode",
+                getExpectedBlockSize(algorithm, encryptMode, providerName), c.getBlockSize());
+        assertTrue(cipherID + " getOutputSize(0) encryptMode",
+                getExpectedOutputSize(algorithm, encryptMode, providerName) <= c.getOutputSize(0));
+        if ((algorithm.endsWith("/PKCS5PADDING") || algorithm.endsWith("/PKCS7PADDING"))
+                && isStreamMode(algorithm)) {
+            assertEquals(getExpectedOutputSize(algorithm, encryptMode, providerName),
+                    c.doFinal(new byte[1]).length);
+        }
+
+        if (isPBE(algorithm)) {
+            if (algorithm.endsWith("RC4")) {
+                assertNull(cipherID + " getIV()", c.getIV());
+            } else {
+                assertNotNull(cipherID + " getIV()", c.getIV());
+            }
+        } else if (encryptSpec instanceof IvParameterSpec) {
+            assertEquals(cipherID + " getIV()",
+                    Arrays.toString(((IvParameterSpec) encryptSpec).getIV()),
+                    Arrays.toString(c.getIV()));
+        } else if (encryptSpec instanceof GCMParameterSpec) {
+            assertNotNull(c.getIV());
+            assertEquals(cipherID + " getIV()",
+                    Arrays.toString(((GCMParameterSpec) encryptSpec).getIV()),
+                    Arrays.toString(c.getIV()));
+        } else {
+            try {
+                assertNull(cipherID + " getIV()", c.getIV());
+            } catch (NullPointerException e) {
+                // Bouncycastle apparently has a bug here with AESWRAP, et al.
+                if (!("BC".equals(providerName) && isOnlyWrappingAlgorithm(algorithm))) {
+                    throw e;
+                }
+            }
+        }
+
+        AlgorithmParameters encParams = c.getParameters();
+        assertCorrectAlgorithmParameters(providerName, cipherID, encryptSpec, encParams);
+
+        AlgorithmParameterSpec decryptSpec = getDecryptAlgorithmParameterSpec(encryptSpec, c);
+        int decryptMode = getDecryptMode(algorithm);
+
+        Key decryptKey = getDecryptKey(algorithm);
+
+        test_Cipher_init_Decrypt_NullParameters(c, decryptMode, decryptKey, decryptSpec != null);
+
+        c.init(decryptMode, decryptKey, decryptSpec);
+        assertEquals(cipherID + " getBlockSize() decryptMode",
+                     getExpectedBlockSize(algorithm, decryptMode, providerName), c.getBlockSize());
+        assertEquals(cipherID + " getOutputSize(0) decryptMode",
+                     getExpectedOutputSize(algorithm, decryptMode, providerName), c.getOutputSize(0));
+
+        if (isPBE(algorithm)) {
+            if (algorithm.endsWith("RC4")) {
+                assertNull(cipherID + " getIV()", c.getIV());
+            } else {
+                assertNotNull(cipherID + " getIV()", c.getIV());
+            }
+        } else if (decryptSpec instanceof IvParameterSpec) {
+            assertEquals(cipherID + " getIV()",
+                    Arrays.toString(((IvParameterSpec) decryptSpec).getIV()),
+                    Arrays.toString(c.getIV()));
+        } else if (decryptSpec instanceof GCMParameterSpec) {
+            assertNotNull(c.getIV());
+            assertEquals(cipherID + " getIV()",
+                    Arrays.toString(((GCMParameterSpec) decryptSpec).getIV()),
+                    Arrays.toString(c.getIV()));
+        } else {
+            try {
+                assertNull(cipherID + " getIV()", c.getIV());
+            } catch (NullPointerException e) {
+                // Bouncycastle apparently has a bug here with AESWRAP, et al.
+                if (!("BC".equals(providerName) && isOnlyWrappingAlgorithm(algorithm))) {
+                    throw e;
+                }
+            }
+        }
+
+        AlgorithmParameters decParams = c.getParameters();
+        assertCorrectAlgorithmParameters(providerName, cipherID, decryptSpec, decParams);
+
+        assertNull(cipherID, c.getExemptionMechanism());
+
+        // Test wrapping a key.  Every cipher should be able to wrap. Except those that can't.
+        /* Bouncycastle is broken for wrapping because getIV() fails. */
+        if (isSupportedForWrapping(algorithm) && !providerName.equals("BC")) {
+            // Generate a small SecretKey for AES.
+            KeyGenerator kg = KeyGenerator.getInstance("AES");
+            kg.init(128);
+            SecretKey sk = kg.generateKey();
+
+            // Wrap it.  Use a new encrypt spec so that AEAD algorithms that prohibit IV reuse
+            // don't complain.
+            encryptSpec = getEncryptAlgorithmParameterSpec(algorithm);
+            c.init(Cipher.WRAP_MODE, encryptKey, encryptSpec);
+            byte[] cipherText = c.wrap(sk);
+
+            // Unwrap it
+            c.init(Cipher.UNWRAP_MODE, decryptKey, getDecryptAlgorithmParameterSpec(encryptSpec, c));
+            Key decryptedKey = c.unwrap(cipherText, sk.getAlgorithm(), Cipher.SECRET_KEY);
+
+            assertEquals(cipherID
+                    + " sk.getAlgorithm()=" + sk.getAlgorithm()
+                    + " decryptedKey.getAlgorithm()=" + decryptedKey.getAlgorithm()
+                    + " encryptKey.getEncoded()=" + Arrays.toString(sk.getEncoded())
+                    + " decryptedKey.getEncoded()=" + Arrays.toString(decryptedKey.getEncoded()),
+                    sk, decryptedKey);
+        }
+
+        if (!isOnlyWrappingAlgorithm(algorithm)) {
+            // Use a new encrypt spec so that AEAD algorithms that prohibit IV reuse don't complain
+            encryptSpec = getEncryptAlgorithmParameterSpec(algorithm);
+            c.init(Cipher.ENCRYPT_MODE, encryptKey, encryptSpec);
+            if (isAEAD(algorithm)) {
+                c.updateAAD(new byte[24]);
+            }
+            byte[] cipherText = c.doFinal(getActualPlainText(algorithm));
+            if (!isRandomizedEncryption(algorithm) && !isAEAD(algorithm)) {
+                byte[] cipherText2 = c.doFinal(getActualPlainText(algorithm));
+                assertEquals(cipherID, Arrays.toString(cipherText), Arrays.toString(cipherText2));
+            }
+            decryptSpec = getDecryptAlgorithmParameterSpec(encryptSpec, c);
+            c.init(Cipher.DECRYPT_MODE, decryptKey, decryptSpec);
+            if (isAEAD(algorithm)) {
+                c.updateAAD(new byte[24]);
+            }
+            byte[] decryptedPlainText = c.doFinal(cipherText);
+            assertEquals(cipherID,
+                         Arrays.toString(getExpectedPlainText(algorithm, providerName)),
+                         Arrays.toString(decryptedPlainText));
+            if (isAEAD(algorithm)) {
+                c.updateAAD(new byte[24]);
+            }
+            byte[] decryptedPlainText2 = c.doFinal(cipherText);
+            assertEquals(cipherID,
+                         Arrays.toString(decryptedPlainText),
+                         Arrays.toString(decryptedPlainText2));
+
+            // Use a new encrypt spec so that AEAD algorithms that prohibit IV reuse don't complain
+            encryptSpec = getEncryptAlgorithmParameterSpec(algorithm);
+            test_Cipher_ShortBufferException(c, algorithm, Cipher.ENCRYPT_MODE, encryptSpec,
+                    encryptKey, getActualPlainText(algorithm));
+            decryptSpec = getDecryptAlgorithmParameterSpec(encryptSpec, c);
+            test_Cipher_ShortBufferException(c, algorithm, Cipher.DECRYPT_MODE, decryptSpec,
+                    decryptKey, cipherText);
+
+            test_Cipher_aborted_doFinal(c, algorithm, providerName, encryptKey, decryptKey);
+        }
+    }
+
+    private void assertCorrectAlgorithmParameters(String providerName, String cipherID,
+            final AlgorithmParameterSpec spec, AlgorithmParameters params)
+            throws InvalidParameterSpecException, Exception {
+        if (spec == null) {
+            return;
+        }
+
+        // Bouncycastle has a bug where PBE algorithms sometimes return null parameters.
+        if ("BC".equals(providerName) && isPBE(cipherID) && params == null) {
+            return;
+        }
+
+        assertNotNull(cipherID + " getParameters() should not be null", params);
+
+        if (spec instanceof GCMParameterSpec) {
+            GCMParameterSpec gcmDecryptSpec = params.getParameterSpec(GCMParameterSpec.class);
+            assertEquals(cipherID + " getIV()", Arrays.toString(((GCMParameterSpec) spec).getIV()),
+                    Arrays.toString(gcmDecryptSpec.getIV()));
+            assertEquals(cipherID + " getTLen()", ((GCMParameterSpec) spec).getTLen(),
+                    gcmDecryptSpec.getTLen());
+        } else if (spec instanceof IvParameterSpec) {
+            IvParameterSpec ivDecryptSpec = params.getParameterSpec(IvParameterSpec.class);
+            assertEquals(cipherID + " getIV()", Arrays.toString(((IvParameterSpec) spec).getIV()),
+                    Arrays.toString(ivDecryptSpec.getIV()));
+        } else if (spec instanceof PBEParameterSpec) {
+            // Bouncycastle seems to be undecided about whether it returns this
+            // or not
+            if (!"BC".equals(providerName)) {
+                assertNotNull(cipherID + " getParameters()", params);
+            }
+        } else if (spec instanceof OAEPParameterSpec) {
+            assertOAEPParametersEqual((OAEPParameterSpec) spec,
+                    params.getParameterSpec(OAEPParameterSpec.class));
+        } else {
+            fail("Unhandled algorithm specification class: " + spec.getClass().getName());
+        }
+    }
+
+    private static void assertOAEPParametersEqual(OAEPParameterSpec expectedOaepSpec,
+            OAEPParameterSpec actualOaepSpec) throws Exception {
+        assertEquals(expectedOaepSpec.getDigestAlgorithm(), actualOaepSpec.getDigestAlgorithm());
+
+        assertEquals(expectedOaepSpec.getMGFAlgorithm(), actualOaepSpec.getMGFAlgorithm());
+        if ("MGF1".equals(expectedOaepSpec.getMGFAlgorithm())) {
+            MGF1ParameterSpec expectedMgf1Spec = (MGF1ParameterSpec) expectedOaepSpec
+                    .getMGFParameters();
+            MGF1ParameterSpec actualMgf1Spec = (MGF1ParameterSpec) actualOaepSpec
+                    .getMGFParameters();
+            assertEquals(expectedMgf1Spec.getDigestAlgorithm(),
+                    actualMgf1Spec.getDigestAlgorithm());
+        } else {
+            fail("Unknown MGF algorithm: " + expectedOaepSpec.getMGFAlgorithm());
+        }
+
+        if (expectedOaepSpec.getPSource() instanceof PSource.PSpecified
+                && actualOaepSpec.getPSource() instanceof PSource.PSpecified) {
+            assertEquals(Arrays.toString(
+                                 ((PSource.PSpecified) expectedOaepSpec.getPSource()).getValue()),
+                    Arrays.toString(((PSource.PSpecified) actualOaepSpec.getPSource()).getValue()));
+        } else {
+            fail("Unknown PSource type");
+        }
+    }
+
+    /**
+     * Try various .init(...) calls with null parameters to make sure it is
+     * handled.
+     */
+    private void test_Cipher_init_NullParameters(Cipher c, int encryptMode, Key encryptKey)
+            throws Exception {
+        try {
+            c.init(encryptMode, encryptKey, (AlgorithmParameterSpec) null);
+        } catch (InvalidAlgorithmParameterException e) {
+            if (!isPBE(c.getAlgorithm())) {
+                throw e;
+            }
+        }
+
+        try {
+            c.init(encryptMode, encryptKey, (AlgorithmParameterSpec) null, (SecureRandom) null);
+        } catch (InvalidAlgorithmParameterException e) {
+            if (!isPBE(c.getAlgorithm())) {
+                throw e;
+            }
+        }
+
+        try {
+            c.init(encryptMode, encryptKey, (AlgorithmParameters) null);
+        } catch (InvalidAlgorithmParameterException e) {
+            if (!isPBE(c.getAlgorithm())) {
+                throw e;
+            }
+        }
+
+        try {
+            c.init(encryptMode, encryptKey, (AlgorithmParameters) null, (SecureRandom) null);
+        } catch (InvalidAlgorithmParameterException e) {
+            if (!isPBE(c.getAlgorithm())) {
+                throw e;
+            }
+        }
+    }
+
+    private void test_Cipher_init_Decrypt_NullParameters(Cipher c, int decryptMode, Key encryptKey,
+            boolean needsParameters) throws Exception {
+        try {
+            c.init(decryptMode, encryptKey, (AlgorithmParameterSpec) null);
+            if (needsParameters) {
+                fail("Should throw InvalidAlgorithmParameterException with null parameters");
+            }
+        } catch (InvalidAlgorithmParameterException e) {
+            if (!needsParameters) {
+                throw e;
+            }
+        }
+
+        try {
+            c.init(decryptMode, encryptKey, (AlgorithmParameterSpec) null, (SecureRandom) null);
+            if (needsParameters) {
+                fail("Should throw InvalidAlgorithmParameterException with null parameters");
+            }
+        } catch (InvalidAlgorithmParameterException e) {
+            if (!needsParameters) {
+                throw e;
+            }
+        }
+
+        try {
+            c.init(decryptMode, encryptKey, (AlgorithmParameters) null);
+            if (needsParameters) {
+                fail("Should throw InvalidAlgorithmParameterException with null parameters");
+            }
+        } catch (InvalidAlgorithmParameterException e) {
+            if (!needsParameters) {
+                throw e;
+            }
+        }
+
+        try {
+            c.init(decryptMode, encryptKey, (AlgorithmParameters) null, (SecureRandom) null);
+            if (needsParameters) {
+                fail("Should throw InvalidAlgorithmParameterException with null parameters");
+            }
+        } catch (InvalidAlgorithmParameterException e) {
+            if (!needsParameters) {
+                throw e;
+            }
+        }
+    }
+
+    // Checks that the Cipher throws ShortBufferException when given a too-short buffer
+    private void test_Cipher_ShortBufferException(Cipher c, String algorithm, int encryptMode,
+            AlgorithmParameterSpec spec, Key key, byte[] text) throws Exception {
+        c.init(encryptMode, key, spec);
+        if (isAEAD(algorithm)) {
+            c.updateAAD(new byte[24]);
+        }
+        if (c.getOutputSize(text.length) > 0) {
+            byte[] output;
+            if (algorithm.startsWith("RSA/")) {
+                // RSA encryption pads the input data to a full block before encrypting,
+                // so unlike most algorithms, getOutputSize can't determine how much space
+                // is necessary until the data is actually decrypted.
+                output = new byte[1];
+            } else {
+                // Other algorithms can much more easily forsee how much output data there
+                // will be, so don't let them get away with being overly conservative.
+                output = new byte[c.getOutputSize(text.length) - 1];
+            }
+            try {
+                c.doFinal(text, 0, text.length, output);
+                fail("Short buffer should have thrown ShortBufferException");
+            } catch (ShortBufferException expected) {
+                // Ignored
+            }
+        }
+    }
+
+    // Checks that if the cipher operation is aborted by a ShortBufferException the output
+    // is still correct.
+    private void test_Cipher_aborted_doFinal(Cipher c, String algorithm, String provider,
+            Key encryptKey, Key decryptKey) throws Exception {
+        byte[] text = getActualPlainText(algorithm);
+        AlgorithmParameterSpec encryptSpec = getEncryptAlgorithmParameterSpec(algorithm);
+        c.init(Cipher.ENCRYPT_MODE, encryptKey, encryptSpec);
+        if (isAEAD(algorithm)) {
+            c.updateAAD(new byte[24]);
+        }
+        try {
+            c.doFinal(text, 0, text.length, new byte[0]);
+            fail("Short buffer should have thrown ShortBufferException");
+        } catch (ShortBufferException expected) {
+            // Ignored
+        }
+        byte[] cipherText = c.doFinal(text);
+        c.init(Cipher.DECRYPT_MODE, decryptKey, getDecryptAlgorithmParameterSpec(encryptSpec, c));
+        if (isAEAD(algorithm)) {
+            c.updateAAD(new byte[24]);
+        }
+        byte[] plainText = c.doFinal(cipherText);
+        byte[] expectedPlainText = getExpectedPlainText(algorithm, provider);
+        assertTrue("Expected " + Arrays.toString(expectedPlainText)
+                + " but was " + Arrays.toString(plainText),
+                Arrays.equals(expectedPlainText, plainText));
+    }
+
+    @Test
+    public void testInputPKCS1Padding() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testInputPKCS1Padding(provider);
+        }
+    }
+
+    private void testInputPKCS1Padding(String provider) throws Exception {
+        // Type 1 is for signatures (PrivateKey to "encrypt")
+        testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_01_PADDED_PLAIN_TEXT, getDecryptKey("RSA"), getEncryptKey("RSA"));
+        try {
+            testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_02_PADDED_PLAIN_TEXT, getDecryptKey("RSA"), getEncryptKey("RSA"));
+            fail();
+        } catch (BadPaddingException expected) {
+        }
+
+        // Type 2 is for enciphering (PublicKey to "encrypt")
+        testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_02_PADDED_PLAIN_TEXT, getEncryptKey("RSA"), getDecryptKey("RSA"));
+        try {
+            testInputPKCS1Padding(provider, PKCS1_BLOCK_TYPE_01_PADDED_PLAIN_TEXT, getEncryptKey("RSA"), getDecryptKey("RSA"));
+            fail();
+        } catch (BadPaddingException expected) {
+        }
+    }
+
+    private void testInputPKCS1Padding(String provider, byte[] prePaddedPlainText, Key encryptKey, Key decryptKey) throws Exception {
+        Cipher encryptCipher = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+        encryptCipher.init(Cipher.ENCRYPT_MODE, encryptKey);
+        byte[] cipherText = encryptCipher.doFinal(prePaddedPlainText);
+        encryptCipher.update(prePaddedPlainText);
+        encryptCipher.init(Cipher.ENCRYPT_MODE, encryptKey);
+        byte[] cipherText2 = encryptCipher.doFinal(prePaddedPlainText);
+        assertEquals(Arrays.toString(cipherText),
+                     Arrays.toString(cipherText2));
+
+        Cipher decryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
+        decryptCipher.init(Cipher.DECRYPT_MODE, decryptKey);
+        byte[] plainText = decryptCipher.doFinal(cipherText);
+        assertEquals(Arrays.toString(ORIGINAL_PLAIN_TEXT),
+                     Arrays.toString(plainText));
+        decryptCipher.update(prePaddedPlainText);
+        decryptCipher.init(Cipher.DECRYPT_MODE, decryptKey);
+        byte[] plainText2 = decryptCipher.doFinal(cipherText);
+        assertEquals(Arrays.toString(plainText),
+                     Arrays.toString(plainText2));
+    }
+
+    @Test
+    public void testOutputPKCS1Padding() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testOutputPKCS1Padding(provider);
+        }
+    }
+
+    private void testOutputPKCS1Padding(String provider) throws Exception {
+        // Type 1 is for signatures (PrivateKey to "encrypt")
+        testOutputPKCS1Padding(provider, (byte) 1, getDecryptKey("RSA"), getEncryptKey("RSA"));
+        // Type 2 is for enciphering (PublicKey to "encrypt")
+        testOutputPKCS1Padding(provider, (byte) 2, getEncryptKey("RSA"), getDecryptKey("RSA"));
+    }
+
+    private void testOutputPKCS1Padding(String provider, byte expectedBlockType, Key encryptKey, Key decryptKey) throws Exception {
+        Cipher encryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
+        encryptCipher.init(Cipher.ENCRYPT_MODE, encryptKey);
+        byte[] cipherText = encryptCipher.doFinal(ORIGINAL_PLAIN_TEXT);
+        Cipher decryptCipher = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+        decryptCipher.init(Cipher.DECRYPT_MODE, decryptKey);
+        byte[] plainText = decryptCipher.doFinal(cipherText);
+        assertPadding(provider, expectedBlockType, ORIGINAL_PLAIN_TEXT, plainText);
+    }
+
+    private void assertPadding(String provider, byte expectedBlockType, byte[] expectedData, byte[] actualDataWithPadding) {
+        assertNotNull(provider, actualDataWithPadding);
+        int expectedOutputSize = getExpectedOutputSize("RSA", Cipher.DECRYPT_MODE, provider);
+        assertEquals(provider, expectedOutputSize, actualDataWithPadding.length);
+        int expectedBlockTypeOffset;
+        if (provider.equals("BC")) {
+            // BC strips the leading 0 for us on decrypt even when NoPadding is specified...
+            expectedBlockTypeOffset = 0;
+        } else {
+            expectedBlockTypeOffset = 1;
+            assertEquals(provider, 0, actualDataWithPadding[0]);
+        }
+        byte actualBlockType = actualDataWithPadding[expectedBlockTypeOffset];
+        assertEquals(provider, expectedBlockType, actualBlockType);
+        int actualDataOffset = actualDataWithPadding.length - expectedData.length;
+        if (actualBlockType == 1) {
+            int expectedDataOffset = expectedBlockTypeOffset + 1;
+            for (int i = expectedDataOffset; i < actualDataOffset - 1; i++) {
+                assertEquals(provider, (byte) 0xFF, actualDataWithPadding[i]);
+            }
+        }
+        assertEquals(provider, 0x00, actualDataWithPadding[actualDataOffset-1]);
+        byte[] actualData = new byte[expectedData.length];
+        System.arraycopy(actualDataWithPadding, actualDataOffset, actualData, 0, actualData.length);
+        assertEquals(provider, Arrays.toString(expectedData), Arrays.toString(actualData));
+    }
+
+    @Test
+    public void testCipherInitWithCertificate () throws Exception {
+        // no key usage specified, everything is fine
+        assertCipherInitWithKeyUsage(0,                         true,  true, true,  true);
+
+        // common case is that encrypt/wrap is prohibited when special usage is specified
+        assertCipherInitWithKeyUsage(KeyUsage.digitalSignature, false, true, false, true);
+        assertCipherInitWithKeyUsage(KeyUsage.nonRepudiation,   false, true, false, true);
+        assertCipherInitWithKeyUsage(KeyUsage.keyAgreement,     false, true, false, true);
+        assertCipherInitWithKeyUsage(KeyUsage.keyCertSign,      false, true, false, true);
+        assertCipherInitWithKeyUsage(KeyUsage.cRLSign,          false, true, false, true);
+
+        // Note they encipherOnly/decipherOnly don't have to do with
+        // ENCRYPT_MODE or DECRYPT_MODE, but restrict usage relative
+        // to keyAgreement. There is not a *_MODE option that
+        // corresponds to this in Cipher, the RI does not enforce
+        // anything in Cipher.
+        // http://code.google.com/p/android/issues/detail?id=12955
+        assertCipherInitWithKeyUsage(KeyUsage.encipherOnly,     false, true, false, true);
+        assertCipherInitWithKeyUsage(KeyUsage.decipherOnly,     false, true, false, true);
+        assertCipherInitWithKeyUsage(KeyUsage.keyAgreement | KeyUsage.encipherOnly,
+                                                                false, true, false, true);
+        assertCipherInitWithKeyUsage(KeyUsage.keyAgreement | KeyUsage.decipherOnly,
+                                                                false, true, false, true);
+
+        // except when wrapping a key is specifically allowed or
+        assertCipherInitWithKeyUsage(KeyUsage.keyEncipherment,  false, true, true,  true);
+        // except when wrapping data encryption is specifically allowed
+        assertCipherInitWithKeyUsage(KeyUsage.dataEncipherment, true,  true, false, true);
+    }
+
+    private void assertCipherInitWithKeyUsage (int keyUsage,
+                                               boolean allowEncrypt,
+                                               boolean allowDecrypt,
+                                               boolean allowWrap,
+                                               boolean allowUnwrap) throws Exception {
+        Certificate certificate = certificateWithKeyUsage(keyUsage);
+        assertCipherInitWithKeyUsage(certificate, allowEncrypt, Cipher.ENCRYPT_MODE);
+        assertCipherInitWithKeyUsage(certificate, allowDecrypt, Cipher.DECRYPT_MODE);
+        assertCipherInitWithKeyUsage(certificate, allowWrap,    Cipher.WRAP_MODE);
+        assertCipherInitWithKeyUsage(certificate, allowUnwrap,  Cipher.UNWRAP_MODE);
+    }
+
+    private void assertCipherInitWithKeyUsage(Certificate certificate,
+                                              boolean allowMode,
+                                              int mode) throws Exception {
+        Cipher cipher = Cipher.getInstance("RSA");
+        if (allowMode) {
+            cipher.init(mode, certificate);
+        } else {
+            try {
+                cipher.init(mode, certificate);
+                String modeString;
+                switch (mode) {
+                    case Cipher.ENCRYPT_MODE:
+                        modeString = "ENCRYPT_MODE";
+                        break;
+                    case Cipher.DECRYPT_MODE:
+                        modeString = "DECRYPT_MODE";
+                        break;
+                    case Cipher.WRAP_MODE:
+                        modeString = "WRAP_MODE";
+                        break;
+                    case Cipher.UNWRAP_MODE:
+                        modeString = "UNWRAP_MODE";
+                        break;
+                    default:
+                        throw new AssertionError("Unknown Cipher.*_MODE " + mode);
+                }
+                fail("Should have had InvalidKeyException for " + modeString
+                     + " for " + certificate);
+            } catch (InvalidKeyException expected) {
+            }
+        }
+    }
+
+    private Certificate certificateWithKeyUsage(int keyUsage) throws Exception {
+        // note the rare usage of non-zero keyUsage
+        return new TestKeyStore.Builder()
+                .aliasPrefix("rsa-dsa-ec")
+                .keyUsage(keyUsage)
+                .build()
+                .getPrivateKey("RSA", "RSA").getCertificate();
+    }
+
+    /*
+     * Test vectors generated with this private key:
+     *
+     * -----BEGIN RSA PRIVATE KEY-----
+     * MIIEpAIBAAKCAQEA4Ec+irjyKE/rnnQv+XSPoRjtmGM8kvUq63ouvg075gMpvnZq
+     * 0Q62pRXQ0s/ZvqeTDwwwZTeJn3lYzT6FsB+IGFJNMSWEqUslHjYltUFB7b/uGYgI
+     * 4buX/Hy0m56qr2jpyY19DtxTu8D6ADQ1bWMF+7zDxwAUBThqu8hzyw8+90JfPTPf
+     * ezFa4DbSoLZq/UdQOxab8247UWJRW3Ff2oPeryxYrrmr+zCXw8yd2dvl7ylsF2E5
+     * Ao6KZx5jBW1F9AGI0sQTNJCEXeUsJTTpxrJHjAe9rpKII7YtBmx3cPn2Pz26JH9T
+     * CER0e+eqqF2FO4vSRKzsPePImrRkU6tNJMOsaQIDAQABAoIBADd4R3al8XaY9ayW
+     * DfuDobZ1ZOZIvQWXz4q4CHGG8macJ6nsvdSA8Bl6gNBzCebGqW+SUzHlf4tKxvTU
+     * XtpFojJpwJ/EKMB6Tm7fc4oV3sl/q9Lyu0ehTyDqcvz+TDbgGtp3vRN82NTaELsW
+     * LpSkZilx8XX5hfoYjwVsuX7igW9Dq503R2Ekhs2owWGWwwgYqZXshdOEZ3kSZ7O/
+     * IfJzcQppJYYldoQcW2cSwS1L0govMpmtt8E12l6VFavadufK8qO+gFUdBzt4vxFi
+     * xIrSt/R0OgI47k0lL31efmUzzK5kzLOTYAdaL9HgNOw65c6cQIzL8OJeQRQCFoez
+     * 3UdUroECgYEA9UGIS8Nzeyki1BGe9F4t7izUy7dfRVBaFXqlAJ+Zxzot8HJKxGAk
+     * MGMy6omBd2NFRl3G3x4KbxQK/ztzluaomUrF2qloc0cv43dJ0U6z4HXmKdvrNYMz
+     * im82SdCiZUp6Qv2atr+krE1IHTkLsimwZL3DEcwb4bYxidp8QM3s8rECgYEA6hp0
+     * LduIHO23KIyH442GjdekCdFaQ/RF1Td6C1cx3b/KLa8oqOE81cCvzsM0fXSjniNa
+     * PNljPydN4rlPkt9DgzkR2enxz1jyfeLgj/RZZMcg0+whOdx8r8kSlTzeyy81Wi4s
+     * NaUPrXVMs7IxZkJLo7bjESoriYw4xcFe2yOGkzkCgYBRgo8exv2ZYCmQG68dfjN7
+     * pfCvJ+mE6tiVrOYr199O5FoiQInyzBUa880XP84EdLywTzhqLNzA4ANrokGfVFeS
+     * YtRxAL6TGYSj76Bb7PFBV03AebOpXEqD5sQ/MhTW3zLVEt4ZgIXlMeYWuD/X3Z0f
+     * TiYHwzM9B8VdEH0dOJNYcQKBgQDbT7UPUN6O21P/NMgJMYigUShn2izKBIl3WeWH
+     * wkQBDa+GZNWegIPRbBZHiTAfZ6nweAYNg0oq29NnV1toqKhCwrAqibPzH8zsiiL+
+     * OVeVxcbHQitOXXSh6ajzDndZufwtY5wfFWc+hOk6XvFQb0MVODw41Fy9GxQEj0ch
+     * 3IIyYQKBgQDYEUWTr0FfthLb8ZI3ENVNB0hiBadqO0MZSWjA3/HxHvD2GkozfV/T
+     * dBu8lkDkR7i2tsR8OsEgQ1fTsMVbqShr2nP2KSlvX6kUbYl2NX08dR51FIaWpAt0
+     * aFyCzjCQLWOdck/yTV4ulAfuNO3tLjtN9lqpvP623yjQe6aQPxZXaA==
+     * -----END RSA PRIVATE KEY-----
+     *
+     */
+
+    private static final BigInteger RSA_2048_modulus = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0xe0, (byte) 0x47, (byte) 0x3e, (byte) 0x8a, (byte) 0xb8, (byte) 0xf2, (byte) 0x28,
+        (byte) 0x4f, (byte) 0xeb, (byte) 0x9e, (byte) 0x74, (byte) 0x2f, (byte) 0xf9, (byte) 0x74, (byte) 0x8f,
+        (byte) 0xa1, (byte) 0x18, (byte) 0xed, (byte) 0x98, (byte) 0x63, (byte) 0x3c, (byte) 0x92, (byte) 0xf5,
+        (byte) 0x2a, (byte) 0xeb, (byte) 0x7a, (byte) 0x2e, (byte) 0xbe, (byte) 0x0d, (byte) 0x3b, (byte) 0xe6,
+        (byte) 0x03, (byte) 0x29, (byte) 0xbe, (byte) 0x76, (byte) 0x6a, (byte) 0xd1, (byte) 0x0e, (byte) 0xb6,
+        (byte) 0xa5, (byte) 0x15, (byte) 0xd0, (byte) 0xd2, (byte) 0xcf, (byte) 0xd9, (byte) 0xbe, (byte) 0xa7,
+        (byte) 0x93, (byte) 0x0f, (byte) 0x0c, (byte) 0x30, (byte) 0x65, (byte) 0x37, (byte) 0x89, (byte) 0x9f,
+        (byte) 0x79, (byte) 0x58, (byte) 0xcd, (byte) 0x3e, (byte) 0x85, (byte) 0xb0, (byte) 0x1f, (byte) 0x88,
+        (byte) 0x18, (byte) 0x52, (byte) 0x4d, (byte) 0x31, (byte) 0x25, (byte) 0x84, (byte) 0xa9, (byte) 0x4b,
+        (byte) 0x25, (byte) 0x1e, (byte) 0x36, (byte) 0x25, (byte) 0xb5, (byte) 0x41, (byte) 0x41, (byte) 0xed,
+        (byte) 0xbf, (byte) 0xee, (byte) 0x19, (byte) 0x88, (byte) 0x08, (byte) 0xe1, (byte) 0xbb, (byte) 0x97,
+        (byte) 0xfc, (byte) 0x7c, (byte) 0xb4, (byte) 0x9b, (byte) 0x9e, (byte) 0xaa, (byte) 0xaf, (byte) 0x68,
+        (byte) 0xe9, (byte) 0xc9, (byte) 0x8d, (byte) 0x7d, (byte) 0x0e, (byte) 0xdc, (byte) 0x53, (byte) 0xbb,
+        (byte) 0xc0, (byte) 0xfa, (byte) 0x00, (byte) 0x34, (byte) 0x35, (byte) 0x6d, (byte) 0x63, (byte) 0x05,
+        (byte) 0xfb, (byte) 0xbc, (byte) 0xc3, (byte) 0xc7, (byte) 0x00, (byte) 0x14, (byte) 0x05, (byte) 0x38,
+        (byte) 0x6a, (byte) 0xbb, (byte) 0xc8, (byte) 0x73, (byte) 0xcb, (byte) 0x0f, (byte) 0x3e, (byte) 0xf7,
+        (byte) 0x42, (byte) 0x5f, (byte) 0x3d, (byte) 0x33, (byte) 0xdf, (byte) 0x7b, (byte) 0x31, (byte) 0x5a,
+        (byte) 0xe0, (byte) 0x36, (byte) 0xd2, (byte) 0xa0, (byte) 0xb6, (byte) 0x6a, (byte) 0xfd, (byte) 0x47,
+        (byte) 0x50, (byte) 0x3b, (byte) 0x16, (byte) 0x9b, (byte) 0xf3, (byte) 0x6e, (byte) 0x3b, (byte) 0x51,
+        (byte) 0x62, (byte) 0x51, (byte) 0x5b, (byte) 0x71, (byte) 0x5f, (byte) 0xda, (byte) 0x83, (byte) 0xde,
+        (byte) 0xaf, (byte) 0x2c, (byte) 0x58, (byte) 0xae, (byte) 0xb9, (byte) 0xab, (byte) 0xfb, (byte) 0x30,
+        (byte) 0x97, (byte) 0xc3, (byte) 0xcc, (byte) 0x9d, (byte) 0xd9, (byte) 0xdb, (byte) 0xe5, (byte) 0xef,
+        (byte) 0x29, (byte) 0x6c, (byte) 0x17, (byte) 0x61, (byte) 0x39, (byte) 0x02, (byte) 0x8e, (byte) 0x8a,
+        (byte) 0x67, (byte) 0x1e, (byte) 0x63, (byte) 0x05, (byte) 0x6d, (byte) 0x45, (byte) 0xf4, (byte) 0x01,
+        (byte) 0x88, (byte) 0xd2, (byte) 0xc4, (byte) 0x13, (byte) 0x34, (byte) 0x90, (byte) 0x84, (byte) 0x5d,
+        (byte) 0xe5, (byte) 0x2c, (byte) 0x25, (byte) 0x34, (byte) 0xe9, (byte) 0xc6, (byte) 0xb2, (byte) 0x47,
+        (byte) 0x8c, (byte) 0x07, (byte) 0xbd, (byte) 0xae, (byte) 0x92, (byte) 0x88, (byte) 0x23, (byte) 0xb6,
+        (byte) 0x2d, (byte) 0x06, (byte) 0x6c, (byte) 0x77, (byte) 0x70, (byte) 0xf9, (byte) 0xf6, (byte) 0x3f,
+        (byte) 0x3d, (byte) 0xba, (byte) 0x24, (byte) 0x7f, (byte) 0x53, (byte) 0x08, (byte) 0x44, (byte) 0x74,
+        (byte) 0x7b, (byte) 0xe7, (byte) 0xaa, (byte) 0xa8, (byte) 0x5d, (byte) 0x85, (byte) 0x3b, (byte) 0x8b,
+        (byte) 0xd2, (byte) 0x44, (byte) 0xac, (byte) 0xec, (byte) 0x3d, (byte) 0xe3, (byte) 0xc8, (byte) 0x9a,
+        (byte) 0xb4, (byte) 0x64, (byte) 0x53, (byte) 0xab, (byte) 0x4d, (byte) 0x24, (byte) 0xc3, (byte) 0xac,
+        (byte) 0x69,
+    });
+
+    private static final BigInteger RSA_2048_privateExponent = new BigInteger(new byte[] {
+        (byte) 0x37, (byte) 0x78, (byte) 0x47, (byte) 0x76, (byte) 0xa5, (byte) 0xf1, (byte) 0x76, (byte) 0x98,
+        (byte) 0xf5, (byte) 0xac, (byte) 0x96, (byte) 0x0d, (byte) 0xfb, (byte) 0x83, (byte) 0xa1, (byte) 0xb6,
+        (byte) 0x75, (byte) 0x64, (byte) 0xe6, (byte) 0x48, (byte) 0xbd, (byte) 0x05, (byte) 0x97, (byte) 0xcf,
+        (byte) 0x8a, (byte) 0xb8, (byte) 0x08, (byte) 0x71, (byte) 0x86, (byte) 0xf2, (byte) 0x66, (byte) 0x9c,
+        (byte) 0x27, (byte) 0xa9, (byte) 0xec, (byte) 0xbd, (byte) 0xd4, (byte) 0x80, (byte) 0xf0, (byte) 0x19,
+        (byte) 0x7a, (byte) 0x80, (byte) 0xd0, (byte) 0x73, (byte) 0x09, (byte) 0xe6, (byte) 0xc6, (byte) 0xa9,
+        (byte) 0x6f, (byte) 0x92, (byte) 0x53, (byte) 0x31, (byte) 0xe5, (byte) 0x7f, (byte) 0x8b, (byte) 0x4a,
+        (byte) 0xc6, (byte) 0xf4, (byte) 0xd4, (byte) 0x5e, (byte) 0xda, (byte) 0x45, (byte) 0xa2, (byte) 0x32,
+        (byte) 0x69, (byte) 0xc0, (byte) 0x9f, (byte) 0xc4, (byte) 0x28, (byte) 0xc0, (byte) 0x7a, (byte) 0x4e,
+        (byte) 0x6e, (byte) 0xdf, (byte) 0x73, (byte) 0x8a, (byte) 0x15, (byte) 0xde, (byte) 0xc9, (byte) 0x7f,
+        (byte) 0xab, (byte) 0xd2, (byte) 0xf2, (byte) 0xbb, (byte) 0x47, (byte) 0xa1, (byte) 0x4f, (byte) 0x20,
+        (byte) 0xea, (byte) 0x72, (byte) 0xfc, (byte) 0xfe, (byte) 0x4c, (byte) 0x36, (byte) 0xe0, (byte) 0x1a,
+        (byte) 0xda, (byte) 0x77, (byte) 0xbd, (byte) 0x13, (byte) 0x7c, (byte) 0xd8, (byte) 0xd4, (byte) 0xda,
+        (byte) 0x10, (byte) 0xbb, (byte) 0x16, (byte) 0x2e, (byte) 0x94, (byte) 0xa4, (byte) 0x66, (byte) 0x29,
+        (byte) 0x71, (byte) 0xf1, (byte) 0x75, (byte) 0xf9, (byte) 0x85, (byte) 0xfa, (byte) 0x18, (byte) 0x8f,
+        (byte) 0x05, (byte) 0x6c, (byte) 0xb9, (byte) 0x7e, (byte) 0xe2, (byte) 0x81, (byte) 0x6f, (byte) 0x43,
+        (byte) 0xab, (byte) 0x9d, (byte) 0x37, (byte) 0x47, (byte) 0x61, (byte) 0x24, (byte) 0x86, (byte) 0xcd,
+        (byte) 0xa8, (byte) 0xc1, (byte) 0x61, (byte) 0x96, (byte) 0xc3, (byte) 0x08, (byte) 0x18, (byte) 0xa9,
+        (byte) 0x95, (byte) 0xec, (byte) 0x85, (byte) 0xd3, (byte) 0x84, (byte) 0x67, (byte) 0x79, (byte) 0x12,
+        (byte) 0x67, (byte) 0xb3, (byte) 0xbf, (byte) 0x21, (byte) 0xf2, (byte) 0x73, (byte) 0x71, (byte) 0x0a,
+        (byte) 0x69, (byte) 0x25, (byte) 0x86, (byte) 0x25, (byte) 0x76, (byte) 0x84, (byte) 0x1c, (byte) 0x5b,
+        (byte) 0x67, (byte) 0x12, (byte) 0xc1, (byte) 0x2d, (byte) 0x4b, (byte) 0xd2, (byte) 0x0a, (byte) 0x2f,
+        (byte) 0x32, (byte) 0x99, (byte) 0xad, (byte) 0xb7, (byte) 0xc1, (byte) 0x35, (byte) 0xda, (byte) 0x5e,
+        (byte) 0x95, (byte) 0x15, (byte) 0xab, (byte) 0xda, (byte) 0x76, (byte) 0xe7, (byte) 0xca, (byte) 0xf2,
+        (byte) 0xa3, (byte) 0xbe, (byte) 0x80, (byte) 0x55, (byte) 0x1d, (byte) 0x07, (byte) 0x3b, (byte) 0x78,
+        (byte) 0xbf, (byte) 0x11, (byte) 0x62, (byte) 0xc4, (byte) 0x8a, (byte) 0xd2, (byte) 0xb7, (byte) 0xf4,
+        (byte) 0x74, (byte) 0x3a, (byte) 0x02, (byte) 0x38, (byte) 0xee, (byte) 0x4d, (byte) 0x25, (byte) 0x2f,
+        (byte) 0x7d, (byte) 0x5e, (byte) 0x7e, (byte) 0x65, (byte) 0x33, (byte) 0xcc, (byte) 0xae, (byte) 0x64,
+        (byte) 0xcc, (byte) 0xb3, (byte) 0x93, (byte) 0x60, (byte) 0x07, (byte) 0x5a, (byte) 0x2f, (byte) 0xd1,
+        (byte) 0xe0, (byte) 0x34, (byte) 0xec, (byte) 0x3a, (byte) 0xe5, (byte) 0xce, (byte) 0x9c, (byte) 0x40,
+        (byte) 0x8c, (byte) 0xcb, (byte) 0xf0, (byte) 0xe2, (byte) 0x5e, (byte) 0x41, (byte) 0x14, (byte) 0x02,
+        (byte) 0x16, (byte) 0x87, (byte) 0xb3, (byte) 0xdd, (byte) 0x47, (byte) 0x54, (byte) 0xae, (byte) 0x81,
+    });
+
+    private static final BigInteger RSA_2048_publicExponent = new BigInteger(new byte[] {
+        (byte) 0x01, (byte) 0x00, (byte) 0x01,
+    });
+
+    private static final BigInteger RSA_2048_primeP = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0xf5, (byte) 0x41, (byte) 0x88, (byte) 0x4b, (byte) 0xc3, (byte) 0x73, (byte) 0x7b,
+        (byte) 0x29, (byte) 0x22, (byte) 0xd4, (byte) 0x11, (byte) 0x9e, (byte) 0xf4, (byte) 0x5e, (byte) 0x2d,
+        (byte) 0xee, (byte) 0x2c, (byte) 0xd4, (byte) 0xcb, (byte) 0xb7, (byte) 0x5f, (byte) 0x45, (byte) 0x50,
+        (byte) 0x5a, (byte) 0x15, (byte) 0x7a, (byte) 0xa5, (byte) 0x00, (byte) 0x9f, (byte) 0x99, (byte) 0xc7,
+        (byte) 0x3a, (byte) 0x2d, (byte) 0xf0, (byte) 0x72, (byte) 0x4a, (byte) 0xc4, (byte) 0x60, (byte) 0x24,
+        (byte) 0x30, (byte) 0x63, (byte) 0x32, (byte) 0xea, (byte) 0x89, (byte) 0x81, (byte) 0x77, (byte) 0x63,
+        (byte) 0x45, (byte) 0x46, (byte) 0x5d, (byte) 0xc6, (byte) 0xdf, (byte) 0x1e, (byte) 0x0a, (byte) 0x6f,
+        (byte) 0x14, (byte) 0x0a, (byte) 0xff, (byte) 0x3b, (byte) 0x73, (byte) 0x96, (byte) 0xe6, (byte) 0xa8,
+        (byte) 0x99, (byte) 0x4a, (byte) 0xc5, (byte) 0xda, (byte) 0xa9, (byte) 0x68, (byte) 0x73, (byte) 0x47,
+        (byte) 0x2f, (byte) 0xe3, (byte) 0x77, (byte) 0x49, (byte) 0xd1, (byte) 0x4e, (byte) 0xb3, (byte) 0xe0,
+        (byte) 0x75, (byte) 0xe6, (byte) 0x29, (byte) 0xdb, (byte) 0xeb, (byte) 0x35, (byte) 0x83, (byte) 0x33,
+        (byte) 0x8a, (byte) 0x6f, (byte) 0x36, (byte) 0x49, (byte) 0xd0, (byte) 0xa2, (byte) 0x65, (byte) 0x4a,
+        (byte) 0x7a, (byte) 0x42, (byte) 0xfd, (byte) 0x9a, (byte) 0xb6, (byte) 0xbf, (byte) 0xa4, (byte) 0xac,
+        (byte) 0x4d, (byte) 0x48, (byte) 0x1d, (byte) 0x39, (byte) 0x0b, (byte) 0xb2, (byte) 0x29, (byte) 0xb0,
+        (byte) 0x64, (byte) 0xbd, (byte) 0xc3, (byte) 0x11, (byte) 0xcc, (byte) 0x1b, (byte) 0xe1, (byte) 0xb6,
+        (byte) 0x31, (byte) 0x89, (byte) 0xda, (byte) 0x7c, (byte) 0x40, (byte) 0xcd, (byte) 0xec, (byte) 0xf2,
+        (byte) 0xb1,
+    });
+
+    private static final BigInteger RSA_2048_primeQ = new BigInteger(new byte[] {
+        (byte) 0x00, (byte) 0xea, (byte) 0x1a, (byte) 0x74, (byte) 0x2d, (byte) 0xdb, (byte) 0x88, (byte) 0x1c,
+        (byte) 0xed, (byte) 0xb7, (byte) 0x28, (byte) 0x8c, (byte) 0x87, (byte) 0xe3, (byte) 0x8d, (byte) 0x86,
+        (byte) 0x8d, (byte) 0xd7, (byte) 0xa4, (byte) 0x09, (byte) 0xd1, (byte) 0x5a, (byte) 0x43, (byte) 0xf4,
+        (byte) 0x45, (byte) 0xd5, (byte) 0x37, (byte) 0x7a, (byte) 0x0b, (byte) 0x57, (byte) 0x31, (byte) 0xdd,
+        (byte) 0xbf, (byte) 0xca, (byte) 0x2d, (byte) 0xaf, (byte) 0x28, (byte) 0xa8, (byte) 0xe1, (byte) 0x3c,
+        (byte) 0xd5, (byte) 0xc0, (byte) 0xaf, (byte) 0xce, (byte) 0xc3, (byte) 0x34, (byte) 0x7d, (byte) 0x74,
+        (byte) 0xa3, (byte) 0x9e, (byte) 0x23, (byte) 0x5a, (byte) 0x3c, (byte) 0xd9, (byte) 0x63, (byte) 0x3f,
+        (byte) 0x27, (byte) 0x4d, (byte) 0xe2, (byte) 0xb9, (byte) 0x4f, (byte) 0x92, (byte) 0xdf, (byte) 0x43,
+        (byte) 0x83, (byte) 0x39, (byte) 0x11, (byte) 0xd9, (byte) 0xe9, (byte) 0xf1, (byte) 0xcf, (byte) 0x58,
+        (byte) 0xf2, (byte) 0x7d, (byte) 0xe2, (byte) 0xe0, (byte) 0x8f, (byte) 0xf4, (byte) 0x59, (byte) 0x64,
+        (byte) 0xc7, (byte) 0x20, (byte) 0xd3, (byte) 0xec, (byte) 0x21, (byte) 0x39, (byte) 0xdc, (byte) 0x7c,
+        (byte) 0xaf, (byte) 0xc9, (byte) 0x12, (byte) 0x95, (byte) 0x3c, (byte) 0xde, (byte) 0xcb, (byte) 0x2f,
+        (byte) 0x35, (byte) 0x5a, (byte) 0x2e, (byte) 0x2c, (byte) 0x35, (byte) 0xa5, (byte) 0x0f, (byte) 0xad,
+        (byte) 0x75, (byte) 0x4c, (byte) 0xb3, (byte) 0xb2, (byte) 0x31, (byte) 0x66, (byte) 0x42, (byte) 0x4b,
+        (byte) 0xa3, (byte) 0xb6, (byte) 0xe3, (byte) 0x11, (byte) 0x2a, (byte) 0x2b, (byte) 0x89, (byte) 0x8c,
+        (byte) 0x38, (byte) 0xc5, (byte) 0xc1, (byte) 0x5e, (byte) 0xdb, (byte) 0x23, (byte) 0x86, (byte) 0x93,
+        (byte) 0x39,
+    });
+
+    private static final BigInteger RSA_2048_primeExponentP = new BigInteger(1, new byte[] {
+        (byte) 0x51, (byte) 0x82, (byte) 0x8F, (byte) 0x1E, (byte) 0xC6, (byte) 0xFD, (byte) 0x99, (byte) 0x60,
+        (byte) 0x29, (byte) 0x90, (byte) 0x1B, (byte) 0xAF, (byte) 0x1D, (byte) 0x7E, (byte) 0x33, (byte) 0x7B,
+        (byte) 0xA5, (byte) 0xF0, (byte) 0xAF, (byte) 0x27, (byte) 0xE9, (byte) 0x84, (byte) 0xEA, (byte) 0xD8,
+        (byte) 0x95, (byte) 0xAC, (byte) 0xE6, (byte) 0x2B, (byte) 0xD7, (byte) 0xDF, (byte) 0x4E, (byte) 0xE4,
+        (byte) 0x5A, (byte) 0x22, (byte) 0x40, (byte) 0x89, (byte) 0xF2, (byte) 0xCC, (byte) 0x15, (byte) 0x1A,
+        (byte) 0xF3, (byte) 0xCD, (byte) 0x17, (byte) 0x3F, (byte) 0xCE, (byte) 0x04, (byte) 0x74, (byte) 0xBC,
+        (byte) 0xB0, (byte) 0x4F, (byte) 0x38, (byte) 0x6A, (byte) 0x2C, (byte) 0xDC, (byte) 0xC0, (byte) 0xE0,
+        (byte) 0x03, (byte) 0x6B, (byte) 0xA2, (byte) 0x41, (byte) 0x9F, (byte) 0x54, (byte) 0x57, (byte) 0x92,
+        (byte) 0x62, (byte) 0xD4, (byte) 0x71, (byte) 0x00, (byte) 0xBE, (byte) 0x93, (byte) 0x19, (byte) 0x84,
+        (byte) 0xA3, (byte) 0xEF, (byte) 0xA0, (byte) 0x5B, (byte) 0xEC, (byte) 0xF1, (byte) 0x41, (byte) 0x57,
+        (byte) 0x4D, (byte) 0xC0, (byte) 0x79, (byte) 0xB3, (byte) 0xA9, (byte) 0x5C, (byte) 0x4A, (byte) 0x83,
+        (byte) 0xE6, (byte) 0xC4, (byte) 0x3F, (byte) 0x32, (byte) 0x14, (byte) 0xD6, (byte) 0xDF, (byte) 0x32,
+        (byte) 0xD5, (byte) 0x12, (byte) 0xDE, (byte) 0x19, (byte) 0x80, (byte) 0x85, (byte) 0xE5, (byte) 0x31,
+        (byte) 0xE6, (byte) 0x16, (byte) 0xB8, (byte) 0x3F, (byte) 0xD7, (byte) 0xDD, (byte) 0x9D, (byte) 0x1F,
+        (byte) 0x4E, (byte) 0x26, (byte) 0x07, (byte) 0xC3, (byte) 0x33, (byte) 0x3D, (byte) 0x07, (byte) 0xC5,
+        (byte) 0x5D, (byte) 0x10, (byte) 0x7D, (byte) 0x1D, (byte) 0x38, (byte) 0x93, (byte) 0x58, (byte) 0x71,
+    });
+
+    private static final BigInteger RSA_2048_primeExponentQ = new BigInteger(1, new byte[] {
+        (byte) 0xDB, (byte) 0x4F, (byte) 0xB5, (byte) 0x0F, (byte) 0x50, (byte) 0xDE, (byte) 0x8E, (byte) 0xDB,
+        (byte) 0x53, (byte) 0xFF, (byte) 0x34, (byte) 0xC8, (byte) 0x09, (byte) 0x31, (byte) 0x88, (byte) 0xA0,
+        (byte) 0x51, (byte) 0x28, (byte) 0x67, (byte) 0xDA, (byte) 0x2C, (byte) 0xCA, (byte) 0x04, (byte) 0x89,
+        (byte) 0x77, (byte) 0x59, (byte) 0xE5, (byte) 0x87, (byte) 0xC2, (byte) 0x44, (byte) 0x01, (byte) 0x0D,
+        (byte) 0xAF, (byte) 0x86, (byte) 0x64, (byte) 0xD5, (byte) 0x9E, (byte) 0x80, (byte) 0x83, (byte) 0xD1,
+        (byte) 0x6C, (byte) 0x16, (byte) 0x47, (byte) 0x89, (byte) 0x30, (byte) 0x1F, (byte) 0x67, (byte) 0xA9,
+        (byte) 0xF0, (byte) 0x78, (byte) 0x06, (byte) 0x0D, (byte) 0x83, (byte) 0x4A, (byte) 0x2A, (byte) 0xDB,
+        (byte) 0xD3, (byte) 0x67, (byte) 0x57, (byte) 0x5B, (byte) 0x68, (byte) 0xA8, (byte) 0xA8, (byte) 0x42,
+        (byte) 0xC2, (byte) 0xB0, (byte) 0x2A, (byte) 0x89, (byte) 0xB3, (byte) 0xF3, (byte) 0x1F, (byte) 0xCC,
+        (byte) 0xEC, (byte) 0x8A, (byte) 0x22, (byte) 0xFE, (byte) 0x39, (byte) 0x57, (byte) 0x95, (byte) 0xC5,
+        (byte) 0xC6, (byte) 0xC7, (byte) 0x42, (byte) 0x2B, (byte) 0x4E, (byte) 0x5D, (byte) 0x74, (byte) 0xA1,
+        (byte) 0xE9, (byte) 0xA8, (byte) 0xF3, (byte) 0x0E, (byte) 0x77, (byte) 0x59, (byte) 0xB9, (byte) 0xFC,
+        (byte) 0x2D, (byte) 0x63, (byte) 0x9C, (byte) 0x1F, (byte) 0x15, (byte) 0x67, (byte) 0x3E, (byte) 0x84,
+        (byte) 0xE9, (byte) 0x3A, (byte) 0x5E, (byte) 0xF1, (byte) 0x50, (byte) 0x6F, (byte) 0x43, (byte) 0x15,
+        (byte) 0x38, (byte) 0x3C, (byte) 0x38, (byte) 0xD4, (byte) 0x5C, (byte) 0xBD, (byte) 0x1B, (byte) 0x14,
+        (byte) 0x04, (byte) 0x8F, (byte) 0x47, (byte) 0x21, (byte) 0xDC, (byte) 0x82, (byte) 0x32, (byte) 0x61,
+    });
+
+    private static final BigInteger RSA_2048_crtCoefficient = new BigInteger(1, new byte[] {
+        (byte) 0xD8, (byte) 0x11, (byte) 0x45, (byte) 0x93, (byte) 0xAF, (byte) 0x41, (byte) 0x5F, (byte) 0xB6,
+        (byte) 0x12, (byte) 0xDB, (byte) 0xF1, (byte) 0x92, (byte) 0x37, (byte) 0x10, (byte) 0xD5, (byte) 0x4D,
+        (byte) 0x07, (byte) 0x48, (byte) 0x62, (byte) 0x05, (byte) 0xA7, (byte) 0x6A, (byte) 0x3B, (byte) 0x43,
+        (byte) 0x19, (byte) 0x49, (byte) 0x68, (byte) 0xC0, (byte) 0xDF, (byte) 0xF1, (byte) 0xF1, (byte) 0x1E,
+        (byte) 0xF0, (byte) 0xF6, (byte) 0x1A, (byte) 0x4A, (byte) 0x33, (byte) 0x7D, (byte) 0x5F, (byte) 0xD3,
+        (byte) 0x74, (byte) 0x1B, (byte) 0xBC, (byte) 0x96, (byte) 0x40, (byte) 0xE4, (byte) 0x47, (byte) 0xB8,
+        (byte) 0xB6, (byte) 0xB6, (byte) 0xC4, (byte) 0x7C, (byte) 0x3A, (byte) 0xC1, (byte) 0x20, (byte) 0x43,
+        (byte) 0x57, (byte) 0xD3, (byte) 0xB0, (byte) 0xC5, (byte) 0x5B, (byte) 0xA9, (byte) 0x28, (byte) 0x6B,
+        (byte) 0xDA, (byte) 0x73, (byte) 0xF6, (byte) 0x29, (byte) 0x29, (byte) 0x6F, (byte) 0x5F, (byte) 0xA9,
+        (byte) 0x14, (byte) 0x6D, (byte) 0x89, (byte) 0x76, (byte) 0x35, (byte) 0x7D, (byte) 0x3C, (byte) 0x75,
+        (byte) 0x1E, (byte) 0x75, (byte) 0x14, (byte) 0x86, (byte) 0x96, (byte) 0xA4, (byte) 0x0B, (byte) 0x74,
+        (byte) 0x68, (byte) 0x5C, (byte) 0x82, (byte) 0xCE, (byte) 0x30, (byte) 0x90, (byte) 0x2D, (byte) 0x63,
+        (byte) 0x9D, (byte) 0x72, (byte) 0x4F, (byte) 0xF2, (byte) 0x4D, (byte) 0x5E, (byte) 0x2E, (byte) 0x94,
+        (byte) 0x07, (byte) 0xEE, (byte) 0x34, (byte) 0xED, (byte) 0xED, (byte) 0x2E, (byte) 0x3B, (byte) 0x4D,
+        (byte) 0xF6, (byte) 0x5A, (byte) 0xA9, (byte) 0xBC, (byte) 0xFE, (byte) 0xB6, (byte) 0xDF, (byte) 0x28,
+        (byte) 0xD0, (byte) 0x7B, (byte) 0xA6, (byte) 0x90, (byte) 0x3F, (byte) 0x16, (byte) 0x57, (byte) 0x68,
+    });
+
+    /**
+     * Test data is PKCS#1 padded "Android.\n" which can be generated by:
+     * echo "Android." | openssl rsautl -inkey rsa.key -sign | openssl rsautl -inkey rsa.key -raw -verify | recode ../x1
+     */
+    private static final byte[] RSA_2048_Vector1 = new byte[] {
+        (byte) 0x00, (byte) 0x01, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0x00, (byte) 0x41, (byte) 0x6E, (byte) 0x64, (byte) 0x72, (byte) 0x6F,
+        (byte) 0x69, (byte) 0x64, (byte) 0x2E, (byte) 0x0A,
+    };
+
+    /**
+     * This vector is simply "Android.\n" which is too short.
+     */
+    private static final byte[] TooShort_Vector = new byte[] {
+        (byte) 0x41, (byte) 0x6E, (byte) 0x64, (byte) 0x72, (byte) 0x6F, (byte) 0x69,
+        (byte) 0x64, (byte) 0x2E, (byte) 0x0A,
+    };
+
+    /**
+     * This vector is simply "Android.\n" padded with zeros.
+     */
+    private static final byte[] TooShort_Vector_Zero_Padded = new byte[] {
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f,
+        (byte) 0x69, (byte) 0x64, (byte) 0x2e, (byte) 0x0a,
+    };
+
+    /**
+     * openssl rsautl -raw -sign -inkey rsa.key | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] RSA_Vector1_Encrypt_Private = new byte[] {
+        (byte) 0x35, (byte) 0x43, (byte) 0x38, (byte) 0x44, (byte) 0xAD, (byte) 0x3F,
+        (byte) 0x97, (byte) 0x02, (byte) 0xFB, (byte) 0x59, (byte) 0x1F, (byte) 0x4A,
+        (byte) 0x2B, (byte) 0xB9, (byte) 0x06, (byte) 0xEC, (byte) 0x66, (byte) 0xE6,
+        (byte) 0xD2, (byte) 0xC5, (byte) 0x8B, (byte) 0x7B, (byte) 0xE3, (byte) 0x18,
+        (byte) 0xBF, (byte) 0x07, (byte) 0xD6, (byte) 0x01, (byte) 0xF9, (byte) 0xD9,
+        (byte) 0x89, (byte) 0xC4, (byte) 0xDB, (byte) 0x00, (byte) 0x68, (byte) 0xFF,
+        (byte) 0x9B, (byte) 0x43, (byte) 0x90, (byte) 0xF2, (byte) 0xDB, (byte) 0x83,
+        (byte) 0xF4, (byte) 0x7E, (byte) 0xC6, (byte) 0x81, (byte) 0x01, (byte) 0x3A,
+        (byte) 0x0B, (byte) 0xE5, (byte) 0xED, (byte) 0x08, (byte) 0x73, (byte) 0x3E,
+        (byte) 0xE1, (byte) 0x3F, (byte) 0xDF, (byte) 0x1F, (byte) 0x07, (byte) 0x6D,
+        (byte) 0x22, (byte) 0x8D, (byte) 0xCC, (byte) 0x4E, (byte) 0xE3, (byte) 0x9A,
+        (byte) 0xBC, (byte) 0xCC, (byte) 0x8F, (byte) 0x9E, (byte) 0x9B, (byte) 0x02,
+        (byte) 0x48, (byte) 0x00, (byte) 0xAC, (byte) 0x9F, (byte) 0xA4, (byte) 0x8F,
+        (byte) 0x87, (byte) 0xA1, (byte) 0xA8, (byte) 0xE6, (byte) 0x9D, (byte) 0xCD,
+        (byte) 0x8B, (byte) 0x05, (byte) 0xE9, (byte) 0xD2, (byte) 0x05, (byte) 0x8D,
+        (byte) 0xC9, (byte) 0x95, (byte) 0x16, (byte) 0xD0, (byte) 0xCD, (byte) 0x43,
+        (byte) 0x25, (byte) 0x8A, (byte) 0x11, (byte) 0x46, (byte) 0xD7, (byte) 0x74,
+        (byte) 0x4C, (byte) 0xCF, (byte) 0x58, (byte) 0xF9, (byte) 0xA1, (byte) 0x30,
+        (byte) 0x84, (byte) 0x52, (byte) 0xC9, (byte) 0x01, (byte) 0x5F, (byte) 0x24,
+        (byte) 0x4C, (byte) 0xB1, (byte) 0x9F, (byte) 0x7D, (byte) 0x12, (byte) 0x38,
+        (byte) 0x27, (byte) 0x0F, (byte) 0x5E, (byte) 0xFF, (byte) 0xE0, (byte) 0x55,
+        (byte) 0x8B, (byte) 0xA3, (byte) 0xAD, (byte) 0x60, (byte) 0x35, (byte) 0x83,
+        (byte) 0x58, (byte) 0xAF, (byte) 0x99, (byte) 0xDE, (byte) 0x3F, (byte) 0x5D,
+        (byte) 0x80, (byte) 0x80, (byte) 0xFF, (byte) 0x9B, (byte) 0xDE, (byte) 0x5C,
+        (byte) 0xAB, (byte) 0x97, (byte) 0x43, (byte) 0x64, (byte) 0xD9, (byte) 0x9F,
+        (byte) 0xFB, (byte) 0x67, (byte) 0x65, (byte) 0xA5, (byte) 0x99, (byte) 0xE7,
+        (byte) 0xE6, (byte) 0xEB, (byte) 0x05, (byte) 0x95, (byte) 0xFC, (byte) 0x46,
+        (byte) 0x28, (byte) 0x4B, (byte) 0xD8, (byte) 0x8C, (byte) 0xF5, (byte) 0x0A,
+        (byte) 0xEB, (byte) 0x1F, (byte) 0x30, (byte) 0xEA, (byte) 0xE7, (byte) 0x67,
+        (byte) 0x11, (byte) 0x25, (byte) 0xF0, (byte) 0x44, (byte) 0x75, (byte) 0x74,
+        (byte) 0x94, (byte) 0x06, (byte) 0x78, (byte) 0xD0, (byte) 0x21, (byte) 0xF4,
+        (byte) 0x3F, (byte) 0xC8, (byte) 0xC4, (byte) 0x4A, (byte) 0x57, (byte) 0xBE,
+        (byte) 0x02, (byte) 0x3C, (byte) 0x93, (byte) 0xF6, (byte) 0x95, (byte) 0xFB,
+        (byte) 0xD1, (byte) 0x77, (byte) 0x8B, (byte) 0x43, (byte) 0xF0, (byte) 0xB9,
+        (byte) 0x7D, (byte) 0xE0, (byte) 0x32, (byte) 0xE1, (byte) 0x72, (byte) 0xB5,
+        (byte) 0x62, (byte) 0x3F, (byte) 0x86, (byte) 0xC3, (byte) 0xD4, (byte) 0x5F,
+        (byte) 0x5E, (byte) 0x54, (byte) 0x1B, (byte) 0x5B, (byte) 0xE6, (byte) 0x74,
+        (byte) 0xA1, (byte) 0x0B, (byte) 0xE5, (byte) 0x18, (byte) 0xD2, (byte) 0x4F,
+        (byte) 0x93, (byte) 0xF3, (byte) 0x09, (byte) 0x58, (byte) 0xCE, (byte) 0xF0,
+        (byte) 0xA3, (byte) 0x61, (byte) 0xE4, (byte) 0x6E, (byte) 0x46, (byte) 0x45,
+        (byte) 0x89, (byte) 0x50, (byte) 0xBD, (byte) 0x03, (byte) 0x3F, (byte) 0x38,
+        (byte) 0xDA, (byte) 0x5D, (byte) 0xD0, (byte) 0x1B, (byte) 0x1F, (byte) 0xB1,
+        (byte) 0xEE, (byte) 0x89, (byte) 0x59, (byte) 0xC5,
+    };
+
+    private static final byte[] RSA_Vector1_ZeroPadded_Encrypted = new byte[] {
+        (byte) 0x60, (byte) 0x4a, (byte) 0x12, (byte) 0xa3, (byte) 0xa7, (byte) 0x4a,
+        (byte) 0xa4, (byte) 0xbf, (byte) 0x6c, (byte) 0x36, (byte) 0xad, (byte) 0x66,
+        (byte) 0xdf, (byte) 0xce, (byte) 0xf1, (byte) 0xe4, (byte) 0x0f, (byte) 0xd4,
+        (byte) 0x54, (byte) 0x5f, (byte) 0x03, (byte) 0x15, (byte) 0x4b, (byte) 0x9e,
+        (byte) 0xeb, (byte) 0xfe, (byte) 0x9e, (byte) 0x24, (byte) 0xce, (byte) 0x8e,
+        (byte) 0xc3, (byte) 0x36, (byte) 0xa5, (byte) 0x76, (byte) 0xf6, (byte) 0x54,
+        (byte) 0xb7, (byte) 0x84, (byte) 0x48, (byte) 0x2f, (byte) 0xd4, (byte) 0x45,
+        (byte) 0x74, (byte) 0x48, (byte) 0x5f, (byte) 0x08, (byte) 0x4e, (byte) 0x9c,
+        (byte) 0x89, (byte) 0xcc, (byte) 0x34, (byte) 0x40, (byte) 0xb1, (byte) 0x5f,
+        (byte) 0xa7, (byte) 0x0e, (byte) 0x11, (byte) 0x4b, (byte) 0xb5, (byte) 0x94,
+        (byte) 0xbe, (byte) 0x14, (byte) 0xaa, (byte) 0xaa, (byte) 0xe0, (byte) 0x38,
+        (byte) 0x1c, (byte) 0xce, (byte) 0x40, (byte) 0x61, (byte) 0xfc, (byte) 0x08,
+        (byte) 0xcb, (byte) 0x14, (byte) 0x2b, (byte) 0xa6, (byte) 0x54, (byte) 0xdf,
+        (byte) 0x05, (byte) 0x5c, (byte) 0x9b, (byte) 0x4f, (byte) 0x14, (byte) 0x93,
+        (byte) 0xb0, (byte) 0x70, (byte) 0xd9, (byte) 0x32, (byte) 0xdc, (byte) 0x24,
+        (byte) 0xe0, (byte) 0xae, (byte) 0x48, (byte) 0xfc, (byte) 0x53, (byte) 0xee,
+        (byte) 0x7c, (byte) 0x9f, (byte) 0x69, (byte) 0x34, (byte) 0xf4, (byte) 0x76,
+        (byte) 0xee, (byte) 0x67, (byte) 0xb2, (byte) 0xa7, (byte) 0x33, (byte) 0x1c,
+        (byte) 0x47, (byte) 0xff, (byte) 0x5c, (byte) 0xf0, (byte) 0xb8, (byte) 0x04,
+        (byte) 0x2c, (byte) 0xfd, (byte) 0xe2, (byte) 0xb1, (byte) 0x4a, (byte) 0x0a,
+        (byte) 0x69, (byte) 0x1c, (byte) 0x80, (byte) 0x2b, (byte) 0xb4, (byte) 0x50,
+        (byte) 0x65, (byte) 0x5c, (byte) 0x76, (byte) 0x78, (byte) 0x9a, (byte) 0x0c,
+        (byte) 0x05, (byte) 0x62, (byte) 0xf0, (byte) 0xc4, (byte) 0x1c, (byte) 0x38,
+        (byte) 0x15, (byte) 0xd0, (byte) 0xe2, (byte) 0x5a, (byte) 0x3d, (byte) 0xb6,
+        (byte) 0xe0, (byte) 0x88, (byte) 0x85, (byte) 0xd1, (byte) 0x4f, (byte) 0x7e,
+        (byte) 0xfc, (byte) 0x77, (byte) 0x0d, (byte) 0x2a, (byte) 0x45, (byte) 0xd5,
+        (byte) 0xf8, (byte) 0x3c, (byte) 0x7b, (byte) 0x2d, (byte) 0x1b, (byte) 0x82,
+        (byte) 0xfe, (byte) 0x58, (byte) 0x22, (byte) 0x47, (byte) 0x06, (byte) 0x58,
+        (byte) 0x8b, (byte) 0x4f, (byte) 0xfb, (byte) 0x9b, (byte) 0x1c, (byte) 0x70,
+        (byte) 0x36, (byte) 0x12, (byte) 0x04, (byte) 0x17, (byte) 0x47, (byte) 0x8a,
+        (byte) 0x0a, (byte) 0xec, (byte) 0x12, (byte) 0x3b, (byte) 0xf8, (byte) 0xd2,
+        (byte) 0xdc, (byte) 0x3c, (byte) 0xc8, (byte) 0x46, (byte) 0xc6, (byte) 0x51,
+        (byte) 0x06, (byte) 0x06, (byte) 0xcb, (byte) 0x84, (byte) 0x67, (byte) 0xb5,
+        (byte) 0x68, (byte) 0xd9, (byte) 0x9c, (byte) 0xd4, (byte) 0x16, (byte) 0x5c,
+        (byte) 0xb4, (byte) 0xe2, (byte) 0x55, (byte) 0xe6, (byte) 0x3a, (byte) 0x73,
+        (byte) 0x01, (byte) 0x1d, (byte) 0x6f, (byte) 0x30, (byte) 0x31, (byte) 0x59,
+        (byte) 0x8b, (byte) 0x2f, (byte) 0x4c, (byte) 0xe7, (byte) 0x86, (byte) 0x4c,
+        (byte) 0x39, (byte) 0x4e, (byte) 0x67, (byte) 0x3b, (byte) 0x22, (byte) 0x9b,
+        (byte) 0x85, (byte) 0x5a, (byte) 0xc3, (byte) 0x29, (byte) 0xaf, (byte) 0x8c,
+        (byte) 0x7c, (byte) 0x59, (byte) 0x4a, (byte) 0x24, (byte) 0xfa, (byte) 0xba,
+        (byte) 0x55, (byte) 0x40, (byte) 0x13, (byte) 0x64, (byte) 0xd8, (byte) 0xcb,
+        (byte) 0x4b, (byte) 0x98, (byte) 0x3f, (byte) 0xae, (byte) 0x20, (byte) 0xfd,
+        (byte) 0x8a, (byte) 0x50, (byte) 0x73, (byte) 0xe4,
+    };
+    /*
+     * echo -n 'This is a test of OAEP' | xxd -p -i | sed 's/0x/(byte) 0x/g'
+     */
+    public static final byte[] RSA_Vector2_Plaintext = new byte[] {
+            (byte) 0x54, (byte) 0x68, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x69,
+            (byte) 0x73, (byte) 0x20, (byte) 0x61, (byte) 0x20, (byte) 0x74, (byte) 0x65,
+            (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x6f, (byte) 0x66, (byte) 0x20,
+            (byte) 0x4f, (byte) 0x41, (byte) 0x45, (byte) 0x50
+    };
+
+    /*
+     * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey rsakey.pem \
+     * -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha1 -pkeyopt rsa_mgf1_md:sha1 \
+     * | xxd -p -i | sed 's/0x/(byte) 0x/g'
+     */
+    public static final byte[] RSA_Vector2_OAEP_SHA1_MGF1_SHA1 = new byte[] {
+            (byte) 0x53, (byte) 0x71, (byte) 0x84, (byte) 0x2e, (byte) 0x01, (byte) 0x74,
+            (byte) 0x82, (byte) 0xb3, (byte) 0x01, (byte) 0xac, (byte) 0x2b, (byte) 0xbd,
+            (byte) 0x40, (byte) 0xa7, (byte) 0x5b, (byte) 0x60, (byte) 0xf1, (byte) 0xde,
+            (byte) 0x54, (byte) 0x1d, (byte) 0x94, (byte) 0xc1, (byte) 0x10, (byte) 0x31,
+            (byte) 0x6f, (byte) 0xa3, (byte) 0xd8, (byte) 0x41, (byte) 0x2e, (byte) 0x82,
+            (byte) 0xad, (byte) 0x07, (byte) 0x6f, (byte) 0x25, (byte) 0x6c, (byte) 0xb5,
+            (byte) 0xef, (byte) 0xc6, (byte) 0xa6, (byte) 0xfb, (byte) 0xb1, (byte) 0x9d,
+            (byte) 0x75, (byte) 0x67, (byte) 0xb0, (byte) 0x97, (byte) 0x21, (byte) 0x3c,
+            (byte) 0x17, (byte) 0x04, (byte) 0xdc, (byte) 0x4e, (byte) 0x7e, (byte) 0x3f,
+            (byte) 0x5c, (byte) 0x13, (byte) 0x5e, (byte) 0x15, (byte) 0x0f, (byte) 0xe2,
+            (byte) 0xa7, (byte) 0x62, (byte) 0x6a, (byte) 0x08, (byte) 0xb1, (byte) 0xbc,
+            (byte) 0x2f, (byte) 0xcb, (byte) 0xb5, (byte) 0x96, (byte) 0x2d, (byte) 0xec,
+            (byte) 0x71, (byte) 0x4d, (byte) 0x59, (byte) 0x6e, (byte) 0x27, (byte) 0x85,
+            (byte) 0x87, (byte) 0x9b, (byte) 0xcc, (byte) 0x40, (byte) 0x32, (byte) 0x09,
+            (byte) 0x06, (byte) 0xe6, (byte) 0x7d, (byte) 0xdf, (byte) 0xeb, (byte) 0x2f,
+            (byte) 0xa8, (byte) 0x1c, (byte) 0x53, (byte) 0xdb, (byte) 0xa7, (byte) 0x48,
+            (byte) 0xf5, (byte) 0xbf, (byte) 0x2f, (byte) 0xbb, (byte) 0xee, (byte) 0xc7,
+            (byte) 0x55, (byte) 0x5e, (byte) 0xc4, (byte) 0x1c, (byte) 0x84, (byte) 0xed,
+            (byte) 0x97, (byte) 0x7e, (byte) 0xce, (byte) 0xa5, (byte) 0x69, (byte) 0x73,
+            (byte) 0xb3, (byte) 0xe0, (byte) 0x8c, (byte) 0x2a, (byte) 0xf2, (byte) 0xc7,
+            (byte) 0x65, (byte) 0xff, (byte) 0x10, (byte) 0xed, (byte) 0x25, (byte) 0xf0,
+            (byte) 0xf8, (byte) 0xda, (byte) 0x2f, (byte) 0x7f, (byte) 0xe0, (byte) 0x69,
+            (byte) 0xed, (byte) 0xb1, (byte) 0x0e, (byte) 0xcb, (byte) 0x43, (byte) 0xe4,
+            (byte) 0x31, (byte) 0xe6, (byte) 0x52, (byte) 0xfd, (byte) 0xa7, (byte) 0xe5,
+            (byte) 0x21, (byte) 0xd0, (byte) 0x67, (byte) 0x0a, (byte) 0xc1, (byte) 0xa1,
+            (byte) 0xb9, (byte) 0x04, (byte) 0xdb, (byte) 0x98, (byte) 0x4f, (byte) 0xf9,
+            (byte) 0x5c, (byte) 0x60, (byte) 0x4d, (byte) 0xac, (byte) 0x7a, (byte) 0x69,
+            (byte) 0xbd, (byte) 0x63, (byte) 0x0d, (byte) 0xb2, (byte) 0x01, (byte) 0x83,
+            (byte) 0xd7, (byte) 0x22, (byte) 0x5d, (byte) 0xed, (byte) 0xbd, (byte) 0x32,
+            (byte) 0x98, (byte) 0xd1, (byte) 0x4a, (byte) 0x2e, (byte) 0xb7, (byte) 0xb1,
+            (byte) 0x6d, (byte) 0x8a, (byte) 0x8f, (byte) 0xef, (byte) 0xc3, (byte) 0x89,
+            (byte) 0xdf, (byte) 0xa5, (byte) 0xac, (byte) 0xfb, (byte) 0x38, (byte) 0x61,
+            (byte) 0x32, (byte) 0xc5, (byte) 0x19, (byte) 0x83, (byte) 0x1f, (byte) 0x9c,
+            (byte) 0x45, (byte) 0x58, (byte) 0xdd, (byte) 0xa3, (byte) 0x57, (byte) 0xe4,
+            (byte) 0x91, (byte) 0xd2, (byte) 0x11, (byte) 0xf8, (byte) 0x96, (byte) 0x36,
+            (byte) 0x67, (byte) 0x99, (byte) 0x2b, (byte) 0x62, (byte) 0x21, (byte) 0xe3,
+            (byte) 0xa8, (byte) 0x5e, (byte) 0xa4, (byte) 0x2e, (byte) 0x0c, (byte) 0x29,
+            (byte) 0xf9, (byte) 0xcd, (byte) 0xfa, (byte) 0xbe, (byte) 0x3f, (byte) 0xd8,
+            (byte) 0xec, (byte) 0x6b, (byte) 0x32, (byte) 0xb3, (byte) 0x40, (byte) 0x4f,
+            (byte) 0x48, (byte) 0xe3, (byte) 0x14, (byte) 0x87, (byte) 0xa7, (byte) 0x5c,
+            (byte) 0xba, (byte) 0xdf, (byte) 0x0e, (byte) 0x64, (byte) 0xdc, (byte) 0xe2,
+            (byte) 0x51, (byte) 0xf4, (byte) 0x41, (byte) 0x25, (byte) 0x23, (byte) 0xc8,
+            (byte) 0x50, (byte) 0x1e, (byte) 0x9e, (byte) 0xb0
+    };
+
+    /*
+     * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey rsakey.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha1 | xxd -p -i | sed 's/0x/(byte) 0x/g'
+     */
+    public static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA1 = new byte[] {
+            (byte) 0x25, (byte) 0x9f, (byte) 0xc3, (byte) 0x69, (byte) 0xbc, (byte) 0x3f,
+            (byte) 0xe7, (byte) 0x9e, (byte) 0x76, (byte) 0xef, (byte) 0x6c, (byte) 0xd2,
+            (byte) 0x2b, (byte) 0x7b, (byte) 0xf0, (byte) 0xeb, (byte) 0xc2, (byte) 0x28,
+            (byte) 0x40, (byte) 0x4e, (byte) 0x9b, (byte) 0x2a, (byte) 0x4e, (byte) 0xa4,
+            (byte) 0x79, (byte) 0x66, (byte) 0xf1, (byte) 0x10, (byte) 0x96, (byte) 0x8c,
+            (byte) 0x58, (byte) 0x92, (byte) 0xb7, (byte) 0x70, (byte) 0xed, (byte) 0x3a,
+            (byte) 0xe0, (byte) 0x99, (byte) 0xd1, (byte) 0x80, (byte) 0x4b, (byte) 0x53,
+            (byte) 0x70, (byte) 0x9b, (byte) 0x51, (byte) 0xbf, (byte) 0xc1, (byte) 0x3a,
+            (byte) 0x70, (byte) 0xc5, (byte) 0x79, (byte) 0x21, (byte) 0x6e, (byte) 0xb3,
+            (byte) 0xf7, (byte) 0xa9, (byte) 0xe6, (byte) 0xcb, (byte) 0x70, (byte) 0xe4,
+            (byte) 0xf3, (byte) 0x4f, (byte) 0x45, (byte) 0xcf, (byte) 0xb7, (byte) 0x2b,
+            (byte) 0x38, (byte) 0xfd, (byte) 0x5d, (byte) 0x9a, (byte) 0x53, (byte) 0xc5,
+            (byte) 0x05, (byte) 0x74, (byte) 0x8d, (byte) 0x1d, (byte) 0x6e, (byte) 0x83,
+            (byte) 0xaa, (byte) 0x71, (byte) 0xc5, (byte) 0xe1, (byte) 0xa1, (byte) 0xa6,
+            (byte) 0xf3, (byte) 0xee, (byte) 0x5f, (byte) 0x9e, (byte) 0x4f, (byte) 0xe8,
+            (byte) 0x15, (byte) 0xd5, (byte) 0xa9, (byte) 0x1b, (byte) 0xa6, (byte) 0x41,
+            (byte) 0x2b, (byte) 0x18, (byte) 0x13, (byte) 0x20, (byte) 0x9f, (byte) 0x6b,
+            (byte) 0xf1, (byte) 0xd8, (byte) 0xf4, (byte) 0x87, (byte) 0xfa, (byte) 0x80,
+            (byte) 0xec, (byte) 0x0e, (byte) 0xa4, (byte) 0x4b, (byte) 0x24, (byte) 0x03,
+            (byte) 0x14, (byte) 0x25, (byte) 0xf2, (byte) 0x20, (byte) 0xfc, (byte) 0x52,
+            (byte) 0xf9, (byte) 0xd6, (byte) 0x7a, (byte) 0x4a, (byte) 0x45, (byte) 0x33,
+            (byte) 0xec, (byte) 0xde, (byte) 0x3c, (byte) 0x5b, (byte) 0xf2, (byte) 0xdc,
+            (byte) 0x8e, (byte) 0xc6, (byte) 0xb3, (byte) 0x26, (byte) 0xd3, (byte) 0x68,
+            (byte) 0xa7, (byte) 0xd8, (byte) 0x3a, (byte) 0xde, (byte) 0xa9, (byte) 0x25,
+            (byte) 0x1d, (byte) 0x42, (byte) 0x75, (byte) 0x66, (byte) 0x16, (byte) 0x29,
+            (byte) 0xad, (byte) 0x09, (byte) 0x74, (byte) 0x41, (byte) 0xbb, (byte) 0x45,
+            (byte) 0x39, (byte) 0x04, (byte) 0x7a, (byte) 0x93, (byte) 0xad, (byte) 0x1c,
+            (byte) 0xa6, (byte) 0x38, (byte) 0xf4, (byte) 0xac, (byte) 0xca, (byte) 0x5a,
+            (byte) 0xab, (byte) 0x92, (byte) 0x76, (byte) 0x26, (byte) 0x3c, (byte) 0xeb,
+            (byte) 0xda, (byte) 0xfc, (byte) 0x25, (byte) 0x93, (byte) 0x23, (byte) 0x01,
+            (byte) 0xe2, (byte) 0xac, (byte) 0x5e, (byte) 0x4c, (byte) 0xb7, (byte) 0xbc,
+            (byte) 0x5b, (byte) 0xaa, (byte) 0x14, (byte) 0xe9, (byte) 0xbf, (byte) 0x2d,
+            (byte) 0x3a, (byte) 0xdc, (byte) 0x2f, (byte) 0x6b, (byte) 0x4d, (byte) 0x0e,
+            (byte) 0x0a, (byte) 0x82, (byte) 0x3c, (byte) 0xd9, (byte) 0x32, (byte) 0xc1,
+            (byte) 0xc4, (byte) 0xa2, (byte) 0x46, (byte) 0x71, (byte) 0x10, (byte) 0x54,
+            (byte) 0x1a, (byte) 0xa6, (byte) 0xaa, (byte) 0x64, (byte) 0xe7, (byte) 0xc2,
+            (byte) 0xae, (byte) 0xbc, (byte) 0x3d, (byte) 0xa4, (byte) 0xa8, (byte) 0xd1,
+            (byte) 0xb7, (byte) 0x27, (byte) 0xef, (byte) 0x5f, (byte) 0xe7, (byte) 0xa7,
+            (byte) 0x5d, (byte) 0xa0, (byte) 0xcd, (byte) 0x57, (byte) 0xf1, (byte) 0xe0,
+            (byte) 0xd8, (byte) 0x42, (byte) 0x10, (byte) 0x77, (byte) 0xc3, (byte) 0xa7,
+            (byte) 0x1e, (byte) 0x0c, (byte) 0x37, (byte) 0x16, (byte) 0x11, (byte) 0x94,
+            (byte) 0x21, (byte) 0xf2, (byte) 0xca, (byte) 0x60, (byte) 0xce, (byte) 0xca,
+            (byte) 0x59, (byte) 0xf9, (byte) 0xe5, (byte) 0xe4
+    };
+
+    /*
+     * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha1 -pkeyopt rsa_oaep_label:010203FFA00A | xxd -p -i | sed 's/0x/(byte) 0x/g'
+     */
+    public static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA1_LABEL = new byte[] {
+            (byte) 0x80, (byte) 0xb1, (byte) 0xf2, (byte) 0xc2, (byte) 0x03, (byte) 0xc5,
+            (byte) 0xdf, (byte) 0xbd, (byte) 0xed, (byte) 0xfe, (byte) 0xe6, (byte) 0xff,
+            (byte) 0xd3, (byte) 0x38, (byte) 0x1e, (byte) 0x6d, (byte) 0xae, (byte) 0x47,
+            (byte) 0xfe, (byte) 0x19, (byte) 0xf9, (byte) 0x8c, (byte) 0xf1, (byte) 0x4d,
+            (byte) 0x18, (byte) 0x2b, (byte) 0x7e, (byte) 0x8e, (byte) 0x47, (byte) 0x39,
+            (byte) 0xa8, (byte) 0x04, (byte) 0xc4, (byte) 0x7d, (byte) 0x56, (byte) 0x03,
+            (byte) 0x15, (byte) 0x92, (byte) 0x18, (byte) 0xde, (byte) 0x56, (byte) 0xb3,
+            (byte) 0x01, (byte) 0x93, (byte) 0x16, (byte) 0xe3, (byte) 0xfa, (byte) 0xaa,
+            (byte) 0xf3, (byte) 0x73, (byte) 0x39, (byte) 0x26, (byte) 0xfb, (byte) 0xb0,
+            (byte) 0x18, (byte) 0x20, (byte) 0xdb, (byte) 0xa1, (byte) 0xbf, (byte) 0x31,
+            (byte) 0x22, (byte) 0xc8, (byte) 0x1d, (byte) 0xdb, (byte) 0xa0, (byte) 0x5a,
+            (byte) 0x22, (byte) 0xcd, (byte) 0x09, (byte) 0xb3, (byte) 0xcb, (byte) 0xa2,
+            (byte) 0x46, (byte) 0x14, (byte) 0x35, (byte) 0x66, (byte) 0xe8, (byte) 0xb8,
+            (byte) 0x07, (byte) 0x23, (byte) 0xc5, (byte) 0xae, (byte) 0xe6, (byte) 0xf1,
+            (byte) 0x7a, (byte) 0x8f, (byte) 0x5c, (byte) 0x44, (byte) 0x34, (byte) 0xbf,
+            (byte) 0xd6, (byte) 0xf8, (byte) 0x0c, (byte) 0xc7, (byte) 0x8d, (byte) 0xcd,
+            (byte) 0x23, (byte) 0x84, (byte) 0xbe, (byte) 0x9b, (byte) 0xbf, (byte) 0x9a,
+            (byte) 0x70, (byte) 0x0f, (byte) 0x18, (byte) 0xc0, (byte) 0x6f, (byte) 0x23,
+            (byte) 0x67, (byte) 0xf8, (byte) 0xbb, (byte) 0xce, (byte) 0xc2, (byte) 0x47,
+            (byte) 0x82, (byte) 0xa0, (byte) 0xa5, (byte) 0x60, (byte) 0xcd, (byte) 0x25,
+            (byte) 0xa5, (byte) 0x4b, (byte) 0xe4, (byte) 0x06, (byte) 0x7f, (byte) 0x46,
+            (byte) 0x62, (byte) 0x86, (byte) 0x94, (byte) 0xbc, (byte) 0x7f, (byte) 0xb0,
+            (byte) 0x2e, (byte) 0xc1, (byte) 0x8c, (byte) 0x6c, (byte) 0x58, (byte) 0x05,
+            (byte) 0x6f, (byte) 0x35, (byte) 0x76, (byte) 0xd3, (byte) 0xdf, (byte) 0xc0,
+            (byte) 0xdd, (byte) 0x66, (byte) 0xbe, (byte) 0xa1, (byte) 0x7e, (byte) 0x52,
+            (byte) 0xed, (byte) 0x81, (byte) 0x0e, (byte) 0x2d, (byte) 0x5b, (byte) 0x2b,
+            (byte) 0xe3, (byte) 0x52, (byte) 0x0e, (byte) 0x56, (byte) 0x9b, (byte) 0x05,
+            (byte) 0x72, (byte) 0xa8, (byte) 0xc8, (byte) 0x57, (byte) 0x22, (byte) 0x67,
+            (byte) 0x0e, (byte) 0x5f, (byte) 0x01, (byte) 0xf2, (byte) 0x69, (byte) 0x66,
+            (byte) 0x6a, (byte) 0x47, (byte) 0x4f, (byte) 0x78, (byte) 0xb3, (byte) 0x1e,
+            (byte) 0x7d, (byte) 0xce, (byte) 0xb3, (byte) 0x35, (byte) 0xdf, (byte) 0x23,
+            (byte) 0xac, (byte) 0xf8, (byte) 0x88, (byte) 0xa1, (byte) 0xde, (byte) 0x38,
+            (byte) 0x96, (byte) 0xfd, (byte) 0xa2, (byte) 0x5d, (byte) 0x09, (byte) 0x52,
+            (byte) 0x11, (byte) 0x2b, (byte) 0x21, (byte) 0xf0, (byte) 0x0d, (byte) 0x4c,
+            (byte) 0x15, (byte) 0xc3, (byte) 0x88, (byte) 0x2b, (byte) 0xf6, (byte) 0x2b,
+            (byte) 0xe3, (byte) 0xfd, (byte) 0x52, (byte) 0xf0, (byte) 0x09, (byte) 0x5c,
+            (byte) 0x4f, (byte) 0x5b, (byte) 0x8b, (byte) 0x84, (byte) 0x71, (byte) 0x72,
+            (byte) 0x8d, (byte) 0xaa, (byte) 0x6c, (byte) 0x55, (byte) 0xba, (byte) 0xe7,
+            (byte) 0x9c, (byte) 0xba, (byte) 0xbf, (byte) 0xf4, (byte) 0x09, (byte) 0x0a,
+            (byte) 0x60, (byte) 0xec, (byte) 0x53, (byte) 0xa4, (byte) 0x01, (byte) 0xa5,
+            (byte) 0xf2, (byte) 0x58, (byte) 0xab, (byte) 0x95, (byte) 0x68, (byte) 0x79,
+            (byte) 0x0b, (byte) 0xc3, (byte) 0xc4, (byte) 0x00, (byte) 0x68, (byte) 0x19,
+            (byte) 0xca, (byte) 0x07, (byte) 0x0d, (byte) 0x32
+    };
+
+    /*
+     * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey rsakey.pem \
+     * -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha224 -pkeyopt rsa_mgf1_md:sha224 \
+     * | xxd -p -i | sed 's/0x/(byte) 0x/g'
+     */
+    public static final byte[] RSA_Vector2_OAEP_SHA224_MGF1_SHA224 = new byte[] {
+            (byte) 0xae, (byte) 0xdd, (byte) 0xe6, (byte) 0xab, (byte) 0x00, (byte) 0xd6,
+            (byte) 0x1e, (byte) 0x7e, (byte) 0x85, (byte) 0x63, (byte) 0xab, (byte) 0x51,
+            (byte) 0x79, (byte) 0x92, (byte) 0xf1, (byte) 0xb9, (byte) 0x4f, (byte) 0x23,
+            (byte) 0xae, (byte) 0xf7, (byte) 0x1b, (byte) 0x5f, (byte) 0x10, (byte) 0x5b,
+            (byte) 0xa5, (byte) 0x15, (byte) 0x87, (byte) 0xa3, (byte) 0xbb, (byte) 0x26,
+            (byte) 0xfe, (byte) 0x7f, (byte) 0xc0, (byte) 0xa3, (byte) 0x67, (byte) 0x95,
+            (byte) 0xda, (byte) 0xc4, (byte) 0x6f, (byte) 0x6e, (byte) 0x08, (byte) 0x23,
+            (byte) 0x28, (byte) 0x0b, (byte) 0xdd, (byte) 0x29, (byte) 0x29, (byte) 0xdc,
+            (byte) 0xb0, (byte) 0x35, (byte) 0x16, (byte) 0x2e, (byte) 0x0f, (byte) 0xb9,
+            (byte) 0x1d, (byte) 0x90, (byte) 0x27, (byte) 0x68, (byte) 0xc7, (byte) 0x92,
+            (byte) 0x52, (byte) 0x8a, (byte) 0x1d, (byte) 0x48, (byte) 0x6a, (byte) 0x7d,
+            (byte) 0x0b, (byte) 0xf6, (byte) 0x35, (byte) 0xca, (byte) 0xe1, (byte) 0x57,
+            (byte) 0xdd, (byte) 0x36, (byte) 0x3b, (byte) 0x51, (byte) 0x45, (byte) 0x77,
+            (byte) 0x28, (byte) 0x4f, (byte) 0x98, (byte) 0xc0, (byte) 0xe0, (byte) 0xa7,
+            (byte) 0x51, (byte) 0x98, (byte) 0x84, (byte) 0x7a, (byte) 0x29, (byte) 0x05,
+            (byte) 0x9f, (byte) 0x60, (byte) 0x66, (byte) 0xf6, (byte) 0x83, (byte) 0xcd,
+            (byte) 0x03, (byte) 0x3e, (byte) 0x82, (byte) 0x0f, (byte) 0x57, (byte) 0x4b,
+            (byte) 0x27, (byte) 0x14, (byte) 0xf6, (byte) 0xc8, (byte) 0x5b, (byte) 0xed,
+            (byte) 0xc3, (byte) 0x77, (byte) 0x6f, (byte) 0xec, (byte) 0x0e, (byte) 0xae,
+            (byte) 0x59, (byte) 0xbe, (byte) 0x68, (byte) 0x76, (byte) 0x16, (byte) 0x17,
+            (byte) 0x77, (byte) 0xe2, (byte) 0xbd, (byte) 0xe0, (byte) 0x5a, (byte) 0x14,
+            (byte) 0xd9, (byte) 0xf4, (byte) 0x3f, (byte) 0x50, (byte) 0x31, (byte) 0xf0,
+            (byte) 0x0c, (byte) 0x82, (byte) 0x6c, (byte) 0xcc, (byte) 0x81, (byte) 0x84,
+            (byte) 0x3e, (byte) 0x63, (byte) 0x93, (byte) 0xe7, (byte) 0x12, (byte) 0x2d,
+            (byte) 0xc9, (byte) 0xa3, (byte) 0xe3, (byte) 0xce, (byte) 0xfd, (byte) 0xc7,
+            (byte) 0xe1, (byte) 0xef, (byte) 0xa4, (byte) 0x16, (byte) 0x5c, (byte) 0x60,
+            (byte) 0xb1, (byte) 0x80, (byte) 0x31, (byte) 0x15, (byte) 0x5c, (byte) 0x35,
+            (byte) 0x25, (byte) 0x0b, (byte) 0x89, (byte) 0xe4, (byte) 0x56, (byte) 0x74,
+            (byte) 0x8b, (byte) 0xaf, (byte) 0x8e, (byte) 0xe9, (byte) 0xe2, (byte) 0x37,
+            (byte) 0x17, (byte) 0xe6, (byte) 0x7b, (byte) 0x78, (byte) 0xd8, (byte) 0x2c,
+            (byte) 0x27, (byte) 0x52, (byte) 0x21, (byte) 0x96, (byte) 0xa0, (byte) 0x92,
+            (byte) 0x95, (byte) 0x64, (byte) 0xc3, (byte) 0x7f, (byte) 0x45, (byte) 0xfc,
+            (byte) 0x3d, (byte) 0x48, (byte) 0x4a, (byte) 0xd5, (byte) 0xa4, (byte) 0x0a,
+            (byte) 0x57, (byte) 0x07, (byte) 0x57, (byte) 0x95, (byte) 0x9f, (byte) 0x2f,
+            (byte) 0x75, (byte) 0x32, (byte) 0x2a, (byte) 0x4d, (byte) 0x64, (byte) 0xbd,
+            (byte) 0xb1, (byte) 0xe0, (byte) 0x46, (byte) 0x4f, (byte) 0xe8, (byte) 0x6c,
+            (byte) 0x4b, (byte) 0x77, (byte) 0xcc, (byte) 0x36, (byte) 0x87, (byte) 0x05,
+            (byte) 0x56, (byte) 0x9a, (byte) 0xe4, (byte) 0x2c, (byte) 0x43, (byte) 0xfd,
+            (byte) 0x34, (byte) 0x97, (byte) 0xf8, (byte) 0xd7, (byte) 0x91, (byte) 0xff,
+            (byte) 0x56, (byte) 0x86, (byte) 0x17, (byte) 0x49, (byte) 0x0a, (byte) 0x52,
+            (byte) 0xfb, (byte) 0xe5, (byte) 0x49, (byte) 0xdf, (byte) 0xc1, (byte) 0x28,
+            (byte) 0x9d, (byte) 0x85, (byte) 0x66, (byte) 0x9d, (byte) 0x1d, (byte) 0xa4,
+            (byte) 0x7e, (byte) 0x9a, (byte) 0x5b, (byte) 0x30
+    };
+
+    /*
+     * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt \
+     * -pkeyopt rsa_padding_mode:oaep -pkey rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 \
+     * | xxd -p -i | sed 's/0x/(byte) 0x/g'
+     */
+    public static final byte[] RSA_Vector2_OAEP_SHA256_MGF1_SHA256 = new byte[] {
+            (byte) 0x6a, (byte) 0x2b, (byte) 0xb2, (byte) 0xa3, (byte) 0x26, (byte) 0xa6,
+            (byte) 0x7a, (byte) 0x4a, (byte) 0x1f, (byte) 0xe5, (byte) 0xc8, (byte) 0x94,
+            (byte) 0x11, (byte) 0x1a, (byte) 0x92, (byte) 0x07, (byte) 0x0a, (byte) 0xf4,
+            (byte) 0x07, (byte) 0x0b, (byte) 0xd6, (byte) 0x37, (byte) 0xa5, (byte) 0x5d,
+            (byte) 0x16, (byte) 0x0a, (byte) 0x7d, (byte) 0x13, (byte) 0x27, (byte) 0x32,
+            (byte) 0x5a, (byte) 0xc3, (byte) 0x0d, (byte) 0x7a, (byte) 0x54, (byte) 0xfe,
+            (byte) 0x02, (byte) 0x28, (byte) 0xc6, (byte) 0x8e, (byte) 0x32, (byte) 0x7b,
+            (byte) 0x0a, (byte) 0x52, (byte) 0xf8, (byte) 0xe6, (byte) 0xab, (byte) 0x16,
+            (byte) 0x77, (byte) 0x7c, (byte) 0x53, (byte) 0xcd, (byte) 0xb0, (byte) 0xb6,
+            (byte) 0x90, (byte) 0xce, (byte) 0x7b, (byte) 0xa5, (byte) 0xdb, (byte) 0xab,
+            (byte) 0xfd, (byte) 0xf5, (byte) 0xbb, (byte) 0x49, (byte) 0x63, (byte) 0xb7,
+            (byte) 0xa8, (byte) 0x3e, (byte) 0x53, (byte) 0xf1, (byte) 0x00, (byte) 0x4d,
+            (byte) 0x72, (byte) 0x15, (byte) 0x34, (byte) 0xa8, (byte) 0x5b, (byte) 0x00,
+            (byte) 0x01, (byte) 0x75, (byte) 0xdc, (byte) 0xb6, (byte) 0xd1, (byte) 0xdf,
+            (byte) 0xcb, (byte) 0x93, (byte) 0xf3, (byte) 0x31, (byte) 0x04, (byte) 0x7e,
+            (byte) 0x48, (byte) 0x3e, (byte) 0xc9, (byte) 0xaf, (byte) 0xd7, (byte) 0xbd,
+            (byte) 0x9e, (byte) 0x73, (byte) 0x01, (byte) 0x79, (byte) 0xf8, (byte) 0xdc,
+            (byte) 0x46, (byte) 0x31, (byte) 0x55, (byte) 0x83, (byte) 0x21, (byte) 0xd1,
+            (byte) 0x19, (byte) 0x0b, (byte) 0x57, (byte) 0xf1, (byte) 0x06, (byte) 0xb9,
+            (byte) 0x32, (byte) 0x0e, (byte) 0x9d, (byte) 0x38, (byte) 0x53, (byte) 0x94,
+            (byte) 0x96, (byte) 0xd4, (byte) 0x6d, (byte) 0x18, (byte) 0xe2, (byte) 0xe3,
+            (byte) 0xcd, (byte) 0xfa, (byte) 0xfe, (byte) 0xb3, (byte) 0xe3, (byte) 0x27,
+            (byte) 0xd7, (byte) 0x45, (byte) 0xe8, (byte) 0x46, (byte) 0x6b, (byte) 0x06,
+            (byte) 0x0f, (byte) 0x5e, (byte) 0x24, (byte) 0x02, (byte) 0xef, (byte) 0xa2,
+            (byte) 0x69, (byte) 0xe6, (byte) 0x15, (byte) 0xb3, (byte) 0x8f, (byte) 0x71,
+            (byte) 0x97, (byte) 0x39, (byte) 0xfb, (byte) 0x32, (byte) 0xe0, (byte) 0xe5,
+            (byte) 0xac, (byte) 0x46, (byte) 0xb4, (byte) 0xe7, (byte) 0x3d, (byte) 0x89,
+            (byte) 0xba, (byte) 0xd9, (byte) 0x4c, (byte) 0x25, (byte) 0x97, (byte) 0xef,
+            (byte) 0xe6, (byte) 0x17, (byte) 0x23, (byte) 0x4e, (byte) 0xc8, (byte) 0xdb,
+            (byte) 0x18, (byte) 0x9b, (byte) 0xba, (byte) 0xb5, (byte) 0x7e, (byte) 0x19,
+            (byte) 0x4d, (byte) 0x95, (byte) 0x7d, (byte) 0x60, (byte) 0x1b, (byte) 0xa7,
+            (byte) 0x06, (byte) 0x1e, (byte) 0x99, (byte) 0x4a, (byte) 0xf2, (byte) 0x82,
+            (byte) 0x71, (byte) 0x62, (byte) 0x41, (byte) 0xa4, (byte) 0xa7, (byte) 0xdb,
+            (byte) 0x88, (byte) 0xb0, (byte) 0x4a, (byte) 0xc7, (byte) 0x3b, (byte) 0xce,
+            (byte) 0x91, (byte) 0x4f, (byte) 0xc7, (byte) 0xca, (byte) 0x6f, (byte) 0x89,
+            (byte) 0xac, (byte) 0x1a, (byte) 0x36, (byte) 0x84, (byte) 0x0c, (byte) 0x97,
+            (byte) 0xa0, (byte) 0x1a, (byte) 0x08, (byte) 0x6f, (byte) 0x70, (byte) 0xf3,
+            (byte) 0x94, (byte) 0xa0, (byte) 0x0f, (byte) 0x44, (byte) 0xdd, (byte) 0x86,
+            (byte) 0x9d, (byte) 0x2c, (byte) 0xac, (byte) 0x43, (byte) 0xed, (byte) 0xb8,
+            (byte) 0xa1, (byte) 0x66, (byte) 0xf3, (byte) 0xd3, (byte) 0x5c, (byte) 0xe5,
+            (byte) 0xe2, (byte) 0x4c, (byte) 0x7e, (byte) 0xda, (byte) 0x20, (byte) 0xbd,
+            (byte) 0x5a, (byte) 0x75, (byte) 0x12, (byte) 0x31, (byte) 0x23, (byte) 0x02,
+            (byte) 0xb5, (byte) 0x1f, (byte) 0x38, (byte) 0x98
+    };
+
+    /*
+     * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt \
+     * -pkeyopt rsa_padding_mode:oaep -pkey rsa_oaep_md:sha384 -pkeyopt rsa_mgf1_md:sha384 \
+     * | xxd -p -i | sed 's/0x/(byte) 0x/g'
+     */
+    public static final byte[] RSA_Vector2_OAEP_SHA384_MGF1_SHA384 = new byte[] {
+            (byte) 0xa1, (byte) 0xb3, (byte) 0x3b, (byte) 0x34, (byte) 0x69, (byte) 0x9e,
+            (byte) 0xd8, (byte) 0xa0, (byte) 0x37, (byte) 0x2c, (byte) 0xeb, (byte) 0xef,
+            (byte) 0xf2, (byte) 0xaf, (byte) 0xfa, (byte) 0x63, (byte) 0x5d, (byte) 0x88,
+            (byte) 0xac, (byte) 0x51, (byte) 0xd4, (byte) 0x7f, (byte) 0x85, (byte) 0xf0,
+            (byte) 0x5e, (byte) 0xb4, (byte) 0x81, (byte) 0x7c, (byte) 0x82, (byte) 0x4f,
+            (byte) 0x92, (byte) 0xf7, (byte) 0x77, (byte) 0x48, (byte) 0x4c, (byte) 0xb1,
+            (byte) 0x42, (byte) 0xb3, (byte) 0x0e, (byte) 0x94, (byte) 0xc8, (byte) 0x5a,
+            (byte) 0xae, (byte) 0xed, (byte) 0x8d, (byte) 0x51, (byte) 0x72, (byte) 0x6b,
+            (byte) 0xa9, (byte) 0xd4, (byte) 0x1e, (byte) 0xbe, (byte) 0x38, (byte) 0x2c,
+            (byte) 0xd0, (byte) 0x43, (byte) 0xae, (byte) 0xb4, (byte) 0x30, (byte) 0xa9,
+            (byte) 0x93, (byte) 0x47, (byte) 0xb5, (byte) 0x9d, (byte) 0x03, (byte) 0x92,
+            (byte) 0x25, (byte) 0x74, (byte) 0xed, (byte) 0xfa, (byte) 0xfe, (byte) 0xf1,
+            (byte) 0xba, (byte) 0x04, (byte) 0x3a, (byte) 0x4d, (byte) 0x6d, (byte) 0x9a,
+            (byte) 0x0d, (byte) 0x95, (byte) 0x02, (byte) 0xb0, (byte) 0xac, (byte) 0x77,
+            (byte) 0x11, (byte) 0x44, (byte) 0xeb, (byte) 0xd2, (byte) 0x02, (byte) 0x90,
+            (byte) 0xea, (byte) 0x2f, (byte) 0x68, (byte) 0x2a, (byte) 0x69, (byte) 0xcf,
+            (byte) 0x45, (byte) 0x34, (byte) 0xff, (byte) 0x00, (byte) 0xc6, (byte) 0x3c,
+            (byte) 0x0b, (byte) 0x2c, (byte) 0x5f, (byte) 0x8c, (byte) 0x2c, (byte) 0xbf,
+            (byte) 0xc2, (byte) 0x4b, (byte) 0x16, (byte) 0x07, (byte) 0x84, (byte) 0x74,
+            (byte) 0xf0, (byte) 0x7a, (byte) 0x01, (byte) 0x7e, (byte) 0x74, (byte) 0x01,
+            (byte) 0x88, (byte) 0xce, (byte) 0xda, (byte) 0xe4, (byte) 0x21, (byte) 0x89,
+            (byte) 0xfc, (byte) 0xac, (byte) 0x68, (byte) 0xdb, (byte) 0xfc, (byte) 0x5f,
+            (byte) 0x3f, (byte) 0x00, (byte) 0xd9, (byte) 0x32, (byte) 0x1d, (byte) 0xa5,
+            (byte) 0xec, (byte) 0x72, (byte) 0x46, (byte) 0x23, (byte) 0xe5, (byte) 0x7f,
+            (byte) 0x49, (byte) 0x0e, (byte) 0x3e, (byte) 0xf2, (byte) 0x2b, (byte) 0x16,
+            (byte) 0x52, (byte) 0x9f, (byte) 0x9d, (byte) 0x0c, (byte) 0xfe, (byte) 0xab,
+            (byte) 0xdd, (byte) 0x77, (byte) 0x77, (byte) 0x94, (byte) 0xa4, (byte) 0x92,
+            (byte) 0xa2, (byte) 0x41, (byte) 0x0d, (byte) 0x4b, (byte) 0x57, (byte) 0x80,
+            (byte) 0xd6, (byte) 0x74, (byte) 0x63, (byte) 0xd5, (byte) 0xbf, (byte) 0x5c,
+            (byte) 0xa0, (byte) 0xda, (byte) 0x3c, (byte) 0xe6, (byte) 0xbf, (byte) 0xa4,
+            (byte) 0xc3, (byte) 0xfb, (byte) 0x46, (byte) 0x3b, (byte) 0x73, (byte) 0x30,
+            (byte) 0x4b, (byte) 0x57, (byte) 0x27, (byte) 0x0c, (byte) 0x81, (byte) 0xde,
+            (byte) 0x8a, (byte) 0x01, (byte) 0xe5, (byte) 0x7e, (byte) 0xe0, (byte) 0x16,
+            (byte) 0x11, (byte) 0x24, (byte) 0x34, (byte) 0x22, (byte) 0x01, (byte) 0x9f,
+            (byte) 0xe6, (byte) 0xa9, (byte) 0xfb, (byte) 0xad, (byte) 0x55, (byte) 0x17,
+            (byte) 0x2a, (byte) 0x92, (byte) 0x87, (byte) 0xf3, (byte) 0x72, (byte) 0xc9,
+            (byte) 0x3d, (byte) 0xc9, (byte) 0x2e, (byte) 0x32, (byte) 0x8e, (byte) 0xbb,
+            (byte) 0xdc, (byte) 0x1b, (byte) 0xa7, (byte) 0x7b, (byte) 0x73, (byte) 0xd7,
+            (byte) 0xf4, (byte) 0xad, (byte) 0xa9, (byte) 0x3a, (byte) 0xf7, (byte) 0xa8,
+            (byte) 0x82, (byte) 0x92, (byte) 0x40, (byte) 0xd4, (byte) 0x51, (byte) 0x87,
+            (byte) 0xe1, (byte) 0xb7, (byte) 0x4f, (byte) 0x91, (byte) 0x75, (byte) 0x5b,
+            (byte) 0x03, (byte) 0x9d, (byte) 0xa1, (byte) 0xd4, (byte) 0x00, (byte) 0x05,
+            (byte) 0x79, (byte) 0x42, (byte) 0x93, (byte) 0x76
+    };
+
+    /*
+     * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt \
+     * -pkeyopt rsa_padding_mode:oaep -pkey rsa_oaep_md:sha512 -pkeyopt rsa_mgf1_md:sha512 \
+     * | xxd -p -i | sed 's/0x/(byte) 0x/g'
+     */
+    public static final byte[] RSA_Vector2_OAEP_SHA512_MGF1_SHA512 = new byte[] {
+            (byte) 0x75, (byte) 0x0f, (byte) 0xf9, (byte) 0x21, (byte) 0xca, (byte) 0xcc,
+            (byte) 0x0e, (byte) 0x13, (byte) 0x9e, (byte) 0x38, (byte) 0xa4, (byte) 0xa7,
+            (byte) 0xee, (byte) 0x61, (byte) 0x6d, (byte) 0x56, (byte) 0xea, (byte) 0x36,
+            (byte) 0xeb, (byte) 0xec, (byte) 0xfa, (byte) 0x1a, (byte) 0xeb, (byte) 0x0c,
+            (byte) 0xb2, (byte) 0x58, (byte) 0x9d, (byte) 0xde, (byte) 0x47, (byte) 0x27,
+            (byte) 0x2d, (byte) 0xbd, (byte) 0x8b, (byte) 0xa7, (byte) 0xf1, (byte) 0x8b,
+            (byte) 0xba, (byte) 0x4c, (byte) 0xab, (byte) 0x39, (byte) 0x6a, (byte) 0x82,
+            (byte) 0x0d, (byte) 0xaf, (byte) 0x4c, (byte) 0xde, (byte) 0xdb, (byte) 0x5e,
+            (byte) 0xdb, (byte) 0x08, (byte) 0x98, (byte) 0x06, (byte) 0xc5, (byte) 0x99,
+            (byte) 0xb6, (byte) 0x6d, (byte) 0xbc, (byte) 0x5b, (byte) 0xf9, (byte) 0xe4,
+            (byte) 0x97, (byte) 0x0b, (byte) 0xba, (byte) 0xe3, (byte) 0x17, (byte) 0xa9,
+            (byte) 0x3c, (byte) 0x4b, (byte) 0x21, (byte) 0xd8, (byte) 0x29, (byte) 0xf8,
+            (byte) 0xa7, (byte) 0x1c, (byte) 0x15, (byte) 0xd7, (byte) 0xf6, (byte) 0xfc,
+            (byte) 0x53, (byte) 0x64, (byte) 0x97, (byte) 0x9e, (byte) 0x22, (byte) 0xb1,
+            (byte) 0x93, (byte) 0x26, (byte) 0x80, (byte) 0xdc, (byte) 0xaa, (byte) 0x1b,
+            (byte) 0xae, (byte) 0x69, (byte) 0x0f, (byte) 0x74, (byte) 0x3d, (byte) 0x61,
+            (byte) 0x80, (byte) 0x68, (byte) 0xb8, (byte) 0xaf, (byte) 0x63, (byte) 0x72,
+            (byte) 0x37, (byte) 0x4f, (byte) 0xf3, (byte) 0x29, (byte) 0x4a, (byte) 0x75,
+            (byte) 0x4f, (byte) 0x29, (byte) 0x40, (byte) 0x01, (byte) 0xd3, (byte) 0xc6,
+            (byte) 0x56, (byte) 0x1a, (byte) 0xaf, (byte) 0xc3, (byte) 0xb3, (byte) 0xd2,
+            (byte) 0xb9, (byte) 0x91, (byte) 0x35, (byte) 0x1b, (byte) 0x89, (byte) 0x4c,
+            (byte) 0x61, (byte) 0xa2, (byte) 0x8e, (byte) 0x6f, (byte) 0x12, (byte) 0x4a,
+            (byte) 0x10, (byte) 0xc2, (byte) 0xcc, (byte) 0xab, (byte) 0x51, (byte) 0xec,
+            (byte) 0x1b, (byte) 0xb5, (byte) 0xfe, (byte) 0x20, (byte) 0x16, (byte) 0xb2,
+            (byte) 0xc5, (byte) 0x0f, (byte) 0xe1, (byte) 0x6a, (byte) 0xb4, (byte) 0x6c,
+            (byte) 0x27, (byte) 0xd9, (byte) 0x42, (byte) 0xb9, (byte) 0xb6, (byte) 0x55,
+            (byte) 0xa8, (byte) 0xbc, (byte) 0x1c, (byte) 0x32, (byte) 0x54, (byte) 0x84,
+            (byte) 0xec, (byte) 0x1e, (byte) 0x95, (byte) 0xd8, (byte) 0xae, (byte) 0xca,
+            (byte) 0xc1, (byte) 0xad, (byte) 0x4c, (byte) 0x65, (byte) 0xd6, (byte) 0xc2,
+            (byte) 0x19, (byte) 0x66, (byte) 0xad, (byte) 0x9f, (byte) 0x55, (byte) 0x15,
+            (byte) 0xe1, (byte) 0x5d, (byte) 0x8f, (byte) 0xab, (byte) 0x18, (byte) 0x68,
+            (byte) 0x42, (byte) 0x7c, (byte) 0x48, (byte) 0xb7, (byte) 0x2c, (byte) 0xfd,
+            (byte) 0x1a, (byte) 0x07, (byte) 0xa1, (byte) 0x6a, (byte) 0xfb, (byte) 0x81,
+            (byte) 0xc6, (byte) 0x93, (byte) 0xbf, (byte) 0xa3, (byte) 0x5d, (byte) 0xfd,
+            (byte) 0xce, (byte) 0xf3, (byte) 0x17, (byte) 0x26, (byte) 0xf0, (byte) 0xda,
+            (byte) 0x0e, (byte) 0xd1, (byte) 0x86, (byte) 0x9d, (byte) 0x61, (byte) 0xd1,
+            (byte) 0x8a, (byte) 0xdb, (byte) 0x36, (byte) 0x39, (byte) 0x1c, (byte) 0xd4,
+            (byte) 0x99, (byte) 0x53, (byte) 0x30, (byte) 0x5a, (byte) 0x01, (byte) 0xf4,
+            (byte) 0xa0, (byte) 0xca, (byte) 0x94, (byte) 0x72, (byte) 0x3d, (byte) 0xe3,
+            (byte) 0x50, (byte) 0x95, (byte) 0xcb, (byte) 0xa9, (byte) 0x37, (byte) 0xeb,
+            (byte) 0x66, (byte) 0x21, (byte) 0x20, (byte) 0x2e, (byte) 0xf2, (byte) 0xfd,
+            (byte) 0xfa, (byte) 0x54, (byte) 0xbf, (byte) 0x17, (byte) 0x23, (byte) 0xbb,
+            (byte) 0x9e, (byte) 0x77, (byte) 0xe0, (byte) 0xaa
+    };
+
+    /*
+     * echo -n 'This is a test of OAEP' | openssl pkeyutl -encrypt -inkey /tmp/rsakey.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha512 -pkeyopt rsa_mgf1_md:sha512 -pkeyopt rsa_oaep_label:010203FFA00A | xxd -p -i | sed 's/0x/(byte) 0x/g'
+     */
+    public static final byte[] RSA_Vector2_OAEP_SHA512_MGF1_SHA512_LABEL = new byte[] {
+            (byte) 0x31, (byte) 0x3b, (byte) 0x23, (byte) 0xcf, (byte) 0x40, (byte) 0xfe,
+            (byte) 0x15, (byte) 0x94, (byte) 0xd6, (byte) 0x81, (byte) 0x21, (byte) 0x69,
+            (byte) 0x8e, (byte) 0x58, (byte) 0xd5, (byte) 0x0f, (byte) 0xa8, (byte) 0x72,
+            (byte) 0x94, (byte) 0x13, (byte) 0xfe, (byte) 0xf9, (byte) 0xa1, (byte) 0x47,
+            (byte) 0x49, (byte) 0x91, (byte) 0xcb, (byte) 0x66, (byte) 0xe6, (byte) 0x5d,
+            (byte) 0x02, (byte) 0xad, (byte) 0xd4, (byte) 0x2f, (byte) 0x4f, (byte) 0xab,
+            (byte) 0xb7, (byte) 0x9e, (byte) 0xc0, (byte) 0xf0, (byte) 0x3d, (byte) 0x66,
+            (byte) 0x0e, (byte) 0x20, (byte) 0x82, (byte) 0x7f, (byte) 0x22, (byte) 0x8f,
+            (byte) 0x81, (byte) 0xba, (byte) 0x47, (byte) 0xc7, (byte) 0xaf, (byte) 0xb6,
+            (byte) 0x0e, (byte) 0x78, (byte) 0xe3, (byte) 0x30, (byte) 0xd7, (byte) 0x6c,
+            (byte) 0x81, (byte) 0xc2, (byte) 0x05, (byte) 0x7e, (byte) 0xe9, (byte) 0xac,
+            (byte) 0x8d, (byte) 0x45, (byte) 0x25, (byte) 0xe8, (byte) 0x26, (byte) 0x39,
+            (byte) 0x88, (byte) 0x64, (byte) 0x2e, (byte) 0xc6, (byte) 0xed, (byte) 0xd4,
+            (byte) 0xad, (byte) 0x94, (byte) 0xc8, (byte) 0x4e, (byte) 0x4a, (byte) 0x71,
+            (byte) 0x1e, (byte) 0x11, (byte) 0x14, (byte) 0x03, (byte) 0x56, (byte) 0x02,
+            (byte) 0x28, (byte) 0x32, (byte) 0x8f, (byte) 0xe2, (byte) 0x16, (byte) 0x4a,
+            (byte) 0x62, (byte) 0xa6, (byte) 0x9a, (byte) 0x8d, (byte) 0xf8, (byte) 0x33,
+            (byte) 0x35, (byte) 0xa2, (byte) 0xc7, (byte) 0x70, (byte) 0xcc, (byte) 0x26,
+            (byte) 0x1e, (byte) 0x4d, (byte) 0x9c, (byte) 0x4e, (byte) 0x2b, (byte) 0xe8,
+            (byte) 0xfd, (byte) 0x07, (byte) 0x33, (byte) 0x15, (byte) 0x53, (byte) 0x11,
+            (byte) 0x5c, (byte) 0x6f, (byte) 0x5d, (byte) 0x23, (byte) 0x7b, (byte) 0x3f,
+            (byte) 0x73, (byte) 0xff, (byte) 0xf4, (byte) 0xbe, (byte) 0x1f, (byte) 0xe6,
+            (byte) 0x5a, (byte) 0xb8, (byte) 0x2b, (byte) 0xd2, (byte) 0xbe, (byte) 0xa0,
+            (byte) 0x91, (byte) 0x5d, (byte) 0xca, (byte) 0x89, (byte) 0xb3, (byte) 0xce,
+            (byte) 0x0a, (byte) 0x2b, (byte) 0xce, (byte) 0xb9, (byte) 0xbe, (byte) 0x5d,
+            (byte) 0xb2, (byte) 0xc2, (byte) 0xd6, (byte) 0xa9, (byte) 0xbc, (byte) 0x37,
+            (byte) 0xed, (byte) 0x9a, (byte) 0xba, (byte) 0x35, (byte) 0xf8, (byte) 0x6e,
+            (byte) 0x63, (byte) 0x76, (byte) 0xd1, (byte) 0x12, (byte) 0xf5, (byte) 0x89,
+            (byte) 0xf0, (byte) 0x13, (byte) 0x86, (byte) 0xe7, (byte) 0x1b, (byte) 0x94,
+            (byte) 0xcb, (byte) 0xc8, (byte) 0x5c, (byte) 0x4c, (byte) 0x1b, (byte) 0x8a,
+            (byte) 0x2d, (byte) 0x6b, (byte) 0x24, (byte) 0x1a, (byte) 0x38, (byte) 0x14,
+            (byte) 0x77, (byte) 0x49, (byte) 0xe5, (byte) 0x08, (byte) 0x25, (byte) 0xe4,
+            (byte) 0xa6, (byte) 0xcf, (byte) 0x62, (byte) 0xfd, (byte) 0x66, (byte) 0x28,
+            (byte) 0xf0, (byte) 0x3a, (byte) 0x9c, (byte) 0x31, (byte) 0xef, (byte) 0x48,
+            (byte) 0x2a, (byte) 0xd3, (byte) 0x3e, (byte) 0x29, (byte) 0xfa, (byte) 0x18,
+            (byte) 0x8f, (byte) 0xd6, (byte) 0xaa, (byte) 0x1d, (byte) 0x10, (byte) 0xcd,
+            (byte) 0x35, (byte) 0x25, (byte) 0x92, (byte) 0x48, (byte) 0xa0, (byte) 0x2c,
+            (byte) 0xc1, (byte) 0x31, (byte) 0xeb, (byte) 0x47, (byte) 0x5b, (byte) 0x22,
+            (byte) 0x52, (byte) 0x7c, (byte) 0xf5, (byte) 0xec, (byte) 0x76, (byte) 0x90,
+            (byte) 0x94, (byte) 0x58, (byte) 0xd9, (byte) 0xd6, (byte) 0xe0, (byte) 0x0a,
+            (byte) 0x3f, (byte) 0x09, (byte) 0x98, (byte) 0x03, (byte) 0xc5, (byte) 0x07,
+            (byte) 0x8f, (byte) 0x89, (byte) 0x1e, (byte) 0x62, (byte) 0x2c, (byte) 0xea,
+            (byte) 0x17, (byte) 0x0a, (byte) 0x2e, (byte) 0x68
+    };
+
+    @Test
+    public void testRSA_ECB_NoPadding_Private_OnlyDoFinal_Success() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_Private_OnlyDoFinal_Success(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_Private_OnlyDoFinal_Success(String provider) throws Exception {
+        final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA");
+
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+
+        /*
+         * You're actually decrypting with private keys, but there is no
+         * distinction made here. It's all keyed off of what kind of key you're
+         * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+         */
+        c.init(Cipher.ENCRYPT_MODE, privKey);
+        byte[] encrypted = c.doFinal(RSA_2048_Vector1);
+        assertTrue("Encrypted should match expected",
+                Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+
+        c.init(Cipher.DECRYPT_MODE, privKey);
+        encrypted = c.doFinal(RSA_2048_Vector1);
+        assertTrue("Encrypted should match expected",
+                Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_Private_UpdateThenEmptyDoFinal_Success() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_Private_UpdateThenEmptyDoFinal_Success(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_Private_UpdateThenEmptyDoFinal_Success(String provider) throws Exception {
+        final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA");
+
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+
+        /*
+         * You're actually decrypting with private keys, but there is no
+         * distinction made here. It's all keyed off of what kind of key you're
+         * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+         */
+        c.init(Cipher.ENCRYPT_MODE, privKey);
+        c.update(RSA_2048_Vector1);
+        byte[] encrypted = c.doFinal();
+        assertTrue("Encrypted should match expected",
+                Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+
+        c.init(Cipher.DECRYPT_MODE, privKey);
+        c.update(RSA_2048_Vector1);
+        encrypted = c.doFinal();
+        assertTrue("Encrypted should match expected",
+                Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_Private_SingleByteUpdateThenEmptyDoFinal_Success()
+            throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_Private_SingleByteUpdateThenEmptyDoFinal_Success(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_Private_SingleByteUpdateThenEmptyDoFinal_Success(String provider)
+            throws Exception {
+        final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA");
+
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+
+        /*
+         * You're actually decrypting with private keys, but there is no
+         * distinction made here. It's all keyed off of what kind of key you're
+         * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+         */
+        c.init(Cipher.ENCRYPT_MODE, privKey);
+        int i;
+        for (i = 0; i < RSA_2048_Vector1.length / 2; i++) {
+            c.update(RSA_2048_Vector1, i, 1);
+        }
+        byte[] encrypted = c.doFinal(RSA_2048_Vector1, i, RSA_2048_Vector1.length - i);
+        assertTrue("Encrypted should match expected",
+                Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+
+        c.init(Cipher.DECRYPT_MODE, privKey);
+        for (i = 0; i < RSA_2048_Vector1.length / 2; i++) {
+            c.update(RSA_2048_Vector1, i, 1);
+        }
+        encrypted = c.doFinal(RSA_2048_Vector1, i, RSA_2048_Vector1.length - i);
+        assertTrue("Encrypted should match expected",
+                Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_Private_OnlyDoFinalWithOffset_Success() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_Private_OnlyDoFinalWithOffset_Success(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_Private_OnlyDoFinalWithOffset_Success(String provider) throws Exception {
+        final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA");
+
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+
+        /*
+         * You're actually decrypting with private keys, but there is no
+         * distinction made here. It's all keyed off of what kind of key you're
+         * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+         */
+        c.init(Cipher.ENCRYPT_MODE, privKey);
+        byte[] encrypted = new byte[RSA_Vector1_Encrypt_Private.length];
+        final int encryptLen = c
+                .doFinal(RSA_2048_Vector1, 0, RSA_2048_Vector1.length, encrypted, 0);
+        assertEquals("Encrypted size should match expected", RSA_Vector1_Encrypt_Private.length,
+                encryptLen);
+        assertTrue("Encrypted should match expected",
+                Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+
+        c.init(Cipher.DECRYPT_MODE, privKey);
+        final int decryptLen = c
+                .doFinal(RSA_2048_Vector1, 0, RSA_2048_Vector1.length, encrypted, 0);
+        assertEquals("Encrypted size should match expected", RSA_Vector1_Encrypt_Private.length,
+                decryptLen);
+        assertTrue("Encrypted should match expected",
+                Arrays.equals(RSA_Vector1_Encrypt_Private, encrypted));
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_Public_OnlyDoFinal_Success() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_Public_OnlyDoFinal_Success(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_Public_OnlyDoFinal_Success(String provider) throws Exception {
+        final PublicKey pubKey = (PublicKey) getEncryptKey("RSA");
+
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+
+        /*
+         * You're actually encrypting with public keys, but there is no
+         * distinction made here. It's all keyed off of what kind of key you're
+         * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+         */
+        c.init(Cipher.ENCRYPT_MODE, pubKey);
+        byte[] encrypted = c.doFinal(RSA_Vector1_Encrypt_Private);
+        assertEncryptedEqualsNoPadding(provider, Cipher.ENCRYPT_MODE, RSA_2048_Vector1, encrypted);
+
+        c.init(Cipher.DECRYPT_MODE, pubKey);
+        encrypted = c.doFinal(RSA_Vector1_Encrypt_Private);
+        assertEncryptedEqualsNoPadding(provider, Cipher.DECRYPT_MODE, RSA_2048_Vector1, encrypted);
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_Public_OnlyDoFinalWithOffset_Success() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_Public_OnlyDoFinalWithOffset_Success(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_Public_OnlyDoFinalWithOffset_Success(String provider) throws Exception {
+        final PublicKey pubKey = (PublicKey) getEncryptKey("RSA");
+
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+
+        /*
+         * You're actually encrypting with public keys, but there is no
+         * distinction made here. It's all keyed off of what kind of key you're
+         * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+         */
+        c.init(Cipher.ENCRYPT_MODE, pubKey);
+        byte[] encrypted = new byte[RSA_2048_Vector1.length];
+        final int encryptLen = c.doFinal(RSA_Vector1_Encrypt_Private, 0,
+                RSA_Vector1_Encrypt_Private.length, encrypted, 0);
+        assertEquals("Encrypted size should match expected", RSA_2048_Vector1.length, encryptLen);
+        assertEncryptedEqualsNoPadding(provider, Cipher.ENCRYPT_MODE, RSA_2048_Vector1, encrypted);
+
+        c.init(Cipher.DECRYPT_MODE, pubKey);
+        int decryptLen = c.doFinal(RSA_Vector1_Encrypt_Private, 0,
+                RSA_Vector1_Encrypt_Private.length, encrypted, 0);
+        if (provider.equals("BC")) {
+            // BC strips the leading 0 for us on decrypt even when NoPadding is specified...
+            decryptLen++;
+            encrypted = Arrays.copyOf(encrypted, encrypted.length - 1);
+        }
+        assertEquals("Encrypted size should match expected", RSA_2048_Vector1.length, decryptLen);
+        assertEncryptedEqualsNoPadding(provider, Cipher.DECRYPT_MODE, RSA_2048_Vector1, encrypted);
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_Public_UpdateThenEmptyDoFinal_Success() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_Public_UpdateThenEmptyDoFinal_Success(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_Public_UpdateThenEmptyDoFinal_Success(String provider) throws Exception {
+        final PublicKey pubKey = (PublicKey) getEncryptKey("RSA");
+
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+
+        /*
+         * You're actually encrypting with public keys, but there is no
+         * distinction made here. It's all keyed off of what kind of key you're
+         * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+         */
+        c.init(Cipher.ENCRYPT_MODE, pubKey);
+        c.update(RSA_Vector1_Encrypt_Private);
+        byte[] encrypted = c.doFinal();
+        assertEncryptedEqualsNoPadding(provider, Cipher.ENCRYPT_MODE, RSA_2048_Vector1, encrypted);
+
+        c.init(Cipher.DECRYPT_MODE, pubKey);
+        c.update(RSA_Vector1_Encrypt_Private);
+        encrypted = c.doFinal();
+        assertEncryptedEqualsNoPadding(provider, Cipher.DECRYPT_MODE, RSA_2048_Vector1, encrypted);
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_Public_SingleByteUpdateThenEmptyDoFinal_Success()
+            throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_Public_SingleByteUpdateThenEmptyDoFinal_Success(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_Public_SingleByteUpdateThenEmptyDoFinal_Success(String provider)
+            throws Exception {
+        final PublicKey pubKey = (PublicKey) getEncryptKey("RSA");
+
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+
+        /*
+         * You're actually encrypting with public keys, but there is no
+         * distinction made here. It's all keyed off of what kind of key you're
+         * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+         */
+        c.init(Cipher.ENCRYPT_MODE, pubKey);
+        int i;
+        for (i = 0; i < RSA_Vector1_Encrypt_Private.length / 2; i++) {
+            c.update(RSA_Vector1_Encrypt_Private, i, 1);
+        }
+        byte[] encrypted = c.doFinal(RSA_Vector1_Encrypt_Private, i, RSA_2048_Vector1.length - i);
+        assertEncryptedEqualsNoPadding(provider, Cipher.ENCRYPT_MODE, RSA_2048_Vector1, encrypted);
+
+        c.init(Cipher.DECRYPT_MODE, pubKey);
+        for (i = 0; i < RSA_Vector1_Encrypt_Private.length / 2; i++) {
+            c.update(RSA_Vector1_Encrypt_Private, i, 1);
+        }
+        encrypted = c.doFinal(RSA_Vector1_Encrypt_Private, i, RSA_2048_Vector1.length - i);
+        assertEncryptedEqualsNoPadding(provider, Cipher.DECRYPT_MODE, RSA_2048_Vector1, encrypted);
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_Public_TooSmall_Success() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_Public_TooSmall_Success(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_Public_TooSmall_Success(String provider) throws Exception {
+        final PublicKey pubKey = (PublicKey) getEncryptKey("RSA");
+
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+
+        /*
+         * You're actually encrypting with public keys, but there is no
+         * distinction made here. It's all keyed off of what kind of key you're
+         * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+         */
+        c.init(Cipher.ENCRYPT_MODE, pubKey);
+        byte[] encrypted = c.doFinal(TooShort_Vector);
+        assertTrue("Encrypted should match expected",
+                Arrays.equals(RSA_Vector1_ZeroPadded_Encrypted, encrypted));
+
+        c.init(Cipher.DECRYPT_MODE, pubKey);
+        encrypted = c.doFinal(TooShort_Vector);
+        assertTrue("Encrypted should match expected",
+                Arrays.equals(RSA_Vector1_ZeroPadded_Encrypted, encrypted));
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_Private_TooSmall_Success() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_Private_TooSmall_Success(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_Private_TooSmall_Success(String provider) throws Exception {
+        final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA");
+
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+
+        /*
+         * You're actually encrypting with public keys, but there is no
+         * distinction made here. It's all keyed off of what kind of key you're
+         * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+         */
+        c.init(Cipher.ENCRYPT_MODE, privKey);
+        byte[] encrypted = c.doFinal(RSA_Vector1_ZeroPadded_Encrypted);
+        assertEncryptedEqualsNoPadding(provider, Cipher.ENCRYPT_MODE,
+                                       TooShort_Vector_Zero_Padded, encrypted);
+
+        c.init(Cipher.DECRYPT_MODE, privKey);
+        encrypted = c.doFinal(RSA_Vector1_ZeroPadded_Encrypted);
+        assertEncryptedEqualsNoPadding(provider, Cipher.DECRYPT_MODE,
+                                       TooShort_Vector_Zero_Padded, encrypted);
+    }
+
+    private static void assertEncryptedEqualsNoPadding(String provider, int mode,
+                                                       byte[] expected, byte[] actual) {
+        if (provider.equals("BC") && mode == Cipher.DECRYPT_MODE) {
+            // BouncyCastle does us the favor of stripping leading zeroes in DECRYPT_MODE
+            int nonZeroOffset = 0;
+            for (byte b : expected) {
+                if (b != 0) {
+                    break;
+                }
+                nonZeroOffset++;
+            }
+            expected = Arrays.copyOfRange(expected, nonZeroOffset, expected.length);
+        }
+        assertEquals("Encrypted should match expected",
+                     Arrays.toString(expected), Arrays.toString(actual));
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_Private_CombinedUpdateAndDoFinal_TooBig_Failure()
+            throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_Private_CombinedUpdateAndDoFinal_TooBig_Failure(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_Private_CombinedUpdateAndDoFinal_TooBig_Failure(String provider)
+            throws Exception {
+        final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA");
+
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+
+        /*
+         * You're actually encrypting with public keys, but there is no
+         * distinction made here. It's all keyed off of what kind of key you're
+         * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+         */
+        c.init(Cipher.ENCRYPT_MODE, privKey);
+        c.update(RSA_Vector1_ZeroPadded_Encrypted);
+
+        try {
+            c.doFinal(RSA_Vector1_ZeroPadded_Encrypted);
+            fail("Should have error when block size is too big.");
+        } catch (IllegalBlockSizeException success) {
+            assertFalse(provider, "BC".equals(provider));
+        } catch (ArrayIndexOutOfBoundsException success) {
+            assertEquals("BC", provider);
+        }
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_Private_UpdateInAndOutPlusDoFinal_TooBig_Failure()
+            throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_Private_UpdateInAndOutPlusDoFinal_TooBig_Failure(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_Private_UpdateInAndOutPlusDoFinal_TooBig_Failure(String provider)
+            throws Exception {
+        final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA");
+
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+
+        /*
+         * You're actually encrypting with public keys, but there is no
+         * distinction made here. It's all keyed off of what kind of key you're
+         * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+         */
+        c.init(Cipher.ENCRYPT_MODE, privKey);
+
+        byte[] output = new byte[RSA_2048_Vector1.length];
+        c.update(RSA_Vector1_ZeroPadded_Encrypted, 0, RSA_Vector1_ZeroPadded_Encrypted.length,
+                output);
+
+        try {
+            c.doFinal(RSA_Vector1_ZeroPadded_Encrypted);
+            fail("Should have error when block size is too big.");
+        } catch (IllegalBlockSizeException success) {
+            assertFalse(provider, "BC".equals(provider));
+        } catch (ArrayIndexOutOfBoundsException success) {
+            assertEquals("BC", provider);
+        }
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_Private_OnlyDoFinal_TooBig_Failure() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_Private_OnlyDoFinal_TooBig_Failure(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_Private_OnlyDoFinal_TooBig_Failure(String provider) throws Exception {
+        final PrivateKey privKey = (PrivateKey) getDecryptKey("RSA");
+
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+
+        /*
+         * You're actually encrypting with public keys, but there is no
+         * distinction made here. It's all keyed off of what kind of key you're
+         * using. ENCRYPT_MODE and DECRYPT_MODE are the same.
+         */
+        c.init(Cipher.ENCRYPT_MODE, privKey);
+
+        byte[] tooBig_Vector = new byte[RSA_Vector1_ZeroPadded_Encrypted.length * 2];
+        System.arraycopy(RSA_Vector1_ZeroPadded_Encrypted, 0, tooBig_Vector, 0,
+                RSA_Vector1_ZeroPadded_Encrypted.length);
+        System.arraycopy(RSA_Vector1_ZeroPadded_Encrypted, 0, tooBig_Vector,
+                RSA_Vector1_ZeroPadded_Encrypted.length, RSA_Vector1_ZeroPadded_Encrypted.length);
+
+        try {
+            c.doFinal(tooBig_Vector);
+            fail("Should have error when block size is too big.");
+        } catch (IllegalBlockSizeException success) {
+            assertFalse(provider, "BC".equals(provider));
+        } catch (ArrayIndexOutOfBoundsException success) {
+            assertEquals("BC", provider);
+        }
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_GetBlockSize_Success() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_GetBlockSize_Success(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_GetBlockSize_Success(String provider) throws Exception {
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+        if (provider.equals("SunJCE")) {
+            assertEquals(0, c.getBlockSize());
+        } else {
+            try {
+                c.getBlockSize();
+                fail();
+            } catch (IllegalStateException expected) {
+            }
+        }
+
+        final PublicKey pubKey = (PublicKey) getEncryptKey("RSA");
+        c.init(Cipher.ENCRYPT_MODE, pubKey);
+        assertEquals(getExpectedBlockSize("RSA", Cipher.ENCRYPT_MODE, provider), c.getBlockSize());
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_GetOutputSize_NoInit_Failure() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_GetOutputSize_NoInit_Failure(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_GetOutputSize_NoInit_Failure(String provider) throws Exception {
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+        try {
+            c.getOutputSize(RSA_2048_Vector1.length);
+            fail("Should throw IllegalStateException if getOutputSize is called before init");
+        } catch (IllegalStateException success) {
+        }
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_GetOutputSize_Success() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_GetOutputSize_Success(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_GetOutputSize_Success(String provider) throws Exception {
+        final PublicKey pubKey = (PublicKey) getEncryptKey("RSA");
+
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+        c.init(Cipher.ENCRYPT_MODE, pubKey);
+
+        final int modulusInBytes = RSA_2048_modulus.bitLength() / 8;
+        assertEquals(modulusInBytes, c.getOutputSize(RSA_2048_Vector1.length));
+        assertEquals(modulusInBytes, c.getOutputSize(RSA_2048_Vector1.length * 2));
+        assertEquals(modulusInBytes, c.getOutputSize(0));
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_GetIV_Success() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_GetIV_Success(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_GetIV_Success(String provider) throws Exception {
+        final PublicKey pubKey = (PublicKey) getEncryptKey("RSA");
+
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+        assertNull("ECB mode has no IV and should be null", c.getIV());
+
+        c.init(Cipher.ENCRYPT_MODE, pubKey);
+
+        assertNull("ECB mode has no IV and should be null", c.getIV());
+    }
+
+    @Test
+    public void testRSA_ECB_NoPadding_GetParameters_NoneProvided_Success() throws Exception {
+        for (String provider : RSA_PROVIDERS) {
+            testRSA_ECB_NoPadding_GetParameters_NoneProvided_Success(provider);
+        }
+    }
+
+    private void testRSA_ECB_NoPadding_GetParameters_NoneProvided_Success(String provider) throws Exception {
+        Cipher c = Cipher.getInstance("RSA/ECB/NoPadding", provider);
+        assertNull("Parameters should be null", c.getParameters());
+    }
+
+    /*
+     * Test vector generation:
+     * openssl rand -hex 16 | sed 's/\(..\)/(byte) 0x\1, /g'
+     */
+    private static final SecretKeySpec DES_112_KEY = new SecretKeySpec(new byte[] {
+            (byte) 0x6b, (byte) 0xb3, (byte) 0x85, (byte) 0x1c, (byte) 0x3d, (byte) 0x50,
+            (byte) 0xd4, (byte) 0x95, (byte) 0x39, (byte) 0x48, (byte) 0x77, (byte) 0x30,
+            (byte) 0x1a, (byte) 0xd7, (byte) 0x86, (byte) 0x57,
+    }, "DESede");
+
+    /*
+     * Test vector generation:
+     * openssl rand -hex 24 | sed 's/\(..\)/(byte) 0x\1, /g'
+     */
+    private static final SecretKeySpec DES_168_KEY = new SecretKeySpec(new byte[] {
+            (byte) 0xfe, (byte) 0xd4, (byte) 0xd7, (byte) 0xc9, (byte) 0x8a, (byte) 0x13,
+            (byte) 0x6a, (byte) 0xa8, (byte) 0x5a, (byte) 0xb8, (byte) 0x19, (byte) 0xb8,
+            (byte) 0xcf, (byte) 0x3c, (byte) 0x5f, (byte) 0xe0, (byte) 0xa2, (byte) 0xf7,
+            (byte) 0x7b, (byte) 0x65, (byte) 0x43, (byte) 0xc0, (byte) 0xc4, (byte) 0xe1,
+    }, "DESede");
+
+    /*
+     * Test vector generation:
+     * openssl rand -hex 5 | sed 's/\(..\)/(byte) 0x\1, /g'
+     */
+    private static final SecretKeySpec ARC4_40BIT_KEY = new SecretKeySpec(new byte[] {
+            (byte) 0x9c, (byte) 0xc8, (byte) 0xb9, (byte) 0x94, (byte) 0x98,
+    }, "ARC4");
+
+    /*
+     * Test vector generation:
+     * openssl rand -hex 24 | sed 's/\(..\)/(byte) 0x\1, /g'
+     */
+    private static final SecretKeySpec ARC4_128BIT_KEY = new SecretKeySpec(new byte[] {
+            (byte) 0xbc, (byte) 0x0a, (byte) 0x3c, (byte) 0xca, (byte) 0xb5, (byte) 0x42,
+            (byte) 0xfa, (byte) 0x5d, (byte) 0x86, (byte) 0x5b, (byte) 0x44, (byte) 0x87,
+            (byte) 0x83, (byte) 0xd8, (byte) 0xcb, (byte) 0xd4,
+    }, "ARC4");
+
+    /*
+     * Test vector generation:
+     * openssl rand -hex 16
+     * echo '3d4f8970b1f27537f40a39298a41555f' | sed 's/\(..\)/(byte) 0x\1, /g'
+     */
+    private static final SecretKeySpec AES_128_KEY = new SecretKeySpec(new byte[] {
+            (byte) 0x3d, (byte) 0x4f, (byte) 0x89, (byte) 0x70, (byte) 0xb1, (byte) 0xf2,
+            (byte) 0x75, (byte) 0x37, (byte) 0xf4, (byte) 0x0a, (byte) 0x39, (byte) 0x29,
+            (byte) 0x8a, (byte) 0x41, (byte) 0x55, (byte) 0x5f,
+    }, "AES");
+
+    /*
+     * Test key generation:
+     * openssl rand -hex 24
+     * echo '5a7a3d7e40b64ed996f7afa15f97fd595e27db6af428e342' | sed 's/\(..\)/(byte) 0x\1, /g'
+     */
+    private static final SecretKeySpec AES_192_KEY = new SecretKeySpec(new byte[] {
+            (byte) 0x5a, (byte) 0x7a, (byte) 0x3d, (byte) 0x7e, (byte) 0x40, (byte) 0xb6,
+            (byte) 0x4e, (byte) 0xd9, (byte) 0x96, (byte) 0xf7, (byte) 0xaf, (byte) 0xa1,
+            (byte) 0x5f, (byte) 0x97, (byte) 0xfd, (byte) 0x59, (byte) 0x5e, (byte) 0x27,
+            (byte) 0xdb, (byte) 0x6a, (byte) 0xf4, (byte) 0x28, (byte) 0xe3, (byte) 0x42,
+    }, "AES");
+
+    /*
+     * Test key generation:
+     * openssl rand -hex 32
+     * echo 'ec53c6d51d2c4973585fb0b8e51cd2e39915ff07a1837872715d6121bf861935' | sed 's/\(..\)/(byte) 0x\1, /g'
+     */
+    private static final SecretKeySpec AES_256_KEY = new SecretKeySpec(new byte[] {
+            (byte) 0xec, (byte) 0x53, (byte) 0xc6, (byte) 0xd5, (byte) 0x1d, (byte) 0x2c,
+            (byte) 0x49, (byte) 0x73, (byte) 0x58, (byte) 0x5f, (byte) 0xb0, (byte) 0xb8,
+            (byte) 0xe5, (byte) 0x1c, (byte) 0xd2, (byte) 0xe3, (byte) 0x99, (byte) 0x15,
+            (byte) 0xff, (byte) 0x07, (byte) 0xa1, (byte) 0x83, (byte) 0x78, (byte) 0x72,
+            (byte) 0x71, (byte) 0x5d, (byte) 0x61, (byte) 0x21, (byte) 0xbf, (byte) 0x86,
+            (byte) 0x19, (byte) 0x35,
+    }, "AES");
+
+    /*
+     * Test vector generation:
+     * echo -n 'Testing rocks!' | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] DES_Plaintext1 = new byte[] {
+            (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x69, (byte) 0x6E,
+            (byte) 0x67, (byte) 0x20, (byte) 0x72, (byte) 0x6F, (byte) 0x63, (byte) 0x6B,
+            (byte) 0x73, (byte) 0x21
+    };
+
+
+    /*
+     * Test vector generation: take DES_Plaintext1 and PKCS #5 pad it manually (it's not hard).
+     */
+    private static final byte[] DES_Plaintext1_PKCS5_Padded = new byte[] {
+            (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x69, (byte) 0x6E,
+            (byte) 0x67, (byte) 0x20, (byte) 0x72, (byte) 0x6F, (byte) 0x63, (byte) 0x6B,
+            (byte) 0x73, (byte) 0x21, (byte) 0x02, (byte) 0x02,
+    };
+
+    /*
+     * Test vector generation:
+     * openssl rand -hex 8 | sed 's/\(..\)/(byte) 0x\1, /g'
+     */
+    private static final byte[] DES_IV1 = new byte[] {
+            (byte) 0x5c, (byte) 0x47, (byte) 0x5e, (byte) 0x57, (byte) 0x0c, (byte) 0x46,
+            (byte) 0xcb, (byte) 0x47,
+    };
+
+    /*
+     * Test vector generation:
+     * openssl enc -des-ede-cbc -K 6bb3851c3d50d495394877301ad78657 -iv 5c475e570c46cb47 -in blah
+     * | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[]
+            DES_Plaintext1_Encrypted_With_DES_112_KEY_And_DESEDE_CBC_PKCS5PADDING_With_DES_IV1 =
+                    new byte[] {
+            (byte) 0x09, (byte) 0xA5, (byte) 0x5D, (byte) 0x94, (byte) 0x94, (byte) 0xAA,
+            (byte) 0x3F, (byte) 0xC8, (byte) 0xB7, (byte) 0x73, (byte) 0x94, (byte) 0x0E,
+            (byte) 0xFC, (byte) 0xF4, (byte) 0xA5, (byte) 0x28,
+    };
+
+
+    /*
+     * Test vector generation:
+     * openssl enc -des-ede3-cbc -K fed4d7c98a136aa85ab819b8cf3c5fe0a2f77b6543c0c4e1
+     *     -iv 5c475e570c46cb47 -in blah | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[]
+            DES_Plaintext1_Encrypted_With_DES_168_KEY_And_DESEDE_CBC_PKCS5PADDING_With_DES_IV1 =
+                    new byte[] {
+            (byte) 0xC9, (byte) 0xF1, (byte) 0x83, (byte) 0x1F, (byte) 0x24, (byte) 0x83,
+            (byte) 0x2C, (byte) 0x7B, (byte) 0x66, (byte) 0x66, (byte) 0x99, (byte) 0x98,
+            (byte) 0x27, (byte) 0xB0, (byte) 0xED, (byte) 0x47
+    };
+
+
+    /*
+     * Test vector generation:
+     * echo -n 'Plaintext for arc4' | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] ARC4_Plaintext1 = new byte[] {
+            (byte) 0x50, (byte) 0x6C, (byte) 0x61, (byte) 0x69, (byte) 0x6E, (byte) 0x74,
+            (byte) 0x65, (byte) 0x78, (byte) 0x74, (byte) 0x20, (byte) 0x66, (byte) 0x6F,
+            (byte) 0x72, (byte) 0x20, (byte) 0x61, (byte) 0x72, (byte) 0x63, (byte) 0x34
+    };
+
+    /*
+     * Test vector generation:
+     *  echo -n 'Plaintext for arc4' | openssl enc -rc4-40 -K 9cc8b99498 | recode ../x1 \
+     *     | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] ARC4_Plaintext1_Encrypted_With_ARC4_40Bit_Key = new byte[] {
+            (byte) 0x63, (byte) 0xF7, (byte) 0x11, (byte) 0x90, (byte) 0x63, (byte) 0xEF,
+            (byte) 0x5E, (byte) 0xB3, (byte) 0x93, (byte) 0xB3, (byte) 0x46, (byte) 0x3F,
+            (byte) 0x1B, (byte) 0x02, (byte) 0x53, (byte) 0x9B, (byte) 0xD9, (byte) 0xE0
+    };
+
+    /*
+     * Test vector generation:
+     *  echo -n 'Plaintext for arc4' | openssl enc -rc4 -K bc0a3ccab542fa5d865b448783d8cbd4 \
+     *     | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] ARC4_Plaintext1_Encrypted_With_ARC4_128Bit_Key = new byte[] {
+            (byte) 0x25, (byte) 0x14, (byte) 0xA9, (byte) 0x72, (byte) 0x4D, (byte) 0xA9,
+            (byte) 0xF6, (byte) 0xA7, (byte) 0x2F, (byte) 0xB7, (byte) 0x0D, (byte) 0x60,
+            (byte) 0x09, (byte) 0xBE, (byte) 0x41, (byte) 0x9B, (byte) 0x32, (byte) 0x2B
+    };
+
+    /*
+     * Test vector creation:
+     * echo -n 'Hello, world!' | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext = new byte[] {
+            (byte) 0x48, (byte) 0x65, (byte) 0x6C, (byte) 0x6C, (byte) 0x6F, (byte) 0x2C,
+            (byte) 0x20, (byte) 0x77, (byte) 0x6F, (byte) 0x72, (byte) 0x6C, (byte) 0x64,
+            (byte) 0x21,
+    };
+
+    /*
+     * Test vector creation:
+     * openssl enc -aes-128-ecb -K 3d4f8970b1f27537f40a39298a41555f -in blah|openssl enc -aes-128-ecb -K 3d4f8970b1f27537f40a39298a41555f -nopad -d|recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded = new byte[] {
+            (byte) 0x48, (byte) 0x65, (byte) 0x6C, (byte) 0x6C, (byte) 0x6F, (byte) 0x2C,
+            (byte) 0x20, (byte) 0x77, (byte) 0x6F, (byte) 0x72, (byte) 0x6C, (byte) 0x64,
+            (byte) 0x21, (byte) 0x03, (byte) 0x03, (byte) 0x03
+    };
+
+    /*
+     * Test vector generation:
+     * openssl enc -aes-128-ecb -K 3d4f8970b1f27537f40a39298a41555f -in blah|recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted = new byte[] {
+            (byte) 0x65, (byte) 0x3E, (byte) 0x86, (byte) 0xFB, (byte) 0x05, (byte) 0x5A,
+            (byte) 0x52, (byte) 0xEA, (byte) 0xDD, (byte) 0x08, (byte) 0xE7, (byte) 0x48,
+            (byte) 0x33, (byte) 0x01, (byte) 0xFC, (byte) 0x5A,
+    };
+
+    /*
+     * Taken from BoringSSL test vectors.
+     */
+    private static final SecretKeySpec AES_128_GCM_TestVector_1_Key = new SecretKeySpec(new byte[] {
+            (byte) 0xca, (byte) 0xbd, (byte) 0xcf, (byte) 0x54, (byte) 0x1a, (byte) 0xeb,
+            (byte) 0xf9, (byte) 0x17, (byte) 0xba, (byte) 0xc0, (byte) 0x19, (byte) 0xf1,
+            (byte) 0x39, (byte) 0x25, (byte) 0xd2, (byte) 0x67,
+    }, "AES");
+
+    /*
+     * Taken from BoringSSL test vectors.
+     */
+    private static final byte[] AES_128_GCM_TestVector_1_IV = new byte[] {
+            (byte) 0x2c, (byte) 0x34, (byte) 0xc0, (byte) 0x0c, (byte) 0x42, (byte) 0xda,
+            (byte) 0xe3, (byte) 0x82, (byte) 0x27, (byte) 0x9d, (byte) 0x79, (byte) 0x74,
+    };
+
+    /*
+     * Taken from BoringSSL test vectors.
+     */
+    private static final byte[] AES_128_GCM_TestVector_1_AAD = new byte[] {
+            (byte) 0xdd, (byte) 0x10, (byte) 0xe3, (byte) 0x71, (byte) 0xb2, (byte) 0x2e,
+            (byte) 0x15, (byte) 0x67, (byte) 0x1c, (byte) 0x31, (byte) 0xaf, (byte) 0xee,
+            (byte) 0x55, (byte) 0x2b, (byte) 0xf1, (byte) 0xde, (byte) 0xa0, (byte) 0x7c,
+            (byte) 0xbb, (byte) 0xf6, (byte) 0x85, (byte) 0xe2, (byte) 0xca, (byte) 0xa0,
+            (byte) 0xe0, (byte) 0x36, (byte) 0x37, (byte) 0x16, (byte) 0xa2, (byte) 0x76,
+            (byte) 0xe1, (byte) 0x20, (byte) 0xc6, (byte) 0xc0, (byte) 0xeb, (byte) 0x4a,
+            (byte) 0xcb, (byte) 0x1a, (byte) 0x4d, (byte) 0x1b, (byte) 0xa7, (byte) 0x3f,
+            (byte) 0xde, (byte) 0x66, (byte) 0x15, (byte) 0xf7, (byte) 0x08, (byte) 0xaa,
+            (byte) 0xa4, (byte) 0x6b, (byte) 0xc7, (byte) 0x6c, (byte) 0x7f, (byte) 0xf3,
+            (byte) 0x45, (byte) 0xa4, (byte) 0xf7, (byte) 0x6b, (byte) 0xda, (byte) 0x11,
+            (byte) 0x7f, (byte) 0xe5, (byte) 0x6f, (byte) 0x0d, (byte) 0xc9, (byte) 0xb9,
+            (byte) 0x39, (byte) 0x04, (byte) 0x0d, (byte) 0xdd,
+    };
+
+    /*
+     * Taken from BoringSSL test vectors.
+     */
+    private static final byte[] AES_128_GCM_TestVector_1_Plaintext = new byte[] {
+            (byte) 0x88, (byte) 0xcc, (byte) 0x1e, (byte) 0x07, (byte) 0xdf, (byte) 0xde,
+            (byte) 0x8e, (byte) 0x08, (byte) 0x08, (byte) 0x2e, (byte) 0x67, (byte) 0x66,
+            (byte) 0xe0, (byte) 0xa8, (byte) 0x81, (byte) 0x03, (byte) 0x38, (byte) 0x47,
+            (byte) 0x42, (byte) 0xaf, (byte) 0x37, (byte) 0x8d, (byte) 0x7b, (byte) 0x6b,
+            (byte) 0x8a, (byte) 0x87, (byte) 0xfc, (byte) 0xe0, (byte) 0x36, (byte) 0xaf,
+            (byte) 0x74, (byte) 0x41, (byte) 0xc1, (byte) 0x39, (byte) 0x61, (byte) 0xc2,
+            (byte) 0x5a, (byte) 0xfe, (byte) 0xa7, (byte) 0xf6, (byte) 0xe5, (byte) 0x61,
+            (byte) 0x93, (byte) 0xf5, (byte) 0x4b, (byte) 0xee, (byte) 0x00, (byte) 0x11,
+            (byte) 0xcb, (byte) 0x78, (byte) 0x64, (byte) 0x2c, (byte) 0x3a, (byte) 0xb9,
+            (byte) 0xe6, (byte) 0xd5, (byte) 0xb2, (byte) 0xe3, (byte) 0x58, (byte) 0x33,
+            (byte) 0xec, (byte) 0x16, (byte) 0xcd, (byte) 0x35, (byte) 0x55, (byte) 0x15,
+            (byte) 0xaf, (byte) 0x1a, (byte) 0x19, (byte) 0x0f,
+    };
+
+    /*
+     * Taken from BoringSSL test vectors.
+     */
+    private static final byte[] AES_128_GCM_TestVector_1_Encrypted = new byte[] {
+            (byte) 0x04, (byte) 0x94, (byte) 0x53, (byte) 0xba, (byte) 0xf1, (byte) 0x57,
+            (byte) 0x87, (byte) 0x87, (byte) 0xd6, (byte) 0x8e, (byte) 0xd5, (byte) 0x47,
+            (byte) 0x87, (byte) 0x26, (byte) 0xc0, (byte) 0xb8, (byte) 0xa6, (byte) 0x36,
+            (byte) 0x33, (byte) 0x7a, (byte) 0x0b, (byte) 0x8a, (byte) 0x82, (byte) 0xb8,
+            (byte) 0x68, (byte) 0x36, (byte) 0xf9, (byte) 0x1c, (byte) 0xde, (byte) 0x25,
+            (byte) 0xe6, (byte) 0xe4, (byte) 0x4c, (byte) 0x34, (byte) 0x59, (byte) 0x40,
+            (byte) 0xe8, (byte) 0x19, (byte) 0xa0, (byte) 0xc5, (byte) 0x05, (byte) 0x75,
+            (byte) 0x1e, (byte) 0x60, (byte) 0x3c, (byte) 0xb8, (byte) 0xf8, (byte) 0xc4,
+            (byte) 0xfe, (byte) 0x98, (byte) 0x71, (byte) 0x91, (byte) 0x85, (byte) 0x56,
+            (byte) 0x27, (byte) 0x94, (byte) 0xa1, (byte) 0x85, (byte) 0xe5, (byte) 0xde,
+            (byte) 0xc4, (byte) 0x15, (byte) 0xc8, (byte) 0x1f, (byte) 0x2f, (byte) 0x16,
+            (byte) 0x2c, (byte) 0xdc, (byte) 0xd6, (byte) 0x50, (byte) 0xdc, (byte) 0xe7,
+            (byte) 0x19, (byte) 0x87, (byte) 0x28, (byte) 0xbf, (byte) 0xc1, (byte) 0xb5,
+            (byte) 0xf9, (byte) 0x49, (byte) 0xb9, (byte) 0xb5, (byte) 0x37, (byte) 0x41,
+            (byte) 0x99, (byte) 0xc6,
+    };
+
+    /*
+     * Test key generation:
+     * openssl rand -hex 16
+     * echo '787bdeecf05556eac5d3d865e435f6d9' | sed 's/\(..\)/(byte) 0x\1, /g'
+     */
+    private static final byte[] AES_192_CTR_NoPadding_TestVector_1_IV = new byte[] {
+            (byte) 0x78, (byte) 0x7b, (byte) 0xde, (byte) 0xec, (byte) 0xf0, (byte) 0x55,
+            (byte) 0x56, (byte) 0xea, (byte) 0xc5, (byte) 0xd3, (byte) 0xd8, (byte) 0x65,
+            (byte) 0xe4, (byte) 0x35, (byte) 0xf6, (byte) 0xd9,
+
+    };
+
+    /*
+     * Test vector generation:
+     * echo -n 'AES-192 is a silly option' | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] AES_192_CTR_NoPadding_TestVector_1_Plaintext = new byte[] {
+            (byte) 0x41, (byte) 0x45, (byte) 0x53, (byte) 0x2D, (byte) 0x31, (byte) 0x39,
+            (byte) 0x32, (byte) 0x20, (byte) 0x69, (byte) 0x73, (byte) 0x20, (byte) 0x61,
+            (byte) 0x20, (byte) 0x73, (byte) 0x69, (byte) 0x6C, (byte) 0x6C, (byte) 0x79,
+            (byte) 0x20, (byte) 0x6F, (byte) 0x70, (byte) 0x74, (byte) 0x69, (byte) 0x6F,
+            (byte) 0x6E
+    };
+
+    /*
+     * Test vector generation:
+     * echo -n 'AES-192 is a silly option' | openssl enc -aes-192-ctr -K 5a7a3d7e40b64ed996f7afa15f97fd595e27db6af428e342 -iv 787bdeecf05556eac5d3d865e435f6d9 | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] AES_192_CTR_NoPadding_TestVector_1_Ciphertext = new byte[] {
+            (byte) 0xE9, (byte) 0xC6, (byte) 0xA0, (byte) 0x40, (byte) 0xC2, (byte) 0x6A,
+            (byte) 0xB5, (byte) 0x20, (byte) 0xFE, (byte) 0x9E, (byte) 0x65, (byte) 0xB7,
+            (byte) 0x7C, (byte) 0x5E, (byte) 0xFE, (byte) 0x1F, (byte) 0xF1, (byte) 0x6F,
+            (byte) 0x20, (byte) 0xAC, (byte) 0x37, (byte) 0xE9, (byte) 0x75, (byte) 0xE3,
+            (byte) 0x52
+    };
+
+    /*
+     * Test key generation: openssl rand -hex 16 echo
+     * 'ceaa31952dfd3d0f5af4b2042ba06094' | sed 's/\(..\)/(byte) 0x\1, /g'
+     */
+    private static final byte[] AES_256_CBC_PKCS5Padding_TestVector_1_IV = new byte[] {
+            (byte) 0xce, (byte) 0xaa, (byte) 0x31, (byte) 0x95, (byte) 0x2d, (byte) 0xfd,
+            (byte) 0x3d, (byte) 0x0f, (byte) 0x5a, (byte) 0xf4, (byte) 0xb2, (byte) 0x04,
+            (byte) 0x2b, (byte) 0xa0, (byte) 0x60, (byte) 0x94,
+    };
+
+    /*
+     * Test vector generation:
+     * echo -n 'I only regret that I have but one test to write.' | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext = new byte[] {
+            (byte) 0x49, (byte) 0x20, (byte) 0x6F, (byte) 0x6E, (byte) 0x6C, (byte) 0x79,
+            (byte) 0x20, (byte) 0x72, (byte) 0x65, (byte) 0x67, (byte) 0x72, (byte) 0x65,
+            (byte) 0x74, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x61, (byte) 0x74,
+            (byte) 0x20, (byte) 0x49, (byte) 0x20, (byte) 0x68, (byte) 0x61, (byte) 0x76,
+            (byte) 0x65, (byte) 0x20, (byte) 0x62, (byte) 0x75, (byte) 0x74, (byte) 0x20,
+            (byte) 0x6F, (byte) 0x6E, (byte) 0x65, (byte) 0x20, (byte) 0x74, (byte) 0x65,
+            (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x74, (byte) 0x6F, (byte) 0x20,
+            (byte) 0x77, (byte) 0x72, (byte) 0x69, (byte) 0x74, (byte) 0x65, (byte) 0x2E
+    };
+
+    /*
+     * Test vector generation:
+     * echo -n 'I only regret that I have but one test to write.' | openssl enc -aes-256-cbc -K ec53c6d51d2c4973585fb0b8e51cd2e39915ff07a1837872715d6121bf861935 -iv ceaa31952dfd3d0f5af4b2042ba06094 | openssl enc -aes-256-cbc -K ec53c6d51d2c4973585fb0b8e51cd2e39915ff07a1837872715d6121bf861935 -iv ceaa31952dfd3d0f5af4b2042ba06094 -d -nopad | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext_Padded = new byte[] {
+            (byte) 0x49, (byte) 0x20, (byte) 0x6F, (byte) 0x6E, (byte) 0x6C, (byte) 0x79,
+            (byte) 0x20, (byte) 0x72, (byte) 0x65, (byte) 0x67, (byte) 0x72, (byte) 0x65,
+            (byte) 0x74, (byte) 0x20, (byte) 0x74, (byte) 0x68, (byte) 0x61, (byte) 0x74,
+            (byte) 0x20, (byte) 0x49, (byte) 0x20, (byte) 0x68, (byte) 0x61, (byte) 0x76,
+            (byte) 0x65, (byte) 0x20, (byte) 0x62, (byte) 0x75, (byte) 0x74, (byte) 0x20,
+            (byte) 0x6F, (byte) 0x6E, (byte) 0x65, (byte) 0x20, (byte) 0x74, (byte) 0x65,
+            (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x74, (byte) 0x6F, (byte) 0x20,
+            (byte) 0x77, (byte) 0x72, (byte) 0x69, (byte) 0x74, (byte) 0x65, (byte) 0x2E,
+            (byte) 0x10, (byte) 0x10, (byte) 0x10, (byte) 0x10, (byte) 0x10, (byte) 0x10,
+            (byte) 0x10, (byte) 0x10, (byte) 0x10, (byte) 0x10, (byte) 0x10, (byte) 0x10,
+            (byte) 0x10, (byte) 0x10, (byte) 0x10, (byte) 0x10
+    };
+
+    /*
+     * Test vector generation:
+     * echo -n 'I only regret that I have but one test to write.' | openssl enc -aes-256-cbc -K ec53c6d51d2c4973585fb0b8e51cd2e39915ff07a1837872715d6121bf861935 -iv ceaa31952dfd3d0f5af4b2042ba06094 | recode ../x1 | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] AES_256_CBC_PKCS5Padding_TestVector_1_Ciphertext = new byte[] {
+            (byte) 0x90, (byte) 0x65, (byte) 0xDD, (byte) 0xAF, (byte) 0x7A, (byte) 0xCE,
+            (byte) 0xAE, (byte) 0xBF, (byte) 0xE8, (byte) 0xF6, (byte) 0x9E, (byte) 0xDB,
+            (byte) 0xEA, (byte) 0x65, (byte) 0x28, (byte) 0xC4, (byte) 0x9A, (byte) 0x28,
+            (byte) 0xEA, (byte) 0xA3, (byte) 0x95, (byte) 0x2E, (byte) 0xFF, (byte) 0xF1,
+            (byte) 0xA0, (byte) 0xCA, (byte) 0xC2, (byte) 0xA4, (byte) 0x65, (byte) 0xCD,
+            (byte) 0xBF, (byte) 0xCE, (byte) 0x9E, (byte) 0xF1, (byte) 0x57, (byte) 0xF6,
+            (byte) 0x32, (byte) 0x2E, (byte) 0x8F, (byte) 0x93, (byte) 0x2E, (byte) 0xAE,
+            (byte) 0x41, (byte) 0x33, (byte) 0x54, (byte) 0xD0, (byte) 0xEF, (byte) 0x8C,
+            (byte) 0x52, (byte) 0x14, (byte) 0xAC, (byte) 0x2D, (byte) 0xD5, (byte) 0xA4,
+            (byte) 0xF9, (byte) 0x20, (byte) 0x77, (byte) 0x25, (byte) 0x91, (byte) 0x3F,
+            (byte) 0xD1, (byte) 0xB9, (byte) 0x00, (byte) 0x3E
+    };
+
+    private static class CipherTestParam {
+        public final String transformation;
+
+        public final AlgorithmParameterSpec spec;
+
+        public final Key encryptKey;
+
+        public final Key decryptKey;
+
+        public final byte[] aad;
+
+        public final byte[] plaintext;
+
+        public final byte[] ciphertext;
+
+        public final byte[] plaintextPadded;
+
+        public final boolean isStreamCipher;
+
+        public CipherTestParam(String transformation, AlgorithmParameterSpec spec, Key encryptKey,
+                Key decryptKey, byte[] aad, byte[] plaintext, byte[] plaintextPadded,
+                byte[] ciphertext, boolean isStreamCipher) {
+            this.transformation = transformation.toUpperCase(Locale.ROOT);
+            this.spec = spec;
+            this.encryptKey = encryptKey;
+            this.decryptKey = decryptKey;
+            this.aad = aad;
+            this.plaintext = plaintext;
+            this.plaintextPadded = plaintextPadded;
+            this.ciphertext = ciphertext;
+            this.isStreamCipher = isStreamCipher;
+        }
+
+        public CipherTestParam(String transformation, AlgorithmParameterSpec spec, Key key,
+                byte[] aad, byte[] plaintext, byte[] plaintextPadded, byte[] ciphertext,
+                boolean isStreamCipher) {
+            this(transformation, spec, key, key, aad, plaintext, plaintextPadded, ciphertext,
+                    isStreamCipher);
+        }
+
+        public CipherTestParam(String transformation, AlgorithmParameterSpec spec, Key key,
+                byte[] aad, byte[] plaintext, byte[] plaintextPadded, byte[] ciphertext) {
+            this(transformation, spec, key, aad, plaintext, plaintextPadded, ciphertext,
+                    false /* isStreamCipher */);
+        }
+
+        public boolean compatibleWith(String provider) {
+            // SunJCE doesn't support PKCS7Padding
+            if (provider.equals("SunJCE") && transformation.endsWith("/PKCS7PADDING")) {
+                return false;
+            }
+            return true;
+        }
+    }
+
+    private static class OAEPCipherTestParam extends CipherTestParam {
+        public OAEPCipherTestParam(String transformation, OAEPParameterSpec spec,
+                PublicKey encryptKey, PrivateKey decryptKey, byte[] plaintext, byte[] ciphertext) {
+            super(transformation, spec, encryptKey, decryptKey, null, plaintext, plaintext, ciphertext,
+                    false);
+        }
+
+        @Override
+        public boolean compatibleWith(String provider) {
+            // OAEP transformations have two digests, the "main" digest and the MGF-1 digest.
+            // BC and Conscrypt set the MGF-1 digest to the same as the main digest when it's
+            // not specified, whereas Sun's provider sets it to SHA-1.  Thus, the results from
+            // the different providers won't match when there isn't an explicit MGF-1 digest set
+            // and the main digest isn't SHA-1.  See b/22405492.
+            if (provider.equals("SunJCE")
+                    && (spec == null)
+                    && !transformation.toUpperCase(Locale.US).equals("RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING")) {
+                return false;
+            }
+            return true;
+        }
+    }
+
+    private static List<CipherTestParam> DES_CIPHER_TEST_PARAMS = new ArrayList<CipherTestParam>();
+    static {
+        DES_CIPHER_TEST_PARAMS.add(new CipherTestParam(
+                "DESede/CBC/PKCS5Padding",
+                new IvParameterSpec(DES_IV1),
+                DES_112_KEY,
+                null,
+                DES_Plaintext1,
+                DES_Plaintext1_PKCS5_Padded,
+                DES_Plaintext1_Encrypted_With_DES_112_KEY_And_DESEDE_CBC_PKCS5PADDING_With_DES_IV1
+                ) {
+                    @Override
+                    public boolean compatibleWith(String provider) {
+                        // SunJCE doesn't support extending 112-bit keys to 168-bit keys
+                        return !provider.equals("SunJCE");
+                    }
+                });
+        DES_CIPHER_TEST_PARAMS.add(new CipherTestParam(
+                "DESede/CBC/PKCS5Padding",
+                new IvParameterSpec(DES_IV1),
+                DES_168_KEY,
+                null,
+                DES_Plaintext1,
+                DES_Plaintext1_PKCS5_Padded,
+                DES_Plaintext1_Encrypted_With_DES_168_KEY_And_DESEDE_CBC_PKCS5PADDING_With_DES_IV1
+                ));
+    }
+
+    private static List<CipherTestParam> ARC4_CIPHER_TEST_PARAMS = new ArrayList<CipherTestParam>();
+    static {
+        ARC4_CIPHER_TEST_PARAMS.add(new CipherTestParam(
+                "ARC4",
+                null,
+                ARC4_40BIT_KEY,
+                null, // aad
+                ARC4_Plaintext1,
+                null, // padded
+                ARC4_Plaintext1_Encrypted_With_ARC4_40Bit_Key,
+                true /*isStreamCipher */
+        ));
+        ARC4_CIPHER_TEST_PARAMS.add(new CipherTestParam(
+                "ARC4",
+                null,
+                ARC4_128BIT_KEY,
+                null, // aad
+                ARC4_Plaintext1,
+                null, // padded
+                ARC4_Plaintext1_Encrypted_With_ARC4_128Bit_Key,
+                true /*isStreamCipher */
+        ));
+    }
+
+    private static List<CipherTestParam> CIPHER_TEST_PARAMS = new ArrayList<CipherTestParam>();
+    static {
+        CIPHER_TEST_PARAMS.add(new CipherTestParam(
+                "AES/ECB/PKCS5Padding",
+                null,
+                AES_128_KEY,
+                null,
+                AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext,
+                AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded,
+                AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted));
+        // PKCS#5 is assumed to be equivalent to PKCS#7 -- same test vectors are thus used for both.
+        CIPHER_TEST_PARAMS.add(new CipherTestParam(
+                "AES/ECB/PKCS7Padding",
+                null,
+                AES_128_KEY,
+                null,
+                AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext,
+                AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded,
+                AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted));
+        CIPHER_TEST_PARAMS.add(new CipherTestParam(
+                "AES/GCM/NOPADDING",
+                new GCMParameterSpec(
+                        (AES_128_GCM_TestVector_1_Encrypted.length -
+                                AES_128_GCM_TestVector_1_Plaintext.length) * 8,
+                        AES_128_GCM_TestVector_1_IV),
+                AES_128_GCM_TestVector_1_Key,
+                AES_128_GCM_TestVector_1_AAD,
+                AES_128_GCM_TestVector_1_Plaintext,
+                AES_128_GCM_TestVector_1_Plaintext,
+                AES_128_GCM_TestVector_1_Encrypted));
+        if (IS_UNLIMITED) {
+            CIPHER_TEST_PARAMS.add(new CipherTestParam(
+                    "AES/CTR/NoPadding",
+                    new IvParameterSpec(AES_192_CTR_NoPadding_TestVector_1_IV),
+                    AES_192_KEY,
+                    null,
+                    AES_192_CTR_NoPadding_TestVector_1_Plaintext,
+                    AES_192_CTR_NoPadding_TestVector_1_Plaintext,
+                    AES_192_CTR_NoPadding_TestVector_1_Ciphertext));
+            CIPHER_TEST_PARAMS.add(new CipherTestParam(
+                    "AES/CBC/PKCS5Padding",
+                    new IvParameterSpec(AES_256_CBC_PKCS5Padding_TestVector_1_IV),
+                    AES_256_KEY,
+                    null,
+                    AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext,
+                    AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext_Padded,
+                    AES_256_CBC_PKCS5Padding_TestVector_1_Ciphertext));
+            CIPHER_TEST_PARAMS.add(new CipherTestParam(
+                    "AES/CBC/PKCS7Padding",
+                    new IvParameterSpec(AES_256_CBC_PKCS5Padding_TestVector_1_IV),
+                    AES_256_KEY,
+                    null,
+                    AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext,
+                    AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext_Padded,
+                    AES_256_CBC_PKCS5Padding_TestVector_1_Ciphertext));
+        }
+    }
+
+    private static final List<CipherTestParam> RSA_OAEP_CIPHER_TEST_PARAMS = new ArrayList<CipherTestParam>();
+    static {
+        addRsaOaepTest("SHA-1", MGF1ParameterSpec.SHA1, RSA_Vector2_OAEP_SHA1_MGF1_SHA1);
+        addRsaOaepTest("SHA-256", MGF1ParameterSpec.SHA1, RSA_Vector2_OAEP_SHA256_MGF1_SHA1);
+        addRsaOaepTest("SHA-224", MGF1ParameterSpec.SHA224, RSA_Vector2_OAEP_SHA224_MGF1_SHA224);
+        addRsaOaepTest("SHA-256", MGF1ParameterSpec.SHA256, RSA_Vector2_OAEP_SHA256_MGF1_SHA256);
+        addRsaOaepTest("SHA-384", MGF1ParameterSpec.SHA384, RSA_Vector2_OAEP_SHA384_MGF1_SHA384);
+        addRsaOaepTest("SHA-512", MGF1ParameterSpec.SHA512, RSA_Vector2_OAEP_SHA512_MGF1_SHA512);
+        addRsaOaepTest("SHA-256", MGF1ParameterSpec.SHA1, RSA_Vector2_OAEP_SHA256_MGF1_SHA1_LABEL,
+                new byte[] { 0x01, 0x02, 0x03, (byte) 0xFF, (byte) 0xA0, 0x0A });
+        addRsaOaepTest("SHA-512", MGF1ParameterSpec.SHA512, RSA_Vector2_OAEP_SHA512_MGF1_SHA512_LABEL,
+                new byte[] { 0x01, 0x02, 0x03, (byte) 0xFF, (byte) 0xA0, 0x0A });
+    }
+
+    private static void addRsaOaepTest(String digest, MGF1ParameterSpec mgf1Spec, byte[] vector) {
+        addRsaOaepTest(digest, mgf1Spec, vector, null);
+    }
+
+    private static void addRsaOaepTest(String digest, MGF1ParameterSpec mgf1Spec, byte[] vector, byte[] label) {
+        final PSource pSource;
+        if (label == null) {
+            pSource = PSource.PSpecified.DEFAULT;
+        } else {
+            pSource = new PSource.PSpecified(label);
+        }
+
+        if (mgf1Spec.getDigestAlgorithm().equals(digest) && label == null) {
+            RSA_OAEP_CIPHER_TEST_PARAMS.add(new OAEPCipherTestParam(
+                    "RSA/ECB/OAEPWith" + digest + "AndMGF1Padding",
+                    null,
+                    (PublicKey) getEncryptKey("RSA"),
+                    (PrivateKey) getDecryptKey("RSA"),
+                    RSA_Vector2_Plaintext,
+                    vector));
+        }
+
+        RSA_OAEP_CIPHER_TEST_PARAMS.add(new OAEPCipherTestParam(
+                "RSA/ECB/OAEPWith" + digest + "AndMGF1Padding",
+                new OAEPParameterSpec(digest, "MGF1", mgf1Spec, pSource),
+                (PublicKey) getEncryptKey("RSA"),
+                (PrivateKey) getDecryptKey("RSA"),
+                RSA_Vector2_Plaintext,
+                vector));
+
+        RSA_OAEP_CIPHER_TEST_PARAMS.add(new OAEPCipherTestParam(
+                "RSA/ECB/OAEPPadding",
+                new OAEPParameterSpec(digest, "MGF1", mgf1Spec, pSource),
+                (PublicKey) getEncryptKey("RSA"),
+                (PrivateKey) getDecryptKey("RSA"),
+                RSA_Vector2_Plaintext,
+                vector));
+    }
+
+    @Test
+    public void testCipher_Success() throws Exception {
+        for (String provider : AES_PROVIDERS) {
+            testCipher_Success(provider);
+        }
+
+        testCipher_Success_ForAllSupportingProviders_AtLeastOneProviderRequired(
+                DES_CIPHER_TEST_PARAMS);
+        testCipher_Success_ForAllSupportingProviders_AtLeastOneProviderRequired(
+                ARC4_CIPHER_TEST_PARAMS);
+        testCipher_Success_ForAllSupportingProviders_AtLeastOneProviderRequired(
+                RSA_OAEP_CIPHER_TEST_PARAMS);
+    }
+
+    /**
+     * For each test vector in the list, tests that the transformation is supported by at least one
+     * provider and that all implementations of the transformation pass the Known Answer Test (KAT)
+     * as well as other functional tests.
+     */
+    private void testCipher_Success_ForAllSupportingProviders_AtLeastOneProviderRequired(
+            List<CipherTestParam> testVectors) throws Exception {
+        ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
+        PrintStream out = new PrintStream(errBuffer);
+        for (CipherTestParam testVector : testVectors) {
+            ArrayList<Provider> providers = new ArrayList<Provider>();
+
+            Provider[] providerArray = Security.getProviders("Cipher." + testVector.transformation);
+            if (providerArray != null) {
+                Collections.addAll(providers, providerArray);
+            }
+
+            if (testVector.transformation.indexOf('/') > 0) {
+                Provider[] baseTransformProviderArray = Security.getProviders("Cipher."
+                        + testVector.transformation.substring(
+                                  0, testVector.transformation.indexOf('/')));
+                if (baseTransformProviderArray != null) {
+                    Collections.addAll(providers, baseTransformProviderArray);
+                }
+            }
+
+            if (providers.isEmpty()) {
+                out.append("No providers offer " + testVector.transformation + "\n");
+                continue;
+            }
+
+            for (Provider provider : providers) {
+                // Do not test AndroidKeyStore's Signature. It needs an AndroidKeyStore-specific key.
+                // It's OKish not to test AndroidKeyStore's Signature here because it's tested
+                // by cts/tests/test/keystore.
+                if (provider.getName().startsWith("AndroidKeyStore")) {
+                    continue;
+                }
+
+                try {
+                    checkCipher(testVector, provider.getName());
+                } catch (Throwable e) {
+                    logTestFailure(out, provider.getName(), testVector, e);
+                }
+            }
+        }
+        out.flush();
+        if (errBuffer.size() > 0) {
+            throw new Exception("Errors encountered:\n\n" + errBuffer.toString() + "\n\n");
+        }
+    }
+
+    private void testCipher_Success(String provider) throws Exception {
+        final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
+        PrintStream out = new PrintStream(errBuffer);
+        for (CipherTestParam p : CIPHER_TEST_PARAMS) {
+            try {
+                checkCipher(p, provider);
+            } catch (Throwable e) {
+                logTestFailure(out, provider, p, e);
+            }
+        }
+        out.flush();
+        if (errBuffer.size() > 0) {
+            throw new Exception("Errors encountered:\n\n" + errBuffer.toString() + "\n\n");
+        }
+    }
+
+    private void logTestFailure(PrintStream logStream, String provider, CipherTestParam params,
+            Throwable e) {
+        logStream.append("Error encountered checking " + params.transformation);
+
+        if (params.encryptKey instanceof SecretKey) {
+            logStream.append(", keySize=" + (params.encryptKey.getEncoded().length * 8));
+        }
+
+        if (params.spec instanceof OAEPParameterSpec) {
+            OAEPParameterSpec oaepSpec = (OAEPParameterSpec) params.spec;
+            logStream.append(", OAEPSpec{digest=" + oaepSpec.getDigestAlgorithm() + ", mgfAlg="
+                    + oaepSpec.getMGFAlgorithm());
+            if (oaepSpec.getMGFParameters() instanceof MGF1ParameterSpec) {
+                MGF1ParameterSpec mgf1Spec = (MGF1ParameterSpec) oaepSpec.getMGFParameters();
+                logStream.append(", mgf1Hash=" + mgf1Spec.getDigestAlgorithm());
+            }
+            logStream.append(", pSource=");
+            PSource pSource = oaepSpec.getPSource();
+            logStream.append(pSource.getAlgorithm());
+            if (pSource.getAlgorithm().equals("PSpecified")) {
+                logStream.append(":{");
+                logStream.append(Arrays.toString(((PSource.PSpecified) pSource).getValue()));
+                logStream.append('}');
+            }
+            logStream.append('}');
+        }
+
+        logStream.append(" with provider " + provider + "\n");
+        e.printStackTrace(logStream);
+    }
+
+    private void checkCipher(CipherTestParam p, String provider) throws Exception {
+        if (!p.compatibleWith(provider)) {
+            return;
+        }
+        Cipher c = Cipher.getInstance(p.transformation, provider);
+
+        c.init(Cipher.ENCRYPT_MODE, p.encryptKey, p.spec);
+
+        // This doesn't quite work on OAEPPadding unless it's the default case,
+        // because its size depends on the message digest algorithms used.
+        if (!p.transformation.endsWith("OAEPPADDING")) {
+            assertEquals(p.transformation + " getBlockSize() ENCRYPT_MODE",
+                    getExpectedBlockSize(p.transformation, Cipher.ENCRYPT_MODE, provider),
+                    c.getBlockSize());
+        }
+        assertTrue(p.transformation + " getOutputSize(0) ENCRYPT_MODE",
+                getExpectedOutputSize(p.transformation, Cipher.ENCRYPT_MODE, provider) <= c
+                        .getOutputSize(0));
+
+        if (p.aad != null) {
+            c.updateAAD(p.aad);
+        }
+        final byte[] actualCiphertext = c.doFinal(p.plaintext);
+        if (!isRandomizedEncryption(p.transformation)) {
+            assertEquals(p.transformation + " " + provider, Arrays.toString(p.ciphertext),
+                    Arrays.toString(actualCiphertext));
+        }
+
+        c = Cipher.getInstance(p.transformation, provider);
+        c.init(Cipher.ENCRYPT_MODE, p.encryptKey, p.spec);
+        if (!(p instanceof OAEPCipherTestParam) || p.spec != null) {
+            assertCorrectAlgorithmParameters(provider, p.transformation, p.spec, c.getParameters());
+        }
+
+        byte[] emptyCipherText = c.doFinal();
+        assertNotNull(emptyCipherText);
+
+        c.init(Cipher.DECRYPT_MODE, p.decryptKey, p.spec);
+
+        assertEquals(p.transformation + " getBlockSize() DECRYPT_MODE",
+                getExpectedBlockSize(p.transformation, Cipher.DECRYPT_MODE, provider),
+                c.getBlockSize());
+
+        // This doesn't quite work on OAEPPadding unless it's the default case,
+        // because its size depends on the message digest algorithms used.
+        if (!p.transformation.endsWith("OAEPPADDING")) {
+            assertTrue(p.transformation + " getOutputSize(0) DECRYPT_MODE",
+                    getExpectedOutputSize(p.transformation, Cipher.DECRYPT_MODE, provider) <= c
+                            .getOutputSize(0));
+        }
+
+        if (!isAEAD(p.transformation)) {
+            try {
+                c.updateAAD(new byte[8]);
+                fail("Cipher should not support AAD");
+            } catch (UnsupportedOperationException expected) {
+            } catch (IllegalStateException expected) {
+            }
+        }
+
+        try {
+            byte[] emptyPlainText = c.doFinal(emptyCipherText);
+            assertEquals(Arrays.toString(new byte[0]), Arrays.toString(emptyPlainText));
+        } catch (AEADBadTagException maybe) {
+            if (!"AndroidOpenSSL".equals(provider) || !isAEAD(p.transformation)) {
+                throw maybe;
+            }
+        } catch (BadPaddingException maybe) {
+            // BC's OAEP has a bug where it doesn't support decrypt of a zero-length plaintext
+            if (!("BC".equals(provider) && p.transformation.contains("OAEP"))) {
+                throw maybe;
+            }
+        }
+
+        // decrypt an empty ciphertext; not valid for RSA
+        if (!p.transformation.contains("OAEP")) {
+            if ((!isAEAD(p.transformation)
+                    && (StandardNames.IS_RI || provider.equals("AndroidOpenSSL") ||
+                            (provider.equals("BC") && p.transformation.contains("/CTR/"))))
+                    || p.transformation.equals("ARC4")) {
+                assertEquals(Arrays.toString(new byte[0]),
+                             Arrays.toString(c.doFinal()));
+
+                c.update(new byte[0]);
+                assertEquals(Arrays.toString(new byte[0]),
+                             Arrays.toString(c.doFinal()));
+            } else if (provider.equals("BC") || isAEAD(p.transformation)) {
+                try {
+                    c.doFinal();
+                    fail(p.transformation + " " + provider);
+                } catch (IllegalBlockSizeException maybe) {
+                    if (isAEAD(p.transformation)) {
+                        throw maybe;
+                    }
+                } catch (AEADBadTagException maybe) {
+                    if (!isAEAD(p.transformation)) {
+                        throw maybe;
+                    }
+                }
+                try {
+                    c.update(new byte[0]);
+                    c.doFinal();
+                    fail(p.transformation + " " + provider);
+                } catch (IllegalBlockSizeException maybe) {
+                    if (isAEAD(p.transformation)) {
+                        throw maybe;
+                    }
+                } catch (AEADBadTagException maybe) {
+                    if (!isAEAD(p.transformation)) {
+                        throw maybe;
+                    }
+                }
+            } else {
+                throw new AssertionError("Define your behavior here for " + provider);
+            }
+        }
+
+        // Cipher might be in unspecified state from failures above.
+        c.init(Cipher.DECRYPT_MODE, p.decryptKey, p.spec);
+
+        // .doFinal(input)
+        {
+            if (p.aad != null) {
+                c.updateAAD(p.aad);
+            }
+            final byte[] actualPlaintext = c.doFinal(p.ciphertext);
+            assertEquals(Arrays.toString(p.plaintext), Arrays.toString(actualPlaintext));
+        }
+
+        // .doFinal(input, offset, len, output)
+        {
+            final byte[] largerThanCiphertext = new byte[p.ciphertext.length + 5];
+            System.arraycopy(p.ciphertext, 0, largerThanCiphertext, 5, p.ciphertext.length);
+
+            if (p.aad != null) {
+                final byte[] largerThanAad = new byte[p.aad.length + 100];
+                System.arraycopy(p.aad, 0, largerThanAad, 50, p.aad.length);
+                assertTrue(p.aad.length > 1);
+                c.updateAAD(largerThanAad, 50, 1);
+                c.updateAAD(largerThanAad, 51, p.aad.length - 1);
+            }
+
+            final byte[] actualPlaintext = new byte[c.getOutputSize(p.ciphertext.length)];
+            assertEquals(p.plaintext.length,
+                    c.doFinal(largerThanCiphertext, 5, p.ciphertext.length, actualPlaintext));
+            assertEquals(Arrays.toString(p.plaintext),
+                    Arrays.toString(Arrays.copyOfRange(actualPlaintext, 0, p.plaintext.length)));
+        }
+
+        // .doFinal(input, offset, len, output, offset)
+        {
+            final byte[] largerThanCiphertext = new byte[p.ciphertext.length + 10];
+            System.arraycopy(p.ciphertext, 0, largerThanCiphertext, 5, p.ciphertext.length);
+
+            if (p.aad != null) {
+                final byte[] largerThanAad = new byte[p.aad.length + 2];
+                System.arraycopy(p.aad, 0, largerThanAad, 2, p.aad.length);
+                c.updateAAD(largerThanAad, 2, p.aad.length);
+            }
+
+            final byte[] actualPlaintext = new byte[c.getOutputSize(p.ciphertext.length) + 2];
+            assertEquals(p.plaintext.length,
+                    c.doFinal(largerThanCiphertext, 5, p.ciphertext.length, actualPlaintext, 1));
+            assertEquals(Arrays.toString(p.plaintext),
+                    Arrays.toString(Arrays.copyOfRange(actualPlaintext, 1, p.plaintext.length + 1)));
+        }
+
+        if (!p.isStreamCipher && !p.transformation.endsWith("NOPADDING")
+                && !isRandomizedEncryption(p.transformation)) {
+            Cipher cNoPad = Cipher.getInstance(
+                    getCipherTransformationWithNoPadding(p.transformation), provider);
+            cNoPad.init(Cipher.DECRYPT_MODE, p.decryptKey, p.spec);
+
+            if (p.aad != null) {
+                c.updateAAD(p.aad);
+            }
+            final byte[] actualPlaintextPadded = cNoPad.doFinal(p.ciphertext);
+            assertEquals(provider + ":" + cNoPad.getAlgorithm(),
+                    Arrays.toString(p.plaintextPadded), Arrays.toString(actualPlaintextPadded));
+        }
+
+        // Test wrapping a key. Every cipher should be able to wrap.
+        {
+            // Generate a small SecretKey for AES.
+            KeyGenerator kg = KeyGenerator.getInstance("AES");
+            kg.init(128);
+            SecretKey sk = kg.generateKey();
+
+            // Wrap it
+            c = Cipher.getInstance(p.transformation, provider);
+            c.init(Cipher.WRAP_MODE, p.encryptKey, p.spec);
+            byte[] cipherText = c.wrap(sk);
+
+            // Unwrap it
+            c.init(Cipher.UNWRAP_MODE, p.decryptKey, p.spec);
+            Key decryptedKey = c.unwrap(cipherText, sk.getAlgorithm(), Cipher.SECRET_KEY);
+
+            assertEquals(
+                    "sk.getAlgorithm()=" + sk.getAlgorithm() + " decryptedKey.getAlgorithm()="
+                            + decryptedKey.getAlgorithm() + " encryptKey.getEncoded()="
+                            + Arrays.toString(sk.getEncoded()) + " decryptedKey.getEncoded()="
+                            + Arrays.toString(decryptedKey.getEncoded()), sk, decryptedKey);
+        }
+    }
+
+    /**
+     * Gets the Cipher transformation with the same algorithm and mode as the provided one but
+     * which uses no padding.
+     */
+    private static String getCipherTransformationWithNoPadding(String transformation) {
+        // The transformation is assumed to be in the Algorithm/Mode/Padding format.
+        int paddingModeDelimiterIndex = transformation.lastIndexOf('/');
+        if (paddingModeDelimiterIndex == -1) {
+            fail("No padding mode delimiter: " + transformation);
+        }
+        String paddingMode = transformation.substring(paddingModeDelimiterIndex + 1);
+        if (!paddingMode.toLowerCase().endsWith("padding")) {
+            fail("No padding mode specified:" + transformation);
+        }
+        return transformation.substring(0, paddingModeDelimiterIndex) + "/NoPadding";
+    }
+
+    @Test
+    public void testCipher_updateAAD_BeforeInit_Failure() throws Exception {
+        Cipher c = Cipher.getInstance("AES/ECB/NoPadding");
+
+        try {
+            c.updateAAD((byte[]) null);
+            fail("should not be able to call updateAAD before Cipher is initialized");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            c.updateAAD((ByteBuffer) null);
+            fail("should not be able to call updateAAD before Cipher is initialized");
+        } catch (IllegalStateException expected) {
+        }
+
+        try {
+            c.updateAAD(new byte[8]);
+            fail("should not be able to call updateAAD before Cipher is initialized");
+        } catch (IllegalStateException expected) {
+        }
+
+        try {
+            c.updateAAD(null, 0, 8);
+            fail("should not be able to call updateAAD before Cipher is initialized");
+        } catch (IllegalStateException expected) {
+        }
+
+        ByteBuffer bb = ByteBuffer.allocate(8);
+        try {
+            c.updateAAD(bb);
+            fail("should not be able to call updateAAD before Cipher is initialized");
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    @Test
+    public void testCipher_updateAAD_AfterInit_Failure() throws Exception {
+        Cipher c = Cipher.getInstance("AES/ECB/NoPadding");
+        c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(new byte[128 / 8], "AES"));
+
+        try {
+            c.updateAAD((byte[]) null);
+            fail("should not be able to call updateAAD with null input");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            c.updateAAD((ByteBuffer) null);
+            fail("should not be able to call updateAAD with null input");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            c.updateAAD(null, 0, 8);
+            fail("should not be able to call updateAAD with null input");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            c.updateAAD(new byte[8], -1, 7);
+            fail("should not be able to call updateAAD with invalid offset");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            c.updateAAD(new byte[8], 0, -1);
+            fail("should not be able to call updateAAD with negative length");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            c.updateAAD(new byte[8], 0, 8 + 1);
+            fail("should not be able to call updateAAD with too large length");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            c.updateAAD(new byte[8]);
+            fail("should not be able to call updateAAD on non-AEAD cipher");
+        } catch (UnsupportedOperationException expected) {
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    @Test
+    public void testCipher_ShortBlock_Failure() throws Exception {
+        for (String provider : AES_PROVIDERS) {
+            testCipher_ShortBlock_Failure(provider);
+        }
+    }
+
+    private void testCipher_ShortBlock_Failure(String provider) throws Exception {
+        final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
+        PrintStream out = new PrintStream(errBuffer);
+        for (CipherTestParam p : CIPHER_TEST_PARAMS) {
+            try {
+                checkCipher_ShortBlock_Failure(p, provider);
+            } catch (Exception e) {
+                logTestFailure(out, provider, p, e);
+            }
+        }
+        out.flush();
+        if (errBuffer.size() > 0) {
+            throw new Exception("Errors encountered:\n\n" + errBuffer.toString() + "\n\n");
+        }
+    }
+
+    @Test
+    public void testCipher_DoFinal_wrapMode_Failure() throws Exception {
+        checkCipher_DoFinal_invalidMode_Failure(Cipher.WRAP_MODE);
+    }
+
+    @Test
+    public void testCipher_DoFinal_unwrapMode_Failure() throws Exception {
+        checkCipher_DoFinal_invalidMode_Failure(Cipher.UNWRAP_MODE);
+    }
+
+    /**
+     * Helper for testing that Cipher.doFinal() throws IllegalStateException when
+     * initialized in modes other than DECRYPT or ENCRYPT.
+     */
+    private static void checkCipher_DoFinal_invalidMode_Failure(int opmode) throws Exception {
+        String msg = String.format(Locale.US,
+                "doFinal() should throw IllegalStateException [mode=%d]", opmode);
+        int bs = createAesCipher(opmode).getBlockSize();
+        assertEquals(16, bs); // check test is set up correctly
+        try {
+            createAesCipher(opmode).doFinal();
+            fail(msg);
+        } catch (IllegalStateException expected) {
+        }
+
+        try {
+            createAesCipher(opmode).doFinal(new byte[0]);
+            fail(msg);
+        } catch (IllegalStateException expected) {
+        }
+
+        try {
+            createAesCipher(opmode).doFinal(new byte[2 * bs], 0, bs);
+            fail(msg);
+        } catch (IllegalStateException expected) {
+        }
+
+        try {
+            createAesCipher(opmode).doFinal(new byte[2 * bs], 0, bs, new byte[2 * bs], 0);
+            fail(msg);
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    @Test
+    public void testCipher_Update_wrapMode_Failure() throws Exception {
+        checkCipher_Update_invalidMode_Failure(Cipher.WRAP_MODE);
+    }
+
+    @Test
+    public void testCipher_Update_unwrapMode_Failure() throws Exception {
+        checkCipher_Update_invalidMode_Failure(Cipher.UNWRAP_MODE);
+    }
+
+    /**
+     * Helper for testing that Cipher.update() throws IllegalStateException when
+     * initialized in modes other than DECRYPT or ENCRYPT.
+     */
+    private static void checkCipher_Update_invalidMode_Failure(final int opmode) throws Exception {
+        String msg = "update() should throw IllegalStateException [mode=" + opmode + "]";
+        final int bs = createAesCipher(opmode).getBlockSize();
+        assertEquals(16, bs); // check test is set up correctly
+        assertIllegalStateException(msg, new Runnable() {
+            @Override
+            public void run() {
+                createAesCipher(opmode).update(new byte[0]);
+            }
+        });
+        assertIllegalStateException(msg, new Runnable() {
+            @Override
+            public void run() {
+                createAesCipher(opmode).update(new byte[2 * bs]);
+            }
+        });
+        assertIllegalStateException(msg, new Runnable() {
+            @Override
+            public void run() {
+                createAesCipher(opmode).update(
+                        new byte[2 * bs] /* input */, bs /* inputOffset */, 0 /* inputLen */);
+            }
+        });
+        try {
+            createAesCipher(opmode).update(new byte[2*bs] /* input */, 0 /* inputOffset */,
+                    2 * bs /* inputLen */, new byte[2 * bs] /* output */, 0 /* outputOffset */);
+            fail(msg);
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    @Test
+    public void testCipher_Update_WithZeroLengthInput_ReturnsNull() throws Exception {
+        Cipher c = Cipher.getInstance("AES/ECB/NoPadding");
+        c.init(Cipher.ENCRYPT_MODE, AES_128_KEY);
+        assertNull(c.update(new byte[0]));
+        assertNull(c.update(new byte[c.getBlockSize() * 2], 0, 0));
+
+        // Try with non-zero offset just in case the implementation mixes up offset and inputLen
+        assertNull(c.update(new byte[c.getBlockSize() * 2], 16, 0));
+    }
+
+    @Test
+    public void testCipher_Wrap_decryptMode_Failure() throws Exception {
+        checkCipher_Wrap_invalidMode_Failure(Cipher.DECRYPT_MODE);
+    }
+
+    @Test
+    public void testCipher_Wrap_encryptMode_Failure() throws Exception {
+        checkCipher_Wrap_invalidMode_Failure(Cipher.ENCRYPT_MODE);
+    }
+
+    @Test
+    public void testCipher_Wrap_unwrapMode_Failure() throws Exception {
+        checkCipher_Wrap_invalidMode_Failure(Cipher.UNWRAP_MODE);
+    }
+
+    /**
+     * Helper for testing that Cipher.wrap() throws IllegalStateException when
+     * initialized in modes other than WRAP.
+     */
+    private static void checkCipher_Wrap_invalidMode_Failure(int opmode) throws Exception {
+        KeyGenerator kg = KeyGenerator.getInstance("AES");
+        kg.init(128);
+        SecretKey key = kg.generateKey();
+        Cipher cipher = createAesCipher(opmode);
+        try {
+            cipher.wrap(key);
+            fail("wrap() should throw IllegalStateException [mode=" + opmode + "]");
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    @Test
+    public void testCipher_Unwrap_decryptMode_Failure() throws Exception {
+        checkCipher_Unwrap_invalidMode_Failure(Cipher.DECRYPT_MODE);
+    }
+
+    @Test
+    public void testCipher_Unwrap_encryptMode_Failure() throws Exception {
+        checkCipher_Unwrap_invalidMode_Failure(Cipher.ENCRYPT_MODE);
+    }
+
+    @Test
+    public void testCipher_Unwrap_wrapMode_Failure() throws Exception {
+        checkCipher_Unwrap_invalidMode_Failure(Cipher.WRAP_MODE);
+    }
+
+    /**
+     * Helper for testing that Cipher.unwrap() throws IllegalStateException when
+     * initialized in modes other than UNWRAP.
+     */
+    private static void checkCipher_Unwrap_invalidMode_Failure(int opmode) throws Exception {
+        KeyGenerator kg = KeyGenerator.getInstance("AES");
+        kg.init(128);
+        SecretKey key = kg.generateKey();
+        Cipher cipher = createAesCipher(opmode);
+        byte[] wrappedKey = createAesCipher(Cipher.WRAP_MODE).wrap(key);
+        try {
+            cipher.unwrap(wrappedKey, key.getAlgorithm(), Cipher.PRIVATE_KEY);
+            fail("unwrap() should throw IllegalStateException [mode=" + opmode + "]");
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    private void checkCipher_ShortBlock_Failure(CipherTestParam p, String provider) throws Exception {
+        // Do not try to test ciphers with no padding already.
+        String noPaddingTransform = getCipherTransformationWithNoPadding(p.transformation);
+        if (p.transformation.equals(noPaddingTransform)) {
+            return;
+        }
+
+        Cipher c = Cipher.getInstance(
+                getCipherTransformationWithNoPadding(p.transformation), provider);
+        if (c.getBlockSize() == 0) {
+            return;
+        }
+
+        if (!p.transformation.endsWith("NOPADDING")) {
+            c.init(Cipher.ENCRYPT_MODE, p.encryptKey);
+            try {
+                c.doFinal(new byte[] { 0x01, 0x02, 0x03 });
+                fail("Should throw IllegalBlockSizeException on wrong-sized block; transform="
+                        + p.transformation + " provider=" + provider);
+            } catch (IllegalBlockSizeException expected) {
+            }
+        }
+    }
+
+    // Test that when reading GCM parameters encoded using ASN1, a value for the tag size
+    // not present indicates a value of 12.
+    // https://b/29876633
+    @Test
+    public void test_DefaultGCMTagSizeAlgorithmParameterSpec() throws Exception {
+        Assume.assumeNotNull(Security.getProvider("BC"));
+        final String AES = "AES";
+        final String AES_GCM = "AES/GCM/NoPadding";
+        byte[] input = new byte[16];
+        byte[] key = new byte[16];
+        Cipher cipher = Cipher.getInstance(AES_GCM, "BC");
+        AlgorithmParameters param = AlgorithmParameters.getInstance("GCM");
+        param.init(new byte[] {
+            (byte) 48,    // DER encoding : tag_Sequence
+            (byte) 14,    // DER encoding : total length
+            (byte) 4,     // DER encoding : tag_OctetString
+            (byte) 12,    // DER encoding : counter length
+            (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0,
+            (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0 });
+        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, AES), param);
+        byte[] ciphertext = cipher.update(input);
+        assertEquals(16, ciphertext.length);
+        byte[] tag = cipher.doFinal();
+        assertEquals(12, tag.length);
+    }
+
+    @Test
+    public void testAES_ECB_PKCS5Padding_ShortBuffer_Failure() throws Exception {
+        for (String provider : AES_PROVIDERS) {
+            testAES_ECB_PKCS5Padding_ShortBuffer_Failure(provider);
+        }
+    }
+
+    private void testAES_ECB_PKCS5Padding_ShortBuffer_Failure(String provider) throws Exception {
+        Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding", provider);
+        c.init(Cipher.ENCRYPT_MODE, AES_128_KEY);
+
+        final byte[] fragmentOutput = c.update(AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext);
+        if (fragmentOutput != null) {
+            assertEquals(0, fragmentOutput.length);
+        }
+
+        // Provide null buffer.
+        {
+            try {
+                c.doFinal(null, 0);
+                fail("Should throw NullPointerException on null output buffer");
+            } catch (NullPointerException expected) {
+            } catch (IllegalArgumentException expected) {
+            }
+        }
+
+        // Provide short buffer.
+        {
+            final byte[] output = new byte[c.getBlockSize() - 1];
+            try {
+                c.doFinal(output, 0);
+                fail("Should throw ShortBufferException on short output buffer");
+            } catch (ShortBufferException expected) {
+            }
+        }
+
+        // Start 1 byte into output buffer.
+        {
+            final byte[] output = new byte[c.getBlockSize()];
+            try {
+                c.doFinal(output, 1);
+                fail("Should throw ShortBufferException on short output buffer");
+            } catch (ShortBufferException expected) {
+            }
+        }
+
+        // Should keep data for real output buffer
+        {
+            final byte[] output = new byte[c.getBlockSize()];
+            assertEquals(AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted.length, c.doFinal(output, 0));
+            assertTrue(Arrays.equals(AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted, output));
+        }
+    }
+
+    @Test
+    public void testAES_ECB_NoPadding_IncrementalUpdate_Success() throws Exception {
+        for (String provider : AES_PROVIDERS) {
+            testAES_ECB_NoPadding_IncrementalUpdate_Success(provider);
+        }
+    }
+
+    private void testAES_ECB_NoPadding_IncrementalUpdate_Success(String provider) throws Exception {
+        Cipher c = Cipher.getInstance("AES/ECB/NoPadding", provider);
+        assertEquals(provider, c.getProvider().getName());
+        c.init(Cipher.ENCRYPT_MODE, AES_128_KEY);
+
+        for (int i = 0; i < AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded.length - 1; i++) {
+            final byte[] outputFragment = c.update(AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded, i, 1);
+            if (outputFragment != null) {
+                assertEquals(0, outputFragment.length);
+            }
+        }
+
+        final byte[] output = c.doFinal(AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded,
+                AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded.length - 1, 1);
+        assertNotNull(provider, output);
+        assertEquals(provider, AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded.length,
+                output.length);
+
+        assertTrue(provider, Arrays.equals(AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted, output));
+    }
+
+    private static final byte[] AES_IV_ZEROES = new byte[] {
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+    };
+
+    @Test
+    public void testAES_ECB_NoPadding_IvParameters_Failure() throws Exception {
+        for (String provider : AES_PROVIDERS) {
+            testAES_ECB_NoPadding_IvParameters_Failure(provider);
+        }
+    }
+
+    private void testAES_ECB_NoPadding_IvParameters_Failure(String provider) throws Exception {
+        Cipher c = Cipher.getInstance("AES/ECB/NoPadding", provider);
+
+        AlgorithmParameterSpec spec = new IvParameterSpec(AES_IV_ZEROES);
+        try {
+            c.init(Cipher.ENCRYPT_MODE, AES_128_KEY, spec);
+            fail("Should not accept an IV in ECB mode; provider=" + provider);
+        } catch (InvalidAlgorithmParameterException expected) {
+        }
+    }
+
+    @Test
+    public void testRC4_MultipleKeySizes() throws Exception {
+        final int SMALLEST_KEY_SIZE = 40;
+        final int LARGEST_KEY_SIZE = 1024;
+
+        /* Make an array of keys for our tests */
+        SecretKey[] keys = new SecretKey[LARGEST_KEY_SIZE - SMALLEST_KEY_SIZE];
+        {
+            KeyGenerator kg = KeyGenerator.getInstance("ARC4");
+            for (int keysize = SMALLEST_KEY_SIZE; keysize < LARGEST_KEY_SIZE; keysize++) {
+                final int index = keysize - SMALLEST_KEY_SIZE;
+                kg.init(keysize);
+                keys[index] = kg.generateKey();
+            }
+        }
+
+        /*
+         * Use this to compare the output of the first provider against
+         * subsequent providers.
+         */
+        String[] expected = new String[LARGEST_KEY_SIZE - SMALLEST_KEY_SIZE];
+
+        /* Find all providers that provide ARC4. We must have at least one! */
+        Map<String, String> filter = new HashMap<String, String>();
+        filter.put("Cipher.ARC4", "");
+        Provider[] providers = Security.getProviders(filter);
+        assertTrue("There must be security providers of Cipher.ARC4", providers.length > 0);
+
+        /* Keep track of this for later error messages */
+        String firstProvider = providers[0].getName();
+
+        for (Provider p : providers) {
+            Cipher c = Cipher.getInstance("ARC4", p);
+
+            for (int keysize = SMALLEST_KEY_SIZE; keysize < LARGEST_KEY_SIZE; keysize++) {
+                final int index = keysize - SMALLEST_KEY_SIZE;
+                final SecretKey sk = keys[index];
+
+                /*
+                 * Test that encryption works. Donig this in a loop also has the
+                 * benefit of testing that re-initialization works for this
+                 * cipher.
+                 */
+                c.init(Cipher.ENCRYPT_MODE, sk);
+                byte[] cipherText = c.doFinal(ORIGINAL_PLAIN_TEXT);
+                assertNotNull(cipherText);
+
+                /*
+                 * Compare providers against eachother to make sure they're all
+                 * in agreement. This helps when you add a brand new provider.
+                 */
+                if (expected[index] == null) {
+                    expected[index] = Arrays.toString(cipherText);
+                } else {
+                    assertEquals(firstProvider + " should output the same as " + p.getName()
+                            + " for key size " + keysize, expected[index],
+                            Arrays.toString(cipherText));
+                }
+
+                c.init(Cipher.DECRYPT_MODE, sk);
+                byte[] actualPlaintext = c.doFinal(cipherText);
+                assertEquals("Key size: " + keysize, Arrays.toString(ORIGINAL_PLAIN_TEXT),
+                        Arrays.toString(actualPlaintext));
+            }
+        }
+    }
+
+    @Test
+    public void testAES_keyConstrained() throws Exception {
+        Provider[] providers = Security.getProviders();
+        for (Provider p : providers) {
+            for (Provider.Service s : p.getServices()) {
+                if (s.getType().equals("Cipher")) {
+                    if (s.getAlgorithm().startsWith("AES_128/")) {
+                        Cipher c = Cipher.getInstance(s.getAlgorithm(), p);
+                        assertTrue(s.getAlgorithm(), checkAES_keyConstraint(c, 128));
+                        assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 192));
+                        assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 256));
+                    } else if (s.getAlgorithm().startsWith("AES_256/")) {
+                        Cipher c = Cipher.getInstance(s.getAlgorithm(), p);
+                        assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 128));
+                        assertFalse(s.getAlgorithm(), checkAES_keyConstraint(c, 192));
+                        assertTrue(s.getAlgorithm(), checkAES_keyConstraint(c, 256));
+                    }
+                }
+            }
+        }
+    }
+
+    private boolean checkAES_keyConstraint(Cipher c, int keySize) throws Exception {
+        KeyGenerator kg = KeyGenerator.getInstance(getBaseAlgorithm(c.getAlgorithm()));
+        kg.init(keySize);
+        SecretKey key = kg.generateKey();
+        try {
+            c.init(Cipher.ENCRYPT_MODE, key);
+            return true;
+        } catch (InvalidKeyException e) {
+            return false;
+        }
+    }
+
+    /*
+     * When in decrypt mode and using padding, the buffer shouldn't necessarily have room for an
+     * extra block when using padding.
+     * http://b/19186852
+     */
+    @Test
+    public void testDecryptBufferMultipleBlockSize_mustNotThrowException() throws Exception {
+        String testString = "Hello, World!";
+        byte[] testKey = "0123456789012345".getBytes(StandardCharsets.US_ASCII);
+        String testedCipher = "AES/ECB/PKCS7Padding";
+
+        Cipher encCipher = Cipher.getInstance(testedCipher);
+        encCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(testKey, "AES"));
+        byte[] plainBuffer = testString.getBytes(StandardCharsets.US_ASCII);
+        byte[] encryptedBuffer = new byte[16];
+        int encryptedLength = encCipher.doFinal(
+                plainBuffer, 0, plainBuffer.length, encryptedBuffer);
+        assertEquals(16, encryptedLength);
+
+        Cipher cipher = Cipher.getInstance(testedCipher);
+        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(testKey, "AES"));
+        // Must not throw exception.
+        int unencryptedBytes = cipher.doFinal(
+                encryptedBuffer, 0, encryptedBuffer.length, encryptedBuffer);
+        assertEquals(testString,
+                new String(encryptedBuffer, 0, unencryptedBytes, StandardCharsets.US_ASCII));
+    }
+
+    /**
+     * When using padding in decrypt mode, ensure that empty buffers decode to empty strings
+     * (no padding needed for the empty buffer).
+     * http://b/19186852
+     */
+    @Test
+    public void testDecryptBufferZeroSize_mustDecodeToEmptyString() throws Exception {
+        String[] androidOpenSSLCiphers = { "AES/CBC/PKCS5PADDING", "AES/CBC/PKCS7PADDING",
+                "AES/ECB/PKCS5PADDING", "AES/ECB/PKCS7PADDING", "DESEDE/CBC/PKCS5PADDING",
+                "DESEDE/CBC/PKCS7PADDING" };
+        for (String c : androidOpenSSLCiphers) {
+            Cipher cipher = Cipher.getInstance(c);
+            assertTrue(Conscrypt.isConscrypt(cipher.getProvider()));
+            if (c.contains("/CBC/")) {
+                cipher.init(Cipher.DECRYPT_MODE,
+                        new SecretKeySpec("0123456789012345".getBytes(StandardCharsets.US_ASCII),
+                                c.startsWith("AES/") ? "AES" : "DESEDE"),
+                        new IvParameterSpec(("01234567" + (c.startsWith("AES/") ? "89012345" : ""))
+                                                    .getBytes(StandardCharsets.US_ASCII)));
+            } else {
+                cipher.init(Cipher.DECRYPT_MODE,
+                        new SecretKeySpec("0123456789012345".getBytes(StandardCharsets.US_ASCII),
+                                c.startsWith("AES/") ? "AES" : "DESEDE"));
+            }
+
+            byte[] buffer = new byte[0];
+            int bytesProduced = cipher.doFinal(buffer, 0, buffer.length, buffer);
+            assertEquals("", new String(buffer, 0, bytesProduced, StandardCharsets.US_ASCII));
+        }
+    }
+
+    /**
+     * Check that RSA with OAEPPadding is supported.
+     * http://b/22208820
+     */
+    @Test
+    public void test_RSA_OAEPPadding() throws Exception {
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+        keyGen.initialize(1024, SecureRandom.getInstance("SHA1PRNG"));
+        Cipher cipher = Cipher.getInstance("RSA/NONE/OAEPPadding");
+        cipher.init(Cipher.ENCRYPT_MODE, keyGen.generateKeyPair().getPublic());
+        cipher.doFinal(new byte[] {1,2,3,4});
+    }
+
+    /**
+     * Check that initializing with a GCM AlgorithmParameters produces the same result
+     * as initializing with a GCMParameterSpec.
+     */
+    @Test
+    public void test_AESGCMNoPadding_init_algParams() throws Exception {
+        SecretKeySpec key = new SecretKeySpec(new byte[16], "AES");
+        GCMParameterSpec spec = new GCMParameterSpec(96, new byte[12]);
+        AlgorithmParameters params = AlgorithmParameters.getInstance("GCM");
+        params.init(spec);
+        Cipher c1 = Cipher.getInstance("AES/GCM/NoPadding");
+        Cipher c2 = Cipher.getInstance("AES/GCM/NoPadding");
+
+        c1.init(Cipher.ENCRYPT_MODE, key, spec);
+        c2.init(Cipher.ENCRYPT_MODE, key, params);
+        // Cipher can adjust the provider based on the reponses to the init call, make sure
+        // we got the same provider for both
+        assertEquals(c1.getProvider(), c2.getProvider());
+        c1.updateAAD(new byte[] {
+                0x01, 0x02, 0x03, 0x04, 0x05,
+        });
+        c2.updateAAD(new byte[] {
+                0x01, 0x02, 0x03, 0x04, 0x05,
+        });
+
+        assertEquals(Arrays.toString(c1.doFinal()), Arrays.toString(c2.doFinal()));
+    }
+
+    /**
+     * http://b/27224566
+     * http://b/27994930
+     * Check that a PBKDF2WITHHMACSHA1 secret key factory works well with a
+     * PBEWITHSHAAND128BITAES-CBC-BC cipher. The former is PKCS5 and the latter is PKCS12, and so
+     * mixing them is not recommended. However, until 1.52 BouncyCastle was accepting this mixture,
+     * assuming the IV was a 0 vector. Some apps still use this functionality. This
+     * compatibility is likely to be removed in later versions of Android.
+     * TODO(27995180): consider whether we keep this compatibility. Consider whether we only allow
+     * if an IV is passed in the parameters.
+     */
+    @Test
+    public void test_PBKDF2WITHHMACSHA1_SKFactory_and_PBEAESCBC_Cipher_noIV() throws Exception {
+        Assume.assumeNotNull(Security.getProvider("BC"));
+        byte[] plaintext = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+                17, 18, 19 };
+        byte[] ciphertext = new byte[] {  92, -65, -128, 16, -102, -115, -44, 52, 16, 124, -34,
+                -45, 58, -70, -17, 127, 119, -67, 87, 91, 63, -13, -40, 9, 97, -17, -71, 97, 10,
+                -61, -19, -73 };
+        SecretKeyFactory skf =
+                SecretKeyFactory.getInstance("PBKDF2WITHHMACSHA1");
+        PBEKeySpec pbeks = new PBEKeySpec("password".toCharArray(),
+                "salt".getBytes(TestUtils.UTF_8),
+                100, 128);
+        SecretKey secretKey = skf.generateSecret(pbeks);
+
+        Cipher cipher =
+                Cipher.getInstance("PBEWITHSHAAND128BITAES-CBC-BC");
+        PBEParameterSpec paramSpec = new PBEParameterSpec("salt".getBytes(TestUtils.UTF_8), 100);
+        cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);
+        assertEquals(Arrays.toString(ciphertext), Arrays.toString(cipher.doFinal(plaintext)));
+
+        secretKey = skf.generateSecret(pbeks);
+        cipher.init(Cipher.DECRYPT_MODE, secretKey, paramSpec);
+        assertEquals(Arrays.toString(plaintext), Arrays.toString(cipher.doFinal(ciphertext)));
+    }
+
+    /**
+     * http://b/27224566
+     * http://b/27994930
+     * Check that a PBKDF2WITHHMACSHA1 secret key factory works well with a
+     * PBEWITHSHAAND128BITAES-CBC-BC cipher. The former is PKCS5 and the latter is PKCS12, and so
+     * mixing them is not recommended. However, until 1.52 BouncyCastle was accepting this mixture,
+     * assuming the IV was a 0 vector. Some apps still use this functionality. This
+     * compatibility is likely to be removed in later versions of Android.
+     * TODO(27995180): consider whether we keep this compatibility. Consider whether we only allow
+     * if an IV is passed in the parameters.
+     */
+    @Test
+    public void test_PBKDF2WITHHMACSHA1_SKFactory_and_PBEAESCBC_Cipher_withIV() throws Exception {
+        Assume.assumeNotNull(Security.getProvider("BC"));
+        byte[] plaintext = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,  12, 13, 14, 15, 16,
+                17, 18, 19 };
+        byte[] ciphertext = { 68, -87, 71, -6, 32, -77, 124, 3, 35, -26, 96, -16, 100, -17, 52, -32,
+                110, 26, -117, 112, -25, -113, -58, -30, 19, -46, -21, 59, -126, -8, -70, -89 };
+        byte[] iv = new byte[] { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
+        SecretKeyFactory skf =
+                SecretKeyFactory.getInstance("PBKDF2WITHHMACSHA1");
+        PBEKeySpec pbeks = new PBEKeySpec("password".toCharArray(),
+                "salt".getBytes(TestUtils.UTF_8),
+                100, 128);
+        SecretKey secretKey = skf.generateSecret(pbeks);
+        Cipher cipher =
+                Cipher.getInstance("PBEWITHSHAAND128BITAES-CBC-BC");
+        cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
+        assertEquals(Arrays.toString(ciphertext), Arrays.toString(cipher.doFinal(plaintext)));
+
+        secretKey = skf.generateSecret(pbeks);
+        cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
+        assertEquals(Arrays.toString(plaintext), Arrays.toString(cipher.doFinal(ciphertext)));
+    }
+
+    private static Cipher createAesCipher(int opmode) {
+        try {
+            final Cipher c = Cipher.getInstance("AES/ECB/NoPadding");
+            c.init(opmode, AES_128_KEY);
+            return c;
+        } catch (Exception e) {
+            fail("Unexpected Exception: " + e.getMessage());
+            return null; // unreachable
+        }
+    }
+
+    /**
+     * Asserts that running the given runnable results in an IllegalStateException
+     */
+    private static void assertIllegalStateException(String failureMessage, Runnable runnable) {
+        try {
+            runnable.run();
+            fail(failureMessage);
+        } catch (IllegalStateException expected) {
+            // expected
+        }
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java
new file mode 100644
index 0000000..7a0d8f0
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/crypto/ECDHKeyAgreementTest.java
@@ -0,0 +1,551 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.crypto;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.org.conscrypt.Conscrypt;
+import com.android.org.conscrypt.TestUtils;
+import dalvik.system.VMRuntime;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.interfaces.ECKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import javax.crypto.KeyAgreement;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import junit.framework.AssertionFailedError;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
+
+/**
+ * Tests for all registered Elliptic Curve Diffie-Hellman {@link KeyAgreement} providers.
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class ECDHKeyAgreementTest {
+
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
+    // Two key pairs and the resulting shared secret for the Known Answer Test
+    private static final byte[] KAT_PUBLIC_KEY1_X509 = TestUtils.decodeHex(
+            "3059301306072a8648ce3d020106082a8648ce3d030107034200049fc2f71f85446b1371244491d83"
+            + "9cf97b5d27cedbb04d2c0058b59709df3a216e6b4ca1b2d622588c5a0e6968144a8965e816a600c"
+            + "05305a1da3df2bf02b41d1");
+    private static final byte[] KAT_PRIVATE_KEY1_PKCS8 = TestUtils.decodeHex(
+            "308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420e1e683003"
+            + "c8b963a92742e5f955ce7fddc81d0c3ae9b149d6af86a0cacb2271ca00a06082a8648ce3d030107"
+            + "a144034200049fc2f71f85446b1371244491d839cf97b5d27cedbb04d2c0058b59709df3a216e6b"
+            + "4ca1b2d622588c5a0e6968144a8965e816a600c05305a1da3df2bf02b41d1");
+
+    private static final byte[] KAT_PUBLIC_KEY2_X509 = TestUtils.decodeHex(
+            "3059301306072a8648ce3d020106082a8648ce3d03010703420004358efb6d91e5bbcae21774af3f6"
+            + "d85d0848630e7e61dbeb5ac9e47036ed0f8d38c7a1d1bb249f92861c7c9153fff33f45ab5b171eb"
+            + "e8cad741125e6bb4fc6b07");
+    private static final byte[] KAT_PRIVATE_KEY2_PKCS8 = TestUtils.decodeHex(
+            "308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104202b1810a69"
+            + "e12b74d50bf0343168f705f0104f76299855268aa526fdb31e6eec0a00a06082a8648ce3d030107"
+            + "a14403420004358efb6d91e5bbcae21774af3f6d85d0848630e7e61dbeb5ac9e47036ed0f8d38c7"
+            + "a1d1bb249f92861c7c9153fff33f45ab5b171ebe8cad741125e6bb4fc6b07");
+
+    private static final byte[] KAT_SECRET =
+            TestUtils.decodeHex("4faa0594c0e773eb26c8df2163af2443e88aab9578b9e1f324bc61e42d222783");
+
+    private static final ECPublicKey KAT_PUBLIC_KEY1;
+    private static final ECPrivateKey KAT_PRIVATE_KEY1;
+    private static final ECPublicKey KAT_PUBLIC_KEY2;
+    private static final ECPrivateKey KAT_PRIVATE_KEY2;
+    static {
+        try {
+            KAT_PUBLIC_KEY1 = getPublicKey(KAT_PUBLIC_KEY1_X509);
+            KAT_PRIVATE_KEY1 = getPrivateKey(KAT_PRIVATE_KEY1_PKCS8);
+            KAT_PUBLIC_KEY2 = getPublicKey(KAT_PUBLIC_KEY2_X509);
+            KAT_PRIVATE_KEY2 = getPrivateKey(KAT_PRIVATE_KEY2_PKCS8);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to decode KAT key pairs using default provider", e);
+        }
+    }
+
+    @BeforeClass
+    public static void setUp() {
+        TestUtils.assumeAllowsUnsignedCrypto();
+    }
+
+    /**
+     * Performs a known-answer test of the shared secret for all permutations of {@code Providers}
+     * of: first key pair, second key pair, and the {@code KeyAgreement}. This is to check that
+     * the {@code KeyAgreement} instances work with keys of all registered providers.
+     */
+    @Test
+    public void testKnownAnswer() throws Exception {
+        for (Provider keyFactoryProvider1 : getKeyFactoryProviders()) {
+            ECPrivateKey privateKey1 = getPrivateKey(KAT_PRIVATE_KEY1_PKCS8, keyFactoryProvider1);
+            ECPublicKey publicKey1 = getPublicKey(KAT_PUBLIC_KEY1_X509, keyFactoryProvider1);
+            for (Provider keyFactoryProvider2 : getKeyFactoryProviders()) {
+                ECPrivateKey privateKey2 =
+                        getPrivateKey(KAT_PRIVATE_KEY2_PKCS8, keyFactoryProvider2);
+                ECPublicKey publicKey2 =
+                        getPublicKey(KAT_PUBLIC_KEY2_X509, keyFactoryProvider2);
+                for (Provider keyAgreementProvider : getKeyAgreementProviders()) {
+                    try {
+                        testKnownAnswer(publicKey1, privateKey1, publicKey2, privateKey2,
+                                keyAgreementProvider);
+                    } catch (Throwable e) {
+                        throw new RuntimeException(getClass().getSimpleName() + ".testKnownAnswer("
+                                + keyFactoryProvider1.getName()
+                                + ", " + keyFactoryProvider2.getName()
+                                + ", " + keyAgreementProvider.getName() + ")",
+                                e);
+                    }
+                }
+            }
+        }
+    }
+
+    void testKnownAnswer(
+            ECPublicKey publicKey1, ECPrivateKey privateKey1,
+            ECPublicKey publicKey2, ECPrivateKey privateKey2,
+            Provider keyAgreementProvider) throws Exception {
+        assertTrue(Arrays.equals(
+                KAT_SECRET, generateSecret(keyAgreementProvider, privateKey1, publicKey2)));
+        assertTrue(Arrays.equals(
+                KAT_SECRET, generateSecret(keyAgreementProvider, privateKey2, publicKey1)));
+    }
+
+    @Test
+    public void testGetAlgorithm() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testGetAlgorithm(Provider provider) throws Exception {
+        assertEquals("ECDH", getKeyAgreement(provider).getAlgorithm());
+    }
+
+    @Test
+    public void testGetProvider() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testGetProvider(Provider provider) throws Exception {
+        assertSame(provider, getKeyAgreement(provider).getProvider());
+    }
+
+    @Test
+    public void testInit_withNullPrivateKey() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testInit_withNullPrivateKey(Provider provider) throws Exception {
+        KeyAgreement keyAgreement = getKeyAgreement(provider);
+        try {
+            keyAgreement.init(null);
+            fail();
+        } catch (InvalidKeyException expected) {}
+    }
+
+    @Test
+    public void testInit_withUnsupportedPrivateKeyType() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testInit_withUnsupportedPrivateKeyType(Provider provider) throws Exception {
+        KeyAgreement keyAgreement = getKeyAgreement(provider);
+        try {
+            keyAgreement.init(KAT_PUBLIC_KEY1);
+            fail();
+        } catch (InvalidKeyException expected) {}
+    }
+
+    @Test
+    public void testInit_withUnsupportedAlgorithmParameterSpec() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testInit_withUnsupportedAlgorithmParameterSpec(Provider provider) throws Exception {
+        try {
+            getKeyAgreement(provider).init(KAT_PRIVATE_KEY1, new ECGenParameterSpec("prime256v1"));
+            fail();
+        } catch (InvalidAlgorithmParameterException expected) {}
+    }
+
+    @Test
+    public void testDoPhase_whenNotInitialized() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testDoPhase_whenNotInitialized(Provider provider) throws Exception {
+        try {
+            getKeyAgreement(provider).doPhase(KAT_PUBLIC_KEY1, true);
+            fail();
+        } catch (IllegalStateException expected) {}
+    }
+
+    @Test
+    public void testDoPhaseReturnsNull() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testDoPhaseReturnsNull(Provider provider) throws Exception {
+        KeyAgreement keyAgreement = getKeyAgreement(provider);
+        keyAgreement.init(KAT_PRIVATE_KEY1);
+        assertNull(keyAgreement.doPhase(KAT_PUBLIC_KEY2, true));
+    }
+
+    @Test
+    public void testDoPhase_withPhaseWhichIsNotLast() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testDoPhase_withPhaseWhichIsNotLast(Provider provider) throws Exception {
+        KeyAgreement keyAgreement = getKeyAgreement(provider);
+        keyAgreement.init(KAT_PRIVATE_KEY1);
+        try {
+            keyAgreement.doPhase(KAT_PUBLIC_KEY2, false);
+            fail();
+        } catch (IllegalStateException expected) {}
+    }
+
+    @Test
+    public void testDoPhase_withNullKey() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testDoPhase_withNullKey(Provider provider) throws Exception {
+        KeyAgreement keyAgreement = getKeyAgreement(provider);
+        keyAgreement.init(KAT_PRIVATE_KEY1);
+        try {
+            keyAgreement.doPhase(null, true);
+            fail();
+        } catch (InvalidKeyException expected) {}
+    }
+
+    @Test
+    public void testDoPhase_withInvalidKeyType() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testDoPhase_withInvalidKeyType(Provider provider) throws Exception {
+        KeyAgreement keyAgreement = getKeyAgreement(provider);
+        keyAgreement.init(KAT_PRIVATE_KEY1);
+        try {
+            keyAgreement.doPhase(KAT_PRIVATE_KEY1, true);
+            fail();
+        } catch (InvalidKeyException expected) {}
+    }
+
+    @Test
+    public void testGenerateSecret_withNullOutputBuffer() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testGenerateSecret_withNullOutputBuffer(Provider provider) throws Exception {
+        KeyAgreement keyAgreement = getKeyAgreement(provider);
+        keyAgreement.init(KAT_PRIVATE_KEY1);
+        keyAgreement.doPhase(KAT_PUBLIC_KEY2, true);
+        try {
+            keyAgreement.generateSecret(null, 0);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    @Test
+    public void testGenerateSecret_withBufferOfTheRightSize() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testGenerateSecret_withBufferOfTheRightSize(Provider provider) throws Exception {
+        KeyAgreement keyAgreement = getKeyAgreement(provider);
+        keyAgreement.init(KAT_PRIVATE_KEY1);
+        keyAgreement.doPhase(KAT_PUBLIC_KEY2, true);
+
+        byte[] buffer = new byte[KAT_SECRET.length];
+        int secretLengthBytes = keyAgreement.generateSecret(buffer, 0);
+        assertEquals(KAT_SECRET.length, secretLengthBytes);
+        assertTrue(Arrays.equals(KAT_SECRET, buffer));
+    }
+
+    @Test
+    public void testGenerateSecret_withLargerThatNeededBuffer() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testGenerateSecret_withLargerThatNeededBuffer(Provider provider) throws Exception {
+        KeyAgreement keyAgreement = getKeyAgreement(provider);
+        keyAgreement.init(KAT_PRIVATE_KEY1);
+        keyAgreement.doPhase(KAT_PUBLIC_KEY2, true);
+
+        // Place the shared secret in the middle of the larger buffer and check that only that
+        // part of the buffer is affected.
+        byte[] buffer = new byte[KAT_SECRET.length + 2];
+        buffer[0] = (byte) 0x85; // arbitrary canary value
+        buffer[buffer.length - 1] = (byte) 0x3b; // arbitrary canary value
+        int secretLengthBytes = keyAgreement.generateSecret(buffer, 1);
+        assertEquals(KAT_SECRET.length, secretLengthBytes);
+        assertEquals((byte) 0x85, buffer[0]);
+        assertEquals((byte) 0x3b, buffer[buffer.length - 1]);
+        byte[] secret = new byte[KAT_SECRET.length];
+        System.arraycopy(buffer, 1, secret, 0, secret.length);
+        assertTrue(Arrays.equals(KAT_SECRET, secret));
+    }
+
+    @Test
+    public void testGenerateSecret_withSmallerThanNeededBuffer() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testGenerateSecret_withSmallerThanNeededBuffer(Provider provider) throws Exception {
+        KeyAgreement keyAgreement = getKeyAgreement(provider);
+        keyAgreement.init(KAT_PRIVATE_KEY1);
+        keyAgreement.doPhase(KAT_PUBLIC_KEY2, true);
+        try {
+            // Although the buffer is big enough (1024 bytes) the shared secret should be placed
+            // at offset 1020 thus leaving only 4 bytes for the secret, which is not enough.
+            keyAgreement.generateSecret(new byte[1024], 1020);
+            fail();
+        } catch (ShortBufferException expected) {}
+    }
+
+    @Test
+    public void testGenerateSecret_withoutBuffer() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testGenerateSecret_withoutBuffer(Provider provider) throws Exception {
+        KeyAgreement keyAgreement = getKeyAgreement(provider);
+        keyAgreement.init(KAT_PRIVATE_KEY2);
+        keyAgreement.doPhase(KAT_PUBLIC_KEY1, true);
+
+        byte[] secret = keyAgreement.generateSecret();
+        assertTrue(Arrays.equals(KAT_SECRET, secret));
+    }
+
+    @Test
+    public void testGenerateSecret_withAlgorithm() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testGenerateSecret_withAlgorithm(Provider provider) throws Exception {
+        KeyAgreement keyAgreement = getKeyAgreement(provider);
+        keyAgreement.init(KAT_PRIVATE_KEY2);
+        keyAgreement.doPhase(KAT_PUBLIC_KEY1, true);
+
+        try {
+            SecretKey key = keyAgreement.generateSecret("AES");
+            assertEquals("AES", key.getAlgorithm());
+            // The check below will need to change if it's a hardware-backed key.
+            // We'll have to encrypt a known plaintext and check that the ciphertext is as
+            // expected.
+            assertTrue(Arrays.equals(KAT_SECRET, key.getEncoded()));
+        } catch (NoSuchAlgorithmException e) {
+            // This provider doesn't support AES, that's fine as long as it's not Conscrypt
+            assertFalse(Conscrypt.isConscrypt(provider));
+        }
+    }
+
+    @Test
+    public void testDoPhase_IncompatibleCurves_Failure() throws Exception {
+        invokeCallingMethodForEachKeyAgreementProvider();
+    }
+
+    void testDoPhase_IncompatibleCurves_Failure(Provider provider) throws Exception {
+        // The SunEC provider tries to pass a sun-only AlgorithmParameterSpec to the default
+        // AlgorithmParameters:EC when its KeyPairGenerator is initialized.  Since Conscrypt
+        // is the highest-ranked provider when running our tests, its implementation of
+        // AlgorithmParameters:EC is returned, and it doesn't understand the special
+        // AlgorithmParameterSpec, so the KeyPairGenerator can't be initialized.
+        if (provider.getName().equalsIgnoreCase("SunEC")) {
+            return;
+        }
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", provider);
+        ECGenParameterSpec ecSpec256 = new ECGenParameterSpec("secp256r1");
+        keyGen.initialize(ecSpec256);
+        KeyPair keyPairA = keyGen.generateKeyPair();
+
+        ECGenParameterSpec ecSpec224 = new ECGenParameterSpec("secp224r1");
+        keyGen.initialize(ecSpec224);
+        KeyPair keyPairB = keyGen.generateKeyPair();
+
+        assertFalse(((ECKey) keyPairA.getPublic())
+                            .getParams()
+                            .equals(((ECKey) keyPairB.getPublic()).getParams()));
+
+        KeyAgreement kaA = KeyAgreement.getInstance("ECDH", provider);
+        kaA.init(keyPairA.getPrivate());
+        try {
+            kaA.doPhase(keyPairB.getPublic(), true);
+            kaA.generateSecret();
+            fail("Generated secrets with mixed keys");
+        } catch (InvalidKeyException expected) {
+        }
+    }
+
+    private void invokeCallingMethodForEachKeyAgreementProvider() throws Exception {
+        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+        String callingMethodName = null;
+        for (int i = 0; i < stackTrace.length; i++) {
+            if ("invokeCallingMethodForEachKeyAgreementProvider".equals(
+                    stackTrace[i].getMethodName())) {
+                callingMethodName = stackTrace[i + 1].getMethodName();
+            }
+        }
+        if (callingMethodName == null) {
+            throw new RuntimeException("Failed to deduce calling method name from stack trace");
+        }
+
+        String invokedMethodName = callingMethodName;
+        Method method;
+        try {
+            method = getClass().getDeclaredMethod(invokedMethodName, Provider.class);
+        } catch (NoSuchMethodError e) {
+            throw new AssertionFailedError("Failed to find per-Provider test method "
+                    + getClass().getSimpleName() + "." + invokedMethodName + "(Provider)");
+        }
+
+        for (Provider provider : getKeyAgreementProviders()) {
+            try {
+                method.invoke(this, provider);
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException(getClass().getSimpleName() + "." + invokedMethodName
+                        + "(provider: " + provider.getName() + ") failed",
+                        e.getCause());
+            }
+        }
+    }
+
+    private static Provider[] getKeyAgreementProviders() {
+        Provider[] providers = Security.getProviders("KeyAgreement.ECDH");
+        if (providers == null) {
+            return new Provider[0];
+        }
+        // Sort providers by name to guarantee deterministic order in which providers are used in
+        // the tests.
+        return sortByName(providers);
+    }
+
+    private static Provider[] getKeyFactoryProviders() {
+        Provider[] providers = Security.getProviders("KeyFactory.EC");
+        if (providers == null) {
+            return new Provider[0];
+        }
+
+        // Do not test AndroidKeyStore's KeyFactory. It only handles Android Keystore-backed keys.
+        // It's OKish not to test AndroidKeyStore's KeyFactory here because it's tested by
+        // cts/tests/test/keystore.
+        List<Provider> filteredProvidersList = new ArrayList<Provider>(providers.length);
+        for (Provider provider : providers) {
+            if ("AndroidKeyStore".equals(provider.getName())) {
+                continue;
+            }
+            filteredProvidersList.add(provider);
+        }
+        providers = filteredProvidersList.toArray(new Provider[filteredProvidersList.size()]);
+
+        // Sort providers by name to guarantee deterministic order in which providers are used in
+        // the tests.
+        return sortByName(providers);
+    }
+
+    private static ECPrivateKey getPrivateKey(byte[] pkcs8EncodedKey, Provider provider)
+            throws GeneralSecurityException {
+        KeyFactory keyFactory = KeyFactory.getInstance("EC", provider);
+        return (ECPrivateKey) keyFactory.generatePrivate(new PKCS8EncodedKeySpec(pkcs8EncodedKey));
+    }
+
+    private static ECPublicKey getPublicKey(byte[] x509EncodedKey, Provider provider)
+            throws GeneralSecurityException {
+        KeyFactory keyFactory = KeyFactory.getInstance("EC", provider);
+        return (ECPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedKey));
+    }
+
+    private static ECPrivateKey getPrivateKey(byte[] pkcs8EncodedKey)
+            throws GeneralSecurityException {
+        KeyFactory keyFactory = KeyFactory.getInstance("EC");
+        return (ECPrivateKey) keyFactory.generatePrivate(new PKCS8EncodedKeySpec(pkcs8EncodedKey));
+    }
+
+    private static ECPublicKey getPublicKey(byte[] x509EncodedKey)
+            throws GeneralSecurityException {
+        KeyFactory keyFactory = KeyFactory.getInstance("EC");
+        return (ECPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedKey));
+    }
+
+    private static KeyAgreement getKeyAgreement(Provider provider) throws NoSuchAlgorithmException {
+        return KeyAgreement.getInstance("ECDH", provider);
+    }
+
+    private static byte[] generateSecret(
+            Provider keyAgreementProvider, PrivateKey privateKey, PublicKey publicKey)
+            throws GeneralSecurityException {
+        KeyAgreement keyAgreement = getKeyAgreement(keyAgreementProvider);
+        keyAgreement.init(privateKey);
+        keyAgreement.doPhase(publicKey, true);
+        return keyAgreement.generateSecret();
+    }
+
+    private static Provider[] sortByName(Provider[] providers) {
+        Arrays.sort(providers, new Comparator<Provider>() {
+            @Override
+            public int compare(Provider lhs, Provider rhs) {
+                return lhs.getName().compareTo(rhs.getName());
+            }
+        });
+        return providers;
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/crypto/KeyGeneratorTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/crypto/KeyGeneratorTest.java
new file mode 100644
index 0000000..4028ce9
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/crypto/KeyGeneratorTest.java
@@ -0,0 +1,194 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.crypto;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.org.conscrypt.TestUtils;
+import com.android.org.conscrypt.java.security.StandardNames;
+import dalvik.system.VMRuntime;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import sun.security.jca.Providers;
+import tests.util.ServiceTester;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class KeyGeneratorTest {
+    
+    // BEGIN Android-Added: Allow access to deprecated BC algorithms.
+    // Allow access to deprecated BC algorithms in this test, so we can ensure they
+    // continue to work
+    @BeforeClass
+    public static void enableDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                VMRuntime.getRuntime().getTargetSdkVersion());
+    }
+
+    @AfterClass
+    public static void restoreDeprecatedAlgorithms() {
+        Providers.setMaximumAllowableApiLevelForBcDeprecation(
+                Providers.DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION);
+    }
+    // END Android-Added: Allow access to deprecated BC algorithms.
+
+    private static boolean isUnsupported(KeyGenerator kg) {
+        // Don't bother testing "Sun..." KeyGenerators or BC outside of Android
+        return kg.getProvider().getName().startsWith("Sun")
+                || (StandardNames.IS_RI && kg.getProvider().getName().equals("BC"));
+    }
+
+    @BeforeClass
+    public static void setUp() {
+        TestUtils.assumeAllowsUnsignedCrypto();
+    }
+
+    @Test
+    public void test_getInstance() throws Exception {
+        ServiceTester
+                .test("KeyGenerator")
+                // Do not test AndroidKeyStore's KeyGenerator. It cannot be initialized without
+                // providing AndroidKeyStore-specific algorithm parameters.
+                // It's OKish not to test AndroidKeyStore's KeyGenerator here because it's tested
+                // by cts/tests/test/keystore.
+                .skipProvider("AndroidKeyStore")
+                .run(new ServiceTester.Test() {
+                    @Override
+                    public void test(Provider provider, String algorithm) throws Exception {
+                        // KeyGenerator.getInstance(String)
+                        KeyGenerator kg1 = KeyGenerator.getInstance(algorithm);
+                        assertEquals(algorithm, kg1.getAlgorithm());
+                        test_KeyGenerator(kg1);
+
+                        // KeyGenerator.getInstance(String, Provider)
+                        KeyGenerator kg2 = KeyGenerator.getInstance(algorithm, provider);
+                        assertEquals(algorithm, kg2.getAlgorithm());
+                        assertEquals(provider, kg2.getProvider());
+                        test_KeyGenerator(kg2);
+
+                        // KeyGenerator.getInstance(String, String)
+                        KeyGenerator kg3 = KeyGenerator.getInstance(algorithm, provider.getName());
+                        assertEquals(algorithm, kg3.getAlgorithm());
+                        assertEquals(provider, kg3.getProvider());
+                        test_KeyGenerator(kg3);
+                    }
+                });
+    }
+
+    private static final Map<String, List<Integer>> KEY_SIZES
+            = new HashMap<String, List<Integer>>();
+    private static void putKeySize(String algorithm, int keySize) {
+        algorithm = algorithm.toUpperCase();
+        List<Integer> keySizes = KEY_SIZES.get(algorithm);
+        if (keySizes == null) {
+            keySizes = new ArrayList<Integer>();
+            KEY_SIZES.put(algorithm, keySizes);
+        }
+        keySizes.add(keySize);
+    }
+    private static List<Integer> getKeySizes(String algorithm) throws Exception {
+        algorithm = algorithm.toUpperCase();
+        List<Integer> keySizes = KEY_SIZES.get(algorithm);
+        if (keySizes == null) {
+            throw new Exception("Unknown key sizes for KeyGenerator." + algorithm);
+        }
+        return keySizes;
+    }
+    static {
+        putKeySize("AES", 128);
+        putKeySize("AES", 192);
+        putKeySize("AES", 256);
+        putKeySize("ARC4", 1024);
+        putKeySize("ARC4", 40);
+        putKeySize("ARC4", 41);
+        putKeySize("ARCFOUR", 1024);
+        putKeySize("ARCFOUR", 40);
+        putKeySize("ARCFOUR", 41);
+        putKeySize("Blowfish", 32);
+        putKeySize("Blowfish", 32+8);
+        putKeySize("Blowfish", 448);
+        putKeySize("ChaCha20", 256);
+        putKeySize("DES", 56);
+        putKeySize("DESede", 112);
+        putKeySize("DESede", 168);
+        putKeySize("RC2", 40);
+        putKeySize("RC2", 41);
+        putKeySize("RC2", 1024);
+        putKeySize("RC4", 40);
+        putKeySize("RC4", 41);
+        putKeySize("RC4", 1024);
+        putKeySize("HmacMD5", 1);
+        putKeySize("HmacMD5", 1025);
+        putKeySize("HmacSHA1", 1);
+        putKeySize("HmacSHA1", 1025);
+        putKeySize("HmacSHA224", 40);
+        putKeySize("HmacSHA224", 1025);
+        putKeySize("HmacSHA256", 40);
+        putKeySize("HmacSHA256", 1025);
+        putKeySize("HmacSHA384", 40);
+        putKeySize("HmacSHA384", 1025);
+        putKeySize("HmacSHA512", 40);
+        putKeySize("HmacSHA512", 1025);
+    }
+
+    private void test_KeyGenerator(KeyGenerator kg) throws Exception {
+        if (isUnsupported(kg)) {
+            return;
+        }
+
+        kg.init((SecureRandom) null);
+        test_SecretKey(kg, kg.generateKey());
+
+        kg.init(new SecureRandom());
+        test_SecretKey(kg, kg.generateKey());
+
+        String algorithm = kg.getAlgorithm();
+        List<Integer> keySizes = getKeySizes(algorithm);
+        for (int keySize : keySizes) {
+            kg.init(keySize);
+            test_SecretKey(kg, kg.generateKey());
+
+            kg.init(keySize, (SecureRandom) null);
+            test_SecretKey(kg, kg.generateKey());
+
+            kg.init(keySize, new SecureRandom());
+            test_SecretKey(kg, kg.generateKey());
+        }
+    }
+
+    private void test_SecretKey(KeyGenerator kg, SecretKey sk) throws Exception {
+        assertNotNull(sk);
+        assertEquals(kg.getAlgorithm().toUpperCase(), sk.getAlgorithm().toUpperCase());
+        assertNotNull(sk.getEncoded());
+        assertNotNull(sk.getFormat());
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/HttpsURLConnectionTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/HttpsURLConnectionTest.java
new file mode 100644
index 0000000..78425dc
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/HttpsURLConnectionTest.java
@@ -0,0 +1,168 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.URL;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class HttpsURLConnectionTest {
+    /**
+     * HTTPS URL which cannot be resolved and is thus safe to use in tests where network traffic
+     * should be avoided.
+     */
+    private static final String UNRESOLVABLE_HTTPS_URL = "https:///";
+
+    @Test
+    public void testDefaultHostnameVerifierNotNull() {
+        assertNotNull(HttpsURLConnection.getDefaultHostnameVerifier());
+    }
+
+    @Test
+    public void testDefaultHostnameVerifierUsedForNewConnectionsByDefault() throws IOException {
+        HostnameVerifier originalHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
+        HttpsURLConnection connection =
+                (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+        try {
+            assertSame(originalHostnameVerifier, connection.getHostnameVerifier());
+        } finally {
+            connection.disconnect();
+        }
+
+        HostnameVerifier anotherVerifier = new FakeHostnameVerifier();
+        try {
+            HttpsURLConnection.setDefaultHostnameVerifier(anotherVerifier);
+            connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+            try {
+                assertSame(anotherVerifier, connection.getHostnameVerifier());
+            } finally {
+                connection.disconnect();
+            }
+
+            HttpsURLConnection.setDefaultHostnameVerifier(originalHostnameVerifier);
+            connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+            try {
+                assertSame(originalHostnameVerifier, connection.getHostnameVerifier());
+            } finally {
+                connection.disconnect();
+            }
+        } finally {
+            HttpsURLConnection.setDefaultHostnameVerifier(originalHostnameVerifier);
+        }
+    }
+
+    @Test
+    public void testDefaultSSLSocketFactoryNotNull() {
+        assertNotNull(HttpsURLConnection.getDefaultSSLSocketFactory());
+    }
+
+    @Test
+    public void testDefaultSSLSocketFactoryUsedForNewConnectionsByDefault() throws IOException {
+        SSLSocketFactory originalFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
+        HttpsURLConnection connection =
+                (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+        try {
+            assertSame(originalFactory, connection.getSSLSocketFactory());
+        } finally {
+            connection.disconnect();
+        }
+
+        SSLSocketFactory anotherFactory = new FakeSSLSocketFactory();
+        try {
+            HttpsURLConnection.setDefaultSSLSocketFactory(anotherFactory);
+            connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+            try {
+                assertSame(anotherFactory, connection.getSSLSocketFactory());
+            } finally {
+                connection.disconnect();
+            }
+
+            HttpsURLConnection.setDefaultSSLSocketFactory(originalFactory);
+            connection = (HttpsURLConnection) new URL(UNRESOLVABLE_HTTPS_URL).openConnection();
+            try {
+                assertSame(originalFactory, connection.getSSLSocketFactory());
+            } finally {
+                connection.disconnect();
+            }
+        } finally {
+            HttpsURLConnection.setDefaultSSLSocketFactory(originalFactory);
+        }
+    }
+
+    private static final class FakeHostnameVerifier implements HostnameVerifier {
+        @Override
+        public boolean verify(String hostname, SSLSession session) {
+            return true;
+        }
+    }
+
+    private static final class FakeSSLSocketFactory extends SSLSocketFactory {
+        FakeSSLSocketFactory() {}
+
+        @Override
+        public String[] getDefaultCipherSuites() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String[] getSupportedCipherSuites() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Socket createSocket(Socket s, String host, int port, boolean autoClose) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Socket createSocket(
+                InetAddress address, int port, InetAddress localAddress, int localPort) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Socket createSocket(InetAddress host, int port) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Socket createSocket(String host, int port, InetAddress localHost, int localPort) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Socket createSocket(String host, int port) {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/KeyManagerFactoryTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/KeyManagerFactoryTest.java
new file mode 100644
index 0000000..d2424c7
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/KeyManagerFactoryTest.java
@@ -0,0 +1,521 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.org.conscrypt.KeyManagerFactoryImpl;
+import com.android.org.conscrypt.TestUtils;
+import com.android.org.conscrypt.java.security.StandardNames;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStore.PasswordProtection;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStoreException;
+import java.security.KeyStoreSpi;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Enumeration;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.KeyStoreBuilderParameters;
+import javax.net.ssl.ManagerFactoryParameters;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509KeyManager;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import tests.util.ServiceTester;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class KeyManagerFactoryTest {
+    private TestKeyStore testKeyStore;
+
+    @Before
+    public void setUp() throws Exception {
+        // note the rare usage of DSA keys here in addition to RSA
+        String[] keyAlgorithms = StandardNames.IS_RI
+                ? new String[] { "RSA", "DSA", "EC", "EC_RSA" }
+                : new String[] { "RSA", "DH_RSA", "DSA", "DH_DSA", "EC", "EC_RSA" };
+        testKeyStore = new TestKeyStore.Builder()
+                               .keyAlgorithms(keyAlgorithms)
+                               .aliasPrefix("rsa-dsa-ec-dh")
+                               .build();
+    }
+
+    private TestKeyStore getTestKeyStore() throws Exception {
+        return testKeyStore;
+    }
+
+    @Test
+    public void test_KeyManagerFactory_getDefaultAlgorithm() throws Exception {
+        String algorithm = KeyManagerFactory.getDefaultAlgorithm();
+        assertEquals(StandardNames.KEY_MANAGER_FACTORY_DEFAULT, algorithm);
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
+        test_KeyManagerFactory(kmf);
+    }
+
+    private static class UselessManagerFactoryParameters implements ManagerFactoryParameters {}
+
+    private static boolean supportsManagerFactoryParameters(String algorithm) {
+        // Only the "New" one supports ManagerFactoryParameters
+        return algorithm.equals("NewSunX509");
+    }
+
+    private static String[] keyTypes(String algorithm) {
+        // Although the "New" one supports ManagerFactoryParameters,
+        // it can't handle nulls in the key types array.
+        return (algorithm.equals("NewSunX509") ? KEY_TYPES_WITH_EMPTY
+                                               : KEY_TYPES_WITH_EMPTY_AND_NULL);
+    }
+
+    private void test_KeyManagerFactory(KeyManagerFactory kmf) throws Exception {
+        assertNotNull(kmf);
+        assertNotNull(kmf.getAlgorithm());
+        assertNotNull(kmf.getProvider());
+
+        // before init
+        try {
+            kmf.getKeyManagers();
+            fail();
+        } catch (IllegalStateException expected) {
+            // Ignore
+        }
+
+        // init with null ManagerFactoryParameters
+        try {
+            kmf.init(null);
+            fail();
+        } catch (InvalidAlgorithmParameterException expected) {
+            // Ignore
+        }
+
+        // init with useless ManagerFactoryParameters
+        try {
+            kmf.init(new UselessManagerFactoryParameters());
+            fail();
+        } catch (InvalidAlgorithmParameterException expected) {
+            // Ignore
+        }
+
+        // init with KeyStoreBuilderParameters ManagerFactoryParameters
+        PasswordProtection pp = new PasswordProtection(getTestKeyStore().storePassword);
+        KeyStore.Builder builder = KeyStore.Builder.newInstance(getTestKeyStore().keyStore, pp);
+        KeyStoreBuilderParameters ksbp = new KeyStoreBuilderParameters(builder);
+        if (supportsManagerFactoryParameters(kmf.getAlgorithm())) {
+            kmf.init(ksbp);
+            test_KeyManagerFactory_getKeyManagers(kmf, false);
+        } else {
+            try {
+                kmf.init(ksbp);
+                fail();
+            } catch (InvalidAlgorithmParameterException expected) {
+                // Ignore
+            }
+        }
+
+        // init with null for default behavior
+        kmf.init(null, null);
+        test_KeyManagerFactory_getKeyManagers(kmf, true);
+
+        // init with specific key store and password
+        kmf.init(getTestKeyStore().keyStore, getTestKeyStore().storePassword);
+        test_KeyManagerFactory_getKeyManagers(kmf, false);
+    }
+
+    private void test_KeyManagerFactory_getKeyManagers(KeyManagerFactory kmf, boolean empty)
+            throws Exception {
+        KeyManager[] keyManagers = kmf.getKeyManagers();
+        assertNotNull(keyManagers);
+        assertTrue(keyManagers.length > 0);
+        for (KeyManager keyManager : keyManagers) {
+            assertNotNull(keyManager);
+            if (keyManager instanceof X509KeyManager) {
+                test_X509KeyManager((X509KeyManager) keyManager, empty, kmf.getAlgorithm());
+            }
+        }
+    }
+
+    private static final String[] KEY_TYPES_ONLY =
+            StandardNames.KEY_TYPES.toArray(new String[StandardNames.KEY_TYPES.size()]);
+    private static final String[] KEY_TYPES_WITH_EMPTY = new String[KEY_TYPES_ONLY.length + 1];
+    private static final String[] KEY_TYPES_WITH_EMPTY_AND_NULL =
+            new String[KEY_TYPES_ONLY.length + 2];
+    static {
+        System.arraycopy(KEY_TYPES_ONLY, 0, KEY_TYPES_WITH_EMPTY, 0, KEY_TYPES_ONLY.length);
+        KEY_TYPES_WITH_EMPTY[KEY_TYPES_WITH_EMPTY.length - 1] = "";
+
+        System.arraycopy(KEY_TYPES_WITH_EMPTY, 0, KEY_TYPES_WITH_EMPTY_AND_NULL, 0,
+                KEY_TYPES_WITH_EMPTY.length);
+        // extra null at end requires no initialization
+    }
+
+    private void test_X509KeyManager(X509KeyManager km, boolean empty, String algorithm)
+            throws Exception {
+        String[] keyTypes = keyTypes(algorithm);
+        for (String keyType : keyTypes) {
+            String[] aliases = km.getClientAliases(keyType, null);
+            if (empty || keyType == null || keyType.isEmpty()) {
+                assertNull(keyType, aliases);
+                continue;
+            }
+            assertNotNull(keyType, aliases);
+            for (String alias : aliases) {
+                test_X509KeyManager_alias(km, alias, keyType, false, empty);
+            }
+        }
+        for (String keyType : keyTypes) {
+            String[] aliases = km.getServerAliases(keyType, null);
+            if (empty || keyType == null || keyType.isEmpty()) {
+                assertNull(keyType, aliases);
+                continue;
+            }
+            assertNotNull(keyType, aliases);
+            for (String alias : aliases) {
+                test_X509KeyManager_alias(km, alias, keyType, false, empty);
+            }
+        }
+
+        String[][] rotatedTypes = rotate(nonEmpty(keyTypes));
+        for (String[] keyList : rotatedTypes) {
+            String alias = km.chooseClientAlias(keyList, null, null);
+            test_X509KeyManager_alias(km, alias, null, true, empty);
+        }
+
+        for (String keyType : keyTypes) {
+            String[] array = new String[] {keyType};
+            String alias = km.chooseClientAlias(array, null, null);
+            test_X509KeyManager_alias(km, alias, keyType, false, empty);
+        }
+        for (String keyType : keyTypes) {
+            String alias = km.chooseServerAlias(keyType, null, null);
+            test_X509KeyManager_alias(km, alias, keyType, false, empty);
+        }
+        if (km instanceof X509ExtendedKeyManager) {
+            test_X509ExtendedKeyManager((X509ExtendedKeyManager) km, empty, algorithm);
+        }
+    }
+
+    private void test_X509ExtendedKeyManager(
+            X509ExtendedKeyManager km, boolean empty, String algorithm) throws Exception {
+        String[] keyTypes = keyTypes(algorithm);
+        String[][] rotatedTypes = rotate(nonEmpty(keyTypes));
+        for (String[] keyList : rotatedTypes) {
+            String alias = km.chooseEngineClientAlias(keyList, null, null);
+            test_X509KeyManager_alias(km, alias, null, true, empty);
+        }
+
+        for (String keyType : keyTypes) {
+            String[] array = new String[] {keyType};
+            String alias = km.chooseEngineClientAlias(array, null, null);
+            test_X509KeyManager_alias(km, alias, keyType, false, empty);
+        }
+        for (String keyType : keyTypes) {
+            String alias = km.chooseEngineServerAlias(keyType, null, null);
+            test_X509KeyManager_alias(km, alias, keyType, false, empty);
+        }
+    }
+
+    // Filters null or empty values from a String array and returns a new array with the results.
+    private static String[] nonEmpty(String[] input) {
+        String[] nonEmpty = new String[input.length];
+        int size = 0;
+        for (String keyType : input) {
+            if (keyType != null && !keyType.isEmpty()) {
+                nonEmpty[size++] = keyType;
+            }
+        }
+        return Arrays.copyOfRange(nonEmpty, 0, size);
+    }
+
+    // Generates an array of arrays of all the rotational permutations of its input.
+    private static String[][] rotate(String[] input) {
+        int size = input.length;
+        String[][] result = new String[size][size];
+        for (int i = 0; i < size; i++) {
+            for (int j = 0; j < size; j++) {
+                result[i][j] = input[(i + j) % size];
+            }
+        }
+        return result;
+    }
+
+    private void test_X509KeyManager_alias(X509KeyManager km, String alias, String keyType,
+            boolean many, boolean empty) throws Exception {
+        if (empty || (!many && (keyType == null || keyType.isEmpty()))) {
+            assertNull(keyType, alias);
+            assertNull(keyType, km.getCertificateChain(alias));
+            assertNull(keyType, km.getPrivateKey(alias));
+            return;
+        }
+        assertNotNull(alias);
+        X509Certificate[] certificateChain = km.getCertificateChain(alias);
+        PrivateKey privateKey = km.getPrivateKey(alias);
+
+        String keyAlgName = privateKey.getAlgorithm();
+
+        X509Certificate certificate = certificateChain[0];
+        assertEquals(keyType, keyAlgName, certificate.getPublicKey().getAlgorithm());
+
+        String sigAlgName = certificate.getSigAlgName();
+
+        PrivateKeyEntry privateKeyEntry = getTestKeyStore().getPrivateKey(keyAlgName, sigAlgName);
+
+        assertEquals(keyType, Arrays.asList(privateKeyEntry.getCertificateChain()),
+                Arrays.<Certificate>asList(certificateChain));
+        assertEquals(keyType, privateKeyEntry.getPrivateKey(), privateKey);
+
+        if (keyType != null) {
+            assertEquals(TestKeyStore.keyAlgorithm(keyType), keyAlgName);
+
+            // Skip this when we're given only "DH" or "EC" instead of "DH_DSA",
+            // "EC_RSA", etc. since we don't know what the expected
+            // algorithm was.
+            if (!keyType.equals("DH") && !keyType.equals("EC")) {
+                assertTrue("SigAlg: " + sigAlgName + ", KeyType: " + keyType,
+                        sigAlgName.contains(TestKeyStore.signatureAlgorithm(keyType)));
+            }
+        }
+    }
+
+    @Test
+    public void test_KeyManagerFactory_getInstance() throws Exception {
+        ServiceTester.test("KeyManagerFactory").run(new ServiceTester.Test() {
+            @Override
+            public void test(Provider provider, String algorithm) throws Exception {
+                KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
+                assertEquals(algorithm, kmf.getAlgorithm());
+                test_KeyManagerFactory(kmf);
+
+                kmf = KeyManagerFactory.getInstance(algorithm, provider);
+                assertEquals(algorithm, kmf.getAlgorithm());
+                assertEquals(provider, kmf.getProvider());
+                test_KeyManagerFactory(kmf);
+
+                kmf = KeyManagerFactory.getInstance(algorithm, provider.getName());
+                assertEquals(algorithm, kmf.getAlgorithm());
+                assertEquals(provider, kmf.getProvider());
+                test_KeyManagerFactory(kmf);
+            }
+        });
+    }
+
+    // The Conscrypt provider on OpenJDK doesn't provide the KeyManagerFactory, but we want
+    // to test it on OpenJDK anyway
+    @Test
+    public void test_KeyManagerFactory_Conscrypt() throws Exception {
+        KeyManagerFactory kmf = new KeyManagerFactory(new KeyManagerFactoryImpl(),
+                TestUtils.getConscryptProvider(), KeyManagerFactory.getDefaultAlgorithm()) {};
+        test_KeyManagerFactory(kmf);
+
+        // Test that using a KeyStore that doesn't implement getEntry(), like Android Keystore
+        // doesn't, still produces a functional KeyManager.
+        kmf.init(new NoGetEntryKeyStore(getTestKeyStore().keyStore),
+                getTestKeyStore().storePassword);
+        test_KeyManagerFactory_getKeyManagers(kmf, false);
+    }
+
+    private static class NoGetEntryKeyStore extends KeyStore {
+        public NoGetEntryKeyStore(KeyStore keyStore) throws Exception {
+            super(new NoGetEntryKeyStoreSpi(keyStore), keyStore.getProvider(), keyStore.getType());
+            load(null, null);
+        }
+    }
+
+    // Android Keystore's KeyStore doesn't support getEntry(), so we replicate that here
+    // for testing by throwing UnsupportedOperationException and passing everything else through
+    // to a working implementation.
+    private static class NoGetEntryKeyStoreSpi extends KeyStoreSpi {
+        private final KeyStore keyStore;
+
+        public NoGetEntryKeyStoreSpi(KeyStore keyStore) {
+            this.keyStore = keyStore;
+        }
+
+        @Override
+        public KeyStore.Entry engineGetEntry(String alias, KeyStore.ProtectionParameter protParam) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Key engineGetKey(String s, char[] chars)
+                throws NoSuchAlgorithmException, UnrecoverableKeyException {
+            try {
+                return keyStore.getKey(s, chars);
+            } catch (KeyStoreException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public Certificate[] engineGetCertificateChain(String s) {
+            try {
+                return keyStore.getCertificateChain(s);
+            } catch (KeyStoreException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public Certificate engineGetCertificate(String s) {
+            try {
+                return keyStore.getCertificate(s);
+            } catch (KeyStoreException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public Date engineGetCreationDate(String s) {
+            try {
+                return keyStore.getCreationDate(s);
+            } catch (KeyStoreException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public void engineSetKeyEntry(String s, Key key, char[] chars, Certificate[] certificates)
+                throws KeyStoreException {
+            try {
+                keyStore.setKeyEntry(s, key, chars, certificates);
+            } catch (KeyStoreException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public void engineSetKeyEntry(String s, byte[] bytes, Certificate[] certificates)
+                throws KeyStoreException {
+            try {
+                keyStore.setKeyEntry(s, bytes, certificates);
+            } catch (KeyStoreException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public void engineSetCertificateEntry(String s, Certificate certificate)
+                throws KeyStoreException {
+            try {
+                keyStore.setCertificateEntry(s, certificate);
+            } catch (KeyStoreException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public void engineDeleteEntry(String s) throws KeyStoreException {
+            try {
+                keyStore.deleteEntry(s);
+            } catch (KeyStoreException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public Enumeration<String> engineAliases() {
+            try {
+                return keyStore.aliases();
+            } catch (KeyStoreException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public boolean engineContainsAlias(String s) {
+            try {
+                return keyStore.containsAlias(s);
+            } catch (KeyStoreException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public int engineSize() {
+            try {
+                return keyStore.size();
+            } catch (KeyStoreException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public boolean engineIsKeyEntry(String s) {
+            try {
+                return keyStore.isKeyEntry(s);
+            } catch (KeyStoreException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public boolean engineIsCertificateEntry(String s) {
+            try {
+                return keyStore.isCertificateEntry(s);
+            } catch (KeyStoreException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public String engineGetCertificateAlias(Certificate certificate) {
+            try {
+                return keyStore.getCertificateAlias(certificate);
+            } catch (KeyStoreException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public void engineStore(OutputStream outputStream, char[] chars)
+                throws IOException, NoSuchAlgorithmException, CertificateException {
+            try {
+                keyStore.store(outputStream, chars);
+            } catch (KeyStoreException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public void engineLoad(InputStream inputStream, char[] chars)
+                throws IOException, NoSuchAlgorithmException, CertificateException {
+            // Do nothing, the keystore is already loaded
+        }
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/KeyStoreBuilderParametersTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/KeyStoreBuilderParametersTest.java
new file mode 100644
index 0000000..ff34e5b
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/KeyStoreBuilderParametersTest.java
@@ -0,0 +1,118 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+import java.security.KeyStore;
+import java.security.KeyStore.PasswordProtection;
+import java.util.Arrays;
+import java.util.List;
+import javax.net.ssl.KeyStoreBuilderParameters;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class KeyStoreBuilderParametersTest {
+
+    private static void assumeObjectsAvailable() {
+        boolean available = false;
+        try {
+            Class.forName("java.util.Objects");
+            available = true;
+        } catch (ClassNotFoundException ignore) {
+            // Ignored
+        }
+        Assume.assumeTrue("Skipping test: Objects unavailable", available);
+    }
+
+    @Test
+    public void test_init_Builder_null() {
+        // KeyStoreBuilderParameters' constructor didn't check for null until
+        // Objects.requireNonNull was added
+        assumeObjectsAvailable();
+        try {
+            new KeyStoreBuilderParameters((KeyStore.Builder) null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    @Test
+    public void test_init_Builder() {
+        TestKeyStore testKeyStore = TestKeyStore.getClient();
+        KeyStore.Builder builder = KeyStore.Builder.newInstance(
+                testKeyStore.keyStore, new PasswordProtection(testKeyStore.storePassword));
+        KeyStoreBuilderParameters ksbp = new KeyStoreBuilderParameters(builder);
+        assertNotNull(ksbp);
+        assertNotNull(ksbp.getParameters());
+        assertEquals(1, ksbp.getParameters().size());
+        assertSame(builder, ksbp.getParameters().get(0));
+    }
+
+    @Test
+    public void test_init_List_null() {
+        try {
+            new KeyStoreBuilderParameters((List<KeyStore.Builder>) null);
+            fail();
+        } catch (NullPointerException expected) {
+            // Ignored.
+        }
+    }
+
+    @Test
+    public void test_init_List() {
+        TestKeyStore testKeyStore1 = TestKeyStore.getClient();
+        TestKeyStore testKeyStore2 = TestKeyStore.getServer();
+        KeyStore.Builder builder1 = KeyStore.Builder.newInstance(
+                testKeyStore1.keyStore, new PasswordProtection(testKeyStore1.storePassword));
+        KeyStore.Builder builder2 = KeyStore.Builder.newInstance(
+                testKeyStore2.keyStore, new PasswordProtection(testKeyStore2.storePassword));
+
+        List<KeyStore.Builder> list = Arrays.asList(builder1, builder2);
+        KeyStoreBuilderParameters ksbp = new KeyStoreBuilderParameters(list);
+        assertNotNull(ksbp);
+        assertNotNull(ksbp.getParameters());
+        assertNotSame(list, ksbp.getParameters());
+        assertEquals(2, ksbp.getParameters().size());
+        assertSame(builder1, ksbp.getParameters().get(0));
+        assertSame(builder2, ksbp.getParameters().get(1));
+
+        // confirm result is not modifiable
+        try {
+            ksbp.getParameters().set(0, builder2);
+            fail();
+        } catch (UnsupportedOperationException expected) {
+            // Ignored.
+        }
+
+        // confirm result is a copy of original
+        list.set(0, builder2);
+        assertSame(builder1, ksbp.getParameters().get(0));
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SNIHostNameTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SNIHostNameTest.java
new file mode 100644
index 0000000..48e8eee
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SNIHostNameTest.java
@@ -0,0 +1,53 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.StandardConstants;
+import com.android.org.conscrypt.TestUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class SNIHostNameTest {
+    @Test
+    public void test_byteArray_Constructor() throws Exception {
+        TestUtils.assumeSNIHostnameAvailable();
+
+        // From draft-josefsson-idn-test-vectors-00 section 5.2
+        byte[] idnEncoded = new byte[] {
+            (byte) 0xE4, (byte) 0xBB, (byte) 0x96, (byte) 0xE4, (byte) 0xBB, (byte) 0xAC,
+            (byte) 0xE4, (byte) 0xB8, (byte) 0xBA, (byte) 0xE4, (byte) 0xBB, (byte) 0x80,
+            (byte) 0xE4, (byte) 0xB9, (byte) 0x88, (byte) 0xE4, (byte) 0xB8, (byte) 0x8D,
+            (byte) 0xE8, (byte) 0xAF, (byte) 0xB4, (byte) 0xE4, (byte) 0xB8, (byte) 0xAD,
+            (byte) 0xE6, (byte) 0x96, (byte) 0x87,
+        };
+
+        SNIHostName hostName = new SNIHostName(idnEncoded);
+        assertEquals("xn--ihqwcrb4cv8a8dqg056pqjye", hostName.getAsciiName());
+        assertEquals(StandardConstants.SNI_HOST_NAME, hostName.getType());
+        assertEquals(Arrays.toString(idnEncoded), Arrays.toString(hostName.getEncoded()));
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLContextTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLContextTest.java
new file mode 100644
index 0000000..fd767b8
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLContextTest.java
@@ -0,0 +1,745 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivilegedAction;
+import java.security.Provider;
+import java.security.Security;
+import java.security.UnrecoverableKeyException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import javax.net.ServerSocketFactory;
+import javax.net.SocketFactory;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.KeyManagerFactorySpi;
+import javax.net.ssl.ManagerFactoryParameters;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.TrustManagerFactorySpi;
+import javax.net.ssl.X509KeyManager;
+import junit.framework.AssertionFailedError;
+import com.android.org.conscrypt.TestUtils;
+import com.android.org.conscrypt.java.security.StandardNames;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class SSLContextTest {
+
+    @Test
+    public void test_SSLContext_getDefault() throws Exception {
+        SSLContext sslContext = SSLContext.getDefault();
+        assertNotNull(sslContext);
+        try {
+            sslContext.init(null, null, null);
+            fail();
+        } catch (KeyManagementException expected) {
+            // Ignored.
+        }
+    }
+
+    @Test
+    public void test_SSLContext_setDefault() throws Exception {
+        try {
+            SSLContext.setDefault(null);
+            fail();
+        } catch (NullPointerException expected) {
+            // Ignored.
+        }
+
+        SSLContext defaultContext = SSLContext.getDefault();
+        for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+            SSLContext oldContext = SSLContext.getDefault();
+            assertNotNull(oldContext);
+            SSLContext newContext = SSLContext.getInstance(protocol);
+            assertNotNull(newContext);
+            assertNotSame(oldContext, newContext);
+            SSLContext.setDefault(newContext);
+            assertSame(newContext, SSLContext.getDefault());
+        }
+        SSLContext.setDefault(defaultContext);
+    }
+
+    @Test
+    public void test_SSLContext_defaultConfiguration() throws Exception {
+        SSLConfigurationAsserts.assertSSLContextDefaultConfiguration(SSLContext.getDefault());
+
+        for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS_WITH_DEFAULT_CONFIG) {
+            SSLContext sslContext = SSLContext.getInstance(protocol);
+            if (!protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+                sslContext.init(null, null, null);
+            }
+            SSLConfigurationAsserts.assertSSLContextDefaultConfiguration(sslContext);
+        }
+    }
+
+    @Test
+    public void test_SSLContext_pskOnlyConfiguration_defaultProviderOnly() throws Exception {
+        // Test the scenario where only a PSKKeyManager is provided and no TrustManagers are
+        // provided.
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(new KeyManager[] { PSKKeyManagerProxy.getConscryptPSKKeyManager(
+                                new PSKKeyManagerProxy())},
+                new TrustManager[0], null);
+        List<String> expectedCipherSuites =
+                new ArrayList<String>(StandardNames.CIPHER_SUITES_TLS13);
+        expectedCipherSuites.addAll(StandardNames.CIPHER_SUITES_DEFAULT_PSK);
+        expectedCipherSuites.add(StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION);
+        assertEnabledCipherSuites(expectedCipherSuites, sslContext);
+    }
+
+    @Test
+    public void test_SSLContext_x509AndPskConfiguration_defaultProviderOnly() throws Exception {
+        // Test the scenario where an X509TrustManager and PSKKeyManager are provided.
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(new KeyManager[] {PSKKeyManagerProxy.getConscryptPSKKeyManager(
+                                new PSKKeyManagerProxy())},
+                null, // Use default trust managers, one of which is an X.509 one.
+                null);
+        // The TLS 1.3 cipher suites appear before the PSK ones, so we need to dedup them
+        Set<String> expectedCipherSuiteSet = new LinkedHashSet<String>();
+        expectedCipherSuiteSet.addAll(StandardNames.CIPHER_SUITES_TLS13);
+        expectedCipherSuiteSet.addAll(StandardNames.CIPHER_SUITES_DEFAULT_PSK);
+        expectedCipherSuiteSet.addAll(StandardNames.CIPHER_SUITES_DEFAULT);
+        List<String> expectedCipherSuites = new ArrayList<String>(expectedCipherSuiteSet);
+        assertEnabledCipherSuites(expectedCipherSuites, sslContext);
+
+        // Test the scenario where an X509KeyManager and PSKKeyManager are provided.
+        sslContext = SSLContext.getInstance("TLS");
+        // Just an arbitrary X509KeyManager -- it won't be invoked in this test.
+        X509KeyManager x509KeyManager = new RandomPrivateKeyX509ExtendedKeyManager(null);
+        sslContext.init(
+                new KeyManager[] {x509KeyManager,
+                        PSKKeyManagerProxy.getConscryptPSKKeyManager(new PSKKeyManagerProxy())},
+                new TrustManager[0], null);
+        assertEnabledCipherSuites(expectedCipherSuites, sslContext);
+    }
+
+    @Test
+    public void test_SSLContext_emptyConfiguration_defaultProviderOnly() throws Exception {
+        // Test the scenario where neither X.509 nor PSK KeyManagers or TrustManagers are provided.
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(new KeyManager[0], new TrustManager[0], null);
+        // No TLS 1.2 cipher suites should be enabled, since neither PSK nor X.509 key exchange
+        // can be done.  The TLS 1.3 cipher suites should be there, since key exchange isn't
+        // part of the cipher suite in 1.3.
+        List<String> expected = new ArrayList<String>(StandardNames.CIPHER_SUITES_TLS13);
+        expected.add(StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION);
+        assertEnabledCipherSuites(expected, sslContext);
+    }
+
+    @Test
+    public void test_SSLContext_init_correctProtocolVersionsEnabled() throws Exception {
+        for (String tlsVersion : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+            // Don't test the "Default" instance.
+            if (StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT.equals(tlsVersion)) {
+                continue;
+            }
+
+            SSLContext context = SSLContext.getInstance(tlsVersion);
+            context.init(null, null, null);
+
+            StandardNames.assertSSLContextEnabledProtocols(tlsVersion,
+                    ((SSLSocket) context.getSocketFactory().createSocket()).getEnabledProtocols());
+            StandardNames.assertSSLContextEnabledProtocols(tlsVersion,
+                    ((SSLServerSocket) context.getServerSocketFactory().createServerSocket())
+                            .getEnabledProtocols());
+            StandardNames.assertSSLContextEnabledProtocols(
+                    tlsVersion, context.getDefaultSSLParameters().getProtocols());
+            StandardNames.assertSSLContextEnabledProtocols(
+                    tlsVersion, context.createSSLEngine().getEnabledProtocols());
+        }
+    }
+
+    private static void assertEnabledCipherSuites(
+            List<String> expectedCipherSuites, SSLContext sslContext) throws Exception {
+        TestUtils.assumeSetEndpointIdentificationAlgorithmAvailable();
+        assertContentsInOrder(
+                expectedCipherSuites, sslContext.createSSLEngine().getEnabledCipherSuites());
+        assertContentsInOrder(expectedCipherSuites,
+                sslContext.createSSLEngine().getSSLParameters().getCipherSuites());
+        assertContentsInOrder(
+                expectedCipherSuites, sslContext.getSocketFactory().getDefaultCipherSuites());
+        assertContentsInOrder(
+                expectedCipherSuites, sslContext.getServerSocketFactory().getDefaultCipherSuites());
+
+        SSLSocket sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket();
+        try {
+            assertContentsInOrder(expectedCipherSuites, sslSocket.getEnabledCipherSuites());
+            assertContentsInOrder(
+                    expectedCipherSuites, sslSocket.getSSLParameters().getCipherSuites());
+        } finally {
+            try {
+                sslSocket.close();
+            } catch (IOException ignored) {
+            }
+        }
+
+        SSLServerSocket sslServerSocket =
+                (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket();
+        try {
+            assertContentsInOrder(expectedCipherSuites, sslServerSocket.getEnabledCipherSuites());
+        } finally {
+            try {
+                sslSocket.close();
+            } catch (IOException ignored) {
+            }
+        }
+    }
+
+    @Test
+    public void test_SSLContext_getInstance() throws Exception {
+        try {
+            SSLContext.getInstance(null);
+            fail();
+        } catch (NullPointerException expected) {
+            // Ignored.
+        }
+        for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+            assertNotNull(SSLContext.getInstance(protocol));
+            assertNotSame(SSLContext.getInstance(protocol), SSLContext.getInstance(protocol));
+        }
+
+        try {
+            SSLContext.getInstance(null, (String) null);
+            fail();
+        } catch (Exception expected) {
+            if (javaVersion() >= 9) {
+                assertTrue("Expected NullPointerException on Java 9, was "
+                                + expected.getClass().getName(),
+                        expected instanceof NullPointerException);
+            } else {
+                assertTrue(
+                        "Expected IllegalArgumentException, was " + expected.getClass().getName(),
+                        expected instanceof IllegalArgumentException);
+            }
+        }
+        try {
+            SSLContext.getInstance(null, "");
+            fail();
+        } catch (Exception expected) {
+            if (javaVersion() >= 9) {
+                assertTrue("Expected NullPointerException on Java 9, was "
+                        + expected.getClass().getName(),
+                    expected instanceof NullPointerException);
+            } else {
+                assertTrue(
+                    "Expected IllegalArgumentException, was " + expected.getClass().getName(),
+                    expected instanceof IllegalArgumentException);
+            }
+        }
+        for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+            try {
+                SSLContext.getInstance(protocol, (String) null);
+                fail();
+            } catch (IllegalArgumentException expected) {
+                // Ignored.
+            }
+        }
+        try {
+            SSLContext.getInstance(null, StandardNames.JSSE_PROVIDER_NAME);
+            fail();
+        } catch (NullPointerException expected) {
+            // Ignored.
+        }
+    }
+
+    @Test
+    public void test_SSLContext_getProtocol() throws Exception {
+        for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+            String protocolName = SSLContext.getInstance(protocol).getProtocol();
+            assertNotNull(protocolName);
+            assertTrue(protocol.startsWith(protocolName));
+        }
+    }
+
+    @Test
+    public void test_SSLContext_getProvider() throws Exception {
+        Provider provider = SSLContext.getDefault().getProvider();
+        assertNotNull(provider);
+        assertEquals(StandardNames.JSSE_PROVIDER_NAME, provider.getName());
+    }
+
+    @Test
+    public void test_SSLContext_init_Default() throws Exception {
+        // Assert that initializing a default SSLContext fails because it's supposed to be
+        // initialized already.
+        SSLContext sslContext = SSLContext.getInstance(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT);
+        try {
+            sslContext.init(null, null, null);
+            fail();
+        } catch (KeyManagementException expected) {
+            // Ignored.
+        }
+        try {
+            sslContext.init(new KeyManager[0], new TrustManager[0], null);
+            fail();
+        } catch (KeyManagementException expected) {
+            // Ignored.
+        }
+        try {
+            sslContext.init(new KeyManager[] {new KeyManager(){}},
+                    new TrustManager[] {new TrustManager(){}}, null);
+            fail();
+        } catch (KeyManagementException expected) {
+            // Ignored.
+        }
+    }
+
+    @Test
+    public void test_SSLContext_init_withNullManagerArrays() throws Exception {
+        // Assert that SSLContext.init works fine even when provided with null arrays of
+        // KeyManagers and TrustManagers.
+        // The contract of SSLContext.init is that it will for default X.509 KeyManager and
+        // TrustManager from the highest priority KeyManagerFactory and TrustManagerFactory.
+        for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+            if (protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+                // Default SSLContext is provided in an already initialized state
+                continue;
+            }
+            SSLContext sslContext = SSLContext.getInstance(protocol);
+            sslContext.init(null, null, null);
+        }
+    }
+
+    @Test
+    public void test_SSLContext_init_withEmptyManagerArrays() throws Exception {
+        // Assert that SSLContext.init works fine even when provided with empty arrays of
+        // KeyManagers and TrustManagers.
+        // The contract of SSLContext.init is that it will not look for default X.509 KeyManager and
+        // TrustManager.
+        // This test thus installs a Provider of KeyManagerFactory and TrustManagerFactory whose
+        // factories throw exceptions which will make this test fail if the factories are used.
+        Provider provider = new ThrowExceptionKeyAndTrustManagerFactoryProvider();
+        invokeWithHighestPrioritySecurityProvider(provider, new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                assertEquals(ThrowExceptionKeyAndTrustManagerFactoryProvider.class,
+                    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
+                        .getProvider()
+                        .getClass());
+                assertEquals(ThrowExceptionKeyAndTrustManagerFactoryProvider.class,
+                    KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
+                        .getProvider()
+                        .getClass());
+
+                KeyManager[] keyManagers = new KeyManager[0];
+                TrustManager[] trustManagers = new TrustManager[0];
+                for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+                    if (protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+                        // Default SSLContext is provided in an already initialized state
+                        continue;
+                    }
+                    SSLContext sslContext = SSLContext.getInstance(protocol);
+                    sslContext.init(keyManagers, trustManagers, null);
+                }
+
+                return null;
+            }
+        });
+    }
+
+    @Test
+    public void test_SSLContext_init_withoutX509() throws Exception {
+        // Assert that SSLContext.init works fine even when provided with KeyManagers and
+        // TrustManagers which don't include the X.509 ones.
+        // The contract of SSLContext.init is that it will not look for default X.509 KeyManager and
+        // TrustManager.
+        // This test thus installs a Provider of KeyManagerFactory and TrustManagerFactory whose
+        // factories throw exceptions which will make this test fail if the factories are used.
+        Provider provider = new ThrowExceptionKeyAndTrustManagerFactoryProvider();
+        invokeWithHighestPrioritySecurityProvider(provider, new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                assertEquals(ThrowExceptionKeyAndTrustManagerFactoryProvider.class,
+                    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
+                        .getProvider()
+                        .getClass());
+                assertEquals(ThrowExceptionKeyAndTrustManagerFactoryProvider.class,
+                    KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
+                        .getProvider()
+                        .getClass());
+
+                KeyManager[] keyManagers = new KeyManager[]{new KeyManager() {
+                }};
+                TrustManager[] trustManagers = new TrustManager[]{new TrustManager() {
+                }};
+                for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+                    if (protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+                        // Default SSLContext is provided in an already initialized state
+                        continue;
+                    }
+                    SSLContext sslContext = SSLContext.getInstance(protocol);
+                    sslContext.init(keyManagers, trustManagers, null);
+                }
+
+                return null;
+            }
+        });
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class ThrowExceptionKeyAndTrustManagerFactoryProvider extends Provider {
+        public ThrowExceptionKeyAndTrustManagerFactoryProvider() {
+            super("ThrowExceptionKeyAndTrustManagerProvider", 1.0,
+                    "SSLContextTest fake KeyManagerFactory  and TrustManagerFactory provider");
+
+            put("TrustManagerFactory." + TrustManagerFactory.getDefaultAlgorithm(),
+                    ThrowExceptionTrustManagagerFactorySpi.class.getName());
+            put("TrustManagerFactory.PKIX", ThrowExceptionTrustManagagerFactorySpi.class.getName());
+
+            put("KeyManagerFactory." + KeyManagerFactory.getDefaultAlgorithm(),
+                    ThrowExceptionKeyManagagerFactorySpi.class.getName());
+            put("KeyManagerFactory.PKIX", ThrowExceptionKeyManagagerFactorySpi.class.getName());
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class ThrowExceptionTrustManagagerFactorySpi extends TrustManagerFactorySpi {
+        @Override
+        protected void engineInit(KeyStore ks) throws KeyStoreException {
+            fail();
+        }
+
+        @Override
+        protected void engineInit(ManagerFactoryParameters spec)
+                throws InvalidAlgorithmParameterException {
+            fail();
+        }
+
+        @Override
+        protected TrustManager[] engineGetTrustManagers() {
+            throw new AssertionFailedError();
+        }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class ThrowExceptionKeyManagagerFactorySpi extends KeyManagerFactorySpi {
+        @Override
+        protected void engineInit(KeyStore ks, char[] password)
+                throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
+            fail();
+        }
+
+        @Override
+        protected void engineInit(ManagerFactoryParameters spec)
+                throws InvalidAlgorithmParameterException {
+            fail();
+        }
+
+        @Override
+        protected KeyManager[] engineGetKeyManagers() {
+            throw new AssertionFailedError();
+        }
+    }
+
+    /**
+     * Installs the specified security provider as the highest provider, invokes the provided
+     * {@link Callable}, and removes the provider.
+     *
+     * @return result returned by the {@code callable}.
+     */
+    private static <T> T invokeWithHighestPrioritySecurityProvider(
+            Provider provider, Callable<T> callable) throws Exception {
+        int providerPosition = -1;
+        try {
+            providerPosition = Security.insertProviderAt(provider, 1);
+            assertEquals(1, providerPosition);
+            return callable.call();
+        } finally {
+            if (providerPosition != -1) {
+                Security.removeProvider(provider.getName());
+            }
+        }
+    }
+
+    @Test
+    public void test_SSLContext_getSocketFactory() throws Exception {
+        for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+            if (protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+                SSLContext.getInstance(protocol).getSocketFactory();
+            } else {
+                try {
+                    SSLContext.getInstance(protocol).getSocketFactory();
+                    fail();
+                } catch (IllegalStateException expected) {
+                    // Ignored.
+                }
+            }
+
+            SSLContext sslContext = SSLContext.getInstance(protocol);
+            if (!protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+                sslContext.init(null, null, null);
+            }
+            SocketFactory sf = sslContext.getSocketFactory();
+            assertNotNull(sf);
+            assertTrue(SSLSocketFactory.class.isAssignableFrom(sf.getClass()));
+        }
+    }
+
+    @Test
+    public void test_SSLContext_getServerSocketFactory() throws Exception {
+        for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+            if (protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+                SSLContext.getInstance(protocol).getServerSocketFactory();
+            } else {
+                try {
+                    SSLContext.getInstance(protocol).getServerSocketFactory();
+                    fail();
+                } catch (IllegalStateException expected) {
+                    // Ignored.
+                }
+            }
+
+            SSLContext sslContext = SSLContext.getInstance(protocol);
+            if (!protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+                sslContext.init(null, null, null);
+            }
+            ServerSocketFactory ssf = sslContext.getServerSocketFactory();
+            assertNotNull(ssf);
+            assertTrue(SSLServerSocketFactory.class.isAssignableFrom(ssf.getClass()));
+        }
+    }
+
+    @Test
+    public void test_SSLContext_createSSLEngine() throws Exception {
+        for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+            if (protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+                SSLContext.getInstance(protocol).createSSLEngine();
+            } else {
+                try {
+                    SSLContext.getInstance(protocol).createSSLEngine();
+                    fail();
+                } catch (IllegalStateException expected) {
+                    // Ignored.
+                }
+            }
+
+            if (protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+                SSLContext.getInstance(protocol).createSSLEngine(null, -1);
+            } else {
+                try {
+                    SSLContext.getInstance(protocol).createSSLEngine(null, -1);
+                    fail();
+                } catch (IllegalStateException expected) {
+                    // Ignored.
+                }
+            }
+
+            {
+                SSLContext sslContext = SSLContext.getInstance(protocol);
+                if (!protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+                    sslContext.init(null, null, null);
+                }
+                SSLEngine se = sslContext.createSSLEngine();
+                assertNotNull(se);
+            }
+
+            {
+                SSLContext sslContext = SSLContext.getInstance(protocol);
+                if (!protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+                    sslContext.init(null, null, null);
+                }
+                SSLEngine se = sslContext.createSSLEngine(null, -1);
+                assertNotNull(se);
+            }
+        }
+    }
+
+    @Test
+    public void test_SSLContext_getServerSessionContext() throws Exception {
+        for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+            SSLContext sslContext = SSLContext.getInstance(protocol);
+            SSLSessionContext sessionContext = sslContext.getServerSessionContext();
+            assertNotNull(sessionContext);
+
+            if (protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+                assertSame(
+                        SSLContext.getInstance(protocol).getServerSessionContext(), sessionContext);
+            } else {
+                assertNotSame(
+                        SSLContext.getInstance(protocol).getServerSessionContext(), sessionContext);
+            }
+        }
+    }
+
+    @Test
+    public void test_SSLContext_getClientSessionContext() throws Exception {
+        for (String protocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+            SSLContext sslContext = SSLContext.getInstance(protocol);
+            SSLSessionContext sessionContext = sslContext.getClientSessionContext();
+            assertNotNull(sessionContext);
+
+            if (protocol.equals(StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT)) {
+                assertSame(
+                        SSLContext.getInstance(protocol).getClientSessionContext(), sessionContext);
+            } else {
+                assertNotSame(
+                        SSLContext.getInstance(protocol).getClientSessionContext(), sessionContext);
+            }
+        }
+    }
+
+    @Test
+    public void test_SSLContextTest_TestSSLContext_create() {
+        TestSSLContext testContext = TestSSLContext.create();
+        assertNotNull(testContext);
+        assertNotNull(testContext.clientKeyStore);
+        assertNull(testContext.clientStorePassword);
+        assertNotNull(testContext.serverKeyStore);
+        assertNotNull(testContext.clientKeyManagers);
+        assertNotNull(testContext.serverKeyManagers);
+        if (testContext.clientKeyManagers.length == 0) {
+            fail("No client KeyManagers");
+        }
+        if (testContext.serverKeyManagers.length == 0) {
+            fail("No server KeyManagers");
+        }
+        assertNotNull(testContext.clientKeyManagers[0]);
+        assertNotNull(testContext.serverKeyManagers[0]);
+        assertNotNull(testContext.clientTrustManager);
+        assertNotNull(testContext.serverTrustManager);
+        assertNotNull(testContext.clientContext);
+        assertNotNull(testContext.serverContext);
+        assertNotNull(testContext.serverSocket);
+        assertNotNull(testContext.host);
+        assertTrue(testContext.port != 0);
+        testContext.close();
+    }
+
+    @Test(expected = NoSuchAlgorithmException.class)
+    public void test_SSLContext_SSLv3Unsupported() throws Exception {
+        // Find the default provider for TLS and verify that it does NOT support SSLv3.
+        Provider defaultTlsProvider = null;
+        for (String protocol : new String[] {"SSLContext.TLSv1.2", "SSLContext.TLSv1"}) {
+            for (Provider p : Security.getProviders()) {
+                if (p.get(protocol) != null) {
+                    defaultTlsProvider = p;
+                    break;
+                }
+            }
+        }
+        assertNotNull(defaultTlsProvider);
+        SSLContext.getInstance("SSLv3", defaultTlsProvider);
+    }
+
+    private static void assertContentsInOrder(List<String> expected, String... actual) {
+        if (expected.size() != actual.length) {
+            fail("Unexpected length. Expected len <" + expected.size() + ">, actual len <"
+                    + actual.length + ">, expected <" + expected + ">, actual <"
+                    + Arrays.asList(actual) + ">");
+        }
+        if (!expected.equals(Arrays.asList(actual))) {
+            fail("Unexpected element(s). Expected <" + expected + ">, actual <"
+                    + Arrays.asList(actual) + ">");
+        }
+    }
+
+    private static boolean isAndroid() {
+        boolean android;
+        try {
+            Class.forName("android.app.Application", false, getSystemClassLoader());
+            android = true;
+        } catch (Throwable ignored) {
+            // Failed to load the class uniquely available in Android.
+            android = false;
+        }
+        return android;
+    }
+
+    private static int javaVersion() {
+        final int majorVersion;
+
+        if (isAndroid()) {
+            majorVersion = 6;
+        } else {
+            majorVersion = majorVersionFromJavaSpecificationVersion();
+        }
+
+        return majorVersion;
+    }
+
+    private static int majorVersionFromJavaSpecificationVersion() {
+        return majorVersion(System.getProperty("java.specification.version", "1.6"));
+    }
+
+    private static int majorVersion(final String javaSpecVersion) {
+        final String[] components = javaSpecVersion.split("\\.", -1);
+        final int[] version = new int[components.length];
+        for (int i = 0; i < components.length; i++) {
+            version[i] = Integer.parseInt(components[i]);
+        }
+
+        if (version[0] == 1) {
+            assert version[1] >= 6;
+            return version[1];
+        } else {
+            return version[0];
+        }
+    }
+
+    private static ClassLoader getSystemClassLoader() {
+        if (System.getSecurityManager() == null) {
+            return ClassLoader.getSystemClassLoader();
+        } else {
+            return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+                @Override
+                public ClassLoader run() {
+                    return ClassLoader.getSystemClassLoader();
+                }
+            });
+        }
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLEngineTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLEngineTest.java
new file mode 100644
index 0000000..fc773ca
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLEngineTest.java
@@ -0,0 +1,949 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static com.android.org.conscrypt.TestUtils.UTF_8;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.org.conscrypt.TestUtils;
+import com.android.org.conscrypt.java.security.StandardNames;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import java.io.IOException;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509ExtendedTrustManager;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class SSLEngineTest {
+    @Test
+    public void test_SSLEngine_defaultConfiguration() throws Exception {
+        SSLConfigurationAsserts.assertSSLEngineDefaultConfiguration(
+                TestSSLContext.create().clientContext.createSSLEngine());
+    }
+
+    @Test
+    public void test_SSLEngine_getSupportedCipherSuites_returnsCopies() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        SSLEngine e = c.clientContext.createSSLEngine();
+        assertNotSame(e.getSupportedCipherSuites(), e.getSupportedCipherSuites());
+        c.close();
+    }
+
+    @Test
+    public void test_SSLEngine_getSupportedCipherSuites_connect() throws Exception {
+        // note the rare usage of non-RSA keys
+        TestKeyStore testKeyStore = new TestKeyStore.Builder()
+                                            .keyAlgorithms("RSA", "DSA", "EC", "EC_RSA")
+                                            .aliasPrefix("rsa-dsa-ec")
+                                            .ca(true)
+                                            .build();
+        test_SSLEngine_getSupportedCipherSuites_connect(testKeyStore, false);
+        test_SSLEngine_getSupportedCipherSuites_connect(testKeyStore, true);
+    }
+
+    // http://b/18554122
+    @Test
+    public void test_SSLEngine_underflowsOnEmptyBuffersDuringHandshake() throws Exception {
+        final SSLEngine sslEngine = SSLContext.getDefault().createSSLEngine();
+        sslEngine.setUseClientMode(false);
+        ByteBuffer input = ByteBuffer.allocate(1024);
+        input.flip();
+        ByteBuffer output = ByteBuffer.allocate(1024);
+        sslEngine.beginHandshake();
+        assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP, sslEngine.getHandshakeStatus());
+        SSLEngineResult result = sslEngine.unwrap(input, output);
+        assertEquals(SSLEngineResult.Status.BUFFER_UNDERFLOW, result.getStatus());
+        assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus());
+    }
+
+    // http://b/18554122
+    @Test
+    public void test_SSLEngine_underflowsOnEmptyBuffersAfterHandshake() throws Exception {
+        // Note that create performs the handshake.
+        final TestSSLEnginePair engines = TestSSLEnginePair.create();
+        ByteBuffer input = ByteBuffer.allocate(1024);
+        input.flip();
+        ByteBuffer output = ByteBuffer.allocate(1024);
+        assertEquals(SSLEngineResult.Status.BUFFER_UNDERFLOW,
+                engines.client.unwrap(input, output).getStatus());
+    }
+
+    @Test
+    public void test_SSLEngine_wrap_overflowOnEmptyOutputBuffer() throws Exception {
+        TestSSLEnginePair pair = TestSSLEnginePair.create();
+        ByteBuffer input = ByteBuffer.allocate(10);
+        ByteBuffer output = ByteBuffer.allocate(1024);
+        output.flip();
+        assertEquals(Status.BUFFER_OVERFLOW, pair.client.wrap(input, output).getStatus());
+    }
+
+    @Test
+    public void test_SSLEngine_unwrap_overflowOnEmptyOutputBuffer() throws Exception {
+        TestSSLEnginePair pair = TestSSLEnginePair.create();
+        ByteBuffer input = ByteBuffer.allocate(10);
+        ByteBuffer wrapped = ByteBuffer.allocate(1024);
+        assertEquals(Status.OK, pair.client.wrap(input, wrapped).getStatus());
+        wrapped.flip();
+        ByteBuffer output = ByteBuffer.allocate(1024);
+        output.flip();
+        assertEquals(Status.BUFFER_OVERFLOW, pair.server.unwrap(wrapped, output).getStatus());
+    }
+
+    private void test_SSLEngine_getSupportedCipherSuites_connect(
+            TestKeyStore testKeyStore, boolean secureRenegotiation) throws Exception {
+        KeyManager pskKeyManager =
+                PSKKeyManagerProxy.getConscryptPSKKeyManager(new PSKKeyManagerProxy() {
+                    @Override
+                    protected SecretKey getKey(
+                            String identityHint, String identity, SSLEngine engine) {
+                        return new SecretKeySpec("Just an arbitrary key".getBytes(UTF_8), "RAW");
+                    }
+                });
+        TestSSLContext c = TestSSLContext.newBuilder()
+                                   .client(testKeyStore)
+                                   .server(testKeyStore)
+                                   .clientProtocol("TLSv1.2")
+                                   .serverProtocol("TLSv1.2")
+                                   .additionalClientKeyManagers(new KeyManager[] {pskKeyManager})
+                                   .additionalServerKeyManagers(new KeyManager[] {pskKeyManager})
+                                   .build();
+
+        // Create a TestSSLContext where the KeyManager returns wrong (randomly generated) private
+        // keys, matching the algorithm and parameters of the correct keys.
+        // I couldn't find a more elegant way to achieve this other than temporarily replacing the
+        // first X509ExtendedKeyManager element of TestKeyStore.keyManagers while invoking
+        // TestSSLContext.create.
+        TestSSLContext cWithWrongPrivateKeys;
+        {
+            // Create a RandomPrivateKeyX509ExtendedKeyManager based on the first
+            // X509ExtendedKeyManager in c.serverKeyManagers.
+            KeyManager randomPrivateKeyX509ExtendedKeyManager = null;
+            for (KeyManager keyManager : c.serverKeyManagers) {
+                if (keyManager instanceof X509ExtendedKeyManager) {
+                    randomPrivateKeyX509ExtendedKeyManager =
+                            new RandomPrivateKeyX509ExtendedKeyManager(
+                                    (X509ExtendedKeyManager) keyManager);
+                    break;
+                }
+            }
+            if (randomPrivateKeyX509ExtendedKeyManager == null) {
+                fail("No X509ExtendedKeyManager in c.serverKeyManagers");
+            }
+
+            // Find the first X509ExtendedKeyManager in testKeyStore.keyManagers
+            int replaceIndex = -1;
+            for (int i = 0; i < testKeyStore.keyManagers.length; i++) {
+                KeyManager keyManager = testKeyStore.keyManagers[i];
+                if (keyManager instanceof X509ExtendedKeyManager) {
+                    replaceIndex = i;
+                    break;
+                }
+            }
+            if (replaceIndex == -1) {
+                fail("No X509ExtendedKeyManager in testKeyStore.keyManagers");
+            }
+
+            // Temporarily substitute the RandomPrivateKeyX509ExtendedKeyManager in place of the
+            // original X509ExtendedKeyManager.
+            KeyManager originalKeyManager = testKeyStore.keyManagers[replaceIndex];
+            testKeyStore.keyManagers[replaceIndex] = randomPrivateKeyX509ExtendedKeyManager;
+            cWithWrongPrivateKeys = TestSSLContext.create(testKeyStore, testKeyStore);
+            testKeyStore.keyManagers[replaceIndex] = originalKeyManager;
+        }
+
+        // To catch all the errors.
+        StringBuilder error = new StringBuilder();
+
+        String[] cipherSuites = c.clientContext.createSSLEngine().getSupportedCipherSuites();
+        for (String cipherSuite : cipherSuites) {
+            try {
+                // Skip cipher suites that are obsoleted.
+                if (StandardNames.IS_RI && "TLSv1.2".equals(c.clientContext.getProtocol())
+                        && StandardNames.CIPHER_SUITES_OBSOLETE_TLS12.contains(cipherSuite)) {
+                    continue;
+                }
+                /*
+                 * Signaling Cipher Suite Values (SCSV) cannot be used on their own, but instead in
+                 * conjunction with other cipher suites.
+                 */
+                if (cipherSuite.equals(StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION)
+                        || cipherSuite.equals(StandardNames.CIPHER_SUITE_FALLBACK)) {
+                    continue;
+                }
+                /*
+                 * This test uses TLS 1.2, and the TLS 1.3 cipher suites aren't customizable
+                 * anyway.
+                 */
+                if (StandardNames.CIPHER_SUITES_TLS13.contains(cipherSuite)) {
+                    continue;
+                }
+
+                final String[] cipherSuiteArray = (secureRenegotiation
+                                ? new String[] {cipherSuite,
+                                          StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION}
+                                : new String[] {cipherSuite});
+
+                // Check that handshake succeeds.
+                TestSSLEnginePair pair = null;
+                try {
+                    pair = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
+                        @Override
+                        void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+                            client.setEnabledCipherSuites(cipherSuiteArray);
+                            server.setEnabledCipherSuites(cipherSuiteArray);
+                        }
+                    });
+                    assertConnected(pair);
+
+                    boolean needsRecordSplit = "TLS".equalsIgnoreCase(c.clientContext.getProtocol())
+                            && cipherSuite.contains("_CBC_");
+
+                    assertSendsCorrectly("This is the client. Hello!".getBytes(UTF_8), pair.client,
+                            pair.server, needsRecordSplit);
+                    assertSendsCorrectly("This is the server. Hi!".getBytes(UTF_8), pair.server,
+                            pair.client, needsRecordSplit);
+                } finally {
+                    if (pair != null) {
+                        pair.close();
+                    }
+                }
+
+                // Check that handshake fails when the server does not possess the private key
+                // corresponding to the server's certificate. This is achieved by using SSLContext
+                // cWithWrongPrivateKeys whose KeyManager returns wrong private keys that match
+                // the algorithm (and parameters) of the correct keys.
+                boolean serverAuthenticatedUsingPublicKey = true;
+                if (cipherSuite.contains("_anon_")) {
+                    serverAuthenticatedUsingPublicKey = false;
+                } else if (cipherSuite.startsWith("TLS_PSK_")
+                        || cipherSuite.startsWith("TLS_ECDHE_PSK_")) {
+                    serverAuthenticatedUsingPublicKey = false;
+                }
+                if (serverAuthenticatedUsingPublicKey) {
+                    TestSSLEnginePair p = null;
+                    try {
+                        p = TestSSLEnginePair.create(
+                                cWithWrongPrivateKeys, new TestSSLEnginePair.Hooks() {
+                                    @Override
+                                    void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+                                        client.setEnabledCipherSuites(cipherSuiteArray);
+                                        server.setEnabledCipherSuites(cipherSuiteArray);
+                                    }
+                                });
+                        assertNotConnected(p);
+                    } catch (IOException expected) {
+                        // Ignored.
+                    } finally {
+                        if (p != null) {
+                            p.close();
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                String message = ("Problem trying to connect cipher suite " + cipherSuite);
+                System.out.println(message);
+                e.printStackTrace();
+                error.append(message);
+                error.append('\n');
+            }
+        }
+        c.close();
+
+        if (error.length() > 0) {
+            throw new Exception("One or more problems in "
+                    + "test_SSLEngine_getSupportedCipherSuites_connect:\n" + error);
+        }
+    }
+
+    private static void assertSendsCorrectly(final byte[] sourceBytes, SSLEngine source,
+            SSLEngine dest, boolean needsRecordSplit) throws SSLException {
+        ByteBuffer sourceOut = ByteBuffer.wrap(sourceBytes);
+        SSLSession sourceSession = source.getSession();
+        ByteBuffer sourceToDest = ByteBuffer.allocate(sourceSession.getPacketBufferSize());
+        SSLEngineResult sourceOutRes = source.wrap(sourceOut, sourceToDest);
+        sourceToDest.flip();
+
+        String sourceCipherSuite = source.getSession().getCipherSuite();
+        assertEquals(sourceCipherSuite, sourceBytes.length, sourceOutRes.bytesConsumed());
+        assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING,
+                sourceOutRes.getHandshakeStatus());
+
+        SSLSession destSession = dest.getSession();
+        ByteBuffer destIn = ByteBuffer.allocate(destSession.getApplicationBufferSize());
+
+        int numUnwrapCalls = 0;
+        while (destIn.position() != sourceOut.limit()) {
+            SSLEngineResult destRes = dest.unwrap(sourceToDest, destIn);
+            assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING,
+                    destRes.getHandshakeStatus());
+            if (needsRecordSplit && numUnwrapCalls == 0) {
+                assertEquals(sourceCipherSuite, 1, destRes.bytesProduced());
+            }
+            numUnwrapCalls++;
+        }
+
+        destIn.flip();
+        byte[] actual = new byte[destIn.remaining()];
+        destIn.get(actual);
+        assertEquals(sourceCipherSuite, Arrays.toString(sourceBytes), Arrays.toString(actual));
+
+        if (needsRecordSplit) {
+            assertEquals(sourceCipherSuite, 2, numUnwrapCalls);
+        } else {
+            assertEquals(sourceCipherSuite, 1, numUnwrapCalls);
+            assertSendsCorrectlyWhenSplit(sourceBytes, source, dest);
+        }
+    }
+
+    private static void assertSendsCorrectlyWhenSplit(final byte[] sourceBytes, SSLEngine source,
+            SSLEngine dest) throws SSLException {
+        // Split the input into three to test the version that accepts ByteBuffer[].  Three
+        // is chosen somewhat arbitrarily as a number larger than the minimum of 2 but small
+        // enough that it's not unwieldy.
+        ByteBuffer[] sourceBufs = new ByteBuffer[3];
+        int sourceLen = sourceBytes.length;
+        sourceBufs[0] = ByteBuffer.wrap(sourceBytes, 0, sourceLen / 3);
+        sourceBufs[1] = ByteBuffer.wrap(sourceBytes, sourceLen / 3, sourceLen / 3);
+        sourceBufs[2] = ByteBuffer.wrap(
+            sourceBytes, 2 * (sourceLen / 3), sourceLen - 2 * (sourceLen / 3));
+        SSLSession sourceSession = source.getSession();
+        ByteBuffer sourceToDest = ByteBuffer.allocate(sourceSession.getPacketBufferSize());
+        SSLEngineResult sourceOutRes = source.wrap(sourceBufs, sourceToDest);
+        sourceToDest.flip();
+        String sourceCipherSuite = source.getSession().getCipherSuite();
+        assertEquals(sourceCipherSuite, sourceBytes.length, sourceOutRes.bytesConsumed());
+        assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING,
+                sourceOutRes.getHandshakeStatus());
+        SSLSession destSession = dest.getSession();
+        ByteBuffer destIn = ByteBuffer.allocate(destSession.getApplicationBufferSize());
+        int numUnwrapCalls = 0;
+        while (destIn.position() != sourceBytes.length) {
+            SSLEngineResult destRes = dest.unwrap(sourceToDest, destIn);
+            assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING,
+                    destRes.getHandshakeStatus());
+            numUnwrapCalls++;
+        }
+        destIn.flip();
+        byte[] actual = new byte[destIn.remaining()];
+        destIn.get(actual);
+        assertEquals(sourceCipherSuite, Arrays.toString(sourceBytes), Arrays.toString(actual));
+        assertEquals(sourceCipherSuite, 3, numUnwrapCalls);
+    }
+
+    @Test
+    public void test_SSLEngine_getEnabledCipherSuites_returnsCopies() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        SSLEngine e = c.clientContext.createSSLEngine();
+        assertNotSame(e.getEnabledCipherSuites(), e.getEnabledCipherSuites());
+        c.close();
+    }
+
+    @Test
+    public void test_SSLEngine_setEnabledCipherSuites_storesCopy() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        SSLEngine e = c.clientContext.createSSLEngine();
+        String[] array = new String[] {e.getEnabledCipherSuites()[0]};
+        String originalFirstElement = array[0];
+        e.setEnabledCipherSuites(array);
+        array[0] = "Modified after having been set";
+        assertEquals(originalFirstElement, e.getEnabledCipherSuites()[0]);
+    }
+
+    @Test
+    public void test_SSLEngine_setEnabledCipherSuites_TLS12() throws Exception {
+        SSLContext context = SSLContext.getInstance("TLSv1.2");
+        context.init(null, null, null);
+        SSLEngine e = context.createSSLEngine();
+
+        try {
+            e.setEnabledCipherSuites(null);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        try {
+            e.setEnabledCipherSuites(new String[1]);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        try {
+            e.setEnabledCipherSuites(new String[] {"Bogus"});
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+
+        e.setEnabledCipherSuites(new String[0]);
+        e.setEnabledCipherSuites(e.getEnabledCipherSuites());
+        e.setEnabledCipherSuites(e.getSupportedCipherSuites());
+
+        // Check that setEnabledCipherSuites affects getEnabledCipherSuites
+        String[] cipherSuites = new String[] {
+                TestUtils.pickArbitraryNonTls13Suite(e.getSupportedCipherSuites())
+        };
+        e.setEnabledCipherSuites(cipherSuites);
+        assertEquals(Arrays.asList(cipherSuites), Arrays.asList(e.getEnabledCipherSuites()));
+    }
+
+    @Test
+    public void test_SSLEngine_setEnabledCipherSuites_TLS13() throws Exception {
+        SSLContext context = SSLContext.getInstance("TLSv1.3");
+        context.init(null, null, null);
+        SSLEngine e = context.createSSLEngine();
+        // The TLS 1.3 cipher suites should be enabled by default
+        assertTrue(new HashSet<String>(Arrays.asList(e.getEnabledCipherSuites()))
+                .containsAll(StandardNames.CIPHER_SUITES_TLS13));
+        // Disabling them should be ignored
+        e.setEnabledCipherSuites(new String[0]);
+        assertTrue(new HashSet<String>(Arrays.asList(e.getEnabledCipherSuites()))
+                .containsAll(StandardNames.CIPHER_SUITES_TLS13));
+
+        e.setEnabledCipherSuites(new String[] {
+                TestUtils.pickArbitraryNonTls13Suite(e.getSupportedCipherSuites())
+        });
+        assertTrue(new HashSet<String>(Arrays.asList(e.getEnabledCipherSuites()))
+                .containsAll(StandardNames.CIPHER_SUITES_TLS13));
+
+        // Disabling TLS 1.3 should disable the 1.3 cipher suites
+        e.setEnabledProtocols(new String[] { "TLSv1.2" });
+        assertFalse(new HashSet<String>(Arrays.asList(e.getEnabledCipherSuites()))
+                .containsAll(StandardNames.CIPHER_SUITES_TLS13));
+    }
+
+    @Test
+    public void test_SSLEngine_getSupportedProtocols_returnsCopies() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        SSLEngine e = c.clientContext.createSSLEngine();
+        assertNotSame(e.getSupportedProtocols(), e.getSupportedProtocols());
+        c.close();
+    }
+
+    @Test
+    public void test_SSLEngine_getEnabledProtocols_returnsCopies() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        SSLEngine e = c.clientContext.createSSLEngine();
+        assertNotSame(e.getEnabledProtocols(), e.getEnabledProtocols());
+        c.close();
+    }
+
+    @Test
+    public void test_SSLEngine_setEnabledProtocols_storesCopy() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        SSLEngine e = c.clientContext.createSSLEngine();
+        String[] array = new String[] {e.getEnabledProtocols()[0]};
+        String originalFirstElement = array[0];
+        e.setEnabledProtocols(array);
+        array[0] = "Modified after having been set";
+        assertEquals(originalFirstElement, e.getEnabledProtocols()[0]);
+    }
+
+    @Test
+    public void test_SSLEngine_setEnabledProtocols() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        SSLEngine e = c.clientContext.createSSLEngine();
+
+        try {
+            e.setEnabledProtocols(null);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        try {
+            e.setEnabledProtocols(new String[1]);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        try {
+            e.setEnabledProtocols(new String[] {"Bogus"});
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        e.setEnabledProtocols(new String[0]);
+        e.setEnabledProtocols(e.getEnabledProtocols());
+        e.setEnabledProtocols(e.getSupportedProtocols());
+
+        // Check that setEnabledProtocols affects getEnabledProtocols
+        for (String protocol : e.getSupportedProtocols()) {
+            if ("SSLv2Hello".equals(protocol)) {
+                try {
+                    e.setEnabledProtocols(new String[] {protocol});
+                    fail("Should fail when SSLv2Hello is set by itself");
+                } catch (IllegalArgumentException expected) {
+                    // Ignored.
+                }
+            } else {
+                String[] protocols = new String[] {protocol};
+                e.setEnabledProtocols(protocols);
+                assertEquals(Arrays.deepToString(protocols),
+                        Arrays.deepToString(e.getEnabledProtocols()));
+            }
+        }
+
+        c.close();
+    }
+
+    @Test
+    public void test_SSLEngine_getSession() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        SSLEngine e = c.clientContext.createSSLEngine();
+        SSLSession session = e.getSession();
+        assertNotNull(session);
+        assertFalse(session.isValid());
+        c.close();
+    }
+
+    // http://b/37078438
+    @Test
+    public void test_SSLEngine_beginHandshake_redundantCalls() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        SSLEngine client = c.clientContext.createSSLEngine(c.host.getHostName(), c.port);
+        client.setUseClientMode(true);
+        client.beginHandshake();
+        client.beginHandshake(); // This call should be ignored
+        c.close();
+    }
+
+    @Test
+    public void test_SSLEngine_getHandshakeSession_duringHandshake_client() throws Exception {
+        // We can't reference the actual context we're using, since we need to pass
+        // the test trust manager in to construct it, so create reference objects that
+        // we can test against.
+        final TestSSLContext referenceContext = TestSSLContext.create();
+        final SSLEngine referenceEngine = referenceContext.clientContext.createSSLEngine();
+
+        final boolean[] wasCalled = new boolean[1];
+        TestSSLContext c =
+                TestSSLContext.newBuilder()
+                        .clientTrustManager(new X509ExtendedTrustManager() {
+                            @Override
+                            public void checkClientTrusted(X509Certificate[] x509Certificates,
+                                    String s, Socket socket) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkServerTrusted(X509Certificate[] x509Certificates,
+                                    String s, Socket socket) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkClientTrusted(X509Certificate[] x509Certificates,
+                                    String s, SSLEngine sslEngine) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkServerTrusted(X509Certificate[] x509Certificates,
+                                    String s, SSLEngine sslEngine) throws CertificateException {
+                                try {
+                                    SSLSession session = sslEngine.getHandshakeSession();
+                                    assertNotNull(session);
+                                    // By the point of the handshake where we're validating
+                                    // certificates, the hostname is known and the cipher suite
+                                    // should be agreed
+                                    assertEquals(referenceContext.host.getHostName(),
+                                            session.getPeerHost());
+                                    String sessionSuite = session.getCipherSuite();
+                                    List<String> enabledSuites =
+                                            Arrays.asList(referenceEngine.getEnabledCipherSuites());
+                                    String message = "Handshake session has invalid cipher suite: "
+                                            + (sessionSuite == null ? "(null)" : sessionSuite);
+                                    assertTrue("Expected enabled suites to contain " + sessionSuite
+                                                    + ", got: " + enabledSuites,
+                                            enabledSuites.contains(sessionSuite));
+                                    wasCalled[0] = true;
+                                } catch (Exception e) {
+                                    throw new CertificateException("Something broke", e);
+                                }
+                            }
+
+                            @Override
+                            public void checkClientTrusted(X509Certificate[] x509Certificates,
+                                    String s) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkServerTrusted(X509Certificate[] x509Certificates,
+                                    String s) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public X509Certificate[] getAcceptedIssuers() {
+                                return new X509Certificate[0];
+                            }
+                        })
+                        .build();
+        TestSSLEnginePair pair = TestSSLEnginePair.create(c);
+        pair.close();
+        assertTrue(wasCalled[0]);
+    }
+
+    @Test
+    public void test_SSLEngine_getHandshakeSession_duringHandshake_server() throws Exception {
+        // We can't reference the actual context we're using, since we need to pass
+        // the test trust manager in to construct it, so create reference objects that
+        // we can test against.
+        final TestSSLContext referenceContext = TestSSLContext.create();
+        final SSLEngine referenceEngine = referenceContext.clientContext.createSSLEngine();
+
+        final boolean[] wasCalled = new boolean[1];
+        TestSSLContext c =
+                TestSSLContext.newBuilder()
+                        .client(TestKeyStore.getClientCertificate())
+                        .serverTrustManager(new X509ExtendedTrustManager() {
+                            @Override
+                            public void checkClientTrusted(X509Certificate[] x509Certificates,
+                                    String s, Socket socket) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkServerTrusted(X509Certificate[] x509Certificates,
+                                    String s, Socket socket) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkClientTrusted(X509Certificate[] x509Certificates,
+                                    String s, SSLEngine sslEngine) throws CertificateException {
+                                try {
+                                    SSLSession session = sslEngine.getHandshakeSession();
+                                    assertNotNull(session);
+                                    // By the point of the handshake where we're validating client
+                                    // certificates, the cipher suite should be agreed and the
+                                    // server's own certificates should have been delivered
+                                    assertEquals(referenceEngine.getEnabledCipherSuites()[0],
+                                            session.getCipherSuite());
+                                    assertNotNull(session.getLocalCertificates());
+                                    assertEquals("CN=localhost",
+                                            ((X509Certificate) session.getLocalCertificates()[0])
+                                                    .getSubjectDN()
+                                                    .getName());
+                                    assertEquals("CN=Test Intermediate Certificate Authority",
+                                            ((X509Certificate) session.getLocalCertificates()[0])
+                                                    .getIssuerDN()
+                                                    .getName());
+                                    wasCalled[0] = true;
+                                } catch (Exception e) {
+                                    throw new CertificateException("Something broke", e);
+                                }
+                            }
+
+                            @Override
+                            public void checkServerTrusted(X509Certificate[] x509Certificates,
+                                    String s, SSLEngine sslEngine) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkClientTrusted(X509Certificate[] x509Certificates,
+                                    String s) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkServerTrusted(X509Certificate[] x509Certificates,
+                                    String s) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public X509Certificate[] getAcceptedIssuers() {
+                                return referenceContext.serverTrustManager.getAcceptedIssuers();
+                            }
+                        })
+                        .build();
+        TestSSLEnginePair pair = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
+            @Override
+            void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+                server.setNeedClientAuth(true);
+            }
+        });
+        pair.close();
+        assertTrue(wasCalled[0]);
+    }
+
+    @Test
+    public void test_SSLEngine_getUseClientMode() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        assertFalse(c.clientContext.createSSLEngine().getUseClientMode());
+        assertFalse(c.clientContext.createSSLEngine(null, -1).getUseClientMode());
+        c.close();
+    }
+
+    @Test
+    public void test_SSLEngine_setUseClientMode() throws Exception {
+        boolean[] finished;
+        TestSSLEnginePair p;
+
+        // client is client, server is server
+        finished = new boolean[2];
+        p = test_SSLEngine_setUseClientMode(true, false, finished);
+        assertConnected(p);
+        assertTrue(finished[0]);
+        assertTrue(finished[1]);
+        p.close();
+
+        // client is server, server is client
+        finished = new boolean[2];
+        p = test_SSLEngine_setUseClientMode(false, true, finished);
+        assertConnected(p);
+        assertTrue(finished[0]);
+        assertTrue(finished[1]);
+        p.close();
+
+        // both are client
+        /*
+         * Our implementation throws an SSLHandshakeException, but RI just
+         * stalls forever
+         */
+        p = null;
+        try {
+            p = test_SSLEngine_setUseClientMode(true, true, null);
+            assertNotConnected(p);
+        } catch (SSLHandshakeException maybeExpected) {
+            // Ignored.
+        } finally {
+            if (p != null) {
+                p.close();
+            }
+        }
+
+        p = test_SSLEngine_setUseClientMode(false, false, null);
+        // both are server
+        assertNotConnected(p);
+        p.close();
+    }
+
+    @Test
+    public void test_SSLEngine_setUseClientMode_afterHandshake() throws Exception {
+        // can't set after handshake
+        TestSSLEnginePair pair = TestSSLEnginePair.create();
+        try {
+            pair.server.setUseClientMode(false);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        try {
+            pair.client.setUseClientMode(false);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        pair.close();
+    }
+
+    private TestSSLEnginePair test_SSLEngine_setUseClientMode(final boolean clientClientMode,
+            final boolean serverClientMode, final boolean[] finished) throws Exception {
+        TestSSLContext c;
+        if (!clientClientMode && serverClientMode) {
+            c = TestSSLContext.create(TestKeyStore.getServer(), TestKeyStore.getClient());
+        } else {
+            c = TestSSLContext.create();
+        }
+
+        return TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
+            @Override
+            void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+                client.setUseClientMode(clientClientMode);
+                server.setUseClientMode(serverClientMode);
+            }
+        }, finished);
+    }
+
+    @Test
+    public void test_SSLEngine_getEnableSessionCreation() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        SSLEngine e = c.clientContext.createSSLEngine();
+        assertTrue(e.getEnableSessionCreation());
+        c.close();
+        TestSSLEnginePair.close(new SSLEngine[] {e});
+    }
+
+    @Test
+    public void test_SSLEngine_setEnableSessionCreation_server() throws Exception {
+        TestSSLEnginePair p = null;
+        try {
+            p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
+                @Override
+                void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+                    server.setEnableSessionCreation(false);
+                }
+            });
+            assertNotConnected(p);
+        } catch (SSLException maybeExpected) {
+            // Ignored.
+        } finally {
+            if (p != null) {
+                p.close();
+            }
+        }
+    }
+
+    @Test
+    public void test_SSLEngine_setEnableSessionCreation_client() throws Exception {
+        TestSSLEnginePair p = null;
+        try {
+            p = TestSSLEnginePair.create(new TestSSLEnginePair.Hooks() {
+                @Override
+                void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+                    client.setEnableSessionCreation(false);
+                }
+            });
+            fail();
+        } catch (SSLException expected) {
+            // Ignored.
+        } finally {
+            if (p != null) {
+                p.close();
+            }
+        }
+    }
+
+    @Test
+    public void test_SSLEngine_getSSLParameters() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        SSLEngine e = c.clientContext.createSSLEngine();
+
+        SSLParameters p = e.getSSLParameters();
+        assertNotNull(p);
+
+        String[] cipherSuites = p.getCipherSuites();
+        assertNotSame(cipherSuites, e.getEnabledCipherSuites());
+        assertEquals(Arrays.asList(cipherSuites), Arrays.asList(e.getEnabledCipherSuites()));
+
+        String[] protocols = p.getProtocols();
+        assertNotSame(protocols, e.getEnabledProtocols());
+        assertEquals(Arrays.asList(protocols), Arrays.asList(e.getEnabledProtocols()));
+
+        assertEquals(p.getWantClientAuth(), e.getWantClientAuth());
+        assertEquals(p.getNeedClientAuth(), e.getNeedClientAuth());
+
+        c.close();
+    }
+
+    @Test
+    public void test_SSLEngine_setSSLParameters() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        SSLEngine e = c.clientContext.createSSLEngine();
+        String[] defaultCipherSuites = e.getEnabledCipherSuites();
+        String[] defaultProtocols = e.getEnabledProtocols();
+        String[] supportedCipherSuites = e.getSupportedCipherSuites();
+        String[] supportedProtocols = e.getSupportedProtocols();
+
+        {
+            SSLParameters p = new SSLParameters();
+            e.setSSLParameters(p);
+            assertEquals(
+                    Arrays.asList(defaultCipherSuites), Arrays.asList(e.getEnabledCipherSuites()));
+            assertEquals(Arrays.asList(defaultProtocols), Arrays.asList(e.getEnabledProtocols()));
+        }
+
+        {
+            SSLParameters p = new SSLParameters(supportedCipherSuites, supportedProtocols);
+            e.setSSLParameters(p);
+            assertEquals(Arrays.asList(supportedCipherSuites),
+                    Arrays.asList(e.getEnabledCipherSuites()));
+            assertEquals(Arrays.asList(supportedProtocols), Arrays.asList(e.getEnabledProtocols()));
+        }
+        {
+            SSLParameters p = new SSLParameters();
+
+            p.setNeedClientAuth(true);
+            assertFalse(e.getNeedClientAuth());
+            assertFalse(e.getWantClientAuth());
+            e.setSSLParameters(p);
+            assertTrue(e.getNeedClientAuth());
+            assertFalse(e.getWantClientAuth());
+
+            p.setWantClientAuth(true);
+            assertTrue(e.getNeedClientAuth());
+            assertFalse(e.getWantClientAuth());
+            e.setSSLParameters(p);
+            assertFalse(e.getNeedClientAuth());
+            assertTrue(e.getWantClientAuth());
+
+            p.setWantClientAuth(false);
+            assertFalse(e.getNeedClientAuth());
+            assertTrue(e.getWantClientAuth());
+            e.setSSLParameters(p);
+            assertFalse(e.getNeedClientAuth());
+            assertFalse(e.getWantClientAuth());
+        }
+        c.close();
+    }
+
+    private void assertConnected(TestSSLEnginePair e) {
+        assertConnected(e.client, e.server);
+    }
+
+    private void assertNotConnected(TestSSLEnginePair e) {
+        assertNotConnected(e.client, e.server);
+    }
+
+    private void assertConnected(SSLEngine a, SSLEngine b) {
+        assertTrue(connected(a, b));
+    }
+
+    private void assertNotConnected(SSLEngine a, SSLEngine b) {
+        assertFalse(connected(a, b));
+    }
+
+    private boolean connected(SSLEngine a, SSLEngine b) {
+        return (a.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING
+                && b.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING
+                && a.getSession() != null && b.getSession() != null && !a.isInboundDone()
+                && !b.isInboundDone() && !a.isOutboundDone() && !b.isOutboundDone());
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLEngineVersionCompatibilityTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLEngineVersionCompatibilityTest.java
new file mode 100644
index 0000000..bad2a27
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLEngineVersionCompatibilityTest.java
@@ -0,0 +1,755 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static com.android.org.conscrypt.TestUtils.UTF_8;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.org.conscrypt.Conscrypt;
+import com.android.org.conscrypt.TestUtils;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import com.android.org.conscrypt.tlswire.TlsTester;
+import com.android.org.conscrypt.tlswire.handshake.AlpnHelloExtension;
+import com.android.org.conscrypt.tlswire.handshake.ClientHello;
+import com.android.org.conscrypt.tlswire.handshake.HandshakeMessage;
+import com.android.org.conscrypt.tlswire.handshake.HelloExtension;
+import com.android.org.conscrypt.tlswire.handshake.ServerNameHelloExtension;
+import com.android.org.conscrypt.tlswire.record.TlsProtocols;
+import com.android.org.conscrypt.tlswire.record.TlsRecord;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.nio.ByteBuffer;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.X509TrustManager;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * Tests for SSLSocket classes that ensure the TLS 1.2 and TLS 1.3 implementations
+ * are compatible.
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(Parameterized.class)
+public class SSLEngineVersionCompatibilityTest {
+
+    @Parameterized.Parameters(name = "{index}: {0} client, {1} server")
+    public static Iterable<Object[]> data() {
+        // We can't support TLS 1.3 without our own trust manager (which requires
+        // X509ExtendedTrustManager), so only test TLS 1.2 if it's not available.
+        if (TestUtils.isClassAvailable("javax.net.ssl.X509ExtendedTrustManager")) {
+            return Arrays.asList(new Object[][] {
+                    { "TLSv1.2", "TLSv1.2" },
+                    { "TLSv1.2", "TLSv1.3" },
+                    { "TLSv1.3", "TLSv1.2" },
+                    { "TLSv1.3", "TLSv1.3" },
+            });
+        } else {
+            return Arrays.asList(new Object[][]{{ "TLSv1.2", "TLSv1.2"}});
+        }
+    }
+
+    private final String clientVersion;
+    private final String serverVersion;
+
+    public SSLEngineVersionCompatibilityTest(String clientVersion, String serverVersion) {
+        this.clientVersion = clientVersion;
+        this.serverVersion = serverVersion;
+    }
+
+    private static void assertSendsCorrectly(final byte[] sourceBytes, SSLEngine source,
+            SSLEngine dest, boolean needsRecordSplit) throws SSLException {
+        ByteBuffer sourceOut = ByteBuffer.wrap(sourceBytes);
+        SSLSession sourceSession = source.getSession();
+        ByteBuffer sourceToDest = ByteBuffer.allocate(sourceSession.getPacketBufferSize());
+        SSLEngineResult sourceOutRes = source.wrap(sourceOut, sourceToDest);
+        sourceToDest.flip();
+
+        String sourceCipherSuite = source.getSession().getCipherSuite();
+        assertEquals(sourceCipherSuite, sourceBytes.length, sourceOutRes.bytesConsumed());
+        assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING,
+                sourceOutRes.getHandshakeStatus());
+
+        SSLSession destSession = dest.getSession();
+        ByteBuffer destIn = ByteBuffer.allocate(destSession.getApplicationBufferSize());
+
+        int numUnwrapCalls = 0;
+        while (destIn.position() != sourceOut.limit()) {
+            SSLEngineResult destRes = dest.unwrap(sourceToDest, destIn);
+            assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING,
+                    destRes.getHandshakeStatus());
+            if (needsRecordSplit && numUnwrapCalls == 0) {
+                assertEquals(sourceCipherSuite, 1, destRes.bytesProduced());
+            }
+            numUnwrapCalls++;
+        }
+
+        destIn.flip();
+        byte[] actual = new byte[destIn.remaining()];
+        destIn.get(actual);
+        assertEquals(sourceCipherSuite, Arrays.toString(sourceBytes), Arrays.toString(actual));
+
+        if (needsRecordSplit) {
+            assertEquals(sourceCipherSuite, 2, numUnwrapCalls);
+        } else {
+            assertEquals(sourceCipherSuite, 1, numUnwrapCalls);
+            assertSendsCorrectlyWhenSplit(sourceBytes, source, dest);
+        }
+    }
+
+    private static void assertSendsCorrectlyWhenSplit(final byte[] sourceBytes, SSLEngine source,
+            SSLEngine dest) throws SSLException {
+        // Split the input into three to test the version that accepts ByteBuffer[].  Three
+        // is chosen somewhat arbitrarily as a number larger than the minimum of 2 but small
+        // enough that it's not unwieldy.
+        ByteBuffer[] sourceBufs = new ByteBuffer[3];
+        int sourceLen = sourceBytes.length;
+        sourceBufs[0] = ByteBuffer.wrap(sourceBytes, 0, sourceLen / 3);
+        sourceBufs[1] = ByteBuffer.wrap(sourceBytes, sourceLen / 3, sourceLen / 3);
+        sourceBufs[2] = ByteBuffer.wrap(
+            sourceBytes, 2 * (sourceLen / 3), sourceLen - 2 * (sourceLen / 3));
+        SSLSession sourceSession = source.getSession();
+        ByteBuffer sourceToDest = ByteBuffer.allocate(sourceSession.getPacketBufferSize());
+        SSLEngineResult sourceOutRes = source.wrap(sourceBufs, sourceToDest);
+        sourceToDest.flip();
+        String sourceCipherSuite = source.getSession().getCipherSuite();
+        assertEquals(sourceCipherSuite, sourceBytes.length, sourceOutRes.bytesConsumed());
+        assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING,
+                sourceOutRes.getHandshakeStatus());
+        SSLSession destSession = dest.getSession();
+        ByteBuffer destIn = ByteBuffer.allocate(destSession.getApplicationBufferSize());
+        int numUnwrapCalls = 0;
+        while (destIn.position() != sourceBytes.length) {
+            SSLEngineResult destRes = dest.unwrap(sourceToDest, destIn);
+            assertEquals(sourceCipherSuite, HandshakeStatus.NOT_HANDSHAKING,
+                    destRes.getHandshakeStatus());
+            numUnwrapCalls++;
+        }
+        destIn.flip();
+        byte[] actual = new byte[destIn.remaining()];
+        destIn.get(actual);
+        assertEquals(sourceCipherSuite, Arrays.toString(sourceBytes), Arrays.toString(actual));
+        assertEquals(sourceCipherSuite, 3, numUnwrapCalls);
+    }
+
+    @Test
+    public void test_SSLEngine_beginHandshake() throws Exception {
+        TestSSLContext c = TestSSLContext.newBuilder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion).build();
+
+        try {
+            c.clientContext.createSSLEngine().beginHandshake();
+            fail();
+        } catch (IllegalStateException expected) {
+            // Ignored.
+        }
+        c.close();
+
+        TestSSLEnginePair p = TestSSLEnginePair.create();
+        assertConnected(p);
+        p.close();
+    }
+
+    @Test
+    public void test_SSLEngine_beginHandshake_noKeyStore() throws Exception {
+        SSLContext clientContext = SSLContext.getInstance(clientVersion);
+        clientContext.init(null, null, null);
+        SSLContext serverContext = SSLContext.getInstance(serverVersion);
+        serverContext.init(null, null, null);
+        TestSSLContext c = TestSSLContext.newBuilder()
+                .useDefaults(false)
+                .clientContext(clientContext)
+                .serverContext(serverContext).build();
+        SSLEngine[] p = null;
+        try {
+            // TODO Fix KnownFailure AlertException "NO SERVER CERTIFICATE FOUND"
+            // ServerHandshakeImpl.selectSuite should not select a suite without a required cert
+            p = TestSSLEnginePair.connect(c, null);
+            fail();
+        } catch (SSLHandshakeException expected) {
+            // Ignored.
+        } finally {
+            if (p != null) {
+                TestSSLEnginePair.close(p);
+            }
+        }
+        c.close();
+    }
+
+    @Test
+    public void test_SSLEngine_beginHandshake_noClientCertificate() throws Exception {
+        TestSSLContext c = TestSSLContext.newBuilder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion).build();
+        SSLEngine[] engines = TestSSLEnginePair.connect(c, null);
+        assertConnected(engines[0], engines[1]);
+        c.close();
+        TestSSLEnginePair.close(engines);
+    }
+
+    @Test
+    public void test_SSLEngine_clientAuth() throws Exception {
+        TestSSLContext c = TestSSLContext.newBuilder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion).build();
+        SSLEngine e = c.clientContext.createSSLEngine();
+
+        assertFalse(e.getWantClientAuth());
+        assertFalse(e.getNeedClientAuth());
+
+        // confirm turning one on by itself
+        e.setWantClientAuth(true);
+        assertTrue(e.getWantClientAuth());
+        assertFalse(e.getNeedClientAuth());
+
+        // confirm turning setting on toggles the other
+        e.setNeedClientAuth(true);
+        assertFalse(e.getWantClientAuth());
+        assertTrue(e.getNeedClientAuth());
+
+        // confirm toggling back
+        e.setWantClientAuth(true);
+        assertTrue(e.getWantClientAuth());
+        assertFalse(e.getNeedClientAuth());
+
+        // TODO Fix KnownFailure "init - invalid private key"
+        TestSSLContext clientAuthContext = new TestSSLContext.Builder()
+                .client(TestKeyStore.getClientCertificate())
+                .server(TestKeyStore.getServer())
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion).build();
+        TestSSLEnginePair p =
+                TestSSLEnginePair.create(clientAuthContext, new TestSSLEnginePair.Hooks() {
+                    @Override
+                    void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+                        server.setWantClientAuth(true);
+                    }
+                });
+        assertConnected(p);
+        assertNotNull(p.client.getSession().getLocalCertificates());
+        TestKeyStore.assertChainLength(p.client.getSession().getLocalCertificates());
+        TestSSLContext.assertClientCertificateChain(
+                clientAuthContext.clientTrustManager, p.client.getSession().getLocalCertificates());
+        clientAuthContext.close();
+        c.close();
+        p.close();
+    }
+
+    /**
+     * http://code.google.com/p/android/issues/detail?id=31903
+     * This test case directly tests the fix for the issue.
+     */
+    @Test
+    public void test_SSLEngine_clientAuthWantedNoClientCert() throws Exception {
+        TestSSLContext clientAuthContext = new TestSSLContext.Builder()
+                .client(TestKeyStore.getClient())
+                .server(TestKeyStore.getServer())
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion).build();
+        TestSSLEnginePair p =
+                TestSSLEnginePair.create(clientAuthContext, new TestSSLEnginePair.Hooks() {
+                    @Override
+                    void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+                        server.setWantClientAuth(true);
+                    }
+                });
+        assertConnected(p);
+        clientAuthContext.close();
+        p.close();
+    }
+
+    /**
+     * http://code.google.com/p/android/issues/detail?id=31903
+     * This test case verifies that if the server requires a client cert
+     * (setNeedClientAuth) but the client does not provide one SSL connection
+     * establishment will fail
+     */
+    @Test
+    public void test_SSLEngine_clientAuthNeededNoClientCert() throws Exception {
+        TestSSLContext clientAuthContext = new TestSSLContext.Builder()
+                .client(TestKeyStore.getClient())
+                .server(TestKeyStore.getServer())
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion).build();
+        TestSSLEnginePair p = null;
+        try {
+            p = TestSSLEnginePair.create(clientAuthContext, new TestSSLEnginePair.Hooks() {
+                @Override
+                void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+                    server.setNeedClientAuth(true);
+                }
+            });
+            fail();
+        } catch (SSLException expected) {
+            // Ignored.
+        } finally {
+            clientAuthContext.close();
+            if (p != null) {
+                p.close();
+            }
+        }
+    }
+
+    @Test
+    public void test_SSLEngine_endpointVerification_Success() throws Exception {
+        TestUtils.assumeSetEndpointIdentificationAlgorithmAvailable();
+        // The default hostname verifier on OpenJDK just rejects all hostnames,
+        // which is not helpful, so replace with a basic functional one.
+        HostnameVerifier oldDefault = HttpsURLConnection.getDefaultHostnameVerifier();
+        HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier());
+        try {
+            TestSSLContext c = TestSSLContext.newBuilder()
+                    .clientProtocol(clientVersion)
+                    .serverProtocol(serverVersion).build();
+            TestSSLEnginePair p = TestSSLEnginePair.create(c, new TestSSLEnginePair.Hooks() {
+                @Override
+                void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+                    SSLParameters p = client.getSSLParameters();
+                    p.setEndpointIdentificationAlgorithm("HTTPS");
+                    client.setSSLParameters(p);
+                }
+            });
+            assertConnected(p);
+            c.close();
+        } finally {
+            HttpsURLConnection.setDefaultHostnameVerifier(oldDefault);
+        }
+    }
+
+    @Test
+    public void test_TestSSLEnginePair_create() throws Exception {
+        TestSSLEnginePair test = TestSSLEnginePair.create(
+                TestSSLContext.newBuilder()
+                        .clientProtocol(clientVersion)
+                        .serverProtocol(serverVersion).build());
+        assertNotNull(test.c);
+        assertNotNull(test.server);
+        assertNotNull(test.client);
+        assertConnected(test);
+        test.close();
+    }
+
+    private final int NUM_STRESS_ITERATIONS = 1000;
+
+    @Test
+    public void test_SSLEngine_Multiple_Thread_Success() throws Exception {
+        final TestSSLEnginePair pair = TestSSLEnginePair.create(
+                TestSSLContext.newBuilder()
+                        .clientProtocol(clientVersion)
+                        .serverProtocol(serverVersion).build());
+        try {
+            assertConnected(pair);
+
+            final CountDownLatch startUpSync = new CountDownLatch(2);
+            ExecutorService executor = Executors.newFixedThreadPool(2);
+            Future<Void> client = executor.submit(new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    startUpSync.countDown();
+
+                    for (int i = 0; i < NUM_STRESS_ITERATIONS; i++) {
+                        assertSendsCorrectly("This is the client. Hello!".getBytes(UTF_8),
+                                pair.client, pair.server, false);
+                    }
+
+                    return null;
+                }
+            });
+            Future<Void> server = executor.submit(new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    startUpSync.countDown();
+
+                    for (int i = 0; i < NUM_STRESS_ITERATIONS; i++) {
+                        assertSendsCorrectly("This is the server. Hi!".getBytes(UTF_8), pair.server,
+                                pair.client, false);
+                    }
+
+                    return null;
+                }
+            });
+            executor.shutdown();
+            client.get();
+            server.get();
+        } finally {
+            pair.close();
+        }
+    }
+
+    @Test
+    public void test_SSLEngine_CloseOutbound() throws Exception {
+        final TestSSLEnginePair pair = TestSSLEnginePair.create(
+                TestSSLContext.newBuilder()
+                        .clientProtocol(clientVersion)
+                        .serverProtocol(serverVersion).build());
+        try {
+            assertConnected(pair);
+
+            // Closing the outbound direction should cause a close_notify to be sent
+            pair.client.closeOutbound();
+            ByteBuffer clientOut = ByteBuffer
+                    .allocate(pair.client.getSession().getPacketBufferSize());
+            SSLEngineResult res = pair.client.wrap(ByteBuffer.wrap(new byte[0]), clientOut);
+            assertEquals(Status.CLOSED, res.getStatus());
+            assertEquals(HandshakeStatus.NOT_HANDSHAKING, res.getHandshakeStatus());
+            assertTrue(res.bytesProduced() > 0);
+
+            // Read the close_notify in the server
+            clientOut.flip();
+            ByteBuffer serverIn = ByteBuffer
+                    .allocate(pair.server.getSession().getApplicationBufferSize());
+            res = pair.server.unwrap(clientOut, serverIn);
+            assertEquals(Status.CLOSED, res.getStatus());
+            assertEquals(HandshakeStatus.NEED_WRAP, res.getHandshakeStatus());
+
+            // Reading the close_notify should cause a close_notify to be sent back
+            ByteBuffer serverOut = ByteBuffer
+                    .allocate(pair.server.getSession().getPacketBufferSize());
+            res = pair.server.wrap(ByteBuffer.wrap(new byte[0]), serverOut);
+            assertEquals(Status.CLOSED, res.getStatus());
+            assertEquals(HandshakeStatus.NOT_HANDSHAKING, res.getHandshakeStatus());
+            assertTrue(res.bytesProduced() > 0);
+
+            // Read the close_notify in the client
+            serverOut.flip();
+            ByteBuffer clientIn = ByteBuffer
+                    .allocate(pair.client.getSession().getApplicationBufferSize());
+            res = pair.client.unwrap(serverOut, clientIn);
+            assertEquals(Status.CLOSED, res.getStatus());
+            assertEquals(HandshakeStatus.NOT_HANDSHAKING, res.getHandshakeStatus());
+
+            // Both sides have received close_notify messages, so both peers should have
+            // registered that they're finished
+            assertTrue(pair.client.isInboundDone() && pair.client.isOutboundDone());
+            assertTrue(pair.server.isInboundDone() && pair.server.isOutboundDone());
+        } finally {
+            pair.close();
+        }
+    }
+
+    @Test
+    public void test_SSLEngine_Closed() throws Exception {
+        final TestSSLEnginePair pair = TestSSLEnginePair.create(
+                TestSSLContext.newBuilder()
+                        .clientProtocol(clientVersion)
+                        .serverProtocol(serverVersion).build());
+        pair.close();
+        ByteBuffer out = ByteBuffer.allocate(pair.client.getSession().getPacketBufferSize());
+        SSLEngineResult res = pair.client.wrap(ByteBuffer.wrap(new byte[] { 0x01 }), out);
+        assertEquals(Status.CLOSED, res.getStatus());
+        // The engine should have a close_notify alert pending, so it should ignore the
+        // proffered data and push the alert into out
+        assertEquals(0, res.bytesConsumed());
+        assertNotEquals(0, res.bytesProduced());
+
+        res = pair.client.unwrap(ByteBuffer.wrap(new byte[] { 0x01} ), out);
+        assertEquals(Status.CLOSED, res.getStatus());
+        assertEquals(0, res.bytesConsumed());
+        assertEquals(0, res.bytesProduced());
+    }
+
+    @Test
+    public void test_SSLEngine_ClientHello_record_size() throws Exception {
+        // This test checks the size of ClientHello of the default SSLEngine. TLS/SSL handshakes
+        // with older/unpatched F5/BIG-IP appliances are known to stall and time out when
+        // the fragment containing ClientHello is between 256 and 511 (inclusive) bytes long.
+        SSLContext context = SSLContext.getInstance(clientVersion);
+        context.init(null, null, null);
+        SSLEngine e = context.createSSLEngine();
+        e.setUseClientMode(true);
+
+        // Enable SNI extension on the engine (this is typically enabled by default)
+        // to increase the size of ClientHello.
+        Conscrypt.setHostname(e, "sslenginetest.androidcts.google.com");
+
+        // Enable Session Tickets extension on the engine (this is typically enabled
+        // by default) to increase the size of ClientHello.
+        Conscrypt.setUseSessionTickets(e, true);
+
+        TlsRecord firstReceivedTlsRecord = TlsTester.parseRecord(getFirstChunk(e));
+
+        assertEquals("TLS record type", TlsProtocols.HANDSHAKE, firstReceivedTlsRecord.type);
+        HandshakeMessage handshakeMessage = HandshakeMessage.read(
+                new DataInputStream(new ByteArrayInputStream(firstReceivedTlsRecord.fragment)));
+        assertEquals(
+                "HandshakeMessage type", HandshakeMessage.TYPE_CLIENT_HELLO, handshakeMessage.type);
+
+        int fragmentLength = firstReceivedTlsRecord.fragment.length;
+        if ((fragmentLength >= 256) && (fragmentLength <= 511)) {
+            fail("Fragment containing ClientHello is of dangerous length: " + fragmentLength
+                    + " bytes");
+        }
+    }
+
+    @Test
+    public void test_SSLEngine_ClientHello_SNI() throws Exception {
+        SSLContext context = SSLContext.getInstance(clientVersion);
+        context.init(null, null, null);
+        SSLEngine e = context.createSSLEngine();
+        e.setUseClientMode(true);
+
+        Conscrypt.setHostname(e, "sslenginetest.androidcts.google.com");
+
+        ClientHello clientHello = TlsTester.parseClientHello(getFirstChunk(e));
+        ServerNameHelloExtension sniExtension =
+                (ServerNameHelloExtension) clientHello.findExtensionByType(
+                        HelloExtension.TYPE_SERVER_NAME);
+
+        assertNotNull(sniExtension);
+        assertEquals(Arrays.asList("sslenginetest.androidcts.google.com"), sniExtension.hostnames);
+    }
+
+    @Test
+    public void test_SSLEngine_ClientHello_ALPN() throws Exception {
+        String[] protocolList = new String[] { "h2", "http/1.1" };
+
+        SSLContext context = SSLContext.getInstance(clientVersion);
+        context.init(null, null, null);
+        SSLEngine e = context.createSSLEngine();
+        e.setUseClientMode(true);
+
+        Conscrypt.setApplicationProtocols(e, protocolList);
+
+        ClientHello clientHello = TlsTester.parseClientHello(getFirstChunk(e));
+        AlpnHelloExtension alpnExtension =
+                (AlpnHelloExtension) clientHello.findExtensionByType(
+                        HelloExtension.TYPE_APPLICATION_LAYER_PROTOCOL_NEGOTIATION);
+        assertNotNull(alpnExtension);
+        assertEquals(Arrays.asList(protocolList), alpnExtension.protocols);
+    }
+
+    private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
+
+    private static byte[] getFirstChunk(SSLEngine e) throws SSLException {
+        ByteBuffer out = ByteBuffer.allocate(64 * 1024);
+
+        e.wrap(EMPTY_BUFFER, out);
+        out.flip();
+        byte[] data = new byte[out.limit()];
+        out.get(data);
+
+        return data;
+    }
+
+    @Test
+    public void test_SSLEngine_TlsUnique() throws Exception {
+        // tls_unique isn't supported in TLS 1.3
+        assumeTlsV1_2Connection();
+        TestSSLEnginePair pair = TestSSLEnginePair.create(
+                TestSSLContext.newBuilder()
+                        .clientProtocol(clientVersion)
+                        .serverProtocol(serverVersion).build(),
+                new TestSSLEnginePair.Hooks() {
+                    @Override
+                    void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+                        assertNull(Conscrypt.getTlsUnique(client));
+                        assertNull(Conscrypt.getTlsUnique(server));
+                    }
+                });
+        try {
+            assertConnected(pair);
+
+            byte[] clientTlsUnique = Conscrypt.getTlsUnique(pair.client);
+            byte[] serverTlsUnique = Conscrypt.getTlsUnique(pair.server);
+            assertNotNull(clientTlsUnique);
+            assertNotNull(serverTlsUnique);
+            assertArrayEquals(clientTlsUnique, serverTlsUnique);
+        } finally {
+            pair.close();
+        }
+    }
+
+    @Test
+    public void test_SSLEngine_EKM() throws Exception {
+        TestSSLEnginePair pair = TestSSLEnginePair.create(
+                TestSSLContext.newBuilder()
+                        .clientProtocol(clientVersion)
+                        .serverProtocol(serverVersion).build(),
+                new TestSSLEnginePair.Hooks() {
+                    @Override
+                    void beforeBeginHandshake(SSLEngine client, SSLEngine server) {
+                        try {
+                            assertNull(Conscrypt.exportKeyingMaterial(client, "FOO", null, 20));
+                            assertNull(Conscrypt.exportKeyingMaterial(server, "FOO", null, 20));
+                        } catch (SSLException e) {
+                            throw new RuntimeException(e);
+                        }
+                    }
+                });
+        try {
+            assertConnected(pair);
+
+            byte[] clientEkm = Conscrypt.exportKeyingMaterial(pair.client, "FOO", null, 20);
+            byte[] serverEkm = Conscrypt.exportKeyingMaterial(pair.server, "FOO", null, 20);
+            assertNotNull(clientEkm);
+            assertNotNull(serverEkm);
+            assertEquals(20, clientEkm.length);
+            assertEquals(20, serverEkm.length);
+            assertArrayEquals(clientEkm, serverEkm);
+
+            byte[] clientContextEkm = Conscrypt.exportKeyingMaterial(
+                    pair.client, "FOO", new byte[0], 20);
+            byte[] serverContextEkm = Conscrypt.exportKeyingMaterial(
+                    pair.server, "FOO", new byte[0], 20);
+            assertNotNull(clientContextEkm);
+            assertNotNull(serverContextEkm);
+            assertEquals(20, clientContextEkm.length);
+            assertEquals(20, serverContextEkm.length);
+            assertArrayEquals(clientContextEkm, serverContextEkm);
+
+            // In TLS 1.2, an empty context and a null context are different (RFC 5705, section 4),
+            // but in TLS 1.3 they are the same (RFC 8446, section 7.5).
+            if ("TLSv1.2".equals(negotiatedVersion())) {
+                assertFalse(Arrays.equals(clientEkm, clientContextEkm));
+            } else {
+                assertTrue(Arrays.equals(clientEkm, clientContextEkm));
+            }
+        } finally {
+            pair.close();
+        }
+    }
+
+    // Test whether an exception thrown from within the TrustManager properly flows immediately
+    // to the caller and doesn't get caught and held by the SSLEngine.  This was previously
+    // the behavior of Conscrypt, see https://github.com/google/conscrypt/issues/577.
+    @Test
+    public void test_SSLEngine_Exception() throws Exception {
+        final TestSSLContext referenceContext = TestSSLContext.create();
+        class ThrowingTrustManager implements X509TrustManager {
+            public boolean threw = false;
+            @Override
+            public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
+                    throws CertificateException {}
+            @Override
+            public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
+                    throws CertificateException {
+                threw = true;
+                throw new CertificateException("Nope!");
+            }
+            @Override
+            public X509Certificate[] getAcceptedIssuers() {
+                return referenceContext.clientTrustManager.getAcceptedIssuers();
+            }
+        }
+        ThrowingTrustManager trustManager = new ThrowingTrustManager();
+        final TestSSLContext c = TestSSLContext.newBuilder()
+                                         .clientProtocol(clientVersion)
+                                         .serverProtocol(serverVersion)
+                                         .clientTrustManager(trustManager)
+                                         .build();
+
+        // The following code is taken from TestSSLEnginePair.connect()
+        SSLSession session = c.clientContext.createSSLEngine().getSession();
+
+        int packetBufferSize = session.getPacketBufferSize();
+        ByteBuffer clientToServer = ByteBuffer.allocate(packetBufferSize);
+        ByteBuffer serverToClient = ByteBuffer.allocate(packetBufferSize);
+
+        int applicationBufferSize = session.getApplicationBufferSize();
+        ByteBuffer scratch = ByteBuffer.allocate(applicationBufferSize);
+
+        SSLEngine client = c.clientContext.createSSLEngine(c.host.getHostName(), c.port);
+        SSLEngine server = c.serverContext.createSSLEngine();
+        client.setUseClientMode(true);
+        server.setUseClientMode(false);
+        client.beginHandshake();
+        server.beginHandshake();
+
+        try {
+            while (true) {
+                boolean clientDone = client.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING;
+                boolean serverDone = server.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING;
+                if (clientDone && serverDone) {
+                    break;
+                }
+
+                boolean progress = TestSSLEnginePair.handshakeStep(
+                        client, clientToServer, serverToClient, scratch, new boolean[1]);
+                progress |= TestSSLEnginePair.handshakeStep(
+                        server, serverToClient, clientToServer, scratch, new boolean[1]);
+                assertFalse(trustManager.threw);
+                if (!progress) {
+                    break;
+                }
+            }
+            fail();
+        } catch (SSLHandshakeException expected) {
+            assertTrue(expected.getCause() instanceof CertificateException);
+        }
+        assertTrue(trustManager.threw);
+    }
+
+    private void assertConnected(TestSSLEnginePair e) {
+        assertConnected(e.client, e.server);
+    }
+
+    private void assertConnected(SSLEngine a, SSLEngine b) {
+        assertTrue(connected(a, b));
+    }
+
+    private boolean connected(SSLEngine a, SSLEngine b) {
+        return (a.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING
+                && b.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING
+                && a.getSession() != null && b.getSession() != null && !a.isInboundDone()
+                && !b.isInboundDone() && !a.isOutboundDone() && !b.isOutboundDone());
+    }
+
+    // Assumes that the negotiated connection will be TLS 1.2
+    private void assumeTlsV1_2Connection() {
+        assumeTrue("TLSv1.2".equals(negotiatedVersion()));
+    }
+
+    /**
+     * Returns the version that a connection between {@code clientVersion} and
+     * {@code serverVersion} should produce.
+     */
+    private String negotiatedVersion() {
+        if (clientVersion.equals("TLSv1.3") && serverVersion.equals("TLSv1.3")) {
+            return "TLSv1.3";
+        } else {
+            return "TLSv1.2";
+        }
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLParametersTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLParametersTest.java
new file mode 100644
index 0000000..93cb054
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLParametersTest.java
@@ -0,0 +1,241 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIMatcher;
+import javax.net.ssl.SNIServerName;
+import javax.net.ssl.SSLParameters;
+import com.android.org.conscrypt.TestUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class SSLParametersTest {
+    @Test
+    public void test_SSLParameters_emptyConstructor() {
+        SSLParameters p = new SSLParameters();
+        assertNull(p.getCipherSuites());
+        assertNull(p.getProtocols());
+        assertFalse(p.getWantClientAuth());
+        assertFalse(p.getNeedClientAuth());
+    }
+
+    @Test
+    public void test_SSLParameters_cipherSuitesConstructor() {
+        String[] cipherSuites = new String[] {"foo", null, "bar"};
+        SSLParameters p = new SSLParameters(cipherSuites);
+        assertNotNull(p.getCipherSuites());
+        assertNotSame(cipherSuites, p.getCipherSuites());
+        assertEquals(Arrays.asList(cipherSuites), Arrays.asList(p.getCipherSuites()));
+        assertNull(p.getProtocols());
+        assertFalse(p.getWantClientAuth());
+        assertFalse(p.getNeedClientAuth());
+    }
+
+    @Test
+    public void test_SSLParameters_cpherSuitesProtocolsConstructor() {
+        String[] cipherSuites = new String[] {"foo", null, "bar"};
+        String[] protocols = new String[] {"baz", null, "qux"};
+        SSLParameters p = new SSLParameters(cipherSuites, protocols);
+        assertNotNull(p.getCipherSuites());
+        assertNotNull(p.getProtocols());
+        assertNotSame(cipherSuites, p.getCipherSuites());
+        assertNotSame(protocols, p.getProtocols());
+        assertEquals(Arrays.asList(cipherSuites), Arrays.asList(p.getCipherSuites()));
+        assertEquals(Arrays.asList(protocols), Arrays.asList(p.getProtocols()));
+        assertFalse(p.getWantClientAuth());
+        assertFalse(p.getNeedClientAuth());
+    }
+
+    @Test
+    public void test_SSLParameters_CipherSuites() {
+        SSLParameters p = new SSLParameters();
+        assertNull(p.getCipherSuites());
+
+        // confirm clone on input
+        String[] cipherSuites = new String[] {"fnord"};
+        String[] copy = cipherSuites.clone();
+        p.setCipherSuites(copy);
+        copy[0] = null;
+        assertEquals(Arrays.asList(cipherSuites), Arrays.asList(p.getCipherSuites()));
+
+        // confirm clone on output
+        assertNotSame(p.getCipherSuites(), p.getCipherSuites());
+    }
+
+    @Test
+    public void test_SSLParameters_Protocols() {
+        SSLParameters p = new SSLParameters();
+        assertNull(p.getProtocols());
+
+        // confirm clone on input
+        String[] protocols = new String[] {"fnord"};
+        String[] copy = protocols.clone();
+        p.setProtocols(copy);
+        copy[0] = null;
+        assertEquals(Arrays.asList(protocols), Arrays.asList(p.getProtocols()));
+
+        // confirm clone on output
+        assertNotSame(p.getProtocols(), p.getProtocols());
+    }
+
+    @Test
+    public void test_SSLParameters_ClientAuth() {
+        SSLParameters p = new SSLParameters();
+        assertFalse(p.getWantClientAuth());
+        assertFalse(p.getNeedClientAuth());
+
+        // confirm turning one on by itself
+        p.setWantClientAuth(true);
+        assertTrue(p.getWantClientAuth());
+        assertFalse(p.getNeedClientAuth());
+
+        // confirm turning setting on toggles the other
+        p.setNeedClientAuth(true);
+        assertFalse(p.getWantClientAuth());
+        assertTrue(p.getNeedClientAuth());
+
+        // confirm toggling back
+        p.setWantClientAuth(true);
+        assertTrue(p.getWantClientAuth());
+        assertFalse(p.getNeedClientAuth());
+    }
+
+    @Test
+    public void test_SSLParameters_setServerNames_duplicatedNameThrows() throws Exception {
+        TestUtils.assumeSNIHostnameAvailable();
+
+        SSLParameters p = new SSLParameters();
+        ArrayList<SNIServerName> dupeNames = new ArrayList<SNIServerName>();
+        dupeNames.add(new SNIHostName("www.example.com"));
+        dupeNames.add(new SNIHostName("www.example.com"));
+        try {
+            p.setServerNames(dupeNames);
+            fail("Should throw IllegalArgumentException when names are duplicated");
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+    }
+
+    @Test
+    public void test_SSLParameters_setServerNames_setNull_getNull() throws Exception {
+        TestUtils.assumeSNIHostnameAvailable();
+        SSLParameters p = new SSLParameters();
+        p.setServerNames(
+                Collections.singletonList((SNIServerName) new SNIHostName("www.example.com")));
+        assertNotNull(p.getServerNames());
+        p.setServerNames(null);
+        assertNull(p.getServerNames());
+    }
+
+    @Test
+    public void test_SSLParameters_setServerNames_setEmpty_getEmpty() throws Exception {
+        TestUtils.assumeSNIHostnameAvailable();
+        SSLParameters p = new SSLParameters();
+        p.setServerNames(new ArrayList<SNIServerName>());
+        Collection<SNIServerName> actual = p.getServerNames();
+        assertNotNull(actual);
+        assertEquals(0, actual.size());
+    }
+
+    @Test
+    public void test_SSLParameters_getServerNames_unmodifiable() throws Exception {
+        TestUtils.assumeSNIHostnameAvailable();
+        SSLParameters p = new SSLParameters();
+        p.setServerNames(
+                Collections.singletonList((SNIServerName) new SNIHostName("www.example.com")));
+        Collection<SNIServerName> actual = p.getServerNames();
+        try {
+            actual.add(new SNIHostName("www.foo.com"));
+            fail("Should not allow modifications to the list");
+        } catch (UnsupportedOperationException expected) {
+            // Ignored.
+        }
+    }
+
+    @Test
+    public void test_SSLParameters_setSNIMatchers_duplicatedNameThrows() throws Exception {
+        TestUtils.assumeSNIHostnameAvailable();
+        SSLParameters p = new SSLParameters();
+        ArrayList<SNIMatcher> dupeMatchers = new ArrayList<SNIMatcher>();
+        dupeMatchers.add(SNIHostName.createSNIMatcher("www\\.example\\.com"));
+        dupeMatchers.add(SNIHostName.createSNIMatcher("www\\.example\\.com"));
+        try {
+            p.setSNIMatchers(dupeMatchers);
+            fail("Should throw IllegalArgumentException when matchers are duplicated");
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+    }
+
+    @Test
+    public void test_SSLParameters_setSNIMatchers_setNull_getNull() throws Exception {
+        TestUtils.assumeSNIHostnameAvailable();
+        SSLParameters p = new SSLParameters();
+        p.setSNIMatchers(
+                Collections.singletonList(SNIHostName.createSNIMatcher("www\\.example\\.com")));
+        assertNotNull(p.getSNIMatchers());
+        p.setSNIMatchers(null);
+        assertNull(p.getSNIMatchers());
+    }
+
+    @Test
+    public void test_SSLParameters_setSNIMatchers_setEmpty_getEmpty() throws Exception {
+        TestUtils.assumeSNIHostnameAvailable();
+        SSLParameters p = new SSLParameters();
+        p.setSNIMatchers(
+                Collections.singletonList(SNIHostName.createSNIMatcher("www\\.example\\.com")));
+        assertEquals(1, p.getSNIMatchers().size());
+        p.setSNIMatchers(Collections.<SNIMatcher>emptyList());
+        Collection<SNIMatcher> actual = p.getSNIMatchers();
+        assertNotNull(actual);
+        assertEquals(0, actual.size());
+    }
+
+    @Test
+    public void test_SSLParameters_getSNIMatchers_unmodifiable() throws Exception {
+        TestUtils.assumeSNIHostnameAvailable();
+        SSLParameters p = new SSLParameters();
+        p.setSNIMatchers(
+                Collections.singletonList(SNIHostName.createSNIMatcher("www\\.example\\.com")));
+        Collection<SNIMatcher> actual = p.getSNIMatchers();
+        try {
+            actual.add(SNIHostName.createSNIMatcher("www\\.google\\.com"));
+            fail("Should not allow modification of list");
+        } catch (UnsupportedOperationException expected) {
+            // Ignored.
+        }
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLServerSocketFactoryTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLServerSocketFactoryTest.java
new file mode 100644
index 0000000..debf19c
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLServerSocketFactoryTest.java
@@ -0,0 +1,36 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import javax.net.ssl.SSLServerSocketFactory;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class SSLServerSocketFactoryTest {
+
+    @Test
+    public void testDefaultConfiguration() throws Exception {
+        SSLConfigurationAsserts.assertSSLServerSocketFactoryDefaultConfiguration(
+                (SSLServerSocketFactory) SSLServerSocketFactory.getDefault());
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLServerSocketTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLServerSocketTest.java
new file mode 100644
index 0000000..7ad349e
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLServerSocketTest.java
@@ -0,0 +1,130 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import com.android.org.conscrypt.TestUtils;
+import com.android.org.conscrypt.java.security.StandardNames;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class SSLServerSocketTest {
+
+    @Test
+    public void testDefaultConfiguration() throws Exception {
+        SSLConfigurationAsserts.assertSSLServerSocketDefaultConfiguration(
+                (SSLServerSocket) SSLServerSocketFactory.getDefault().createServerSocket());
+    }
+
+    @Test
+    public void testSetEnabledCipherSuitesAffectsGetter_TLS12() throws Exception {
+        SSLContext context = SSLContext.getInstance("TLSv1.2");
+        context.init(null, null, null);
+        SSLServerSocket socket =
+                (SSLServerSocket) context.getServerSocketFactory().createServerSocket();
+        String[] cipherSuites = new String[] {
+                TestUtils.pickArbitraryNonTls13Suite(socket.getSupportedCipherSuites())
+        };
+        socket.setEnabledCipherSuites(cipherSuites);
+        assertEquals(Arrays.asList(cipherSuites), Arrays.asList(socket.getEnabledCipherSuites()));
+    }
+
+    @Test
+    public void testSetEnabledCipherSuitesAffectsGetter_TLS13() throws Exception {
+        SSLServerSocket socket =
+                (SSLServerSocket) SSLServerSocketFactory.getDefault().createServerSocket();
+        String[] cipherSuites = new String[] {
+                TestUtils.pickArbitraryNonTls13Suite(socket.getSupportedCipherSuites())};
+        socket.setEnabledCipherSuites(cipherSuites);
+        List<String> expected = new ArrayList<String>(StandardNames.CIPHER_SUITES_TLS13);
+        expected.addAll(Arrays.asList(cipherSuites));
+        assertEquals(expected, Arrays.asList(socket.getEnabledCipherSuites()));
+    }
+
+    @Test
+    public void testSetEnabledCipherSuitesStoresCopy() throws Exception {
+        SSLServerSocket socket =
+                (SSLServerSocket) SSLServerSocketFactory.getDefault().createServerSocket();
+        String[] array = new String[] {socket.getEnabledCipherSuites()[0]};
+        String originalFirstElement = array[0];
+        socket.setEnabledCipherSuites(array);
+        array[0] = "Modified after having been set";
+        assertEquals(originalFirstElement, socket.getEnabledCipherSuites()[0]);
+    }
+
+    @Test
+    public void testSetEnabledProtocolsAffectsGetter() throws Exception {
+        SSLServerSocket socket =
+                (SSLServerSocket) SSLServerSocketFactory.getDefault().createServerSocket();
+        String[] protocols = new String[] {socket.getSupportedProtocols()[0]};
+        socket.setEnabledProtocols(protocols);
+        assertEquals(Arrays.asList(protocols), Arrays.asList(socket.getEnabledProtocols()));
+    }
+
+    @Test
+    public void testSetEnabledProtocolsStoresCopy() throws Exception {
+        SSLServerSocket socket =
+                (SSLServerSocket) SSLServerSocketFactory.getDefault().createServerSocket();
+        String[] array = new String[] {socket.getEnabledProtocols()[0]};
+        String originalFirstElement = array[0];
+        socket.setEnabledProtocols(array);
+        array[0] = "Modified after having been set";
+        assertEquals(originalFirstElement, socket.getEnabledProtocols()[0]);
+    }
+
+    @Test
+    public void test_SSLSocket_setEnabledCipherSuites_TLS13() throws Exception {
+        SSLContext context = SSLContext.getInstance("TLSv1.3");
+        context.init(null, null, null);
+        SSLServerSocketFactory sf = context.getServerSocketFactory();
+        SSLServerSocket ssl = (SSLServerSocket) sf.createServerSocket();
+        // The TLS 1.3 cipher suites should be enabled by default
+        assertTrue(new HashSet<String>(Arrays.asList(ssl.getEnabledCipherSuites()))
+                .containsAll(StandardNames.CIPHER_SUITES_TLS13));
+        // Disabling them should be ignored
+        ssl.setEnabledCipherSuites(new String[0]);
+        assertTrue(new HashSet<String>(Arrays.asList(ssl.getEnabledCipherSuites()))
+                .containsAll(StandardNames.CIPHER_SUITES_TLS13));
+
+        ssl.setEnabledCipherSuites(new String[] {
+                TestUtils.pickArbitraryNonTls13Suite(ssl.getSupportedCipherSuites())
+        });
+        assertTrue(new HashSet<String>(Arrays.asList(ssl.getEnabledCipherSuites()))
+                .containsAll(StandardNames.CIPHER_SUITES_TLS13));
+
+        // Disabling TLS 1.3 should disable 1.3 cipher suites
+        ssl.setEnabledProtocols(new String[] { "TLSv1.2" });
+        assertFalse(new HashSet<String>(Arrays.asList(ssl.getEnabledCipherSuites()))
+                .containsAll(StandardNames.CIPHER_SUITES_TLS13));
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSessionContextTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSessionContextTest.java
new file mode 100644
index 0000000..4f51dd6
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSessionContextTest.java
@@ -0,0 +1,419 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static com.android.org.conscrypt.Conscrypt.isConscrypt;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.SSLSocket;
+import com.android.org.conscrypt.TestUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(Parameterized.class)
+public class SSLSessionContextTest {
+    @Parameterized.Parameters(name = "{0}")
+    public static Iterable<?> data() {
+        // We can't support TLS 1.3 without our own trust manager (which requires
+        // X509ExtendedTrustManager), so only test TLS 1.2 if it's not available.
+        if (TestUtils.isClassAvailable("javax.net.ssl.X509ExtendedTrustManager")) {
+            return Arrays.asList("TLSv1.2", "TLSv1.3");
+        } else {
+            return Arrays.asList("TLSv1.2");
+        }
+    }
+
+    private final String protocol;
+
+    public SSLSessionContextTest(String protocol) {
+        this.protocol = protocol;
+    }
+
+    private TestSSLContext newTestContext() {
+        return TestSSLContext.newBuilder()
+                .clientProtocol(protocol)
+                .serverProtocol(protocol)
+                .build();
+    }
+
+    private boolean isTls13() {
+        return "TLSv1.3".equals(protocol);
+    }
+
+    @Test
+    public void test_SSLSessionContext_getIds() {
+        TestSSLContext c = newTestContext();
+        assertSSLSessionContextSize(0, c);
+        c.close();
+
+        TestSSLSocketPair s = TestSSLSocketPair.create(newTestContext()).connect();
+        if (isTls13()) {
+            assertSSLSessionContextSizeAtLeast(1, s.c);
+        } else {
+            assertSSLSessionContextSize(1, s.c);
+        }
+        Enumeration<byte[]> clientIds = s.c.clientContext.getClientSessionContext().getIds();
+        Enumeration<byte[]> serverIds = s.c.serverContext.getServerSessionContext().getIds();
+        byte[] clientId = clientIds.nextElement();
+        assertEquals(32, clientId.length);
+        if (TestSSLContext.sslServerSocketSupportsSessionTickets()) {
+            assertFalse(serverIds.hasMoreElements());
+        } else {
+            byte[] serverId = serverIds.nextElement();
+            assertEquals(32, serverId.length);
+            assertTrue(Arrays.equals(clientId, serverId));
+        }
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSessionContext_getSession() {
+        TestSSLContext c = newTestContext();
+        try {
+            c.clientContext.getClientSessionContext().getSession(null);
+            fail();
+        } catch (NullPointerException expected) {
+            // Ignored.
+        }
+        assertNull(c.clientContext.getClientSessionContext().getSession(new byte[0]));
+        assertNull(c.clientContext.getClientSessionContext().getSession(new byte[1]));
+        try {
+            c.serverContext.getServerSessionContext().getSession(null);
+            fail();
+        } catch (NullPointerException expected) {
+            // Ignored.
+        }
+        assertNull(c.serverContext.getServerSessionContext().getSession(new byte[0]));
+        assertNull(c.serverContext.getServerSessionContext().getSession(new byte[1]));
+        c.close();
+
+        TestSSLSocketPair s = TestSSLSocketPair.create(newTestContext()).connect();
+        SSLSessionContext client = s.c.clientContext.getClientSessionContext();
+        SSLSessionContext server = s.c.serverContext.getServerSessionContext();
+        byte[] clientId = client.getIds().nextElement();
+        assertNotNull(client.getSession(clientId));
+        assertTrue(Arrays.equals(clientId, client.getSession(clientId).getId()));
+        if (TestSSLContext.sslServerSocketSupportsSessionTickets()) {
+            assertFalse(server.getIds().hasMoreElements());
+        } else {
+            byte[] serverId = server.getIds().nextElement();
+            assertNotNull(server.getSession(serverId));
+            assertTrue(Arrays.equals(serverId, server.getSession(serverId).getId()));
+        }
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSessionContext_getSessionCacheSize() {
+        TestSSLContext c = newTestContext();
+        int expectedClientSessionCacheSize = expectedClientSslSessionCacheSize(c);
+        int expectedServerSessionCacheSize = expectedServerSslSessionCacheSize(c);
+        assertEquals(expectedClientSessionCacheSize,
+                c.clientContext.getClientSessionContext().getSessionCacheSize());
+        assertEquals(expectedServerSessionCacheSize,
+                c.serverContext.getServerSessionContext().getSessionCacheSize());
+        c.close();
+
+        TestSSLSocketPair s = TestSSLSocketPair.create(newTestContext()).connect();
+        assertEquals(expectedClientSessionCacheSize,
+                s.c.clientContext.getClientSessionContext().getSessionCacheSize());
+        assertEquals(expectedServerSessionCacheSize,
+                s.c.serverContext.getServerSessionContext().getSessionCacheSize());
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSessionContext_setSessionCacheSize_noConnect() {
+        TestSSLContext c = newTestContext();
+        int expectedClientSessionCacheSize = expectedClientSslSessionCacheSize(c);
+        int expectedServerSessionCacheSize = expectedServerSslSessionCacheSize(c);
+        assertNoConnectSetSessionCacheSizeBehavior(
+                expectedClientSessionCacheSize, c.clientContext.getClientSessionContext());
+        assertNoConnectSetSessionCacheSizeBehavior(
+                expectedServerSessionCacheSize, c.serverContext.getServerSessionContext());
+        c.close();
+    }
+
+    private static void assertNoConnectSetSessionCacheSizeBehavior(
+            int expectedDefault, SSLSessionContext s) {
+        try {
+            s.setSessionCacheSize(-1);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        assertEquals(expectedDefault, s.getSessionCacheSize());
+        s.setSessionCacheSize(1);
+        assertEquals(1, s.getSessionCacheSize());
+    }
+
+    @Test
+    public void test_SSLSessionContext_setSessionCacheSize_oneConnect() {
+        TestSSLSocketPair s = TestSSLSocketPair.create(newTestContext()).connect();
+        int expectedClientSessionCacheSize = expectedClientSslSessionCacheSize(s.c);
+        int expectedServerSessionCacheSize = expectedServerSslSessionCacheSize(s.c);
+        SSLSessionContext client = s.c.clientContext.getClientSessionContext();
+        SSLSessionContext server = s.c.serverContext.getServerSessionContext();
+        assertEquals(expectedClientSessionCacheSize, client.getSessionCacheSize());
+        assertEquals(expectedServerSessionCacheSize, server.getSessionCacheSize());
+        if (isTls13()) {
+            assertSSLSessionContextSizeAtLeast(1, s.c);
+        } else {
+            assertSSLSessionContextSize(1, s.c);
+        }
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSessionContext_setSessionCacheSize_dynamic() throws Exception {
+        TestSSLContext c = newTestContext();
+        SSLSessionContext client = c.clientContext.getClientSessionContext();
+        SSLSessionContext server = c.serverContext.getServerSessionContext();
+
+        String[] supportedCipherSuites = c.serverSocket.getSupportedCipherSuites();
+        c.serverSocket.setEnabledCipherSuites(supportedCipherSuites);
+        Deque<String> uniqueCipherSuites =
+                new ArrayDeque<String>(Arrays.asList(supportedCipherSuites));
+        // only use RSA cipher suites which will work with our TrustProvider
+        Iterator<String> i = uniqueCipherSuites.iterator();
+        while (i.hasNext()) {
+            String cipherSuite = i.next();
+
+            // Certificate key length too long for export ciphers
+            if (cipherSuite.startsWith("SSL_RSA_EXPORT_")) {
+                i.remove();
+                continue;
+            }
+
+            if (cipherSuite.startsWith("SSL_RSA_")) {
+                continue;
+            }
+            if (cipherSuite.startsWith("TLS_RSA_")) {
+                continue;
+            }
+            if (cipherSuite.startsWith("TLS_DHE_RSA_")) {
+                continue;
+            }
+            if (cipherSuite.startsWith("SSL_DHE_RSA_")) {
+                continue;
+            }
+            i.remove();
+        }
+
+        /*
+         * having more than 3 uniqueCipherSuites is a test
+         * requirement, not a requirement of the interface or
+         * implementation. It simply allows us to make sure that we
+         * will not get a cached session ID since we'll have to
+         * renegotiate a new session due to the new cipher suite
+         * requirement. even this test only really needs three if it
+         * reused the unique cipher suites every time it resets the
+         * session cache.
+         */
+        assertTrue(uniqueCipherSuites.size() >= 3);
+        String cipherSuite1 = uniqueCipherSuites.pop();
+        String cipherSuite2 = uniqueCipherSuites.pop();
+        String cipherSuite3 = uniqueCipherSuites.pop();
+
+        List<SSLSocket[]> toClose = new ArrayList<SSLSocket[]>();
+        toClose.add(
+                TestSSLSocketPair.create(c).connect(new String[] {cipherSuite1}, null).sockets());
+        if (isTls13()) {
+            assertSSLSessionContextSizeAtLeast(1, c);
+        } else {
+            assertSSLSessionContextSize(1, c);
+        }
+        toClose.add(
+                TestSSLSocketPair.create(c).connect(new String[] {cipherSuite2}, null).sockets());
+        if (isTls13()) {
+            assertSSLSessionContextSizeAtLeast(2, c);
+        } else {
+            assertSSLSessionContextSize(2, c);
+        }
+        toClose.add(
+                TestSSLSocketPair.create(c).connect(new String[] {cipherSuite3}, null).sockets());
+        if (isTls13()) {
+            assertSSLSessionContextSizeAtLeast(3, c);
+        } else {
+            assertSSLSessionContextSize(3, c);
+        }
+
+        client.setSessionCacheSize(1);
+        server.setSessionCacheSize(1);
+        assertEquals(1, client.getSessionCacheSize());
+        assertEquals(1, server.getSessionCacheSize());
+        assertSSLSessionContextSize(1, c);
+        toClose.add(
+                TestSSLSocketPair.create(c).connect(new String[] {cipherSuite1}, null).sockets());
+        assertSSLSessionContextSize(1, c);
+
+        client.setSessionCacheSize(2);
+        server.setSessionCacheSize(2);
+        toClose.add(
+                TestSSLSocketPair.create(c).connect(new String[] {cipherSuite2}, null).sockets());
+        assertSSLSessionContextSize(2, c);
+        toClose.add(
+                TestSSLSocketPair.create(c).connect(new String[] {cipherSuite3}, null).sockets());
+        assertSSLSessionContextSize(2, c);
+
+        for (SSLSocket[] pair : toClose) {
+            for (SSLSocket s : pair) {
+                s.close();
+            }
+        }
+        c.close();
+    }
+
+    @Test
+    public void test_SSLSessionContext_getSessionTimeout() {
+        TestSSLContext c = newTestContext();
+        int expectedCacheTimeout = expectedSslSessionCacheTimeout(c);
+        assertEquals(expectedCacheTimeout,
+                c.clientContext.getClientSessionContext().getSessionTimeout());
+        assertEquals(expectedCacheTimeout,
+                c.serverContext.getServerSessionContext().getSessionTimeout());
+        c.close();
+
+        TestSSLSocketPair s = TestSSLSocketPair.create(newTestContext()).connect();
+        assertEquals(expectedCacheTimeout,
+                s.c.clientContext.getClientSessionContext().getSessionTimeout());
+        assertEquals(expectedCacheTimeout,
+                s.c.serverContext.getServerSessionContext().getSessionTimeout());
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSessionContext_setSessionTimeout() throws Exception {
+        TestSSLContext c = newTestContext();
+        int expectedCacheTimeout = expectedSslSessionCacheTimeout(c);
+        assertEquals(expectedCacheTimeout,
+                c.clientContext.getClientSessionContext().getSessionTimeout());
+        assertEquals(expectedCacheTimeout,
+                c.serverContext.getServerSessionContext().getSessionTimeout());
+        c.clientContext.getClientSessionContext().setSessionTimeout(0);
+        c.serverContext.getServerSessionContext().setSessionTimeout(0);
+        assertEquals(0, c.clientContext.getClientSessionContext().getSessionTimeout());
+        assertEquals(0, c.serverContext.getServerSessionContext().getSessionTimeout());
+
+        try {
+            c.clientContext.getClientSessionContext().setSessionTimeout(-1);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        try {
+            c.serverContext.getServerSessionContext().setSessionTimeout(-1);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        c.close();
+
+        TestSSLSocketPair s = TestSSLSocketPair.create(newTestContext()).connect();
+        if (isTls13()) {
+            assertSSLSessionContextSizeAtLeast(1, s.c);
+        } else {
+            assertSSLSessionContextSize(1, s.c);
+        }
+        Thread.sleep(1000);
+        s.c.clientContext.getClientSessionContext().setSessionTimeout(1);
+        s.c.serverContext.getServerSessionContext().setSessionTimeout(1);
+        assertSSLSessionContextSize(0, s.c);
+        s.close();
+    }
+
+    private static void assertSSLSessionContextSize(int expected, TestSSLContext c) {
+        assertSSLSessionContextSize(expected, c.clientContext.getClientSessionContext(),
+                c.serverContext.getServerSessionContext());
+        assertSSLSessionContextSize(0, c.serverContext.getClientSessionContext(),
+                c.clientContext.getServerSessionContext());
+    }
+
+    private static void assertSSLSessionContextSize(
+            int expected, SSLSessionContext client, SSLSessionContext server) {
+        assertSSLSessionContextSize(expected, client, false);
+        assertSSLSessionContextSize(expected, server, true);
+    }
+
+    private static void assertSSLSessionContextSize(
+            int expected, SSLSessionContext s, boolean server) {
+        if (server && TestSSLContext.sslServerSocketSupportsSessionTickets()) {
+            assertEquals(0, numSessions(s));
+        } else {
+            assertEquals(expected, numSessions(s));
+        }
+    }
+
+    private static void assertSSLSessionContextSizeAtLeast(int expected, TestSSLContext c) {
+        assertSSLSessionContextSizeAtLeast(expected, c.clientContext.getClientSessionContext(),
+                c.serverContext.getServerSessionContext());
+        assertSSLSessionContextSizeAtLeast(0, c.serverContext.getClientSessionContext(),
+                c.clientContext.getServerSessionContext());
+    }
+
+    private static void assertSSLSessionContextSizeAtLeast(
+            int expected, SSLSessionContext client, SSLSessionContext server) {
+        assertSSLSessionContextSizeAtLeast(expected, client, false);
+        assertSSLSessionContextSizeAtLeast(expected, server, true);
+    }
+
+    private static void assertSSLSessionContextSizeAtLeast(
+            int expected, SSLSessionContext s, boolean server) {
+        if (server && TestSSLContext.sslServerSocketSupportsSessionTickets()) {
+            assertEquals(0, numSessions(s));
+        } else {
+            assertTrue("numSessions: " + numSessions(s) + ", expected at least: " + expected,
+                    numSessions(s) >= expected);
+        }
+    }
+
+    private int expectedClientSslSessionCacheSize(TestSSLContext c) {
+        return isConscrypt(c.clientContext.getProvider()) ? 10 : 0;
+    }
+
+    private int expectedServerSslSessionCacheSize(TestSSLContext c) {
+        return isConscrypt(c.serverContext.getProvider()) ? 100 : 0;
+    }
+
+    private int expectedSslSessionCacheTimeout(TestSSLContext c) {
+        return isConscrypt(c.serverContext.getProvider()) ? 8 * 3600 : 24 * 3600;
+    }
+
+    private static int numSessions(SSLSessionContext s) {
+        return Collections.list(s.getIds()).size();
+    }
+
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSessionTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSessionTest.java
new file mode 100644
index 0000000..4fe8310
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSessionTest.java
@@ -0,0 +1,537 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionBindingEvent;
+import javax.net.ssl.SSLSessionBindingListener;
+import javax.net.ssl.SSLSocket;
+import com.android.org.conscrypt.TestUtils;
+import com.android.org.conscrypt.java.security.StandardNames;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class SSLSessionTest {
+    @Test
+    public void test_SSLSocket_TestSSLSessions_create() {
+        TestSSLSessions s = TestSSLSessions.create();
+        assertNotNull(s.invalid);
+        assertFalse(s.invalid.isValid());
+        assertTrue(s.server.isValid());
+        assertTrue(s.client.isValid());
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getApplicationBufferSize() {
+        TestSSLSessions s = TestSSLSessions.create();
+        assertTrue(s.invalid.getApplicationBufferSize() > 0);
+        assertTrue(s.server.getApplicationBufferSize() > 0);
+        assertTrue(s.client.getApplicationBufferSize() > 0);
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getCipherSuite() {
+        TestSSLSessions s = TestSSLSessions.create();
+        assertNotNull(s.invalid.getCipherSuite());
+        assertEquals(StandardNames.CIPHER_SUITE_INVALID, s.invalid.getCipherSuite());
+        assertNotNull(s.server.getCipherSuite());
+        assertNotNull(s.client.getCipherSuite());
+        assertEquals(s.server.getCipherSuite(), s.client.getCipherSuite());
+        StandardNames.assertValidCipherSuites(new String[] {s.server.getCipherSuite()});
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getCreationTime() {
+        // We use OpenSSL, which only returns times accurate to the nearest second.
+        // NativeCrypto just multiplies by 1000, which looks like truncation, which
+        // would make it appear as if the OpenSSL side of things was created before
+        // we called it.
+        long t0 = System.currentTimeMillis() / 1000;
+        TestSSLSessions s = TestSSLSessions.create();
+        long t1 = System.currentTimeMillis() / 1000;
+
+        assertTrue(s.invalid.getCreationTime() > 0);
+
+        long sTime = s.server.getCreationTime() / 1000;
+        assertTrue(sTime + " >= " + t0, sTime >= t0);
+        assertTrue(sTime + " <= " + t1, sTime <= t1);
+
+        long cTime = s.client.getCreationTime() / 1000;
+        assertTrue(cTime + " >= " + t0, cTime >= t0);
+        assertTrue(cTime + " <= " + t1, cTime <= t1);
+
+        s.close();
+    }
+
+    // TLS 1.2 and TLS 1.3 sessions are philosophically different: In TLS 1.3, sessions for
+    // resumption are sent outside the handshake, are generally single-use, and there can be
+    // multiple cached at any time, whereas in TLS 1.2 the session caching info is sent as part of
+    // the handshake and effectively enhances the current session.  So in TLS 1.3, the current
+    // session has no ID (and cannot be resumed), whereas in TLS 1.2, the current session gets
+    // an ID and can be used for session resumption.
+
+    @Test
+    public void test_SSLSession_getId_TLS12() {
+        TestSSLSessions s = TestSSLSessions.create(TestSSLContext.newBuilder()
+                                                           .clientProtocol("TLSv1.2")
+                                                           .serverProtocol("TLSv1.2")
+                                                           .build());
+        assertNotNull(s.invalid.getId());
+        assertNotNull(s.server.getId());
+        assertNotNull(s.client.getId());
+        assertEquals(0, s.invalid.getId().length);
+        if (TestSSLContext.sslServerSocketSupportsSessionTickets()) {
+            assertEquals(0, s.server.getId().length);
+        } else {
+            assertEquals(32, s.server.getId().length);
+            assertTrue(Arrays.equals(s.server.getId(), s.client.getId()));
+        }
+        assertEquals(32, s.client.getId().length);
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getId_TLS13() {
+        TestSSLSessions s = TestSSLSessions.create(TestSSLContext.newBuilder()
+                                                           .clientProtocol("TLSv1.3")
+                                                           .serverProtocol("TLSv1.3")
+                                                           .build());
+        assertNotNull(s.invalid.getId());
+        assertNotNull(s.server.getId());
+        assertNotNull(s.client.getId());
+        assertEquals(0, s.invalid.getId().length);
+        assertEquals(0, s.server.getId().length);
+        assertEquals(0, s.client.getId().length);
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getLastAccessedTime() {
+        TestSSLSessions s = TestSSLSessions.create();
+        assertTrue(s.invalid.getLastAccessedTime() > 0);
+        assertTrue(s.server.getLastAccessedTime() > 0);
+        assertTrue(s.client.getLastAccessedTime() > 0);
+        assertTrue("s.server.getLastAccessedTime()=" + s.server.getLastAccessedTime() + " "
+                        + "s.client.getLastAccessedTime()=" + s.client.getLastAccessedTime(),
+                Math.abs(s.server.getLastAccessedTime() - s.client.getLastAccessedTime())
+                        <= 1000);
+        assertTrue(s.server.getLastAccessedTime() >= s.server.getCreationTime());
+        assertTrue(s.client.getLastAccessedTime() >= s.client.getCreationTime());
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getLocalCertificates() throws Exception {
+        TestSSLSessions s = TestSSLSessions.create();
+        assertNull(s.invalid.getLocalCertificates());
+        assertNull(s.client.getLocalCertificates());
+        assertNotNull(s.server.getLocalCertificates());
+        TestKeyStore.assertChainLength(s.server.getLocalCertificates());
+        TestSSLContext.assertServerCertificateChain(
+                s.s.c.serverTrustManager, s.server.getLocalCertificates());
+        TestSSLContext.assertCertificateInKeyStore(
+                s.server.getLocalCertificates()[0], s.s.c.serverKeyStore);
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getLocalPrincipal() throws Exception {
+        TestSSLSessions s = TestSSLSessions.create();
+        assertNull(s.invalid.getLocalPrincipal());
+        assertNull(s.client.getLocalPrincipal());
+        assertNotNull(s.server.getLocalPrincipal());
+        assertNotNull(s.server.getLocalPrincipal().getName());
+        TestSSLContext.assertCertificateInKeyStore(
+                s.server.getLocalPrincipal(), s.s.c.serverKeyStore);
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getPacketBufferSize() {
+        TestSSLSessions s = TestSSLSessions.create();
+        assertTrue(s.invalid.getPacketBufferSize() > 0);
+        assertTrue(s.server.getPacketBufferSize() > 0);
+        assertTrue(s.client.getPacketBufferSize() > 0);
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getPeerCertificateChain() throws Exception {
+        TestSSLSessions s = TestSSLSessions.create();
+        try {
+            s.invalid.getPeerCertificateChain();
+            fail();
+        } catch (SSLPeerUnverifiedException expected) {
+            // Ignored.
+        }
+        assertNotNull(s.client.getPeerCertificates());
+        TestKeyStore.assertChainLength(s.client.getPeerCertificateChain());
+        try {
+            assertNull(s.server.getPeerCertificateChain());
+            fail();
+        } catch (SSLPeerUnverifiedException expected) {
+            // Ignored.
+        }
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getPeerCertificates() throws Exception {
+        TestSSLSessions s = TestSSLSessions.create();
+        try {
+            s.invalid.getPeerCertificates();
+            fail();
+        } catch (SSLPeerUnverifiedException expected) {
+            // Ignored.
+        }
+        assertNotNull(s.client.getPeerCertificates());
+        TestKeyStore.assertChainLength(s.client.getPeerCertificates());
+        TestSSLContext.assertServerCertificateChain(
+                s.s.c.serverTrustManager, s.client.getPeerCertificates());
+        TestSSLContext.assertCertificateInKeyStore(
+                s.client.getPeerCertificates()[0], s.s.c.serverKeyStore);
+        try {
+            s.server.getPeerCertificates();
+            fail();
+        } catch (SSLPeerUnverifiedException expected) {
+            // Ignored.
+        }
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getPeerCertificates_resumption() throws Exception {
+        TestSSLContext context = TestSSLContext.create();
+
+        // Ensure that the session is reused to the best of our ability
+        context.clientContext.getClientSessionContext().setSessionTimeout(1000000);
+        context.clientContext.getClientSessionContext().setSessionCacheSize(0);
+
+        SSLSocket client = (SSLSocket) context.clientContext.getSocketFactory().createSocket(
+                context.host, context.port);
+        SSLSocket server = (SSLSocket) context.serverSocket.accept();
+        connect(client, server);
+
+        assertNotNull(client.getSession().getPeerCertificates());
+
+        client.close();
+        server.close();
+
+        client = (SSLSocket) context.clientContext.getSocketFactory().createSocket(
+                context.host, context.port);
+        server = (SSLSocket) context.serverSocket.accept();
+        connect(client, server);
+
+        assertNotNull(client.getSession().getPeerCertificates());
+
+        client.close();
+        server.close();
+        context.close();
+    }
+
+    private static void connect(final SSLSocket client, final SSLSocket server)
+            throws InterruptedException, ExecutionException {
+        ExecutorService executor = Executors.newFixedThreadPool(2);
+        Future<Void> s = executor.submit(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                server.startHandshake();
+                return null;
+            }
+        });
+        Future<Void> c = executor.submit(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                client.startHandshake();
+                return null;
+            }
+        });
+        executor.shutdown();
+        s.get();
+        c.get();
+    }
+
+    @Test
+    public void test_SSLSession_getPeerHost() {
+        TestSSLSessions s = TestSSLSessions.create();
+        assertNull(s.invalid.getPeerHost());
+        assertNotNull(s.server.getPeerHost());
+        assertNotNull(s.client.getPeerHost());
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getPeerPort() {
+        TestSSLSessions s = TestSSLSessions.create();
+        assertEquals(-1, s.invalid.getPeerPort());
+        assertTrue(s.server.getPeerPort() > 0);
+        assertEquals(s.s.c.port, s.client.getPeerPort());
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getPeerPrincipal() throws Exception {
+        TestSSLSessions s = TestSSLSessions.create();
+        try {
+            s.invalid.getPeerPrincipal();
+            fail();
+        } catch (SSLPeerUnverifiedException expected) {
+            // Ignored.
+        }
+        try {
+            s.server.getPeerPrincipal();
+            fail();
+        } catch (SSLPeerUnverifiedException expected) {
+            // Ignored.
+        }
+        assertNotNull(s.client.getPeerPrincipal());
+        assertNotNull(s.client.getPeerPrincipal().getName());
+        TestSSLContext.assertCertificateInKeyStore(
+                s.client.getPeerPrincipal(), s.s.c.serverKeyStore);
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getProtocol() {
+        TestSSLSessions s = TestSSLSessions.create();
+        assertNotNull(s.invalid.getProtocol());
+        assertEquals("NONE", s.invalid.getProtocol());
+        assertNotNull(s.server.getProtocol());
+        assertNotNull(s.client.getProtocol());
+        assertEquals(s.server.getProtocol(), s.client.getProtocol());
+        assertTrue(StandardNames.SSL_SOCKET_PROTOCOLS.contains(s.server.getProtocol()));
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getSessionContext() {
+        TestSSLSessions s = TestSSLSessions.create();
+        assertNull(s.invalid.getSessionContext());
+        assertNotNull(s.server.getSessionContext());
+        assertNotNull(s.client.getSessionContext());
+        assertEquals(s.s.c.serverContext.getServerSessionContext(), s.server.getSessionContext());
+        assertEquals(s.s.c.clientContext.getClientSessionContext(), s.client.getSessionContext());
+        assertNotSame(s.server.getSessionContext(), s.client.getSessionContext());
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getValue() {
+        TestSSLSessions s = TestSSLSessions.create();
+        try {
+            s.invalid.getValue(null);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        assertNull(s.invalid.getValue("BOGUS"));
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_getValueNames() {
+        TestSSLSessions s = TestSSLSessions.create();
+        assertNotNull(s.invalid.getValueNames());
+        assertEquals(0, s.invalid.getValueNames().length);
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_invalidate() {
+        TestSSLSessions s = TestSSLSessions.create();
+
+        assertFalse(s.invalid.isValid());
+        s.invalid.invalidate();
+        assertFalse(s.invalid.isValid());
+        assertNull(s.invalid.getSessionContext());
+
+        assertTrue(s.server.isValid());
+        s.server.invalidate();
+        assertFalse(s.server.isValid());
+        assertNull(s.server.getSessionContext());
+
+        assertTrue(s.client.isValid());
+        s.client.invalidate();
+        assertFalse(s.client.isValid());
+        assertNull(s.client.getSessionContext());
+
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_isValid() {
+        TestSSLSessions s = TestSSLSessions.create();
+        assertFalse(s.invalid.isValid());
+        assertTrue(s.server.isValid());
+        assertTrue(s.client.isValid());
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_putValue() {
+        TestSSLSessions s = TestSSLSessions.create();
+        String key = "KEY";
+        String value = "VALUE";
+        assertNull(s.invalid.getValue(key));
+        assertEquals(0, s.invalid.getValueNames().length);
+        s.invalid.putValue(key, value);
+        assertSame(value, s.invalid.getValue(key));
+        assertEquals(1, s.invalid.getValueNames().length);
+        assertEquals(key, s.invalid.getValueNames()[0]);
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_removeValue() {
+        TestSSLSessions s = TestSSLSessions.create();
+        String key = "KEY";
+        String value = "VALUE";
+        s.invalid.putValue(key, value);
+        assertEquals(1, s.invalid.getValueNames().length);
+        assertEquals(key, s.invalid.getValueNames()[0]);
+        s.invalid.removeValue(key);
+        assertNull(s.invalid.getValue(key));
+        assertEquals(0, s.invalid.getValueNames().length);
+        s.close();
+    }
+
+    @Test
+    public void test_SSLSession_BindingListener() {
+        final TestSSLSessions s = TestSSLSessions.create();
+        final String key = "KEY";
+        final boolean[] bound = new boolean[] {false};
+        final Object value = new SSLSessionBindingListener() {
+            @Override
+            public void valueBound(SSLSessionBindingEvent e) {
+                assertEquals(s.client, e.getSession());
+                assertEquals(key, e.getName());
+                assertFalse(bound[0]);
+                bound[0] = true;
+            }
+
+            @Override
+            public void valueUnbound(SSLSessionBindingEvent e) {
+                assertEquals(s.client, e.getSession());
+                assertEquals(key, e.getName());
+                assertTrue(bound[0]);
+                bound[0] = false;
+            }
+        };
+        s.client.putValue(key, value);
+        assertSame(value, s.client.getValue(key));
+        assertTrue(bound[0]);
+        s.client.removeValue(key);
+        assertFalse(bound[0]);
+    }
+
+    @Test
+    public void test_SSLSession_valueIndependence() {
+        // Multiple sessions should have independent value stores
+        for (int i = 0; i < 2; i++) {
+            TestSSLSessions s = TestSSLSessions.create();
+            String key = "KEY";
+            String value = "VALUE";
+            assertNull(s.invalid.getValue(key));
+            assertEquals(0, s.invalid.getValueNames().length);
+            s.invalid.putValue(key, value);
+            assertSame(value, s.invalid.getValue(key));
+            assertEquals(1, s.invalid.getValueNames().length);
+            assertEquals(key, s.invalid.getValueNames()[0]);
+            s.close();
+        }
+    }
+
+    private static String alterOriginalHostName(InetAddress inetAddress, String originalHostName)
+            throws Exception {
+        Method getHolder = InetAddress.class.getDeclaredMethod("holder");
+        getHolder.setAccessible(true);
+
+        Field originalHostNameField = Class.forName("java.net.InetAddress$InetAddressHolder")
+                .getDeclaredField("originalHostName");
+        originalHostNameField.setAccessible(true);
+
+        Object holder = getHolder.invoke(inetAddress);
+        String oldValue = (String)originalHostNameField.get(holder);
+        originalHostNameField.set(holder, originalHostName);
+        return oldValue;
+    }
+
+    // http://b/35942385
+    @Test
+    public void test_SSLSession_getPeerHostFromInetAddress() throws Exception {
+        TestUtils.assumeAndroid();
+        InetAddress inetAddress = TestUtils.getLoopbackAddress();
+        String oldOriginalHostName = alterOriginalHostName(inetAddress, "foobar");
+        try {
+            final TestSSLContext c = TestSSLContext.create();
+            final SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket(
+                    TestUtils.getLoopbackAddress(), c.port);
+            final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+
+            ExecutorService executor = Executors.newSingleThreadExecutor();
+            Future<Void> future = executor.submit(new Callable<Void>() {
+                                                      @Override
+                                                      public Void call() throws Exception {
+                                                          server.startHandshake();
+                                                          return null;
+                                                      }
+                                                  });
+            executor.shutdown();
+            client.startHandshake();
+
+            SSLSession sslSession = client.getSession();
+            assertEquals("foobar", sslSession.getPeerHost());
+
+            future.get();
+            client.close();
+            server.close();
+            c.close();
+        } finally {
+            // Restore the original value (InetAddress objects are cached).
+            alterOriginalHostName(inetAddress, oldOriginalHostName);
+        }
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketFactoryTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketFactoryTest.java
new file mode 100644
index 0000000..d13c039
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketFactoryTest.java
@@ -0,0 +1,97 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import javax.net.ServerSocketFactory;
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class SSLSocketFactoryTest {
+
+    @Test
+    public void test_SSLSocketFactory_getDefault() {
+        SocketFactory sf = SSLSocketFactory.getDefault();
+        assertNotNull(sf);
+        assertTrue(SSLSocketFactory.class.isAssignableFrom(sf.getClass()));
+    }
+
+    @Test
+    public void test_SSLSocketFactory_defaultConfiguration() throws Exception {
+        SSLConfigurationAsserts.assertSSLSocketFactoryDefaultConfiguration(
+                (SSLSocketFactory) SSLSocketFactory.getDefault());
+    }
+
+    @Test
+    public void test_SSLSocketFactory_getDefaultCipherSuitesReturnsCopies() {
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        assertNotSame(sf.getDefaultCipherSuites(), sf.getDefaultCipherSuites());
+    }
+
+    @Test
+    public void test_SSLSocketFactory_getSupportedCipherSuitesReturnsCopies() {
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        assertNotSame(sf.getSupportedCipherSuites(), sf.getSupportedCipherSuites());
+    }
+
+    @Test
+    public void test_SSLSocketFactory_createSocket() throws Exception {
+        try {
+            SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+            sf.createSocket(null, null, -1, false);
+            fail();
+        } catch (NullPointerException expected) {
+            // Ignored.
+        }
+
+        try {
+            SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+            sf.createSocket(new Socket(), null, -1, false);
+            fail();
+        } catch (SocketException expected) {
+            // Ignored.
+        }
+
+        ServerSocket ss = ServerSocketFactory.getDefault().createServerSocket(0);
+        InetSocketAddress sa = (InetSocketAddress) ss.getLocalSocketAddress();
+        InetAddress host = sa.getAddress();
+        int port = sa.getPort();
+        Socket s = new Socket(host, port);
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        Socket ssl = sf.createSocket(s, null, -1, false);
+        assertNotNull(ssl);
+        assertTrue(SSLSocket.class.isAssignableFrom(ssl.getClass()));
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketTest.java
new file mode 100644
index 0000000..457ce1d
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketTest.java
@@ -0,0 +1,1104 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static com.android.org.conscrypt.TestUtils.UTF_8;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLProtocolException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.X509ExtendedTrustManager;
+import com.android.org.conscrypt.TestUtils;
+import com.android.org.conscrypt.java.security.StandardNames;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import com.android.org.conscrypt.tlswire.TlsTester;
+import com.android.org.conscrypt.tlswire.handshake.CipherSuite;
+import com.android.org.conscrypt.tlswire.handshake.ClientHello;
+import com.android.org.conscrypt.tlswire.handshake.CompressionMethod;
+import com.android.org.conscrypt.tlswire.handshake.EllipticCurve;
+import com.android.org.conscrypt.tlswire.handshake.EllipticCurvesHelloExtension;
+import com.android.org.conscrypt.tlswire.handshake.HelloExtension;
+import com.android.org.conscrypt.tlswire.util.TlsProtocolVersion;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import tests.net.DelegatingSSLSocketFactory;
+import tests.util.ForEachRunner;
+import tests.util.ForEachRunner.Callback;
+import tests.util.Pair;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class SSLSocketTest {
+    private ExecutorService executor;
+    private ThreadGroup threadGroup;
+
+    @Before
+    public void setup() {
+        threadGroup = new ThreadGroup("SSLSocketTest");
+        executor = Executors.newCachedThreadPool(new ThreadFactory() {
+            @Override
+            public Thread newThread(Runnable r) {
+                return new Thread(threadGroup, r);
+            }
+        });
+    }
+
+    @After
+    public void teardown() throws InterruptedException {
+        executor.shutdownNow();
+        executor.awaitTermination(5, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void test_SSLSocket_defaultConfiguration() throws Exception {
+        SSLConfigurationAsserts.assertSSLSocketDefaultConfiguration(
+                (SSLSocket) SSLSocketFactory.getDefault().createSocket());
+    }
+
+    @Test
+    public void test_SSLSocket_getSupportedCipherSuites_returnsCopies() throws Exception {
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket ssl = (SSLSocket) sf.createSocket();
+        assertNotSame(ssl.getSupportedCipherSuites(), ssl.getSupportedCipherSuites());
+    }
+
+    @Test
+    public void test_SSLSocket_getSupportedCipherSuites_connect() throws Exception {
+        // note the rare usage of non-RSA keys
+        TestKeyStore testKeyStore = new TestKeyStore.Builder()
+                                            .keyAlgorithms("RSA", "DSA", "EC", "EC_RSA")
+                                            .aliasPrefix("rsa-dsa-ec")
+                                            .ca(true)
+                                            .build();
+        StringBuilder error = new StringBuilder();
+        test_SSLSocket_getSupportedCipherSuites_connect(testKeyStore, error);
+        if (error.length() > 0) {
+            throw new Exception("One or more problems in "
+                    + "test_SSLSocket_getSupportedCipherSuites_connect:\n" + error);
+        }
+    }
+
+    private void test_SSLSocket_getSupportedCipherSuites_connect(
+            TestKeyStore testKeyStore, StringBuilder error) throws Exception {
+        String clientToServerString = "this is sent from the client to the server...";
+        String serverToClientString = "... and this from the server to the client";
+        byte[] clientToServer = clientToServerString.getBytes(UTF_8);
+        byte[] serverToClient = serverToClientString.getBytes(UTF_8);
+        KeyManager pskKeyManager =
+                PSKKeyManagerProxy.getConscryptPSKKeyManager(new PSKKeyManagerProxy() {
+                    @Override
+                    protected SecretKey getKey(
+                            String identityHint, String identity, Socket socket) {
+                        return newKey();
+                    }
+
+                    @Override
+                    protected SecretKey getKey(
+                            String identityHint, String identity, SSLEngine engine) {
+                        return newKey();
+                    }
+
+                    private SecretKey newKey() {
+                        return new SecretKeySpec("Just an arbitrary key".getBytes(UTF_8), "RAW");
+                    }
+                });
+        TestSSLContext c = TestSSLContext.newBuilder()
+                                   .client(testKeyStore)
+                                   .server(testKeyStore)
+                                   .clientProtocol("TLSv1.2")
+                                   .serverProtocol("TLSv1.2")
+                                   .additionalClientKeyManagers(new KeyManager[] {pskKeyManager})
+                                   .additionalServerKeyManagers(new KeyManager[] {pskKeyManager})
+                                   .build();
+        String[] cipherSuites = c.clientContext.getSocketFactory().getSupportedCipherSuites();
+        for (String cipherSuite : cipherSuites) {
+            try {
+                /*
+                 * TLS_EMPTY_RENEGOTIATION_INFO_SCSV cannot be used on
+                 * its own, but instead in conjunction with other
+                 * cipher suites.
+                 */
+                if (cipherSuite.equals(StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION)) {
+                    continue;
+                }
+                /*
+                 * Similarly with the TLS_FALLBACK_SCSV suite, it is not
+                 * a selectable suite, but is used in conjunction with
+                 * other cipher suites.
+                 */
+                if (cipherSuite.equals(StandardNames.CIPHER_SUITE_FALLBACK)) {
+                    continue;
+                }
+                /*
+                 * This test uses TLS 1.2, and the TLS 1.3 cipher suites aren't customizable
+                 * anyway.
+                 */
+                if (StandardNames.CIPHER_SUITES_TLS13.contains(cipherSuite)) {
+                    continue;
+                }
+                String[] clientCipherSuiteArray =
+                        new String[] {cipherSuite, StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION};
+                TestSSLSocketPair socketPair = TestSSLSocketPair.create(c).connect(
+                        clientCipherSuiteArray, clientCipherSuiteArray);
+                SSLSocket server = socketPair.server;
+                SSLSocket client = socketPair.client;
+                // Check that the client can read the message sent by the server
+                server.getOutputStream().write(serverToClient);
+                byte[] clientFromServer = new byte[serverToClient.length];
+                readFully(client.getInputStream(), clientFromServer);
+                assertEquals(serverToClientString, new String(clientFromServer, UTF_8));
+                // Check that the server can read the message sent by the client
+                client.getOutputStream().write(clientToServer);
+                byte[] serverFromClient = new byte[clientToServer.length];
+                readFully(server.getInputStream(), serverFromClient);
+                assertEquals(clientToServerString, new String(serverFromClient, UTF_8));
+                // Check that the server and the client cannot read anything else
+                // (reads should time out)
+                server.setSoTimeout(10);
+                try {
+                    @SuppressWarnings("unused")
+                    int value = server.getInputStream().read();
+                    fail();
+                } catch (IOException expected) {
+                    // Ignored.
+                }
+                client.setSoTimeout(10);
+                try {
+                    @SuppressWarnings("unused")
+                    int value = client.getInputStream().read();
+                    fail();
+                } catch (IOException expected) {
+                    // Ignored.
+                }
+                client.close();
+                server.close();
+            } catch (Exception maybeExpected) {
+                String message = ("Problem trying to connect cipher suite " + cipherSuite);
+                System.out.println(message);
+                maybeExpected.printStackTrace();
+                error.append(message);
+                error.append('\n');
+            }
+        }
+        c.close();
+    }
+
+    @Test
+    public void test_SSLSocket_getEnabledCipherSuites_returnsCopies() throws Exception {
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket ssl = (SSLSocket) sf.createSocket();
+        assertNotSame(ssl.getEnabledCipherSuites(), ssl.getEnabledCipherSuites());
+    }
+
+    @Test
+    public void test_SSLSocket_setEnabledCipherSuites_storesCopy() throws Exception {
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket ssl = (SSLSocket) sf.createSocket();
+        String[] array = new String[] {ssl.getEnabledCipherSuites()[0]};
+        String originalFirstElement = array[0];
+        ssl.setEnabledCipherSuites(array);
+        array[0] = "Modified after having been set";
+        assertEquals(originalFirstElement, ssl.getEnabledCipherSuites()[0]);
+    }
+
+    @Test
+    public void test_SSLSocket_setEnabledCipherSuites_TLS12() throws Exception {
+        SSLContext context = SSLContext.getInstance("TLSv1.2");
+        context.init(null, null, null);
+        SSLSocket ssl = (SSLSocket) context.getSocketFactory().createSocket();
+        try {
+            ssl.setEnabledCipherSuites(null);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        try {
+            ssl.setEnabledCipherSuites(new String[1]);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        try {
+            ssl.setEnabledCipherSuites(new String[] {"Bogus"});
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        ssl.setEnabledCipherSuites(new String[0]);
+        ssl.setEnabledCipherSuites(ssl.getEnabledCipherSuites());
+        ssl.setEnabledCipherSuites(ssl.getSupportedCipherSuites());
+        // Check that setEnabledCipherSuites affects getEnabledCipherSuites
+        String[] cipherSuites = new String[] {
+                TestUtils.pickArbitraryNonTls13Suite(ssl.getSupportedCipherSuites())
+        };
+        ssl.setEnabledCipherSuites(cipherSuites);
+        assertEquals(Arrays.asList(cipherSuites), Arrays.asList(ssl.getEnabledCipherSuites()));
+    }
+
+    @Test
+    public void test_SSLSocket_setEnabledCipherSuites_TLS13() throws Exception {
+        SSLContext context = SSLContext.getInstance("TLSv1.3");
+        context.init(null, null, null);
+        SSLSocketFactory sf = context.getSocketFactory();
+        SSLSocket ssl = (SSLSocket) sf.createSocket();
+        // The TLS 1.3 cipher suites should be enabled by default
+        assertTrue(new HashSet<String>(Arrays.asList(ssl.getEnabledCipherSuites()))
+                .containsAll(StandardNames.CIPHER_SUITES_TLS13));
+        // Disabling them should be ignored
+        ssl.setEnabledCipherSuites(new String[0]);
+        assertTrue(new HashSet<String>(Arrays.asList(ssl.getEnabledCipherSuites()))
+                .containsAll(StandardNames.CIPHER_SUITES_TLS13));
+
+        ssl.setEnabledCipherSuites(new String[] {
+                TestUtils.pickArbitraryNonTls13Suite(ssl.getSupportedCipherSuites())
+        });
+        assertTrue(new HashSet<String>(Arrays.asList(ssl.getEnabledCipherSuites()))
+                .containsAll(StandardNames.CIPHER_SUITES_TLS13));
+
+        // Disabling TLS 1.3 should disable 1.3 cipher suites
+        ssl.setEnabledProtocols(new String[] { "TLSv1.2" });
+        assertFalse(new HashSet<String>(Arrays.asList(ssl.getEnabledCipherSuites()))
+                .containsAll(StandardNames.CIPHER_SUITES_TLS13));
+    }
+
+    @Test
+    public void test_SSLSocket_getSupportedProtocols_returnsCopies() throws Exception {
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket ssl = (SSLSocket) sf.createSocket();
+        assertNotSame(ssl.getSupportedProtocols(), ssl.getSupportedProtocols());
+    }
+
+    @Test
+    public void test_SSLSocket_getEnabledProtocols_returnsCopies() throws Exception {
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket ssl = (SSLSocket) sf.createSocket();
+        assertNotSame(ssl.getEnabledProtocols(), ssl.getEnabledProtocols());
+    }
+
+    @Test
+    public void test_SSLSocket_setEnabledProtocols_storesCopy() throws Exception {
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket ssl = (SSLSocket) sf.createSocket();
+        String[] array = new String[] {ssl.getEnabledProtocols()[0]};
+        String originalFirstElement = array[0];
+        ssl.setEnabledProtocols(array);
+        array[0] = "Modified after having been set";
+        assertEquals(originalFirstElement, ssl.getEnabledProtocols()[0]);
+    }
+
+    @Test
+    public void test_SSLSocket_setEnabledProtocols() throws Exception {
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket ssl = (SSLSocket) sf.createSocket();
+        try {
+            ssl.setEnabledProtocols(null);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        try {
+            ssl.setEnabledProtocols(new String[1]);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        try {
+            ssl.setEnabledProtocols(new String[] {"Bogus"});
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        ssl.setEnabledProtocols(new String[0]);
+        ssl.setEnabledProtocols(ssl.getEnabledProtocols());
+        ssl.setEnabledProtocols(ssl.getSupportedProtocols());
+        // Check that setEnabledProtocols affects getEnabledProtocols
+        for (String protocol : ssl.getSupportedProtocols()) {
+            if ("SSLv2Hello".equals(protocol)) {
+                try {
+                    ssl.setEnabledProtocols(new String[] {protocol});
+                    fail("Should fail when SSLv2Hello is set by itself");
+                } catch (IllegalArgumentException expected) {
+                    // Ignored.
+                }
+            } else {
+                String[] protocols = new String[] {protocol};
+                ssl.setEnabledProtocols(protocols);
+                assertEquals(Arrays.deepToString(protocols),
+                        Arrays.deepToString(ssl.getEnabledProtocols()));
+            }
+        }
+    }
+
+    /**
+     * Tests that when the client has a hole in their supported protocol list, the
+     * lower span of contiguous protocols is used in practice.
+     */
+    @Test
+    public void test_SSLSocket_noncontiguousProtocols_useLower() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        SSLContext clientContext = c.clientContext;
+        SSLSocket client = (SSLSocket)
+                clientContext.getSocketFactory().createSocket(c.host, c.port);
+        client.setEnabledProtocols(new String[] {"TLSv1.2", "TLSv1"});
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        server.setEnabledProtocols(new String[] {"TLSv1.2", "TLSv1.1", "TLSv1"});
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        Future<Void> future = executor.submit(new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                server.startHandshake();
+                return null;
+            }
+        });
+        executor.shutdown();
+        client.startHandshake();
+
+        assertEquals("TLSv1", client.getSession().getProtocol());
+
+        future.get();
+        client.close();
+        server.close();
+        c.close();
+    }
+
+    /**
+     * Tests that protocol negotiation succeeds when the highest-supported protocol
+     * for both client and server isn't supported by the other.
+     */
+    @Test
+    public void test_SSLSocket_noncontiguousProtocols_canNegotiate() throws Exception {
+        TestSSLContext c = TestSSLContext.create();
+        SSLContext clientContext = c.clientContext;
+        SSLSocket client = (SSLSocket)
+                clientContext.getSocketFactory().createSocket(c.host, c.port);
+        client.setEnabledProtocols(new String[] {"TLSv1.2", "TLSv1"});
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        server.setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1"});
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        Future<Void> future = executor.submit(new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                server.startHandshake();
+                return null;
+            }
+        });
+        executor.shutdown();
+        client.startHandshake();
+
+        assertEquals("TLSv1", client.getSession().getProtocol());
+
+        future.get();
+        client.close();
+        server.close();
+        c.close();
+    }
+
+    @Test
+    public void test_SSLSocket_getSession() throws Exception {
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket ssl = (SSLSocket) sf.createSocket();
+        SSLSession session = ssl.getSession();
+        assertNotNull(session);
+        assertFalse(session.isValid());
+    }
+
+    @Test
+    public void test_SSLSocket_getHandshakeSession_unconnected() throws Exception {
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket socket = (SSLSocket) sf.createSocket();
+        SSLSession session = socket.getHandshakeSession();
+        assertNull(session);
+    }
+
+    @Test
+    public void test_SSLSocket_getHandshakeSession_duringHandshake_client() throws Exception {
+        // We can't reference the actual context we're using, since we need to pass
+        // the test trust manager in to construct it, so create reference objects that
+        // we can test against.
+        final TestSSLContext referenceContext = TestSSLContext.create();
+        final SSLSocket referenceClientSocket =
+                (SSLSocket) referenceContext.clientContext.getSocketFactory().createSocket();
+
+        final boolean[] wasCalled = new boolean[1];
+        TestSSLContext c =
+                TestSSLContext.newBuilder()
+                        .clientTrustManager(new X509ExtendedTrustManager() {
+                            @Override
+                            public void checkClientTrusted(X509Certificate[] x509Certificates,
+                                    String s, Socket socket) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkServerTrusted(X509Certificate[] x509Certificates,
+                                    String s, Socket socket) throws CertificateException {
+                                try {
+                                    SSLSocket sslSocket = (SSLSocket) socket;
+                                    SSLSession session = sslSocket.getHandshakeSession();
+                                    assertNotNull(session);
+                                    // By the point of the handshake where we're validating
+                                    // certificates, the hostname is known and the cipher suite
+                                    // should be agreed
+                                    assertEquals(referenceContext.host.getHostName(),
+                                            session.getPeerHost());
+                                    assertEquals(referenceClientSocket.getEnabledCipherSuites()[0],
+                                            session.getCipherSuite());
+                                    wasCalled[0] = true;
+                                } catch (Exception e) {
+                                    throw new CertificateException("Something broke", e);
+                                }
+                            }
+
+                            @Override
+                            public void checkClientTrusted(X509Certificate[] x509Certificates,
+                                    String s, SSLEngine sslEngine) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkServerTrusted(X509Certificate[] x509Certificates,
+                                    String s, SSLEngine sslEngine) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkClientTrusted(X509Certificate[] x509Certificates,
+                                    String s) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkServerTrusted(X509Certificate[] x509Certificates,
+                                    String s) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public X509Certificate[] getAcceptedIssuers() {
+                                return new X509Certificate[0];
+                            }
+                        })
+                        .build();
+        SSLContext clientContext = c.clientContext;
+        SSLSocket client =
+                (SSLSocket) clientContext.getSocketFactory().createSocket(c.host, c.port);
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        Future<Void> future = executor.submit(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                server.startHandshake();
+                return null;
+            }
+        });
+        executor.shutdown();
+        client.startHandshake();
+
+        future.get();
+        client.close();
+        server.close();
+        c.close();
+        assertTrue(wasCalled[0]);
+    }
+
+    @Test
+    public void test_SSLSocket_getHandshakeSession_duringHandshake_server() throws Exception {
+        // We can't reference the actual context we're using, since we need to pass
+        // the test trust manager in to construct it, so create reference objects that
+        // we can test against.
+        final TestSSLContext referenceContext = TestSSLContext.create();
+        final SSLSocket referenceClientSocket =
+                (SSLSocket) referenceContext.clientContext.getSocketFactory().createSocket();
+
+        final boolean[] wasCalled = new boolean[1];
+        TestSSLContext c =
+                TestSSLContext.newBuilder()
+                        .client(TestKeyStore.getClientCertificate())
+                        .serverTrustManager(new X509ExtendedTrustManager() {
+                            @Override
+                            public void checkClientTrusted(X509Certificate[] x509Certificates,
+                                    String s, Socket socket) throws CertificateException {
+                                try {
+                                    SSLSocket sslSocket = (SSLSocket) socket;
+                                    SSLSession session = sslSocket.getHandshakeSession();
+                                    assertNotNull(session);
+                                    // By the point of the handshake where we're validating client
+                                    // certificates, the cipher suite should be agreed and the
+                                    // server's own certificates should have been delivered
+                                    assertEquals(referenceClientSocket.getEnabledCipherSuites()[0],
+                                            session.getCipherSuite());
+                                    assertNotNull(session.getLocalCertificates());
+                                    assertEquals("CN=localhost",
+                                            ((X509Certificate) session.getLocalCertificates()[0])
+                                                    .getSubjectDN()
+                                                    .getName());
+                                    assertEquals("CN=Test Intermediate Certificate Authority",
+                                            ((X509Certificate) session.getLocalCertificates()[0])
+                                                    .getIssuerDN()
+                                                    .getName());
+                                    wasCalled[0] = true;
+                                } catch (Exception e) {
+                                    throw new CertificateException("Something broke", e);
+                                }
+                            }
+
+                            @Override
+                            public void checkServerTrusted(X509Certificate[] x509Certificates,
+                                    String s, Socket socket) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkClientTrusted(X509Certificate[] x509Certificates,
+                                    String s, SSLEngine sslEngine) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkServerTrusted(X509Certificate[] x509Certificates,
+                                    String s, SSLEngine sslEngine) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkClientTrusted(X509Certificate[] x509Certificates,
+                                    String s) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public void checkServerTrusted(X509Certificate[] x509Certificates,
+                                    String s) throws CertificateException {
+                                throw new CertificateException("Shouldn't be called");
+                            }
+
+                            @Override
+                            public X509Certificate[] getAcceptedIssuers() {
+                                return referenceContext.serverTrustManager.getAcceptedIssuers();
+                            }
+                        })
+                        .build();
+        SSLContext clientContext = c.clientContext;
+        SSLSocket client =
+                (SSLSocket) clientContext.getSocketFactory().createSocket(c.host, c.port);
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        Future<Void> future = executor.submit(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                server.setNeedClientAuth(true);
+                server.startHandshake();
+                return null;
+            }
+        });
+        executor.shutdown();
+        client.startHandshake();
+
+        future.get();
+        client.close();
+        server.close();
+        c.close();
+        assertTrue(wasCalled[0]);
+    }
+
+    @Test
+    public void test_SSLSocket_setUseClientMode_afterHandshake() throws Exception {
+        // can't set after handshake
+        TestSSLSocketPair pair = TestSSLSocketPair.create().connect();
+        try {
+            pair.server.setUseClientMode(false);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        try {
+            pair.client.setUseClientMode(false);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+    }
+
+    @Test
+    public void test_SSLSocket_untrustedServer() throws Exception {
+        TestSSLContext c =
+                TestSSLContext.create(TestKeyStore.getClientCA2(), TestKeyStore.getServer());
+        SSLSocket client =
+                (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host, c.port);
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        Future<Void> future = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                try {
+                    server.startHandshake();
+                    fail();
+                } catch (SSLHandshakeException expected) {
+                    // Ignored.
+                }
+                return null;
+            }
+        });
+        try {
+            client.startHandshake();
+            fail();
+        } catch (SSLHandshakeException expected) {
+            assertTrue(expected.getCause() instanceof CertificateException);
+        }
+        future.get();
+        client.close();
+        server.close();
+        c.close();
+    }
+
+    @Test
+    public void test_SSLSocket_getSSLParameters() throws Exception {
+        TestUtils.assumeSetEndpointIdentificationAlgorithmAvailable();
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket ssl = (SSLSocket) sf.createSocket();
+        SSLParameters p = ssl.getSSLParameters();
+        assertNotNull(p);
+        String[] cipherSuites = p.getCipherSuites();
+        assertNotSame(cipherSuites, ssl.getEnabledCipherSuites());
+        assertEquals(Arrays.asList(cipherSuites), Arrays.asList(ssl.getEnabledCipherSuites()));
+        String[] protocols = p.getProtocols();
+        assertNotSame(protocols, ssl.getEnabledProtocols());
+        assertEquals(Arrays.asList(protocols), Arrays.asList(ssl.getEnabledProtocols()));
+        assertEquals(p.getWantClientAuth(), ssl.getWantClientAuth());
+        assertEquals(p.getNeedClientAuth(), ssl.getNeedClientAuth());
+        assertNull(p.getEndpointIdentificationAlgorithm());
+        p.setEndpointIdentificationAlgorithm(null);
+        assertNull(p.getEndpointIdentificationAlgorithm());
+        p.setEndpointIdentificationAlgorithm("HTTPS");
+        assertEquals("HTTPS", p.getEndpointIdentificationAlgorithm());
+        p.setEndpointIdentificationAlgorithm("FOO");
+        assertEquals("FOO", p.getEndpointIdentificationAlgorithm());
+    }
+
+    @Test
+    public void test_SSLSocket_setSSLParameters() throws Exception {
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        SSLSocket ssl = (SSLSocket) sf.createSocket();
+        String[] defaultCipherSuites = ssl.getEnabledCipherSuites();
+        String[] defaultProtocols = ssl.getEnabledProtocols();
+        String[] supportedCipherSuites = ssl.getSupportedCipherSuites();
+        String[] supportedProtocols = ssl.getSupportedProtocols();
+        {
+            SSLParameters p = new SSLParameters();
+            ssl.setSSLParameters(p);
+            assertEquals(Arrays.asList(defaultCipherSuites),
+                    Arrays.asList(ssl.getEnabledCipherSuites()));
+            assertEquals(Arrays.asList(defaultProtocols), Arrays.asList(ssl.getEnabledProtocols()));
+        }
+        {
+            SSLParameters p = new SSLParameters(supportedCipherSuites, supportedProtocols);
+            ssl.setSSLParameters(p);
+            assertEquals(Arrays.asList(supportedCipherSuites),
+                    Arrays.asList(ssl.getEnabledCipherSuites()));
+            assertEquals(
+                    Arrays.asList(supportedProtocols), Arrays.asList(ssl.getEnabledProtocols()));
+        }
+        {
+            SSLParameters p = new SSLParameters();
+            p.setNeedClientAuth(true);
+            assertFalse(ssl.getNeedClientAuth());
+            assertFalse(ssl.getWantClientAuth());
+            ssl.setSSLParameters(p);
+            assertTrue(ssl.getNeedClientAuth());
+            assertFalse(ssl.getWantClientAuth());
+            p.setWantClientAuth(true);
+            assertTrue(ssl.getNeedClientAuth());
+            assertFalse(ssl.getWantClientAuth());
+            ssl.setSSLParameters(p);
+            assertFalse(ssl.getNeedClientAuth());
+            assertTrue(ssl.getWantClientAuth());
+            p.setWantClientAuth(false);
+            assertFalse(ssl.getNeedClientAuth());
+            assertTrue(ssl.getWantClientAuth());
+            ssl.setSSLParameters(p);
+            assertFalse(ssl.getNeedClientAuth());
+            assertFalse(ssl.getWantClientAuth());
+        }
+    }
+
+    @Test
+    public void test_SSLSocket_setSoTimeout_basic() throws Exception {
+        ServerSocket listening = new ServerSocket(0);
+        Socket underlying = new Socket(listening.getInetAddress(), listening.getLocalPort());
+        assertEquals(0, underlying.getSoTimeout());
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        Socket wrapping = sf.createSocket(underlying, null, -1, false);
+        assertEquals(0, wrapping.getSoTimeout());
+        // setting wrapper sets underlying and ...
+        int expectedTimeoutMillis = 1000; // 10 was too small because it was affected by rounding
+        wrapping.setSoTimeout(expectedTimeoutMillis);
+        // The kernel can round the requested value based on the HZ setting. We allow up to 10ms.
+        assertTrue(Math.abs(expectedTimeoutMillis - wrapping.getSoTimeout()) <= 10);
+        assertTrue(Math.abs(expectedTimeoutMillis - underlying.getSoTimeout()) <= 10);
+        // ... getting wrapper inspects underlying
+        underlying.setSoTimeout(0);
+        assertEquals(0, wrapping.getSoTimeout());
+        assertEquals(0, underlying.getSoTimeout());
+    }
+
+    @Test
+    public void test_SSLSocket_setSoTimeout_wrapper() throws Exception {
+        ServerSocket listening = new ServerSocket(0);
+        // setSoTimeout applies to read, not connect, so connect first
+        Socket underlying = new Socket(listening.getInetAddress(), listening.getLocalPort());
+        Socket server = listening.accept();
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        Socket clientWrapping = sf.createSocket(underlying, null, -1, false);
+        underlying.setSoTimeout(1);
+        try {
+            @SuppressWarnings("unused")
+            int value = clientWrapping.getInputStream().read();
+            fail();
+        } catch (SocketTimeoutException expected) {
+            // Ignored.
+        }
+        clientWrapping.close();
+        server.close();
+        underlying.close();
+        listening.close();
+    }
+
+    @Test
+    public void test_TestSSLSocketPair_create() {
+        TestSSLSocketPair test = TestSSLSocketPair.create().connect();
+        assertNotNull(test.c);
+        assertNotNull(test.server);
+        assertNotNull(test.client);
+        assertTrue(test.server.isConnected());
+        assertTrue(test.client.isConnected());
+        assertFalse(test.server.isClosed());
+        assertFalse(test.client.isClosed());
+        assertNotNull(test.server.getSession());
+        assertNotNull(test.client.getSession());
+        assertTrue(test.server.getSession().isValid());
+        assertTrue(test.client.getSession().isValid());
+        test.close();
+    }
+
+    @Test
+    public void test_SSLSocket_ClientHello_cipherSuites() throws Exception {
+        ForEachRunner.runNamed(new Callback<SSLSocketFactory>() {
+            @Override
+            public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+                ClientHello clientHello = TlsTester
+                        .captureTlsHandshakeClientHello(executor, sslSocketFactory);
+                final String[] cipherSuites;
+                // RFC 5746 allows you to send an empty "renegotiation_info" extension *or*
+                // a special signaling cipher suite. The TLS API has no way to check or
+                // indicate that a certain TLS extension should be used.
+                HelloExtension renegotiationInfoExtension =
+                    clientHello.findExtensionByType(HelloExtension.TYPE_RENEGOTIATION_INFO);
+                if (renegotiationInfoExtension != null
+                    && renegotiationInfoExtension.data.length == 1
+                    && renegotiationInfoExtension.data[0] == 0) {
+                    cipherSuites = new String[clientHello.cipherSuites.size() + 1];
+                    cipherSuites[clientHello.cipherSuites.size()] =
+                        StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION;
+                } else {
+                    cipherSuites = new String[clientHello.cipherSuites.size()];
+                }
+                for (int i = 0; i < clientHello.cipherSuites.size(); i++) {
+                    CipherSuite cipherSuite = clientHello.cipherSuites.get(i);
+                    cipherSuites[i] = cipherSuite.getAndroidName();
+                }
+                StandardNames.assertDefaultCipherSuites(cipherSuites);
+            }
+        }, getSSLSocketFactoriesToTest());
+    }
+
+    @Test
+    public void test_SSLSocket_ClientHello_supportedCurves() throws Exception {
+        ForEachRunner.runNamed(new Callback<SSLSocketFactory>() {
+            @Override
+            public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+                ClientHello clientHello = TlsTester
+                        .captureTlsHandshakeClientHello(executor, sslSocketFactory);
+                EllipticCurvesHelloExtension ecExtension =
+                    (EllipticCurvesHelloExtension) clientHello.findExtensionByType(
+                        HelloExtension.TYPE_ELLIPTIC_CURVES);
+                final String[] supportedCurves;
+                if (ecExtension == null) {
+                    supportedCurves = new String[0];
+                } else {
+                    assertTrue(ecExtension.wellFormed);
+                    supportedCurves = new String[ecExtension.supported.size()];
+                    for (int i = 0; i < ecExtension.supported.size(); i++) {
+                        EllipticCurve curve = ecExtension.supported.get(i);
+                        supportedCurves[i] = curve.toString();
+                    }
+                }
+                StandardNames.assertDefaultEllipticCurves(supportedCurves);
+            }
+        }, getSSLSocketFactoriesToTest());
+    }
+
+    @Test
+    public void test_SSLSocket_ClientHello_clientProtocolVersion() throws Exception {
+        ForEachRunner.runNamed(new Callback<SSLSocketFactory>() {
+            @Override
+            public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+                ClientHello clientHello = TlsTester
+                        .captureTlsHandshakeClientHello(executor, sslSocketFactory);
+                assertEquals(TlsProtocolVersion.TLSv1_2, clientHello.clientVersion);
+            }
+        }, getSSLSocketFactoriesToTest());
+    }
+
+    @Test
+    public void test_SSLSocket_ClientHello_compressionMethods() throws Exception {
+        ForEachRunner.runNamed(new Callback<SSLSocketFactory>() {
+            @Override
+            public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+                ClientHello clientHello = TlsTester
+                        .captureTlsHandshakeClientHello(executor, sslSocketFactory);
+                assertEquals(Collections.singletonList(CompressionMethod.NULL),
+                    clientHello.compressionMethods);
+            }
+        }, getSSLSocketFactoriesToTest());
+    }
+
+    private List<Pair<String, SSLSocketFactory>> getSSLSocketFactoriesToTest()
+            throws NoSuchAlgorithmException, KeyManagementException {
+        List<Pair<String, SSLSocketFactory>> result =
+                new ArrayList<Pair<String, SSLSocketFactory>>();
+        result.add(Pair.of("default", (SSLSocketFactory) SSLSocketFactory.getDefault()));
+        for (String sslContextProtocol : StandardNames.SSL_CONTEXT_PROTOCOLS_WITH_DEFAULT_CONFIG) {
+            SSLContext sslContext = SSLContext.getInstance(sslContextProtocol);
+            if (StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT.equals(sslContextProtocol)) {
+                continue;
+            }
+            sslContext.init(null, null, null);
+            result.add(Pair.of("SSLContext(\"" + sslContext.getProtocol() + "\")",
+                    sslContext.getSocketFactory()));
+        }
+        return result;
+    }
+
+    @Test
+    public void test_SSLSocket_sendsTlsFallbackScsv_Fallback_Success() throws Exception {
+        TestSSLContext context = TestSSLContext.create();
+        final SSLSocket client = (SSLSocket) context.clientContext.getSocketFactory().createSocket(
+                context.host, context.port);
+        final SSLSocket server = (SSLSocket) context.serverSocket.accept();
+        final String[] serverCipherSuites = server.getEnabledCipherSuites();
+        final String[] clientCipherSuites = new String[serverCipherSuites.length + 1];
+        System.arraycopy(serverCipherSuites, 0, clientCipherSuites, 0, serverCipherSuites.length);
+        clientCipherSuites[serverCipherSuites.length] = StandardNames.CIPHER_SUITE_FALLBACK;
+        Future<Void> s = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                server.setEnabledProtocols(new String[]{"TLSv1.2"});
+                server.setEnabledCipherSuites(serverCipherSuites);
+                server.startHandshake();
+                return null;
+            }
+        });
+        Future<Void> c = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                client.setEnabledProtocols(new String[]{"TLSv1.2"});
+                client.setEnabledCipherSuites(clientCipherSuites);
+                client.startHandshake();
+                return null;
+            }
+        });
+        s.get();
+        c.get();
+        client.close();
+        server.close();
+        context.close();
+    }
+
+    // Confirms that communication without the TLS_FALLBACK_SCSV cipher works as it always did.
+    @Test
+    public void test_SSLSocket_sendsNoTlsFallbackScsv_Fallback_Success() throws Exception {
+        TestSSLContext context = TestSSLContext.create();
+        final SSLSocket client = (SSLSocket) context.clientContext.getSocketFactory().createSocket(
+                context.host, context.port);
+        final SSLSocket server = (SSLSocket) context.serverSocket.accept();
+        // Confirm absence of TLS_FALLBACK_SCSV.
+        assertFalse(Arrays.asList(client.getEnabledCipherSuites())
+                            .contains(StandardNames.CIPHER_SUITE_FALLBACK));
+        Future<Void> s = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                server.setEnabledProtocols(new String[]{"TLSv1.2", "TLSv1.1"});
+                server.startHandshake();
+                return null;
+            }
+        });
+        Future<Void> c = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                client.setEnabledProtocols(new String[]{"TLSv1.1"});
+                client.startHandshake();
+                return null;
+            }
+        });
+        s.get();
+        c.get();
+        client.close();
+        server.close();
+        context.close();
+    }
+
+    private static void assertInappropriateFallbackIsCause(Throwable cause) {
+        assertTrue(cause.getMessage(),
+                cause.getMessage().contains("inappropriate fallback")
+                        || cause.getMessage().contains("INAPPROPRIATE_FALLBACK"));
+    }
+
+    @Test
+    public void test_SSLSocket_sendsTlsFallbackScsv_InappropriateFallback_Failure()
+            throws Exception {
+        TestSSLContext context = TestSSLContext.create();
+        final SSLSocket client = (SSLSocket) context.clientContext.getSocketFactory().createSocket(
+                context.host, context.port);
+        final SSLSocket server = (SSLSocket) context.serverSocket.accept();
+        final String[] serverCipherSuites = server.getEnabledCipherSuites();
+        // Add TLS_FALLBACK_SCSV
+        final String[] clientCipherSuites = new String[serverCipherSuites.length + 1];
+        System.arraycopy(serverCipherSuites, 0, clientCipherSuites, 0, serverCipherSuites.length);
+        clientCipherSuites[serverCipherSuites.length] = StandardNames.CIPHER_SUITE_FALLBACK;
+        Future<Void> s = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                server.setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1"});
+                server.setEnabledCipherSuites(serverCipherSuites);
+                try {
+                    server.startHandshake();
+                    fail("Should result in inappropriate fallback");
+                } catch (SSLHandshakeException expected) {
+                    Throwable cause = expected.getCause();
+                    assertEquals(SSLProtocolException.class, cause.getClass());
+                    assertInappropriateFallbackIsCause(cause);
+                }
+                return null;
+            }
+        });
+        Future<Void> c = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                client.setEnabledProtocols(new String[]{"TLSv1"});
+                client.setEnabledCipherSuites(clientCipherSuites);
+                try {
+                    client.startHandshake();
+                    fail("Should receive TLS alert inappropriate fallback");
+                } catch (SSLHandshakeException expected) {
+                    Throwable cause = expected.getCause();
+                    assertEquals(SSLProtocolException.class, cause.getClass());
+                    assertInappropriateFallbackIsCause(cause);
+                }
+                return null;
+            }
+        });
+        s.get();
+        c.get();
+        client.close();
+        server.close();
+        context.close();
+    }
+
+    @Test
+    public void test_SSLSocket_tlsFallback_byVersion() throws Exception {
+        for (final String protocol : new String[] {"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}) {
+            SSLSocketFactory factory = new DelegatingSSLSocketFactory(
+                    (SSLSocketFactory) SSLSocketFactory.getDefault()) {
+                @Override
+                protected SSLSocket configureSocket(SSLSocket socket) {
+                    socket.setEnabledProtocols(new String[] {protocol});
+                    String[] enabled = socket.getEnabledCipherSuites();
+                    String[] cipherSuites = new String[socket.getEnabledCipherSuites().length + 1];
+                    System.arraycopy(enabled, 0, cipherSuites, 0, enabled.length);
+                    cipherSuites[cipherSuites.length - 1] = StandardNames.CIPHER_SUITE_FALLBACK;
+                    socket.setEnabledCipherSuites(cipherSuites);
+                    return socket;
+                }
+            };
+            ClientHello clientHello = TlsTester.captureTlsHandshakeClientHello(executor, factory);
+            if (protocol.equals("TLSv1.2") || protocol.equals("TLSv1.3")) {
+                assertFalse(clientHello.cipherSuites.contains(
+                        CipherSuite.valueOf("TLS_FALLBACK_SCSV")));
+            } else {
+                assertTrue(clientHello.cipherSuites.contains(
+                        CipherSuite.valueOf("TLS_FALLBACK_SCSV")));
+            }
+        }
+    }
+
+    private <T> Future<T> runAsync(Callable<T> callable) {
+        return executor.submit(callable);
+    }
+
+    private static void readFully(InputStream in, byte[] dst) throws IOException {
+        int offset = 0;
+        int byteCount = dst.length;
+        while (byteCount > 0) {
+            int bytesRead = in.read(dst, offset, byteCount);
+            if (bytesRead < 0) {
+                throw new EOFException();
+            }
+            offset += bytesRead;
+            byteCount -= bytesRead;
+        }
+    }
+
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java
new file mode 100644
index 0000000..b0594e6
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/SSLSocketVersionCompatibilityTest.java
@@ -0,0 +1,2056 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static com.android.org.conscrypt.TestUtils.UTF_8;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.org.conscrypt.Conscrypt;
+import com.android.org.conscrypt.TestUtils;
+import com.android.org.conscrypt.java.security.StandardNames;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import com.android.org.conscrypt.testing.OpaqueProvider;
+import com.android.org.conscrypt.tlswire.TlsTester;
+import com.android.org.conscrypt.tlswire.handshake.AlpnHelloExtension;
+import com.android.org.conscrypt.tlswire.handshake.ClientHello;
+import com.android.org.conscrypt.tlswire.handshake.HandshakeMessage;
+import com.android.org.conscrypt.tlswire.handshake.HelloExtension;
+import com.android.org.conscrypt.tlswire.handshake.ServerNameHelloExtension;
+import com.android.org.conscrypt.tlswire.record.TlsProtocols;
+import com.android.org.conscrypt.tlswire.record.TlsRecord;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.lang.reflect.Method;
+import java.net.ConnectException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ServerSocketFactory;
+import javax.net.SocketFactory;
+import javax.net.ssl.ExtendedSSLSession;
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIServerName;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.StandardConstants;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import tests.net.DelegatingSSLSocketFactory;
+import tests.util.ForEachRunner;
+import tests.util.ForEachRunner.Callback;
+import tests.util.Pair;
+
+/**
+ * Tests for SSLSocket classes that ensure the TLS 1.2 and TLS 1.3 implementations
+ * are compatible.
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(Parameterized.class)
+public class SSLSocketVersionCompatibilityTest {
+
+    @Parameterized.Parameters(name = "{index}: {0} client, {1} server")
+    public static Iterable<Object[]> data() {
+        // We can't support TLS 1.3 without our own trust manager (which requires
+        // X509ExtendedTrustManager), so only test TLS 1.2 if it's not available.
+        if (TestUtils.isClassAvailable("javax.net.ssl.X509ExtendedTrustManager")) {
+            return Arrays.asList(new Object[][] {
+                    { "TLSv1.2", "TLSv1.2" },
+                    { "TLSv1.2", "TLSv1.3" },
+                    { "TLSv1.3", "TLSv1.2" },
+                    { "TLSv1.3", "TLSv1.3" },
+            });
+        } else {
+            return Arrays.asList(new Object[][]{{ "TLSv1.2", "TLSv1.2"}});
+        }
+    }
+
+    private final String clientVersion;
+    private final String serverVersion;
+    private ExecutorService executor;
+    private ThreadGroup threadGroup;
+
+    public SSLSocketVersionCompatibilityTest(String clientVersion, String serverVersion) {
+        this.clientVersion = clientVersion;
+        this.serverVersion = serverVersion;
+    }
+
+    @Before
+    public void setup() {
+        threadGroup = new ThreadGroup("SSLSocketVersionedTest");
+        executor = Executors.newCachedThreadPool(new ThreadFactory() {
+            @Override
+            public Thread newThread(Runnable r) {
+                return new Thread(threadGroup, r);
+            }
+        });
+    }
+
+    @After
+    public void teardown() throws InterruptedException {
+        executor.shutdownNow();
+        executor.awaitTermination(5, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void test_SSLSocket_startHandshake() throws Exception {
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion).build();
+        SSLSocket client =
+                (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host, c.port);
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        Future<Void> future = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                server.startHandshake();
+                assertNotNull(server.getSession());
+                assertNull(server.getHandshakeSession());
+                try {
+                    server.getSession().getPeerCertificates();
+                    fail();
+                } catch (SSLPeerUnverifiedException expected) {
+                    // Ignored.
+                }
+                Certificate[] localCertificates = server.getSession().getLocalCertificates();
+                assertNotNull(localCertificates);
+                TestKeyStore.assertChainLength(localCertificates);
+                assertNotNull(localCertificates[0]);
+                TestSSLContext
+                    .assertServerCertificateChain(c.serverTrustManager, localCertificates);
+                TestSSLContext.assertCertificateInKeyStore(localCertificates[0], c.serverKeyStore);
+                return null;
+            }
+        });
+        client.startHandshake();
+        assertNotNull(client.getSession());
+        assertNull(client.getSession().getLocalCertificates());
+        Certificate[] peerCertificates = client.getSession().getPeerCertificates();
+        assertNotNull(peerCertificates);
+        TestKeyStore.assertChainLength(peerCertificates);
+        assertNotNull(peerCertificates[0]);
+        TestSSLContext.assertServerCertificateChain(c.clientTrustManager, peerCertificates);
+        TestSSLContext.assertCertificateInKeyStore(peerCertificates[0], c.serverKeyStore);
+        future.get();
+        client.close();
+        server.close();
+        c.close();
+    }
+    private static final class SSLServerSessionIdCallable implements Callable<byte[]> {
+        private final SSLSocket server;
+        private SSLServerSessionIdCallable(SSLSocket server) {
+            this.server = server;
+        }
+        @Override
+        public byte[] call() throws Exception {
+            server.startHandshake();
+            assertNotNull(server.getSession());
+            assertNotNull(server.getSession().getId());
+            return server.getSession().getId();
+        }
+    }
+
+    @Test
+    public void test_SSLSocket_confirmSessionReuse() throws Exception {
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        final SSLSocket client1 = (SSLSocket) c.clientContext.getSocketFactory().createSocket(
+                c.host.getHostName(), c.port);
+        final SSLSocket server1 = (SSLSocket) c.serverSocket.accept();
+        final Future<byte[]> future1 = runAsync(new SSLServerSessionIdCallable(server1));
+        client1.startHandshake();
+        assertNotNull(client1.getSession());
+        assertNotNull(client1.getSession().getId());
+        final byte[] clientSessionId1 = client1.getSession().getId();
+        final byte[] serverSessionId1 = future1.get();
+        assertTrue(Arrays.equals(clientSessionId1, serverSessionId1));
+        client1.close();
+        server1.close();
+        final SSLSocket client2 = (SSLSocket) c.clientContext.getSocketFactory().createSocket(
+                c.host.getHostName(), c.port);
+        final SSLSocket server2 = (SSLSocket) c.serverSocket.accept();
+        final Future<byte[]> future2 = runAsync(new SSLServerSessionIdCallable(server2));
+        client2.startHandshake();
+        assertNotNull(client2.getSession());
+        assertNotNull(client2.getSession().getId());
+        final byte[] clientSessionId2 = client2.getSession().getId();
+        final byte[] serverSessionId2 = future2.get();
+        assertTrue(Arrays.equals(clientSessionId2, serverSessionId2));
+        client2.close();
+        server2.close();
+        assertTrue(Arrays.equals(clientSessionId1, clientSessionId2));
+        c.close();
+    }
+
+    @Test
+    public void test_SSLSocket_NoEnabledCipherSuites_Failure() throws Exception {
+        TestSSLContext c = TestSSLContext.newBuilder()
+                .useDefaults(false)
+                .clientContext(defaultInit(SSLContext.getInstance(clientVersion)))
+                .serverContext(defaultInit(SSLContext.getInstance(serverVersion)))
+                .build();
+        SSLSocket client =
+                (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host, c.port);
+        client.setEnabledCipherSuites(new String[0]);
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        Future<Void> future = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                try {
+                    server.startHandshake();
+                    fail();
+                } catch (SSLHandshakeException expected) {
+                    // Ignored.
+                }
+                return null;
+            }
+        });
+        try {
+            client.startHandshake();
+            fail();
+        } catch (SSLHandshakeException expected) {
+            // Ignored.
+        }
+        future.get();
+        server.close();
+        client.close();
+        c.close();
+    }
+
+    @Test
+    public void test_SSLSocket_startHandshake_noKeyStore() throws Exception {
+        TestSSLContext c = TestSSLContext.newBuilder()
+                .useDefaults(false)
+                .clientContext(defaultInit(SSLContext.getInstance(clientVersion)))
+                .serverContext(defaultInit(SSLContext.getInstance(serverVersion)))
+                .build();
+        SSLSocket client =
+                (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host, c.port);
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        Future<Void> future = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                try {
+                    server.startHandshake();
+                    fail();
+                } catch (SSLHandshakeException expected) {
+                    // Ignored.
+                }
+                return null;
+            }
+        });
+        try {
+            client.startHandshake();
+            fail();
+        } catch (SSLHandshakeException expected) {
+            // Ignored.
+        }
+        future.get();
+        server.close();
+        client.close();
+        c.close();
+    }
+
+    @Test
+    public void test_SSLSocket_startHandshake_noClientCertificate() throws Exception {
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        SSLContext clientContext = c.clientContext;
+        SSLSocket client =
+                (SSLSocket) clientContext.getSocketFactory().createSocket(c.host, c.port);
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        Future<Void> future = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                server.startHandshake();
+                return null;
+            }
+        });
+        client.startHandshake();
+        future.get();
+        client.close();
+        server.close();
+        c.close();
+    }
+
+    @Test
+    public void test_SSLSocket_HandshakeCompletedListener() throws Exception {
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        final SSLSocket client =
+                (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host, c.port);
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        Future<Void> future = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                server.startHandshake();
+                return null;
+            }
+        });
+        final boolean[] handshakeCompletedListenerCalled = new boolean[1];
+        client.addHandshakeCompletedListener(new HandshakeCompletedListener() {
+            @Override
+            public void handshakeCompleted(HandshakeCompletedEvent event) {
+                try {
+                    SSLSession session = event.getSession();
+                    String cipherSuite = event.getCipherSuite();
+                    Certificate[] localCertificates = event.getLocalCertificates();
+                    Certificate[] peerCertificates = event.getPeerCertificates();
+                    javax.security.cert.X509Certificate[] peerCertificateChain =
+                        event.getPeerCertificateChain();
+                    Principal peerPrincipal = event.getPeerPrincipal();
+                    Principal localPrincipal = event.getLocalPrincipal();
+                    SSLSocket socket = event.getSocket();
+                    assertNotNull(session);
+                    byte[] id = session.getId();
+                    assertNotNull(id);
+                    if (negotiatedVersion().equals("TLSv1.2")) {
+                        // Session ticket delivery happens inside the handshake in TLS 1.2,
+                        // but outside it for TLS 1.3.
+                        assertEquals(32, id.length);
+                        assertNotNull(c.clientContext.getClientSessionContext().getSession(id));
+                    } else {
+                        assertEquals(0, id.length);
+                    }
+                    assertNotNull(cipherSuite);
+                    assertTrue(Arrays.asList(client.getEnabledCipherSuites())
+                            .contains(cipherSuite));
+                    assertTrue(Arrays.asList(c.serverSocket.getEnabledCipherSuites())
+                            .contains(cipherSuite));
+
+                    assertNull(localCertificates);
+                    assertNotNull(peerCertificates);
+                    TestKeyStore.assertChainLength(peerCertificates);
+                    assertNotNull(peerCertificates[0]);
+                    TestSSLContext
+                        .assertServerCertificateChain(c.clientTrustManager, peerCertificates);
+                    TestSSLContext
+                        .assertCertificateInKeyStore(peerCertificates[0], c.serverKeyStore);
+                    assertNotNull(peerCertificateChain);
+                    TestKeyStore.assertChainLength(peerCertificateChain);
+                    assertNotNull(peerCertificateChain[0]);
+                    TestSSLContext.assertCertificateInKeyStore(
+                        peerCertificateChain[0].getSubjectDN(), c.serverKeyStore);
+                    assertNotNull(peerPrincipal);
+                    TestSSLContext.assertCertificateInKeyStore(peerPrincipal, c.serverKeyStore);
+                    assertNull(localPrincipal);
+                    assertNotNull(socket);
+                    assertSame(client, socket);
+                    assertNull(socket.getHandshakeSession());
+                    synchronized (handshakeCompletedListenerCalled) {
+                        handshakeCompletedListenerCalled[0] = true;
+                        handshakeCompletedListenerCalled.notify();
+                    }
+                    handshakeCompletedListenerCalled[0] = true;
+                } catch (RuntimeException e) {
+                    throw e;
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        client.startHandshake();
+        future.get();
+        if (negotiatedVersion().equals("TLSv1.2")) {
+            assertNotNull(
+                    c.serverContext.getServerSessionContext()
+                            .getSession(client.getSession().getId()));
+        }
+        synchronized (handshakeCompletedListenerCalled) {
+            while (!handshakeCompletedListenerCalled[0]) {
+                handshakeCompletedListenerCalled.wait();
+            }
+        }
+        client.close();
+        server.close();
+        c.close();
+    }
+    private static final class TestUncaughtExceptionHandler implements UncaughtExceptionHandler {
+        Throwable actualException;
+        @Override
+        public void uncaughtException(Thread thread, Throwable ex) {
+            assertNull(actualException);
+            actualException = ex;
+        }
+    }
+
+    @Test
+    public void test_SSLSocket_HandshakeCompletedListener_RuntimeException() throws Exception {
+        final Thread self = Thread.currentThread();
+        final UncaughtExceptionHandler original = self.getUncaughtExceptionHandler();
+        final RuntimeException expectedException = new RuntimeException("expected");
+        final TestUncaughtExceptionHandler test = new TestUncaughtExceptionHandler();
+        self.setUncaughtExceptionHandler(test);
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        final SSLSocket client =
+                (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host, c.port);
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        Future<Void> future = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                server.startHandshake();
+                return null;
+            }
+        });
+        client.addHandshakeCompletedListener(new HandshakeCompletedListener() {
+            @Override
+            public void handshakeCompleted(HandshakeCompletedEvent event) {
+                throw expectedException;
+            }
+        });
+        client.startHandshake();
+        future.get();
+        client.close();
+        server.close();
+        c.close();
+        assertSame(expectedException, test.actualException);
+        self.setUncaughtExceptionHandler(original);
+    }
+
+    @Test
+    public void test_SSLSocket_getUseClientMode() throws Exception {
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        SSLSocket client =
+                (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host, c.port);
+        SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        assertTrue(client.getUseClientMode());
+        assertFalse(server.getUseClientMode());
+        client.close();
+        server.close();
+        c.close();
+    }
+
+    @Test
+    public void testClientMode_normal() throws Exception {
+        // Client is client and server is server.
+        test_SSLSocket_setUseClientMode(true, false);
+    }
+
+    @Test(expected = SSLHandshakeException.class)
+    public void testClientMode_reverse() throws Exception {
+        // Client is server and server is client.
+        test_SSLSocket_setUseClientMode(false, true);
+    }
+
+    @Test(expected = SSLHandshakeException.class)
+    public void testClientMode_bothClient() throws Exception {
+        test_SSLSocket_setUseClientMode(true, true);
+    }
+
+    @Test
+    public void testClientMode_bothServer() throws Exception {
+        try {
+            test_SSLSocket_setUseClientMode(false, false);
+            fail();
+        } catch (SocketTimeoutException expected) {
+            // Ignore
+        } catch (SSLHandshakeException expected) {
+            // Depending on the timing of the socket closures, this can happen as well.
+            assertTrue("Unexpected handshake error: " + expected.getMessage(),
+                    expected.getMessage().toLowerCase().contains("connection closed"));
+        }
+    }
+
+    private void test_SSLSocket_setUseClientMode(
+            final boolean clientClientMode, final boolean serverClientMode) throws Exception {
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        SSLSocket client =
+                (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host, c.port);
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        Future<IOException> future = runAsync(new Callable<IOException>() {
+            @Override
+            public IOException call() throws Exception {
+                try {
+                    if (!serverClientMode) {
+                        server.setSoTimeout(1000);
+                    }
+                    server.setUseClientMode(serverClientMode);
+                    server.startHandshake();
+                    return null;
+                } catch (SSLHandshakeException e) {
+                    return e;
+                } catch (SocketTimeoutException e) {
+                    return e;
+                }
+            }
+        });
+        if (!clientClientMode) {
+            client.setSoTimeout(1000);
+        }
+        client.setUseClientMode(clientClientMode);
+        client.startHandshake();
+        IOException ioe = future.get();
+        if (ioe != null) {
+            throw ioe;
+        }
+        client.close();
+        server.close();
+        c.close();
+    }
+
+    @Test
+    public void test_SSLSocket_clientAuth() throws Exception {
+        TestSSLContext c = new TestSSLContext.Builder()
+                .client(TestKeyStore.getClientCertificate())
+                .server(TestKeyStore.getServer())
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        SSLSocket client =
+                (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host, c.port);
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        Future<Void> future = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                assertFalse(server.getWantClientAuth());
+                assertFalse(server.getNeedClientAuth());
+                // confirm turning one on by itself
+                server.setWantClientAuth(true);
+                assertTrue(server.getWantClientAuth());
+                assertFalse(server.getNeedClientAuth());
+                // confirm turning setting on toggles the other
+                server.setNeedClientAuth(true);
+                assertFalse(server.getWantClientAuth());
+                assertTrue(server.getNeedClientAuth());
+                // confirm toggling back
+                server.setWantClientAuth(true);
+                assertTrue(server.getWantClientAuth());
+                assertFalse(server.getNeedClientAuth());
+                server.startHandshake();
+                return null;
+            }
+        });
+        client.startHandshake();
+        assertNotNull(client.getSession().getLocalCertificates());
+        TestKeyStore.assertChainLength(client.getSession().getLocalCertificates());
+        TestSSLContext.assertClientCertificateChain(
+                c.clientTrustManager, client.getSession().getLocalCertificates());
+        future.get();
+        client.close();
+        server.close();
+        c.close();
+    }
+
+    @Test
+    public void test_SSLSocket_clientAuth_bogusAlias() throws Exception {
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        SSLContext clientContext = SSLContext.getInstance(clientVersion);
+        X509KeyManager keyManager = new X509KeyManager() {
+            @Override
+            public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
+                return "bogus";
+            }
+            @Override
+            public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
+                throw new AssertionError();
+            }
+            @Override
+            public X509Certificate[] getCertificateChain(String alias) {
+                // return null for "bogus" alias
+                return null;
+            }
+            @Override
+            public String[] getClientAliases(String keyType, Principal[] issuers) {
+                throw new AssertionError();
+            }
+            @Override
+            public String[] getServerAliases(String keyType, Principal[] issuers) {
+                throw new AssertionError();
+            }
+            @Override
+            public PrivateKey getPrivateKey(String alias) {
+                // return null for "bogus" alias
+                return null;
+            }
+        };
+        clientContext.init(
+                new KeyManager[] {keyManager}, new TrustManager[] {c.clientTrustManager}, null);
+        SSLSocket client =
+                (SSLSocket) clientContext.getSocketFactory().createSocket(c.host, c.port);
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        Future<Void> future = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                try {
+                    server.setNeedClientAuth(true);
+                    server.startHandshake();
+                    fail();
+                } catch (SSLHandshakeException expected) {
+                    // Ignored.
+                }
+                return null;
+            }
+        });
+        try {
+            client.startHandshake();
+            // In TLS 1.3, the alert will only show up once we try to use the connection, since
+            // the client finishes the handshake without feedback from the server
+            client.getInputStream().read();
+            fail();
+        } catch (SSLException expected) {
+            // before we would get a NullPointerException from passing
+            // due to the null PrivateKey return by the X509KeyManager.
+        }
+        future.get();
+        client.close();
+        server.close();
+        c.close();
+    }
+
+    @Test
+    public void test_SSLSocket_clientAuth_OpaqueKey_RSA() throws Exception {
+        run_SSLSocket_clientAuth_OpaqueKey(TestKeyStore.getClientCertificate());
+    }
+
+    @Test
+    public void test_SSLSocket_clientAuth_OpaqueKey_EC_RSA() throws Exception {
+        run_SSLSocket_clientAuth_OpaqueKey(TestKeyStore.getClientEcRsaCertificate());
+    }
+
+    @Test
+    public void test_SSLSocket_clientAuth_OpaqueKey_EC_EC() throws Exception {
+        run_SSLSocket_clientAuth_OpaqueKey(TestKeyStore.getClientEcEcCertificate());
+    }
+    private void run_SSLSocket_clientAuth_OpaqueKey(TestKeyStore keyStore) throws Exception {
+        // OpaqueProvider will not be allowed to operate if the VM we're running on
+        // requires Oracle signatures for provider jars, since we don't sign the test jar.
+        TestUtils.assumeAllowsUnsignedCrypto();
+        try {
+            Security.insertProviderAt(new OpaqueProvider(), 1);
+            final TestSSLContext c = new TestSSLContext.Builder()
+                    .client(keyStore)
+                    .server(TestKeyStore.getServer())
+                    .clientProtocol(clientVersion)
+                    .serverProtocol(serverVersion)
+                    .build();
+            SSLContext clientContext = SSLContext.getInstance("TLS");
+            final X509KeyManager delegateKeyManager = (X509KeyManager) c.clientKeyManagers[0];
+            X509KeyManager keyManager = new X509KeyManager() {
+                @Override
+                public String chooseClientAlias(
+                        String[] keyType, Principal[] issuers, Socket socket) {
+                    return delegateKeyManager.chooseClientAlias(keyType, issuers, socket);
+                }
+                @Override
+                public String chooseServerAlias(
+                        String keyType, Principal[] issuers, Socket socket) {
+                    return delegateKeyManager.chooseServerAlias(keyType, issuers, socket);
+                }
+                @Override
+                public X509Certificate[] getCertificateChain(String alias) {
+                    return delegateKeyManager.getCertificateChain(alias);
+                }
+                @Override
+                public String[] getClientAliases(String keyType, Principal[] issuers) {
+                    return delegateKeyManager.getClientAliases(keyType, issuers);
+                }
+                @Override
+                public String[] getServerAliases(String keyType, Principal[] issuers) {
+                    return delegateKeyManager.getServerAliases(keyType, issuers);
+                }
+                @Override
+                public PrivateKey getPrivateKey(String alias) {
+                    PrivateKey privKey = delegateKeyManager.getPrivateKey(alias);
+                    return OpaqueProvider.wrapKey(privKey);
+                }
+            };
+            clientContext.init(
+                    new KeyManager[] {keyManager}, new TrustManager[] {c.clientTrustManager}, null);
+            SSLSocket client =
+                    (SSLSocket) clientContext.getSocketFactory().createSocket(c.host, c.port);
+            final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+            Future<Void> future = runAsync(new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    server.setNeedClientAuth(true);
+                    server.startHandshake();
+                    return null;
+                }
+            });
+            client.startHandshake();
+            assertNotNull(client.getSession().getLocalCertificates());
+            TestKeyStore.assertChainLength(client.getSession().getLocalCertificates());
+            TestSSLContext.assertClientCertificateChain(
+                    c.clientTrustManager, client.getSession().getLocalCertificates());
+            future.get();
+            client.close();
+            server.close();
+            c.close();
+        } finally {
+            Security.removeProvider(OpaqueProvider.NAME);
+        }
+    }
+
+    @Test
+    public void test_SSLSocket_TrustManagerRuntimeException() throws Exception {
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        SSLContext clientContext = SSLContext.getInstance("TLS");
+        X509TrustManager trustManager = new X509TrustManager() {
+            @Override
+            public void checkClientTrusted(X509Certificate[] chain, String authType)
+                    throws CertificateException {
+                throw new AssertionError();
+            }
+            @Override
+            public void checkServerTrusted(X509Certificate[] chain, String authType)
+                    throws CertificateException {
+                throw new RuntimeException(); // throw a RuntimeException from custom TrustManager
+            }
+            @Override
+            public X509Certificate[] getAcceptedIssuers() {
+                throw new AssertionError();
+            }
+        };
+        clientContext.init(null, new TrustManager[] {trustManager}, null);
+        SSLSocket client =
+                (SSLSocket) clientContext.getSocketFactory().createSocket(c.host, c.port);
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        Future<Void> future = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                try {
+                    server.startHandshake();
+                    fail();
+                } catch (SSLHandshakeException expected) {
+                    // Ignored.
+                }
+                return null;
+            }
+        });
+        try {
+            client.startHandshake();
+            fail();
+        } catch (SSLHandshakeException expected) {
+            // before we would get a RuntimeException from checkServerTrusted.
+        }
+        future.get();
+        client.close();
+        server.close();
+        c.close();
+    }
+
+    @Test
+    public void test_SSLSocket_getEnableSessionCreation() throws Exception {
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        SSLSocket client =
+                (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host, c.port);
+        SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        assertTrue(client.getEnableSessionCreation());
+        assertTrue(server.getEnableSessionCreation());
+        client.close();
+        server.close();
+        c.close();
+    }
+
+    @Test
+    public void test_SSLSocket_setEnableSessionCreation_server() throws Exception {
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        SSLSocket client =
+                (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host, c.port);
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        Future<Void> future = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                server.setEnableSessionCreation(false);
+                try {
+                    server.startHandshake();
+                    fail();
+                } catch (SSLException expected) {
+                    // Ignored.
+                }
+                return null;
+            }
+        });
+        try {
+            client.startHandshake();
+            fail();
+        } catch (SSLException expected) {
+            // Ignored.
+        }
+        future.get();
+        client.close();
+        server.close();
+        c.close();
+    }
+
+    @Test
+    public void test_SSLSocket_setEnableSessionCreation_client() throws Exception {
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        SSLSocket client =
+                (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host, c.port);
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        Future<Void> future = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                try {
+                    server.startHandshake();
+                    fail();
+                } catch (SSLException expected) {
+                    // Ignored.
+                }
+                return null;
+            }
+        });
+        client.setEnableSessionCreation(false);
+        try {
+            client.startHandshake();
+            fail();
+        } catch (SSLException expected) {
+            // Ignored.
+        }
+        future.get();
+        client.close();
+        server.close();
+        c.close();
+    }
+
+    @Test
+    public void test_SSLSocket_close() throws Exception {
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        TestSSLSocketPair pair = TestSSLSocketPair.create(c).connect();
+        SSLSocket server = pair.server;
+        SSLSocket client = pair.client;
+        assertFalse(server.isClosed());
+        assertFalse(client.isClosed());
+        InputStream input = client.getInputStream();
+        OutputStream output = client.getOutputStream();
+        server.close();
+        client.close();
+        assertTrue(server.isClosed());
+        assertTrue(client.isClosed());
+        // close after close is okay...
+        server.close();
+        client.close();
+        // ...so are a lot of other operations...
+        HandshakeCompletedListener l = new HandshakeCompletedListener() {
+            @Override
+            public void handshakeCompleted(HandshakeCompletedEvent e) {
+            }
+        };
+        client.addHandshakeCompletedListener(l);
+        assertNotNull(client.getEnabledCipherSuites());
+        assertNotNull(client.getEnabledProtocols());
+        client.getEnableSessionCreation();
+        client.getNeedClientAuth();
+        assertNotNull(client.getSession());
+        assertNotNull(client.getSSLParameters());
+        assertNotNull(client.getSupportedProtocols());
+        client.getUseClientMode();
+        client.getWantClientAuth();
+        client.removeHandshakeCompletedListener(l);
+        client.setEnabledCipherSuites(new String[0]);
+        client.setEnabledProtocols(new String[0]);
+        client.setEnableSessionCreation(false);
+        client.setNeedClientAuth(false);
+        client.setSSLParameters(client.getSSLParameters());
+        client.setWantClientAuth(false);
+        // ...but some operations are expected to give SocketException...
+        try {
+            client.startHandshake();
+            fail();
+        } catch (SocketException expected) {
+            // Ignored.
+        }
+        try {
+            client.getInputStream();
+            fail();
+        } catch (SocketException expected) {
+            // Ignored.
+        }
+        try {
+            client.getOutputStream();
+            fail();
+        } catch (SocketException expected) {
+            // Ignored.
+        }
+        try {
+            @SuppressWarnings("unused")
+            int value = input.read();
+            fail();
+        } catch (SocketException expected) {
+            // Ignored.
+        }
+        try {
+            @SuppressWarnings("unused")
+            int bytesRead = input.read(null, -1, -1);
+            fail();
+        } catch (NullPointerException expected) {
+            // Ignored.
+        } catch (SocketException expected) {
+            // Ignored.
+        }
+        try {
+            output.write(-1);
+            fail();
+        } catch (SocketException expected) {
+            // Ignored.
+        }
+        try {
+            output.write(null, -1, -1);
+            fail();
+        } catch (NullPointerException expected) {
+            // Ignored.
+        } catch (SocketException expected) {
+            // Ignored.
+        }
+        // ... and one gives IllegalArgumentException
+        try {
+            client.setUseClientMode(false);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+        pair.close();
+    }
+    /**
+     * b/3350645 Test to confirm that an SSLSocket.close() performing
+     * an SSL_shutdown does not throw an IOException if the peer
+     * socket has been closed.
+     */
+    @Test
+    public void test_SSLSocket_shutdownCloseOnClosedPeer() throws Exception {
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        final Socket underlying = new Socket(c.host, c.port);
+        final SSLSocket wrapping = (SSLSocket) c.clientContext.getSocketFactory().createSocket(
+                underlying, c.host.getHostName(), c.port, false);
+        Future<Void> clientFuture = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                wrapping.startHandshake();
+                wrapping.getOutputStream().write(42);
+                // close the underlying socket,
+                // so that no SSL shutdown is sent
+                underlying.close();
+                wrapping.close();
+                return null;
+            }
+        });
+        SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        server.startHandshake();
+        @SuppressWarnings("unused")
+        int value = server.getInputStream().read();
+        // wait for thread to finish so we know client is closed.
+        clientFuture.get();
+        // close should cause an SSL_shutdown which will fail
+        // because the peer has closed, but it shouldn't throw.
+        server.close();
+    }
+
+    @Test
+    public void test_SSLSocket_endpointIdentification_Success() throws Exception {
+        TestUtils.assumeSetEndpointIdentificationAlgorithmAvailable();
+        // The default hostname verifier on OpenJDK just rejects all hostnames,
+        // which is not helpful, so replace with a basic functional one.
+        HostnameVerifier oldDefault = HttpsURLConnection.getDefaultHostnameVerifier();
+        HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier());
+        try {
+            final TestSSLContext c = new TestSSLContext.Builder()
+                    .clientProtocol(clientVersion)
+                    .serverProtocol(serverVersion)
+                    .build();
+            SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket();
+            SSLParameters p = client.getSSLParameters();
+            p.setEndpointIdentificationAlgorithm("HTTPS");
+            client.setSSLParameters(p);
+            client.connect(new InetSocketAddress(c.host, c.port));
+            final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+            Future<Void> future = runAsync(new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    server.startHandshake();
+                    assertNotNull(server.getSession());
+                    try {
+                        server.getSession().getPeerCertificates();
+                        fail();
+                    } catch (SSLPeerUnverifiedException expected) {
+                        // Ignored.
+                    }
+                    Certificate[] localCertificates = server.getSession().getLocalCertificates();
+                    assertNotNull(localCertificates);
+                    TestKeyStore.assertChainLength(localCertificates);
+                    assertNotNull(localCertificates[0]);
+                    TestSSLContext
+                            .assertCertificateInKeyStore(localCertificates[0], c.serverKeyStore);
+                    return null;
+                }
+            });
+            client.startHandshake();
+            assertNotNull(client.getSession());
+            assertNull(client.getSession().getLocalCertificates());
+            Certificate[] peerCertificates = client.getSession().getPeerCertificates();
+            assertNotNull(peerCertificates);
+            TestKeyStore.assertChainLength(peerCertificates);
+            assertNotNull(peerCertificates[0]);
+            TestSSLContext.assertCertificateInKeyStore(peerCertificates[0], c.serverKeyStore);
+            future.get();
+            client.close();
+            server.close();
+            c.close();
+        } finally {
+            HttpsURLConnection.setDefaultHostnameVerifier(oldDefault);
+        }
+    }
+
+    @Test
+    public void test_SSLSocket_endpointIdentification_Failure() throws Exception {
+        TestUtils.assumeSetEndpointIdentificationAlgorithmAvailable();
+        // The default hostname verifier on OpenJDK just rejects all hostnames,
+        // which is not helpful, so replace with a basic functional one.
+        HostnameVerifier oldDefault = HttpsURLConnection.getDefaultHostnameVerifier();
+        HttpsURLConnection.setDefaultHostnameVerifier(new TestHostnameVerifier());
+        try {
+            final TestSSLContext c = new TestSSLContext.Builder()
+                    .clientProtocol(clientVersion)
+                    .serverProtocol(serverVersion)
+                    .build();
+            SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket();
+            SSLParameters p = client.getSSLParameters();
+            p.setEndpointIdentificationAlgorithm("HTTPS");
+            client.setSSLParameters(p);
+            client.connect(c.getLoopbackAsHostname("unmatched.example.com", c.port));
+            final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+            Future<Void> future = runAsync(new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    try {
+                        server.startHandshake();
+                        fail("Should receive SSLHandshakeException as server");
+                    } catch (SSLHandshakeException expected) {
+                        // Ignored.
+                    }
+                    return null;
+                }
+            });
+            try {
+                client.startHandshake();
+                fail("Should throw when hostname does not match expected");
+            } catch (SSLHandshakeException expected) {
+                // Ignored.
+            } finally {
+                try {
+                    future.get();
+                } finally {
+                    client.close();
+                    server.close();
+                    c.close();
+                }
+            }
+        } finally {
+            HttpsURLConnection.setDefaultHostnameVerifier(oldDefault);
+        }
+    }
+
+    @Test(expected = SocketTimeoutException.class)
+    public void test_SSLSocket_setSoWriteTimeout() throws Exception {
+        // Only run this test on Linux since it relies on non-posix methods.
+        assumeTrue("Test only runs on Linux. Current OS: " + osName(), isLinux());
+
+        // In jb-mr2 it was found that we need to also set SO_RCVBUF
+        // to a minimal size or the write would not block.
+        final int receiveBufferSize = 128;
+        TestSSLContext c = TestSSLContext.newBuilder()
+                .serverReceiveBufferSize(receiveBufferSize)
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+
+        final SSLSocket client =
+                (SSLSocket) c.clientContext.getSocketFactory().createSocket(c.host, c.port);
+
+        // Try to make the client SO_SNDBUF size as small as possible
+        // (it can default to 512k or even megabytes).  Note that
+        // socket(7) says that the kernel will double the request to
+        // leave room for its own book keeping and that the minimal
+        // value will be 2048. Also note that tcp(7) says the value
+        // needs to be set before connect(2).
+        int sendBufferSize = 1024;
+        client.setSendBufferSize(sendBufferSize);
+        sendBufferSize = client.getSendBufferSize();
+
+        // Start the handshake.
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        Future<Void> future = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                client.startHandshake();
+                return null;
+            }
+        });
+        server.startHandshake();
+
+        assertTrue(isConscryptSocket(client));
+        // The concrete class that Conscrypt returns has methods on it that have no
+        // equivalent on the public API (like setSoWriteTimeout), so users have
+        // previously used reflection to access those otherwise-inaccessible methods
+        // on that class.  The concrete class used to be named OpenSSLSocketImpl, so
+        // check that OpenSSLSocketImpl is still in the class hierarchy so applications
+        // that rely on getting that class back still work.
+        Class<?> superClass = client.getClass();
+        do {
+            superClass = superClass.getSuperclass();
+        } while (superClass != Object.class && !superClass.getName().endsWith("OpenSSLSocketImpl"));
+        assertEquals("OpenSSLSocketImpl", superClass.getSimpleName());
+
+
+        try {
+            setWriteTimeout(client, 1);
+
+            // Add extra space to the write to exceed the send buffer
+            // size and cause the write to block.
+            final int extra = 1;
+            client.getOutputStream().write(new byte[sendBufferSize + extra]);
+        } finally {
+            future.get();
+            client.close();
+            server.close();
+            c.close();
+        }
+    }
+
+    @Test
+    public void test_SSLSocket_reusedNpnSocket() throws Exception {
+        byte[] npnProtocols = new byte[] {
+                8, 'h', 't', 't', 'p', '/', '1', '.', '1'
+        };
+
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket();
+
+        assertTrue(isConscryptSocket(client));
+        Class<?> actualClass = client.getClass();
+        Method setNpnProtocols = actualClass.getMethod("setNpnProtocols", byte[].class);
+
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+
+        // First connection with NPN set on client and server
+        {
+            setNpnProtocols.invoke(client, npnProtocols);
+            client.connect(new InetSocketAddress(c.host, c.port));
+
+            final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+            assertTrue(isConscryptSocket(server));
+            setNpnProtocols.invoke(server, npnProtocols);
+
+            Future<Void> future = executor.submit(new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    server.startHandshake();
+                    return null;
+                }
+            });
+            client.startHandshake();
+
+            future.get();
+            client.close();
+            server.close();
+        }
+
+        // Second connection with client NPN already set on the SSL context, but
+        // without server NPN set.
+        {
+            SSLServerSocket serverSocket = (SSLServerSocket) c.serverContext
+                    .getServerSocketFactory().createServerSocket(0);
+            InetAddress host = InetAddress.getLocalHost();
+            int port = serverSocket.getLocalPort();
+
+            client = (SSLSocket) c.clientContext.getSocketFactory().createSocket();
+            client.connect(new InetSocketAddress(host, port));
+
+            final SSLSocket server = (SSLSocket) serverSocket.accept();
+
+            Future<Void> future = executor.submit(new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    server.startHandshake();
+                    return null;
+                }
+            });
+            client.startHandshake();
+
+            future.get();
+            client.close();
+            server.close();
+            serverSocket.close();
+        }
+
+        c.close();
+    }
+
+    // TODO(nmittler): Conscrypt socket read may return -1 instead of SocketException.
+    @Test
+    public void test_SSLSocket_interrupt_readUnderlyingAndCloseUnderlying() throws Exception {
+        test_SSLSocket_interrupt_case(true, true);
+    }
+
+    // TODO(nmittler): Conscrypt socket read may return -1 instead of SocketException.
+    @Test
+    public void test_SSLSocket_interrupt_readUnderlyingAndCloseWrapper() throws Exception {
+        test_SSLSocket_interrupt_case(true, false);
+    }
+
+    // TODO(nmittler): FD socket gets stuck in read on Windows and OSX.
+    @Test
+    public void test_SSLSocket_interrupt_readWrapperAndCloseUnderlying() throws Exception {
+        test_SSLSocket_interrupt_case(false, true);
+    }
+
+    // TODO(nmittler): Conscrypt socket read may return -1 instead of SocketException.
+    @Test
+    public void test_SSLSocket_interrupt_readWrapperAndCloseWrapper() throws Exception {
+        test_SSLSocket_interrupt_case(false, false);
+    }
+
+    private void test_SSLSocket_interrupt_case(boolean readUnderlying, boolean closeUnderlying)
+            throws Exception {
+        final int readingTimeoutMillis = 5000;
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        final Socket underlying = new Socket(c.host, c.port);
+        final SSLSocket clientWrapping =
+                (SSLSocket) c.clientContext.getSocketFactory().createSocket(
+                        underlying, c.host.getHostName(), c.port, true);
+
+        if (isConscryptFdSocket(clientWrapping) && !readUnderlying && closeUnderlying) {
+            // TODO(nmittler): FD socket gets stuck in the read on Windows and OSX.
+            assumeFalse("Skipping interrupt test on Windows", isWindows());
+            assumeFalse("Skipping interrupt test on OSX", isOsx());
+        }
+
+        SSLSocket server = (SSLSocket) c.serverSocket.accept();
+
+        // Start the handshake.
+        Future<Integer> handshakeFuture = runAsync(new Callable<Integer>() {
+            @Override
+            public Integer call() throws Exception {
+                clientWrapping.startHandshake();
+                return clientWrapping.getInputStream().read();
+            }
+        });
+        server.startHandshake();
+        // TLS 1.3 sends some post-handshake management messages, so send a single byte through
+        // to process through those messages.
+        server.getOutputStream().write(42);
+        assertEquals(42, handshakeFuture.get().intValue());
+
+        final Socket toRead = readUnderlying ? underlying : clientWrapping;
+        final Socket toClose = closeUnderlying ? underlying : clientWrapping;
+
+        // Schedule the socket to be closed in 1 second.
+        Future<Void> future = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                Thread.sleep(1000);
+                toClose.close();
+                return null;
+            }
+        });
+
+        // Read from the socket.
+        try {
+            toRead.setSoTimeout(readingTimeoutMillis);
+            int read = toRead.getInputStream().read();
+            // In the case of reading the wrapper and closing the underlying socket,
+            // there is a race condition between the reading thread being woken and
+            // reading the socket again and the closing thread marking the file descriptor
+            // as invalid.  If the latter happens first, a SocketException is thrown,
+            // but if the former happens first it just looks like the peer closed the
+            // connection and a -1 return is acceptable.
+            if (read != -1 || readUnderlying || !closeUnderlying) {
+                fail();
+            }
+        } catch (SocketTimeoutException e) {
+            throw e;
+        } catch (IOException expected) {
+            // Expected
+        }
+
+        future.get();
+        server.close();
+        underlying.close();
+        server.close();
+    }
+
+    /**
+     * b/7014266 Test to confirm that an SSLSocket.close() on one
+     * thread will interrupt another thread blocked reading on the same
+     * socket.
+     */
+    // TODO(nmittler): Interrupts do not work with the engine-based socket.
+    @Test
+    public void test_SSLSocket_interrupt_read_withoutAutoClose() throws Exception {
+        final int readingTimeoutMillis = 5000;
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        final Socket underlying = new Socket(c.host, c.port);
+        final SSLSocket wrapping = (SSLSocket) c.clientContext.getSocketFactory().createSocket(
+                underlying, c.host.getHostName(), c.port, false);
+
+        // TODO(nmittler): Interrupts do not work with the engine-based socket.
+        assumeFalse(isConscryptEngineSocket(wrapping));
+
+        Future<Void> clientFuture = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                wrapping.startHandshake();
+                try {
+                    wrapping.setSoTimeout(readingTimeoutMillis);
+                    wrapping.getInputStream().read();
+                    fail();
+                } catch (SocketException expected) {
+                    // Conscrypt throws an exception complaining that the socket is closed
+                    // if it's interrupted by a close() in the middle of a read()
+                    assertTrue(expected.getMessage().contains("closed"));
+                }
+                return null;
+            }
+        });
+        SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        server.startHandshake();
+
+        // Wait for the client to at least be in the "read" method before calling close()
+        Thread[] threads = new Thread[1];
+        threadGroup.enumerate(threads);
+        if (threads[0] != null) {
+            boolean clientInRead = false;
+            while (!clientInRead) {
+                StackTraceElement[] elements = threads[0].getStackTrace();
+                for (StackTraceElement element : elements) {
+                    if ("read".equals(element.getMethodName())) {
+                        // The client might be executing "read" but still not have reached the
+                        // point in which it's blocked reading. This is causing flakiness
+                        // (b/24367646). Delaying for a fraction of the timeout.
+                        Thread.sleep(1000);
+                        clientInRead = true;
+                        break;
+                    }
+                }
+            }
+        }
+
+        wrapping.close();
+
+        clientFuture.get();
+        server.close();
+    }
+
+    @Test
+    public void test_SSLSocket_ClientHello_record_size() throws Exception {
+        // This test checks the size of ClientHello of the default SSLSocket. TLS/SSL handshakes
+        // with older/unpatched F5/BIG-IP appliances are known to stall and time out when
+        // the fragment containing ClientHello is between 256 and 511 (inclusive) bytes long.
+        SSLContext sslContext = SSLContext.getInstance(clientVersion);
+        sslContext.init(null, null, null);
+        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+        sslSocketFactory = new DelegatingSSLSocketFactory(sslSocketFactory) {
+            @Override
+            protected SSLSocket configureSocket(SSLSocket socket) {
+                // Enable SNI extension on the socket (this is typically enabled by default)
+                // to increase the size of ClientHello.
+                setHostname(socket);
+
+                // Enable Session Tickets extension on the socket (this is typically enabled
+                // by default) to increase the size of ClientHello.
+                enableSessionTickets(socket);
+                return socket;
+            }
+        };
+        TlsRecord firstReceivedTlsRecord = TlsTester.captureTlsHandshakeFirstTlsRecord(executor, sslSocketFactory);
+        assertEquals("TLS record type", TlsProtocols.HANDSHAKE, firstReceivedTlsRecord.type);
+        HandshakeMessage handshakeMessage = HandshakeMessage.read(
+                new DataInputStream(new ByteArrayInputStream(firstReceivedTlsRecord.fragment)));
+        assertEquals(
+                "HandshakeMessage type", HandshakeMessage.TYPE_CLIENT_HELLO, handshakeMessage.type);
+        int fragmentLength = firstReceivedTlsRecord.fragment.length;
+        if ((fragmentLength >= 256) && (fragmentLength <= 511)) {
+            fail("Fragment containing ClientHello is of dangerous length: " + fragmentLength
+                    + " bytes");
+        }
+    }
+
+    @Test
+    public void test_SSLSocket_ClientHello_SNI() throws Exception {
+        ForEachRunner.runNamed(new Callback<SSLSocketFactory>() {
+            @Override
+            public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+                ClientHello clientHello = TlsTester
+                    .captureTlsHandshakeClientHello(executor, sslSocketFactory);
+                ServerNameHelloExtension sniExtension =
+                    (ServerNameHelloExtension) clientHello.findExtensionByType(
+                        HelloExtension.TYPE_SERVER_NAME);
+                assertNotNull(sniExtension);
+                assertEquals(
+                    Collections.singletonList("localhost.localdomain"), sniExtension.hostnames);
+            }
+        }, getSSLSocketFactoriesToTest());
+    }
+
+    @Test
+    public void test_SSLSocket_ClientHello_ALPN() throws Exception {
+        final String[] protocolList = new String[] { "h2", "http/1.1" };
+        
+        ForEachRunner.runNamed(new Callback<SSLSocketFactory>() {
+            @Override
+            public void run(SSLSocketFactory sslSocketFactory) throws Exception {
+                ClientHello clientHello = TlsTester.captureTlsHandshakeClientHello(executor,
+                        new DelegatingSSLSocketFactory(sslSocketFactory) {
+                            @Override public SSLSocket configureSocket(SSLSocket socket) {
+                                Conscrypt.setApplicationProtocols(socket, protocolList);
+                                return socket;
+                            }
+                        });
+                AlpnHelloExtension alpnExtension =
+                        (AlpnHelloExtension) clientHello.findExtensionByType(
+                                HelloExtension.TYPE_APPLICATION_LAYER_PROTOCOL_NEGOTIATION);
+                assertNotNull(alpnExtension);
+                assertEquals(Arrays.asList(protocolList), alpnExtension.protocols);
+            }
+        }, getSSLSocketFactoriesToTest());
+    }
+
+    private List<Pair<String, SSLSocketFactory>> getSSLSocketFactoriesToTest()
+            throws NoSuchAlgorithmException, KeyManagementException {
+        List<Pair<String, SSLSocketFactory>> result =
+                new ArrayList<Pair<String, SSLSocketFactory>>();
+        result.add(Pair.of("default", (SSLSocketFactory) SSLSocketFactory.getDefault()));
+        for (String sslContextProtocol : StandardNames.SSL_CONTEXT_PROTOCOLS) {
+            SSLContext sslContext = SSLContext.getInstance(sslContextProtocol);
+            if (StandardNames.SSL_CONTEXT_PROTOCOLS_DEFAULT.equals(sslContextProtocol)) {
+                continue;
+            }
+            sslContext.init(null, null, null);
+            result.add(Pair.of("SSLContext(\"" + sslContext.getProtocol() + "\")",
+                    sslContext.getSocketFactory()));
+        }
+        return result;
+    }
+
+    // http://b/18428603
+    @Test
+    public void test_SSLSocket_getPortWithSNI() throws Exception {
+        TestSSLContext context = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        SSLSocket client =
+            (SSLSocket) context.clientContext.getSocketFactory().createSocket();
+        try {
+            client.connect(new InetSocketAddress(context.host, context.port));
+            setHostname(client);
+            assertTrue(client.getPort() > 0);
+        } finally {
+            client.close();
+            context.close();
+        }
+    }
+
+    @Test
+    public void test_SSLSocket_SNIHostName() throws Exception {
+        TestUtils.assumeSNIHostnameAvailable();
+        final TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        final SSLSocket client = (SSLSocket) c.clientContext.getSocketFactory().createSocket();
+        SSLParameters clientParams = client.getSSLParameters();
+        clientParams.setServerNames(
+                Collections.singletonList((SNIServerName) new SNIHostName("www.example.com")));
+        client.setSSLParameters(clientParams);
+        SSLParameters serverParams = c.serverSocket.getSSLParameters();
+        serverParams.setSNIMatchers(
+                Collections.singletonList(SNIHostName.createSNIMatcher("www\\.example\\.com")));
+        c.serverSocket.setSSLParameters(serverParams);
+        client.connect(new InetSocketAddress(c.host, c.port));
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        @SuppressWarnings("unused")
+        Future<?> future = runAsync(new Callable<Object>() {
+            @Override
+            public Object call() throws Exception {
+                client.startHandshake();
+                return null;
+            }
+        });
+        server.startHandshake();
+        SSLSession serverSession = server.getSession();
+        assertTrue(serverSession instanceof ExtendedSSLSession);
+        ExtendedSSLSession extendedServerSession = (ExtendedSSLSession) serverSession;
+        List<SNIServerName> requestedNames = extendedServerSession.getRequestedServerNames();
+        assertNotNull(requestedNames);
+        assertEquals(1, requestedNames.size());
+        SNIServerName serverName = requestedNames.get(0);
+        assertEquals(StandardConstants.SNI_HOST_NAME, serverName.getType());
+        assertTrue(serverName instanceof SNIHostName);
+        SNIHostName serverHostName = (SNIHostName) serverName;
+        assertEquals("www.example.com", serverHostName.getAsciiName());
+    }
+
+    @Test
+    public void test_SSLSocket_ClientGetsAlertDuringHandshake_HasGoodExceptionMessage()
+            throws Exception {
+        TestSSLContext context = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        final ServerSocket listener = ServerSocketFactory.getDefault().createServerSocket(0);
+        final SSLSocket client = (SSLSocket) context.clientContext.getSocketFactory().createSocket(
+                context.host, listener.getLocalPort());
+        final Socket server = listener.accept();
+        Future<Void> c = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                try {
+                    client.startHandshake();
+                    fail("Should receive handshake exception");
+                } catch (SSLHandshakeException expected) {
+                    assertFalse(expected.getMessage().contains("SSL_ERROR_ZERO_RETURN"));
+                    assertFalse(expected.getMessage().contains("You should never see this."));
+                }
+                return null;
+            }
+        });
+        Future<Void> s = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                // Wait until the client sends something.
+                byte[] scratch = new byte[8192];
+                @SuppressWarnings("unused")
+                int bytesRead = server.getInputStream().read(scratch);
+                // Write a bogus TLS alert:
+                // TLSv1.2 Record Layer: Alert (Level: Warning, Description: Protocol Version)
+                server.getOutputStream()
+                    .write(new byte[]{0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x46});
+                // TLSv1.2 Record Layer: Alert (Level: Warning, Description: Close Notify)
+                server.getOutputStream()
+                    .write(new byte[]{0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x00});
+                return null;
+            }
+        });
+        c.get(5, TimeUnit.SECONDS);
+        s.get(5, TimeUnit.SECONDS);
+        client.close();
+        server.close();
+        listener.close();
+        context.close();
+    }
+
+    @Test
+    public void test_SSLSocket_ServerGetsAlertDuringHandshake_HasGoodExceptionMessage()
+            throws Exception {
+        TestSSLContext context = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        final Socket client = SocketFactory.getDefault().createSocket(context.host, context.port);
+        final SSLSocket server = (SSLSocket) context.serverSocket.accept();
+        Future<Void> s = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                try {
+                    server.startHandshake();
+                    fail("Should receive handshake exception");
+                } catch (SSLHandshakeException expected) {
+                    assertFalse(expected.getMessage().contains("SSL_ERROR_ZERO_RETURN"));
+                    assertFalse(expected.getMessage().contains("You should never see this."));
+                }
+                return null;
+            }
+        });
+        Future<Void> c = runAsync(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                // Send bogus ClientHello:
+                // TLSv1.2 Record Layer: Handshake Protocol: Client Hello
+                client.getOutputStream().write(new byte[]{
+                    (byte) 0x16, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0xb9,
+                    (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0xb5, (byte) 0x03,
+                    (byte) 0x03, (byte) 0x5a, (byte) 0x31, (byte) 0xba, (byte) 0x44,
+                    (byte) 0x24, (byte) 0xfd, (byte) 0xf0, (byte) 0x56, (byte) 0x46,
+                    (byte) 0xea, (byte) 0xee, (byte) 0x1c, (byte) 0x62, (byte) 0x8f,
+                    (byte) 0x18, (byte) 0x04, (byte) 0xbd, (byte) 0x1c, (byte) 0xbc,
+                    (byte) 0xbf, (byte) 0x6d, (byte) 0x84, (byte) 0x12, (byte) 0xe9,
+                    (byte) 0x94, (byte) 0xf5, (byte) 0x1c, (byte) 0x15, (byte) 0x3e,
+                    (byte) 0x79, (byte) 0x01, (byte) 0xe2, (byte) 0x00, (byte) 0x00,
+                    (byte) 0x28, (byte) 0xc0, (byte) 0x2b, (byte) 0xc0, (byte) 0x2c,
+                    (byte) 0xc0, (byte) 0x2f, (byte) 0xc0, (byte) 0x30, (byte) 0x00,
+                    (byte) 0x9e, (byte) 0x00, (byte) 0x9f, (byte) 0xc0, (byte) 0x09,
+                    (byte) 0xc0, (byte) 0x0a, (byte) 0xc0, (byte) 0x13, (byte) 0xc0,
+                    (byte) 0x14, (byte) 0x00, (byte) 0x33, (byte) 0x00, (byte) 0x39,
+                    (byte) 0xc0, (byte) 0x07, (byte) 0xc0, (byte) 0x11, (byte) 0x00,
+                    (byte) 0x9c, (byte) 0x00, (byte) 0x9d, (byte) 0x00, (byte) 0x2f,
+                    (byte) 0x00, (byte) 0x35, (byte) 0x00, (byte) 0x05, (byte) 0x00,
+                    (byte) 0xff, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x64,
+                    (byte) 0x00, (byte) 0x0b, (byte) 0x00, (byte) 0x04, (byte) 0x03,
+                    (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x00, (byte) 0x0a,
+                    (byte) 0x00, (byte) 0x34, (byte) 0x00, (byte) 0x32, (byte) 0x00,
+                    (byte) 0x0e, (byte) 0x00, (byte) 0x0d, (byte) 0x00, (byte) 0x19,
+                    (byte) 0x00, (byte) 0x0b, (byte) 0x00, (byte) 0x0c, (byte) 0x00,
+                    (byte) 0x18, (byte) 0x00, (byte) 0x09, (byte) 0x00, (byte) 0x0a,
+                    (byte) 0x00, (byte) 0x16, (byte) 0x00, (byte) 0x17, (byte) 0x00,
+                    (byte) 0x08, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x07,
+                    (byte) 0x00, (byte) 0x14, (byte) 0x00, (byte) 0x15, (byte) 0x00,
+                    (byte) 0x04, (byte) 0x00, (byte) 0x05, (byte) 0x00, (byte) 0x12,
+                    (byte) 0x00, (byte) 0x13, (byte) 0x00, (byte) 0x01, (byte) 0x00,
+                    (byte) 0x02, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x0f,
+                    (byte) 0x00, (byte) 0x10, (byte) 0x00, (byte) 0x11, (byte) 0x00,
+                    (byte) 0x0d, (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x1e,
+                    (byte) 0x06, (byte) 0x01, (byte) 0x06, (byte) 0x02, (byte) 0x06,
+                    (byte) 0x03, (byte) 0x05, (byte) 0x01, (byte) 0x05, (byte) 0x02,
+                    (byte) 0x05, (byte) 0x03, (byte) 0x04, (byte) 0x01, (byte) 0x04,
+                    (byte) 0x02, (byte) 0x04, (byte) 0x03, (byte) 0x03, (byte) 0x01,
+                    (byte) 0x03, (byte) 0x02, (byte) 0x03, (byte) 0x03, (byte) 0x02,
+                    (byte) 0x01, (byte) 0x02, (byte) 0x02, (byte) 0x02, (byte) 0x03,
+                });
+                // Wait until the server sends something.
+                byte[] scratch = new byte[8192];
+                @SuppressWarnings("unused")
+                int bytesRead = client.getInputStream().read(scratch);
+                // Write a bogus TLS alert:
+                // TLSv1.2 Record Layer: Alert (Level: Warning, Description:
+                // Protocol Version)
+                client.getOutputStream()
+                    .write(new byte[]{0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x46});
+                // TLSv1.2 Record Layer: Alert (Level: Warning, Description:
+                // Close Notify)
+                client.getOutputStream()
+                    .write(new byte[]{0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x00});
+                return null;
+            }
+        });
+        c.get(5, TimeUnit.SECONDS);
+        s.get(5, TimeUnit.SECONDS);
+        client.close();
+        server.close();
+        context.close();
+    }
+
+    @Test
+    public void test_SSLSocket_SSLv3Unsupported() throws Exception {
+        TestSSLContext context = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        final SSLSocket client =
+                (SSLSocket) context.clientContext.getSocketFactory().createSocket();
+        // For app compatibility, SSLv3 is stripped out when setting only.
+        client.setEnabledProtocols(new String[] {"SSLv3"});
+        assertEquals(0, client.getEnabledProtocols().length);
+        try {
+            client.setEnabledProtocols(new String[] {"SSL"});
+            fail("SSLSocket should not support SSL protocol");
+        } catch (IllegalArgumentException expected) {
+            // Ignored.
+        }
+    }
+
+    // Under some circumstances, the file descriptor socket may get finalized but still
+    // be reused by the JDK's built-in HTTP connection reuse code.  Ensure that a
+    // SocketException is thrown if that happens.
+    @Test
+    public void test_SSLSocket_finalizeThrowsProperException() throws Exception {
+        TestSSLContext context = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        TestSSLSocketPair test = TestSSLSocketPair.create(context).connect();
+        try {
+            if (isConscryptFdSocket(test.client)) {
+                // The finalize method might be declared on a superclass rather than this
+                // class.
+                Method method = null;
+                Class<?> clazz = test.client.getClass();
+                while (clazz != null) {
+                    try {
+                        method = clazz.getDeclaredMethod("finalize");
+                        break;
+                    } catch (NoSuchMethodException e) {
+                        // Try the superclass
+                    }
+                    clazz = clazz.getSuperclass();
+                }
+                assertNotNull(method);
+                method.setAccessible(true);
+                method.invoke(test.client);
+                try {
+                    test.client.getOutputStream().write(new byte[] { 0x01 });
+                    fail("The socket shouldn't work after being finalized");
+                } catch (SocketException expected) {
+                    // Expected
+                }
+            }
+        } finally {
+            test.close();
+        }
+    }
+
+    @Test
+    public void test_SSLSocket_TlsUnique() throws Exception {
+        // tls_unique isn't supported in TLS 1.3
+        assumeTlsV1_2Connection();
+        TestSSLContext context = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        TestSSLSocketPair pair = TestSSLSocketPair.create(context);
+        try {
+            assertNull(Conscrypt.getTlsUnique(pair.client));
+            assertNull(Conscrypt.getTlsUnique(pair.server));
+
+            pair.connect();
+
+            byte[] clientTlsUnique = Conscrypt.getTlsUnique(pair.client);
+            byte[] serverTlsUnique = Conscrypt.getTlsUnique(pair.server);
+            assertNotNull(clientTlsUnique);
+            assertNotNull(serverTlsUnique);
+            assertArrayEquals(clientTlsUnique, serverTlsUnique);
+        } finally {
+            pair.close();
+        }
+    }
+
+    // Tests that all cipher suites have a 12-byte tls-unique channel binding value.  If this
+    // test fails, that means some cipher suite has been added that uses a customized verify_data
+    // length and we need to update MAX_TLS_UNIQUE_LENGTH in native_crypto.cc to account for that.
+    @Test
+    public void test_SSLSocket_TlsUniqueLength() throws Exception {
+        // tls_unique isn't supported in TLS 1.3
+        assumeTlsV1_2Connection();
+        // note the rare usage of non-RSA keys
+        TestKeyStore testKeyStore = new TestKeyStore.Builder()
+                .keyAlgorithms("RSA", "DSA", "EC", "EC_RSA")
+                .aliasPrefix("rsa-dsa-ec")
+                .ca(true)
+                .build();
+        KeyManager pskKeyManager =
+                PSKKeyManagerProxy.getConscryptPSKKeyManager(new PSKKeyManagerProxy() {
+                    @Override
+                    protected SecretKey getKey(
+                            String identityHint, String identity, Socket socket) {
+                        return newKey();
+                    }
+
+                    @Override
+                    protected SecretKey getKey(
+                            String identityHint, String identity, SSLEngine engine) {
+                        return newKey();
+                    }
+
+                    private SecretKey newKey() {
+                        return new SecretKeySpec("Just an arbitrary key".getBytes(UTF_8), "RAW");
+                    }
+                });
+        TestSSLContext c = TestSSLContext.newBuilder()
+                .client(testKeyStore)
+                .server(testKeyStore)
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .additionalClientKeyManagers(new KeyManager[] {pskKeyManager})
+                .additionalServerKeyManagers(new KeyManager[] {pskKeyManager})
+                .build();
+        for (String cipherSuite : c.clientContext.getSocketFactory().getSupportedCipherSuites()) {
+            if (cipherSuite.equals(StandardNames.CIPHER_SUITE_FALLBACK)
+                    || cipherSuite.equals(StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION)) {
+                continue;
+            }
+            /*
+             * tls_unique only works on 1.2, so skip TLS 1.3 cipher suites.
+             */
+            if (StandardNames.CIPHER_SUITES_TLS13.contains(cipherSuite)) {
+                continue;
+            }
+            TestSSLSocketPair pair = TestSSLSocketPair.create(c);
+            try {
+                String[] cipherSuites =
+                        new String[] {cipherSuite, StandardNames.CIPHER_SUITE_SECURE_RENEGOTIATION};
+                pair.connect(cipherSuites, cipherSuites);
+
+                assertEquals(cipherSuite, pair.client.getSession().getCipherSuite());
+
+                byte[] clientTlsUnique = Conscrypt.getTlsUnique(pair.client);
+                byte[] serverTlsUnique = Conscrypt.getTlsUnique(pair.server);
+                assertNotNull(clientTlsUnique);
+                assertNotNull(serverTlsUnique);
+                assertArrayEquals(clientTlsUnique, serverTlsUnique);
+                assertEquals(12, clientTlsUnique.length);
+            } catch (Exception e) {
+                throw new AssertionError("Cipher suite is " + cipherSuite, e);
+            } finally {
+                pair.client.close();
+                pair.server.close();
+            }
+        }
+    }
+
+    @Test
+    public void test_SSLSocket_EKM() throws Exception {
+        TestSSLContext context = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        TestSSLSocketPair pair = TestSSLSocketPair.create(context);
+        try {
+            // No EKM values available before handshaking
+            assertNull(Conscrypt.exportKeyingMaterial(pair.client, "FOO", null, 20));
+            assertNull(Conscrypt.exportKeyingMaterial(pair.server, "FOO", null, 20));
+
+            pair.connect();
+
+            byte[] clientEkm = Conscrypt.exportKeyingMaterial(pair.client, "FOO", null, 20);
+            byte[] serverEkm = Conscrypt.exportKeyingMaterial(pair.server, "FOO", null, 20);
+            assertNotNull(clientEkm);
+            assertNotNull(serverEkm);
+            assertEquals(20, clientEkm.length);
+            assertEquals(20, serverEkm.length);
+            assertArrayEquals(clientEkm, serverEkm);
+
+            byte[] clientContextEkm = Conscrypt.exportKeyingMaterial(
+                    pair.client, "FOO", new byte[0], 20);
+            byte[] serverContextEkm = Conscrypt.exportKeyingMaterial(
+                    pair.server, "FOO", new byte[0], 20);
+            assertNotNull(clientContextEkm);
+            assertNotNull(serverContextEkm);
+            assertEquals(20, clientContextEkm.length);
+            assertEquals(20, serverContextEkm.length);
+            assertArrayEquals(clientContextEkm, serverContextEkm);
+
+            // In TLS 1.2, an empty context and a null context are different (RFC 5705, section 4),
+            // but in TLS 1.3 they are the same (RFC 8446, section 7.5).
+            if ("TLSv1.2".equals(negotiatedVersion())) {
+                assertFalse(Arrays.equals(clientEkm, clientContextEkm));
+            } else {
+                assertTrue(Arrays.equals(clientEkm, clientContextEkm));
+            }
+        } finally {
+            pair.close();
+        }
+    }
+
+    // Tests that a socket will close cleanly even if it fails to create due to an
+    // internal IOException
+    @Test
+    public void test_SSLSocket_CloseCleanlyOnConstructorFailure() throws Exception {
+        TestSSLContext c = new TestSSLContext.Builder()
+                .clientProtocol(clientVersion)
+                .serverProtocol(serverVersion)
+                .build();
+        try {
+            c.clientContext.getSocketFactory().createSocket(c.host, 1);
+            fail();
+        } catch (ConnectException ignored) {
+            // Ignored.
+        }
+    }
+
+    private static void setWriteTimeout(Object socket, int timeout) {
+        Exception ex = null;
+        try {
+            Method method = socket.getClass().getMethod("setSoWriteTimeout", int.class);
+            method.setAccessible(true);
+            method.invoke(socket, timeout);
+        } catch (Exception e) {
+            ex = e;
+        }
+        // Engine-based socket currently has the method but throws UnsupportedOperationException.
+        assumeNoException("Client socket does not support setting write timeout", ex);
+    }
+
+    private static void setHostname(SSLSocket socket) {
+        try {
+            Method method = socket.getClass().getMethod("setHostname", String.class);
+            method.setAccessible(true);
+            method.invoke(socket, "sslsockettest.androidcts.google.com");
+        } catch (NoSuchMethodException ignored) {
+            // Ignored.
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to enable SNI", e);
+        }
+    }
+
+    private static void enableSessionTickets(SSLSocket socket) {
+        try {
+            Method method =
+                    socket.getClass().getMethod("setUseSessionTickets", boolean.class);
+            method.setAccessible(true);
+            method.invoke(socket, true);
+        } catch (NoSuchMethodException ignored) {
+            // Ignored.
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to enable Session Tickets", e);
+        }
+    }
+
+    private static boolean isConscryptSocket(SSLSocket socket) {
+        return isConscryptFdSocket(socket) || isConscryptEngineSocket(socket);
+    }
+
+    private static boolean isConscryptFdSocket(SSLSocket socket) {
+        Class<?> clazz = socket.getClass();
+        while (clazz != Object.class && !"ConscryptFileDescriptorSocket".equals(clazz.getSimpleName())) {
+            clazz = clazz.getSuperclass();
+        }
+        return "ConscryptFileDescriptorSocket".equals(clazz.getSimpleName());
+    }
+
+    private static boolean isConscryptEngineSocket(SSLSocket socket) {
+        Class<?> clazz = socket.getClass();
+        while (clazz != Object.class && !"ConscryptEngineSocket".equals(clazz.getSimpleName())) {
+            clazz = clazz.getSuperclass();
+        }
+        return "ConscryptEngineSocket".equals(clazz.getSimpleName());
+    }
+
+    private static String osName() {
+        return System.getProperty("os.name").toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", "");
+    }
+
+    private static boolean isLinux() {
+        return osName().startsWith("linux");
+    }
+
+    private static boolean isWindows() {
+        return osName().startsWith("windows");
+    }
+
+    private static boolean isOsx() {
+        String name = osName();
+        return name.startsWith("macosx") || name.startsWith("osx");
+    }
+
+    private <T> Future<T> runAsync(Callable<T> callable) {
+        return executor.submit(callable);
+    }
+
+    private static SSLContext defaultInit(SSLContext context) throws KeyManagementException {
+        context.init(null, null, null);
+        return context;
+    }
+
+    // Assumes that the negotiated connection will be TLS 1.2
+    private void assumeTlsV1_2Connection() {
+        assumeTrue("TLSv1.2".equals(negotiatedVersion()));
+    }
+
+    /**
+     * Returns the version that a connection between {@code clientVersion} and
+     * {@code serverVersion} should produce.
+     */
+    private String negotiatedVersion() {
+        if (clientVersion.equals("TLSv1.3") && serverVersion.equals("TLSv1.3")) {
+            return "TLSv1.3";
+        } else {
+            return "TLSv1.2";
+        }
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/TrustManagerFactoryTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/TrustManagerFactoryTest.java
new file mode 100644
index 0000000..06ec3a2
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/TrustManagerFactoryTest.java
@@ -0,0 +1,320 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.org.conscrypt.Conscrypt;
+import com.android.org.conscrypt.java.security.StandardNames;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.Provider;
+import java.security.cert.CertificateException;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.PKIXParameters;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.CertPathTrustManagerParameters;
+import javax.net.ssl.ManagerFactoryParameters;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+import org.bouncycastle.asn1.x509.KeyPurposeId;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import tests.util.ServiceTester;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class TrustManagerFactoryTest {
+    private static final String[] KEY_TYPES = new String[] {"RSA", "DSA", "EC", "EC_RSA"};
+
+    private static TestKeyStore TEST_KEY_STORE;
+
+    // note the rare usage of DSA keys here in addition to RSA
+    private static TestKeyStore getTestKeyStore() throws Exception {
+        if (TEST_KEY_STORE == null) {
+            TEST_KEY_STORE = new TestKeyStore.Builder()
+                                     .keyAlgorithms(KEY_TYPES)
+                                     .aliasPrefix("rsa-dsa-ec")
+                                     .build();
+        }
+        return TEST_KEY_STORE;
+    }
+
+    private static boolean supportsManagerFactoryParameters(TrustManagerFactory tmf) {
+        return (StandardNames.IS_RI && tmf.getAlgorithm().equals("PKIX")
+            && !Conscrypt.isConscrypt(tmf.getProvider()));
+    }
+
+    @Test
+    public void test_TrustManagerFactory_getDefaultAlgorithm() throws Exception {
+        String algorithm = TrustManagerFactory.getDefaultAlgorithm();
+        assertEquals(StandardNames.TRUST_MANAGER_FACTORY_DEFAULT, algorithm);
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
+        test_TrustManagerFactory(tmf);
+    }
+
+    private static class UselessManagerFactoryParameters implements ManagerFactoryParameters {}
+
+    private void test_TrustManagerFactory(TrustManagerFactory tmf) throws Exception {
+        assertNotNull(tmf);
+        assertNotNull(tmf.getAlgorithm());
+        assertNotNull(tmf.getProvider());
+
+        // before init
+        try {
+            tmf.getTrustManagers();
+            fail();
+        } catch (IllegalStateException expected) {
+            // Ignored.
+        }
+
+        // init with null ManagerFactoryParameters
+        try {
+            tmf.init((ManagerFactoryParameters) null);
+            fail();
+        } catch (InvalidAlgorithmParameterException expected) {
+            // Ignored.
+        }
+
+        // init with useless ManagerFactoryParameters
+        try {
+            tmf.init(new UselessManagerFactoryParameters());
+            fail();
+        } catch (InvalidAlgorithmParameterException expected) {
+            // Ignored.
+        }
+
+        // init with PKIXParameters ManagerFactoryParameters
+        try {
+            PKIXParameters pp = new PKIXParameters(getTestKeyStore().keyStore);
+            CertPathTrustManagerParameters cptmp = new CertPathTrustManagerParameters(pp);
+            tmf.init(cptmp);
+            fail();
+        } catch (InvalidAlgorithmParameterException expected) {
+            // Ignored.
+        }
+
+        // init with PKIXBuilderParameters ManagerFactoryParameters
+        X509CertSelector xcs = new X509CertSelector();
+        PKIXBuilderParameters pbp = new PKIXBuilderParameters(getTestKeyStore().keyStore, xcs);
+        CertPathTrustManagerParameters cptmp = new CertPathTrustManagerParameters(pbp);
+        if (supportsManagerFactoryParameters(tmf)) {
+            tmf.init(cptmp);
+            test_TrustManagerFactory_getTrustManagers(tmf);
+        } else {
+            try {
+                tmf.init(cptmp);
+                fail();
+            } catch (InvalidAlgorithmParameterException expected) {
+                // Ignored.
+            }
+        }
+
+        // init with null for default KeyStore
+        tmf.init((KeyStore) null);
+        test_TrustManagerFactory_getTrustManagers(tmf);
+
+        // init with specific key store
+        tmf.init(getTestKeyStore().keyStore);
+        test_TrustManagerFactory_getTrustManagers(tmf);
+    }
+
+    private void test_TrustManagerFactory_getTrustManagers(TrustManagerFactory tmf)
+            throws Exception {
+        TrustManager[] trustManagers = tmf.getTrustManagers();
+        assertNotNull(trustManagers);
+        assertTrue(trustManagers.length > 0);
+        for (TrustManager trustManager : trustManagers) {
+            assertNotNull(trustManager);
+            if (trustManager instanceof X509TrustManager) {
+                test_X509TrustManager(tmf.getProvider(), (X509TrustManager) trustManager);
+            }
+        }
+    }
+
+    private void test_X509TrustManager(Provider p, X509TrustManager tm) throws Exception {
+        for (String keyType : KEY_TYPES) {
+            X509Certificate[] issuers = tm.getAcceptedIssuers();
+            assertNotNull(issuers);
+            assertTrue(issuers.length > 1);
+            assertNotSame(issuers, tm.getAcceptedIssuers());
+            boolean defaultTrustManager
+                    // RI de-duplicates certs from TrustedCertificateEntry and PrivateKeyEntry
+                    = issuers.length >
+                    (StandardNames.IS_RI && !Conscrypt.isConscrypt(p) ? 1 : 2) * KEY_TYPES.length;
+
+            String keyAlgName = TestKeyStore.keyAlgorithm(keyType);
+            String sigAlgName = TestKeyStore.signatureAlgorithm(keyType);
+            PrivateKeyEntry pke = getTestKeyStore().getPrivateKey(keyAlgName, sigAlgName);
+            X509Certificate[] chain = (X509Certificate[]) pke.getCertificateChain();
+            if (defaultTrustManager) {
+                try {
+                    tm.checkClientTrusted(chain, keyType);
+                    fail();
+                } catch (CertificateException expected) {
+                    // Ignored.
+                }
+                try {
+                    tm.checkServerTrusted(chain, keyType);
+                    fail();
+                } catch (CertificateException expected) {
+                    // Ignored.
+                }
+            } else {
+                tm.checkClientTrusted(chain, keyType);
+                tm.checkServerTrusted(chain, keyType);
+            }
+        }
+    }
+
+    @Test
+    public void test_TrustManagerFactory_getInstance() throws Exception {
+        ServiceTester.test("TrustManagerFactory").run(new ServiceTester.Test() {
+            @Override
+            public void test(Provider provider, String algorithm) throws Exception {
+                TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
+                assertEquals(algorithm, tmf.getAlgorithm());
+                test_TrustManagerFactory(tmf);
+
+                tmf = TrustManagerFactory.getInstance(algorithm, provider);
+                assertEquals(algorithm, tmf.getAlgorithm());
+                assertEquals(provider, tmf.getProvider());
+                test_TrustManagerFactory(tmf);
+
+                tmf = TrustManagerFactory.getInstance(algorithm, provider.getName());
+                assertEquals(algorithm, tmf.getAlgorithm());
+                assertEquals(provider, tmf.getProvider());
+                test_TrustManagerFactory(tmf);
+            }
+        });
+    }
+
+    @Test
+    public void test_TrustManagerFactory_intermediate() throws Exception {
+        // chain should be server/intermediate/root
+        PrivateKeyEntry pke = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
+        final X509Certificate[] chain = (X509Certificate[]) pke.getCertificateChain();
+        assertEquals(3, chain.length);
+
+        // keyStore should contain only the intermediate CA so we can
+        // test proper validation even if there are extra certs after
+        // the trusted one (in this case the original root is "extra")
+        final KeyStore keyStore = TestKeyStore.createKeyStore();
+        keyStore.setCertificateEntry("alias", chain[1]);
+
+        ServiceTester.test("TrustManagerFactory").run(new ServiceTester.Test() {
+            @Override
+            public void test(Provider p, String algorithm) throws Exception {
+                TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
+                tmf.init(keyStore);
+                TrustManager[] trustManagers = tmf.getTrustManagers();
+                for (TrustManager trustManager : trustManagers) {
+                    if (!(trustManager instanceof X509TrustManager)) {
+                        continue;
+                    }
+                    X509TrustManager tm = (X509TrustManager) trustManager;
+                    tm.checkClientTrusted(chain, "RSA");
+                    tm.checkServerTrusted(chain, "RSA");
+                }
+            }
+        });
+    }
+
+    @Test
+    public void test_TrustManagerFactory_keyOnly() throws Exception {
+        // create a KeyStore containing only a private key with chain.
+        // unlike PKIXParameters(KeyStore), the cert chain of the key should be trusted.
+        KeyStore ks = TestKeyStore.createKeyStore();
+        KeyStore.PrivateKeyEntry pke = getTestKeyStore().getPrivateKey("RSA", "RSA");
+        ks.setKeyEntry("key", pke.getPrivateKey(), "pw".toCharArray(), pke.getCertificateChain());
+
+        String algorithm = TrustManagerFactory.getDefaultAlgorithm();
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
+        tmf.init(ks);
+        X509TrustManager trustManager = (X509TrustManager) tmf.getTrustManagers()[0];
+        trustManager.checkServerTrusted((X509Certificate[]) pke.getCertificateChain(), "RSA");
+    }
+
+    @Test
+    public void test_TrustManagerFactory_extendedKeyUsage() throws Exception {
+        // anyExtendedKeyUsage should work for client or server
+        test_TrustManagerFactory_extendedKeyUsage(
+                KeyPurposeId.anyExtendedKeyUsage, false, true, true);
+        test_TrustManagerFactory_extendedKeyUsage(
+                KeyPurposeId.anyExtendedKeyUsage, true, true, true);
+
+        // critical clientAuth should work for client
+        test_TrustManagerFactory_extendedKeyUsage(
+                KeyPurposeId.id_kp_clientAuth, false, true, false);
+        test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.id_kp_clientAuth, true, true, false);
+
+        // critical serverAuth should work for server
+        test_TrustManagerFactory_extendedKeyUsage(
+                KeyPurposeId.id_kp_serverAuth, false, false, true);
+        test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId.id_kp_serverAuth, true, false, true);
+
+        // codeSigning should not work
+        test_TrustManagerFactory_extendedKeyUsage(
+                KeyPurposeId.id_kp_codeSigning, false, false, false);
+        test_TrustManagerFactory_extendedKeyUsage(
+                KeyPurposeId.id_kp_codeSigning, true, false, false);
+    }
+
+    private void test_TrustManagerFactory_extendedKeyUsage(KeyPurposeId keyPurposeId,
+            boolean critical, boolean client, boolean server) throws Exception {
+        String algorithm = "RSA";
+        TestKeyStore intermediateCa = TestKeyStore.getIntermediateCa();
+        TestKeyStore leaf = new TestKeyStore.Builder()
+                                    .keyAlgorithms(algorithm)
+                                    .aliasPrefix("criticalCodeSigning")
+                                    .signer(intermediateCa.getPrivateKey("RSA", "RSA"))
+                                    .rootCa(intermediateCa.getRootCertificate("RSA"))
+                                    .addExtendedKeyUsage(keyPurposeId, critical)
+                                    .build();
+        // leaf.dump("test_TrustManagerFactory_criticalCodeSigning");
+        PrivateKeyEntry privateKeyEntry = leaf.getPrivateKey(algorithm, algorithm);
+        X509Certificate[] chain = (X509Certificate[]) privateKeyEntry.getCertificateChain();
+
+        TestKeyStore rootCa = TestKeyStore.getRootCa();
+        X509TrustManager trustManager = (X509TrustManager) rootCa.trustManagers[0];
+        try {
+            trustManager.checkClientTrusted(chain, algorithm);
+            assertTrue(client);
+        } catch (Exception e) {
+            assertFalse(client);
+        }
+        try {
+            trustManager.checkServerTrusted(chain, algorithm);
+            assertTrue(server);
+        } catch (Exception e) {
+            assertFalse(server);
+        }
+    }
+}
diff --git a/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/X509KeyManagerTest.java b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/X509KeyManagerTest.java
new file mode 100644
index 0000000..96a573d
--- /dev/null
+++ b/repackaged/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/javax/net/ssl/X509KeyManagerTest.java
@@ -0,0 +1,83 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.X509KeyManager;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class X509KeyManagerTest {
+    /**
+     * Tests whether the key manager will select the right key when the CA is of
+     * one key type and the client is of a possibly different key type.
+     *
+     * <p>There was a bug where EC was being interpreted as EC_EC and only
+     * accepting EC signatures when it should accept any signature type.
+     */
+    @Test
+    public void testChooseClientAlias_Combinations() throws Exception {
+        test_ChooseClientAlias_KeyType("RSA", "RSA", "RSA", true);
+        test_ChooseClientAlias_KeyType("RSA", "EC", "RSA", true);
+        test_ChooseClientAlias_KeyType("RSA", "EC", "EC", false);
+
+        test_ChooseClientAlias_KeyType("EC", "RSA", "EC_RSA", true);
+        test_ChooseClientAlias_KeyType("EC", "EC", "EC_RSA", false);
+
+        test_ChooseClientAlias_KeyType("EC", "EC", "EC_EC", true);
+        test_ChooseClientAlias_KeyType("EC", "RSA", "EC_EC", false);
+
+        test_ChooseClientAlias_KeyType("EC", "RSA", "RSA", false);
+    }
+
+    private void test_ChooseClientAlias_KeyType(String clientKeyType, String caKeyType,
+            String selectedKeyType, boolean succeeds) throws Exception {
+        TestKeyStore ca = new TestKeyStore.Builder().keyAlgorithms(caKeyType).build();
+        TestKeyStore client = new TestKeyStore.Builder()
+                                      .keyAlgorithms(clientKeyType)
+                                      .signer(ca.getPrivateKey(caKeyType, caKeyType))
+                                      .build();
+
+        KeyManagerFactory kmf =
+                KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+        kmf.init(client.keyStore, client.keyPassword);
+
+        String[] keyTypes = new String[] {selectedKeyType};
+        KeyManager[] managers = kmf.getKeyManagers();
+        for (KeyManager manager : managers) {
+            if (manager instanceof X509KeyManager) {
+                String alias = ((X509KeyManager) manager).chooseClientAlias(keyTypes, null, null);
+                if (succeeds) {
+                    assertNotNull(alias);
+                } else {
+                    assertNull(alias);
+                }
+            }
+        }
+    }
+}
diff --git a/repackaged/openjdk/src/main/java/com/android/org/conscrypt/HostProperties.java b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/HostProperties.java
new file mode 100644
index 0000000..7adf930
--- /dev/null
+++ b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/HostProperties.java
@@ -0,0 +1,295 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.File;
+import java.util.Locale;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Utilities for interacting with properties of the host being run on.
+ */
+@Internal
+class HostProperties {
+    private static final Logger logger = Logger.getLogger(HostProperties.class.getName());
+
+    private static final String TEMP_DIR_PROPERTY_NAME = "com.android.org.conscrypt.tmpdir";
+
+    static final OperatingSystem OS;
+    static final Architecture ARCH;
+
+    static {
+        OS = getOperatingSystem(System.getProperty("os.name", ""));
+        ARCH = getArchitecture(System.getProperty("os.arch", ""));
+    }
+
+    /**
+     * Enumeration of operating systems.
+     */
+    enum OperatingSystem {
+        AIX,
+        HPUX,
+        OS400,
+        LINUX,
+        OSX,
+        FREEBSD,
+        OPENBSD,
+        NETBSD,
+        SUNOS,
+        WINDOWS,
+        UNKNOWN;
+
+        /**
+         * Returns the value to use when building filenames for this OS.
+         */
+        public String getFileComponent() {
+            return name().toLowerCase();
+        }
+    }
+
+    /**
+     * Enumeration of architectures.
+     */
+    enum Architecture {
+        X86_64,
+        X86_32 {
+            @Override public String getFileComponent() {
+                return "x86";
+            }
+        },
+        ITANIUM_64,
+        SPARC_32,
+        SPARC_64,
+        ARM_32,
+        AARCH_64,
+        PPC_32,
+        PPC_64,
+        PPCLE_64,
+        S390_32,
+        S390_64,
+        UNKNOWN;
+
+        /**
+         * Returns the value to use when building filenames for this architecture.
+         */
+        public String getFileComponent() {
+            return name().toLowerCase();
+        }
+    }
+
+    static boolean isWindows() {
+        return OS == OperatingSystem.WINDOWS;
+    }
+
+    static boolean isOSX() {
+        return OS == OperatingSystem.OSX;
+    }
+
+    static File getTempDir() {
+        File f;
+        try {
+            // First, see if the application specified a temp dir for conscrypt.
+            f = toDirectory(System.getProperty(TEMP_DIR_PROPERTY_NAME));
+            if (f != null) {
+                return f;
+            }
+
+            // Use the Java system property if available.
+            f = toDirectory(System.getProperty("java.io.tmpdir"));
+            if (f != null) {
+                return f;
+            }
+
+            // This shouldn't happen, but just in case ..
+            if (isWindows()) {
+                f = toDirectory(System.getenv("TEMP"));
+                if (f != null) {
+                    return f;
+                }
+
+                String userprofile = System.getenv("USERPROFILE");
+                if (userprofile != null) {
+                    f = toDirectory(userprofile + "\\AppData\\Local\\Temp");
+                    if (f != null) {
+                        return f;
+                    }
+
+                    f = toDirectory(userprofile + "\\Local Settings\\Temp");
+                    if (f != null) {
+                        return f;
+                    }
+                }
+            } else {
+                f = toDirectory(System.getenv("TMPDIR"));
+                if (f != null) {
+                    return f;
+                }
+            }
+        } catch (Exception ignored) {
+            // Environment variable inaccessible
+        }
+
+        // Last resort.
+        if (isWindows()) {
+            f = new File("C:\\Windows\\Temp");
+        } else {
+            f = new File("/tmp");
+        }
+
+        logger.log(Level.WARNING,
+                "Failed to get the temporary directory; falling back to: {0}", f);
+        return f;
+    }
+
+    @SuppressWarnings("ResultOfMethodCallIgnored")
+    private static File toDirectory(String path) {
+        if (path == null) {
+            return null;
+        }
+
+        File f = new File(path);
+        f.mkdirs();
+
+        if (!f.isDirectory()) {
+            return null;
+        }
+
+        try {
+            return f.getAbsoluteFile();
+        } catch (Exception ignored) {
+            return f;
+        }
+    }
+
+    private static String normalize(String value) {
+        return value.toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", "");
+    }
+
+    /**
+     * Normalizes the os.name value into the value used by the Maven os plugin
+     * (https://github.com/trustin/os-maven-plugin). This plugin is used to generate
+     * platform-specific
+     * classifiers for artifacts.
+     */
+    private static OperatingSystem getOperatingSystem(String value) {
+        value = normalize(value);
+        if (value.startsWith("aix")) {
+            return OperatingSystem.AIX;
+        }
+        if (value.startsWith("hpux")) {
+            return OperatingSystem.HPUX;
+        }
+        if (value.startsWith("os400")) {
+            // Avoid the names such as os4000
+            if (value.length() <= 5 || !Character.isDigit(value.charAt(5))) {
+                return OperatingSystem.OS400;
+            }
+        }
+        if (value.startsWith("linux")) {
+            return OperatingSystem.LINUX;
+        }
+        if (value.startsWith("macosx") || value.startsWith("osx")) {
+            return OperatingSystem.OSX;
+        }
+        if (value.startsWith("freebsd")) {
+            return OperatingSystem.FREEBSD;
+        }
+        if (value.startsWith("openbsd")) {
+            return OperatingSystem.OPENBSD;
+        }
+        if (value.startsWith("netbsd")) {
+            return OperatingSystem.NETBSD;
+        }
+        if (value.startsWith("solaris") || value.startsWith("sunos")) {
+            return OperatingSystem.SUNOS;
+        }
+        if (value.startsWith("windows")) {
+            return OperatingSystem.WINDOWS;
+        }
+
+        return OperatingSystem.UNKNOWN;
+    }
+
+    /**
+     * Normalizes the os.arch value into the value used by the Maven os plugin
+     * (https://github.com/trustin/os-maven-plugin). This plugin is used to generate
+     * platform-specific
+     * classifiers for artifacts.
+     */
+    private static Architecture getArchitecture(String value) {
+        value = normalize(value);
+        if (value.matches("^(x8664|amd64|ia32e|em64t|x64)$")) {
+            return Architecture.X86_64;
+        }
+        if (value.matches("^(x8632|x86|i[3-6]86|ia32|x32)$")) {
+            return Architecture.X86_32;
+        }
+        if (value.matches("^(ia64|itanium64)$")) {
+            return Architecture.ITANIUM_64;
+        }
+        if (value.matches("^(sparc|sparc32)$")) {
+            return Architecture.SPARC_32;
+        }
+        if (value.matches("^(sparcv9|sparc64)$")) {
+            return Architecture.SPARC_64;
+        }
+        if (value.matches("^(arm|arm32)$")) {
+            return Architecture.ARM_32;
+        }
+        if ("aarch64".equals(value)) {
+            return Architecture.AARCH_64;
+        }
+        if (value.matches("^(ppc|ppc32)$")) {
+            return Architecture.PPC_32;
+        }
+        if ("ppc64".equals(value)) {
+            return Architecture.PPC_64;
+        }
+        if ("ppc64le".equals(value)) {
+            return Architecture.PPCLE_64;
+        }
+        if ("s390".equals(value)) {
+            return Architecture.S390_32;
+        }
+        if ("s390x".equals(value)) {
+            return Architecture.S390_64;
+        }
+
+        return Architecture.UNKNOWN;
+    }
+
+    private HostProperties() {}
+
+}
diff --git a/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Java8PlatformUtil.java b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Java8PlatformUtil.java
new file mode 100644
index 0000000..65d5d11
--- /dev/null
+++ b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Java8PlatformUtil.java
@@ -0,0 +1,98 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static javax.net.ssl.StandardConstants.SNI_HOST_NAME;
+
+import java.util.Collections;
+import java.util.List;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIServerName;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+
+/**
+ * Utility methods supported on Java 8+.
+ */
+final class Java8PlatformUtil {
+    static void setSSLParameters(
+            SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
+        impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
+        impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
+        List<SNIServerName> serverNames = params.getServerNames();
+
+        if (serverNames != null) {
+            for (SNIServerName serverName : serverNames) {
+                if (serverName.getType() == SNI_HOST_NAME) {
+                    socket.setHostname(((SNIHostName) serverName).getAsciiName());
+                    break;
+                }
+            }
+        }
+    }
+
+    static void getSSLParameters(
+            SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
+        params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
+        params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
+        if (impl.getUseSni() && AddressUtils.isValidSniHostname(socket.getHostname())) {
+            params.setServerNames(Collections.singletonList(
+                    (SNIServerName) new SNIHostName(socket.getHostname())));
+        }
+    }
+
+    static void setSSLParameters(
+            SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
+        impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
+        impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
+        List<SNIServerName> serverNames = params.getServerNames();
+
+        if (serverNames != null) {
+            for (SNIServerName serverName : serverNames) {
+                if (serverName.getType() == SNI_HOST_NAME) {
+                    engine.setHostname(((SNIHostName) serverName).getAsciiName());
+                    break;
+                }
+            }
+        }
+    }
+    static void getSSLParameters(
+            SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
+        params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
+        params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
+        if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getHostname())) {
+            params.setServerNames(Collections.singletonList(
+                    (SNIServerName) new SNIHostName(engine.getHostname())));
+        }
+    }
+
+    static SSLEngine wrapEngine(ConscryptEngine engine) {
+        return new Java8EngineWrapper(engine);
+    }
+
+    static SSLEngine unwrapEngine(SSLEngine engine) {
+        return Java8EngineWrapper.getDelegate(engine);
+    }
+
+    static SSLSession wrapSSLSession(ExternalSession sslSession) {
+        return new Java8ExtendedSSLSession(sslSession);
+    }
+
+    private Java8PlatformUtil() {}
+}
diff --git a/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Java9PlatformUtil.java b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Java9PlatformUtil.java
new file mode 100644
index 0000000..9141d06
--- /dev/null
+++ b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Java9PlatformUtil.java
@@ -0,0 +1,98 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.lang.reflect.Method;
+import javax.net.ssl.SSLParameters;
+
+/**
+ * Utility methods supported on Java 9+.
+ */
+final class Java9PlatformUtil {
+    // TODO(nmittler): Remove reflection once we require Java 9 for building.
+    private static final Method SSL_PARAMETERS_GET_APPLICATION_PROTOCOLS_METHOD;
+    private static final Method SSL_PARAMETERS_SET_APPLICATION_PROTOCOLS_METHOD;
+
+    static {
+        Class<?> sslParameters = SSLParameters.class;
+        Method getApplicationProtocolsMethod;
+        Method setApplicationProtocolsMethod;
+        try {
+            getApplicationProtocolsMethod = sslParameters.getMethod("getApplicationProtocols");
+            setApplicationProtocolsMethod =
+                    sslParameters.getMethod("setApplicationProtocols", String[].class);
+        } catch (NoSuchMethodException e) {
+            getApplicationProtocolsMethod = null;
+            setApplicationProtocolsMethod = null;
+        }
+
+        SSL_PARAMETERS_GET_APPLICATION_PROTOCOLS_METHOD = getApplicationProtocolsMethod;
+        SSL_PARAMETERS_SET_APPLICATION_PROTOCOLS_METHOD = setApplicationProtocolsMethod;
+    }
+
+    static void setSSLParameters(
+            SSLParameters src, SSLParametersImpl dest, AbstractConscryptSocket socket) {
+        Java8PlatformUtil.setSSLParameters(src, dest, socket);
+
+        dest.setApplicationProtocols(getApplicationProtocols(src));
+    }
+
+    static void getSSLParameters(
+            SSLParameters dest, SSLParametersImpl src, AbstractConscryptSocket socket) {
+        Java8PlatformUtil.getSSLParameters(dest, src, socket);
+
+        setApplicationProtocols(dest, src.getApplicationProtocols());
+    }
+
+    static void setSSLParameters(
+            SSLParameters src, SSLParametersImpl dest, ConscryptEngine engine) {
+        Java8PlatformUtil.setSSLParameters(src, dest, engine);
+
+        dest.setApplicationProtocols(getApplicationProtocols(src));
+    }
+
+    static void getSSLParameters(
+            SSLParameters dest, SSLParametersImpl src, ConscryptEngine engine) {
+        Java8PlatformUtil.getSSLParameters(dest, src, engine);
+
+        setApplicationProtocols(dest, src.getApplicationProtocols());
+    }
+
+    private static String[] getApplicationProtocols(SSLParameters params) {
+        if (SSL_PARAMETERS_GET_APPLICATION_PROTOCOLS_METHOD != null) {
+            try {
+                return (String[]) SSL_PARAMETERS_GET_APPLICATION_PROTOCOLS_METHOD.invoke(params);
+            } catch (ReflectiveOperationException ignored) {
+                // TODO(nmittler): Should we throw here?
+            }
+        }
+        return EmptyArray.STRING;
+    }
+
+    private static void setApplicationProtocols(SSLParameters params, String[] protocols) {
+        if (SSL_PARAMETERS_SET_APPLICATION_PROTOCOLS_METHOD != null) {
+            try {
+                SSL_PARAMETERS_SET_APPLICATION_PROTOCOLS_METHOD.invoke(params, (Object) protocols);
+            } catch (ReflectiveOperationException ignored) {
+                // TODO(nmittler): Should we throw here?
+            }
+        }
+    }
+
+    private Java9PlatformUtil() {}
+}
diff --git a/repackaged/openjdk/src/main/java/com/android/org/conscrypt/NativeCryptoJni.java b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/NativeCryptoJni.java
new file mode 100644
index 0000000..4b8f045
--- /dev/null
+++ b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/NativeCryptoJni.java
@@ -0,0 +1,129 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import com.android.org.conscrypt.NativeLibraryLoader.LoadResult;
+
+/**
+ * Helper to initialize the JNI libraries. This version runs when compiled as part of a host OpenJDK
+ * build.
+ */
+final class NativeCryptoJni {
+    private static final String STATIC_LIB_NAME = "conscrypt";
+    private static final String DYNAMIC_LIB_NAME_PREFIX = "conscrypt_openjdk_jni";
+
+    /**
+     * Attempts to load the shared JNI library. First try loading the platform-specific library
+     * name (e.g. conscrypt_openjdk_jni-linux-x86_64). If that doesn't work, try to load the
+     * library via just the prefix (e.g. conscrypt_openjdk_jni).  If not found, try the static
+     * library name.
+     *
+     * The non-suffixed dynamic library name is used by the Android build system, which builds
+     * the appropriate library for the system it's being run on under that name.
+     *
+     * The static library name is needed in order to support Java 8 static linking
+     * (http://openjdk.java.net/jeps/178), where the library name is used to invoke a
+     * library-specific load method (i.e. {@code JNI_OnLoad_conscrypt}).
+     *
+     * @throws UnsatisfiedLinkError if the library failed to load.
+     */
+    static void init() throws UnsatisfiedLinkError {
+        List<LoadResult> results = new ArrayList<LoadResult>();
+        if (!NativeLibraryLoader.loadFirstAvailable(classLoader(), results,
+                platformLibName(), DYNAMIC_LIB_NAME_PREFIX, STATIC_LIB_NAME)) {
+            logResults(results);
+            throwBestError(results);
+        }
+    }
+
+    private NativeCryptoJni() {}
+
+    private static void logResults(List<LoadResult> results) {
+        for (LoadResult result : results) {
+            result.log();
+        }
+    }
+
+    private static void throwBestError(List<LoadResult> results) {
+        Collections.sort(results, ErrorComparator.INSTANCE);
+
+        Throwable bestError = results.get(0).error;
+        for (LoadResult result : results.subList(1, results.size())) {
+            // Suppress all of the other errors, so that they're available to the caller if
+            // desired.
+            bestError.addSuppressed(result.error);
+        }
+
+        if (bestError instanceof Error) {
+            throw (Error) bestError;
+        }
+
+        throw (Error) new UnsatisfiedLinkError(bestError.getMessage()).initCause(bestError);
+    }
+
+    private static ClassLoader classLoader() {
+        return NativeCrypto.class.getClassLoader();
+    }
+
+    private static String platformLibName() {
+        return DYNAMIC_LIB_NAME_PREFIX + "-" + osName() + '-' + archName();
+    }
+
+    private static String osName() {
+        return HostProperties.OS.getFileComponent();
+    }
+
+    private static String archName() {
+        return HostProperties.ARCH.getFileComponent();
+    }
+
+    /**
+     * Sorts the errors in a list in descending order of value. After a list is sorted,
+     * the first element is the most important error.
+     */
+    private static final class ErrorComparator implements Comparator<LoadResult> {
+        static final ErrorComparator INSTANCE = new ErrorComparator();
+
+        @Override
+        public int compare(LoadResult o1, LoadResult o2) {
+            Throwable e1 = o1.error;
+            Throwable e2 = o2.error;
+
+            // First, sort by error type.
+            int value1 = e1 instanceof UnsatisfiedLinkError ? 1 : 0;
+            int value2 = e2 instanceof UnsatisfiedLinkError ? 1 : 0;
+            if (value1 != value2) {
+                // Order so that the UnsatisfiedLinkError is first.
+                return value2 - value1;
+            }
+
+            // Both are either link errors or not. Compare the message. Treat messages in
+            // the form "no <libName> in java.library.path" as lower value, since there may be
+            // a more interesting message for a library that was found.
+            String m1 = e1.getMessage();
+            String m2 = e2.getMessage();
+            value1 = m1 != null && m1.contains("java.library.path") ? 0 : 1;
+            value2 = m2 != null && m2.contains("java.library.path") ? 0 : 1;
+            return value2 - value1;
+        }
+    }
+}
diff --git a/repackaged/openjdk/src/main/java/com/android/org/conscrypt/NativeLibraryLoader.java b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/NativeLibraryLoader.java
new file mode 100644
index 0000000..aa9c95f
--- /dev/null
+++ b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/NativeLibraryLoader.java
@@ -0,0 +1,423 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2014 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.android.org.conscrypt;
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Helper class to load JNI resources.
+ */
+final class NativeLibraryLoader {
+    private static final Logger logger = Logger.getLogger(NativeLibraryLoader.class.getName());
+
+    private static final String WORK_DIR_PROPERTY_NAME = "com.android.org.conscrypt.native.workdir";
+    private static final String DELETE_LIB_PROPERTY_NAME =
+            "com.android.org.conscrypt.native.deleteLibAfterLoading";
+    private static final String NATIVE_RESOURCE_HOME = "META-INF/native/";
+    private static final File WORKDIR;
+    private static final boolean DELETE_NATIVE_LIB_AFTER_LOADING;
+
+    static {
+        File workdir = getWorkDir();
+        if (workdir == null) {
+            workdir = HostProperties.getTempDir();
+        }
+        WORKDIR = workdir;
+        log("-D{0}: {1}", WORK_DIR_PROPERTY_NAME, WORKDIR);
+
+        DELETE_NATIVE_LIB_AFTER_LOADING =
+                Boolean.valueOf(System.getProperty(DELETE_LIB_PROPERTY_NAME, "true"));
+    }
+
+    private static File getWorkDir() {
+        String dirName = System.getProperty(WORK_DIR_PROPERTY_NAME);
+        if (dirName == null) {
+            // Application didn't specify a workdir.
+            return null;
+        }
+
+        File f = new File(dirName);
+        // Create the directory if it doesn't already exist.
+        if (!f.mkdirs() && !f.exists()) {
+            // Unable to create the directory.
+            log("Unable to find or create working directory: {0}", dirName);
+            return null;
+        }
+
+        try {
+            f = f.getAbsoluteFile();
+        } catch (Exception ignored) {
+            // Good to have an absolute path, but it's OK.
+        }
+        return f;
+    }
+
+    /**
+     * Loads the first available library in the collection with the specified
+     * {@link ClassLoader}.
+     */
+    static boolean loadFirstAvailable(
+            ClassLoader loader, List<LoadResult> results, String... names) {
+        for (String name : names) {
+            if (load(name, loader, results)) {
+                // Successfully loaded
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * A result of a single attempt to load a library.
+     */
+    static final class LoadResult {
+        final String name;
+        final boolean absolute;
+        final boolean loaded;
+        final boolean usingHelperClassloader;
+        final Throwable error;
+
+        private static LoadResult newSuccessResult(
+                String name, boolean absolute, boolean usingHelperClassloader) {
+            return new LoadResult(name, absolute, true, usingHelperClassloader, null);
+        }
+
+        private static LoadResult newFailureResult(
+                String name, boolean absolute, boolean usingHelperClassloader, Throwable error) {
+            return new LoadResult(name, absolute, false, usingHelperClassloader, error);
+        }
+
+        private LoadResult(String name, boolean absolute, boolean loaded,
+                boolean usingHelperClassloader, Throwable error) {
+            this.name = name;
+            this.absolute = absolute;
+            this.loaded = loaded;
+            this.usingHelperClassloader = usingHelperClassloader;
+            this.error = error;
+        }
+
+        void log() {
+            if (error != null) {
+                NativeLibraryLoader.log(
+                        "Unable to load the library {0} (using helper classloader={1})", name,
+                        usingHelperClassloader, error);
+            } else {
+                NativeLibraryLoader.log(
+                        "Successfully loaded library {0}  (using helper classloader={1})", name,
+                        usingHelperClassloader);
+            }
+        }
+    }
+
+    /**
+     * Load the given library with the specified {@link ClassLoader}
+     */
+    private static boolean load(String name, ClassLoader loader, List<LoadResult> results) {
+        // Try loading from the fully-qualified classpath resource first. Otherwise just try
+        // loading the non-absolute library name directly.
+        return loadFromWorkdir(name, loader, results) || loadLibrary(loader, name, false, results);
+    }
+
+    private static boolean loadFromWorkdir(
+            String name, ClassLoader loader, List<LoadResult> results) {
+        String libname = System.mapLibraryName(name);
+        String path = NATIVE_RESOURCE_HOME + libname;
+
+        URL url = loader.getResource(path);
+        if (url == null && HostProperties.isOSX()) {
+            if (path.endsWith(".jnilib")) {
+                url = loader.getResource(NATIVE_RESOURCE_HOME + "lib" + name + ".dynlib");
+            } else {
+                url = loader.getResource(NATIVE_RESOURCE_HOME + "lib" + name + ".jnilib");
+            }
+        }
+
+        if (url == null) {
+            return false;
+        }
+
+        int index = libname.lastIndexOf('.');
+        String prefix = libname.substring(0, index);
+        String suffix = libname.substring(index, libname.length());
+        File tmpFile = null;
+        try {
+            // Create a temporary file.
+            tmpFile = Platform.createTempFile(prefix, suffix, WORKDIR);
+            if (tmpFile.isFile() && tmpFile.canRead() && !Platform.canExecuteExecutable(tmpFile)) {
+                throw new IOException(MessageFormat.format("{0} exists but cannot be executed even "
+                                + "when execute permissions set; check volume for "
+                                + "\"noexec\" flag; use -D{1}=[path] to set native "
+                                + "working directory separately.",
+                        tmpFile.getPath(), WORK_DIR_PROPERTY_NAME));
+            }
+
+            // Copy the library from classpath to tmpFile
+            copyLibrary(url, tmpFile);
+
+            return loadLibrary(loader, tmpFile.getPath(), true, results);
+        } catch (IOException e) {
+            // Convert to an UnsatisfiedLinkError.
+            Throwable error = new UnsatisfiedLinkError(
+                    MessageFormat.format("Failed creating temp file ({0})",
+                            tmpFile)).initCause(e);
+            results.add(LoadResult.newFailureResult(name, true, false, error));
+            return false;
+        } finally {
+            // After we load the library it is safe to delete the file.
+            // We delete the file immediately to free up resources as soon as possible,
+            // and if this fails fallback to deleting on JVM exit.
+            if (tmpFile != null) {
+                boolean deleted = false;
+                if (DELETE_NATIVE_LIB_AFTER_LOADING) {
+                    deleted = tmpFile.delete();
+                }
+                if (!deleted) {
+                    tmpFile.deleteOnExit();
+                }
+            }
+        }
+    }
+
+    /**
+     * Copies the given shared library file from classpath to a temporary file.
+     *
+     * @param classpathUrl the URL of the library on classpath
+     * @param tmpFile the destination temporary file.
+     */
+    private static void copyLibrary(URL classpathUrl, File tmpFile) throws IOException {
+        InputStream in = null;
+        OutputStream out = null;
+        try {
+            in = classpathUrl.openStream();
+            out = new FileOutputStream(tmpFile);
+
+            byte[] buffer = new byte[8192];
+            int length;
+            while ((length = in.read(buffer)) > 0) {
+                out.write(buffer, 0, length);
+            }
+            out.flush();
+        } finally {
+            closeQuietly(in);
+            closeQuietly(out);
+        }
+    }
+
+    /**
+     * Loading the native library into the specified {@link ClassLoader}.
+     * @param loader - The {@link ClassLoader} where the native library will be loaded into
+     * @param name - The native library path or name
+     * @param absolute - Whether the native library will be loaded by path or by name
+     * @return {@code true} if the library was successfully loaded.
+     */
+    private static boolean loadLibrary(final ClassLoader loader, final String name,
+            final boolean absolute, List<LoadResult> results) {
+        try {
+            // Make sure the helper belongs to the target ClassLoader.
+            final Class<?> newHelper = tryToLoadClass(loader, NativeLibraryUtil.class);
+            LoadResult result = loadLibraryFromHelperClassloader(newHelper, name, absolute);
+            results.add(result);
+            if (result.loaded) {
+                // Successfully loaded the library.
+                return true;
+            }
+        } catch (Exception ignore) {
+            // Failed loading the helper in the provided classloader - ignore.
+        }
+
+        // Fallback to loading from the local classloader.
+        LoadResult result = loadLibraryFromCurrentClassloader(name, absolute);
+        results.add(result);
+        return result.loaded;
+    }
+
+    /**
+     * Attempts to load the library by reflectively using the {@link NativeLibraryUtil} helper
+     * from its classloader.
+     *
+     * @param helper The {@link NativeLibraryUtil} helper class
+     * @param name the name of the library to load.
+     * @param absolute true if {@code name} is an absolute path to the file.
+     * @return the result of the load operation.
+     */
+    private static LoadResult loadLibraryFromHelperClassloader(
+            final Class<?> helper, final String name, final boolean absolute) {
+        return AccessController.doPrivileged(new PrivilegedAction<LoadResult>() {
+            @Override
+            public LoadResult run() {
+                try {
+                    // Invoke the helper to load the native library, if succeed, then the native
+                    // library belongs to the specified ClassLoader.
+                    Method method = helper.getMethod("loadLibrary", String.class, boolean.class);
+                    method.setAccessible(true);
+                    method.invoke(null, name, absolute);
+                    return LoadResult.newSuccessResult(name, absolute, true);
+                } catch (InvocationTargetException e) {
+                    return LoadResult.newFailureResult(name, absolute, true, e.getCause());
+                } catch (Throwable e) {
+                    return LoadResult.newFailureResult(name, absolute, true, e);
+                }
+            }
+        });
+    }
+
+    /**
+     * Attempts to load the library using the {@link NativeLibraryUtil} helper from the current
+     * classloader.
+     *
+     * @param name the name of the library to load.
+     * @param absolute true if {@code name} is an absolute path
+     * @return the result of the load operation.
+     */
+    private static LoadResult loadLibraryFromCurrentClassloader(String name, boolean absolute) {
+        try {
+            NativeLibraryUtil.loadLibrary(name, absolute);
+            return LoadResult.newSuccessResult(name, absolute, false);
+        } catch (Throwable e) {
+            return LoadResult.newFailureResult(name, absolute, false, e);
+        }
+    }
+
+    /**
+     * Try to load the helper {@link Class} into specified {@link ClassLoader}.
+     * @param loader - The {@link ClassLoader} where to load the helper {@link Class}
+     * @param helper - The helper {@link Class}
+     * @return A new helper Class defined in the specified ClassLoader.
+     * @throws ClassNotFoundException Helper class not found or loading failed
+     */
+    private static Class<?> tryToLoadClass(final ClassLoader loader, final Class<?> helper)
+            throws ClassNotFoundException {
+        try {
+            return loader.loadClass(helper.getName());
+        } catch (ClassNotFoundException e) {
+            // The helper class is NOT found in target ClassLoader, we have to define the helper
+            // class.
+            final byte[] classBinary = classToByteArray(helper);
+            return AccessController.doPrivileged(new PrivilegedAction<Class<?>>() {
+                @Override
+                public Class<?> run() {
+                    try {
+                        // Define the helper class in the target ClassLoader,
+                        //  then we can call the helper to load the native library.
+                        Method defineClass = ClassLoader.class.getDeclaredMethod(
+                                "defineClass", String.class, byte[].class, int.class, int.class);
+                        defineClass.setAccessible(true);
+                        return (Class<?>) defineClass.invoke(
+                                loader, helper.getName(), classBinary, 0, classBinary.length);
+                    } catch (Exception e) {
+                        throw new IllegalStateException("Define class failed!", e);
+                    }
+                }
+            });
+        }
+    }
+
+    /**
+     * Load the helper {@link Class} as a byte array, to be redefined in specified {@link
+     * ClassLoader}.
+     * @param clazz - The helper {@link Class} provided by this bundle
+     * @return The binary content of helper {@link Class}.
+     * @throws ClassNotFoundException Helper class not found or loading failed
+     */
+    private static byte[] classToByteArray(Class<?> clazz) throws ClassNotFoundException {
+        String fileName = clazz.getName();
+        int lastDot = fileName.lastIndexOf('.');
+        if (lastDot > 0) {
+            fileName = fileName.substring(lastDot + 1);
+        }
+        URL classUrl = clazz.getResource(fileName + ".class");
+        if (classUrl == null) {
+            throw new ClassNotFoundException(clazz.getName());
+        }
+        byte[] buf = new byte[1024];
+        ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
+        InputStream in = null;
+        try {
+            in = classUrl.openStream();
+            for (int r; (r = in.read(buf)) != -1;) {
+                out.write(buf, 0, r);
+            }
+            return out.toByteArray();
+        } catch (IOException ex) {
+            throw new ClassNotFoundException(clazz.getName(), ex);
+        } finally {
+            closeQuietly(in);
+            closeQuietly(out);
+        }
+    }
+
+    private static void closeQuietly(Closeable c) {
+        if (c != null) {
+            try {
+                c.close();
+            } catch (IOException ignore) {
+                // ignore
+            }
+        }
+    }
+
+    private NativeLibraryLoader() {
+        // Utility
+    }
+
+    private static void log(String format, Object arg) {
+        logger.log(Level.FINE, format, arg);
+    }
+
+    private static void log(String format, Object arg1, Object arg2) {
+        logger.log(Level.FINE, format, new Object[] {arg1, arg2});
+    }
+
+    private static void log(String format, Object arg1, Object arg2, Throwable t) {
+        debug(MessageFormat.format(format, arg1, arg2), t);
+    }
+
+    private static void debug(String message, Throwable t) {
+        logger.log(Level.FINE, message, t);
+    }
+}
diff --git a/repackaged/openjdk/src/main/java/com/android/org/conscrypt/NativeLibraryUtil.java b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/NativeLibraryUtil.java
new file mode 100644
index 0000000..2ebf789
--- /dev/null
+++ b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/NativeLibraryUtil.java
@@ -0,0 +1,60 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2017 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.android.org.conscrypt;
+
+/**
+ * A Utility to Call the {@link System#load(String)} or {@link System#loadLibrary(String)}.
+ * Because the {@link System#load(String)} and {@link System#loadLibrary(String)} are both
+ * CallerSensitive, it will load the native library into its caller's {@link ClassLoader}.
+ * In OSGi environment, we need this helper to delegate the calling to {@link System#load(String)}
+ * and it should be as simple as possible. It will be injected into the native library's
+ * ClassLoader when it is undefined. And therefore, when the defined new helper is invoked,
+ * the native library would be loaded into the native library's ClassLoader, not the
+ * caller's ClassLoader.
+ */
+final class NativeLibraryUtil {
+    /**
+     * Delegate the calling to {@link System#load(String)} or {@link System#loadLibrary(String)}.
+     * @param libName - The native library path or name
+     * @param absolute - Whether the native library will be loaded by path or by name
+     */
+    public static void loadLibrary(String libName, boolean absolute) {
+        if (absolute) {
+            System.load(libName);
+        } else {
+            System.loadLibrary(libName);
+        }
+    }
+
+    private NativeLibraryUtil() {}
+}
diff --git a/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Platform.java b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Platform.java
new file mode 100644
index 0000000..7916ee0
--- /dev/null
+++ b/repackaged/openjdk/src/main/java/com/android/org/conscrypt/Platform.java
@@ -0,0 +1,782 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static java.nio.file.attribute.PosixFilePermission.GROUP_EXECUTE;
+import static java.nio.file.attribute.PosixFilePermission.OTHERS_EXECUTE;
+import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.SocketImpl;
+import java.nio.channels.SocketChannel;
+import java.nio.file.Files;
+import java.nio.file.attribute.PosixFilePermission;
+import java.security.AccessController;
+import java.security.AlgorithmParameters;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PrivilegedAction;
+import java.security.Provider;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509ExtendedTrustManager;
+import javax.net.ssl.X509TrustManager;
+import com.android.org.conscrypt.ct.CTLogStore;
+import com.android.org.conscrypt.ct.CTPolicy;
+import sun.security.x509.AlgorithmId;
+
+/**
+ * Platform-specific methods for OpenJDK.
+ *
+ * Uses reflection to implement Java 8 SSL features for backwards compatibility.
+ */
+final class Platform {
+    private static final int JAVA_VERSION = javaVersion0();
+    private static final Method GET_CURVE_NAME_METHOD;
+
+    static {
+
+        Method getCurveNameMethod = null;
+        try {
+            getCurveNameMethod = ECParameterSpec.class.getDeclaredMethod("getCurveName");
+            getCurveNameMethod.setAccessible(true);
+        } catch (Exception ignored) {
+        }
+        GET_CURVE_NAME_METHOD = getCurveNameMethod;
+    }
+
+    private Platform() {}
+
+    static void setup() {}
+
+
+    /**
+     * Approximates the behavior of File.createTempFile without depending on SecureRandom.
+     */
+    static File createTempFile(String prefix, String suffix, File directory)
+        throws IOException {
+        if (directory == null) {
+            throw new NullPointerException();
+        }
+        long time = System.currentTimeMillis();
+        prefix = new File(prefix).getName();
+        IOException suppressed = null;
+        for (int i = 0; i < 10000; i++) {
+            String tempName = String.format(Locale.US, "%s%d%04d%s", prefix, time, i, suffix);
+            File tempFile = new File(directory, tempName);
+            if (!tempName.equals(tempFile.getName())) {
+                // The given prefix or suffix contains path separators.
+                throw new IOException("Unable to create temporary file: " + tempFile);
+            }
+            try {
+                if (tempFile.createNewFile()) {
+                    return tempFile.getCanonicalFile();
+                }
+            } catch (IOException e) {
+                // This may just be a transient error; store it just in case.
+                suppressed = e;
+            }
+        }
+        if (suppressed != null) {
+            throw suppressed;
+        } else {
+            throw new IOException("Unable to create temporary file");
+        }
+    }
+
+    /**
+     * Default name used in the {@link java.security.Security JCE system} by {@code OpenSSLProvider}
+     * if the default constructor is used.
+     */
+    static String getDefaultProviderName() {
+        return "Conscrypt";
+    }
+
+    static boolean provideTrustManagerByDefault() {
+        return true;
+    }
+
+    static boolean canExecuteExecutable(File file) throws IOException {
+        // If we can already execute, there is nothing to do.
+        if (file.canExecute()) {
+            return true;
+        }
+
+        // On volumes, with noexec set, even files with the executable POSIX permissions will
+        // fail to execute. The File#canExecute() method honors this behavior, probably via
+        // parsing the noexec flag when initializing the UnixFileStore, though the flag is not
+        // exposed via a public API.  To find out if library is being loaded off a volume with
+        // noexec, confirm or add executable permissions, then check File#canExecute().
+
+        Set<PosixFilePermission> existingFilePermissions =
+                Files.getPosixFilePermissions(file.toPath());
+        Set<PosixFilePermission> executePermissions =
+                EnumSet.of(OWNER_EXECUTE, GROUP_EXECUTE, OTHERS_EXECUTE);
+        if (existingFilePermissions.containsAll(executePermissions)) {
+            return false;
+        }
+
+        Set<PosixFilePermission> newPermissions = EnumSet.copyOf(existingFilePermissions);
+        newPermissions.addAll(executePermissions);
+        Files.setPosixFilePermissions(file.toPath(), newPermissions);
+        return file.canExecute();
+    }
+
+    static FileDescriptor getFileDescriptor(Socket s) {
+        try {
+            SocketChannel channel = s.getChannel();
+            if (channel != null) {
+                Field f_fd = channel.getClass().getDeclaredField("fd");
+                f_fd.setAccessible(true);
+                return (FileDescriptor) f_fd.get(channel);
+            }
+        } catch (Exception e) {
+            // Try socket class below...
+        }
+
+        try {
+            Field f_impl = Socket.class.getDeclaredField("impl");
+            f_impl.setAccessible(true);
+            Object socketImpl = f_impl.get(s);
+            Field f_fd = SocketImpl.class.getDeclaredField("fd");
+            f_fd.setAccessible(true);
+            return (FileDescriptor) f_fd.get(socketImpl);
+        } catch (Exception e) {
+            throw new RuntimeException("Can't get FileDescriptor from socket", e);
+        }
+    }
+
+    @SuppressWarnings("unused")
+    static FileDescriptor getFileDescriptorFromSSLSocket(AbstractConscryptSocket socket) {
+        return getFileDescriptor(socket);
+    }
+
+    @SuppressWarnings("unused")
+    static String getCurveName(ECParameterSpec spec) {
+        if (GET_CURVE_NAME_METHOD != null) {
+            try {
+                return (String) GET_CURVE_NAME_METHOD.invoke(spec);
+            } catch (Exception ignored) {
+                // Ignored
+            }
+        }
+        return null;
+    }
+
+    @SuppressWarnings("unused")
+    static void setCurveName(@SuppressWarnings("unused") ECParameterSpec spec,
+            @SuppressWarnings("unused") String curveName) {
+        // This doesn't appear to be needed.
+    }
+
+    @SuppressWarnings("unused")
+    static void setSocketWriteTimeout(@SuppressWarnings("unused") Socket s,
+            @SuppressWarnings("unused") long timeoutMillis) throws SocketException {
+        // TODO: figure this out on the RI
+    }
+
+    static void setSSLParameters(
+            SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
+        if (JAVA_VERSION >= 9) {
+            Java9PlatformUtil.setSSLParameters(params, impl, socket);
+        } else if (JAVA_VERSION >= 8) {
+            Java8PlatformUtil.setSSLParameters(params, impl, socket);
+        } else {
+            impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
+        }
+    }
+
+    static void getSSLParameters(
+            SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
+        if (JAVA_VERSION >= 9) {
+            Java9PlatformUtil.getSSLParameters(params, impl, socket);
+        } else if (JAVA_VERSION >= 8) {
+            Java8PlatformUtil.getSSLParameters(params, impl, socket);
+        } else {
+            params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
+        }
+    }
+
+    static void setSSLParameters(
+            SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
+        if (JAVA_VERSION >= 9) {
+            Java9PlatformUtil.setSSLParameters(params, impl, engine);
+        } else if (JAVA_VERSION >= 8) {
+            Java8PlatformUtil.setSSLParameters(params, impl, engine);
+        } else {
+            impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
+        }
+    }
+
+    static void getSSLParameters(
+            SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
+        if (JAVA_VERSION >= 9) {
+            Java9PlatformUtil.getSSLParameters(params, impl, engine);
+        } else if (JAVA_VERSION >= 8) {
+            Java8PlatformUtil.getSSLParameters(params, impl, engine);
+        } else {
+            params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
+        }
+    }
+
+    @SuppressWarnings("unused")
+    static void setEndpointIdentificationAlgorithm(
+            SSLParameters params, String endpointIdentificationAlgorithm) {
+        params.setEndpointIdentificationAlgorithm(endpointIdentificationAlgorithm);
+    }
+
+    @SuppressWarnings("unused")
+    static String getEndpointIdentificationAlgorithm(SSLParameters params) {
+        return params.getEndpointIdentificationAlgorithm();
+    }
+
+    @SuppressWarnings("unused")
+    static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
+            AbstractConscryptSocket socket) throws CertificateException {
+        if (tm instanceof X509ExtendedTrustManager) {
+            X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
+            x509etm.checkClientTrusted(chain, authType, socket);
+        } else {
+            tm.checkClientTrusted(chain, authType);
+        }
+    }
+
+    @SuppressWarnings("unused")
+    static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
+            AbstractConscryptSocket socket) throws CertificateException {
+        if (tm instanceof X509ExtendedTrustManager) {
+            X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
+            x509etm.checkServerTrusted(chain, authType, socket);
+        } else {
+            tm.checkServerTrusted(chain, authType);
+        }
+    }
+
+    @SuppressWarnings("unused")
+    static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
+            ConscryptEngine engine) throws CertificateException {
+        if (tm instanceof X509ExtendedTrustManager) {
+            X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
+            x509etm.checkClientTrusted(chain, authType, engine);
+        } else {
+            tm.checkClientTrusted(chain, authType);
+        }
+    }
+
+    @SuppressWarnings("unused")
+    static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
+            ConscryptEngine engine) throws CertificateException {
+        if (tm instanceof X509ExtendedTrustManager) {
+            X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
+            x509etm.checkServerTrusted(chain, authType, engine);
+        } else {
+            tm.checkServerTrusted(chain, authType);
+        }
+    }
+
+    /**
+     * Wraps an old AndroidOpenSSL key instance. This is not needed on RI.
+     */
+    @SuppressWarnings("unused")
+    static OpenSSLKey wrapRsaKey(@SuppressWarnings("unused") PrivateKey javaKey) {
+        return null;
+    }
+
+    /**
+     * Logs to the system EventLog system.
+     */
+    @SuppressWarnings("unused")
+    static void logEvent(@SuppressWarnings("unused") String message) {}
+
+    /**
+     * For unbundled versions, SNI is always enabled by default.
+     */
+    @SuppressWarnings("unused")
+    static boolean isSniEnabledByDefault() {
+        return true;
+    }
+
+    static SSLEngine wrapEngine(ConscryptEngine engine) {
+        if (JAVA_VERSION >= 8) {
+            return Java8PlatformUtil.wrapEngine(engine);
+        }
+        return engine;
+    }
+
+    static SSLEngine unwrapEngine(SSLEngine engine) {
+        if (JAVA_VERSION >= 8) {
+            return Java8PlatformUtil.unwrapEngine(engine);
+        }
+        return engine;
+    }
+
+    static ConscryptEngineSocket createEngineSocket(SSLParametersImpl sslParameters)
+            throws IOException {
+        if (JAVA_VERSION >= 8) {
+            return new Java8EngineSocket(sslParameters);
+        }
+        return new ConscryptEngineSocket(sslParameters);
+    }
+
+    static ConscryptEngineSocket createEngineSocket(String hostname, int port,
+            SSLParametersImpl sslParameters) throws IOException {
+        if (JAVA_VERSION >= 8) {
+            return new Java8EngineSocket(hostname, port, sslParameters);
+        }
+        return new ConscryptEngineSocket(hostname, port, sslParameters);
+    }
+
+    static ConscryptEngineSocket createEngineSocket(InetAddress address, int port,
+            SSLParametersImpl sslParameters) throws IOException {
+        if (JAVA_VERSION >= 8) {
+            return new Java8EngineSocket(address, port, sslParameters);
+        }
+        return new ConscryptEngineSocket(address, port, sslParameters);
+    }
+
+    static ConscryptEngineSocket createEngineSocket(String hostname, int port,
+            InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
+            throws IOException {
+        if (JAVA_VERSION >= 8) {
+            return new Java8EngineSocket(hostname, port, clientAddress, clientPort, sslParameters);
+        }
+        return new ConscryptEngineSocket(hostname, port, clientAddress, clientPort, sslParameters);
+    }
+
+    static ConscryptEngineSocket createEngineSocket(InetAddress address, int port,
+            InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
+            throws IOException {
+        if (JAVA_VERSION >= 8) {
+            return new Java8EngineSocket(address, port, clientAddress, clientPort, sslParameters);
+        }
+        return new ConscryptEngineSocket(address, port, clientAddress, clientPort, sslParameters);
+    }
+
+    static ConscryptEngineSocket createEngineSocket(Socket socket, String hostname, int port,
+            boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
+        if (JAVA_VERSION >= 8) {
+            return new Java8EngineSocket(socket, hostname, port, autoClose, sslParameters);
+        }
+        return new ConscryptEngineSocket(socket, hostname, port, autoClose, sslParameters);
+    }
+
+    static ConscryptFileDescriptorSocket createFileDescriptorSocket(SSLParametersImpl sslParameters)
+            throws IOException {
+        if (JAVA_VERSION >= 8) {
+            return new Java8FileDescriptorSocket(sslParameters);
+        }
+        return new ConscryptFileDescriptorSocket(sslParameters);
+    }
+
+    static ConscryptFileDescriptorSocket createFileDescriptorSocket(String hostname, int port,
+            SSLParametersImpl sslParameters) throws IOException {
+        if (JAVA_VERSION >= 8) {
+            return new Java8FileDescriptorSocket(hostname, port, sslParameters);
+        }
+        return new ConscryptFileDescriptorSocket(hostname, port, sslParameters);
+    }
+
+    static ConscryptFileDescriptorSocket createFileDescriptorSocket(InetAddress address, int port,
+            SSLParametersImpl sslParameters) throws IOException {
+        if (JAVA_VERSION >= 8) {
+            return new Java8FileDescriptorSocket(address, port, sslParameters);
+        }
+        return new ConscryptFileDescriptorSocket(address, port, sslParameters);
+    }
+
+    static ConscryptFileDescriptorSocket createFileDescriptorSocket(String hostname, int port,
+            InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
+            throws IOException {
+        if (JAVA_VERSION >= 8) {
+            return new Java8FileDescriptorSocket(
+                    hostname, port, clientAddress, clientPort, sslParameters);
+        }
+        return new ConscryptFileDescriptorSocket(
+                hostname, port, clientAddress, clientPort, sslParameters);
+    }
+
+    static ConscryptFileDescriptorSocket createFileDescriptorSocket(InetAddress address, int port,
+            InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
+            throws IOException {
+        if (JAVA_VERSION >= 8) {
+            return new Java8FileDescriptorSocket(
+                    address, port, clientAddress, clientPort, sslParameters);
+        }
+        return new ConscryptFileDescriptorSocket(
+                address, port, clientAddress, clientPort, sslParameters);
+    }
+
+    static ConscryptFileDescriptorSocket createFileDescriptorSocket(Socket socket, String hostname,
+            int port, boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
+        if (JAVA_VERSION >= 8) {
+            return new Java8FileDescriptorSocket(socket, hostname, port, autoClose, sslParameters);
+        }
+        return new ConscryptFileDescriptorSocket(socket, hostname, port, autoClose, sslParameters);
+    }
+
+    /**
+     * Currently we don't wrap anything from the RI.
+     */
+    @SuppressWarnings("unused")
+    static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) {
+        return factory;
+    }
+
+    /**
+     * Convert from platform's GCMParameterSpec to our internal version.
+     */
+    @SuppressWarnings("unused")
+    static GCMParameters fromGCMParameterSpec(AlgorithmParameterSpec params) {
+        if (params instanceof GCMParameterSpec) {
+            GCMParameterSpec gcmParams = (GCMParameterSpec) params;
+            return new GCMParameters(gcmParams.getTLen(), gcmParams.getIV());
+        }
+        return null;
+    }
+
+    /**
+     * Convert from an opaque AlgorithmParameters to the platform's GCMParameterSpec.
+     */
+    static AlgorithmParameterSpec fromGCMParameters(AlgorithmParameters params) {
+        try {
+            return params.getParameterSpec(GCMParameterSpec.class);
+        } catch (InvalidParameterSpecException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Creates a platform version of {@code GCMParameterSpec}.
+     */
+    @SuppressWarnings("unused")
+    static AlgorithmParameterSpec toGCMParameterSpec(int tagLenInBits, byte[] iv) {
+        return new GCMParameterSpec(tagLenInBits, iv);
+    }
+
+    /*
+     * CloseGuard functions.
+     */
+
+    @SuppressWarnings("unused")
+    static Object closeGuardGet() {
+        return null;
+    }
+
+    @SuppressWarnings("unused")
+    static void closeGuardOpen(@SuppressWarnings("unused") Object guardObj,
+            @SuppressWarnings("unused") String message) {}
+
+    @SuppressWarnings("unused")
+    static void closeGuardClose(@SuppressWarnings("unused") Object guardObj) {}
+
+    @SuppressWarnings("unused")
+    static void closeGuardWarnIfOpen(@SuppressWarnings("unused") Object guardObj) {}
+
+    /*
+     * BlockGuard functions.
+     */
+
+    @SuppressWarnings("unused")
+    static void blockGuardOnNetwork() {}
+
+    /**
+     * OID to Algorithm Name mapping.
+     */
+    @SuppressWarnings("unused")
+    static String oidToAlgorithmName(String oid) {
+        try {
+            return AlgorithmId.get(oid).getName();
+        } catch (Exception e) {
+            return oid;
+        } catch (IllegalAccessError e) {
+            // This can happen under JPMS because AlgorithmId isn't exported by java.base
+            return oid;
+        }
+    }
+
+    /*
+     * Pre-Java-8 backward compatibility.
+     */
+
+    @SuppressWarnings("unused")
+    static SSLSession wrapSSLSession(ExternalSession sslSession) {
+        if (JAVA_VERSION >= 8) {
+            return Java8PlatformUtil.wrapSSLSession(sslSession);
+        }
+        return new Java7ExtendedSSLSession(sslSession);
+    }
+
+    public static String getOriginalHostNameFromInetAddress(InetAddress addr) {
+        try {
+            Method getHolder = InetAddress.class.getDeclaredMethod("holder");
+            getHolder.setAccessible(true);
+
+            Method getOriginalHostName = Class.forName("java.net.InetAddress$InetAddressHolder")
+                                                 .getDeclaredMethod("getOriginalHostName");
+            getOriginalHostName.setAccessible(true);
+
+            String originalHostName = (String) getOriginalHostName.invoke(getHolder.invoke(addr));
+            if (originalHostName == null) {
+                return addr.getHostAddress();
+            }
+            return originalHostName;
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException("Failed to get originalHostName", e);
+        } catch (ClassNotFoundException ignore) {
+            // passthrough and return addr.getHostAddress()
+        } catch (IllegalAccessException ignore) {
+        } catch (NoSuchMethodException ignore) {
+        }
+
+        return addr.getHostAddress();
+    }
+
+    @SuppressWarnings("unused")
+    static String getHostStringFromInetSocketAddress(InetSocketAddress addr) {
+        return addr.getHostString();
+    }
+
+    // OpenJDK always has X509ExtendedTrustManager
+    static boolean supportsX509ExtendedTrustManager() {
+        return true;
+    }
+
+    /**
+     * Check if SCT verification is required for a given hostname.
+     *
+     * SCT Verification is enabled using {@code Security} properties.
+     * The "conscrypt.ct.enable" property must be true, as well as a per domain property.
+     * The reverse notation of the domain name, prefixed with "conscrypt.ct.enforce."
+     * is used as the property name.
+     * Basic globbing is also supported.
+     *
+     * For example, for the domain foo.bar.com, the following properties will be
+     * looked up, in order of precedence.
+     * - conscrypt.ct.enforce.com.bar.foo
+     * - conscrypt.ct.enforce.com.bar.*
+     * - conscrypt.ct.enforce.com.*
+     * - conscrypt.ct.enforce.*
+     */
+    static boolean isCTVerificationRequired(String hostname) {
+        if (hostname == null) {
+            return false;
+        }
+
+        String property = Security.getProperty("conscrypt.ct.enable");
+        if (property == null || !Boolean.valueOf(property.toLowerCase())) {
+            return false;
+        }
+
+        List<String> parts = Arrays.asList(hostname.split("\\."));
+        Collections.reverse(parts);
+
+        boolean enable = false;
+        StringBuilder propertyName = new StringBuilder("conscrypt.ct.enforce");
+        // The loop keeps going on even once we've found a match
+        // This allows for finer grained settings on subdomains
+        for (String part : parts) {
+            property = Security.getProperty(propertyName + ".*");
+            if (property != null) {
+                enable = Boolean.valueOf(property.toLowerCase());
+            }
+
+            propertyName.append(".").append(part);
+        }
+
+        property = Security.getProperty(propertyName.toString());
+        if (property != null) {
+            enable = Boolean.valueOf(property.toLowerCase());
+        }
+        return enable;
+    }
+
+    static boolean supportsConscryptCertStore() {
+        return false;
+    }
+
+    static KeyStore getDefaultCertKeyStore() throws KeyStoreException {
+        // Start with an empty KeyStore.  In the worst case, we'll end up returning it.
+        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+        try {
+            ks.load(null, null);
+        } catch (CertificateException ignored) {
+        } catch (NoSuchAlgorithmException ignored) {
+        } catch (IOException ignored) {
+            // We're not loading anything, so ignore it
+        }
+        // Find the highest-priority non-Conscrypt provider that provides a PKIX
+        // TrustManagerFactory implementation and ask it for its trusted CAs.  This is most
+        // likely the OpenJDK-provided provider, in which case the platform default properties
+        // for configuring CA certs will be used, but we'll accept any provider that can give
+        // us at least one cert.
+        Provider[] providers = Security.getProviders("TrustManagerFactory.PKIX");
+        for (Provider p : providers) {
+            if (Conscrypt.isConscrypt(p)) {
+                // We need to skip any Conscrypt provider we find because this method is called
+                // when we're trying to determine the default set of CA certs for one of our
+                // TrustManagers, so trying to construct a TrustManager from this provider
+                // would result in calling this method again and recursing infinitely.
+                continue;
+            }
+            try {
+                TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX", p);
+                tmf.init((KeyStore) null);
+                TrustManager[] tms = tmf.getTrustManagers();
+                if (tms.length > 0) {
+                    // Aliases are irrelevant for our purposes, so just number the certs
+                    int certNum = 1;
+                    for (TrustManager tm : tms) {
+                        if (tm instanceof X509TrustManager) {
+                            X509TrustManager xtm = (X509TrustManager) tm;
+                            for (X509Certificate cert : xtm.getAcceptedIssuers()) {
+                                ks.setCertificateEntry(Integer.toString(certNum++), cert);
+                            }
+                        }
+                    }
+                    if (certNum > 1) {
+                        // We've loaded at least one certificate, so we're done.
+                        break;
+                    }
+                }
+            } catch (NoSuchAlgorithmException ignored) {
+                // This TrustManagerFactory didn't work, try another one
+            }
+        }
+        return ks;
+    }
+
+    static ConscryptCertStore newDefaultCertStore() {
+        return null;
+    }
+
+    static CertBlacklist newDefaultBlacklist() {
+        return null;
+    }
+
+    static CTLogStore newDefaultLogStore() {
+        return null;
+    }
+
+    static CTPolicy newDefaultPolicy(CTLogStore logStore) {
+        return null;
+    }
+
+    private static boolean isAndroid() {
+        boolean android;
+        try {
+            Class.forName("android.app.Application", false, getSystemClassLoader());
+            android = true;
+        } catch (Throwable ignored) {
+            // Failed to load the class uniquely available in Android.
+            android = false;
+        }
+        return android;
+    }
+
+    static int javaVersion() {
+        return JAVA_VERSION;
+    }
+
+    private static int javaVersion0() {
+        final int majorVersion;
+
+        if (isAndroid()) {
+            majorVersion = 6;
+        } else {
+            majorVersion = majorVersionFromJavaSpecificationVersion();
+        }
+
+        return majorVersion;
+    }
+
+    private static int majorVersionFromJavaSpecificationVersion() {
+        return majorVersion(System.getProperty("java.specification.version", "1.6"));
+    }
+
+    private static int majorVersion(final String javaSpecVersion) {
+        final String[] components = javaSpecVersion.split("\\.", -1);
+        final int[] version = new int[components.length];
+        for (int i = 0; i < components.length; i++) {
+            version[i] = Integer.parseInt(components[i]);
+        }
+
+        if (version[0] == 1) {
+            assert version[1] >= 6;
+            return version[1];
+        } else {
+            return version[0];
+        }
+    }
+
+    private static ClassLoader getSystemClassLoader() {
+        if (System.getSecurityManager() == null) {
+            return ClassLoader.getSystemClassLoader();
+        } else {
+            return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+                @Override
+                public ClassLoader run() {
+                    return ClassLoader.getSystemClassLoader();
+                }
+            });
+        }
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/AbstractSessionContextTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/AbstractSessionContextTest.java
new file mode 100644
index 0000000..e4520d8
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/AbstractSessionContextTest.java
@@ -0,0 +1,160 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.security.cert.Certificate;
+import javax.net.ssl.SSLSession;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class AbstractSessionContextTest<T extends AbstractSessionContext> {
+    private T context;
+
+    @Before
+    public void setup() {
+        context = newContext();
+    }
+
+    abstract T newContext();
+    abstract int size(T context);
+    private static NativeSslSession[] toArray(NativeSslSession... sessions) {
+        return sessions;
+    }
+
+    abstract NativeSslSession getCachedSession(T context, NativeSslSession s);
+
+    @Test
+    public void testSimpleAddition() {
+        NativeSslSession a = newSession("a");
+        NativeSslSession b = newSession("b");
+
+        context.cacheSession(a);
+        assertSessionContextContents(toArray(a), toArray(b));
+
+        context.cacheSession(b);
+        assertSessionContextContents(toArray(a, b), toArray());
+    }
+
+    @Test
+    public void testTrimToSize() {
+        NativeSslSession a = newSession("a");
+        NativeSslSession b = newSession("b");
+        NativeSslSession c = newSession("c");
+        NativeSslSession d = newSession("d");
+
+        context.cacheSession(a);
+        context.cacheSession(b);
+        context.cacheSession(c);
+        context.cacheSession(d);
+        assertSessionContextContents(toArray(a, b, c, d), toArray());
+
+        context.setSessionCacheSize(2);
+        assertSessionContextContents(toArray(c, d), toArray(a, b));
+    }
+
+    @Test
+    public void testImplicitRemovalOfOldest() {
+        context.setSessionCacheSize(2);
+        NativeSslSession a = newSession("a");
+        NativeSslSession b = newSession("b");
+        NativeSslSession c = newSession("c");
+        NativeSslSession d = newSession("d");
+
+        context.cacheSession(a);
+        assertSessionContextContents(toArray(a), toArray(b, c, d));
+
+        context.cacheSession(b);
+        assertSessionContextContents(toArray(a, b), toArray(c, d));
+
+        context.cacheSession(c);
+        assertSessionContextContents(toArray(b, c), toArray(a, d));
+
+        context.cacheSession(d);
+        assertSessionContextContents(toArray(c, d), toArray(a, b));
+    }
+
+    @Test
+    public void testRemoveIfSingleUse() {
+        NativeSslSession multi = new MockSessionBuilder().host("multi").singleUse(false).build();
+        NativeSslSession single = new MockSessionBuilder().host("single").singleUse(true).build();
+
+        context.cacheSession(multi);
+        assertEquals(1, size(context));
+
+        context.cacheSession(single);
+        assertEquals(2, size(context));
+
+        NativeSslSession out = getCachedSession(context, multi);
+        assertEquals(multi, out);
+        assertEquals(2, size(context));
+
+        out = getCachedSession(context, single);
+        assertEquals(single, out);
+        assertEquals(1, size(context));
+
+        assertNull(getCachedSession(context, single));
+    }
+
+    @Test
+    public void testSerializeSession() throws Exception {
+        Certificate mockCert = mock(Certificate.class);
+        when(mockCert.getEncoded()).thenReturn(new byte[] {0x05, 0x06, 0x07, 0x10});
+
+        byte[] encodedBytes = new byte[] {0x01, 0x02, 0x03};
+        NativeSslSession session = new MockSessionBuilder()
+                .id(new byte[] {0x11, 0x09, 0x03, 0x20})
+                .host("ssl.example.com")
+                .encodedBytes(encodedBytes)
+                .build();
+
+        SSLClientSessionCache mockCache = mock(SSLClientSessionCache.class);
+        ClientSessionContext context = new ClientSessionContext();
+        context.setPersistentCache(mockCache);
+
+        context.cacheSession(session);
+        verify(mockCache).putSessionData(any(SSLSession.class), same(encodedBytes));
+    }
+
+    private void assertSessionContextContents(
+            NativeSslSession[] contains, NativeSslSession[] excludes) {
+        assertEquals(contains.length, size(context));
+
+        for (NativeSslSession s : contains) {
+            assertSame(s.getPeerHost(), s, getCachedSession(context, s));
+        }
+        for (NativeSslSession s : excludes) {
+            assertNull(s.getPeerHost(), getCachedSession(context, s));
+        }
+    }
+
+    private NativeSslSession newSession(String host) {
+        return new MockSessionBuilder().host(host).build();
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/AddressUtilsTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/AddressUtilsTest.java
new file mode 100644
index 0000000..bdf1fdd
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/AddressUtilsTest.java
@@ -0,0 +1,102 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import junit.framework.TestCase;
+
+/**
+ * Test for AddressUtils
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AddressUtilsTest extends TestCase {
+    public void test_isValidSniHostname_Success() throws Exception {
+        assertTrue(AddressUtils.isValidSniHostname("www.google.com"));
+    }
+
+    public void test_isValidSniHostname_NotFQDN_Failure() throws Exception {
+        assertFalse(AddressUtils.isValidSniHostname("www"));
+    }
+
+    public void test_isValidSniHostname_Localhost_Success() throws Exception {
+        assertTrue(AddressUtils.isValidSniHostname("LOCALhost"));
+    }
+
+    public void test_isValidSniHostname_IPv4_Failure() throws Exception {
+        assertFalse(AddressUtils.isValidSniHostname("192.168.0.1"));
+    }
+
+    public void test_isValidSniHostname_IPv6_Failure() throws Exception {
+        assertFalse(AddressUtils.isValidSniHostname("2001:db8::1"));
+    }
+
+    public void test_isValidSniHostname_TrailingDot() throws Exception {
+        assertFalse(AddressUtils.isValidSniHostname("www.google.com."));
+    }
+
+    public void test_isValidSniHostname_NullByte() throws Exception {
+        assertFalse(AddressUtils.isValidSniHostname("www\0.google.com"));
+    }
+
+    public void test_isLiteralIpAddress_IPv4_Success() throws Exception {
+        assertTrue(AddressUtils.isLiteralIpAddress("127.0.0.1"));
+        assertTrue(AddressUtils.isLiteralIpAddress("255.255.255.255"));
+        assertTrue(AddressUtils.isLiteralIpAddress("0.0.00.000"));
+        assertTrue(AddressUtils.isLiteralIpAddress("192.009.010.19"));
+        assertTrue(AddressUtils.isLiteralIpAddress("254.249.190.094"));
+    }
+
+    public void test_isLiteralIpAddress_IPv4_ExtraCharacters_Failure() throws Exception {
+        assertFalse(AddressUtils.isLiteralIpAddress("127.0.0.1a"));
+        assertFalse(AddressUtils.isLiteralIpAddress(" 255.255.255.255"));
+        assertFalse(AddressUtils.isLiteralIpAddress("0.0.00.0009"));
+        assertFalse(AddressUtils.isLiteralIpAddress("192.009z.010.19"));
+        assertFalse(AddressUtils.isLiteralIpAddress("254.249..094"));
+        assertFalse(AddressUtils.isLiteralIpAddress("192.168.2.1%1"));
+        assertFalse(AddressUtils.isLiteralIpAddress("192.168.2.1%eth0"));
+    }
+
+    public void test_isLiteralIpAddress_IPv4_NumbersTooLarge_Failure() throws Exception {
+        assertFalse(AddressUtils.isLiteralIpAddress("256.255.255.255"));
+        assertFalse(AddressUtils.isLiteralIpAddress("255.255.255.256"));
+        assertFalse(AddressUtils.isLiteralIpAddress("192.168.1.260"));
+    }
+
+    public void test_isLiteralIpAddress_IPv6_Success() throws Exception {
+        assertTrue(AddressUtils.isLiteralIpAddress("::1"));
+        assertTrue(AddressUtils.isLiteralIpAddress("2001:Db8::1"));
+        assertTrue(AddressUtils.isLiteralIpAddress("2001:cdbA:0000:0000:0000:0000:3257:9652"));
+        assertTrue(AddressUtils.isLiteralIpAddress("2001:cdba:0:0:0:0:3257:9652"));
+        assertTrue(AddressUtils.isLiteralIpAddress("2001:cdBA::3257:9652"));
+        assertTrue(AddressUtils.isLiteralIpAddress("2001:cdba::3257:9652%1"));
+        assertTrue(AddressUtils.isLiteralIpAddress("2001:cdba::3257:9652%eth0"));
+        assertTrue(AddressUtils.isLiteralIpAddress("2001:cdba::3257:9652%int2.3!"));
+    }
+
+    public void test_isLiteralIpAddress_IPv6_Failure() throws Exception {
+        assertFalse(AddressUtils.isLiteralIpAddress(":::1"));
+        assertFalse(AddressUtils.isLiteralIpAddress("::11111"));
+        assertFalse(AddressUtils.isLiteralIpAddress("20011::1111"));
+        assertFalse(AddressUtils.isLiteralIpAddress("2001:db8:::1"));
+        assertFalse(AddressUtils.isLiteralIpAddress("2001:cdba:0000:00000:0000:0000:3257:9652"));
+        assertFalse(AddressUtils.isLiteralIpAddress("2001:cdbA:0000:0000:0000:0000:0000:3257:9652"));
+        assertFalse(AddressUtils.isLiteralIpAddress("2001:cdba:0::0:0:0:3257:9652"));
+        assertFalse(AddressUtils.isLiteralIpAddress("02001:cdba::3257:9652"));
+        assertFalse(AddressUtils.isLiteralIpAddress("2001:cdba::3257:96521"));
+        assertFalse(AddressUtils.isLiteralIpAddress("2001:cdba::3257:9652%"));
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ApplicationProtocolSelectorAdapterTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ApplicationProtocolSelectorAdapterTest.java
new file mode 100644
index 0000000..adfe804
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ApplicationProtocolSelectorAdapterTest.java
@@ -0,0 +1,92 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.when;
+
+import java.nio.charset.Charset;
+import javax.net.ssl.SSLEngine;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ApplicationProtocolSelectorAdapterTest {
+    private static Charset US_ASCII = Charset.forName("US-ASCII");
+    private static final String[] PROTOCOLS = new String[] {"a", "b", "c"};
+    private static final byte[] PROTOCOL_BYTES = SSLUtils.encodeProtocols(PROTOCOLS);
+
+    @Mock private ApplicationProtocolSelector selector;
+
+    @Mock private SSLEngine engine;
+
+    private ApplicationProtocolSelectorAdapter adapter;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        adapter = new ApplicationProtocolSelectorAdapter(engine, selector);
+    }
+
+    @Test
+    public void nullProtocolsShouldNotSelect() {
+        mockSelection("a");
+        assertEquals(-1, select(null));
+    }
+
+    @Test
+    public void emptyProtocolsShouldNotSelect() {
+        mockSelection("a");
+        assertEquals(-1, select(EmptyArray.BYTE));
+    }
+
+    @Test
+    public void selectCorrectProtocol() {
+        for (String protocol : PROTOCOLS) {
+            mockSelection(protocol);
+            assertEquals(protocol, getProtocolAt(select(PROTOCOL_BYTES)));
+        }
+    }
+
+    @Test
+    public void invalidProtocolShouldNotSelect() {
+        mockSelection("d");
+        assertEquals(-1, select(PROTOCOL_BYTES));
+    }
+
+    private int select(byte[] protocols) {
+        return adapter.selectApplicationProtocol(protocols);
+    }
+
+    private void mockSelection(String returnValue) {
+        when(selector.selectApplicationProtocol(same(engine), Matchers.anyListOf(String.class)))
+                .thenReturn(returnValue);
+    }
+
+    private String getProtocolAt(int index) {
+        int len = PROTOCOL_BYTES[index];
+        return new String(PROTOCOL_BYTES, index + 1, len, US_ASCII);
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ClientSessionContextTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ClientSessionContextTest.java
new file mode 100644
index 0000000..a05ba8f
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ClientSessionContextTest.java
@@ -0,0 +1,120 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.MockSessionBuilder.DEFAULT_PORT;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import java.security.KeyManagementException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class ClientSessionContextTest extends AbstractSessionContextTest<ClientSessionContext> {
+
+    @Override
+    ClientSessionContext newContext() {
+        return new ClientSessionContext();
+    }
+
+    @Override
+    NativeSslSession getCachedSession(ClientSessionContext context, NativeSslSession s) {
+        return context.getCachedSession(s.getPeerHost(), DEFAULT_PORT,
+                getDefaultSSLParameters());
+    }
+
+    @Override
+    int size(ClientSessionContext context) {
+        return context.size();
+    }
+
+    private static SSLParametersImpl getDefaultSSLParameters() {
+        try {
+            return SSLParametersImpl.getDefault();
+        } catch (KeyManagementException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    public void testNoMixingOfSingleAndMultiUseSessions() {
+        ClientSessionContext context = newContext();
+
+        NativeSslSession a = new MockSessionBuilder().host("a").singleUse(false).build();
+        NativeSslSession bSingle1 = new MockSessionBuilder()
+                .id(new byte[] {1}).host("b").singleUse(true).build();
+        NativeSslSession bSingle2 = new MockSessionBuilder()
+                .id(new byte[] {2}).host("b").singleUse(true).build();
+        NativeSslSession bMulti = new MockSessionBuilder()
+                .id(new byte[] {3}).host("b").singleUse(false).build();
+
+        context.cacheSession(a);
+        assertEquals(1, size(context));
+
+        context.cacheSession(bSingle1);
+        assertEquals(2, size(context));
+
+        context.cacheSession(bSingle2);
+        assertEquals(3, size(context));
+
+        context.cacheSession(bMulti);
+        assertEquals(2, size(context));
+
+        NativeSslSession out = context.getCachedSession(
+                "b", DEFAULT_PORT, getDefaultSSLParameters());
+        assertEquals(bMulti, out);
+
+        context.cacheSession(bSingle2);
+        assertEquals(2, size(context));
+
+        out = context.getCachedSession("b", DEFAULT_PORT, getDefaultSSLParameters());
+        assertEquals(bSingle2, out);
+
+        out = context.getCachedSession("b", DEFAULT_PORT, getDefaultSSLParameters());
+        assertNull(out);
+    }
+
+    @Test
+    public void testCanRetrieveMultipleSingleUseSessions() {
+        ClientSessionContext context = newContext();
+
+        NativeSslSession single1 = new MockSessionBuilder()
+                .id(new byte[] {1}).host("host").singleUse(true).build();
+        NativeSslSession single2 = new MockSessionBuilder()
+                .id(new byte[] {2}).host("host").singleUse(true).build();
+
+        context.cacheSession(single1);
+        assertEquals(1, size(context));
+
+        context.cacheSession(single2);
+        assertEquals(2, size(context));
+
+        assertSame(single1,
+                context.getCachedSession("host", DEFAULT_PORT, getDefaultSSLParameters()));
+        assertEquals(1, size(context));
+        assertSame(single2,
+                context.getCachedSession("host", DEFAULT_PORT, getDefaultSSLParameters()));
+        assertEquals(0, size(context));
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ConscryptEngineTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ConscryptEngineTest.java
new file mode 100644
index 0000000..a92b6d8
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ConscryptEngineTest.java
@@ -0,0 +1,555 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.TestUtils.getConscryptProvider;
+import static com.android.org.conscrypt.TestUtils.getJdkProvider;
+import static com.android.org.conscrypt.TestUtils.getProtocols;
+import static com.android.org.conscrypt.TestUtils.initSslContext;
+import static com.android.org.conscrypt.TestUtils.newTextMessage;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLSession;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(Parameterized.class)
+public class ConscryptEngineTest {
+    private static final int MESSAGE_SIZE = 4096;
+    private static final int LARGE_MESSAGE_SIZE = 16413;
+    private static final String[] CIPHERS = TestUtils.getCommonCipherSuites();
+    private static final String RENEGOTIATION_CIPHER = CIPHERS[CIPHERS.length - 1];
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    @SuppressWarnings("ImmutableEnumChecker")
+    public enum BufferType {
+        HEAP_ALLOCATOR(BufferAllocator.unpooled()) {
+            @Override
+            ByteBuffer newBuffer(int size) {
+                return ByteBuffer.allocate(size);
+            }
+        },
+        HEAP_NO_ALLOCATOR(null) {
+            @Override
+            ByteBuffer newBuffer(int size) {
+                return ByteBuffer.allocate(size);
+            }
+        },
+        DIRECT(null) {
+            @Override
+            ByteBuffer newBuffer(int size) {
+                return ByteBuffer.allocateDirect(size);
+            }
+        };
+
+        abstract ByteBuffer newBuffer(int size);
+
+        BufferType(BufferAllocator allocator) {
+            this.allocator = allocator;
+        }
+
+        private final BufferAllocator allocator;
+    }
+
+    private enum ClientAuth {
+        NONE {
+            @Override
+            void apply(SSLEngine engine) {
+                engine.setWantClientAuth(false);
+                engine.setNeedClientAuth(false);
+            }
+        },
+        OPTIONAL {
+            @Override
+            void apply(SSLEngine engine) {
+                engine.setWantClientAuth(true);
+                engine.setNeedClientAuth(false);
+            }
+        },
+        REQUIRED {
+            @Override
+            void apply(SSLEngine engine) {
+                engine.setWantClientAuth(false);
+                engine.setNeedClientAuth(true);
+            }
+        };
+
+        abstract void apply(SSLEngine engine);
+    }
+
+    @Parameters(name = "{0}")
+    public static Iterable<BufferType> data() {
+        return Arrays.asList(
+                BufferType.HEAP_ALLOCATOR, BufferType.HEAP_NO_ALLOCATOR, BufferType.DIRECT);
+    }
+
+    @Parameter public BufferType bufferType;
+
+    private SSLEngine clientEngine;
+    private SSLEngine serverEngine;
+
+    @Test
+    public void closingOutboundBeforeHandshakeShouldCloseAll() throws Exception {
+        setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
+        assertFalse(clientEngine.isInboundDone());
+        assertFalse(clientEngine.isOutboundDone());
+        assertFalse(serverEngine.isInboundDone());
+        assertFalse(serverEngine.isOutboundDone());
+
+        clientEngine.closeOutbound();
+        serverEngine.closeOutbound();
+
+        assertTrue(clientEngine.isInboundDone());
+        assertTrue(clientEngine.isOutboundDone());
+        assertTrue(serverEngine.isInboundDone());
+        assertTrue(serverEngine.isOutboundDone());
+    }
+
+    @Test
+    public void closingOutboundAfterHandshakeShouldOnlyCloseOutbound() throws Exception {
+        setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
+        doHandshake(true);
+
+        assertFalse(clientEngine.isInboundDone());
+        assertFalse(clientEngine.isOutboundDone());
+        assertFalse(serverEngine.isInboundDone());
+        assertFalse(serverEngine.isOutboundDone());
+
+        clientEngine.closeOutbound();
+        serverEngine.closeOutbound();
+
+        // After closing the outbound direction, a shutdown alert should still be pending
+        assertFalse(clientEngine.isOutboundDone());
+        assertFalse(serverEngine.isOutboundDone());
+
+        ByteBuffer drain =
+                bufferType.newBuffer(Math.max(clientEngine.getSession().getPacketBufferSize(),
+                        serverEngine.getSession().getPacketBufferSize()));
+        clientEngine.wrap(ByteBuffer.wrap(new byte[0]), drain);
+        drain.clear();
+        serverEngine.wrap(ByteBuffer.wrap(new byte[0]), drain);
+
+        assertTrue(clientEngine.isOutboundDone());
+        assertTrue(serverEngine.isOutboundDone());
+
+        // The inbound directions should still be open
+        assertFalse(clientEngine.isInboundDone());
+        assertFalse(serverEngine.isInboundDone());
+    }
+
+    @Test
+    public void closingInboundShouldOnlyCloseInbound() throws Exception {
+        setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
+        doHandshake(true);
+
+        assertFalse(clientEngine.isInboundDone());
+        assertFalse(clientEngine.isOutboundDone());
+        assertFalse(serverEngine.isInboundDone());
+        assertFalse(serverEngine.isOutboundDone());
+
+        clientEngine.closeInbound();
+        serverEngine.closeInbound();
+
+        assertTrue(clientEngine.isInboundDone());
+        assertFalse(clientEngine.isOutboundDone());
+        assertTrue(serverEngine.isInboundDone());
+        assertFalse(serverEngine.isOutboundDone());
+    }
+
+    @Test
+    public void mutualAuthWithSameCertsShouldSucceed() throws Exception {
+        doMutualAuthHandshake(TestKeyStore.getServer(), TestKeyStore.getServer(), ClientAuth.NONE);
+    }
+
+    @Test
+    public void mutualAuthWithDifferentCertsShouldSucceed() throws Exception {
+        doMutualAuthHandshake(TestKeyStore.getClient(), TestKeyStore.getServer(), ClientAuth.NONE);
+    }
+
+    @Test(expected = SSLHandshakeException.class)
+    public void mutualAuthWithUntrustedServerShouldFail() throws Exception {
+        doMutualAuthHandshake(
+                TestKeyStore.getClientCA2(), TestKeyStore.getServer(), ClientAuth.NONE);
+    }
+
+    @Test(expected = SSLHandshakeException.class)
+    public void mutualAuthWithUntrustedClientShouldFail() throws Exception {
+        doMutualAuthHandshake(TestKeyStore.getClient(), TestKeyStore.getClient(), ClientAuth.NONE);
+    }
+
+    @Test
+    public void optionalClientAuthShouldSucceed() throws Exception {
+        doMutualAuthHandshake(
+                TestKeyStore.getClient(), TestKeyStore.getServer(), ClientAuth.OPTIONAL);
+    }
+
+    @Test(expected = SSLHandshakeException.class)
+    public void optionalClientAuthShouldFail() throws Exception {
+        doMutualAuthHandshake(
+                TestKeyStore.getClient(), TestKeyStore.getClient(), ClientAuth.OPTIONAL);
+    }
+
+    @Test
+    public void requiredClientAuthShouldSucceed() throws Exception {
+        doMutualAuthHandshake(
+                TestKeyStore.getServer(), TestKeyStore.getServer(), ClientAuth.REQUIRED);
+    }
+
+    @Test(expected = SSLHandshakeException.class)
+    public void requiredClientAuthShouldFail() throws Exception {
+        doMutualAuthHandshake(
+                TestKeyStore.getClient(), TestKeyStore.getClient(), ClientAuth.REQUIRED);
+    }
+
+    @Test
+    public void exchangeMessages() throws Exception {
+        setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
+        doHandshake(true);
+
+        ByteBuffer message = newMessage(MESSAGE_SIZE);
+        byte[] messageBytes = toArray(message);
+
+        // Wrap the original message and create the encrypted data.
+        final int numMessages = 100;
+        ByteBuffer[] encryptedBuffers = new ByteBuffer[numMessages];
+        for (int i = 0; i < numMessages; ++i) {
+            List<ByteBuffer> wrapped = wrap(message.duplicate(), clientEngine);
+            // Small message, we should only have 1 buffer created.
+            assertEquals(1, wrapped.size());
+            encryptedBuffers[i] = wrapped.get(0);
+        }
+
+        // Unwrap the all of the encrypted messages.
+        byte[] actualBytes = unwrap(encryptedBuffers, serverEngine);
+        assertEquals(MESSAGE_SIZE * numMessages, actualBytes.length);
+        for (int i = 0; i < numMessages; ++i) {
+            int offset = i * MESSAGE_SIZE;
+            byte[] actualMessageBytes =
+                    Arrays.copyOfRange(actualBytes, offset, offset + MESSAGE_SIZE);
+            assertArrayEquals(messageBytes, actualMessageBytes);
+        }
+    }
+
+    @Test
+    public void exchangeLargeMessage() throws Exception {
+        setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
+        doHandshake(true);
+
+        ByteBuffer inputBuffer = newMessage(LARGE_MESSAGE_SIZE);
+        exchangeMessage(inputBuffer, clientEngine, serverEngine);
+    }
+
+    @Test
+    public void alpnWithProtocolListShouldSucceed() throws Exception {
+        setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
+
+        // Configure ALPN protocols
+        String[] clientAlpnProtocols = new String[]{"http/1.1", "foo", "spdy/2"};
+        String[] serverAlpnProtocols = new String[]{"spdy/2", "foo", "bar"};
+
+        Conscrypt.setApplicationProtocols(clientEngine, clientAlpnProtocols);
+        Conscrypt.setApplicationProtocols(serverEngine, serverAlpnProtocols);
+
+        doHandshake(true);
+        assertEquals("spdy/2", Conscrypt.getApplicationProtocol(clientEngine));
+        assertEquals("spdy/2", Conscrypt.getApplicationProtocol(serverEngine));
+    }
+
+    @Test
+    public void alpnWithProtocolListShouldFail() throws Exception {
+        setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
+
+        // Configure ALPN protocols
+        String[] clientAlpnProtocols = new String[]{"http/1.1", "foo", "spdy/2"};
+        String[] serverAlpnProtocols = new String[]{"h2", "bar", "baz"};
+
+        Conscrypt.setApplicationProtocols(clientEngine, clientAlpnProtocols);
+        Conscrypt.setApplicationProtocols(serverEngine, serverAlpnProtocols);
+
+        doHandshake(true);
+        assertNull(Conscrypt.getApplicationProtocol(clientEngine));
+        assertNull(Conscrypt.getApplicationProtocol(serverEngine));
+    }
+
+    @Test
+    public void alpnWithServerProtocolSelectorShouldSucceed() throws Exception {
+        setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
+
+        // Configure client protocols.
+        String[] clientAlpnProtocols = new String[]{"http/1.1", "foo", "spdy/2"};
+        Conscrypt.setApplicationProtocols(clientEngine, clientAlpnProtocols);
+
+        // Configure server selector
+        ApplicationProtocolSelector selector = Mockito.mock(ApplicationProtocolSelector.class);
+        when(selector.selectApplicationProtocol(same(serverEngine), Matchers.anyListOf(String.class)))
+                .thenReturn("spdy/2");
+        Conscrypt.setApplicationProtocolSelector(serverEngine, selector);
+
+        doHandshake(true);
+        assertEquals("spdy/2", Conscrypt.getApplicationProtocol(clientEngine));
+        assertEquals("spdy/2", Conscrypt.getApplicationProtocol(serverEngine));
+    }
+
+    @Test
+    public void alpnWithServerProtocolSelectorShouldFail() throws Exception {
+        setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
+
+        // Configure client protocols.
+        String[] clientAlpnProtocols = new String[]{"http/1.1", "foo", "spdy/2"};
+        Conscrypt.setApplicationProtocols(clientEngine, clientAlpnProtocols);
+
+        // Configure server selector
+        ApplicationProtocolSelector selector = Mockito.mock(ApplicationProtocolSelector.class);
+        when(selector.selectApplicationProtocol(same(serverEngine), Matchers.anyListOf(String.class)))
+                .thenReturn("h2");
+        Conscrypt.setApplicationProtocolSelector(serverEngine, selector);
+
+        doHandshake(true);
+        assertNull(Conscrypt.getApplicationProtocol(clientEngine));
+        assertNull(Conscrypt.getApplicationProtocol(serverEngine));
+    }
+
+    /**
+     * BoringSSL server doesn't support renegotiation. BoringSSL clients do not support
+     * initiating a renegotiation, only processing a renegotiation initiated by the
+     * (non-BoringSSL) server. For this reason we test a server-initiated renegotiation with
+     * a Conscrypt client and a JDK server.
+     */
+    @Test
+    public void serverInitiatedRenegotiationShouldSucceed() throws Exception {
+        setupClientEngine(getConscryptProvider(), TestKeyStore.getClient());
+        setupServerEngine(getJdkProvider(), TestKeyStore.getServer());
+
+        // Perform the initial handshake.
+        doHandshake(true);
+
+        // Send a message from client->server.
+        exchangeMessage(newMessage(MESSAGE_SIZE), clientEngine, serverEngine);
+
+        // Trigger a renegotiation from the server and send a message back from Server->Client
+        serverEngine.setEnabledCipherSuites(new String[] {RENEGOTIATION_CIPHER});
+        serverEngine.beginHandshake();
+        doHandshake(false);
+
+        exchangeMessage(newMessage(MESSAGE_SIZE), serverEngine, clientEngine);
+    }
+
+    @Test
+    public void savedSessionWorksAfterClose() throws Exception {
+        setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
+        doHandshake(true);
+
+        SSLSession session = clientEngine.getSession();
+        String cipherSuite = session.getCipherSuite();
+
+        clientEngine.closeOutbound();
+        clientEngine.closeInbound();
+
+        assertEquals(cipherSuite, session.getCipherSuite());
+    }
+
+    private void doMutualAuthHandshake(
+            TestKeyStore clientKs, TestKeyStore serverKs, ClientAuth clientAuth) throws Exception {
+        setupEngines(clientKs, serverKs);
+        clientAuth.apply(serverEngine);
+        doHandshake(true);
+        assertEquals(HandshakeStatus.NOT_HANDSHAKING, clientEngine.getHandshakeStatus());
+        assertEquals(HandshakeStatus.NOT_HANDSHAKING, serverEngine.getHandshakeStatus());
+    }
+
+    private void doHandshake(boolean beginHandshake) throws SSLException {
+        ByteBuffer clientApplicationBuffer =
+                bufferType.newBuffer(clientEngine.getSession().getApplicationBufferSize());
+        ByteBuffer clientPacketBuffer =
+                bufferType.newBuffer(clientEngine.getSession().getPacketBufferSize());
+        ByteBuffer serverApplicationBuffer =
+                bufferType.newBuffer(serverEngine.getSession().getApplicationBufferSize());
+        ByteBuffer serverPacketBuffer =
+                bufferType.newBuffer(serverEngine.getSession().getPacketBufferSize());
+        TestUtils.doEngineHandshake(clientEngine, serverEngine, clientApplicationBuffer,
+                clientPacketBuffer, serverApplicationBuffer, serverPacketBuffer, beginHandshake);
+    }
+
+    private void setupEngines(TestKeyStore clientKeyStore, TestKeyStore serverKeyStore) throws SSLException {
+        setupClientEngine(getConscryptProvider(), clientKeyStore);
+        setupServerEngine(getConscryptProvider(), serverKeyStore);
+    }
+
+    private void setupClientEngine(Provider provider, TestKeyStore clientKeyStore)
+            throws SSLException {
+        clientEngine = newEngine(provider, clientKeyStore, true);
+    }
+
+    private void setupServerEngine(Provider provider, TestKeyStore serverKeyStore)
+            throws SSLException {
+        serverEngine = newEngine(provider, serverKeyStore, false);
+    }
+
+    private SSLEngine newEngine(
+            Provider provider, TestKeyStore keyStore, boolean client) {
+        SSLContext serverContext = newContext(provider, keyStore);
+        SSLEngine engine = serverContext.createSSLEngine();
+        engine.setEnabledCipherSuites(CIPHERS);
+        engine.setUseClientMode(client);
+        if (Conscrypt.isConscrypt(engine)) {
+            Conscrypt.setBufferAllocator(engine, bufferType.allocator);
+        }
+        return engine;
+    }
+
+    private void exchangeMessage(ByteBuffer inputBuffer, SSLEngine src, SSLEngine dest)
+            throws IOException {
+        byte[] messageBytes = toArray(inputBuffer);
+
+        // Encrypt the input message.
+        List<ByteBuffer> encryptedBufferList = wrap(inputBuffer, src);
+
+        // Unwrap the all of the encrypted messages.
+        ByteBuffer[] encryptedBuffers =
+                encryptedBufferList.toArray(new ByteBuffer[encryptedBufferList.size()]);
+        byte[] actualBytes = unwrap(encryptedBuffers, dest);
+        assertArrayEquals(messageBytes, actualBytes);
+    }
+
+    private List<ByteBuffer> wrap(ByteBuffer input, SSLEngine engine) throws SSLException {
+        // Encrypt the input message.
+        List<ByteBuffer> wrapped = new ArrayList<ByteBuffer>();
+        while (input.hasRemaining()) {
+            ByteBuffer encryptedBuffer =
+                    bufferType.newBuffer(engine.getSession().getPacketBufferSize());
+            SSLEngineResult wrapResult = engine.wrap(input, encryptedBuffer);
+            assertEquals(SSLEngineResult.Status.OK, wrapResult.getStatus());
+            encryptedBuffer.flip();
+            wrapped.add(encryptedBuffer);
+        }
+        return wrapped;
+    }
+
+    private byte[] unwrap(ByteBuffer[] encryptedBuffers, SSLEngine engine) throws IOException {
+        ByteArrayOutputStream cleartextStream = new ByteArrayOutputStream();
+        int decryptedBufferSize = 8192;
+        final ByteBuffer encryptedBuffer = combine(encryptedBuffers);
+        final ByteBuffer decryptedBuffer = bufferType.newBuffer(decryptedBufferSize);
+        while (encryptedBuffer.hasRemaining()) {
+            if (!decryptedBuffer.hasRemaining()) {
+                decryptedBuffer.clear();
+            }
+            int prevPos = decryptedBuffer.position();
+            SSLEngineResult unwrapResult = engine.unwrap(encryptedBuffer, decryptedBuffer);
+            SSLEngineResult.Status status = unwrapResult.getStatus();
+            switch (status) {
+                case BUFFER_OVERFLOW:
+                case OK: {
+                    break;
+                }
+                default: { throw new RuntimeException("Unexpected SSLEngine status: " + status); }
+            }
+            int newPos = decryptedBuffer.position();
+            int bytesProduced = unwrapResult.bytesProduced();
+            assertEquals(bytesProduced, newPos - prevPos);
+
+            // Add any generated bytes to the output stream.
+            if (bytesProduced > 0 || status == Status.BUFFER_OVERFLOW) {
+                byte[] decryptedBytes = new byte[unwrapResult.bytesProduced()];
+
+                // Read the chunk that was just written to the output array.
+                int limit = decryptedBuffer.limit();
+                decryptedBuffer.limit(newPos);
+                decryptedBuffer.position(prevPos);
+                decryptedBuffer.get(decryptedBytes);
+
+                // Restore the position and limit.
+                decryptedBuffer.limit(limit);
+
+                // Write the decrypted bytes to the stream.
+                cleartextStream.write(decryptedBytes);
+            }
+        }
+
+        return cleartextStream.toByteArray();
+    }
+
+    private ByteBuffer combine(ByteBuffer[] buffers) {
+        int size = 0;
+        for (ByteBuffer buffer : buffers) {
+            size += buffer.remaining();
+        }
+        ByteBuffer combined = bufferType.newBuffer(size);
+        for (ByteBuffer buffer : buffers) {
+            combined.put(buffer);
+        }
+        combined.flip();
+        return combined;
+    }
+
+    private ByteBuffer newMessage(int size) {
+        ByteBuffer buffer = bufferType.newBuffer(size);
+        buffer.put(newTextMessage(size));
+        buffer.flip();
+        return buffer;
+    }
+
+    private static byte[] toArray(ByteBuffer buffer) {
+        int pos = buffer.position();
+        byte[] bytes = new byte[buffer.remaining()];
+        buffer.get(bytes);
+        buffer.position(pos);
+        return bytes;
+    }
+
+    private static SSLContext newContext(Provider provider, TestKeyStore keyStore) {
+        try {
+            SSLContext ctx = SSLContext.getInstance(getProtocols()[0], provider);
+            return initSslContext(ctx, keyStore);
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ConscryptSocketTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ConscryptSocketTest.java
new file mode 100644
index 0000000..2ffbbf9
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ConscryptSocketTest.java
@@ -0,0 +1,649 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.TestUtils.openTestFile;
+import static com.android.org.conscrypt.TestUtils.readTestFile;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyListOf;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.mockito.Mockito;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(Parameterized.class)
+public class ConscryptSocketTest {
+    private static final long TIMEOUT_SECONDS = 5;
+    private static final char[] EMPTY_PASSWORD = new char[0];
+
+    @BeforeClass
+    public static void installConscrypt() {
+        TestUtils.installConscryptAsDefaultProvider();
+    }
+
+    @AfterClass
+    public static void removeConscrypt() {
+        Security.removeProvider(TestUtils.getConscryptProvider().getName());
+    }
+
+    /**
+     * Factories for underlying sockets.
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum UnderlyingSocketType {
+        NONE {
+            @Override
+            Socket newClientSocket(
+                    OpenSSLContextImpl context, ServerSocket server, SSLSocketFactory factory) {
+                return null;
+            }
+
+            @Override
+            Socket newServerSocket(OpenSSLContextImpl context, ServerSocket server,
+                    SSLSocketFactory factory) throws IOException {
+                return server.accept();
+            }
+        },
+        PLAIN {
+            @Override
+            Socket newClientSocket(OpenSSLContextImpl context, ServerSocket server,
+                    SSLSocketFactory factory) throws IOException {
+                return new Socket(server.getInetAddress(), server.getLocalPort());
+            }
+
+            @Override
+            Socket newServerSocket(OpenSSLContextImpl context, ServerSocket server,
+                    SSLSocketFactory factory) throws IOException {
+                return server.accept();
+            }
+        },
+        SSL {
+            @Override
+            Socket newClientSocket(OpenSSLContextImpl context, ServerSocket server,
+                    SSLSocketFactory factory) throws IOException {
+                SSLSocket sslSocket = (SSLSocket) factory.createSocket(
+                        server.getInetAddress(), server.getLocalPort());
+                sslSocket.setUseClientMode(true);
+                return sslSocket;
+            }
+
+            @Override
+            Socket newServerSocket(OpenSSLContextImpl context, ServerSocket server,
+                    SSLSocketFactory factory) throws IOException {
+                SSLSocket sslSocket =
+                        (SSLSocket) factory.createSocket(server.accept(), null, -1, true);
+                sslSocket.setUseClientMode(false);
+                return sslSocket;
+            }
+        };
+
+        abstract Socket newClientSocket(OpenSSLContextImpl context, ServerSocket server,
+                SSLSocketFactory factory) throws IOException;
+        abstract Socket newServerSocket(OpenSSLContextImpl context, ServerSocket server,
+                SSLSocketFactory factory) throws IOException;
+    }
+
+    /**
+     * Various factories for SSL server sockets.
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum SocketType {
+        FILE_DESCRIPTOR(false) {
+            @Override
+            void assertSocketType(Socket socket) {
+                assertTrue("Unexpected socket type: " + socket.getClass().getName(),
+                        socket instanceof ConscryptFileDescriptorSocket);
+            }
+        },
+        ENGINE(true) {
+            @Override
+            void assertSocketType(Socket socket) {
+                assertTrue("Unexpected socket type: " + socket.getClass().getName(),
+                        socket instanceof ConscryptEngineSocket);
+            }
+        };
+
+        private final boolean useEngineSocket;
+
+        SocketType(boolean useEngineSocket) {
+            this.useEngineSocket = useEngineSocket;
+        }
+
+        AbstractConscryptSocket newClientSocket(OpenSSLContextImpl context, ServerSocket server,
+                UnderlyingSocketType underlyingSocketType) throws IOException {
+            SSLSocketFactory factory = socketFactory(context);
+            Socket underlying = underlyingSocketType.newClientSocket(context, server, factory);
+            if (underlying != null) {
+                return newClientSocket(context, server, underlying);
+            }
+            return init(factory.createSocket(server.getInetAddress(), server.getLocalPort()), true);
+        }
+
+        AbstractConscryptSocket newClientSocket(OpenSSLContextImpl context, ServerSocket server,
+                Socket underlying) throws IOException {
+            SSLSocketFactory factory = socketFactory(context);
+            return init(factory.createSocket(underlying, server.getInetAddress().getHostName(),
+                                server.getLocalPort(), true),
+                    true);
+        }
+
+        AbstractConscryptSocket newServerSocket(OpenSSLContextImpl context, ServerSocket server,
+                UnderlyingSocketType underlyingSocketType) throws IOException {
+            SSLSocketFactory factory = socketFactory(context);
+            Socket underlying = underlyingSocketType.newServerSocket(context, server, factory);
+            return init(socketFactory(context).createSocket(underlying, null, -1, true), false);
+        }
+
+        abstract void assertSocketType(Socket socket);
+
+        private SSLSocketFactory socketFactory(OpenSSLContextImpl context) {
+            SSLSocketFactory factory = context.engineGetSocketFactory();
+            Conscrypt.setUseEngineSocket(factory, useEngineSocket);
+            return factory;
+        }
+
+        private AbstractConscryptSocket init(Socket socket, boolean useClientMode) {
+            assertSocketType(socket);
+            AbstractConscryptSocket sslSocket = (AbstractConscryptSocket) socket;
+            sslSocket.setUseClientMode(useClientMode);
+            return sslSocket;
+        }
+    }
+
+    @Parameters(name = "{0} wrapping {1}")
+    public static Object[][] data() {
+        return new Object[][] {
+            {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.NONE},
+            {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.PLAIN},
+            // Not supported: {SocketType.FILE_DESCRIPTOR, UnderlyingSocketType.SSL},
+            {SocketType.ENGINE, UnderlyingSocketType.NONE},
+            {SocketType.ENGINE, UnderlyingSocketType.PLAIN},
+            {SocketType.ENGINE, UnderlyingSocketType.SSL}};
+    }
+
+    @Parameter
+    public SocketType socketType;
+
+    @Parameter(1)
+    public UnderlyingSocketType underlyingSocketType;
+
+    private X509Certificate ca;
+    private X509Certificate cert;
+    private X509Certificate certEmbedded;
+    private PrivateKey certKey;
+
+    private Field contextSSLParameters;
+    private ExecutorService executor;
+
+    @Before
+    public void setUp() throws Exception {
+        contextSSLParameters = OpenSSLContextImpl.class.getDeclaredField("sslParameters");
+        contextSSLParameters.setAccessible(true);
+
+        ca = OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("ca-cert.pem"));
+        cert = OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("cert.pem"));
+        certEmbedded =
+                OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("cert-ct-embedded.pem"));
+        certKey = OpenSSLKey.fromPrivateKeyPemInputStream(openTestFile("cert-key.pem"))
+                          .getPrivateKey();
+        executor = Executors.newCachedThreadPool();
+    }
+
+    @After
+    public void teardown() throws Exception {
+        executor.shutdown();
+        executor.awaitTermination(5, TimeUnit.SECONDS);
+    }
+
+    abstract class Hooks implements HandshakeCompletedListener {
+        KeyManager[] keyManagers;
+        TrustManager[] trustManagers;
+        String[] alpnProtocols;
+
+        abstract AbstractConscryptSocket createSocket(ServerSocket listener) throws IOException;
+
+        OpenSSLContextImpl createContext() throws IOException {
+            OpenSSLContextImpl context = OpenSSLContextImpl.getPreferred();
+            try {
+                context.engineInit(keyManagers, trustManagers, null);
+            } catch (KeyManagementException e) {
+                throw new IOException(e);
+            }
+            return context;
+        }
+
+        boolean isHandshakeCompleted = false;
+        @Override
+        public void handshakeCompleted(HandshakeCompletedEvent event) {
+            isHandshakeCompleted = true;
+        }
+
+        SSLParametersImpl getContextSSLParameters(OpenSSLContextImpl context)
+                throws IllegalAccessException {
+            return (SSLParametersImpl) contextSSLParameters.get(context);
+        }
+    }
+
+    class ClientHooks extends Hooks {
+        String hostname = "example.com";
+
+        @Override
+        public OpenSSLContextImpl createContext() throws IOException {
+            OpenSSLContextImpl context = super.createContext();
+            try {
+                SSLParametersImpl sslParameters = getContextSSLParameters(context);
+                sslParameters.setCTVerificationEnabled(true);
+            } catch (IllegalAccessException e) {
+                throw new IOException(e);
+            }
+            return context;
+        }
+
+        @Override
+        AbstractConscryptSocket createSocket(ServerSocket listener) throws IOException {
+            AbstractConscryptSocket socket =
+                    socketType.newClientSocket(createContext(), listener, underlyingSocketType);
+            socket.setHostname(hostname);
+            if (alpnProtocols != null) {
+                Conscrypt.setApplicationProtocols(socket, alpnProtocols);
+            }
+            return socket;
+        }
+    }
+
+    class ServerHooks extends Hooks {
+        byte[] sctTLSExtension;
+        byte[] ocspResponse;
+        ApplicationProtocolSelector alpnProtocolSelector;
+
+        @Override
+        public OpenSSLContextImpl createContext() throws IOException {
+            OpenSSLContextImpl context = super.createContext();
+            try {
+                SSLParametersImpl sslParameters = getContextSSLParameters(context);
+                sslParameters.setSCTExtension(sctTLSExtension);
+                sslParameters.setOCSPResponse(ocspResponse);
+                return context;
+            } catch (IllegalAccessException e) {
+                throw new IOException(e);
+            }
+        }
+
+        @Override
+        AbstractConscryptSocket createSocket(ServerSocket listener) throws IOException {
+            AbstractConscryptSocket socket =
+                    socketType.newServerSocket(createContext(), listener, underlyingSocketType);
+            if (alpnProtocols != null) {
+                Conscrypt.setApplicationProtocols(socket, alpnProtocols);
+            }
+            if (alpnProtocolSelector != null) {
+                Conscrypt.setApplicationProtocolSelector(socket, alpnProtocolSelector);
+            }
+            return socket;
+        }
+    }
+
+    class TestConnection {
+        ServerHooks serverHooks;
+        ClientHooks clientHooks;
+
+        AbstractConscryptSocket client;
+        AbstractConscryptSocket server;
+
+        Exception clientException;
+        Exception serverException;
+
+        TestConnection(X509Certificate[] chain, PrivateKey key) throws Exception {
+            clientHooks = new ClientHooks();
+            serverHooks = new ServerHooks();
+            setCertificates(chain, key);
+        }
+
+        private void setCertificates(X509Certificate[] chain, PrivateKey key) throws Exception {
+            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+            ks.load(null, null);
+            ks.setKeyEntry("default", key, EMPTY_PASSWORD, chain);
+            ks.setCertificateEntry("CA", chain[chain.length - 1]);
+
+            TrustManagerFactory tmf =
+                    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+            tmf.init(ks);
+            TrustManager[] tms = tmf.getTrustManagers();
+
+            KeyManagerFactory kmf =
+                    KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+            kmf.init(ks, EMPTY_PASSWORD);
+            KeyManager[] kms = kmf.getKeyManagers();
+
+            clientHooks.trustManagers = tms;
+            serverHooks.keyManagers = kms;
+            serverHooks.trustManagers = tms;
+        }
+
+        private <T> T getOrThrowCause(Future<T> future, long timeout, TimeUnit timeUnit)
+                throws Exception {
+            try {
+                return future.get(timeout, timeUnit);
+            } catch (ExecutionException e) {
+                if (e.getCause() instanceof Exception) {
+                    throw(Exception) e.getCause();
+                } else {
+                    throw e;
+                }
+            }
+        }
+
+        void doHandshake() throws Exception {
+            ServerSocket listener = newServerSocket();
+            Future<AbstractConscryptSocket> clientFuture = handshake(listener, clientHooks);
+            Future<AbstractConscryptSocket> serverFuture = handshake(listener, serverHooks);
+
+            try {
+                client = getOrThrowCause(clientFuture, TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            } catch (Exception e) {
+                clientException = e;
+            }
+            try {
+                server = getOrThrowCause(serverFuture, TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            } catch (Exception e) {
+                serverException = e;
+            }
+        }
+
+        Future<AbstractConscryptSocket> handshake(final ServerSocket listener, final Hooks hooks) {
+            return executor.submit(new Callable<AbstractConscryptSocket>() {
+                @Override
+                public AbstractConscryptSocket call() throws Exception {
+                    AbstractConscryptSocket socket = hooks.createSocket(listener);
+                    socket.addHandshakeCompletedListener(hooks);
+
+                    socket.startHandshake();
+
+                    return socket;
+                }
+            });
+        }
+    }
+
+    @Test
+    public void test_handshake() throws Exception {
+        TestConnection connection = new TestConnection(new X509Certificate[] {cert, ca}, certKey);
+        connection.doHandshake();
+
+        assertTrue(connection.clientHooks.isHandshakeCompleted);
+        assertTrue(connection.serverHooks.isHandshakeCompleted);
+    }
+
+    @Test
+    public void alpnWithProtocolListShouldSucceed() throws Exception {
+        TestConnection c = new TestConnection(new X509Certificate[] {cert, ca}, certKey);
+
+        // Configure ALPN protocols
+        String[] clientAlpnProtocols = new String[]{"http/1.1", "foo", "spdy/2"};
+        String[] serverAlpnProtocols = new String[]{"spdy/2", "foo", "bar"};
+
+        c.clientHooks.alpnProtocols = clientAlpnProtocols;
+        c.serverHooks.alpnProtocols = serverAlpnProtocols;
+
+        c.doHandshake();
+
+        assertEquals("spdy/2", Conscrypt.getApplicationProtocol(c.client));
+        assertEquals("spdy/2", Conscrypt.getApplicationProtocol(c.server));
+    }
+
+    @Test
+    public void alpnWithProtocolListShouldFail() throws Exception {
+        TestConnection c = new TestConnection(new X509Certificate[] {cert, ca}, certKey);
+
+        // Configure ALPN protocols
+        String[] clientAlpnProtocols = new String[]{"http/1.1", "foo", "spdy/2"};
+        String[] serverAlpnProtocols = new String[]{"h2", "bar", "baz"};
+
+        c.clientHooks.alpnProtocols = clientAlpnProtocols;
+        c.serverHooks.alpnProtocols = serverAlpnProtocols;
+
+        c.doHandshake();
+
+        assertNull(Conscrypt.getApplicationProtocol(c.client));
+        assertNull(Conscrypt.getApplicationProtocol(c.server));
+    }
+
+    @Test
+    public void alpnWithServerProtocolSelectorShouldSucceed() throws Exception {
+        TestConnection c = new TestConnection(new X509Certificate[] {cert, ca}, certKey);
+
+        // Configure client ALPN protocols
+        String[] clientAlpnProtocols = new String[]{"http/1.1", "foo", "spdy/2"};
+        c.clientHooks.alpnProtocols = clientAlpnProtocols;
+
+        // Configure server selector
+        ApplicationProtocolSelector selector = Mockito.mock(ApplicationProtocolSelector.class);
+        when(selector.selectApplicationProtocol(any(SSLSocket.class), anyListOf(String.class)))
+                .thenReturn("spdy/2");
+        c.serverHooks.alpnProtocolSelector = selector;
+
+        c.doHandshake();
+
+        assertEquals("spdy/2", Conscrypt.getApplicationProtocol(c.client));
+        assertEquals("spdy/2", Conscrypt.getApplicationProtocol(c.server));
+    }
+
+    @Test
+    public void alpnWithServerProtocolSelectorShouldFail() throws Exception {
+        TestConnection c = new TestConnection(new X509Certificate[] {cert, ca}, certKey);
+
+        // Configure client ALPN protocols
+        String[] clientAlpnProtocols = new String[]{"http/1.1", "foo", "spdy/2"};
+        c.clientHooks.alpnProtocols = clientAlpnProtocols;
+
+        // Configure server selector
+        ApplicationProtocolSelector selector = Mockito.mock(ApplicationProtocolSelector.class);
+        when(selector.selectApplicationProtocol(any(SSLSocket.class), anyListOf(String.class)))
+                .thenReturn("h2");
+        c.serverHooks.alpnProtocolSelector = selector;
+
+        c.doHandshake();
+
+        assertNull(Conscrypt.getApplicationProtocol(c.client));
+        assertNull(Conscrypt.getApplicationProtocol(c.server));
+    }
+
+    @Test
+    public void test_handshakeWithEmbeddedSCT() throws Exception {
+        TestConnection connection =
+                new TestConnection(new X509Certificate[] {certEmbedded, ca}, certKey);
+
+        connection.doHandshake();
+
+        assertTrue(connection.clientHooks.isHandshakeCompleted);
+        assertTrue(connection.serverHooks.isHandshakeCompleted);
+    }
+
+    @Test
+    public void test_handshakeWithSCTFromOCSPResponse() throws Exception {
+        TestConnection connection = new TestConnection(new X509Certificate[] {cert, ca}, certKey);
+
+        connection.serverHooks.ocspResponse = readTestFile("ocsp-response.der");
+
+        connection.doHandshake();
+
+        assertTrue(connection.clientHooks.isHandshakeCompleted);
+        assertTrue(connection.serverHooks.isHandshakeCompleted);
+    }
+
+    @Test
+    public void test_handshakeWithSCTFromTLSExtension() throws Exception {
+        TestConnection connection = new TestConnection(new X509Certificate[] {cert, ca}, certKey);
+
+        connection.serverHooks.sctTLSExtension = readTestFile("ct-signed-timestamp-list");
+
+        connection.doHandshake();
+
+        assertTrue(connection.clientHooks.isHandshakeCompleted);
+        assertTrue(connection.serverHooks.isHandshakeCompleted);
+    }
+
+    @Ignore("TODO(nathanmittler): Fix or remove")
+    @Test
+    public void test_handshake_failsWithMissingSCT() throws Exception {
+        TestConnection connection = new TestConnection(new X509Certificate[] {cert, ca}, certKey);
+
+        connection.doHandshake();
+        assertThat(connection.clientException, instanceOf(SSLHandshakeException.class));
+        assertThat(connection.clientException.getCause(), instanceOf(CertificateException.class));
+    }
+
+    @Ignore("TODO(nathanmittler): Fix or remove")
+    @Test
+    public void test_handshake_failsWithInvalidSCT() throws Exception {
+        TestConnection connection = new TestConnection(new X509Certificate[] {cert, ca}, certKey);
+
+        connection.serverHooks.sctTLSExtension = readTestFile("ct-signed-timestamp-list-invalid");
+
+        connection.doHandshake();
+        assertThat(connection.clientException, instanceOf(SSLHandshakeException.class));
+        assertThat(connection.clientException.getCause(), instanceOf(CertificateException.class));
+    }
+
+    @Test
+    @SuppressWarnings("deprecation")
+    public void setAlpnProtocolWithNullShouldSucceed() throws Exception {
+        ServerSocket listening = newServerSocket();
+        OpenSSLSocketImpl clientSocket = null;
+        try {
+            Socket underlying = new Socket(listening.getInetAddress(), listening.getLocalPort());
+            clientSocket = (OpenSSLSocketImpl) socketType.newClientSocket(
+                    new ClientHooks().createContext(), listening, underlying);
+
+            // Both versions should succeed.
+            clientSocket.setAlpnProtocols((byte[]) null);
+            clientSocket.setAlpnProtocols((String[]) null);
+        } finally {
+            if (clientSocket != null) {
+                clientSocket.close();
+            }
+            listening.close();
+        }
+    }
+
+    // http://b/27250522
+    @Test
+    public void test_setSoTimeout_doesNotCreateSocketImpl() throws Exception {
+        ServerSocket listening = newServerSocket();
+        try {
+            Socket underlying = new Socket(listening.getInetAddress(), listening.getLocalPort());
+            Socket socket = socketType.newClientSocket(
+                    new ClientHooks().createContext(), listening, underlying);
+            socketType.assertSocketType(socket);
+            socket.setSoTimeout(1000);
+            socket.close();
+
+            Field f = Socket.class.getDeclaredField("created");
+            f.setAccessible(true);
+            assertFalse(f.getBoolean(socket));
+        } finally {
+            listening.close();
+        }
+    }
+
+    @Test
+    public void test_setEnabledProtocols_FiltersSSLv3_HandshakeException() throws Exception {
+        TestConnection connection = new TestConnection(new X509Certificate[] {cert, ca}, certKey);
+
+        connection.clientHooks = new ClientHooks() {
+            @Override
+            public AbstractConscryptSocket createSocket(ServerSocket listener) throws IOException {
+                AbstractConscryptSocket socket = super.createSocket(listener);
+                socket.setEnabledProtocols(new String[] {"SSLv3"});
+                assertEquals(
+                        "SSLv3 should be filtered out", 0, socket.getEnabledProtocols().length);
+                return socket;
+            }
+        };
+
+        connection.doHandshake();
+        assertThat(connection.clientException, instanceOf(SSLHandshakeException.class));
+        assertTrue(
+                connection.clientException.getMessage().contains("SSLv3 is no longer supported"));
+        assertThat(connection.serverException, instanceOf(SSLHandshakeException.class));
+
+        assertFalse(connection.clientHooks.isHandshakeCompleted);
+        assertFalse(connection.serverHooks.isHandshakeCompleted);
+    }
+
+    @Test
+    public void savedSessionWorksAfterClose() throws Exception {
+        TestConnection connection = new TestConnection(new X509Certificate[] {cert, ca}, certKey);
+        connection.doHandshake();
+
+        SSLSession session = connection.client.getSession();
+        String cipherSuite = session.getCipherSuite();
+
+        connection.client.close();
+
+        assertEquals(cipherSuite, session.getCipherSuite());
+    }
+
+    private static ServerSocket newServerSocket() throws IOException {
+        return new ServerSocket(0, 50, TestUtils.getLoopbackAddress());
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ConscryptTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ConscryptTest.java
new file mode 100644
index 0000000..91c4f24
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ConscryptTest.java
@@ -0,0 +1,48 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class ConscryptTest {
+
+    /**
+     * This confirms that the version machinery is working.
+     */
+    @Test
+    public void testVersionIsSensible() {
+        Conscrypt.Version version = Conscrypt.version();
+        assertNotNull(version);
+        // The version object should be a singleton
+        assertSame(version, Conscrypt.version());
+
+        assertTrue("Major version: " + version.major(), 1 <= version.major());
+        assertTrue("Minor version: " + version.minor(), 0 <= version.minor());
+        assertTrue("Patch version: " + version.patch(), 0 <= version.patch());
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/DuckTypedPSKKeyManagerTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/DuckTypedPSKKeyManagerTest.java
new file mode 100644
index 0000000..8d03c21
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/DuckTypedPSKKeyManagerTest.java
@@ -0,0 +1,321 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.Socket;
+import java.security.Key;
+import java.util.Arrays;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import junit.framework.TestCase;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DuckTypedPSKKeyManagerTest extends TestCase {
+    private SSLSocket mSSLSocket;
+    private SSLEngine mSSLEngine;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        SSLContext sslContext = SSLContext.getDefault();
+        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+        mSSLSocket = (SSLSocket) sslSocketFactory.createSocket();
+        mSSLEngine = sslContext.createSSLEngine();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        try {
+            if (mSSLSocket != null) {
+                try {
+                    mSSLSocket.close();
+                } catch (Exception ignored) {}
+            }
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testDuckTypingFailsWhenOneMethodMissing() throws Exception {
+        try {
+            DuckTypedPSKKeyManager.getInstance(new AlmostPSKKeyManager());
+            fail();
+        } catch (NoSuchMethodException expected) {}
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testDuckTypingFailsWhenOneMethodReturnTypeIncompatible() throws Exception {
+        try {
+            assertNotNull(DuckTypedPSKKeyManager.getInstance(
+                    new KeyManagerOfferingAllPSKKeyManagerMethodsWithIncompatibleReturnTypes()));
+        fail();
+        } catch (NoSuchMethodException expected) {}
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testDuckTypingSucceedsWhenAllMethodsPresentWithExactReturnTypes() throws Exception {
+        assertNotNull(DuckTypedPSKKeyManager.getInstance(
+                new KeyManagerOfferingAllPSKKeyManagerMethodsWithExactReturnTypes()));
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testDuckTypingSucceedsWhenAllMethodsPresentWithDifferentButCompatibleReturnTypes()
+            throws Exception {
+        assertNotNull(DuckTypedPSKKeyManager.getInstance(
+                new KeyManagerOfferingAllPSKKeyManagerMethodsWithCompatibleReturnTypes()));
+    }
+
+    public void testMethodInvocationDelegation() throws Exception {
+        // IMPLEMENTATION NOTE: We create a DuckTypedPSKKeyManager wrapping a Reflection Proxy,
+        // invoke each method of the PSKKeyManager interface on the DuckTypedPSKKeyManager instance,
+        // and assert that invocations on the Proxy are as expected and that values returned by the
+        // Proxy are returned to us.
+
+        MockInvocationHandler mockInvocationHandler = new MockInvocationHandler();
+        @SuppressWarnings("deprecation")
+        PSKKeyManager delegate = (PSKKeyManager) Proxy.newProxyInstance(
+                DuckTypedPSKKeyManager.class.getClassLoader(), new Class<?>[] {PSKKeyManager.class},
+                mockInvocationHandler);
+        @SuppressWarnings("deprecation")
+        PSKKeyManager pskKeyManager = DuckTypedPSKKeyManager.getInstance(delegate);
+        String identityHint = "hint";
+        String identity = "identity";
+
+        mockInvocationHandler.returnValue = identityHint;
+        assertSame(identityHint, pskKeyManager.chooseServerKeyIdentityHint(mSSLSocket));
+        assertEquals("chooseServerKeyIdentityHint",
+                mockInvocationHandler.lastInvokedMethod.getName());
+        assertEquals(Arrays.asList(new Class<?>[] {Socket.class}),
+                Arrays.asList(mockInvocationHandler.lastInvokedMethod.getParameterTypes()));
+        assertEquals(1, mockInvocationHandler.lastInvokedMethodArgs.length);
+        assertSame(mSSLSocket, mockInvocationHandler.lastInvokedMethodArgs[0]);
+
+        mockInvocationHandler.returnValue = identityHint;
+        assertSame(identityHint, pskKeyManager.chooseServerKeyIdentityHint(mSSLEngine));
+        assertEquals("chooseServerKeyIdentityHint",
+                mockInvocationHandler.lastInvokedMethod.getName());
+        assertEquals(Arrays.asList(new Class<?>[] {SSLEngine.class}),
+                Arrays.asList(mockInvocationHandler.lastInvokedMethod.getParameterTypes()));
+        assertEquals(1, mockInvocationHandler.lastInvokedMethodArgs.length);
+        assertSame(mSSLEngine, mockInvocationHandler.lastInvokedMethodArgs[0]);
+
+        mockInvocationHandler.returnValue = identity;
+        assertSame(identity, pskKeyManager.chooseClientKeyIdentity(identityHint, mSSLSocket));
+        assertEquals("chooseClientKeyIdentity", mockInvocationHandler.lastInvokedMethod.getName());
+        assertEquals(Arrays.asList(new Class<?>[] {String.class, Socket.class}),
+                Arrays.asList(mockInvocationHandler.lastInvokedMethod.getParameterTypes()));
+        assertEquals(2, mockInvocationHandler.lastInvokedMethodArgs.length);
+        assertSame(identityHint, mockInvocationHandler.lastInvokedMethodArgs[0]);
+        assertSame(mSSLSocket, mockInvocationHandler.lastInvokedMethodArgs[1]);
+
+        mockInvocationHandler.returnValue = identity;
+        assertSame(identity, pskKeyManager.chooseClientKeyIdentity(identityHint, mSSLEngine));
+        assertEquals("chooseClientKeyIdentity", mockInvocationHandler.lastInvokedMethod.getName());
+        assertEquals(Arrays.asList(new Class<?>[] {String.class, SSLEngine.class}),
+                Arrays.asList(mockInvocationHandler.lastInvokedMethod.getParameterTypes()));
+        assertEquals(2, mockInvocationHandler.lastInvokedMethodArgs.length);
+        assertSame(identityHint, mockInvocationHandler.lastInvokedMethodArgs[0]);
+        assertSame(mSSLEngine, mockInvocationHandler.lastInvokedMethodArgs[1]);
+
+        SecretKey key = new SecretKeySpec("arbitrary".getBytes("UTF-8"), "RAW");
+        mockInvocationHandler.returnValue = key;
+        assertSame(key, pskKeyManager.getKey(identityHint, identity, mSSLSocket));
+        assertEquals("getKey", mockInvocationHandler.lastInvokedMethod.getName());
+        assertEquals(Arrays.asList(new Class<?>[] {String.class, String.class, Socket.class}),
+                Arrays.asList(mockInvocationHandler.lastInvokedMethod.getParameterTypes()));
+        assertEquals(3, mockInvocationHandler.lastInvokedMethodArgs.length);
+        assertSame(identityHint, mockInvocationHandler.lastInvokedMethodArgs[0]);
+        assertSame(identity, mockInvocationHandler.lastInvokedMethodArgs[1]);
+        assertSame(mSSLSocket, mockInvocationHandler.lastInvokedMethodArgs[2]);
+
+        mockInvocationHandler.returnValue = key;
+        assertSame(key, pskKeyManager.getKey(identityHint, identity, mSSLEngine));
+        assertEquals("getKey", mockInvocationHandler.lastInvokedMethod.getName());
+        assertEquals(Arrays.asList(new Class<?>[] {String.class, String.class, SSLEngine.class}),
+                Arrays.asList(mockInvocationHandler.lastInvokedMethod.getParameterTypes()));
+        assertEquals(3, mockInvocationHandler.lastInvokedMethodArgs.length);
+        assertSame(identityHint, mockInvocationHandler.lastInvokedMethodArgs[0]);
+        assertSame(identity, mockInvocationHandler.lastInvokedMethodArgs[1]);
+        assertSame(mSSLEngine, mockInvocationHandler.lastInvokedMethodArgs[2]);
+    }
+
+    public void testMethodInvocationDelegationWithDifferentButCompatibleReturnType()
+            throws Exception {
+        // Check that nothing blows up when we invoke getKey which is declared to return
+        // SecretKeySpec rather than SecretKey as declared in the PSKKeyManager interface.
+        @SuppressWarnings("deprecation")
+        PSKKeyManager pskKeyManager = DuckTypedPSKKeyManager.getInstance(
+                new KeyManagerOfferingAllPSKKeyManagerMethodsWithCompatibleReturnTypes());
+        pskKeyManager.getKey(null, "", mSSLSocket);
+        pskKeyManager.getKey(null, "", mSSLEngine);
+    }
+
+    /**
+     * {@link KeyManager} which implements all methods of {@link PSKKeyManager} except for one.
+     */
+    @SuppressWarnings("unused")
+    private static class AlmostPSKKeyManager implements KeyManager {
+        public String chooseServerKeyIdentityHint(Socket socket) {
+            return null;
+        }
+
+        public String chooseServerKeyIdentityHint(SSLEngine engine) {
+            return null;
+        }
+
+        public String chooseClientKeyIdentity(String identityHint, Socket socket) {
+            return null;
+        }
+
+        public String chooseClientKeyIdentity(String identityHint, SSLEngine engine) {
+            return null;
+        }
+
+        public SecretKey getKey(String identityHint, String identity, Socket socket) {
+            return null;
+        }
+
+        // Missing method from the PSKKeyManager interface:
+        // SecretKey getKey(String identityHint, String identity, SSLEngine engine);
+    }
+
+    /**
+     * {@link KeyManager} which exposes all methods of the {@link PSKKeyManager} interface but does
+     * not implement the interface.
+     */
+    @SuppressWarnings("unused")
+    private static class KeyManagerOfferingAllPSKKeyManagerMethodsWithExactReturnTypes
+            implements KeyManager {
+        public String chooseServerKeyIdentityHint(Socket socket) {
+            return null;
+        }
+
+        public String chooseServerKeyIdentityHint(SSLEngine engine) {
+            return null;
+        }
+
+        public String chooseClientKeyIdentity(String identityHint, Socket socket) {
+            return null;
+        }
+
+        public String chooseClientKeyIdentity(String identityHint, SSLEngine engine) {
+            return null;
+        }
+
+        public SecretKey getKey(String identityHint, String identity, Socket socket) {
+            return null;
+        }
+
+        public SecretKey getKey(String identityHint, String identity, SSLEngine engine) {
+            return null;
+        }
+    }
+
+    /**
+     * {@link KeyManager} which exposes all methods of the {@link PSKKeyManager} interface but does
+     * not implement the interface. Additionally, the return types of some methods are different
+     * but compatible with the {@code PSKKeyManager} interface.
+     */
+    @SuppressWarnings("unused")
+    private static class KeyManagerOfferingAllPSKKeyManagerMethodsWithCompatibleReturnTypes
+            implements KeyManager {
+        public String chooseServerKeyIdentityHint(Socket socket) {
+            return null;
+        }
+
+        public String chooseServerKeyIdentityHint(SSLEngine engine) {
+            return null;
+        }
+
+        public String chooseClientKeyIdentity(String identityHint, Socket socket) {
+            return null;
+        }
+
+        public String chooseClientKeyIdentity(String identityHint, SSLEngine engine) {
+            return null;
+        }
+
+        // PSKKeyManager's return type: SecretKey
+        public SecretKeySpec getKey(String identityHint, String identity, Socket socket) {
+            return null;
+        }
+
+        // PSKKeyManager's return type: SecretKey
+        public SecretKeySpec getKey(String identityHint, String identity, SSLEngine engine) {
+            return null;
+        }
+    }
+
+    /**
+     * {@link KeyManager} which exposes all methods of the {@link PSKKeyManager} interface but does
+     * not implement the interface. Additionally, the return types of some methods are incompatible
+     * with the {@code PSKKeyManager} interface.
+     */
+    @SuppressWarnings("unused")
+    private static class KeyManagerOfferingAllPSKKeyManagerMethodsWithIncompatibleReturnTypes
+            implements KeyManager {
+        public String chooseServerKeyIdentityHint(Socket socket) {
+            return null;
+        }
+
+        public String chooseServerKeyIdentityHint(SSLEngine engine) {
+            return null;
+        }
+
+        public String chooseClientKeyIdentity(String identityHint, Socket socket) {
+            return null;
+        }
+
+        public String chooseClientKeyIdentity(String identityHint, SSLEngine engine) {
+            return null;
+        }
+
+        public SecretKey getKey(String identityHint, String identity, Socket socket) {
+            return null;
+        }
+
+        // PSKKeyManager's return type: SecretKey
+        public Key getKey(String identityHint, String identity, SSLEngine engine) {
+            return null;
+        }
+    }
+
+    static class MockInvocationHandler implements InvocationHandler {
+        Object returnValue;
+        Method lastInvokedMethod;
+        Object[] lastInvokedMethodArgs;
+
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+            lastInvokedMethod = method;
+            lastInvokedMethodArgs = args;
+            return returnValue;
+        }
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/FileClientSessionCacheTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/FileClientSessionCacheTest.java
new file mode 100644
index 0000000..38d2b99
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/FileClientSessionCacheTest.java
@@ -0,0 +1,60 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.File;
+import java.io.IOException;
+import junit.framework.TestCase;
+import com.android.org.conscrypt.javax.net.ssl.FakeSSLSession;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class FileClientSessionCacheTest extends TestCase {
+
+    public void testMaxSize() throws IOException, InterruptedException {
+        String tmpDir = System.getProperty("java.io.tmpdir");
+        if (tmpDir == null) {
+            fail("Please set 'java.io.tmpdir' system property.");
+        }
+        File cacheDir = new File(tmpDir
+                + "/" + FileClientSessionCacheTest.class.getName() + "/cache");
+        final SSLClientSessionCache cache
+                = FileClientSessionCache.usingDirectory(cacheDir);
+        Thread[] threads = new Thread[10];
+        final int iterations = FileClientSessionCache.MAX_SIZE * 10;
+        for (int i = 0; i < threads.length; i++) {
+            final int id = i;
+            threads[i] = new Thread() {
+                @Override
+                public void run() {
+                    for (int i = 0; i < iterations; i++) {
+                        cache.putSessionData(new FakeSSLSession(id + "" + i), new byte[10]);
+                    }
+                }
+            };
+        }
+        for (Thread thread : threads) {
+            thread.start();
+        }
+        for (Thread thread : threads) {
+            thread.join();
+        }
+        assertEquals(FileClientSessionCache.MAX_SIZE, cacheDir.list().length);
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/MockSessionBuilder.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/MockSessionBuilder.java
new file mode 100644
index 0000000..f8a4a6d
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/MockSessionBuilder.java
@@ -0,0 +1,86 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.TestUtils.UTF_8;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Utility class for constructing mock sessions.
+ */
+final class MockSessionBuilder {
+    static final String DEFAULT_CIPHER_SUITE = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256";
+    static final int DEFAULT_PORT = 443;
+
+    private byte[] id;
+    private boolean valid = true;
+    private boolean singleUse = false;
+    private String host;
+    private int port = DEFAULT_PORT;
+    private String cipherSuite = DEFAULT_CIPHER_SUITE;
+    private byte[] encodedBytes = EmptyArray.BYTE;
+
+    MockSessionBuilder id(byte[] id) {
+        this.id = id;
+        return this;
+    }
+
+    MockSessionBuilder host(String host) {
+        this.host = host;
+        return this;
+    }
+
+    MockSessionBuilder port(int port) {
+        this.port = port;
+        return this;
+    }
+
+    MockSessionBuilder valid(boolean valid) {
+        this.valid = valid;
+        return this;
+    }
+
+    MockSessionBuilder cipherSuite(String cipherSuite) {
+        this.cipherSuite = cipherSuite;
+        return this;
+    }
+
+    MockSessionBuilder encodedBytes(byte[] encodedBytes) {
+        this.encodedBytes = encodedBytes;
+        return this;
+    }
+
+    MockSessionBuilder singleUse(boolean singleUse) {
+        this.singleUse = singleUse;
+        return this;
+    }
+
+    NativeSslSession build() {
+        NativeSslSession session = mock(NativeSslSession.class);
+        byte[] id = this.id == null ? host.getBytes(UTF_8) : this.id;
+        when(session.getId()).thenReturn(id);
+        when(session.isValid()).thenReturn(valid);
+        when(session.isSingleUse()).thenReturn(singleUse);
+        when(session.getProtocol()).thenReturn(TestUtils.getProtocols()[0]);
+        when(session.getPeerHost()).thenReturn(host);
+        when(session.getPeerPort()).thenReturn(port);
+        when(session.getCipherSuite()).thenReturn(cipherSuite);
+        when(session.toBytes()).thenReturn(encodedBytes);
+        return session;
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/NativeCryptoTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/NativeCryptoTest.java
new file mode 100644
index 0000000..e6c43f4
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/NativeCryptoTest.java
@@ -0,0 +1,3140 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.NativeConstants.SSL_MODE_CBC_RECORD_SPLITTING;
+import static com.android.org.conscrypt.NativeConstants.SSL_MODE_ENABLE_FALSE_START;
+import static com.android.org.conscrypt.NativeConstants.SSL_OP_CIPHER_SERVER_PREFERENCE;
+import static com.android.org.conscrypt.NativeConstants.SSL_OP_NO_TICKET;
+import static com.android.org.conscrypt.NativeConstants.SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+import static com.android.org.conscrypt.NativeConstants.SSL_VERIFY_NONE;
+import static com.android.org.conscrypt.NativeConstants.SSL_VERIFY_PEER;
+import static com.android.org.conscrypt.NativeConstants.TLS1_1_VERSION;
+import static com.android.org.conscrypt.NativeConstants.TLS1_2_VERSION;
+import static com.android.org.conscrypt.NativeConstants.TLS1_VERSION;
+import static com.android.org.conscrypt.TestUtils.openTestFile;
+import static com.android.org.conscrypt.TestUtils.readTestFile;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Method;
+import java.math.BigInteger;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.ECPrivateKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLProtocolException;
+import javax.security.auth.x500.X500Principal;
+import com.android.org.conscrypt.NativeCrypto.SSLHandshakeCallbacks;
+import com.android.org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
+import com.android.org.conscrypt.io.IoUtils;
+import com.android.org.conscrypt.java.security.StandardNames;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class NativeCryptoTest {
+    private static final long NULL = 0;
+    private static final FileDescriptor INVALID_FD = new FileDescriptor();
+    private static final SSLHandshakeCallbacks DUMMY_CB =
+            new TestSSLHandshakeCallbacks(null, 0, null);
+
+    private static final long TIMEOUT_SECONDS = 5;
+
+    private static OpenSSLKey SERVER_PRIVATE_KEY;
+    private static OpenSSLX509Certificate[] SERVER_CERTIFICATES_HOLDER;
+    private static long[] SERVER_CERTIFICATE_REFS;
+    private static byte[][] ENCODED_SERVER_CERTIFICATES;
+    private static OpenSSLKey CLIENT_PRIVATE_KEY;
+    private static OpenSSLX509Certificate[] CLIENT_CERTIFICATES_HOLDER;
+    private static long[] CLIENT_CERTIFICATE_REFS;
+    private static byte[][] ENCODED_CLIENT_CERTIFICATES;
+    private static byte[][] CA_PRINCIPALS;
+    private static OpenSSLKey CHANNEL_ID_PRIVATE_KEY;
+    private static byte[] CHANNEL_ID;
+    private static Method m_Platform_getFileDescriptor;
+
+    @BeforeClass
+    public static void getPlatformMethods() throws Exception {
+        Class<?> c_Platform = TestUtils.conscryptClass("Platform");
+        m_Platform_getFileDescriptor =
+                c_Platform.getDeclaredMethod("getFileDescriptor", Socket.class);
+        m_Platform_getFileDescriptor.setAccessible(true);
+    }
+
+    private static OpenSSLKey getServerPrivateKey() {
+        initCerts();
+        return SERVER_PRIVATE_KEY;
+    }
+
+    private static long[] getServerCertificateRefs() {
+        initCerts();
+        return SERVER_CERTIFICATE_REFS;
+    }
+
+    private static byte[][] getEncodedServerCertificates() {
+        initCerts();
+        return ENCODED_SERVER_CERTIFICATES;
+    }
+
+    private static OpenSSLKey getClientPrivateKey() {
+        initCerts();
+        return CLIENT_PRIVATE_KEY;
+    }
+
+    private static long[] getClientCertificateRefs() {
+        initCerts();
+        return CLIENT_CERTIFICATE_REFS;
+    }
+
+    private static byte[][] getEncodedClientCertificates() {
+        initCerts();
+        return ENCODED_CLIENT_CERTIFICATES;
+    }
+
+    private static byte[][] getCaPrincipals() {
+        initCerts();
+        return CA_PRINCIPALS;
+    }
+
+    /**
+     * Lazily create shared test certificates.
+     */
+    private static synchronized void initCerts() {
+        if (SERVER_PRIVATE_KEY != null) {
+            return;
+        }
+
+        try {
+            PrivateKeyEntry serverPrivateKeyEntry =
+                    TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
+            SERVER_PRIVATE_KEY = OpenSSLKey.fromPrivateKey(serverPrivateKeyEntry.getPrivateKey());
+            SERVER_CERTIFICATES_HOLDER =
+                    encodeCertificateList(serverPrivateKeyEntry.getCertificateChain());
+            SERVER_CERTIFICATE_REFS = getCertificateReferences(SERVER_CERTIFICATES_HOLDER);
+            ENCODED_SERVER_CERTIFICATES = getEncodedCertificates(SERVER_CERTIFICATES_HOLDER);
+
+            PrivateKeyEntry clientPrivateKeyEntry =
+                    TestKeyStore.getClientCertificate().getPrivateKey("RSA", "RSA");
+            CLIENT_PRIVATE_KEY = OpenSSLKey.fromPrivateKey(clientPrivateKeyEntry.getPrivateKey());
+            CLIENT_CERTIFICATES_HOLDER =
+                    encodeCertificateList(clientPrivateKeyEntry.getCertificateChain());
+            CLIENT_CERTIFICATE_REFS = getCertificateReferences(CLIENT_CERTIFICATES_HOLDER);
+            ENCODED_CLIENT_CERTIFICATES = getEncodedCertificates(CLIENT_CERTIFICATES_HOLDER);
+
+            KeyStore ks = TestKeyStore.getClient().keyStore;
+            String caCertAlias = ks.aliases().nextElement();
+            X509Certificate certificate = (X509Certificate) ks.getCertificate(caCertAlias);
+            X500Principal principal = certificate.getIssuerX500Principal();
+            CA_PRINCIPALS = new byte[][] {principal.getEncoded()};
+            initChannelIdKey();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static long[] getCertificateReferences(OpenSSLX509Certificate[] certs) {
+        final long[] certRefs = new long[certs.length];
+        for (int i = 0; i < certs.length; i++) {
+            certRefs[i] = certs[i].getContext();
+        }
+        return certRefs;
+    }
+
+    private static byte[][] getEncodedCertificates(OpenSSLX509Certificate[] certs) {
+        try {
+            final byte[][] encoded = new byte[certs.length][];
+            for (int i = 0; i < certs.length; i++) {
+                encoded[i] = certs[i].getEncoded();
+            }
+            return encoded;
+        } catch (CertificateEncodingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static OpenSSLX509Certificate[] encodeCertificateList(Certificate[] chain)
+            throws CertificateEncodingException {
+        final OpenSSLX509Certificate[] openSslCerts = new OpenSSLX509Certificate[chain.length];
+        for (int i = 0; i < chain.length; i++) {
+            openSslCerts[i] = OpenSSLX509Certificate.fromCertificate(chain[i]);
+        }
+        return openSslCerts;
+    }
+
+    private static synchronized void initChannelIdKey() throws Exception {
+        if (CHANNEL_ID_PRIVATE_KEY != null) {
+            return;
+        }
+
+        // NIST P-256 aka SECG secp256r1 aka X9.62 prime256v1
+        OpenSSLECGroupContext openSslSpec = OpenSSLECGroupContext.getCurveByName("prime256v1");
+        BigInteger s = new BigInteger(
+                "229cdbbf489aea584828a261a23f9ff8b0f66f7ccac98bf2096ab3aee41497c5", 16);
+        CHANNEL_ID_PRIVATE_KEY =
+                new OpenSSLECPrivateKey(new ECPrivateKeySpec(s, openSslSpec.getECParameterSpec()))
+                        .getOpenSSLKey();
+
+        // Channel ID is the concatenation of the X and Y coordinates of the public key.
+        CHANNEL_ID = new BigInteger(
+                "702b07871fd7955c320b26f15e244e47eed60272124c92b9ebecf0b42f90069b"
+                        + "ab53592ebfeb4f167dbf3ce61513afb0e354c479b1c1b69874fa471293494f77",
+                16).toByteArray();
+    }
+
+    private static RSAPrivateCrtKey generateRsaKey() throws Exception {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+        kpg.initialize(512);
+
+        KeyPair keyPair = kpg.generateKeyPair();
+        return (RSAPrivateCrtKey) keyPair.getPrivate();
+    }
+
+    private static NativeRef.EVP_PKEY getRsaPkey(RSAPrivateCrtKey privKey) throws Exception {
+        return new NativeRef.EVP_PKEY(NativeCrypto.EVP_PKEY_new_RSA(
+                privKey.getModulus().toByteArray(), privKey.getPublicExponent().toByteArray(),
+                privKey.getPrivateExponent().toByteArray(), privKey.getPrimeP().toByteArray(),
+                privKey.getPrimeQ().toByteArray(), privKey.getPrimeExponentP().toByteArray(),
+                privKey.getPrimeExponentQ().toByteArray(),
+                privKey.getCrtCoefficient().toByteArray()));
+    }
+
+    public static void assertEqualSessions(long expected, long actual) {
+        assertEqualByteArrays(NativeCrypto.SSL_SESSION_session_id(expected),
+                NativeCrypto.SSL_SESSION_session_id(actual));
+    }
+    public static void assertEqualByteArrays(byte[] expected, byte[] actual) {
+        assertEquals(Arrays.toString(expected), Arrays.toString(actual));
+    }
+
+    public static void assertEqualPrincipals(byte[][] expected, byte[][] actual) {
+        assertEqualByteArrays(expected, actual);
+    }
+
+    public static void assertEqualCertificateChains(long[] expected, long[] actual) {
+        assertEquals(expected.length, actual.length);
+        for (int i = 0; i < expected.length; i++) {
+            NativeCrypto.X509_cmp(expected[i], null, actual[i], null);
+        }
+    }
+
+    public static void assertEqualByteArrays(byte[][] expected, byte[][] actual) {
+        assertEquals(Arrays.deepToString(expected), Arrays.deepToString(actual));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_cmp_BothNullParameters() throws Exception {
+        NativeCrypto.EVP_PKEY_cmp(null, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_cmp_withNullShouldThrow() throws Exception {
+        RSAPrivateCrtKey privKey1 = generateRsaKey();
+        NativeRef.EVP_PKEY pkey1 = getRsaPkey(privKey1);
+        assertNotSame(NULL, pkey1);
+        NativeCrypto.EVP_PKEY_cmp(pkey1, null);
+    }
+
+    @Test
+    public void test_EVP_PKEY_cmp() throws Exception {
+        RSAPrivateCrtKey privKey1 = generateRsaKey();
+
+        NativeRef.EVP_PKEY pkey1 = getRsaPkey(privKey1);
+        assertNotSame(NULL, pkey1);
+
+        NativeRef.EVP_PKEY pkey1_copy = getRsaPkey(privKey1);
+        assertNotSame(NULL, pkey1_copy);
+
+        NativeRef.EVP_PKEY pkey2 = getRsaPkey(generateRsaKey());
+        assertNotSame(NULL, pkey2);
+
+        assertEquals("Same keys should be the equal", 1, NativeCrypto.EVP_PKEY_cmp(pkey1, pkey1));
+
+        assertEquals(
+                "Same keys should be the equal", 1, NativeCrypto.EVP_PKEY_cmp(pkey1, pkey1_copy));
+
+        assertEquals(
+                "Different keys should not be equal", 0, NativeCrypto.EVP_PKEY_cmp(pkey1, pkey2));
+    }
+
+    @Test
+    public void test_SSL_CTX_new() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        assertTrue(c != NULL);
+        long c2 = NativeCrypto.SSL_CTX_new();
+        assertTrue(c != c2);
+        NativeCrypto.SSL_CTX_free(c, null);
+        NativeCrypto.SSL_CTX_free(c2, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_SSL_CTX_free_NullArgument() throws Exception {
+        NativeCrypto.SSL_CTX_free(NULL, null);
+    }
+
+    @Test
+    public void test_SSL_CTX_free() throws Exception {
+        NativeCrypto.SSL_CTX_free(NativeCrypto.SSL_CTX_new(), null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_CTX_set_session_id_context_NullContextArgument() throws Exception {
+        NativeCrypto.SSL_CTX_set_session_id_context(NULL, null, new byte[0]);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_CTX_set_session_id_context_withNullShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        try {
+            NativeCrypto.SSL_CTX_set_session_id_context(c, null, null);
+        } finally {
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void test_SSL_CTX_set_session_id_context_withInvalidIdShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        try {
+            NativeCrypto.SSL_CTX_set_session_id_context(c, null, new byte[33]);
+        } finally {
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test
+    public void test_SSL_CTX_set_session_id_context() throws Exception {
+        byte[] empty = new byte[0];
+
+        long c = NativeCrypto.SSL_CTX_new();
+        try {
+            NativeCrypto.SSL_CTX_set_session_id_context(c, null, empty);
+            NativeCrypto.SSL_CTX_set_session_id_context(c, null, new byte[32]);
+        } finally {
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test
+    public void test_SSL_new() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+
+        assertTrue(s != NULL);
+        assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_NO_TICKET) != 0);
+
+        long s2 = NativeCrypto.SSL_new(c, null);
+        assertTrue(s != s2);
+        NativeCrypto.SSL_free(s2, null);
+
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void setLocalCertsAndPrivateKey_withNullSSLShouldThrow() throws Exception {
+        NativeCrypto.setLocalCertsAndPrivateKey(
+                NULL, null, getEncodedServerCertificates(), getServerPrivateKey().getNativeRef());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void setLocalCertsAndPrivateKey_withNullCertificatesShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        try {
+            NativeCrypto.setLocalCertsAndPrivateKey(s, null, null, getServerPrivateKey().getNativeRef());
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void setLocalCertsAndPrivateKey_withNullKeyShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        try {
+            NativeCrypto.setLocalCertsAndPrivateKey(s, null, getEncodedServerCertificates(), null);
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test
+    public void setLocalCertsAndPrivateKey() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+
+        NativeCrypto.setLocalCertsAndPrivateKey(
+                s, null, getEncodedServerCertificates(), getServerPrivateKey().getNativeRef());
+
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_set1_tls_channel_id_withNullChannelShouldThrow() throws Exception {
+        NativeCrypto.SSL_set1_tls_channel_id(NULL, null, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_set1_tls_channel_id_withNullKeyShouldThrow() throws Exception {
+        initChannelIdKey();
+
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        try {
+            NativeCrypto.SSL_set1_tls_channel_id(s, null, null);
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test
+    public void test_SSL_use_PrivateKey_for_tls_channel_id() throws Exception {
+        initChannelIdKey();
+
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+
+        // Use the key natively. This works because the initChannelIdKey method ensures that the
+        // key is backed by OpenSSL.
+        NativeCrypto.SSL_set1_tls_channel_id(s, null, CHANNEL_ID_PRIVATE_KEY.getNativeRef());
+
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_get_mode_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_get_mode(NULL, null);
+    }
+
+    @Test
+    public void test_SSL_get_mode() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        assertTrue(NativeCrypto.SSL_get_mode(s, null) != 0);
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_set_mode_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_set_mode(NULL, null, 0);
+    }
+
+    @Test
+    public void test_SSL_set_mode_and_clear_mode() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        // check SSL_MODE_ENABLE_FALSE_START on by default for BoringSSL
+        assertEquals(SSL_MODE_ENABLE_FALSE_START,
+                NativeCrypto.SSL_get_mode(s, null) & SSL_MODE_ENABLE_FALSE_START);
+        // check SSL_MODE_CBC_RECORD_SPLITTING off by default
+        assertEquals(0, NativeCrypto.SSL_get_mode(s, null) & SSL_MODE_CBC_RECORD_SPLITTING);
+
+        // set SSL_MODE_ENABLE_FALSE_START on
+        NativeCrypto.SSL_set_mode(s, null, SSL_MODE_ENABLE_FALSE_START);
+        assertTrue((NativeCrypto.SSL_get_mode(s, null) & SSL_MODE_ENABLE_FALSE_START) != 0);
+        // clear SSL_MODE_ENABLE_FALSE_START off
+        NativeCrypto.SSL_clear_mode(s, null, SSL_MODE_ENABLE_FALSE_START);
+        assertTrue((NativeCrypto.SSL_get_mode(s, null) & SSL_MODE_ENABLE_FALSE_START) == 0);
+
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_get_options_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_get_options(NULL, null);
+    }
+
+    @Test
+    public void test_SSL_get_options() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        assertTrue(NativeCrypto.SSL_get_options(s, null) != 0);
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_set_options_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_set_options(NULL, null, 0);
+    }
+
+    @Test
+    public void test_SSL_set_options() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_CIPHER_SERVER_PREFERENCE) == 0);
+        NativeCrypto.SSL_set_options(s, null, SSL_OP_CIPHER_SERVER_PREFERENCE);
+        assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_CIPHER_SERVER_PREFERENCE) != 0);
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_clear_options_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_clear_options(NULL, null, 0);
+    }
+
+    @Test
+    public void test_SSL_clear_options() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_CIPHER_SERVER_PREFERENCE) == 0);
+        NativeCrypto.SSL_set_options(s, null, SSL_OP_CIPHER_SERVER_PREFERENCE);
+        assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_CIPHER_SERVER_PREFERENCE) != 0);
+        NativeCrypto.SSL_clear_options(s, null, SSL_OP_CIPHER_SERVER_PREFERENCE);
+        assertTrue((NativeCrypto.SSL_get_options(s, null) & SSL_OP_CIPHER_SERVER_PREFERENCE) == 0);
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_set_protocol_versions_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_set_protocol_versions(NULL, null, 0, 0);
+    }
+
+    @Test
+    public void SSL_set_protocol_versions() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        assertEquals(1, NativeCrypto.SSL_set_protocol_versions(s, null, TLS1_VERSION, TLS1_1_VERSION));
+        assertEquals(1, NativeCrypto.SSL_set_protocol_versions(s, null, TLS1_2_VERSION, TLS1_2_VERSION));
+        assertEquals(0, NativeCrypto.SSL_set_protocol_versions(s, null, TLS1_2_VERSION + 413, TLS1_1_VERSION));
+        assertEquals(0, NativeCrypto.SSL_set_protocol_versions(s, null, TLS1_1_VERSION, TLS1_2_VERSION + 413));
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_set_cipher_lists_withNullSslShouldThrow() throws Exception {
+        NativeCrypto.SSL_set_cipher_lists(NULL, null, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_set_cipher_lists_withNullCiphersShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        try {
+            NativeCrypto.SSL_set_cipher_lists(s, null, null);
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_SSL_set_cipher_lists_withNullCipherShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        try {
+            NativeCrypto.SSL_set_cipher_lists(s, null, new String[] {null});
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test
+    public void SSL_set_cipher_lists_withEmptyCiphersShouldSucceed() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+
+        // Explicitly checking that the empty list is allowed.
+        // b/21816861
+        NativeCrypto.SSL_set_cipher_lists(s, null, new String[] {});
+
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+    }
+
+    @Test
+    public void SSL_set_cipher_lists_withIllegalCipherShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+
+        // see OpenSSL ciphers man page
+        String[] illegals = new String[] {// empty
+                "",
+                // never standardized
+                "EXP1024-DES-CBC-SHA",
+                // IDEA
+                "IDEA-CBC-SHA", "IDEA-CBC-MD5"};
+
+        for (String illegal : illegals) {
+            try {
+                NativeCrypto.SSL_set_cipher_lists(s, null, new String[] {illegal});
+                fail("Exception now thrown for illegal cipher: " + illegal);
+            } catch (IllegalArgumentException expected) {
+                // Expected.
+            }
+        }
+
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+    }
+
+    @Test
+    public void SSL_set_cipher_lists_withValidCiphersShouldSucceed() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+
+        List<String> ciphers = new ArrayList<String>(NativeCrypto.SUPPORTED_TLS_1_2_CIPHER_SUITES_SET);
+        NativeCrypto.SSL_set_cipher_lists(s, null, ciphers.toArray(new String[ciphers.size()]));
+
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_set_verify_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_set_verify(NULL, null, 0);
+    }
+
+    @Test
+    public void test_SSL_set_verify() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        NativeCrypto.SSL_set_verify(s, null, SSL_VERIFY_NONE);
+        NativeCrypto.SSL_set_verify(s, null, SSL_VERIFY_PEER);
+        NativeCrypto.SSL_set_verify(s, null, SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
+        NativeCrypto.SSL_set_verify(s, null, (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT));
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+    }
+
+    private static final boolean DEBUG = false;
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class Hooks {
+        String negotiatedCipherSuite;
+        private OpenSSLKey channelIdPrivateKey;
+        boolean pskEnabled;
+        byte[] pskKey;
+        List<String> enabledCipherSuites;
+
+        /**
+         * @throws SSLException if an error occurs creating the context.
+         */
+        public long getContext() throws SSLException {
+            return NativeCrypto.SSL_CTX_new();
+        }
+
+        public long beforeHandshake(long context) throws SSLException {
+            long s = NativeCrypto.SSL_new(context, null);
+            // Limit cipher suites to a known set so authMethod is known.
+            List<String> cipherSuites = new ArrayList<String>();
+            if (enabledCipherSuites == null) {
+                cipherSuites.add("ECDHE-RSA-AES128-SHA");
+                if (pskEnabled) {
+                    // In TLS-PSK the client indicates that PSK key exchange is desired by offering
+                    // at least one PSK cipher suite.
+                    cipherSuites.add(0, "PSK-AES128-CBC-SHA");
+                }
+            } else {
+                cipherSuites.addAll(enabledCipherSuites);
+            }
+            // Protocol list is included for determining whether to send TLS_FALLBACK_SCSV
+            NativeCrypto.setEnabledCipherSuites(s, null,
+                    cipherSuites.toArray(new String[cipherSuites.size()]),
+                    new String[] {"TLSv1.2"});
+
+            if (channelIdPrivateKey != null) {
+                NativeCrypto.SSL_set1_tls_channel_id(s, null, channelIdPrivateKey.getNativeRef());
+            }
+            return s;
+        }
+        public void configureCallbacks(
+                @SuppressWarnings("unused") TestSSLHandshakeCallbacks callbacks) {}
+        public void clientCertificateRequested(@SuppressWarnings("unused") long s)
+                throws CertificateEncodingException, SSLException {}
+        public void afterHandshake(long session, long ssl, long context, Socket socket,
+                FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+            if (session != NULL) {
+                negotiatedCipherSuite = NativeCrypto.SSL_SESSION_cipher(session);
+                NativeCrypto.SSL_SESSION_free(session);
+            }
+            if (ssl != NULL) {
+                try {
+                    NativeCrypto.SSL_shutdown(ssl, null, fd, callback);
+                } catch (IOException e) {
+                    // Expected.
+                }
+                NativeCrypto.SSL_free(ssl, null);
+            }
+            if (context != NULL) {
+                NativeCrypto.SSL_CTX_free(context, null);
+            }
+            if (socket != null) {
+                socket.close();
+            }
+        }
+    }
+
+    static class TestSSLHandshakeCallbacks implements SSLHandshakeCallbacks {
+        private final Socket socket;
+        private final long sslNativePointer;
+        private final Hooks hooks;
+
+        TestSSLHandshakeCallbacks(Socket socket, long sslNativePointer, Hooks hooks) {
+            this.socket = socket;
+            this.sslNativePointer = sslNativePointer;
+            this.hooks = hooks;
+        }
+
+        private long[] certificateChainRefs;
+        private String authMethod;
+        private boolean verifyCertificateChainCalled;
+
+        @Override
+        public void verifyCertificateChain(byte[][] certs, String authMethod)
+                throws CertificateException {
+            certificateChainRefs = new long[certs.length];
+            for (int i = 0; i < certs.length; ++i) {
+                byte[] cert = certs[i];
+                try {
+                    certificateChainRefs[i] = NativeCrypto.d2i_X509(cert);
+                } catch (ParsingException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            this.authMethod = authMethod;
+            this.verifyCertificateChainCalled = true;
+        }
+
+        private byte[] keyTypes;
+        private int[] signatureAlgs;
+        private byte[][] asn1DerEncodedX500Principals;
+        private boolean clientCertificateRequestedCalled;
+
+        @Override
+        public void clientCertificateRequested(
+                byte[] keyTypes, int[] signatureAlgs, byte[][] asn1DerEncodedX500Principals)
+                throws CertificateEncodingException, SSLException {
+            if (DEBUG) {
+                System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+                        + " clientCertificateRequested"
+                        + " keyTypes=" + Arrays.toString(keyTypes)
+                        + " asn1DerEncodedX500Principals="
+                        + Arrays.toString(asn1DerEncodedX500Principals));
+            }
+            this.keyTypes = keyTypes;
+            this.signatureAlgs = signatureAlgs;
+            this.asn1DerEncodedX500Principals = asn1DerEncodedX500Principals;
+            this.clientCertificateRequestedCalled = true;
+            if (hooks != null) {
+                hooks.clientCertificateRequested(sslNativePointer);
+            }
+        }
+
+        private boolean handshakeCompletedCalled;
+
+        @Override
+        public void onSSLStateChange(int type, int val) {
+            if (DEBUG) {
+                System.out.println(
+                        "ssl=0x" + Long.toString(sslNativePointer, 16) + " onSSLStateChange");
+            }
+            this.handshakeCompletedCalled = true;
+        }
+
+        Socket getSocket() {
+            return socket;
+        }
+
+        private boolean clientPSKKeyRequestedInvoked;
+        private String clientPSKKeyRequestedIdentityHint;
+        private int clientPSKKeyRequestedResult;
+        private byte[] clientPSKKeyRequestedResultKey;
+        private byte[] clientPSKKeyRequestedResultIdentity;
+
+        @Override
+        public int clientPSKKeyRequested(String identityHint, byte[] identity, byte[] key) {
+            if (DEBUG) {
+                System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+                        + " clientPSKKeyRequested"
+                        + " identityHint=" + identityHint + " identity capacity=" + identity.length
+                        + " key capacity=" + key.length);
+            }
+            clientPSKKeyRequestedInvoked = true;
+            clientPSKKeyRequestedIdentityHint = identityHint;
+            if (clientPSKKeyRequestedResultKey != null) {
+                System.arraycopy(clientPSKKeyRequestedResultKey, 0, key, 0,
+                        clientPSKKeyRequestedResultKey.length);
+            }
+            if (clientPSKKeyRequestedResultIdentity != null) {
+                System.arraycopy(clientPSKKeyRequestedResultIdentity, 0, identity, 0,
+                        Math.min(clientPSKKeyRequestedResultIdentity.length, identity.length));
+            }
+            return clientPSKKeyRequestedResult;
+        }
+
+        private boolean serverPSKKeyRequestedInvoked;
+        private int serverPSKKeyRequestedResult;
+        private byte[] serverPSKKeyRequestedResultKey;
+        private String serverPSKKeyRequestedIdentityHint;
+        private String serverPSKKeyRequestedIdentity;
+
+        @Override
+        public int serverPSKKeyRequested(String identityHint, String identity, byte[] key) {
+            if (DEBUG) {
+                System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+                        + " serverPSKKeyRequested"
+                        + " identityHint=" + identityHint + " identity=" + identity
+                        + " key capacity=" + key.length);
+            }
+            serverPSKKeyRequestedInvoked = true;
+            serverPSKKeyRequestedIdentityHint = identityHint;
+            serverPSKKeyRequestedIdentity = identity;
+            if (serverPSKKeyRequestedResultKey != null) {
+                System.arraycopy(serverPSKKeyRequestedResultKey, 0, key, 0,
+                        serverPSKKeyRequestedResultKey.length);
+            }
+            return serverPSKKeyRequestedResult;
+        }
+
+        private boolean onNewSessionEstablishedInvoked;
+        private boolean onNewSessionEstablishedSaveSession;
+        private long onNewSessionEstablishedSessionNativePointer;
+
+        @Override
+        public void onNewSessionEstablished(long sslSessionNativePtr) {
+            if (DEBUG) {
+                System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+                        + " onNewSessionCreated"
+                        + " ssl=0x" + Long.toString(sslSessionNativePtr, 16));
+            }
+            onNewSessionEstablishedInvoked = true;
+
+            if (onNewSessionEstablishedSaveSession) {
+                NativeCrypto.SSL_SESSION_up_ref(sslSessionNativePtr);
+                onNewSessionEstablishedSessionNativePointer = sslSessionNativePtr;
+            }
+        }
+
+        @Override
+        public long serverSessionRequested(byte[] id) {
+            // TODO(nathanmittler): Implement server-side caching for TLS < 1.3
+            return 0;
+        }
+    }
+
+    static class ClientHooks extends Hooks {
+        private String pskIdentity;
+
+        @Override
+        public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
+            super.configureCallbacks(callbacks);
+            if (pskEnabled) {
+                if (pskIdentity != null) {
+                    // Create a NULL-terminated modified UTF-8 representation of pskIdentity.
+                    byte[] b;
+                    try {
+                        b = pskIdentity.getBytes("UTF-8");
+                    } catch (UnsupportedEncodingException e) {
+                        throw new RuntimeException("UTF-8 encoding not supported", e);
+                    }
+                    callbacks.clientPSKKeyRequestedResultIdentity = Arrays.copyOf(b, b.length + 1);
+                }
+                callbacks.clientPSKKeyRequestedResultKey = pskKey;
+                callbacks.clientPSKKeyRequestedResult = (pskKey != null) ? pskKey.length : 0;
+            }
+        }
+
+        @Override
+        public long beforeHandshake(long c) throws SSLException {
+            long s = super.beforeHandshake(c);
+            if (pskEnabled) {
+                NativeCrypto.set_SSL_psk_client_callback_enabled(s, null, true);
+            }
+            return s;
+        }
+    }
+
+    static class ServerHooks extends Hooks {
+        private final OpenSSLKey privateKey;
+        private final byte[][] certificates;
+        private boolean channelIdEnabled;
+        private byte[] channelIdAfterHandshake;
+        private Throwable channelIdAfterHandshakeException;
+
+        private String pskIdentityHint;
+
+        public ServerHooks() {
+            this(null, null);
+        }
+
+        ServerHooks(OpenSSLKey privateKey, byte[][] certificates) {
+            this.privateKey = privateKey;
+            this.certificates = certificates;
+        }
+
+        @Override
+        public long beforeHandshake(long c) throws SSLException {
+            long s = super.beforeHandshake(c);
+            if (privateKey != null && certificates != null) {
+                NativeCrypto.setLocalCertsAndPrivateKey(s, null, certificates, privateKey.getNativeRef());
+            }
+            if (channelIdEnabled) {
+                NativeCrypto.SSL_enable_tls_channel_id(s, null);
+            }
+            if (pskEnabled) {
+                NativeCrypto.set_SSL_psk_server_callback_enabled(s, null, true);
+                NativeCrypto.SSL_use_psk_identity_hint(s, null, pskIdentityHint);
+            }
+            NativeCrypto.SSL_set_verify(s, null, SSL_VERIFY_NONE);
+            return s;
+        }
+
+        @Override
+        public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
+            super.configureCallbacks(callbacks);
+            if (pskEnabled) {
+                callbacks.serverPSKKeyRequestedResultKey = pskKey;
+                callbacks.serverPSKKeyRequestedResult = (pskKey != null) ? pskKey.length : 0;
+            }
+        }
+
+        @Override
+        public void afterHandshake(long session, long ssl, long context, Socket socket,
+                FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+            if (channelIdEnabled) {
+                try {
+                    channelIdAfterHandshake = NativeCrypto.SSL_get_tls_channel_id(ssl, null);
+                } catch (Exception e) {
+                    channelIdAfterHandshakeException = e;
+                }
+            }
+            super.afterHandshake(session, ssl, context, socket, fd, callback);
+        }
+
+        @Override
+        public void clientCertificateRequested(long s) {
+            fail("Server asked for client certificates");
+        }
+    }
+
+    public static Future<TestSSLHandshakeCallbacks> handshake(final ServerSocket listener,
+            final int timeout, final boolean client, final Hooks hooks, final byte[] alpnProtocols,
+            final ApplicationProtocolSelectorAdapter alpnSelector) {
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        Future<TestSSLHandshakeCallbacks> future =
+                executor.submit(new Callable<TestSSLHandshakeCallbacks>() {
+                    @Override
+                    public TestSSLHandshakeCallbacks call() throws Exception {
+                        @SuppressWarnings("resource")
+                        // Socket needs to remain open after the handshake
+                        Socket socket = (client ? new Socket(listener.getInetAddress(),
+                                                          listener.getLocalPort())
+                                                : listener.accept());
+                        if (timeout == -1) {
+                            return new TestSSLHandshakeCallbacks(socket, 0, null);
+                        }
+                        FileDescriptor fd =
+                                (FileDescriptor) m_Platform_getFileDescriptor.invoke(
+                                        null, socket);
+                        long c = hooks.getContext();
+                        long s = hooks.beforeHandshake(c);
+                        TestSSLHandshakeCallbacks callback =
+                                new TestSSLHandshakeCallbacks(socket, s, hooks);
+                        hooks.configureCallbacks(callback);
+                        if (DEBUG) {
+                            System.out.println("ssl=0x" + Long.toString(s, 16) + " handshake"
+                                    + " context=0x" + Long.toString(c, 16) + " socket=" + socket
+                                    + " fd=0x" + Long.toString(System.identityHashCode(fd), 16)
+                                    + " timeout=" + timeout + " client=" + client);
+                        }
+                        long session = NULL;
+                        try {
+                            if (client) {
+                                NativeCrypto.SSL_set_connect_state(s, null);
+                            } else {
+                                NativeCrypto.SSL_set_accept_state(s, null);
+                            }
+                            if (alpnProtocols != null) {
+                                NativeCrypto.setApplicationProtocols(s, null, client, alpnProtocols);
+                            }
+                            if (!client && alpnSelector != null) {
+                                NativeCrypto.setApplicationProtocolSelector(s, null, alpnSelector);
+                            }
+                            NativeCrypto.SSL_do_handshake(s, null, fd, callback, timeout);
+                            session = NativeCrypto.SSL_get1_session(s, null);
+                            if (DEBUG) {
+                                System.out.println("ssl=0x" + Long.toString(s, 16)
+                                        + " handshake"
+                                        + " session=0x" + Long.toString(session, 16));
+                            }
+                        } finally {
+                            // Ensure afterHandshake is called to free resources
+                            hooks.afterHandshake(session, s, c, socket, fd, callback);
+                        }
+                        return callback;
+                    }
+                });
+        executor.shutdown();
+        return future;
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_SSL_do_handshake_NULL_SSL() throws Exception {
+        NativeCrypto.SSL_do_handshake(NULL, null, null, null, 0);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_SSL_do_handshake_withNullFdShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        NativeCrypto.SSL_set_connect_state(s, null);
+        try {
+            NativeCrypto.SSL_do_handshake(s, null, null, null, 0);
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_SSL_do_handshake_withNullShcShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        NativeCrypto.SSL_set_connect_state(s, null);
+        try {
+            NativeCrypto.SSL_do_handshake(s, null, INVALID_FD, null, 0);
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test
+    public void test_SSL_do_handshake_normal() throws Exception {
+        // normal client and server case
+        final ServerSocket listener = newServerSocket();
+        Hooks cHooks = new Hooks();
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue(clientCallback.verifyCertificateChainCalled);
+        assertEqualCertificateChains(
+                getServerCertificateRefs(), clientCallback.certificateChainRefs);
+        assertEquals("ECDHE_RSA", clientCallback.authMethod);
+        assertFalse(serverCallback.verifyCertificateChainCalled);
+        assertFalse(clientCallback.clientCertificateRequestedCalled);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+        assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+        assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+        assertTrue(serverCallback.onNewSessionEstablishedInvoked);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_reusedSession() throws Exception {
+        // normal client and server case
+        final ServerSocket listener = newServerSocket();
+
+        Future<TestSSLHandshakeCallbacks> client1 = handshake(listener, 0, true, new ClientHooks() {
+            @Override
+            public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
+                callbacks.onNewSessionEstablishedSaveSession = true;
+            }
+        }, null, null);
+        Future<TestSSLHandshakeCallbacks> server1 = handshake(listener, 0,
+                false, new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
+                    @Override
+                    public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
+                        callbacks.onNewSessionEstablishedSaveSession = true;
+                    }
+                }, null, null);
+        TestSSLHandshakeCallbacks clientCallback1 = client1.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback1 = server1.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue(clientCallback1.verifyCertificateChainCalled);
+        assertEqualCertificateChains(
+                getServerCertificateRefs(), clientCallback1.certificateChainRefs);
+        assertEquals("ECDHE_RSA", clientCallback1.authMethod);
+        assertFalse(serverCallback1.verifyCertificateChainCalled);
+        assertFalse(clientCallback1.clientCertificateRequestedCalled);
+        assertFalse(serverCallback1.clientCertificateRequestedCalled);
+        assertFalse(clientCallback1.clientPSKKeyRequestedInvoked);
+        assertFalse(serverCallback1.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback1.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback1.serverPSKKeyRequestedInvoked);
+        assertTrue(clientCallback1.onNewSessionEstablishedInvoked);
+        assertTrue(serverCallback1.onNewSessionEstablishedInvoked);
+        assertTrue(clientCallback1.handshakeCompletedCalled);
+        assertTrue(serverCallback1.handshakeCompletedCalled);
+
+        final long clientSessionContext =
+                clientCallback1.onNewSessionEstablishedSessionNativePointer;
+        final long serverSessionContext =
+                serverCallback1.onNewSessionEstablishedSessionNativePointer;
+
+        Future<TestSSLHandshakeCallbacks> client2 = handshake(listener, 0, true, new ClientHooks() {
+            @Override
+            public long beforeHandshake(long c) throws SSLException {
+                long sslNativePtr = super.beforeHandshake(c);
+                NativeCrypto.SSL_set_session(sslNativePtr, null, clientSessionContext);
+                return sslNativePtr;
+            }
+        }, null, null);
+        Future<TestSSLHandshakeCallbacks> server2 = handshake(listener, 0,
+                false, new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
+                    @Override
+                    public long beforeHandshake(long c) throws SSLException {
+                        long sslNativePtr = super.beforeHandshake(c);
+                        NativeCrypto.SSL_set_session(sslNativePtr, null, serverSessionContext);
+                        return sslNativePtr;
+                    }
+                }, null, null);
+        TestSSLHandshakeCallbacks clientCallback2 = client2.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback2 = server2.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue(clientCallback2.verifyCertificateChainCalled);
+        assertEqualCertificateChains(
+                getServerCertificateRefs(), clientCallback2.certificateChainRefs);
+        assertEquals("ECDHE_RSA", clientCallback2.authMethod);
+        assertFalse(serverCallback2.verifyCertificateChainCalled);
+        assertFalse(clientCallback2.clientCertificateRequestedCalled);
+        assertFalse(serverCallback2.clientCertificateRequestedCalled);
+        assertFalse(clientCallback2.clientPSKKeyRequestedInvoked);
+        assertFalse(serverCallback2.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback2.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback2.serverPSKKeyRequestedInvoked);
+        assertTrue(clientCallback2.onNewSessionEstablishedInvoked);
+        assertTrue(serverCallback2.onNewSessionEstablishedInvoked);
+        assertTrue(clientCallback2.handshakeCompletedCalled);
+        assertTrue(serverCallback2.handshakeCompletedCalled);
+
+        NativeCrypto.SSL_SESSION_free(clientSessionContext);
+        NativeCrypto.SSL_SESSION_free(serverSessionContext);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_optional_client_certificate() throws Exception {
+        // optional client certificate case
+        final ServerSocket listener = newServerSocket();
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void clientCertificateRequested(long s)
+                    throws CertificateEncodingException, SSLException {
+                super.clientCertificateRequested(s);
+                NativeCrypto.setLocalCertsAndPrivateKey(
+                        s, null, getEncodedClientCertificates(), getClientPrivateKey().getNativeRef());
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
+            @Override
+            public long beforeHandshake(long c) throws SSLException {
+                long s = super.beforeHandshake(c);
+                NativeCrypto.SSL_set_client_CA_list(s, null, getCaPrincipals());
+                NativeCrypto.SSL_set_verify(s, null, SSL_VERIFY_PEER);
+                return s;
+            }
+        };
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue(clientCallback.verifyCertificateChainCalled);
+        assertEqualCertificateChains(
+                getServerCertificateRefs(), clientCallback.certificateChainRefs);
+        assertEquals("ECDHE_RSA", clientCallback.authMethod);
+        assertTrue(serverCallback.verifyCertificateChainCalled);
+        assertEqualCertificateChains(
+                getClientCertificateRefs(), serverCallback.certificateChainRefs);
+        assertEquals("ECDHE_RSA", serverCallback.authMethod);
+
+        assertTrue(clientCallback.clientCertificateRequestedCalled);
+        assertNotNull(clientCallback.keyTypes);
+        assertNotNull(clientCallback.signatureAlgs);
+        assertEquals(new HashSet<String>(Arrays.asList("EC", "RSA")),
+                SSLUtils.getSupportedClientKeyTypes(
+                        clientCallback.keyTypes, clientCallback.signatureAlgs));
+        assertEqualPrincipals(getCaPrincipals(), clientCallback.asn1DerEncodedX500Principals);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+
+        assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+        assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+        assertTrue(serverCallback.onNewSessionEstablishedInvoked);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_missing_required_certificate() throws Exception {
+        // required client certificate negative case
+        final ServerSocket listener = newServerSocket();
+        try {
+            Hooks cHooks = new Hooks();
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
+                @Override
+                public long beforeHandshake(long c) throws SSLException {
+                    long s = super.beforeHandshake(c);
+                    NativeCrypto.SSL_set_client_CA_list(s, null, getCaPrincipals());
+                    NativeCrypto.SSL_set_verify(
+                            s, null, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
+                    return s;
+                }
+            };
+            @SuppressWarnings("unused")
+            Future<TestSSLHandshakeCallbacks> client =
+                    handshake(listener, 0, true, cHooks, null, null);
+            Future<TestSSLHandshakeCallbacks> server =
+                    handshake(listener, 0, false, sHooks, null, null);
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void test_SSL_do_handshake_client_timeout() throws Exception {
+        // client timeout
+        final ServerSocket listener = newServerSocket();
+        Socket serverSocket = null;
+        try {
+            Hooks cHooks = new Hooks();
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
+            Future<TestSSLHandshakeCallbacks> client =
+                    handshake(listener, 1, true, cHooks, null, null);
+            Future<TestSSLHandshakeCallbacks> server =
+                    handshake(listener, -1, false, sHooks, null, null);
+            serverSocket = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).getSocket();
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            assertEquals(SocketTimeoutException.class, expected.getCause().getClass());
+        } finally {
+            // Manually close peer socket when testing timeout
+            IoUtils.closeQuietly(serverSocket);
+        }
+    }
+
+    @Test
+    public void test_SSL_do_handshake_server_timeout() throws Exception {
+        // server timeout
+        final ServerSocket listener = newServerSocket();
+        Socket clientSocket = null;
+        try {
+            Hooks cHooks = new Hooks();
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
+            Future<TestSSLHandshakeCallbacks> client =
+                    handshake(listener, -1, true, cHooks, null, null);
+            Future<TestSSLHandshakeCallbacks> server =
+                    handshake(listener, 1, false, sHooks, null, null);
+            clientSocket = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).getSocket();
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            assertEquals(SocketTimeoutException.class, expected.getCause().getClass());
+        } finally {
+            // Manually close peer socket when testing timeout
+            IoUtils.closeQuietly(clientSocket);
+        }
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_channel_id_normal() throws Exception {
+        initChannelIdKey();
+
+        // Normal handshake with TLS Channel ID.
+        final ServerSocket listener = newServerSocket();
+        Hooks cHooks = new Hooks();
+        cHooks.channelIdPrivateKey = CHANNEL_ID_PRIVATE_KEY;
+        // TLS Channel ID currently requires ECDHE-based key exchanges.
+        cHooks.enabledCipherSuites = Collections.singletonList("ECDHE-RSA-AES128-SHA");
+        ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
+        sHooks.channelIdEnabled = true;
+        sHooks.enabledCipherSuites = cHooks.enabledCipherSuites;
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue(clientCallback.verifyCertificateChainCalled);
+        assertEqualCertificateChains(
+                getServerCertificateRefs(), clientCallback.certificateChainRefs);
+        assertEquals("ECDHE_RSA", clientCallback.authMethod);
+        assertFalse(serverCallback.verifyCertificateChainCalled);
+        assertFalse(clientCallback.clientCertificateRequestedCalled);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+        assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+        assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+        assertTrue(serverCallback.onNewSessionEstablishedInvoked);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+        assertNull(sHooks.channelIdAfterHandshakeException);
+        assertEqualByteArrays(CHANNEL_ID, sHooks.channelIdAfterHandshake);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_channel_id_not_supported_by_server() throws Exception {
+        initChannelIdKey();
+
+        // Client tries to use TLS Channel ID but the server does not enable/offer the extension.
+        final ServerSocket listener = newServerSocket();
+        Hooks cHooks = new Hooks();
+        cHooks.channelIdPrivateKey = CHANNEL_ID_PRIVATE_KEY;
+        // TLS Channel ID currently requires ECDHE-based key exchanges.
+        cHooks.enabledCipherSuites = Collections.singletonList("ECDHE-RSA-AES128-SHA");
+        ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
+        sHooks.channelIdEnabled = false;
+        sHooks.enabledCipherSuites = cHooks.enabledCipherSuites;
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue(clientCallback.verifyCertificateChainCalled);
+        assertEqualCertificateChains(
+                getServerCertificateRefs(), clientCallback.certificateChainRefs);
+        assertEquals("ECDHE_RSA", clientCallback.authMethod);
+        assertFalse(serverCallback.verifyCertificateChainCalled);
+        assertFalse(clientCallback.clientCertificateRequestedCalled);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+        assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+        assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+        assertTrue(serverCallback.onNewSessionEstablishedInvoked);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+        assertNull(sHooks.channelIdAfterHandshakeException);
+        assertNull(sHooks.channelIdAfterHandshake);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_channel_id_not_enabled_by_client() throws Exception {
+        initChannelIdKey();
+
+        // Client does not use TLS Channel ID when the server has the extension enabled/offered.
+        final ServerSocket listener = newServerSocket();
+        Hooks cHooks = new Hooks();
+        cHooks.channelIdPrivateKey = null;
+        // TLS Channel ID currently requires ECDHE-based key exchanges.
+        cHooks.enabledCipherSuites = Collections.singletonList("ECDHE-RSA-AES128-SHA");
+        ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
+        sHooks.channelIdEnabled = true;
+        sHooks.enabledCipherSuites = cHooks.enabledCipherSuites;
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue(clientCallback.verifyCertificateChainCalled);
+        assertEqualCertificateChains(
+                getServerCertificateRefs(), clientCallback.certificateChainRefs);
+        assertEquals("ECDHE_RSA", clientCallback.authMethod);
+        assertFalse(serverCallback.verifyCertificateChainCalled);
+        assertFalse(clientCallback.clientCertificateRequestedCalled);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+        assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+        assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+        assertTrue(serverCallback.onNewSessionEstablishedInvoked);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+        assertNull(sHooks.channelIdAfterHandshakeException);
+        assertNull(sHooks.channelIdAfterHandshake);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_psk_normal() throws Exception {
+        // normal TLS-PSK client and server case
+        final ServerSocket listener = newServerSocket();
+        Hooks cHooks = new ClientHooks();
+        ServerHooks sHooks = new ServerHooks();
+        cHooks.pskEnabled = true;
+        sHooks.pskEnabled = true;
+        cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+        sHooks.pskKey = cHooks.pskKey;
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertFalse(clientCallback.verifyCertificateChainCalled);
+        assertFalse(serverCallback.verifyCertificateChainCalled);
+        assertFalse(clientCallback.clientCertificateRequestedCalled);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+        assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+        assertTrue(serverCallback.onNewSessionEstablishedInvoked);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+        assertTrue(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertTrue(serverCallback.serverPSKKeyRequestedInvoked);
+        assertContains(cHooks.negotiatedCipherSuite, "PSK");
+        assertEquals(cHooks.negotiatedCipherSuite, sHooks.negotiatedCipherSuite);
+        assertNull(clientCallback.clientPSKKeyRequestedIdentityHint);
+        assertNull(serverCallback.serverPSKKeyRequestedIdentityHint);
+        assertEquals("", serverCallback.serverPSKKeyRequestedIdentity);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_psk_with_identity_and_hint() throws Exception {
+        // normal TLS-PSK client and server case where the server provides the client with a PSK
+        // identity hint, and the client provides the server with a PSK identity.
+        final ServerSocket listener = newServerSocket();
+        ClientHooks cHooks = new ClientHooks();
+        ServerHooks sHooks = new ServerHooks();
+        cHooks.pskEnabled = true;
+        sHooks.pskEnabled = true;
+        cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+        sHooks.pskKey = cHooks.pskKey;
+        sHooks.pskIdentityHint = "Some non-ASCII characters: \u00c4\u0332";
+        cHooks.pskIdentity = "More non-ASCII characters: \u00f5\u044b";
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertFalse(clientCallback.verifyCertificateChainCalled);
+        assertFalse(serverCallback.verifyCertificateChainCalled);
+        assertFalse(clientCallback.clientCertificateRequestedCalled);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+        assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+        assertTrue(serverCallback.onNewSessionEstablishedInvoked);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+        assertTrue(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertTrue(serverCallback.serverPSKKeyRequestedInvoked);
+        assertContains(cHooks.negotiatedCipherSuite, "PSK");
+        assertEquals(cHooks.negotiatedCipherSuite, sHooks.negotiatedCipherSuite);
+        assertEquals(sHooks.pskIdentityHint, clientCallback.clientPSKKeyRequestedIdentityHint);
+        assertEquals(sHooks.pskIdentityHint, serverCallback.serverPSKKeyRequestedIdentityHint);
+        assertEquals(cHooks.pskIdentity, serverCallback.serverPSKKeyRequestedIdentity);
+    }
+
+    @Test
+    @SuppressWarnings("deprecation")
+    public void test_SSL_do_handshake_with_psk_with_identity_and_hint_of_max_length()
+            throws Exception {
+        // normal TLS-PSK client and server case where the server provides the client with a PSK
+        // identity hint, and the client provides the server with a PSK identity.
+        final ServerSocket listener = newServerSocket();
+        ClientHooks cHooks = new ClientHooks();
+        ServerHooks sHooks = new ServerHooks();
+        cHooks.pskEnabled = true;
+        sHooks.pskEnabled = true;
+        cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+        sHooks.pskKey = cHooks.pskKey;
+        sHooks.pskIdentityHint = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+                + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx";
+        cHooks.pskIdentity = "123456789012345678901234567890123456789012345678901234567890"
+                + "12345678901234567890123456789012345678901234567890123456789012345678";
+        assertEquals(PSKKeyManager.MAX_IDENTITY_HINT_LENGTH_BYTES, sHooks.pskIdentityHint.length());
+        assertEquals(PSKKeyManager.MAX_IDENTITY_LENGTH_BYTES, cHooks.pskIdentity.length());
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertFalse(clientCallback.verifyCertificateChainCalled);
+        assertFalse(serverCallback.verifyCertificateChainCalled);
+        assertFalse(clientCallback.clientCertificateRequestedCalled);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+        assertTrue(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertTrue(serverCallback.serverPSKKeyRequestedInvoked);
+        assertContains(cHooks.negotiatedCipherSuite, "PSK");
+        assertEquals(cHooks.negotiatedCipherSuite, sHooks.negotiatedCipherSuite);
+        assertEquals(sHooks.pskIdentityHint, clientCallback.clientPSKKeyRequestedIdentityHint);
+        assertEquals(sHooks.pskIdentityHint, serverCallback.serverPSKKeyRequestedIdentityHint);
+        assertEquals(cHooks.pskIdentity, serverCallback.serverPSKKeyRequestedIdentity);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_psk_key_mismatch() throws Exception {
+        final ServerSocket listener = newServerSocket();
+        ClientHooks cHooks = new ClientHooks();
+        ServerHooks sHooks = new ServerHooks();
+        cHooks.pskEnabled = true;
+        sHooks.pskEnabled = true;
+        cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+        sHooks.pskKey = "1, 2, 3, 3, Testing...".getBytes("UTF-8");
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        try {
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_psk_with_no_client_key() throws Exception {
+        final ServerSocket listener = newServerSocket();
+        ClientHooks cHooks = new ClientHooks();
+        ServerHooks sHooks = new ServerHooks();
+        cHooks.pskEnabled = true;
+        sHooks.pskEnabled = true;
+        cHooks.pskKey = null;
+        sHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        try {
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_psk_with_no_server_key() throws Exception {
+        final ServerSocket listener = newServerSocket();
+        ClientHooks cHooks = new ClientHooks();
+        ServerHooks sHooks = new ServerHooks();
+        cHooks.pskEnabled = true;
+        sHooks.pskEnabled = true;
+        cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+        sHooks.pskKey = null;
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        try {
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+        }
+    }
+
+    @Test
+    @SuppressWarnings("deprecation")
+    public void test_SSL_do_handshake_with_psk_key_too_long() throws Exception {
+        final ServerSocket listener = newServerSocket();
+        ClientHooks cHooks = new ClientHooks() {
+            @Override
+            public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
+                super.configureCallbacks(callbacks);
+                callbacks.clientPSKKeyRequestedResult = PSKKeyManager.MAX_KEY_LENGTH_BYTES + 1;
+            }
+        };
+        ServerHooks sHooks = new ServerHooks();
+        cHooks.pskEnabled = true;
+        sHooks.pskEnabled = true;
+        cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+        sHooks.pskKey = cHooks.pskKey;
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        try {
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_ocsp_response() throws Exception {
+        final byte[] OCSP_TEST_DATA = new byte[] {1, 2, 3, 4};
+
+        final ServerSocket listener = newServerSocket();
+        Hooks cHooks = new Hooks() {
+            @Override
+            public long beforeHandshake(long c) throws SSLException {
+                long s = super.beforeHandshake(c);
+                NativeCrypto.SSL_enable_ocsp_stapling(s, null);
+                return s;
+            }
+
+            @Override
+            public void afterHandshake(long session, long ssl, long context, Socket socket,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                assertEqualByteArrays(OCSP_TEST_DATA, NativeCrypto.SSL_get_ocsp_response(ssl, null));
+                super.afterHandshake(session, ssl, context, socket, fd, callback);
+            }
+        };
+
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
+            @Override
+            public long beforeHandshake(long c) throws SSLException {
+                long s = super.beforeHandshake(c);
+                NativeCrypto.SSL_set_ocsp_response(s, null, OCSP_TEST_DATA);
+                return s;
+            }
+        };
+
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_sct_extension() throws Exception {
+        // Fake SCT extension has a length of overall extension (unsigned 16-bit).
+        // Each SCT entry has a length (unsigned 16-bit) and data.
+        final byte[] SCT_TEST_DATA = new byte[] {0, 6, 0, 4, 1, 2, 3, 4};
+
+        final ServerSocket listener = newServerSocket();
+        Hooks cHooks = new Hooks() {
+            @Override
+            public long beforeHandshake(long c) throws SSLException {
+                long s = super.beforeHandshake(c);
+                NativeCrypto.SSL_enable_signed_cert_timestamps(s, null);
+                return s;
+            }
+
+            @Override
+            public void afterHandshake(long session, long ssl, long context, Socket socket,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                assertEqualByteArrays(
+                        SCT_TEST_DATA, NativeCrypto.SSL_get_signed_cert_timestamp_list(ssl, null));
+                super.afterHandshake(session, ssl, context, socket, fd, callback);
+            }
+        };
+
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
+            @Override
+            public long beforeHandshake(long c) throws SSLException {
+                long s = super.beforeHandshake(c);
+                NativeCrypto.SSL_set_signed_cert_timestamp_list(s, null, SCT_TEST_DATA);
+                return s;
+            }
+        };
+
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+
+        assertTrue(clientCallback.onNewSessionEstablishedInvoked);
+        assertTrue(serverCallback.onNewSessionEstablishedInvoked);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+    }
+
+    @Test
+    @SuppressWarnings("deprecation")
+    public void test_SSL_use_psk_identity_hint() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        try {
+            NativeCrypto.SSL_use_psk_identity_hint(s, null, null);
+            NativeCrypto.SSL_use_psk_identity_hint(s, null, "test");
+
+            try {
+                // 800 characters is much longer than the permitted maximum.
+                StringBuilder pskIdentityHint = new StringBuilder();
+                for (int i = 0; i < 160; i++) {
+                    pskIdentityHint.append(" long");
+                }
+                assertTrue(pskIdentityHint.length() > PSKKeyManager.MAX_IDENTITY_HINT_LENGTH_BYTES);
+                NativeCrypto.SSL_use_psk_identity_hint(s, null, pskIdentityHint.toString());
+                fail();
+            } catch (SSLException expected) {
+                // Expected.
+            }
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_set_session_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_set_session(NULL, null, NULL);
+    }
+
+    @Test
+    public void test_SSL_set_session() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        NativeCrypto.SSL_set_session(s, null, NULL);
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+
+        {
+            final long clientContext = NativeCrypto.SSL_CTX_new();
+            final long serverContext = NativeCrypto.SSL_CTX_new();
+            final ServerSocket listener = newServerSocket();
+            final long[] clientSession = new long[] {NULL};
+            final long[] serverSession = new long[] {NULL};
+            {
+                Hooks cHooks = new Hooks() {
+                    @Override
+                    public long getContext() throws SSLException {
+                        return clientContext;
+                    }
+                    @Override
+                    public void afterHandshake(long session, long s, long c, Socket sock,
+                            FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                        super.afterHandshake(NULL, s, NULL, sock, fd, callback);
+                        clientSession[0] = session;
+                    }
+                };
+                Hooks sHooks = new ServerHooks(
+                        getServerPrivateKey(), getEncodedServerCertificates()) {
+                    @Override
+                    public long getContext() throws SSLException {
+                        return serverContext;
+                    }
+                    @Override
+                    public void afterHandshake(long session, long s, long c, Socket sock,
+                            FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                        super.afterHandshake(NULL, s, NULL, sock, fd, callback);
+                        serverSession[0] = session;
+                    }
+                };
+                Future<TestSSLHandshakeCallbacks> client =
+                        handshake(listener, 0, true, cHooks, null, null);
+                Future<TestSSLHandshakeCallbacks> server =
+                        handshake(listener, 0, false, sHooks, null, null);
+                client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+                server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            }
+            assertEqualSessions(clientSession[0], serverSession[0]);
+            {
+                Hooks cHooks = new Hooks() {
+                    @Override
+                    public long getContext() throws SSLException {
+                        return clientContext;
+                    }
+                    @Override
+                    public long beforeHandshake(long c) throws SSLException {
+                        long s = NativeCrypto.SSL_new(clientContext, null);
+                        NativeCrypto.SSL_set_session(s, null, clientSession[0]);
+                        return s;
+                    }
+                    @Override
+                    public void afterHandshake(long session, long s, long c, Socket sock,
+                            FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                        assertEqualSessions(clientSession[0], session);
+                        super.afterHandshake(NULL, s, NULL, sock, fd, callback);
+                    }
+                };
+                Hooks sHooks = new ServerHooks(
+                        getServerPrivateKey(), getEncodedServerCertificates()) {
+                    @Override
+                    public long getContext() throws SSLException {
+                        return serverContext;
+                    }
+                    @Override
+                    public void afterHandshake(long session, long s, long c, Socket sock,
+                            FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                        assertEqualSessions(serverSession[0], session);
+                        super.afterHandshake(NULL, s, NULL, sock, fd, callback);
+                    }
+                };
+                Future<TestSSLHandshakeCallbacks> client =
+                        handshake(listener, 0, true, cHooks, null, null);
+                Future<TestSSLHandshakeCallbacks> server =
+                        handshake(listener, 0, false, sHooks, null, null);
+                client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+                server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            }
+            NativeCrypto.SSL_SESSION_free(clientSession[0]);
+            NativeCrypto.SSL_SESSION_free(serverSession[0]);
+            NativeCrypto.SSL_CTX_free(serverContext, null);
+            NativeCrypto.SSL_CTX_free(clientContext, null);
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_set_session_creation_enabled_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_set_session_creation_enabled(NULL, null, false);
+    }
+
+    @Test
+    public void test_SSL_set_session_creation_enabled() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        NativeCrypto.SSL_set_session_creation_enabled(s, null, false);
+        NativeCrypto.SSL_set_session_creation_enabled(s, null, true);
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+
+        final ServerSocket listener = newServerSocket();
+
+        // negative test case for SSL_set_session_creation_enabled(false) on client
+        {
+            Hooks cHooks = new Hooks() {
+                @Override
+                public long beforeHandshake(long c) throws SSLException {
+                    long s = super.beforeHandshake(c);
+                    NativeCrypto.SSL_set_session_creation_enabled(s, null, false);
+                    return s;
+                }
+            };
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
+            Future<TestSSLHandshakeCallbacks> client =
+                    handshake(listener, 0, true, cHooks, null, null);
+            @SuppressWarnings("unused")
+            Future<TestSSLHandshakeCallbacks> server =
+                    handshake(listener, 0, false, sHooks, null, null);
+            try {
+                client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+                fail();
+            } catch (ExecutionException expected) {
+                assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+            }
+            try {
+                server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+                fail();
+            } catch (ExecutionException expected) {
+                assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+            }
+        }
+
+        // negative test case for SSL_set_session_creation_enabled(false) on server
+        {
+            Hooks cHooks = new Hooks();
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
+                @Override
+                public long beforeHandshake(long c) throws SSLException {
+                    long s = super.beforeHandshake(c);
+                    NativeCrypto.SSL_set_session_creation_enabled(s, null, false);
+                    return s;
+                }
+            };
+            Future<TestSSLHandshakeCallbacks> client =
+                    handshake(listener, 0, true, cHooks, null, null);
+            @SuppressWarnings("unused")
+            Future<TestSSLHandshakeCallbacks> server =
+                    handshake(listener, 0, false, sHooks, null, null);
+            try {
+                client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+                fail();
+            } catch (ExecutionException expected) {
+                assertEquals(SSLHandshakeException.class, expected.getCause().getClass());
+            }
+            try {
+                server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+                fail();
+            } catch (ExecutionException expected) {
+                assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+            }
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_set_tlsext_host_name_withNullSslShouldThrow() throws Exception {
+        NativeCrypto.SSL_set_tlsext_host_name(NULL, null, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_set_tlsext_host_name_withNullHostnameShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+
+        try {
+            NativeCrypto.SSL_set_tlsext_host_name(s, null, null);
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test(expected = SSLException.class)
+    public void SSL_set_tlsext_host_name_withTooLongHostnameShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+
+        try {
+            char[] longHostname = new char[256];
+            Arrays.fill(longHostname, 'w');
+            NativeCrypto.SSL_set_tlsext_host_name(s, null, new String(longHostname));
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test
+    public void test_SSL_set_tlsext_host_name() throws Exception {
+        final String hostname = "www.android.com";
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+
+        assertNull(NativeCrypto.SSL_get_servername(s, null));
+        NativeCrypto.SSL_set_tlsext_host_name(s, null, hostname);
+        assertEquals(hostname, NativeCrypto.SSL_get_servername(s, null));
+
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+
+        final ServerSocket listener = newServerSocket();
+
+        // normal
+        Hooks cHooks = new Hooks() {
+            @Override
+            public long beforeHandshake(long c) throws SSLException {
+                long s = super.beforeHandshake(c);
+                NativeCrypto.SSL_set_tlsext_host_name(s, null, hostname);
+                return s;
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                assertEquals(hostname, NativeCrypto.SSL_get_servername(s, null));
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void alpnWithProtocolListShouldSucceed() throws Exception {
+        final byte[] clientAlpnProtocols =
+                SSLUtils.encodeProtocols(new String[] {"http/1.1", "foo", "spdy/2"});
+        final byte[] serverAlpnProtocols =
+                SSLUtils.encodeProtocols(new String[] {"spdy/2", "foo", "bar"});
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long ssl, long context, Socket socket,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
+                assertEquals("spdy/2", new String(negotiated, "UTF-8"));
+                super.afterHandshake(session, ssl, context, socket, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
+            @Override
+            public void afterHandshake(long session, long ssl, long c, Socket sock,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
+                assertEquals("spdy/2", new String(negotiated, "UTF-8"));
+                super.afterHandshake(session, ssl, c, sock, fd, callback);
+            }
+        };
+
+        ServerSocket listener = newServerSocket();
+        Future<TestSSLHandshakeCallbacks> client =
+                handshake(listener, 0, true, cHooks, clientAlpnProtocols, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, serverAlpnProtocols, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void alpnWithProtocolListShouldFail() throws Exception {
+        final byte[] clientAlpnProtocols =
+                SSLUtils.encodeProtocols(new String[] {"http/1.1", "foo", "spdy/2"});
+        final byte[] serverAlpnProtocols =
+                SSLUtils.encodeProtocols(new String[] {"h2", "bar", "baz"});
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long ssl, long context, Socket socket,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
+                assertNull(negotiated);
+                super.afterHandshake(session, ssl, context, socket, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
+            @Override
+            public void afterHandshake(long session, long ssl, long c, Socket sock,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
+                assertNull(negotiated);
+                super.afterHandshake(session, ssl, c, sock, fd, callback);
+            }
+        };
+
+        ServerSocket listener = newServerSocket();
+        Future<TestSSLHandshakeCallbacks> client =
+                handshake(listener, 0, true, cHooks, clientAlpnProtocols, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, serverAlpnProtocols, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void alpnWithServerProtocolSelectorShouldSucceed() throws Exception {
+        final byte[] clientAlpnProtocols =
+                SSLUtils.encodeProtocols(new String[] {"http/1.1", "foo", "spdy/2"});
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long ssl, long context, Socket socket,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
+                assertEquals("spdy/2", new String(negotiated, "UTF-8"));
+                super.afterHandshake(session, ssl, context, socket, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
+            @Override
+            public void afterHandshake(long session, long ssl, long c, Socket sock,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
+                assertEquals("spdy/2", new String(negotiated, "UTF-8"));
+                super.afterHandshake(session, ssl, c, sock, fd, callback);
+            }
+        };
+
+        ApplicationProtocolSelector selector = Mockito.mock(ApplicationProtocolSelector.class);
+        SSLEngine engine = Mockito.mock(SSLEngine.class);
+        ApplicationProtocolSelectorAdapter adapter = new ApplicationProtocolSelectorAdapter(engine, selector);
+        when(selector.selectApplicationProtocol(same(engine), Matchers.anyListOf(String.class)))
+                .thenReturn("spdy/2");
+
+        ServerSocket listener = newServerSocket();
+        Future<TestSSLHandshakeCallbacks> client =
+                handshake(listener, 0, true, cHooks, clientAlpnProtocols, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, adapter);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void alpnWithServerProtocolSelectorShouldFail() throws Exception {
+        final byte[] clientAlpnProtocols =
+                SSLUtils.encodeProtocols(new String[] {"http/1.1", "foo", "spdy/2"});
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long ssl, long context, Socket socket,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
+                assertNull(negotiated);
+                super.afterHandshake(session, ssl, context, socket, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
+            @Override
+            public void afterHandshake(long session, long ssl, long c, Socket sock,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                byte[] negotiated = NativeCrypto.getApplicationProtocol(ssl, null);
+                assertNull(negotiated);
+                super.afterHandshake(session, ssl, c, sock, fd, callback);
+            }
+        };
+
+        ApplicationProtocolSelector selector = Mockito.mock(ApplicationProtocolSelector.class);
+        SSLEngine engine = Mockito.mock(SSLEngine.class);
+        ApplicationProtocolSelectorAdapter adapter = new ApplicationProtocolSelectorAdapter(engine, selector);
+        when(selector.selectApplicationProtocol(same(engine), Matchers.anyListOf(String.class)))
+                .thenReturn("h2");
+
+        ServerSocket listener = newServerSocket();
+        Future<TestSSLHandshakeCallbacks> client =
+                handshake(listener, 0, true, cHooks, clientAlpnProtocols, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, adapter);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_SSL_get_servername_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_get_servername(NULL, null);
+    }
+
+    @Test
+    public void SSL_get_servername_shouldReturnNull() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        assertNull(NativeCrypto.SSL_get_servername(s, null));
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+
+        // additional positive testing by test_SSL_set_tlsext_host_name
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_get0_peer_certificates_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_get0_peer_certificates(NULL, null);
+    }
+
+    @Test
+    public void test_SSL_get0_peer_certificates() throws Exception {
+        final ServerSocket listener = newServerSocket();
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                byte[][] cc = NativeCrypto.SSL_get0_peer_certificates(s, null);
+                assertEqualByteArrays(getEncodedServerCertificates(), cc);
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void test_SSL_cipher_names() throws Exception {
+        final ServerSocket listener = newServerSocket();
+        Hooks cHooks = new Hooks();
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
+        // Both legacy and standard names are accepted.
+        cHooks.enabledCipherSuites = Collections.singletonList("ECDHE-RSA-AES128-GCM-SHA256");
+        sHooks.enabledCipherSuites =
+                Collections.singletonList("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        // The standard name is always reported.
+        assertEquals("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", cHooks.negotiatedCipherSuite);
+        assertEquals("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", sHooks.negotiatedCipherSuite);
+    }
+
+    private final byte[] BYTES = new byte[] {2, -3, 5, 127, 0, -128};
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_read_withNullSslShouldThrow() throws Exception {
+        NativeCrypto.SSL_read(NULL, null, null, null, null, 0, 0, 0);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_read_withNullFdShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        try {
+            NativeCrypto.SSL_read(s, null, null, DUMMY_CB, null, 0, 0, 0);
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_read_withNullCallbacksShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        try {
+            NativeCrypto.SSL_read(s, null, INVALID_FD, null, null, 0, 0, 0);
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_read_withNullBytesShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        try {
+            NativeCrypto.SSL_read(s, null, INVALID_FD, DUMMY_CB, null, 0, 0, 0);
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test(expected = SSLException.class)
+    public void SSL_read_beforeHandshakeShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        try {
+            NativeCrypto.SSL_read(s, null, INVALID_FD, DUMMY_CB, new byte[1], 0, 1, 0);
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test
+    public void test_SSL_read() throws Exception {
+        final ServerSocket listener = newServerSocket();
+
+        // normal case
+        {
+            Hooks cHooks = new Hooks() {
+                @Override
+                public void afterHandshake(long session, long s, long c, Socket sock,
+                        FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                    byte[] in = new byte[256];
+                    assertEquals(BYTES.length,
+                            NativeCrypto.SSL_read(s, null, fd, callback, in, 0, BYTES.length, 0));
+                    for (int i = 0; i < BYTES.length; i++) {
+                        assertEquals(BYTES[i], in[i]);
+                    }
+                    super.afterHandshake(session, s, c, sock, fd, callback);
+                }
+            };
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
+                @Override
+                public void afterHandshake(long session, long s, long c, Socket sock,
+                        FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                    NativeCrypto.SSL_write(s, null, fd, callback, BYTES, 0, BYTES.length, 0);
+                    super.afterHandshake(session, s, c, sock, fd, callback);
+                }
+            };
+            Future<TestSSLHandshakeCallbacks> client =
+                    handshake(listener, 0, true, cHooks, null, null);
+            Future<TestSSLHandshakeCallbacks> server =
+                    handshake(listener, 0, false, sHooks, null, null);
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        }
+
+        // timeout case
+        try {
+            Hooks cHooks = new Hooks() {
+                @Override
+                public void afterHandshake(long session, long s, long c, Socket sock,
+                        FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                    NativeCrypto.SSL_read(s, null, fd, callback, new byte[1], 0, 1, 1);
+                    fail();
+                }
+            };
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
+                @Override
+                public void afterHandshake(long session, long s, long c, Socket sock,
+                        FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                    NativeCrypto.SSL_read(s, null, fd, callback, new byte[1], 0, 1, 0);
+                    super.afterHandshake(session, s, c, sock, fd, callback);
+                }
+            };
+            Future<TestSSLHandshakeCallbacks> client =
+                    handshake(listener, 0, true, cHooks, null, null);
+            @SuppressWarnings("unused")
+            Future<TestSSLHandshakeCallbacks> server =
+                    handshake(listener, 0, false, sHooks, null, null);
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            assertEquals(SocketTimeoutException.class, expected.getCause().getClass());
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_write_withNullSslShouldThrow() throws Exception {
+        NativeCrypto.SSL_write(NULL, null, null, null, null, 0, 0, 0);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_write_withNullFdShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        try {
+            NativeCrypto.SSL_write(s, null, null, DUMMY_CB, null, 0, 1, 0);
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_write_withNullCallbacksShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        try {
+            NativeCrypto.SSL_write(s, null, INVALID_FD, null, null, 0, 1, 0);
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_write_withNullBytesShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        try {
+            NativeCrypto.SSL_write(s, null, INVALID_FD, DUMMY_CB, null, 0, 1, 0);
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test(expected = SSLException.class)
+    public void SSL_write_beforeHandshakeShouldThrow() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        try {
+            NativeCrypto.SSL_write(s, null, INVALID_FD, DUMMY_CB, new byte[1], 0, 1, 0);
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test
+    public void SSL_interrupt_withNullShouldSucceed() {
+        // SSL_interrupt is a rare case that tolerates a null SSL argument
+        NativeCrypto.SSL_interrupt(NULL, null);
+    }
+
+    @Test
+    public void SSL_interrupt_withoutHandshakeShouldSucceed() throws Exception {
+        // also works without handshaking
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        NativeCrypto.SSL_interrupt(s, null);
+        NativeCrypto.SSL_free(s, null);
+        NativeCrypto.SSL_CTX_free(c, null);
+    }
+
+    @Test
+    public void test_SSL_interrupt() throws Exception {
+        final ServerSocket listener = newServerSocket();
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                NativeCrypto.SSL_read(s, null, fd, callback, new byte[1], 0, 1, 0);
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates()) {
+            @Override
+            public void afterHandshake(long session, final long s, long c, Socket sock,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                new Thread() {
+                    @Override
+                    public void run() {
+                        try {
+                            Thread.sleep(1000);
+                            NativeCrypto.SSL_interrupt(s, null);
+                        } catch (Exception e) {
+                            // Expected.
+                        }
+                    }
+                }.start();
+                assertEquals(-1, NativeCrypto.SSL_read(s, null, fd, callback, new byte[1], 0, 1, 0));
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    private static abstract class SSLSessionWrappedTask {
+        public abstract void run(long sslSession) throws Exception;
+    }
+
+    private void wrapWithSSLSession(SSLSessionWrappedTask task) throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c, null);
+        try {
+            task.run(s);
+        } finally {
+            NativeCrypto.SSL_free(s, null);
+            NativeCrypto.SSL_CTX_free(c, null);
+        }
+    }
+
+    @Test
+    public void SSL_shutdown_withNullFdShouldSucceed() throws Exception {
+        // We tolerate a null FileDescriptor
+        wrapWithSSLSession(new SSLSessionWrappedTask() {
+            @Override
+            public void run(long sslSession) throws Exception {
+                NativeCrypto.SSL_shutdown(sslSession, null, null, DUMMY_CB);
+            }
+        });
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_shutdown_withNullCallbacksShouldThrow() throws Exception {
+        wrapWithSSLSession(new SSLSessionWrappedTask() {
+            @Override
+            public void run(long sslSession) throws Exception {
+                NativeCrypto.SSL_shutdown(sslSession, null, INVALID_FD, null);
+            }
+        });
+    }
+
+    @Test
+    public void SSL_shutdown_withNullSslShouldSucceed() throws Exception {
+        // SSL_shutdown is a rare case that tolerates a null SSL argument
+        NativeCrypto.SSL_shutdown(NULL, null, INVALID_FD, DUMMY_CB);
+    }
+
+    @Test(expected = SocketException.class)
+    public void SSL_shutdown_beforeHandshakeShouldThrow() throws Exception {
+        // handshaking not yet performed
+        wrapWithSSLSession(new SSLSessionWrappedTask() {
+            @Override
+            public void run(long sslSession) throws Exception {
+                NativeCrypto.SSL_shutdown(sslSession, null, INVALID_FD, DUMMY_CB);
+            }
+        });
+
+        // positively tested elsewhere because handshake uses use
+        // SSL_shutdown to ensure SSL_SESSIONs are reused.
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_free_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_free(NULL, null);
+    }
+
+    @Test
+    public void test_SSL_free() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        NativeCrypto.SSL_free(NativeCrypto.SSL_new(c, null), null);
+        NativeCrypto.SSL_CTX_free(c, null);
+
+        // additional positive testing elsewhere because handshake
+        // uses use SSL_free to cleanup in afterHandshake.
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_SESSION_session_id_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_SESSION_session_id(NULL);
+    }
+
+    @Test
+    public void test_SSL_SESSION_session_id() throws Exception {
+        final ServerSocket listener = newServerSocket();
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                byte[] id = NativeCrypto.SSL_SESSION_session_id(session);
+                assertNotNull(id);
+                assertEquals(32, id.length);
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_SESSION_get_time_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_SESSION_get_time(NULL);
+    }
+
+    @Test
+    public void test_SSL_SESSION_get_time() throws Exception {
+        final ServerSocket listener = newServerSocket();
+
+        {
+            Hooks cHooks = new Hooks() {
+                @Override
+                public void afterHandshake(long session, long s, long c, Socket sock,
+                        FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                    long time = NativeCrypto.SSL_SESSION_get_time(session);
+                    assertTrue(time != 0);
+                    assertTrue(time < System.currentTimeMillis());
+                    super.afterHandshake(session, s, c, sock, fd, callback);
+                }
+            };
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
+            Future<TestSSLHandshakeCallbacks> client =
+                    handshake(listener, 0, true, cHooks, null, null);
+            Future<TestSSLHandshakeCallbacks> server =
+                    handshake(listener, 0, false, sHooks, null, null);
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_SESSION_get_version_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_SESSION_get_version(NULL);
+    }
+
+    @Test
+    public void test_SSL_SESSION_get_version() throws Exception {
+        final ServerSocket listener = newServerSocket();
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                String v = NativeCrypto.SSL_SESSION_get_version(session);
+                assertTrue(StandardNames.SSL_SOCKET_PROTOCOLS.contains(v));
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_SESSION_cipher_withNullShouldThrow() throws Exception {
+        NativeCrypto.SSL_SESSION_cipher(NULL);
+    }
+
+    @Test
+    public void test_SSL_SESSION_cipher() throws Exception {
+        final ServerSocket listener = newServerSocket();
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                String nativeCipher = NativeCrypto.SSL_SESSION_cipher(session);
+                String javaCipher = NativeCrypto.cipherSuiteFromJava(nativeCipher);
+                assertTrue(NativeCrypto.SUPPORTED_TLS_1_2_CIPHER_SUITES_SET.contains(javaCipher));
+                // SSL_SESSION_cipher should return a standard name rather than an OpenSSL name.
+                assertTrue(nativeCipher.startsWith("TLS_"));
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    /*
+     * Additional positive testing elsewhere because handshake
+     * uses use SSL_SESSION_free to cleanup in afterHandshake.
+     */
+    @Test(expected = NullPointerException.class)
+    public void SSL_SESSION_free_NullArgument() throws Exception {
+        NativeCrypto.SSL_SESSION_free(NULL);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void i2d_SSL_Session_WithNullSessionShouldThrow() throws Exception {
+        NativeCrypto.i2d_SSL_SESSION(NULL);
+    }
+
+    @Test
+    public void test_i2d_SSL_SESSION() throws Exception {
+        final ServerSocket listener = newServerSocket();
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                byte[] b = NativeCrypto.i2d_SSL_SESSION(session);
+                assertNotNull(b);
+                long session2 = NativeCrypto.d2i_SSL_SESSION(b);
+                assertTrue(session2 != NULL);
+
+                // Make sure d2i_SSL_SESSION retores SSL_SESSION_cipher value http://b/7091840
+                assertTrue(NativeCrypto.SSL_SESSION_cipher(session2) != null);
+                assertEquals(NativeCrypto.SSL_SESSION_cipher(session),
+                        NativeCrypto.SSL_SESSION_cipher(session2));
+
+                NativeCrypto.SSL_SESSION_free(session2);
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getEncodedServerCertificates());
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null, null);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, null, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void d2i_SSL_SESSION_NullArgument() throws Exception {
+        NativeCrypto.d2i_SSL_SESSION(null);
+    }
+
+    @Test(expected = IOException.class)
+    public void d2i_SSL_SESSION_EmptyArgument() throws Exception {
+        NativeCrypto.d2i_SSL_SESSION(new byte[0]);
+    }
+
+    @Test(expected = IOException.class)
+    public void d2i_SSL_SESSION_InvalidArgument() throws Exception {
+        NativeCrypto.d2i_SSL_SESSION(new byte[1]);
+    }
+
+    @Test
+    public void test_X509_NAME_hashes() {
+        // ensure these hash functions are stable over time since the
+        // /system/etc/security/cacerts CA filenames have to be
+        // consistent with the output.
+        X500Principal name = new X500Principal("CN=localhost");
+        assertEquals(-1372642656, NativeCrypto.X509_NAME_hash(name)); // SHA1
+        assertEquals(-1626170662, NativeCrypto.X509_NAME_hash_old(name)); // MD5
+    }
+
+    @Test
+    public void test_RAND_bytes_Success() throws Exception {
+        byte[] output = new byte[128];
+        NativeCrypto.RAND_bytes(output);
+
+        boolean isZero = true;
+        for (byte anOutput : output) {
+            isZero &= (anOutput == 0);
+        }
+
+        assertFalse("Random output was zero. This is a very low probability event (1 in 2^128) "
+                        + "and probably indicates an error.",
+                isZero);
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void RAND_bytes_withNullShouldThrow() throws Exception {
+        NativeCrypto.RAND_bytes(null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_EVP_get_digestbyname_NullArgument() throws Exception {
+        NativeCrypto.EVP_get_digestbyname(null);
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void EVP_get_digestbyname_withEmptyShouldThrow() throws Exception {
+        NativeCrypto.EVP_get_digestbyname("");
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void EVP_get_digestbyname_withInvalidDigestShouldThrow() throws Exception {
+        NativeCrypto.EVP_get_digestbyname("foobar");
+    }
+
+    @Test
+    public void test_EVP_get_digestbyname() throws Exception {
+        assertTrue(NativeCrypto.EVP_get_digestbyname("sha256") != NULL);
+    }
+
+    @Test
+    public void test_EVP_DigestSignInit() throws Exception {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+        kpg.initialize(512);
+
+        KeyPair kp = kpg.generateKeyPair();
+        RSAPrivateCrtKey privKey = (RSAPrivateCrtKey) kp.getPrivate();
+
+        NativeRef.EVP_PKEY pkey;
+        pkey = new NativeRef.EVP_PKEY(NativeCrypto.EVP_PKEY_new_RSA(
+                privKey.getModulus().toByteArray(), privKey.getPublicExponent().toByteArray(),
+                privKey.getPrivateExponent().toByteArray(), privKey.getPrimeP().toByteArray(),
+                privKey.getPrimeQ().toByteArray(), privKey.getPrimeExponentP().toByteArray(),
+                privKey.getPrimeExponentQ().toByteArray(),
+                privKey.getCrtCoefficient().toByteArray()));
+        assertNotNull(pkey);
+
+        final NativeRef.EVP_MD_CTX ctx = new NativeRef.EVP_MD_CTX(NativeCrypto.EVP_MD_CTX_create());
+        long evpMd = NativeCrypto.EVP_get_digestbyname("sha256");
+        NativeCrypto.EVP_DigestSignInit(ctx, evpMd, pkey);
+
+        try {
+            NativeCrypto.EVP_DigestSignInit(ctx, 0, pkey);
+            fail();
+        } catch (RuntimeException expected) {
+            // Expected.
+        }
+
+        try {
+            NativeCrypto.EVP_DigestSignInit(ctx, evpMd, null);
+            fail();
+        } catch (RuntimeException expected) {
+            // Expected.
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void get_RSA_private_params_NullArgument() throws Exception {
+        NativeCrypto.get_RSA_private_params(null);
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void test_get_RSA_private_params() throws Exception {
+        // Test getting params for the wrong kind of key.
+        final long groupCtx = NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1");
+        assertFalse(groupCtx == NULL);
+        NativeRef.EC_GROUP group = new NativeRef.EC_GROUP(groupCtx);
+        NativeRef.EVP_PKEY ctx = new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(group));
+        NativeCrypto.get_RSA_private_params(ctx);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void get_RSA_public_params_NullArgument() throws Exception {
+        NativeCrypto.get_RSA_public_params(null);
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void test_get_RSA_public_params() throws Exception {
+        // Test getting params for the wrong kind of key.
+        final long groupCtx = NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1");
+        assertFalse(groupCtx == NULL);
+        NativeRef.EC_GROUP group = new NativeRef.EC_GROUP(groupCtx);
+        NativeRef.EVP_PKEY ctx = new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(group));
+        NativeCrypto.get_RSA_public_params(ctx);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void RSA_size_NullArgumentFailure() throws Exception {
+        NativeCrypto.RSA_size(null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void RSA_private_encrypt_NullArgumentFailure() throws Exception {
+        NativeCrypto.RSA_private_encrypt(0, new byte[0], new byte[0], null, 0);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void RSA_private_decrypt_NullArgumentFailure() throws Exception {
+        NativeCrypto.RSA_private_decrypt(0, new byte[0], new byte[0], null, 0);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_RSA_public_encrypt_NullArgumentFailure() throws Exception {
+        NativeCrypto.RSA_public_encrypt(0, new byte[0], new byte[0], null, 0);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_RSA_public_decrypt_NullArgumentFailure() throws Exception {
+        NativeCrypto.RSA_public_decrypt(0, new byte[0], new byte[0], null, 0);
+    }
+
+    /*
+     * Test vector generation:
+     * openssl rand -hex 16
+     */
+    private static final byte[] AES_128_KEY = new byte[] {
+            (byte) 0x3d, (byte) 0x4f, (byte) 0x89, (byte) 0x70, (byte) 0xb1, (byte) 0xf2,
+            (byte) 0x75, (byte) 0x37, (byte) 0xf4, (byte) 0x0a, (byte) 0x39, (byte) 0x29,
+            (byte) 0x8a, (byte) 0x41, (byte) 0x55, (byte) 0x5f,
+    };
+
+    @Test
+    public void testEC_GROUP() throws Exception {
+        /* Test using NIST's P-256 curve */
+        check_EC_GROUP("prime256v1",
+                "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
+                "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",
+                "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
+                "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
+                "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
+                "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 1L);
+    }
+
+    private void check_EC_GROUP(String name, String pStr, String aStr, String bStr, String xStr,
+            String yStr, String nStr, long hLong) throws Exception {
+        long groupRef = NativeCrypto.EC_GROUP_new_by_curve_name(name);
+        assertFalse(groupRef == NULL);
+        NativeRef.EC_GROUP group = new NativeRef.EC_GROUP(groupRef);
+
+        // prime
+        BigInteger p = new BigInteger(pStr, 16);
+        // first coefficient
+        BigInteger a = new BigInteger(aStr, 16);
+        // second coefficient
+        BigInteger b = new BigInteger(bStr, 16);
+        // x affine coordinate of generator
+        BigInteger x = new BigInteger(xStr, 16);
+        // y affine coordinate of generator
+        BigInteger y = new BigInteger(yStr, 16);
+        // order of the generator
+        BigInteger n = new BigInteger(nStr, 16);
+        // cofactor of generator
+        BigInteger h = BigInteger.valueOf(hLong);
+
+        byte[][] pab = NativeCrypto.EC_GROUP_get_curve(group);
+        assertEquals(3, pab.length);
+
+        BigInteger p2 = new BigInteger(pab[0]);
+        assertEquals(p, p2);
+
+        BigInteger a2 = new BigInteger(pab[1]);
+        assertEquals(a, a2);
+
+        BigInteger b2 = new BigInteger(pab[2]);
+        assertEquals(b, b2);
+
+        NativeRef.EC_POINT point =
+                new NativeRef.EC_POINT(NativeCrypto.EC_GROUP_get_generator(group));
+
+        byte[][] xy = NativeCrypto.EC_POINT_get_affine_coordinates(group, point);
+        assertEquals(2, xy.length);
+
+        BigInteger x2 = new BigInteger(xy[0]);
+        assertEquals(x, x2);
+
+        BigInteger y2 = new BigInteger(xy[1]);
+        assertEquals(y, y2);
+
+        BigInteger n2 = new BigInteger(NativeCrypto.EC_GROUP_get_order(group));
+        assertEquals(n, n2);
+
+        BigInteger h2 = new BigInteger(NativeCrypto.EC_GROUP_get_cofactor(group));
+        assertEquals(h, h2);
+
+        NativeRef.EVP_PKEY key1 = new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(group));
+        NativeRef.EC_GROUP groupTmp = new NativeRef.EC_GROUP(NativeCrypto.EC_KEY_get1_group(key1));
+        assertEquals(NativeCrypto.EC_GROUP_get_curve_name(group),
+                NativeCrypto.EC_GROUP_get_curve_name(groupTmp));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_EC_KEY_get_private_key_NullArgumentFailure() throws Exception {
+        NativeCrypto.EC_KEY_get_private_key(null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_EC_KEY_get_public_key_NullArgumentFailure() throws Exception {
+        NativeCrypto.EC_KEY_get_public_key(null);
+    }
+
+    @Test
+    public void test_ECKeyPairGenerator_CurvesAreValid() throws Exception {
+        OpenSSLECKeyPairGenerator.assertCurvesAreValid();
+    }
+
+    @Test
+    public void test_ECDH_compute_key_null_key_Failure() throws Exception {
+        final long groupCtx = NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1");
+        assertFalse(groupCtx == NULL);
+        NativeRef.EC_GROUP groupRef = new NativeRef.EC_GROUP(groupCtx);
+        NativeRef.EVP_PKEY pkey1Ref =
+                new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(groupRef));
+        NativeRef.EVP_PKEY pkey2Ref =
+                new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(groupRef));
+
+        byte[] out = new byte[128];
+        int outOffset = 0;
+        // Assert that the method under test works fine with the two
+        // non-null keys
+        NativeCrypto.ECDH_compute_key(out, outOffset, pkey1Ref, pkey2Ref);
+
+        // Assert that it fails when only the first key is null
+        try {
+            NativeCrypto.ECDH_compute_key(out, outOffset, null, pkey2Ref);
+            fail();
+        } catch (NullPointerException expected) {
+            // Expected.
+        }
+
+        // Assert that it fails when only the second key is null
+        try {
+            NativeCrypto.ECDH_compute_key(out, outOffset, pkey1Ref, null);
+            fail();
+        } catch (NullPointerException expected) {
+            // Expected.
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_CipherInit_ex_withNullCtxShouldThrow() throws Exception {
+        final long evpCipher = NativeCrypto.EVP_get_cipherbyname("aes-128-ecb");
+        NativeCrypto.EVP_CipherInit_ex(null, evpCipher, null, null, true);
+    }
+
+    @Test
+    public void test_EVP_CipherInit_ex_Null_Failure() throws Exception {
+        final NativeRef.EVP_CIPHER_CTX ctx =
+                new NativeRef.EVP_CIPHER_CTX(NativeCrypto.EVP_CIPHER_CTX_new());
+        final long evpCipher = NativeCrypto.EVP_get_cipherbyname("aes-128-ecb");
+
+        /* Initialize encrypting. */
+        NativeCrypto.EVP_CipherInit_ex(ctx, evpCipher, null, null, true);
+        NativeCrypto.EVP_CipherInit_ex(ctx, NULL, null, null, true);
+
+        /* Initialize decrypting. */
+        NativeCrypto.EVP_CipherInit_ex(ctx, evpCipher, null, null, false);
+        NativeCrypto.EVP_CipherInit_ex(ctx, NULL, null, null, false);
+    }
+
+    @Test
+    public void test_EVP_CipherInit_ex_Success() throws Exception {
+        final NativeRef.EVP_CIPHER_CTX ctx =
+                new NativeRef.EVP_CIPHER_CTX(NativeCrypto.EVP_CIPHER_CTX_new());
+        final long evpCipher = NativeCrypto.EVP_get_cipherbyname("aes-128-ecb");
+        NativeCrypto.EVP_CipherInit_ex(ctx, evpCipher, AES_128_KEY, null, true);
+    }
+
+    @Test
+    public void test_EVP_CIPHER_iv_length() throws Exception {
+        long aes128ecb = NativeCrypto.EVP_get_cipherbyname("aes-128-ecb");
+        assertEquals(0, NativeCrypto.EVP_CIPHER_iv_length(aes128ecb));
+
+        long aes128cbc = NativeCrypto.EVP_get_cipherbyname("aes-128-cbc");
+        assertEquals(16, NativeCrypto.EVP_CIPHER_iv_length(aes128cbc));
+    }
+
+    @Test
+    public void test_OpenSSLKey_toJava() throws Exception {
+        OpenSSLKey key1;
+
+        BigInteger e = BigInteger.valueOf(65537);
+        key1 = new OpenSSLKey(NativeCrypto.RSA_generate_key_ex(1024, e.toByteArray()));
+        assertTrue(key1.getPublicKey() instanceof RSAPublicKey);
+
+        final long groupCtx = NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1");
+        assertFalse(groupCtx == NULL);
+        NativeRef.EC_GROUP group1 = new NativeRef.EC_GROUP(groupCtx);
+        key1 = new OpenSSLKey(NativeCrypto.EC_KEY_generate_key(group1));
+        assertTrue(key1.getPublicKey() instanceof ECPublicKey);
+    }
+
+    @Test
+    public void test_create_BIO_InputStream() throws Exception {
+        byte[] actual = "Test".getBytes("UTF-8");
+        ByteArrayInputStream is = new ByteArrayInputStream(actual);
+
+        @SuppressWarnings("resource")
+        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
+        try {
+            byte[] buffer = new byte[1024];
+            int numRead = NativeCrypto.BIO_read(bis.getBioContext(), buffer);
+            assertEquals(actual.length, numRead);
+            assertEquals(Arrays.toString(actual),
+                    Arrays.toString(Arrays.copyOfRange(buffer, 0, numRead)));
+        } finally {
+            bis.release();
+        }
+    }
+
+    @Test
+    public void test_create_BIO_OutputStream() throws Exception {
+        byte[] actual = "Test".getBytes("UTF-8");
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+        long ctx = NativeCrypto.create_BIO_OutputStream(os);
+        try {
+            NativeCrypto.BIO_write(ctx, actual, 0, actual.length);
+            assertEquals(actual.length, os.size());
+            assertEquals(Arrays.toString(actual), Arrays.toString(os.toByteArray()));
+        } finally {
+            NativeCrypto.BIO_free_all(ctx);
+        }
+    }
+
+    @Test
+    public void test_get_ocsp_single_extension() throws Exception {
+        final String OCSP_SCT_LIST_OID = "1.3.6.1.4.1.11129.2.4.5";
+
+        byte[] ocspResponse = readTestFile("ocsp-response.der");
+        byte[] expected = readTestFile("ocsp-response-sct-extension.der");
+        OpenSSLX509Certificate certificate =
+                OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("cert-ct-poisoned.pem"));
+        OpenSSLX509Certificate issuer =
+                OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("ca-cert.pem"));
+
+        byte[] extension = NativeCrypto.get_ocsp_single_extension(
+                ocspResponse, OCSP_SCT_LIST_OID, certificate.getContext(), certificate, issuer.getContext(), issuer);
+
+        assertEqualByteArrays(expected, extension);
+    }
+
+    private static long getRawPkeyCtxForEncrypt() throws Exception {
+        return NativeCrypto.EVP_PKEY_encrypt_init(getRsaPkey(generateRsaKey()));
+    }
+
+    private static NativeRef.EVP_PKEY_CTX getPkeyCtxForEncrypt() throws Exception {
+        return new NativeRef.EVP_PKEY_CTX(getRawPkeyCtxForEncrypt());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_encrypt_NullKeyArgument() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(null, new byte[128], 0, new byte[128], 0, 128);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_encrypt_NullOutputArgument() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(getPkeyCtxForEncrypt(), null, 0, new byte[128], 0, 128);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_encrypt_NullInputArgument() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(getPkeyCtxForEncrypt(), new byte[128], 0, null, 0, 128);
+    }
+
+    @Test(expected = ArrayIndexOutOfBoundsException.class)
+    public void EVP_PKEY_encrypt_OutputIndexOOBUnder() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(
+                getPkeyCtxForEncrypt(), new byte[128], -1, new byte[128], 0, 128);
+    }
+
+    @Test(expected = ArrayIndexOutOfBoundsException.class)
+    public void EVP_PKEY_encrypt_OutputIndexOOBOver() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(
+                getPkeyCtxForEncrypt(), new byte[128], 129, new byte[128], 0, 128);
+    }
+
+    @Test(expected = ArrayIndexOutOfBoundsException.class)
+    public void EVP_PKEY_encrypt_InputIndexOOBUnder() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(
+                getPkeyCtxForEncrypt(), new byte[128], 0, new byte[128], -1, 128);
+    }
+
+    @Test(expected = ArrayIndexOutOfBoundsException.class)
+    public void EVP_PKEY_encrypt_InputIndexOOBOver() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(
+                getPkeyCtxForEncrypt(), new byte[128], 0, new byte[128], 128, 128);
+    }
+
+    @Test(expected = ArrayIndexOutOfBoundsException.class)
+    public void EVP_PKEY_encrypt_InputLengthNegative() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(
+                getPkeyCtxForEncrypt(), new byte[128], 0, new byte[128], 0, -1);
+    }
+
+    @Test(expected = ArrayIndexOutOfBoundsException.class)
+    public void EVP_PKEY_encrypt_InputIndexLengthOOB() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(
+                getPkeyCtxForEncrypt(), new byte[128], 0, new byte[128], 100, 29);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_CTX_set_rsa_mgf1_md_NullPkeyCtx() throws Exception {
+        NativeCrypto.EVP_PKEY_CTX_set_rsa_mgf1_md(NULL, EvpMdRef.SHA256.EVP_MD);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_CTX_set_rsa_mgf1_md_NullMdCtx() throws Exception {
+        long pkeyCtx = getRawPkeyCtxForEncrypt();
+        NativeRef.EVP_PKEY_CTX holder = new NativeRef.EVP_PKEY_CTX(pkeyCtx);
+        NativeCrypto.EVP_PKEY_CTX_set_rsa_mgf1_md(pkeyCtx, NULL);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_CTX_set_rsa_oaep_md_NullPkeyCtx() throws Exception {
+        NativeCrypto.EVP_PKEY_CTX_set_rsa_oaep_md(NULL, EvpMdRef.SHA256.EVP_MD);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_CTX_set_rsa_oaep_md_NullMdCtx() throws Exception {
+        long pkeyCtx = getRawPkeyCtxForEncrypt();
+        new NativeRef.EVP_PKEY_CTX(pkeyCtx);
+        NativeCrypto.EVP_PKEY_CTX_set_rsa_oaep_md(pkeyCtx, NULL);
+    }
+
+    @Test(expected = ParsingException.class)
+    public void d2i_X509_InvalidFailure() throws Exception {
+        NativeCrypto.d2i_X509(new byte[1]);
+    }
+
+    private static void assertContains(String actualValue, String expectedSubstring) {
+        if (actualValue == null) {
+            return;
+        }
+        if (actualValue.contains(expectedSubstring)) {
+            return;
+        }
+        fail("\"" + actualValue + "\" does not contain \"" + expectedSubstring + "\"");
+    }
+
+    private static ServerSocket newServerSocket() throws IOException {
+        return new ServerSocket(0, 50, TestUtils.getLoopbackAddress());
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/NativeRefTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/NativeRefTest.java
new file mode 100644
index 0000000..18a81aa
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/NativeRefTest.java
@@ -0,0 +1,37 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.org.conscrypt;
+
+import junit.framework.TestCase;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class NativeRefTest extends TestCase {
+    public void test_zeroContextThrowsNullPointException() {
+        try {
+            new NativeRef(0) {
+                @Override
+                void doFree(long context) {
+                }
+            };
+            fail("Should throw NullPointerException when arguments are NULL");
+        } catch (NullPointerException expected) {
+        }
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/NativeSslSessionTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/NativeSslSessionTest.java
new file mode 100644
index 0000000..c80461f
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/NativeSslSessionTest.java
@@ -0,0 +1,627 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License", "www.google.com", 443);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class NativeSslSessionTest {
+    /*
+     * Taken from external/boringssl/src/ssl/ssl_test.cc: kOpenSSLSession is a
+     * serialized SSL_SESSION.
+     */
+    private static final byte[] kOpenSSLSession = new byte[] {(byte) 0x30, (byte) 0x82, (byte) 0x05,
+            (byte) 0xAA, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x02,
+            (byte) 0x03, (byte) 0x03, (byte) 0x04, (byte) 0x02, (byte) 0xC0, (byte) 0x2F,
+            (byte) 0x04, (byte) 0x20, (byte) 0x06, (byte) 0xE5, (byte) 0x0D, (byte) 0x67,
+            (byte) 0x76, (byte) 0xAE, (byte) 0x18, (byte) 0x7E, (byte) 0x66, (byte) 0xDE,
+            (byte) 0xA3, (byte) 0x5C, (byte) 0xF0, (byte) 0x2E, (byte) 0x43, (byte) 0x51,
+            (byte) 0x2A, (byte) 0x60, (byte) 0x97, (byte) 0x19, (byte) 0xD3, (byte) 0x60,
+            (byte) 0x5A, (byte) 0xF1, (byte) 0x93, (byte) 0xDD, (byte) 0xCB, (byte) 0x24,
+            (byte) 0x57, (byte) 0x4C, (byte) 0x90, (byte) 0x90, (byte) 0x04, (byte) 0x30,
+            (byte) 0x26, (byte) 0x5A, (byte) 0xE5, (byte) 0xCE, (byte) 0x40, (byte) 0x16,
+            (byte) 0x04, (byte) 0xE5, (byte) 0xA2, (byte) 0x2E, (byte) 0x3F, (byte) 0xE3,
+            (byte) 0x27, (byte) 0xBE, (byte) 0x83, (byte) 0xEE, (byte) 0x5F, (byte) 0x94,
+            (byte) 0x5E, (byte) 0x88, (byte) 0xB3, (byte) 0x3F, (byte) 0x62, (byte) 0x88,
+            (byte) 0xD8, (byte) 0x2E, (byte) 0xC8, (byte) 0xD8, (byte) 0x57, (byte) 0x1C,
+            (byte) 0xA8, (byte) 0xC9, (byte) 0x88, (byte) 0x7C, (byte) 0x59, (byte) 0xA6,
+            (byte) 0x91, (byte) 0x4C, (byte) 0xB7, (byte) 0xDA, (byte) 0x72, (byte) 0x09,
+            (byte) 0xD2, (byte) 0x66, (byte) 0x47, (byte) 0x21, (byte) 0x6A, (byte) 0x09,
+            (byte) 0xA1, (byte) 0x06, (byte) 0x02, (byte) 0x04, (byte) 0x54, (byte) 0x43,
+            (byte) 0x3B, (byte) 0x8E, (byte) 0xA2, (byte) 0x04, (byte) 0x02, (byte) 0x02,
+            (byte) 0x01, (byte) 0x2C, (byte) 0xA3, (byte) 0x82, (byte) 0x04, (byte) 0x7A,
+            (byte) 0x30, (byte) 0x82, (byte) 0x04, (byte) 0x76, (byte) 0x30, (byte) 0x82,
+            (byte) 0x03, (byte) 0x5E, (byte) 0xA0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+            (byte) 0x02, (byte) 0x02, (byte) 0x08, (byte) 0x2B, (byte) 0xD7, (byte) 0x54,
+            (byte) 0xBE, (byte) 0xC3, (byte) 0xD6, (byte) 0x4A, (byte) 0x55, (byte) 0x30,
+            (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A, (byte) 0x86, (byte) 0x48,
+            (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01, (byte) 0x01, (byte) 0x05,
+            (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x49, (byte) 0x31, (byte) 0x0B,
+            (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+            (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31,
+            (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x04, (byte) 0x0A, (byte) 0x13, (byte) 0x0A, (byte) 0x47, (byte) 0x6F,
+            (byte) 0x6F, (byte) 0x67, (byte) 0x6C, (byte) 0x65, (byte) 0x20, (byte) 0x49,
+            (byte) 0x6E, (byte) 0x63, (byte) 0x31, (byte) 0x25, (byte) 0x30, (byte) 0x23,
+            (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x13,
+            (byte) 0x1C, (byte) 0x47, (byte) 0x6F, (byte) 0x6F, (byte) 0x67, (byte) 0x6C,
+            (byte) 0x65, (byte) 0x20, (byte) 0x49, (byte) 0x6E, (byte) 0x74, (byte) 0x65,
+            (byte) 0x72, (byte) 0x6E, (byte) 0x65, (byte) 0x74, (byte) 0x20, (byte) 0x41,
+            (byte) 0x75, (byte) 0x74, (byte) 0x68, (byte) 0x6F, (byte) 0x72, (byte) 0x69,
+            (byte) 0x74, (byte) 0x79, (byte) 0x20, (byte) 0x47, (byte) 0x32, (byte) 0x30,
+            (byte) 0x1E, (byte) 0x17, (byte) 0x0D, (byte) 0x31, (byte) 0x34, (byte) 0x31,
+            (byte) 0x30, (byte) 0x30, (byte) 0x38, (byte) 0x31, (byte) 0x32, (byte) 0x30,
+            (byte) 0x37, (byte) 0x35, (byte) 0x37, (byte) 0x5A, (byte) 0x17, (byte) 0x0D,
+            (byte) 0x31, (byte) 0x35, (byte) 0x30, (byte) 0x31, (byte) 0x30, (byte) 0x36,
+            (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x30,
+            (byte) 0x5A, (byte) 0x30, (byte) 0x68, (byte) 0x31, (byte) 0x0B, (byte) 0x30,
+            (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
+            (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x13,
+            (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+            (byte) 0x08, (byte) 0x0C, (byte) 0x0A, (byte) 0x43, (byte) 0x61, (byte) 0x6C,
+            (byte) 0x69, (byte) 0x66, (byte) 0x6F, (byte) 0x72, (byte) 0x6E, (byte) 0x69,
+            (byte) 0x61, (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06,
+            (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x0C, (byte) 0x0D,
+            (byte) 0x4D, (byte) 0x6F, (byte) 0x75, (byte) 0x6E, (byte) 0x74, (byte) 0x61,
+            (byte) 0x69, (byte) 0x6E, (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65,
+            (byte) 0x77, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06,
+            (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0A, (byte) 0x0C, (byte) 0x0A,
+            (byte) 0x47, (byte) 0x6F, (byte) 0x6F, (byte) 0x67, (byte) 0x6C, (byte) 0x65,
+            (byte) 0x20, (byte) 0x49, (byte) 0x6E, (byte) 0x63, (byte) 0x31, (byte) 0x17,
+            (byte) 0x30, (byte) 0x15, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+            (byte) 0x03, (byte) 0x0C, (byte) 0x0E, (byte) 0x77, (byte) 0x77, (byte) 0x77,
+            (byte) 0x2E, (byte) 0x67, (byte) 0x6F, (byte) 0x6F, (byte) 0x67, (byte) 0x6C,
+            (byte) 0x65, (byte) 0x2E, (byte) 0x63, (byte) 0x6F, (byte) 0x6D, (byte) 0x30,
+            (byte) 0x82, (byte) 0x01, (byte) 0x22, (byte) 0x30, (byte) 0x0D, (byte) 0x06,
+            (byte) 0x09, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7,
+            (byte) 0x0D, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00,
+            (byte) 0x03, (byte) 0x82, (byte) 0x01, (byte) 0x0F, (byte) 0x00, (byte) 0x30,
+            (byte) 0x82, (byte) 0x01, (byte) 0x0A, (byte) 0x02, (byte) 0x82, (byte) 0x01,
+            (byte) 0x01, (byte) 0x00, (byte) 0x9C, (byte) 0x29, (byte) 0xE2, (byte) 0xEB,
+            (byte) 0xA6, (byte) 0x50, (byte) 0x02, (byte) 0xF8, (byte) 0xBA, (byte) 0x1F,
+            (byte) 0xCB, (byte) 0xCB, (byte) 0x7F, (byte) 0xC0, (byte) 0x3C, (byte) 0x2D,
+            (byte) 0x07, (byte) 0xA7, (byte) 0xAE, (byte) 0xEF, (byte) 0x60, (byte) 0x95,
+            (byte) 0xA7, (byte) 0x47, (byte) 0x09, (byte) 0xE1, (byte) 0x5D, (byte) 0xE5,
+            (byte) 0x92, (byte) 0x73, (byte) 0x7A, (byte) 0x86, (byte) 0xE1, (byte) 0xFD,
+            (byte) 0x72, (byte) 0xDE, (byte) 0x85, (byte) 0x16, (byte) 0x4E, (byte) 0xF4,
+            (byte) 0xA1, (byte) 0x12, (byte) 0x21, (byte) 0xFD, (byte) 0x50, (byte) 0x4D,
+            (byte) 0x04, (byte) 0x1C, (byte) 0xFD, (byte) 0xD3, (byte) 0x48, (byte) 0xD8,
+            (byte) 0xCB, (byte) 0xEE, (byte) 0xF5, (byte) 0xD7, (byte) 0x52, (byte) 0x66,
+            (byte) 0xD5, (byte) 0xBF, (byte) 0x22, (byte) 0xA8, (byte) 0xE4, (byte) 0xD0,
+            (byte) 0xF5, (byte) 0xA4, (byte) 0xF9, (byte) 0x0B, (byte) 0xB4, (byte) 0x84,
+            (byte) 0x84, (byte) 0xD7, (byte) 0x10, (byte) 0x14, (byte) 0x9B, (byte) 0xEA,
+            (byte) 0xCC, (byte) 0x7D, (byte) 0xDE, (byte) 0x30, (byte) 0xF9, (byte) 0x1B,
+            (byte) 0xE9, (byte) 0x94, (byte) 0x96, (byte) 0x1A, (byte) 0x6D, (byte) 0x72,
+            (byte) 0x18, (byte) 0x5E, (byte) 0xCC, (byte) 0x09, (byte) 0x04, (byte) 0xC6,
+            (byte) 0x41, (byte) 0x71, (byte) 0x76, (byte) 0xD1, (byte) 0x29, (byte) 0x3F,
+            (byte) 0x3B, (byte) 0x5E, (byte) 0x85, (byte) 0x4A, (byte) 0x30, (byte) 0x32,
+            (byte) 0x9D, (byte) 0x4F, (byte) 0xDB, (byte) 0xDE, (byte) 0x82, (byte) 0x66,
+            (byte) 0x39, (byte) 0xCB, (byte) 0x5C, (byte) 0xC9, (byte) 0xC5, (byte) 0x98,
+            (byte) 0x91, (byte) 0x8D, (byte) 0x32, (byte) 0xB5, (byte) 0x2F, (byte) 0xE4,
+            (byte) 0xDC, (byte) 0xB0, (byte) 0x6E, (byte) 0x21, (byte) 0xDE, (byte) 0x39,
+            (byte) 0x3C, (byte) 0x96, (byte) 0xA8, (byte) 0x32, (byte) 0xA8, (byte) 0xC1,
+            (byte) 0xD1, (byte) 0x6C, (byte) 0xA9, (byte) 0xAA, (byte) 0xF3, (byte) 0x5E,
+            (byte) 0x24, (byte) 0x70, (byte) 0xB7, (byte) 0xAB, (byte) 0x92, (byte) 0x63,
+            (byte) 0x08, (byte) 0x1E, (byte) 0x11, (byte) 0x3F, (byte) 0xB3, (byte) 0x5F,
+            (byte) 0xC7, (byte) 0x98, (byte) 0xE3, (byte) 0x1D, (byte) 0x2A, (byte) 0xC2,
+            (byte) 0x32, (byte) 0x1C, (byte) 0x3C, (byte) 0x95, (byte) 0x43, (byte) 0x16,
+            (byte) 0xE0, (byte) 0x46, (byte) 0x83, (byte) 0xC6, (byte) 0x36, (byte) 0x91,
+            (byte) 0xF4, (byte) 0xA0, (byte) 0xE1, (byte) 0x3C, (byte) 0xB8, (byte) 0x23,
+            (byte) 0xB2, (byte) 0x4F, (byte) 0x8B, (byte) 0x0C, (byte) 0x8C, (byte) 0x92,
+            (byte) 0x45, (byte) 0x24, (byte) 0x43, (byte) 0x68, (byte) 0x24, (byte) 0x06,
+            (byte) 0x84, (byte) 0x43, (byte) 0x96, (byte) 0x2C, (byte) 0x96, (byte) 0x55,
+            (byte) 0x2F, (byte) 0x32, (byte) 0xE8, (byte) 0xE0, (byte) 0xDE, (byte) 0xBF,
+            (byte) 0x52, (byte) 0x57, (byte) 0x2D, (byte) 0x08, (byte) 0x71, (byte) 0x25,
+            (byte) 0x96, (byte) 0x90, (byte) 0x54, (byte) 0x4A, (byte) 0xF1, (byte) 0x0E,
+            (byte) 0xC8, (byte) 0x58, (byte) 0x1A, (byte) 0xE7, (byte) 0x6A, (byte) 0xAB,
+            (byte) 0xA0, (byte) 0x68, (byte) 0xE0, (byte) 0xAD, (byte) 0xFD, (byte) 0xD6,
+            (byte) 0x39, (byte) 0x0F, (byte) 0x76, (byte) 0xE4, (byte) 0xC1, (byte) 0x70,
+            (byte) 0xCD, (byte) 0xDE, (byte) 0x80, (byte) 0x2B, (byte) 0xE2, (byte) 0x1C,
+            (byte) 0x87, (byte) 0x48, (byte) 0x03, (byte) 0x46, (byte) 0x0F, (byte) 0x2C,
+            (byte) 0x41, (byte) 0xF7, (byte) 0x4B, (byte) 0x1F, (byte) 0x93, (byte) 0xAE,
+            (byte) 0x3F, (byte) 0x57, (byte) 0x1F, (byte) 0x2D, (byte) 0xF5, (byte) 0x35,
+            (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xA3,
+            (byte) 0x82, (byte) 0x01, (byte) 0x41, (byte) 0x30, (byte) 0x82, (byte) 0x01,
+            (byte) 0x3D, (byte) 0x30, (byte) 0x1D, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x1D, (byte) 0x25, (byte) 0x04, (byte) 0x16, (byte) 0x30, (byte) 0x14,
+            (byte) 0x06, (byte) 0x08, (byte) 0x2B, (byte) 0x06, (byte) 0x01, (byte) 0x05,
+            (byte) 0x05, (byte) 0x07, (byte) 0x03, (byte) 0x01, (byte) 0x06, (byte) 0x08,
+            (byte) 0x2B, (byte) 0x06, (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x07,
+            (byte) 0x03, (byte) 0x02, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03,
+            (byte) 0x55, (byte) 0x1D, (byte) 0x11, (byte) 0x04, (byte) 0x12, (byte) 0x30,
+            (byte) 0x10, (byte) 0x82, (byte) 0x0E, (byte) 0x77, (byte) 0x77, (byte) 0x77,
+            (byte) 0x2E, (byte) 0x67, (byte) 0x6F, (byte) 0x6F, (byte) 0x67, (byte) 0x6C,
+            (byte) 0x65, (byte) 0x2E, (byte) 0x63, (byte) 0x6F, (byte) 0x6D, (byte) 0x30,
+            (byte) 0x68, (byte) 0x06, (byte) 0x08, (byte) 0x2B, (byte) 0x06, (byte) 0x01,
+            (byte) 0x05, (byte) 0x05, (byte) 0x07, (byte) 0x01, (byte) 0x01, (byte) 0x04,
+            (byte) 0x5C, (byte) 0x30, (byte) 0x5A, (byte) 0x30, (byte) 0x2B, (byte) 0x06,
+            (byte) 0x08, (byte) 0x2B, (byte) 0x06, (byte) 0x01, (byte) 0x05, (byte) 0x05,
+            (byte) 0x07, (byte) 0x30, (byte) 0x02, (byte) 0x86, (byte) 0x1F, (byte) 0x68,
+            (byte) 0x74, (byte) 0x74, (byte) 0x70, (byte) 0x3A, (byte) 0x2F, (byte) 0x2F,
+            (byte) 0x70, (byte) 0x6B, (byte) 0x69, (byte) 0x2E, (byte) 0x67, (byte) 0x6F,
+            (byte) 0x6F, (byte) 0x67, (byte) 0x6C, (byte) 0x65, (byte) 0x2E, (byte) 0x63,
+            (byte) 0x6F, (byte) 0x6D, (byte) 0x2F, (byte) 0x47, (byte) 0x49, (byte) 0x41,
+            (byte) 0x47, (byte) 0x32, (byte) 0x2E, (byte) 0x63, (byte) 0x72, (byte) 0x74,
+            (byte) 0x30, (byte) 0x2B, (byte) 0x06, (byte) 0x08, (byte) 0x2B, (byte) 0x06,
+            (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x07, (byte) 0x30, (byte) 0x01,
+            (byte) 0x86, (byte) 0x1F, (byte) 0x68, (byte) 0x74, (byte) 0x74, (byte) 0x70,
+            (byte) 0x3A, (byte) 0x2F, (byte) 0x2F, (byte) 0x63, (byte) 0x6C, (byte) 0x69,
+            (byte) 0x65, (byte) 0x6E, (byte) 0x74, (byte) 0x73, (byte) 0x31, (byte) 0x2E,
+            (byte) 0x67, (byte) 0x6F, (byte) 0x6F, (byte) 0x67, (byte) 0x6C, (byte) 0x65,
+            (byte) 0x2E, (byte) 0x63, (byte) 0x6F, (byte) 0x6D, (byte) 0x2F, (byte) 0x6F,
+            (byte) 0x63, (byte) 0x73, (byte) 0x70, (byte) 0x30, (byte) 0x1D, (byte) 0x06,
+            (byte) 0x03, (byte) 0x55, (byte) 0x1D, (byte) 0x0E, (byte) 0x04, (byte) 0x16,
+            (byte) 0x04, (byte) 0x14, (byte) 0x3B, (byte) 0x6B, (byte) 0xE0, (byte) 0x9C,
+            (byte) 0xC6, (byte) 0xC6, (byte) 0x41, (byte) 0xC8, (byte) 0xEA, (byte) 0x5C,
+            (byte) 0xFB, (byte) 0x1A, (byte) 0x58, (byte) 0x15, (byte) 0xC2, (byte) 0x1B,
+            (byte) 0x9D, (byte) 0x43, (byte) 0x19, (byte) 0x85, (byte) 0x30, (byte) 0x0C,
+            (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1D, (byte) 0x13, (byte) 0x01,
+            (byte) 0x01, (byte) 0xFF, (byte) 0x04, (byte) 0x02, (byte) 0x30, (byte) 0x00,
+            (byte) 0x30, (byte) 0x1F, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1D,
+            (byte) 0x23, (byte) 0x04, (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x80,
+            (byte) 0x14, (byte) 0x4A, (byte) 0xDD, (byte) 0x06, (byte) 0x16, (byte) 0x1B,
+            (byte) 0xBC, (byte) 0xF6, (byte) 0x68, (byte) 0xB5, (byte) 0x76, (byte) 0xF5,
+            (byte) 0x81, (byte) 0xB6, (byte) 0xBB, (byte) 0x62, (byte) 0x1A, (byte) 0xBA,
+            (byte) 0x5A, (byte) 0x81, (byte) 0x2F, (byte) 0x30, (byte) 0x17, (byte) 0x06,
+            (byte) 0x03, (byte) 0x55, (byte) 0x1D, (byte) 0x20, (byte) 0x04, (byte) 0x10,
+            (byte) 0x30, (byte) 0x0E, (byte) 0x30, (byte) 0x0C, (byte) 0x06, (byte) 0x0A,
+            (byte) 0x2B, (byte) 0x06, (byte) 0x01, (byte) 0x04, (byte) 0x01, (byte) 0xD6,
+            (byte) 0x79, (byte) 0x02, (byte) 0x05, (byte) 0x01, (byte) 0x30, (byte) 0x30,
+            (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1D, (byte) 0x1F, (byte) 0x04,
+            (byte) 0x29, (byte) 0x30, (byte) 0x27, (byte) 0x30, (byte) 0x25, (byte) 0xA0,
+            (byte) 0x23, (byte) 0xA0, (byte) 0x21, (byte) 0x86, (byte) 0x1F, (byte) 0x68,
+            (byte) 0x74, (byte) 0x74, (byte) 0x70, (byte) 0x3A, (byte) 0x2F, (byte) 0x2F,
+            (byte) 0x70, (byte) 0x6B, (byte) 0x69, (byte) 0x2E, (byte) 0x67, (byte) 0x6F,
+            (byte) 0x6F, (byte) 0x67, (byte) 0x6C, (byte) 0x65, (byte) 0x2E, (byte) 0x63,
+            (byte) 0x6F, (byte) 0x6D, (byte) 0x2F, (byte) 0x47, (byte) 0x49, (byte) 0x41,
+            (byte) 0x47, (byte) 0x32, (byte) 0x2E, (byte) 0x63, (byte) 0x72, (byte) 0x6C,
+            (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A, (byte) 0x86,
+            (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01, (byte) 0x01,
+            (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x82, (byte) 0x01,
+            (byte) 0x01, (byte) 0x00, (byte) 0x9A, (byte) 0x39, (byte) 0x70, (byte) 0x81,
+            (byte) 0x76, (byte) 0x8A, (byte) 0x94, (byte) 0xCB, (byte) 0x96, (byte) 0xF1,
+            (byte) 0xCA, (byte) 0xAF, (byte) 0x96, (byte) 0xAE, (byte) 0x1D, (byte) 0x73,
+            (byte) 0xB3, (byte) 0x2C, (byte) 0x82, (byte) 0x16, (byte) 0x29, (byte) 0xB5,
+            (byte) 0x3C, (byte) 0x7E, (byte) 0x55, (byte) 0x53, (byte) 0x6F, (byte) 0xB2,
+            (byte) 0xBC, (byte) 0x34, (byte) 0x96, (byte) 0xAE, (byte) 0x00, (byte) 0xD8,
+            (byte) 0xF2, (byte) 0x26, (byte) 0xD1, (byte) 0x18, (byte) 0x99, (byte) 0x9F,
+            (byte) 0x7D, (byte) 0xFD, (byte) 0xEB, (byte) 0xE0, (byte) 0xBB, (byte) 0x9D,
+            (byte) 0xE6, (byte) 0x46, (byte) 0xA5, (byte) 0x74, (byte) 0xAB, (byte) 0x3D,
+            (byte) 0x93, (byte) 0xC6, (byte) 0x25, (byte) 0x28, (byte) 0x3D, (byte) 0xC8,
+            (byte) 0x4C, (byte) 0x6E, (byte) 0xCF, (byte) 0xD1, (byte) 0x84, (byte) 0xFF,
+            (byte) 0x46, (byte) 0x4F, (byte) 0x21, (byte) 0x2E, (byte) 0x07, (byte) 0xC4,
+            (byte) 0xB8, (byte) 0xB7, (byte) 0x2A, (byte) 0xE5, (byte) 0xC7, (byte) 0x34,
+            (byte) 0xC6, (byte) 0xA9, (byte) 0x84, (byte) 0xE3, (byte) 0x6C, (byte) 0x49,
+            (byte) 0xF8, (byte) 0x4A, (byte) 0x36, (byte) 0xBB, (byte) 0x3A, (byte) 0xBD,
+            (byte) 0xAD, (byte) 0x8A, (byte) 0x2B, (byte) 0x73, (byte) 0x97, (byte) 0xA6,
+            (byte) 0x30, (byte) 0x2C, (byte) 0x5F, (byte) 0xE4, (byte) 0xBD, (byte) 0x13,
+            (byte) 0x24, (byte) 0xE5, (byte) 0xD9, (byte) 0xA8, (byte) 0x74, (byte) 0x29,
+            (byte) 0x38, (byte) 0x47, (byte) 0x2E, (byte) 0xA6, (byte) 0xD6, (byte) 0x50,
+            (byte) 0xE0, (byte) 0xE8, (byte) 0xDD, (byte) 0x60, (byte) 0xC7, (byte) 0xD2,
+            (byte) 0xC6, (byte) 0x4E, (byte) 0x54, (byte) 0xCE, (byte) 0xE7, (byte) 0x94,
+            (byte) 0x84, (byte) 0x0D, (byte) 0xE8, (byte) 0x81, (byte) 0x92, (byte) 0x91,
+            (byte) 0x71, (byte) 0x19, (byte) 0x1D, (byte) 0x07, (byte) 0x75, (byte) 0x9E,
+            (byte) 0x59, (byte) 0x1A, (byte) 0x7E, (byte) 0x9D, (byte) 0x84, (byte) 0x61,
+            (byte) 0xC7, (byte) 0x84, (byte) 0xAD, (byte) 0xA3, (byte) 0x6A, (byte) 0xED,
+            (byte) 0xD8, (byte) 0x0D, (byte) 0x0C, (byte) 0x2A, (byte) 0x66, (byte) 0x3D,
+            (byte) 0xD7, (byte) 0xAE, (byte) 0x46, (byte) 0x1D, (byte) 0x4A, (byte) 0x8C,
+            (byte) 0x2B, (byte) 0xD6, (byte) 0x1A, (byte) 0x69, (byte) 0x71, (byte) 0xC3,
+            (byte) 0x5E, (byte) 0xA0, (byte) 0x6E, (byte) 0xED, (byte) 0x27, (byte) 0x9F,
+            (byte) 0xAF, (byte) 0x5B, (byte) 0x92, (byte) 0xA0, (byte) 0x03, (byte) 0xFD,
+            (byte) 0x83, (byte) 0x22, (byte) 0x09, (byte) 0x29, (byte) 0xE8, (byte) 0xA1,
+            (byte) 0x32, (byte) 0x2B, (byte) 0xEC, (byte) 0x1A, (byte) 0xA2, (byte) 0x75,
+            (byte) 0x4C, (byte) 0x3E, (byte) 0x99, (byte) 0x71, (byte) 0xCE, (byte) 0x8B,
+            (byte) 0x31, (byte) 0xEF, (byte) 0x9D, (byte) 0x37, (byte) 0x63, (byte) 0xFC,
+            (byte) 0x71, (byte) 0x91, (byte) 0x10, (byte) 0x1E, (byte) 0xD0, (byte) 0xF5,
+            (byte) 0xCB, (byte) 0x6F, (byte) 0x7A, (byte) 0xBA, (byte) 0x5E, (byte) 0x0C,
+            (byte) 0x8A, (byte) 0xFA, (byte) 0xA4, (byte) 0xDE, (byte) 0x36, (byte) 0xAD,
+            (byte) 0x51, (byte) 0x52, (byte) 0xFC, (byte) 0xFE, (byte) 0x10, (byte) 0xB0,
+            (byte) 0x81, (byte) 0xC8, (byte) 0x7D, (byte) 0x03, (byte) 0xC3, (byte) 0xB8,
+            (byte) 0x3C, (byte) 0x66, (byte) 0x6A, (byte) 0xF5, (byte) 0x6A, (byte) 0x81,
+            (byte) 0x7C, (byte) 0x45, (byte) 0xA6, (byte) 0x23, (byte) 0x21, (byte) 0xE1,
+            (byte) 0xD5, (byte) 0xD3, (byte) 0xED, (byte) 0x6E, (byte) 0x0D, (byte) 0x65,
+            (byte) 0x39, (byte) 0x77, (byte) 0x58, (byte) 0x09, (byte) 0x6B, (byte) 0x63,
+            (byte) 0xA4, (byte) 0x02, (byte) 0x04, (byte) 0x00, (byte) 0xA5, (byte) 0x03,
+            (byte) 0x02, (byte) 0x01, (byte) 0x14, (byte) 0xA9, (byte) 0x05, (byte) 0x02,
+            (byte) 0x03, (byte) 0x01, (byte) 0x89, (byte) 0xC0, (byte) 0xAA, (byte) 0x81,
+            (byte) 0xA7, (byte) 0x04, (byte) 0x81, (byte) 0xA4, (byte) 0x1C, (byte) 0x14,
+            (byte) 0x42, (byte) 0xFA, (byte) 0x1E, (byte) 0x3A, (byte) 0x4D, (byte) 0x0A,
+            (byte) 0x83, (byte) 0x7E, (byte) 0x92, (byte) 0x61, (byte) 0x37, (byte) 0x0B,
+            (byte) 0x12, (byte) 0x45, (byte) 0xEA, (byte) 0x2B, (byte) 0x03, (byte) 0x81,
+            (byte) 0x7C, (byte) 0x5F, (byte) 0x6F, (byte) 0x13, (byte) 0x82, (byte) 0x97,
+            (byte) 0xD0, (byte) 0xDC, (byte) 0x5E, (byte) 0x2F, (byte) 0x08, (byte) 0xDC,
+            (byte) 0x0D, (byte) 0x3A, (byte) 0x6C, (byte) 0xBA, (byte) 0x1D, (byte) 0xEA,
+            (byte) 0x5C, (byte) 0x46, (byte) 0x99, (byte) 0xF7, (byte) 0xDD, (byte) 0xAB,
+            (byte) 0xD4, (byte) 0xDD, (byte) 0xFC, (byte) 0x54, (byte) 0x37, (byte) 0x32,
+            (byte) 0x4B, (byte) 0xA3, (byte) 0xFB, (byte) 0x23, (byte) 0xA1, (byte) 0xC1,
+            (byte) 0x60, (byte) 0xDF, (byte) 0x41, (byte) 0xB0, (byte) 0xD1, (byte) 0xCC,
+            (byte) 0xDF, (byte) 0xAD, (byte) 0xB3, (byte) 0x66, (byte) 0x76, (byte) 0x36,
+            (byte) 0xEC, (byte) 0x6A, (byte) 0x53, (byte) 0xC3, (byte) 0xE2, (byte) 0xB0,
+            (byte) 0x77, (byte) 0xBE, (byte) 0x75, (byte) 0x08, (byte) 0xBA, (byte) 0x17,
+            (byte) 0x14, (byte) 0xFA, (byte) 0x1A, (byte) 0x30, (byte) 0xE7, (byte) 0xB9,
+            (byte) 0xED, (byte) 0xD6, (byte) 0xC1, (byte) 0xA5, (byte) 0x7A, (byte) 0x2B,
+            (byte) 0xA3, (byte) 0xA3, (byte) 0xDD, (byte) 0xDC, (byte) 0x14, (byte) 0xDB,
+            (byte) 0x7F, (byte) 0xF4, (byte) 0xF3, (byte) 0xAF, (byte) 0xCF, (byte) 0x0A,
+            (byte) 0xD3, (byte) 0xAC, (byte) 0x84, (byte) 0x39, (byte) 0x30, (byte) 0xCA,
+            (byte) 0x3C, (byte) 0xD8, (byte) 0xF7, (byte) 0xFA, (byte) 0x29, (byte) 0xDB,
+            (byte) 0x31, (byte) 0xA5, (byte) 0x62, (byte) 0x82, (byte) 0xD2, (byte) 0xB8,
+            (byte) 0x3C, (byte) 0xBC, (byte) 0x8F, (byte) 0xAB, (byte) 0xE4, (byte) 0xE8,
+            (byte) 0xA7, (byte) 0x2C, (byte) 0xEF, (byte) 0xC7, (byte) 0xD5, (byte) 0x12,
+            (byte) 0x16, (byte) 0x04, (byte) 0x6F, (byte) 0xCA, (byte) 0xEA, (byte) 0x31,
+            (byte) 0x9F, (byte) 0x41, (byte) 0xE0, (byte) 0x6F, (byte) 0xE4, (byte) 0x74,
+            (byte) 0x03, (byte) 0x78, (byte) 0xFA, (byte) 0x48, (byte) 0xB4, (byte) 0x6E,
+            (byte) 0xC8, (byte) 0xE7, (byte) 0x40, (byte) 0x8B, (byte) 0x88, (byte) 0x2F,
+            (byte) 0xED, (byte) 0x8E, (byte) 0x68, (byte) 0x96, (byte) 0x2C, (byte) 0xA7,
+            (byte) 0xB6, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0x00};
+
+    private static final byte[] DUMMY_CERT =
+            new byte[] {(byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x58, (byte) 0x30,
+                    (byte) 0x82, (byte) 0x01, (byte) 0xC1, (byte) 0xA0, (byte) 0x03, (byte) 0x02,
+                    (byte) 0x01, (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xFB,
+                    (byte) 0xB0, (byte) 0x4C, (byte) 0x2E, (byte) 0xAB, (byte) 0x10, (byte) 0x9B,
+                    (byte) 0x0C, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A,
+                    (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01,
+                    (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x45,
+                    (byte) 0x31, (byte) 0x0B, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
+                    (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41,
+                    (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06,
+                    (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0C, (byte) 0x0A,
+                    (byte) 0x53, (byte) 0x6F, (byte) 0x6D, (byte) 0x65, (byte) 0x2D, (byte) 0x53,
+                    (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x21,
+                    (byte) 0x30, (byte) 0x1F, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+                    (byte) 0x0A, (byte) 0x0C, (byte) 0x18, (byte) 0x49, (byte) 0x6E, (byte) 0x74,
+                    (byte) 0x65, (byte) 0x72, (byte) 0x6E, (byte) 0x65, (byte) 0x74, (byte) 0x20,
+                    (byte) 0x57, (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69, (byte) 0x74,
+                    (byte) 0x73, (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79, (byte) 0x20,
+                    (byte) 0x4C, (byte) 0x74, (byte) 0x64, (byte) 0x30, (byte) 0x1E, (byte) 0x17,
+                    (byte) 0x0D, (byte) 0x31, (byte) 0x34, (byte) 0x30, (byte) 0x34, (byte) 0x32,
+                    (byte) 0x33, (byte) 0x32, (byte) 0x30, (byte) 0x35, (byte) 0x30, (byte) 0x34,
+                    (byte) 0x30, (byte) 0x5A, (byte) 0x17, (byte) 0x0D, (byte) 0x31, (byte) 0x37,
+                    (byte) 0x30, (byte) 0x34, (byte) 0x32, (byte) 0x32, (byte) 0x32, (byte) 0x30,
+                    (byte) 0x35, (byte) 0x30, (byte) 0x34, (byte) 0x30, (byte) 0x5A, (byte) 0x30,
+                    (byte) 0x45, (byte) 0x31, (byte) 0x0B, (byte) 0x30, (byte) 0x09, (byte) 0x06,
+                    (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02,
+                    (byte) 0x41, (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11,
+                    (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0C,
+                    (byte) 0x0A, (byte) 0x53, (byte) 0x6F, (byte) 0x6D, (byte) 0x65, (byte) 0x2D,
+                    (byte) 0x53, (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31,
+                    (byte) 0x21, (byte) 0x30, (byte) 0x1F, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+                    (byte) 0x04, (byte) 0x0A, (byte) 0x0C, (byte) 0x18, (byte) 0x49, (byte) 0x6E,
+                    (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x6E, (byte) 0x65, (byte) 0x74,
+                    (byte) 0x20, (byte) 0x57, (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69,
+                    (byte) 0x74, (byte) 0x73, (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79,
+                    (byte) 0x20, (byte) 0x4C, (byte) 0x74, (byte) 0x64, (byte) 0x30, (byte) 0x81,
+                    (byte) 0x9F, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A,
+                    (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01,
+                    (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81,
+                    (byte) 0x8D, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89, (byte) 0x02,
+                    (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xD8, (byte) 0x2B, (byte) 0xC8,
+                    (byte) 0xA6, (byte) 0x32, (byte) 0xE4, (byte) 0x62, (byte) 0xFF, (byte) 0x4D,
+                    (byte) 0xF3, (byte) 0xD0, (byte) 0xAD, (byte) 0x59, (byte) 0x8B, (byte) 0x45,
+                    (byte) 0xA7, (byte) 0xBD, (byte) 0xF1, (byte) 0x47, (byte) 0xBF, (byte) 0x09,
+                    (byte) 0x58, (byte) 0x7B, (byte) 0x22, (byte) 0xBD, (byte) 0x35, (byte) 0xAE,
+                    (byte) 0x97, (byte) 0x25, (byte) 0x86, (byte) 0x94, (byte) 0xA0, (byte) 0x80,
+                    (byte) 0xC0, (byte) 0xB4, (byte) 0x1F, (byte) 0x76, (byte) 0x91, (byte) 0x67,
+                    (byte) 0x46, (byte) 0x31, (byte) 0xD0, (byte) 0x10, (byte) 0x84, (byte) 0xB7,
+                    (byte) 0x22, (byte) 0x1E, (byte) 0x70, (byte) 0x23, (byte) 0x91, (byte) 0x72,
+                    (byte) 0xC8, (byte) 0xE9, (byte) 0x6D, (byte) 0x79, (byte) 0x3A, (byte) 0x85,
+                    (byte) 0x77, (byte) 0x80, (byte) 0x0F, (byte) 0xC4, (byte) 0x95, (byte) 0x16,
+                    (byte) 0x75, (byte) 0xC5, (byte) 0x4A, (byte) 0x71, (byte) 0x4C, (byte) 0xC8,
+                    (byte) 0x63, (byte) 0x3F, (byte) 0xA3, (byte) 0xF2, (byte) 0x63, (byte) 0x9C,
+                    (byte) 0x2A, (byte) 0x4F, (byte) 0x9A, (byte) 0xFA, (byte) 0xCB, (byte) 0xC1,
+                    (byte) 0x71, (byte) 0x6E, (byte) 0x28, (byte) 0x85, (byte) 0x28, (byte) 0xA0,
+                    (byte) 0x27, (byte) 0x1E, (byte) 0x65, (byte) 0x1C, (byte) 0xAE, (byte) 0x07,
+                    (byte) 0xD5, (byte) 0x5B, (byte) 0x6F, (byte) 0x2D, (byte) 0x43, (byte) 0xED,
+                    (byte) 0x2B, (byte) 0x90, (byte) 0xB1, (byte) 0x8C, (byte) 0xAF, (byte) 0x24,
+                    (byte) 0x6D, (byte) 0xAE, (byte) 0xE9, (byte) 0x17, (byte) 0x3A, (byte) 0x05,
+                    (byte) 0xC1, (byte) 0xBF, (byte) 0xB8, (byte) 0x1C, (byte) 0xAE, (byte) 0x65,
+                    (byte) 0x3B, (byte) 0x1B, (byte) 0x58, (byte) 0xC2, (byte) 0xD9, (byte) 0xAE,
+                    (byte) 0xD6, (byte) 0xAA, (byte) 0x67, (byte) 0x88, (byte) 0xF1, (byte) 0x02,
+                    (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xA3, (byte) 0x50,
+                    (byte) 0x30, (byte) 0x4E, (byte) 0x30, (byte) 0x1D, (byte) 0x06, (byte) 0x03,
+                    (byte) 0x55, (byte) 0x1D, (byte) 0x0E, (byte) 0x04, (byte) 0x16, (byte) 0x04,
+                    (byte) 0x14, (byte) 0x8B, (byte) 0x75, (byte) 0xD5, (byte) 0xAC, (byte) 0xCB,
+                    (byte) 0x08, (byte) 0xBE, (byte) 0x0E, (byte) 0x1F, (byte) 0x65, (byte) 0xB7,
+                    (byte) 0xFA, (byte) 0x56, (byte) 0xBE, (byte) 0x6C, (byte) 0xA7, (byte) 0x75,
+                    (byte) 0xDA, (byte) 0x85, (byte) 0xAF, (byte) 0x30, (byte) 0x1F, (byte) 0x06,
+                    (byte) 0x03, (byte) 0x55, (byte) 0x1D, (byte) 0x23, (byte) 0x04, (byte) 0x18,
+                    (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0x8B, (byte) 0x75,
+                    (byte) 0xD5, (byte) 0xAC, (byte) 0xCB, (byte) 0x08, (byte) 0xBE, (byte) 0x0E,
+                    (byte) 0x1F, (byte) 0x65, (byte) 0xB7, (byte) 0xFA, (byte) 0x56, (byte) 0xBE,
+                    (byte) 0x6C, (byte) 0xA7, (byte) 0x75, (byte) 0xDA, (byte) 0x85, (byte) 0xAF,
+                    (byte) 0x30, (byte) 0x0C, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1D,
+                    (byte) 0x13, (byte) 0x04, (byte) 0x05, (byte) 0x30, (byte) 0x03, (byte) 0x01,
+                    (byte) 0x01, (byte) 0xFF, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09,
+                    (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D,
+                    (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x03,
+                    (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x3B, (byte) 0xE8, (byte) 0x78,
+                    (byte) 0x6D, (byte) 0x95, (byte) 0xD6, (byte) 0x3D, (byte) 0x6A, (byte) 0xF7,
+                    (byte) 0x13, (byte) 0x19, (byte) 0x2C, (byte) 0x1B, (byte) 0xC2, (byte) 0x88,
+                    (byte) 0xAE, (byte) 0x22, (byte) 0xAB, (byte) 0xF4, (byte) 0x8D, (byte) 0x32,
+                    (byte) 0xF5, (byte) 0x7C, (byte) 0x71, (byte) 0x67, (byte) 0xCF, (byte) 0x2D,
+                    (byte) 0xD1, (byte) 0x1C, (byte) 0xC2, (byte) 0xC3, (byte) 0x87, (byte) 0xE2,
+                    (byte) 0xE9, (byte) 0xBE, (byte) 0x89, (byte) 0x5C, (byte) 0xE4, (byte) 0x34,
+                    (byte) 0xAB, (byte) 0x48, (byte) 0x91, (byte) 0xC2, (byte) 0x3F, (byte) 0x95,
+                    (byte) 0xAE, (byte) 0x2B, (byte) 0x47, (byte) 0x9E, (byte) 0x25, (byte) 0x78,
+                    (byte) 0x6B, (byte) 0x4F, (byte) 0x9A, (byte) 0x10, (byte) 0xA4, (byte) 0x72,
+                    (byte) 0xFD, (byte) 0xCF, (byte) 0xF7, (byte) 0x02, (byte) 0x0C, (byte) 0xB0,
+                    (byte) 0x0A, (byte) 0x08, (byte) 0xA4, (byte) 0x5A, (byte) 0xE2, (byte) 0xE5,
+                    (byte) 0x74, (byte) 0x7E, (byte) 0x11, (byte) 0x1D, (byte) 0x39, (byte) 0x60,
+                    (byte) 0x6A, (byte) 0xC9, (byte) 0x1F, (byte) 0x69, (byte) 0xF3, (byte) 0x2E,
+                    (byte) 0x63, (byte) 0x26, (byte) 0xDC, (byte) 0x9E, (byte) 0xEF, (byte) 0x6B,
+                    (byte) 0x7A, (byte) 0x0A, (byte) 0xE1, (byte) 0x54, (byte) 0x57, (byte) 0x98,
+                    (byte) 0xAA, (byte) 0x72, (byte) 0x91, (byte) 0x78, (byte) 0x04, (byte) 0x7E,
+                    (byte) 0x1F, (byte) 0x8F, (byte) 0x65, (byte) 0x4D, (byte) 0x1F, (byte) 0x0B,
+                    (byte) 0x12, (byte) 0xAC, (byte) 0x9C, (byte) 0x24, (byte) 0x0F, (byte) 0x84,
+                    (byte) 0x14, (byte) 0x1A, (byte) 0x55, (byte) 0x2D, (byte) 0x1F, (byte) 0xBB,
+                    (byte) 0xF0, (byte) 0x9D, (byte) 0x09, (byte) 0xB2, (byte) 0x08, (byte) 0x5C,
+                    (byte) 0x59, (byte) 0x32, (byte) 0x65, (byte) 0x80, (byte) 0x26};
+
+    private static final byte[] DUMMY_OCSP_DATA = new byte[1];
+
+    private static final byte[] DUMMY_TLS_SCT_DATA = new byte[1];
+
+    private static TestSessionBuilder getType1() {
+        return new TestSessionBuilder()
+                .setType(0x01)
+                .setSessionData(kOpenSSLSession)
+                .addCertificate(DUMMY_CERT);
+    }
+
+    private static TestSessionBuilder getType2() {
+        return new TestSessionBuilder()
+                .setType(0x02)
+                .setSessionData(kOpenSSLSession)
+                .addCertificate(DUMMY_CERT)
+                .addOcspData(DUMMY_OCSP_DATA);
+    }
+
+    private static TestSessionBuilder getType3() {
+        return new TestSessionBuilder()
+                .setType(0x03)
+                .setSessionData(kOpenSSLSession)
+                .addCertificate(DUMMY_CERT)
+                .addOcspData(DUMMY_OCSP_DATA)
+                .setTlsSctData(DUMMY_TLS_SCT_DATA);
+    }
+
+    @Test
+    public void toSession_EmptyArray_Invalid_Failure() throws Exception {
+        assertInvalidSession(new byte[0]);
+    }
+
+    @Test
+    public void toSession_Type1_Valid_Success() throws Exception {
+        assertValidSession(getType1().build());
+    }
+
+    @Test
+    public void toSession_Type2_Valid_Success() throws Exception {
+        assertValidSession(getType2().build());
+    }
+
+    @Test
+    public void toSession_Type3_Valid_Success() throws Exception {
+        assertValidSession(getType3().build());
+    }
+
+    private static void assertValidSession(byte[] data) {
+        assertNotNull(NativeSslSession.newInstance(null, data, "www.google.com", 443));
+    }
+
+    @Test
+    public void toSession_Type3_Truncated_Failure() throws Exception {
+        assertTruncatedSessionFails(getType3().build());
+    }
+
+    private static void assertInvalidSession(byte[] data) {
+        assertNull(NativeSslSession.newInstance(null, data, "www.google.com", 443));
+    }
+
+    private static void check_reserializableFromByteArray_roundTrip(
+            byte[] data, byte[] expectedTrailingBytesAfterReserialization) throws Exception {
+        NativeSslSession session =
+                NativeSslSession.newInstance(null, data, "www.example.com", 12345);
+        byte[] sessionBytes = session.toBytes();
+
+        NativeSslSession session2 =
+                NativeSslSession.newInstance(null, sessionBytes, "www.example.com", 12345);
+        byte[] sessionBytes2 = session2.toBytes();
+
+        assertSSLSessionEquals(session, session2);
+        assertByteArrayEquals(sessionBytes, sessionBytes2);
+
+        assertEquals("www.example.com", session.getPeerHost());
+        assertEquals(12345, session.getPeerPort());
+        assertTrue(sessionBytes.length >= data.length);
+
+        byte[] expectedReserializedData = concat(data, expectedTrailingBytesAfterReserialization);
+        // AbstractSessionContext.toBytes() always writes type 3 == OPEN_SSL_WITH_TLS_SCT
+        expectedReserializedData[3] = 3;
+        assertByteArrayEquals(expectedReserializedData, sessionBytes);
+    }
+
+    @Test
+    public void toSession_UnknownType_Failure() throws Exception {
+        assertInvalidSession(getType3().setType((byte) 0xEE).build());
+    }
+
+    @Test
+    public void toSession_CertificatesCountTooLarge_Failure() throws Exception {
+        assertInvalidSession(getType3().setCertificatesLength(16834).build());
+    }
+
+    @Test
+    public void toSession_CertificatesCountNegative_Failure() throws Exception {
+        assertInvalidSession(getType3().setCertificatesLength(-1).build());
+    }
+
+    @Test
+    public void toSession_CertificateSizeNegative_Failure() throws Exception {
+        assertInvalidSession(getType3().setCertificateLength(0, -1).build());
+    }
+
+    @Test
+    public void toSession_CertificateSizeTooLarge_Failure() throws Exception {
+        assertInvalidSession(getType3().setCertificateLength(0, 16834).build());
+    }
+
+    @Test
+    public void toSession_SessionDataSizeTooLarge_Failure() throws Exception {
+        assertInvalidSession(getType3().setSessionDataLength(16834).build());
+    }
+
+    @Test
+    public void toSession_SessionDataSizeNegative_Failure() throws Exception {
+        assertInvalidSession(getType3().setSessionDataLength(-1).build());
+    }
+
+    @Test
+    public void toSession_OcspDatasNumberTooMany_Failure() throws Exception {
+        assertInvalidSession(getType3().setOcspDatasLength(32791).build());
+    }
+
+    @Test
+    public void toSession_OcspDatasNumberNegative_Failure() throws Exception {
+        assertInvalidSession(getType3().setOcspDatasLength(-1).build());
+    }
+
+    @Test
+    public void toSession_OcspDataSizeNegative_Failure() throws Exception {
+        assertInvalidSession(getType3().setOcspDataLength(0, -1).build());
+    }
+
+    @Test
+    public void toSession_OcspDataSizeTooLarge_Failure() throws Exception {
+        assertInvalidSession(getType3().setOcspDataLength(0, 92948).build());
+    }
+
+    @Test
+    public void toSession_TlsSctDataSizeNegative_Failure() throws Exception {
+        assertInvalidSession(getType3().setTlsSctDataLength(-1).build());
+    }
+
+    @Test
+    public void toSession_TlsSctDataSizeTooLarge_Failure() throws Exception {
+        assertInvalidSession(getType3().setTlsSctDataLength(931148).build());
+    }
+
+    @Test
+    public void toSession_Type2OcspDataEmpty_Success() throws Exception {
+        assertValidSession(getType1().setType(0x02).setOcspDataEmpty().build());
+    }
+
+    @Test
+    public void toSession_Type3TlsSctDataEmpty_Success() throws Exception {
+        assertValidSession(getType2().setType(0x03).setTlsSctDataEmpty().build());
+    }
+
+    @Test
+    public void toSession_Type3OcspAndTlsSctDataEmpty_Success() throws Exception {
+        assertValidSession(
+                getType1().setType(0x03).setOcspDataEmpty().setTlsSctDataEmpty().build());
+    }
+
+    private static void assertTrailingDataFails(byte[] validSession) {
+        byte[] invalidSession = new byte[validSession.length + 1];
+        System.arraycopy(validSession, 0, invalidSession, 0, validSession.length);
+        assertInvalidSession(invalidSession);
+    }
+
+    @Test
+    public void toSession_Type1TrailingData_Failure() throws Exception {
+        assertTrailingDataFails(getType1().build());
+    }
+
+    @Test
+    public void toSession_Type2TrailingData_Failure() throws Exception {
+        assertTrailingDataFails(getType2().build());
+    }
+
+    @Test
+    public void toSession_Type3TrailingData_Failure() throws Exception {
+        assertTrailingDataFails(getType3().build());
+    }
+
+    @Test
+    public void test_reserializableFromByteArray_roundTrip_type1() throws Exception {
+        // Converting OPEN_SSL (type 1) -> OPEN_SSL_WITH_TLS_SCT (type 3) adds
+        // eight zero-bytes:
+        //  1.) 4 bytes for int32 value 0 == countOcspResponses
+        //  2.) 4 bytes for int32 value 0 == tlsSctDataLength
+        // since OPEN_SSL (type 1) cannot contain OSCP or TLS SCT data.
+        check_reserializableFromByteArray_roundTrip(getType1().build(), new byte[8]);
+    }
+
+    @Test
+    public void test_reserializableFromByteArray_roundTrip_type2() throws Exception {
+        // Converting OPEN_SSL_WITH_OCSP (type 2) -> OPEN_SSL_WITH_TLS_SCT (type 3) adds
+        // four zero-bytes for int32 value 0 == tlsSctDataLength
+        // since OPEN_SSL_WITH_OCSP (type 2) cannot contain TLS SCT data.
+        check_reserializableFromByteArray_roundTrip(getType2().build(), new byte[4]);
+    }
+
+    @Test
+    public void test_reserializableFromByteArray_roundTrip_type3() throws Exception {
+        check_reserializableFromByteArray_roundTrip(getType3().build(), new byte[0]);
+    }
+
+    private static void assertSSLSessionEquals(NativeSslSession a, NativeSslSession b)
+            throws Exception {
+        assertEquals(a.getCipherSuite(), b.getCipherSuite());
+        assertByteArrayEquals(a.getId(), b.getId());
+        assertEquals(a.getPeerHost(), b.getPeerHost());
+        assertEquals(a.getPeerPort(), b.getPeerPort());
+        assertEquals(a.getProtocol(), b.getProtocol());
+    }
+
+    private static byte[] concat(byte[] a, byte[] b) {
+        byte[] result = new byte[a.length + b.length];
+        System.arraycopy(a, 0, result, 0, a.length);
+        System.arraycopy(b, 0, result, a.length, b.length);
+        return result;
+    }
+
+    private void assertTruncatedSessionFails(byte[] validSession) {
+        for (int i = 0; i < validSession.length - 1; i++) {
+            byte[] truncatedSession = new byte[i];
+            System.arraycopy(validSession, 0, truncatedSession, 0, i);
+            assertNull("Truncating to " + i + " bytes of " + validSession.length
+                            + " should not succeed",
+                    NativeSslSession.newInstance(null, truncatedSession, "www.google.com", 443));
+        }
+    }
+
+    private static void assertByteArrayEquals(byte[] expected, byte[] actual) {
+        // If running on OpenJDK 8+, could use java.util.Base64 for better failure messages:
+        // assertEquals(Base64.encode(expected), Base64.encode(actual));
+        assertTrue("Expected " + Arrays.toString(expected) + ", got " + Arrays.toString(actual),
+                Arrays.equals(expected, actual));
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/OpenSSLKeyTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/OpenSSLKeyTest.java
new file mode 100644
index 0000000..51e7077
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/OpenSSLKeyTest.java
@@ -0,0 +1,70 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
+import junit.framework.TestCase;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class OpenSSLKeyTest extends TestCase {
+    static final String RSA_PUBLIC_KEY =
+        "-----BEGIN PUBLIC KEY-----\n" +
+        "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAOHsK2E2FLYfEMWEVH/rJMTqDZLLLysh\n" +
+        "AH5odcfhYdF9xvFFU9rqJT7zXUDH4SjdhZGUUAO5IOC1e8ZIyRsbiY0CAwEAAQ==\n" +
+        "-----END PUBLIC KEY-----";
+
+    static final String RSA_PRIVATE_KEY =
+        "-----BEGIN RSA PRIVATE KEY-----\n" +
+        "MIIBOgIBAAJBAOHsK2E2FLYfEMWEVH/rJMTqDZLLLyshAH5odcfhYdF9xvFFU9rq\n" +
+        "JT7zXUDH4SjdhZGUUAO5IOC1e8ZIyRsbiY0CAwEAAQJBALcu+oGJC0QcbknpIWbT\n" +
+        "L+4mZTkYXLeYu8DDTHT0j47+6eEyYBOoRGcZDdlMWquvFIrV48RSot0GPh1MBE1p\n" +
+        "lKECIQD4krM4UshCwUHH9ZVkoxcPsxzPTTW7ukky4RZVN6mgWQIhAOisOAXVVjon\n" +
+        "fbGNQ6CezH7oOttEeZmiWCu48AVCyixVAiAaDZ41OA//Vywi3i2jV6iyH47Ud347\n" +
+        "R+ImMAtcMTJZOQIgF0+Z1UvIdc8bErzad68xQc22h91WaYQQXWEL+xrz8nkCIDcA\n" +
+        "MpCP/H5qTCj/l5rxQg+/NUGCg2pHHNLL+cy5N5RM\n" +
+        "-----END RSA PRIVATE KEY-----";
+
+    static final BigInteger RSA_MODULUS = new BigInteger(
+        "e1ec2b613614b61f10c584547feb24c4ea0d92cb2f2b21007e6875c7e161d17d" +
+        "c6f14553daea253ef35d40c7e128dd8591945003b920e0b57bc648c91b1b898d", 16);
+
+    static final BigInteger RSA_PUBLIC_EXPONENT = new BigInteger("10001", 16);
+    static final BigInteger RSA_PRIVATE_EXPONENT = new BigInteger(
+        "b72efa81890b441c6e49e92166d32fee266539185cb798bbc0c34c74f48f8efe" +
+        "e9e1326013a84467190dd94c5aabaf148ad5e3c452a2dd063e1d4c044d6994a1", 16);
+
+    public void test_fromPublicKeyPemInputStream() throws Exception {
+        ByteArrayInputStream is = new ByteArrayInputStream(RSA_PUBLIC_KEY.getBytes("UTF-8"));
+        OpenSSLKey key = OpenSSLKey.fromPublicKeyPemInputStream(is);
+        OpenSSLRSAPublicKey publicKey = (OpenSSLRSAPublicKey)key.getPublicKey();
+        assertEquals(RSA_MODULUS, publicKey.getModulus());
+        assertEquals(RSA_PUBLIC_EXPONENT, publicKey.getPublicExponent());
+    }
+
+    public void test_fromPrivateKeyPemInputStream() throws Exception {
+        ByteArrayInputStream is = new ByteArrayInputStream(RSA_PRIVATE_KEY.getBytes("UTF-8"));
+        OpenSSLKey key = OpenSSLKey.fromPrivateKeyPemInputStream(is);
+        OpenSSLRSAPrivateKey privateKey = (OpenSSLRSAPrivateKey)key.getPrivateKey();
+        assertEquals(RSA_MODULUS, privateKey.getModulus());
+        assertEquals(RSA_PRIVATE_EXPONENT, privateKey.getPrivateExponent());
+    }
+}
+
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/OpenSSLServerSocketImplTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/OpenSSLServerSocketImplTest.java
new file mode 100644
index 0000000..7cf7072
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/OpenSSLServerSocketImplTest.java
@@ -0,0 +1,129 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.TestUtils.LOCALHOST;
+import static com.android.org.conscrypt.TestUtils.getConscryptServerSocketFactory;
+import static com.android.org.conscrypt.TestUtils.getJdkSocketFactory;
+import static com.android.org.conscrypt.TestUtils.getProtocols;
+import static com.android.org.conscrypt.TestUtils.newTextMessage;
+import static com.android.org.conscrypt.TestUtils.pickUnusedPort;
+import static org.junit.Assert.assertArrayEquals;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(Parameterized.class)
+public class OpenSSLServerSocketImplTest {
+    private static final String CIPHER = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256";
+    private static final int MESSAGE_SIZE = 4096;
+
+    /**
+     * Various factories for SSL server sockets.
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum SocketType {
+        DEFAULT(getConscryptServerSocketFactory(false)),
+        ENGINE(getConscryptServerSocketFactory(true));
+
+        @SuppressWarnings("ImmutableEnumChecker")
+        private final SSLServerSocketFactory serverSocketFactory;
+
+        SocketType(SSLServerSocketFactory serverSocketFactory) {
+            this.serverSocketFactory = serverSocketFactory;
+        }
+
+        final SSLServerSocket newServerSocket(String cipher) {
+            try {
+                int port = pickUnusedPort();
+                SSLServerSocket sslSocket =
+                        (SSLServerSocket) serverSocketFactory.createServerSocket(port);
+                sslSocket.setEnabledProtocols(getProtocols());
+                sslSocket.setEnabledCipherSuites(new String[] {cipher});
+                return sslSocket;
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    @Parameters(name = "{0}")
+    public static Iterable<SocketType> data() {
+        // Android-changed: Temporarily (2017 Q2) disable ENGINE tests. http://b/37271061#comment9
+        // This experimental (unused by default) implementation is unstable and causing test
+        // failures on Android.
+        // return Arrays.asList(SocketType.DEFAULT, SocketType.ENGINE);
+        return Arrays.asList(SocketType.DEFAULT);
+    }
+
+    @Parameter public SocketType socketType;
+
+    private TestClient client;
+    private TestServer server;
+
+    @Before
+    public void setup() throws Exception {
+        // Create and start the server.
+        server = new TestServer(socketType.newServerSocket(CIPHER), MESSAGE_SIZE);
+        Future<?> connectedFuture = server.start();
+
+        // Create and start the client.
+        SSLSocketFactory socketFactory = getJdkSocketFactory();
+        SSLSocket socket = (SSLSocket) socketFactory.createSocket(LOCALHOST, server.port());
+        socket.setEnabledProtocols(getProtocols());
+        socket.setEnabledCipherSuites(new String[] {CIPHER});
+        client = new TestClient(socket);
+        client.start();
+
+        // Wait for the initial connection to complete.
+        connectedFuture.get(5, TimeUnit.SECONDS);
+    }
+
+    @After
+    public void teardown() throws Exception {
+        client.stop();
+        server.stop();
+    }
+
+    @Test
+    public void pingPong() throws IOException {
+        byte[] request = newTextMessage(MESSAGE_SIZE);
+        byte[] responseBuffer = new byte[MESSAGE_SIZE];
+        client.sendMessage(request);
+        client.flush();
+        int numBytes = client.readMessage(responseBuffer);
+        byte[] response = Arrays.copyOfRange(responseBuffer, 0, numBytes);
+        assertArrayEquals(request, response);
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/OpenSSLX509CertificateTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/OpenSSLX509CertificateTest.java
new file mode 100644
index 0000000..07439e0
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/OpenSSLX509CertificateTest.java
@@ -0,0 +1,140 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.TestUtils.openTestFile;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import junit.framework.TestCase;
+import com.android.org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class OpenSSLX509CertificateTest extends TestCase {
+    public void testSerialization_NoContextDeserialization() throws Exception {
+        // Set correct serialVersionUID
+        {
+            ObjectStreamClass clDesc = ObjectStreamClass.lookup(OpenSSLX509Certificate.class);
+            assertNotNull(clDesc);
+
+            // Set our fake class's serialization UID.
+            Field targetUID = ZpenSSLX509Certificate.class.getDeclaredField("serialVersionUID");
+            targetUID.setAccessible(true);
+
+            // Mark the field as non-final on JVM that need it.
+            try {
+                Field modifiersField = Field.class.getDeclaredField("modifiers");
+                modifiersField.setAccessible(true);
+                modifiersField.setInt(targetUID, targetUID.getModifiers() & ~Modifier.FINAL);
+            } catch (NoSuchFieldException ignored) {
+            }
+
+            targetUID.set(null, clDesc.getSerialVersionUID());
+        }
+
+        final byte[] impostorBytes;
+        // Serialization
+        {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(baos);
+            oos.writeObject(new ZpenSSLX509Certificate(0xA5A5A5A5A5A5A5A5L));
+            oos.close();
+            impostorBytes = baos.toByteArray();
+        }
+
+        // Fix class name
+        {
+            boolean fixed = false;
+            for (int i = 0; i < impostorBytes.length - 4; i++) {
+                if (impostorBytes[i] == 'Z' && impostorBytes[i + 1] == 'p'
+                        && impostorBytes[i + 2] == 'e' && impostorBytes[i + 3] == 'n') {
+                    impostorBytes[i] = 'O';
+                    fixed = true;
+                    break;
+                }
+            }
+            assertTrue(fixed);
+        }
+
+        // Deserialization
+        {
+            ByteArrayInputStream bais = new ByteArrayInputStream(impostorBytes);
+            ObjectInputStream ois = new ObjectInputStream(bais);
+            OpenSSLX509Certificate cert = (OpenSSLX509Certificate) ois.readObject();
+            ois.close();
+            assertEquals(0L, cert.getContext());
+        }
+    }
+
+    static final String CT_POISON_EXTENSION = "1.3.6.1.4.1.11129.2.4.3";
+
+    private OpenSSLX509Certificate loadTestCertificate(String name)
+            throws FileNotFoundException, ParsingException {
+        return OpenSSLX509Certificate.fromX509PemInputStream(openTestFile(name));
+    }
+
+    public void test_deletingCTPoisonExtension() throws Exception {
+        /* certPoisoned has an extra poison extension.
+         * With the extension, the certificates have different TBS.
+         * Without it, the certificates should have the same TBS.
+         */
+        OpenSSLX509Certificate cert = loadTestCertificate("cert.pem");
+        OpenSSLX509Certificate certPoisoned = loadTestCertificate("cert-ct-poisoned.pem");
+
+        assertFalse(Arrays.equals(
+                certPoisoned.getTBSCertificate(),
+                cert.getTBSCertificate()));
+
+        assertTrue(Arrays.equals(
+                certPoisoned.withDeletedExtension(CT_POISON_EXTENSION).getTBSCertificate(),
+                cert.getTBSCertificate()));
+    }
+
+    public void test_deletingExtensionMakesCopy() throws Exception {
+        /* Calling withDeletedExtension should not modify the original certificate, only make a copy.
+         * Make sure the extension is still present in the original object.
+         */
+        OpenSSLX509Certificate certPoisoned = loadTestCertificate("cert-ct-poisoned.pem");
+        assertTrue(certPoisoned.getCriticalExtensionOIDs().contains(CT_POISON_EXTENSION));
+
+        OpenSSLX509Certificate certWithoutExtension = certPoisoned.withDeletedExtension(CT_POISON_EXTENSION);
+
+        assertTrue(certPoisoned.getCriticalExtensionOIDs().contains(CT_POISON_EXTENSION));
+        assertFalse(certWithoutExtension.getCriticalExtensionOIDs().contains(CT_POISON_EXTENSION));
+    }
+
+    public void test_deletingMissingExtension() throws Exception {
+        /* withDeletedExtension should be safe to call on a certificate without the extension, and
+         * return an identical copy.
+         */
+        OpenSSLX509Certificate cert = loadTestCertificate("cert.pem");
+        assertFalse(cert.getCriticalExtensionOIDs().contains(CT_POISON_EXTENSION));
+
+        OpenSSLX509Certificate cert2 = cert.withDeletedExtension(CT_POISON_EXTENSION);
+        assertEquals(cert, cert2);
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/PlatformTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/PlatformTest.java
new file mode 100644
index 0000000..eddaa18
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/PlatformTest.java
@@ -0,0 +1,179 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.lang.reflect.Method;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIServerName;
+import javax.net.ssl.SSLParameters;
+import org.junit.Assume;
+import org.junit.Test;
+
+/**
+ * Test for Platform
+ * @hide This class is not part of the Android public SDK API
+ */
+public class PlatformTest {
+    private static final Method SSL_PARAMETERS_GET_APPLICATION_PROTOCOLS_METHOD;
+    private static final Method SSL_PARAMETERS_SET_APPLICATION_PROTOCOLS_METHOD;
+
+    static {
+        Class<?> sslParameters = SSLParameters.class;
+        Method getApplicationProtocolsMethod;
+        Method setApplicationProtocolsMethod;
+        try {
+            getApplicationProtocolsMethod = sslParameters.getMethod("getApplicationProtocols");
+            setApplicationProtocolsMethod =
+                sslParameters.getMethod("setApplicationProtocols", String[].class);
+        } catch (NoSuchMethodException e) {
+            getApplicationProtocolsMethod = null;
+            setApplicationProtocolsMethod = null;
+        }
+
+        SSL_PARAMETERS_GET_APPLICATION_PROTOCOLS_METHOD = getApplicationProtocolsMethod;
+        SSL_PARAMETERS_SET_APPLICATION_PROTOCOLS_METHOD = setApplicationProtocolsMethod;
+    }
+
+    private static boolean isJavaVersion(int version) {
+        return Platform.javaVersion() >= version;
+    }
+
+    private static void assumeJava8() {
+        Assume.assumeTrue("Require Java 8: " + Platform.javaVersion(), isJavaVersion(8));
+    }
+
+    @Test
+    public void test_setSSLParameters_Socket() throws Exception {
+        assumeJava8();
+        Socket socket = new OpenSSLSocketFactoryImpl().createSocket();
+        SSLParametersImpl impl = SSLParametersImpl.getDefault();
+        SSLParameters params = new SSLParameters();
+        List<SNIServerName> names = new ArrayList<SNIServerName>();
+        names.add(new SNIHostName("some.host"));
+        params.setServerNames(names);
+        params.setUseCipherSuitesOrder(false);
+        params.setEndpointIdentificationAlgorithm("ABC");
+        String[] applicationProtocols = new String[] {"foo", "bar"};
+        if (isJavaVersion(9)) {
+            setApplicationProtocols(params, applicationProtocols);
+        }
+        Platform.setSSLParameters(params, impl, (AbstractConscryptSocket) socket);
+        assertEquals("some.host", ((AbstractConscryptSocket) socket).getHostname());
+        assertFalse(impl.getUseCipherSuitesOrder());
+        assertEquals("ABC", impl.getEndpointIdentificationAlgorithm());
+        if (isJavaVersion(9)) {
+            assertArrayEquals(applicationProtocols, impl.getApplicationProtocols());
+        }
+    }
+
+    @Test
+    public void test_getSSLParameters_Socket() throws Exception {
+        assumeJava8();
+        Socket socket = new OpenSSLSocketFactoryImpl().createSocket();
+        SSLParametersImpl impl = SSLParametersImpl.getDefault();
+        SSLParameters params = new SSLParameters();
+        impl.setUseCipherSuitesOrder(false);
+        impl.setEndpointIdentificationAlgorithm("ABC");
+        String[] applicationProtocols = new String[] {"foo", "bar"};
+        if (isJavaVersion(9)) {
+            impl.setApplicationProtocols(applicationProtocols);
+        }
+        ((AbstractConscryptSocket) socket).setHostname("some.host");
+        Platform.getSSLParameters(params, impl, (AbstractConscryptSocket) socket);
+        assertEquals("some.host", ((SNIHostName) params.getServerNames().get(0)).getAsciiName());
+        assertFalse(params.getUseCipherSuitesOrder());
+        assertEquals("ABC", params.getEndpointIdentificationAlgorithm());
+        if (isJavaVersion(9)) {
+            assertArrayEquals(applicationProtocols, getApplicationProtocols(params));
+        }
+    }
+
+    @Test
+    public void test_setSSLParameters_Engine() throws Exception {
+        assumeJava8();
+        SSLParametersImpl impl = SSLParametersImpl.getDefault();
+        SSLParameters params = new SSLParameters();
+        ConscryptEngine engine = new ConscryptEngine(impl);
+        List<SNIServerName> names = new ArrayList<SNIServerName>();
+        names.add(new SNIHostName("some.host"));
+        params.setServerNames(names);
+        params.setUseCipherSuitesOrder(false);
+        params.setEndpointIdentificationAlgorithm("ABC");
+        String[] applicationProtocols = new String[] {"foo", "bar"};
+        if (isJavaVersion(9)) {
+            setApplicationProtocols(params, applicationProtocols);
+        }
+        Platform.setSSLParameters(params, impl, engine);
+        assertEquals("some.host", engine.getHostname());
+        assertFalse(impl.getUseCipherSuitesOrder());
+        assertEquals("ABC", impl.getEndpointIdentificationAlgorithm());
+        if (isJavaVersion(9)) {
+            assertArrayEquals(applicationProtocols, impl.getApplicationProtocols());
+        }
+    }
+
+    @Test
+    public void test_getSSLParameters_Engine() throws Exception {
+        assumeJava8();
+        SSLParametersImpl impl = SSLParametersImpl.getDefault();
+        SSLParameters params = new SSLParameters();
+        ConscryptEngine engine = new ConscryptEngine(impl);
+        impl.setUseCipherSuitesOrder(false);
+        impl.setEndpointIdentificationAlgorithm("ABC");
+        engine.setHostname("some.host");
+        String[] applicationProtocols = new String[] {"foo", "bar"};
+        if (isJavaVersion(9)) {
+            impl.setApplicationProtocols(applicationProtocols);
+        }
+        Platform.getSSLParameters(params, impl, engine);
+        assertEquals("some.host", ((SNIHostName) params.getServerNames().get(0)).getAsciiName());
+        assertFalse(params.getUseCipherSuitesOrder());
+        assertEquals("ABC", params.getEndpointIdentificationAlgorithm());
+        if (isJavaVersion(9)) {
+            assertArrayEquals(applicationProtocols, getApplicationProtocols(params));
+        }
+    }
+
+    private static String[] getApplicationProtocols(SSLParameters params) {
+        if (SSL_PARAMETERS_GET_APPLICATION_PROTOCOLS_METHOD != null) {
+            try {
+                return (String[]) SSL_PARAMETERS_GET_APPLICATION_PROTOCOLS_METHOD.invoke(params);
+            } catch (Exception ignored) {
+                // TODO(nmittler): Should we throw here?
+            }
+        }
+        return EmptyArray.STRING;
+    }
+
+    private static void setApplicationProtocols(SSLParameters params, String[] protocols) {
+        if (SSL_PARAMETERS_SET_APPLICATION_PROTOCOLS_METHOD != null) {
+            try {
+                SSL_PARAMETERS_SET_APPLICATION_PROTOCOLS_METHOD.invoke(params, (Object) protocols);
+            } catch (Exception ignored) {
+                // TODO(nmittler): Should we throw here?
+            }
+        }
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/RenegotiationTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/RenegotiationTest.java
new file mode 100644
index 0000000..a6df39d
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/RenegotiationTest.java
@@ -0,0 +1,447 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.org.conscrypt;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.Status;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * This tests that server-initiated cipher renegotiation works properly with a Conscrypt client.
+ * BoringSSL does not support user-initiated renegotiation, so we use the JDK implementation for
+ * the server.
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(Parameterized.class)
+public class RenegotiationTest {
+    private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocateDirect(0);
+    private static final String[] CIPHERS = TestUtils.getCommonCipherSuites();
+    private static final byte[] MESSAGE_BYTES = "Hello".getBytes(TestUtils.UTF_8);
+    private static final ByteBuffer MESSAGE_BUFFER =
+            ByteBuffer.wrap(MESSAGE_BYTES).asReadOnlyBuffer();
+    private static final int MESSAGE_LENGTH = MESSAGE_BYTES.length;
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public enum SocketType {
+        FILE_DESCRIPTOR {
+            @Override
+            Client newClient(int port) {
+                return new Client(false, port);
+            }
+        },
+        ENGINE {
+            @Override
+            Client newClient(int port) {
+                return new Client(true, port);
+            }
+        };
+
+        abstract Client newClient(int port);
+    }
+
+    @Parameters(name = "{0}")
+    public static Object[] data() {
+        return new Object[] {SocketType.FILE_DESCRIPTOR, SocketType.ENGINE};
+    }
+
+    @Parameter
+    public SocketType socketType;
+
+    private Client client;
+    private Server server;
+
+    @Before
+    public void setup() throws Exception {
+        server = new Server();
+        Future<?> connectedFuture = server.start();
+
+        client = socketType.newClient(server.port());
+        client.start();
+
+        // Wait for the initial connection to complete.
+        connectedFuture.get(5, TimeUnit.SECONDS);
+    }
+
+    @After
+    public void teardown() {
+        client.stop();
+        server.stop();
+    }
+
+    @Test
+    public void test() throws Exception {
+        client.socket.startHandshake();
+        String initialCipher = client.socket.getSession().getCipherSuite();
+
+        client.sendMessage();
+
+        Future<?> repliesFuture = client.readReplies();
+        server.await(5, TimeUnit.SECONDS);
+        repliesFuture.get(5, TimeUnit.SECONDS);
+
+        // Verify that the cipher has changed.
+        assertNotEquals(initialCipher, client.socket.getSession().getCipherSuite());
+    }
+
+    private static SSLContext newConscryptClientContext() {
+        SSLContext context = TestUtils.newContext(TestUtils.getConscryptProvider());
+        return TestUtils.initSslContext(context, TestKeyStore.getClient());
+    }
+
+    private static SSLContext newJdkServerContext() {
+        SSLContext context = TestUtils.newContext(TestUtils.getJdkProvider());
+        return TestUtils.initSslContext(context, TestKeyStore.getServer());
+    }
+
+    private static final class Client {
+        private final SSLSocket socket;
+        private ExecutorService executor;
+        private volatile boolean stopping;
+
+        Client(boolean useEngineSocket, int port) {
+            try {
+                SSLSocketFactory socketFactory = newConscryptClientContext().getSocketFactory();
+                Conscrypt.setUseEngineSocket(socketFactory, useEngineSocket);
+                socket = (SSLSocket) socketFactory.createSocket(
+                        TestUtils.getLoopbackAddress(), port);
+                socket.setEnabledCipherSuites(CIPHERS);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        void start() {
+            try {
+                executor = Executors.newSingleThreadExecutor();
+                socket.startHandshake();
+            } catch (IOException e) {
+                e.printStackTrace();
+                throw new RuntimeException(e);
+            }
+        }
+
+        void stop() {
+            try {
+                stopping = true;
+                socket.close();
+
+                if (executor != null) {
+                    executor.shutdown();
+                    executor.awaitTermination(5, TimeUnit.SECONDS);
+                    executor = null;
+                }
+            } catch (RuntimeException e) {
+                throw e;
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        Future<?> readReplies() {
+            return executor.submit(new Runnable() {
+                @Override
+                public void run() {
+                    readReply();
+                }
+            });
+        }
+
+        private void readReply() {
+            try {
+                byte[] buffer = new byte[MESSAGE_LENGTH];
+                int totalBytesRead = 0;
+                while (totalBytesRead < MESSAGE_LENGTH) {
+                    int remaining = MESSAGE_LENGTH - totalBytesRead;
+                    int bytesRead = socket.getInputStream().read(buffer, totalBytesRead, remaining);
+                    if (bytesRead == -1) {
+                        throw new EOFException();
+                    }
+                    totalBytesRead += bytesRead;
+                }
+
+                // Verify the reply is correct.
+                assertEquals(MESSAGE_LENGTH, totalBytesRead);
+                assertArrayEquals(MESSAGE_BYTES, buffer);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        void sendMessage() throws IOException {
+            try {
+                socket.getOutputStream().write(MESSAGE_BYTES);
+                socket.getOutputStream().flush();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private static final class Server {
+        private final ServerSocketChannel serverChannel;
+        private final SSLEngine engine;
+        private final ByteBuffer inboundPacketBuffer;
+        private final ByteBuffer inboundAppBuffer;
+        private final ByteBuffer outboundPacketBuffer;
+        private final Set<String> ciphers = new LinkedHashSet<String>(Arrays.asList(CIPHERS));
+        private SocketChannel channel;
+        private ExecutorService executor;
+        private volatile boolean stopping;
+        private volatile Future<?> echoFuture;
+
+        Server() throws IOException {
+            serverChannel = ServerSocketChannel.open();
+            serverChannel.socket().bind(new InetSocketAddress(TestUtils.getLoopbackAddress(), 0));
+            engine = newJdkServerContext().createSSLEngine();
+            engine.setEnabledCipherSuites(CIPHERS);
+            engine.setUseClientMode(false);
+
+            inboundPacketBuffer =
+                    ByteBuffer.allocateDirect(engine.getSession().getPacketBufferSize());
+            inboundAppBuffer =
+                    ByteBuffer.allocateDirect(engine.getSession().getApplicationBufferSize());
+            outboundPacketBuffer =
+                    ByteBuffer.allocateDirect(engine.getSession().getPacketBufferSize());
+        }
+
+        Future<?> start() throws IOException {
+            executor = Executors.newSingleThreadExecutor();
+            return executor.submit(new AcceptTask());
+        }
+
+        void await(long timeout, TimeUnit unit)
+                throws InterruptedException, ExecutionException, TimeoutException {
+            echoFuture.get(timeout, unit);
+        }
+
+        void stop() {
+            try {
+                stopping = true;
+
+                if (channel != null) {
+                    channel.close();
+                    channel = null;
+                }
+
+                serverChannel.close();
+
+                if (executor != null) {
+                    executor.shutdown();
+                    executor.awaitTermination(5, TimeUnit.SECONDS);
+                    executor = null;
+                }
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        int port() {
+            return serverChannel.socket().getLocalPort();
+        }
+
+        private final class AcceptTask implements Runnable {
+            @Override
+            public void run() {
+                try {
+                    if (stopping) {
+                        return;
+                    }
+                    channel = serverChannel.accept();
+                    channel.configureBlocking(false);
+
+                    doHandshake();
+
+                    if (stopping) {
+                        return;
+                    }
+                    echoFuture = executor.submit(new EchoTask());
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+
+        private final class EchoTask implements Runnable {
+            @Override
+            public void run() {
+                try {
+                    readMessage();
+                    renegotiate();
+                    reply();
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                    throw new RuntimeException(e);
+                }
+            }
+
+            private void renegotiate() throws Exception {
+                // Remove the current cipher from the set and renegotiate to force a new
+                // cipher to be selected.
+                String currentCipher = engine.getSession().getCipherSuite();
+                ciphers.remove(currentCipher);
+                engine.setEnabledCipherSuites(ciphers.toArray(new String[ciphers.size()]));
+                doHandshake();
+            }
+
+            private void reply() throws IOException {
+                SSLEngineResult result = wrap(newMessage());
+                if (result.getStatus() != Status.OK) {
+                    throw new RuntimeException("Wrap failed. Status: " + result.getStatus());
+                }
+            }
+
+            private ByteBuffer newMessage() {
+                return MESSAGE_BUFFER.duplicate();
+            }
+
+            private void readMessage() throws IOException {
+                int totalProduced = 0;
+                while (!stopping) {
+                    SSLEngineResult result = unwrap();
+                    if (result.getStatus() != Status.OK) {
+                        throw new RuntimeException("Failed reading message: " + result);
+                    }
+                    totalProduced += result.bytesProduced();
+                    if (totalProduced == MESSAGE_LENGTH) {
+                        return;
+                    }
+                }
+            }
+        }
+
+        private SSLEngineResult wrap(ByteBuffer src) throws IOException {
+            outboundPacketBuffer.clear();
+
+            // Check if the engine has bytes to wrap.
+            SSLEngineResult result = engine.wrap(src, outboundPacketBuffer);
+
+            // Write any wrapped bytes to the socket.
+            outboundPacketBuffer.flip();
+
+            do {
+                channel.write(outboundPacketBuffer);
+            } while (outboundPacketBuffer.hasRemaining());
+
+            return result;
+        }
+
+        private SSLEngineResult unwrap() throws IOException {
+            // Unwrap any available bytes from the socket.
+            SSLEngineResult result = null;
+            boolean done = false;
+            while (!done) {
+                if (channel.read(inboundPacketBuffer) == -1) {
+                    throw new EOFException();
+                }
+                // Just clear the app buffer - we don't really use it.
+                inboundAppBuffer.clear();
+                inboundPacketBuffer.flip();
+                result = engine.unwrap(inboundPacketBuffer, inboundAppBuffer);
+                switch (result.getStatus()) {
+                    case BUFFER_UNDERFLOW:
+                        // Continue reading from the socket in a moment.
+                        try {
+                            Thread.sleep(10);
+                        } catch (InterruptedException e) {
+                            throw new RuntimeException(e);
+                        }
+                        break;
+                    case OK:
+                        done = true;
+                        break;
+                    default: { throw new RuntimeException("Unexpected unwrap result: " + result); }
+                }
+
+                // Compact for the next socket read.
+                inboundPacketBuffer.compact();
+            }
+            return result;
+        }
+
+        private void doHandshake() throws IOException {
+            engine.beginHandshake();
+
+            boolean done = false;
+            while (!done) {
+                switch (engine.getHandshakeStatus()) {
+                    case NEED_WRAP: {
+                        wrap(EMPTY_BUFFER);
+                        break;
+                    }
+                    case NEED_UNWRAP: {
+                        unwrap();
+                        break;
+                    }
+                    case NEED_TASK: {
+                        runDelegatedTasks();
+                        break;
+                    }
+                    default: {
+                        done = true;
+                        break;
+                    }
+                }
+            }
+        }
+
+        private void runDelegatedTasks() {
+            for (;;) {
+                Runnable task = engine.getDelegatedTask();
+                if (task == null) {
+                    break;
+                }
+                task.run();
+            }
+        }
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/SSLUtilsTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/SSLUtilsTest.java
new file mode 100644
index 0000000..8366738
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/SSLUtilsTest.java
@@ -0,0 +1,226 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static com.android.org.conscrypt.TestUtils.UTF_8;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class SSLUtilsTest {
+    private static final byte[] VALID_CHARACTERS =
+            "0123456789abcdefghijklmnopqrstuvwxyz".getBytes(UTF_8);
+
+    @Test
+    public void noProtocolsShouldSucceed() {
+        byte[] expected = new byte[0];
+        byte[] actual = SSLUtils.encodeProtocols(EmptyArray.STRING);
+        assertArrayEquals(expected, actual);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void emptyProtocolShouldThrow() {
+        SSLUtils.encodeProtocols(new String[] {""});
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void longProtocolShouldThrow() {
+        SSLUtils.encodeProtocols(new String[] {new String(newValidProtocol(256), UTF_8)});
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void protocolWithInvalidCharacterShouldThrow() {
+        SSLUtils.encodeProtocols(new String[] {"This is a bad character: €"});
+    }
+
+    @Test
+    public void encodeProtocolsShouldSucceed() {
+        byte[][] protocols = new byte[][]{
+                "protocol-1".getBytes(UTF_8),
+                "protocol-2".getBytes(UTF_8),
+                "protocol-3".getBytes(UTF_8),
+        };
+        byte[] expected = getExpectedEncodedBytes(protocols);
+        byte[] actual = SSLUtils.encodeProtocols(toStrings(protocols));
+        assertArrayEquals(expected, actual);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void decodeNullProtocolsShouldThrow() {
+        SSLUtils.decodeProtocols(null);
+    }
+
+    @Test
+    public void decodeEmptyProtocolsShouldSucceed() {
+        assertArrayEquals(EmptyArray.STRING, SSLUtils.decodeProtocols(EmptyArray.BYTE));
+    }
+
+    @Test
+    public void decodeProtocolsShouldSucceed() {
+        byte[][] protocols = new byte[][]{
+            "protocol-1".getBytes(UTF_8),
+            "protocol-2".getBytes(UTF_8),
+            "protocol-3".getBytes(UTF_8),
+        };
+        byte[] encoded = getExpectedEncodedBytes(protocols);
+        String[] strings = SSLUtils.decodeProtocols(encoded);
+        assertArrayEquals(toStrings(protocols), strings);
+    }
+
+    @Test
+    public void testGetClientKeyType() throws Exception {
+        // See http://www.ietf.org/assignments/tls-parameters/tls-parameters.xml
+        byte b = Byte.MIN_VALUE;
+        do {
+            String byteString = Byte.toString(b);
+            String keyType = SSLUtils.getClientKeyType(b);
+            switch (b) {
+                case 1:
+                    assertEquals(byteString, "RSA", keyType);
+                    break;
+                case 64:
+                    assertEquals(byteString, "EC", keyType);
+                    break;
+                default:
+                    assertNull(byteString, keyType);
+            }
+            b++;
+        } while (b != Byte.MIN_VALUE);
+    }
+
+    @Test
+    public void testGetSupportedClientKeyTypes_onlyCertTypes() throws Exception {
+        // Create an array with all possible values. Also, duplicate all values.
+        byte[] allClientCertificateTypes = new byte[512];
+        for (int i = 0; i < allClientCertificateTypes.length; i++) {
+            allClientCertificateTypes[i] = (byte) i;
+        }
+        assertEquals(new HashSet<String>(Arrays.asList("RSA", "EC")),
+                SSLUtils.getSupportedClientKeyTypes(allClientCertificateTypes, new int[0]));
+    }
+
+    @Test
+    public void testGetSupportedClientKeyTypes_onlySignatureAlgs() {
+        // Create an array with lots of values in the supported range
+        int[] allSignatureAlgTypes = new int[7 * 7];
+        int i = 0;
+        for (int upper = 0x02; upper < 0x09; upper++) {
+            for (int lower = 0x01; lower < 0x08; lower++) {
+                allSignatureAlgTypes[i++] = (upper << 8) | lower;
+            }
+        }
+        assertEquals(new HashSet<String>(Arrays.asList("RSA", "EC")),
+                SSLUtils.getSupportedClientKeyTypes(new byte[0], allSignatureAlgTypes));
+    }
+
+    @Test
+    public void testGetSupportedClientKeyTypes_intersection() {
+        assertEquals(new HashSet<String>(Arrays.asList("EC")),
+                SSLUtils.getSupportedClientKeyTypes(
+                        new byte[] { NativeConstants.TLS_CT_RSA_SIGN,
+                                NativeConstants.TLS_CT_ECDSA_SIGN },
+                        new int[] { NativeConstants.SSL_SIGN_ECDSA_SECP256R1_SHA256,
+                                NativeConstants.SSL_SIGN_ECDSA_SECP384R1_SHA384,
+                                NativeConstants.SSL_SIGN_ECDSA_SECP521R1_SHA512 }));
+    }
+
+    @Test
+    public void testGetSupportedClientKeyTypes_intersection_empty() {
+        assertEquals(new HashSet<String>(),
+                SSLUtils.getSupportedClientKeyTypes(
+                        new byte[] { NativeConstants.TLS_CT_RSA_SIGN },
+                        new int[] { NativeConstants.SSL_SIGN_ECDSA_SECP256R1_SHA256,
+                                NativeConstants.SSL_SIGN_ECDSA_SECP384R1_SHA384,
+                                NativeConstants.SSL_SIGN_ECDSA_SECP521R1_SHA512 }));
+    }
+
+    @Test
+    public void testGetSupportedClientKeyTypes_ordered() {
+        List<String> keyTypes = new ArrayList<String>(SSLUtils.getSupportedClientKeyTypes(
+                new byte[0],
+                new int[] { NativeConstants.SSL_SIGN_RSA_PKCS1_SHA1,
+                        NativeConstants.SSL_SIGN_ECDSA_SECP256R1_SHA256 }));
+        assertEquals(Arrays.asList("RSA", "EC"), keyTypes);
+
+        keyTypes = new ArrayList<String>(SSLUtils.getSupportedClientKeyTypes(
+                new byte[0],
+                new int[] { NativeConstants.SSL_SIGN_ECDSA_SECP256R1_SHA256,
+                        NativeConstants.SSL_SIGN_RSA_PKCS1_SHA1 }));
+        assertEquals(Arrays.asList("EC", "RSA"), keyTypes);
+
+        keyTypes = new ArrayList<String>(SSLUtils.getSupportedClientKeyTypes(
+                new byte[0],
+                new int[] { NativeConstants.SSL_SIGN_RSA_PKCS1_SHA512,
+                        NativeConstants.SSL_SIGN_ECDSA_SECP256R1_SHA256,
+                        NativeConstants.SSL_SIGN_RSA_PKCS1_SHA1 }));
+        assertEquals(Arrays.asList("RSA", "EC"), keyTypes);
+
+        keyTypes = new ArrayList<String>(SSLUtils.getSupportedClientKeyTypes(
+                new byte[0],
+                new int[] { NativeConstants.SSL_SIGN_ECDSA_SECP256R1_SHA256,
+                        NativeConstants.SSL_SIGN_RSA_PKCS1_SHA1,
+                        NativeConstants.SSL_SIGN_ECDSA_SECP521R1_SHA512 }));
+        assertEquals(Arrays.asList("EC", "RSA"), keyTypes);
+    }
+
+    private static String[] toStrings(byte[][] protocols) {
+        int numProtocols = protocols.length;
+        String[] out = new String[numProtocols];
+        for(int i = 0; i < numProtocols; ++i) {
+            out[i] = new String(protocols[i], UTF_8);
+        }
+        return out;
+    }
+
+    private static byte[] getExpectedEncodedBytes(byte[][] protocols) {
+        int numProtocols = protocols.length;
+        int encodedLength = numProtocols;
+        for (byte[] protocol : protocols) {
+            encodedLength += protocol.length;
+        }
+        byte[] encoded = new byte[encodedLength];
+        for(int encodedIndex = 0, i = 0; i < numProtocols; ++i) {
+            byte[] protocol = protocols[i];
+            encoded[encodedIndex++] = (byte) protocol.length;
+            System.arraycopy(protocol, 0, encoded, encodedIndex, protocol.length);
+            encodedIndex += protocol.length;
+        }
+        return encoded;
+    }
+
+    private static byte[] newValidProtocol(int length) {
+        byte[] chars = new byte[length];
+        for (int i = 0; i < length; ++i) {
+            int charIndex = i % VALID_CHARACTERS.length;
+            chars[i] = VALID_CHARACTERS[charIndex];
+        }
+        return chars;
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ServerSessionContextTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ServerSessionContextTest.java
new file mode 100644
index 0000000..c5986cc
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ServerSessionContextTest.java
@@ -0,0 +1,50 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.util.Enumeration;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@RunWith(JUnit4.class)
+public class ServerSessionContextTest extends AbstractSessionContextTest<ServerSessionContext> {
+
+    @Override
+    ServerSessionContext newContext() {
+        return new ServerSessionContext();
+    }
+
+    @Override
+    NativeSslSession getCachedSession(ServerSessionContext context, NativeSslSession s) {
+        return context.getSessionFromCache(s.getId());
+    }
+
+    @Override
+    int size(ServerSessionContext context) {
+        int count = 0;
+        Enumeration<byte[]> ids = context.getIds();
+        while (ids.hasMoreElements()) {
+            ids.nextElement();
+            count++;
+        }
+        return count;
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/TestSessionBuilder.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/TestSessionBuilder.java
new file mode 100644
index 0000000..3962c81
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/TestSessionBuilder.java
@@ -0,0 +1,171 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class TestSessionBuilder {
+    private int type;
+
+    private boolean sessionDataSet;
+    private byte[] sessionData;
+    private int sessionDataLength;
+
+    private boolean certificatesSet;
+    private ArrayList<byte[]> certificates = new ArrayList<byte[]>();
+    private int certificatesLength;
+    private ArrayList<Integer> certificateLengths = new ArrayList<Integer>();
+
+    private boolean ocspDataSet;
+    private ArrayList<byte[]> ocspDatas = new ArrayList<byte[]>();
+    private int ocspDatasLength;
+    private ArrayList<Integer> ocspDataLengths = new ArrayList<Integer>();
+
+    private boolean tlsSctDataSet;
+    private byte[] tlsSctData;
+    private int tlsSctDataLength;
+
+    public TestSessionBuilder setType(int type) {
+        this.type = type;
+        return this;
+    }
+
+    public TestSessionBuilder setSessionData(byte[] sessionData) {
+        sessionDataSet = true;
+        this.sessionData = sessionData;
+        sessionDataLength = sessionData.length;
+        return this;
+    }
+
+    public TestSessionBuilder setSessionDataLength(int sessionDataLength) {
+        assertTrue("call setSessionData first", sessionDataSet);
+        this.sessionDataLength = sessionDataLength;
+        return this;
+    }
+
+    public TestSessionBuilder addCertificate(byte[] certificate) {
+        certificatesSet = true;
+        certificates.add(certificate);
+        certificateLengths.add(certificate.length);
+        certificatesLength = certificates.size();
+        return this;
+    }
+
+    public TestSessionBuilder setCertificatesLength(int certificatesLength) {
+        assertTrue("call addCertificate first", certificatesSet);
+        this.certificatesLength = certificatesLength;
+        return this;
+    }
+
+    public TestSessionBuilder setCertificateLength(int certIndex, int certLength) {
+        assertTrue("call addCertificate first", certificatesSet);
+        certificateLengths.set(certIndex, certLength);
+        return this;
+    }
+
+    public TestSessionBuilder setOcspDataEmpty() {
+        ocspDataSet = true;
+        return this;
+    }
+
+    public TestSessionBuilder addOcspData(byte[] ocspData) {
+        ocspDataSet = true;
+        ocspDatas.add(ocspData);
+        ocspDataLengths.add(ocspData.length);
+        ocspDatasLength = ocspDatas.size();
+        return this;
+    }
+
+    public TestSessionBuilder setOcspDatasLength(int ocspDatasLength) {
+        assertTrue("Call addOcspData before setting length", ocspDataSet);
+        this.ocspDatasLength = ocspDatasLength;
+        return this;
+    }
+
+    public TestSessionBuilder setOcspDataLength(int ocspDataIndex, int ocspDataLength) {
+        assertTrue("Call addOcspData before setting length", ocspDataSet);
+        this.ocspDataLengths.set(ocspDataIndex, ocspDataLength);
+        return this;
+    }
+
+    public TestSessionBuilder setTlsSctData(byte[] tlsSctData) {
+        tlsSctDataSet = true;
+        this.tlsSctData = tlsSctData.clone();
+        tlsSctDataLength = tlsSctData.length;
+        return this;
+    }
+
+    public TestSessionBuilder setTlsSctDataLength(int tlsSctDataLength) {
+        assertTrue("Call setTlsSctData before setting length", tlsSctDataSet);
+        this.tlsSctDataLength = tlsSctDataLength;
+        return this;
+    }
+
+    public TestSessionBuilder setTlsSctDataEmpty() {
+        tlsSctDataSet = true;
+        return this;
+    }
+
+    public byte[] build() {
+        assertTrue("Must set session data", sessionDataSet);
+        assertTrue("Must call addCertificate at least once", certificatesSet);
+
+        ByteBuffer buf = ByteBuffer.allocate(4096);
+        buf.putInt(type);
+
+        buf.putInt(sessionDataLength);
+        buf.put(sessionData);
+
+        buf.putInt(certificatesLength);
+        for (int i = 0; i < certificates.size(); i++) {
+            buf.putInt(certificateLengths.get(i));
+            buf.put(certificates.get(i));
+        }
+
+        if (ocspDataSet) {
+            buf.putInt(ocspDatasLength);
+            for (int i = 0; i < ocspDatas.size(); i++) {
+                buf.putInt(ocspDataLengths.get(i));
+                buf.put(ocspDatas.get(i));
+            }
+
+            if (tlsSctDataSet) {
+                if (tlsSctData == null) {
+                    buf.putInt(0);
+                } else {
+                    buf.putInt(tlsSctDataLength);
+                    buf.put(tlsSctData);
+                }
+            }
+        } else {
+            assertFalse("If ocspData is not set, then tlsSctData must not be set", tlsSctDataSet);
+        }
+
+        buf.flip();
+        byte[] output = new byte[buf.remaining()];
+        buf.get(output);
+        return output;
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/TestSessionBuilderTest.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/TestSessionBuilderTest.java
new file mode 100644
index 0000000..624110f
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/TestSessionBuilderTest.java
@@ -0,0 +1,111 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import org.junit.Test;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class TestSessionBuilderTest {
+    @Test
+    public void buildsValidBasicSession() {
+        assertArrayEquals(new byte[] {0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x22, 0x00,
+                                  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x33},
+                new TestSessionBuilder()
+                        .setType(0x11)
+                        .setSessionData(new byte[] {0x22})
+                        .addCertificate(new byte[] {0x33})
+                        .build());
+    }
+
+    @Test
+    public void buildsValidOcspSession() {
+        assertArrayEquals(
+                new byte[] {
+                        0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x22, 0x00, 0x00, 0x00,
+                        0x01, 0x00, 0x00, 0x00, 0x01, 0x33, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+                        0x00, 0x01, 0x44,
+                },
+                new TestSessionBuilder()
+                        .setType(0x11)
+                        .setSessionData(new byte[] {0x22})
+                        .addCertificate(new byte[] {0x33})
+                        .addOcspData(new byte[] {0x44})
+                        .build());
+    }
+
+    @Test
+    public void buildsValidOcspAndTlsSctSession() {
+        assertArrayEquals(
+                new byte[] {
+                        0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x22, 0x00, 0x00, 0x00,
+                        0x01, 0x00, 0x00, 0x00, 0x01, 0x33, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+                        0x00, 0x01, 0x44, 0x00, 0x00, 0x00, 0x01, 0x55,
+                },
+                new TestSessionBuilder()
+                        .setType(0x11)
+                        .setSessionData(new byte[] {0x22})
+                        .addCertificate(new byte[] {0x33})
+                        .addOcspData(new byte[] {0x44})
+                        .setTlsSctData(new byte[] {0x55})
+                        .build());
+    }
+
+    @Test
+    public void buildsValidButEmptyOcspAndTlsSctSession() {
+        assertArrayEquals(
+                new byte[] {
+                        0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x22, 0x00, 0x00, 0x00,
+                        0x01, 0x00, 0x00, 0x00, 0x01, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                        0x00, 0x00,
+                },
+                new TestSessionBuilder()
+                        .setType(0x11)
+                        .setSessionData(new byte[] {0x22})
+                        .addCertificate(new byte[] {0x33})
+                        .setOcspDataEmpty()
+                        .setTlsSctDataEmpty()
+                        .build());
+    }
+
+    @Test
+    public void buildsInvalidOcspAndTlsSctSession() {
+        assertArrayEquals(
+                new byte[] {
+                        0x00, 0x00, 0x00, 0x11, 0x00, 0x33, 0x22, 0x11, 0x22, 0x12, 0x11, 0x22,
+                        0x34, 0x10, 0x20, 0x30, 0x40, 0x33, 0x38, 0x48, 0x18, 0x28, 0x13, 0x24,
+                        0x57, 0x68, 0x44, (byte) 0x99, (byte) 0x88, 0x77, 0x66, 0x55,
+                },
+                new TestSessionBuilder()
+                        .setType(0x11)
+                        .setSessionData(new byte[] {0x22})
+                        .setSessionDataLength(0x332211)
+                        .addCertificate(new byte[] {0x33})
+                        .setCertificatesLength(0x12112234)
+                        .setCertificateLength(0, 0x10203040)
+                        .addOcspData(new byte[] {0x44})
+                        .setOcspDatasLength(0x38481828)
+                        .setOcspDataLength(0, 0x13245768)
+                        .setTlsSctData(new byte[] {0x55})
+                        .setTlsSctDataLength(0x99887766)
+                        .build());
+    }
+}
diff --git a/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ZpenSSLX509Certificate.java b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ZpenSSLX509Certificate.java
new file mode 100644
index 0000000..256f4a4
--- /dev/null
+++ b/repackaged/openjdk/src/test/java/com/android/org/conscrypt/ZpenSSLX509Certificate.java
@@ -0,0 +1,35 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.Serializable;
+
+/**
+ * This is a fake class to test de-serialization with malicious payloads.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ZpenSSLX509Certificate implements Serializable {
+    /** This will be set via reflection in the test. */
+    private static final long serialVersionUID = 0L;
+
+    public final long mContext;
+
+    ZpenSSLX509Certificate(long ctx) {
+        mContext = ctx;
+    }
+}
diff --git a/repackaged/platform/src/main/java/com/android/org/conscrypt/CertBlacklistImpl.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/CertBlacklistImpl.java
new file mode 100644
index 0000000..e2c0773
--- /dev/null
+++ b/repackaged/platform/src/main/java/com/android/org/conscrypt/CertBlacklistImpl.java
@@ -0,0 +1,266 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public final class CertBlacklistImpl implements CertBlacklist {
+    private static final Logger logger = Logger.getLogger(CertBlacklistImpl.class.getName());
+
+    private final Set<BigInteger> serialBlacklist;
+    private final Set<byte[]> pubkeyBlacklist;
+
+    /**
+     * public for testing only.
+     */
+    public CertBlacklistImpl(Set<BigInteger> serialBlacklist, Set<byte[]> pubkeyBlacklist) {
+        this.serialBlacklist = serialBlacklist;
+        this.pubkeyBlacklist = pubkeyBlacklist;
+    }
+
+    public static CertBlacklist getDefault() {
+        String androidData = System.getenv("ANDROID_DATA");
+        String blacklistRoot = androidData + "/misc/keychain/";
+        String defaultPubkeyBlacklistPath = blacklistRoot + "pubkey_blacklist.txt";
+        String defaultSerialBlacklistPath = blacklistRoot + "serial_blacklist.txt";
+
+        Set<byte[]> pubkeyBlacklist = readPublicKeyBlackList(defaultPubkeyBlacklistPath);
+        Set<BigInteger> serialBlacklist = readSerialBlackList(defaultSerialBlacklistPath);
+        return new CertBlacklistImpl(serialBlacklist, pubkeyBlacklist);
+    }
+
+    private static boolean isHex(String value) {
+        try {
+            new BigInteger(value, 16);
+            return true;
+        } catch (NumberFormatException e) {
+            logger.log(Level.WARNING, "Could not parse hex value " + value, e);
+            return false;
+        }
+    }
+
+    private static boolean isPubkeyHash(String value) {
+        if (value.length() != 40) {
+            logger.log(Level.WARNING, "Invalid pubkey hash length: " + value.length());
+            return false;
+        }
+        return isHex(value);
+    }
+
+    private static String readBlacklist(String path) {
+        try {
+            return readFileAsString(path);
+        } catch (FileNotFoundException ignored) {
+        } catch (IOException e) {
+            logger.log(Level.WARNING, "Could not read blacklist", e);
+        }
+        return "";
+    }
+
+    // From IoUtils.readFileAsString
+    private static String readFileAsString(String path) throws IOException {
+        return readFileAsBytes(path).toString("UTF-8");
+    }
+
+    // Based on IoUtils.readFileAsBytes
+    private static ByteArrayOutputStream readFileAsBytes(String path) throws IOException {
+        RandomAccessFile f = null;
+        try {
+            f = new RandomAccessFile(path, "r");
+            ByteArrayOutputStream bytes = new ByteArrayOutputStream((int) f.length());
+            byte[] buffer = new byte[8192];
+            while (true) {
+                int byteCount = f.read(buffer);
+                if (byteCount == -1) {
+                    return bytes;
+                }
+                bytes.write(buffer, 0, byteCount);
+            }
+        } finally {
+            closeQuietly(f);
+        }
+    }
+
+    // Base on IoUtils.closeQuietly
+    private static void closeQuietly(Closeable closeable) {
+        if (closeable != null) {
+            try {
+                closeable.close();
+            } catch (RuntimeException rethrown) {
+                throw rethrown;
+            } catch (Exception ignored) {
+            }
+        }
+    }
+
+    private static Set<BigInteger> readSerialBlackList(String path) {
+
+        /* Start out with a base set of known bad values.
+         *
+         * WARNING: Do not add short serials to this list!
+         *
+         * Since this currently doesn't compare the serial + issuer, you
+         * should only add serials that have enough entropy here. Short
+         * serials may inadvertently match a certificate that was issued
+         * not in compliance with the Baseline Requirements.
+         */
+        Set<BigInteger> bl = new HashSet<BigInteger>(Arrays.asList(
+            // From http://src.chromium.org/viewvc/chrome/trunk/src/net/base/x509_certificate.cc?revision=78748&view=markup
+            // Not a real certificate. For testing only.
+            new BigInteger("077a59bcd53459601ca6907267a6dd1c", 16),
+            new BigInteger("047ecbe9fca55f7bd09eae36e10cae1e", 16),
+            new BigInteger("d8f35f4eb7872b2dab0692e315382fb0", 16),
+            new BigInteger("b0b7133ed096f9b56fae91c874bd3ac0", 16),
+            new BigInteger("9239d5348f40d1695a745470e1f23f43", 16),
+            new BigInteger("e9028b9578e415dc1a710a2b88154447", 16),
+            new BigInteger("d7558fdaf5f1105bb213282b707729a3", 16),
+            new BigInteger("f5c86af36162f13a64f54f6dc9587c06", 16),
+            new BigInteger("392a434f0e07df1f8aa305de34e0c229", 16),
+            new BigInteger("3e75ced46b693021218830ae86a82a71", 16)
+        ));
+
+        // attempt to augment it with values taken from gservices
+        String serialBlacklist = readBlacklist(path);
+        if (!serialBlacklist.equals("")) {
+            for(String value : serialBlacklist.split(",")) {
+                try {
+                    bl.add(new BigInteger(value, 16));
+                } catch (NumberFormatException e) {
+                    logger.log(Level.WARNING, "Tried to blacklist invalid serial number " + value, e);
+                }
+            }
+        }
+
+        // whether that succeeds or fails, send it on its merry way
+        return Collections.unmodifiableSet(bl);
+    }
+
+    private static Set<byte[]> readPublicKeyBlackList(String path) {
+
+        // start out with a base set of known bad values
+        Set<byte[]> bl = new HashSet<byte[]>(Arrays.asList(
+            // Blacklist test cert for CTS. The cert and key can be found in
+            // src/test/resources/blacklist_test_ca.pem and
+            // src/test/resources/blacklist_test_ca_key.pem.
+            "bae78e6bed65a2bf60ddedde7fd91e825865e93d".getBytes(UTF_8),
+            // From http://src.chromium.org/viewvc/chrome/branches/782/src/net/base/x509_certificate.cc?r1=98750&r2=98749&pathrev=98750
+            // C=NL, O=DigiNotar, CN=DigiNotar Root CA/emailAddress=info@diginotar.nl
+            "410f36363258f30b347d12ce4863e433437806a8".getBytes(UTF_8),
+            // Subject: CN=DigiNotar Cyber CA
+            // Issuer: CN=GTE CyberTrust Global Root
+            "ba3e7bd38cd7e1e6b9cd4c219962e59d7a2f4e37".getBytes(UTF_8),
+            // Subject: CN=DigiNotar Services 1024 CA
+            // Issuer: CN=Entrust.net
+            "e23b8d105f87710a68d9248050ebefc627be4ca6".getBytes(UTF_8),
+            // Subject: CN=DigiNotar PKIoverheid CA Organisatie - G2
+            // Issuer: CN=Staat der Nederlanden Organisatie CA - G2
+            "7b2e16bc39bcd72b456e9f055d1de615b74945db".getBytes(UTF_8),
+            // Subject: CN=DigiNotar PKIoverheid CA Overheid en Bedrijven
+            // Issuer: CN=Staat der Nederlanden Overheid CA
+            "e8f91200c65cee16e039b9f883841661635f81c5".getBytes(UTF_8),
+            // From http://src.chromium.org/viewvc/chrome?view=rev&revision=108479
+            // Subject: O=Digicert Sdn. Bhd.
+            // Issuer: CN=GTE CyberTrust Global Root
+            "0129bcd5b448ae8d2496d1c3e19723919088e152".getBytes(UTF_8),
+            // Subject: CN=e-islem.kktcmerkezbankasi.org/emailAddress=ileti@kktcmerkezbankasi.org
+            // Issuer: CN=T\xC3\x9CRKTRUST Elektronik Sunucu Sertifikas\xC4\xB1 Hizmetleri
+            "5f3ab33d55007054bc5e3e5553cd8d8465d77c61".getBytes(UTF_8),
+            // Subject: CN=*.EGO.GOV.TR 93
+            // Issuer: CN=T\xC3\x9CRKTRUST Elektronik Sunucu Sertifikas\xC4\xB1 Hizmetleri
+            "783333c9687df63377efceddd82efa9101913e8e".getBytes(UTF_8),
+            // Subject: Subject: C=FR, O=DG Tr\xC3\xA9sor, CN=AC DG Tr\xC3\xA9sor SSL
+            // Issuer: C=FR, O=DGTPE, CN=AC DGTPE Signature Authentification
+            "3ecf4bbbe46096d514bb539bb913d77aa4ef31bf".getBytes(UTF_8)
+        ));
+
+        // attempt to augment it with values taken from gservices
+        String pubkeyBlacklist = readBlacklist(path);
+        if (!pubkeyBlacklist.equals("")) {
+            for (String value : pubkeyBlacklist.split(",")) {
+                value = value.trim();
+                if (isPubkeyHash(value)) {
+                    bl.add(value.getBytes(UTF_8));
+                } else {
+                    logger.log(Level.WARNING, "Tried to blacklist invalid pubkey " + value);
+                }
+            }
+        }
+
+        return bl;
+    }
+
+    @Override
+    public boolean isPublicKeyBlackListed(PublicKey publicKey) {
+        byte[] encoded = publicKey.getEncoded();
+        MessageDigest md;
+        try {
+            md = MessageDigest.getInstance("SHA1");
+        } catch (GeneralSecurityException e) {
+            logger.log(Level.SEVERE, "Unable to get SHA1 MessageDigest", e);
+            return false;
+        }
+        byte[] out = toHex(md.digest(encoded));
+        for (byte[] blacklisted : pubkeyBlacklist) {
+            if (Arrays.equals(blacklisted, out)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static final byte[] HEX_TABLE = { (byte) '0', (byte) '1', (byte) '2', (byte) '3',
+        (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a',
+        (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f'};
+
+    private static byte[] toHex(byte[] in) {
+        byte[] out = new byte[in.length * 2];
+        int outIndex = 0;
+        for (int i = 0; i < in.length; i++) {
+            int value = in[i] & 0xff;
+            out[outIndex++] = HEX_TABLE[value >> 4];
+            out[outIndex++] = HEX_TABLE[value & 0xf];
+        }
+        return out;
+    }
+
+    @Override
+    public boolean isSerialNumberBlackListed(BigInteger serial) {
+        return serialBlacklist.contains(serial);
+    }
+
+}
diff --git a/repackaged/platform/src/main/java/com/android/org/conscrypt/Hex.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/Hex.java
new file mode 100644
index 0000000..cadbf44
--- /dev/null
+++ b/repackaged/platform/src/main/java/com/android/org/conscrypt/Hex.java
@@ -0,0 +1,54 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+/**
+ * Helper class for dealing with hexadecimal strings.
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+// public for testing by TrustedCertificateStoreTest
+// TODO(nathanmittler): Move to InternalUtil?
+public final class Hex {
+    private Hex() {}
+
+    private final static char[] DIGITS = {
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+    public static String bytesToHexString(byte[] bytes) {
+        char[] buf = new char[bytes.length * 2];
+        int c = 0;
+        for (byte b : bytes) {
+            buf[c++] = DIGITS[(b >> 4) & 0xf];
+            buf[c++] = DIGITS[b & 0xf];
+        }
+        return new String(buf);
+    }
+
+    public static String intToHexString(int i, int minWidth) {
+        int bufLen = 8;  // Max number of hex digits in an int
+        char[] buf = new char[bufLen];
+        int cursor = bufLen;
+
+        do {
+            buf[--cursor] = DIGITS[i & 0xf];
+        } while ((i >>>= 4) != 0 || (bufLen - cursor < minWidth));
+
+        return new String(buf, cursor, bufLen - cursor);
+    }
+}
diff --git a/repackaged/platform/src/main/java/com/android/org/conscrypt/InternalUtil.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/InternalUtil.java
new file mode 100644
index 0000000..bf3f043
--- /dev/null
+++ b/repackaged/platform/src/main/java/com/android/org/conscrypt/InternalUtil.java
@@ -0,0 +1,48 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.InputStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import com.android.org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
+
+/**
+ * Helper to initialize the JNI libraries. This version runs when compiled
+ * as part of the platform.
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public final class InternalUtil {
+    public static PublicKey logKeyToPublicKey(byte[] logKey)
+            throws NoSuchAlgorithmException {
+        try {
+            return new OpenSSLKey(NativeCrypto.EVP_parse_public_key(logKey)).getPublicKey();
+        } catch (ParsingException e) {
+            throw new NoSuchAlgorithmException(e);
+        }
+    }
+
+    public static PublicKey readPublicKeyPem(InputStream pem) throws InvalidKeyException, NoSuchAlgorithmException {
+        return OpenSSLKey.fromPublicKeyPemInputStream(pem).getPublicKey();
+    }
+
+    private InternalUtil() {
+    }
+}
diff --git a/repackaged/platform/src/main/java/com/android/org/conscrypt/JSSEProvider.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/JSSEProvider.java
new file mode 100644
index 0000000..d4e2d4a
--- /dev/null
+++ b/repackaged/platform/src/main/java/com/android/org/conscrypt/JSSEProvider.java
@@ -0,0 +1,57 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.security.Provider;
+
+/**
+ * JSSE Provider implementation.
+ *
+ * The current JSSE provider implementation uses the following
+ * crypto algorithms:
+ *
+ * Algorithms that MUST be provided by crypto provider:
+ *     Mac    HmacMD5
+ *     Mac    HmacSHA1
+ *     MessageDigest    MD5
+ *     MessageDigest    SHA-1
+ *     CertificateFactory    X509
+ *
+ * Trust manager implementation requires:
+ *     CertPathValidator    PKIX
+ *     CertificateFactory    X509
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public final class JSSEProvider extends Provider {
+
+    private static final long serialVersionUID = 3075686092260669675L;
+
+    public JSSEProvider() {
+        super("HarmonyJSSE", 1.0, "Harmony JSSE Provider");
+
+        put("KeyManagerFactory.PKIX", KeyManagerFactoryImpl.class.getName());
+        put("Alg.Alias.KeyManagerFactory.X509", "PKIX");
+
+        put("TrustManagerFactory.PKIX", TrustManagerFactoryImpl.class.getName());
+        put("Alg.Alias.TrustManagerFactory.X509", "PKIX");
+
+        put("KeyStore.AndroidCAStore", TrustedCertificateKeyStoreSpi.class.getName());
+    }
+}
diff --git a/repackaged/platform/src/main/java/com/android/org/conscrypt/NativeCryptoJni.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/NativeCryptoJni.java
new file mode 100644
index 0000000..254b45c
--- /dev/null
+++ b/repackaged/platform/src/main/java/com/android/org/conscrypt/NativeCryptoJni.java
@@ -0,0 +1,31 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+/**
+ * Helper to initialize the JNI libraries. This version runs when compiled
+ * as part of the platform.
+ */
+class NativeCryptoJni {
+    public static void init() {
+        System.loadLibrary("javacrypto");
+    }
+
+    private NativeCryptoJni() {
+    }
+}
diff --git a/repackaged/platform/src/main/java/com/android/org/conscrypt/Platform.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/Platform.java
new file mode 100644
index 0000000..c24a313
--- /dev/null
+++ b/repackaged/platform/src/main/java/com/android/org/conscrypt/Platform.java
@@ -0,0 +1,516 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_SNDTIMEO;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructTimeval;
+import dalvik.system.BlockGuard;
+import dalvik.system.CloseGuard;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.SocketImpl;
+import java.security.AlgorithmParameters;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.util.Collections;
+import java.util.List;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIServerName;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.StandardConstants;
+import javax.net.ssl.X509ExtendedTrustManager;
+import javax.net.ssl.X509TrustManager;
+import libcore.net.NetworkSecurityPolicy;
+import com.android.org.conscrypt.ct.CTLogStore;
+import com.android.org.conscrypt.ct.CTLogStoreImpl;
+import com.android.org.conscrypt.ct.CTPolicy;
+import com.android.org.conscrypt.ct.CTPolicyImpl;
+import sun.security.x509.AlgorithmId;
+
+final class Platform {
+    private static class NoPreloadHolder { public static final Platform MAPPER = new Platform(); }
+
+    /**
+     * Runs all the setup for the platform that only needs to run once.
+     */
+    public static void setup() {
+        NoPreloadHolder.MAPPER.ping();
+    }
+
+    /**
+     * Just a placeholder to make sure the class is initialized.
+     */
+    private void ping() {}
+
+    private Platform() {}
+
+    /**
+     * Default name used in the {@link java.security.Security JCE system} by {@code OpenSSLProvider}
+     * if the default constructor is used.
+     */
+    // @VisibleForTesting - used by CTS
+    public static String getDefaultProviderName() {
+        return "AndroidOpenSSL";
+    }
+
+    static boolean provideTrustManagerByDefault() {
+        return false;
+    }
+
+    static FileDescriptor getFileDescriptor(Socket s) {
+        return s.getFileDescriptor$();
+    }
+
+    static FileDescriptor getFileDescriptorFromSSLSocket(AbstractConscryptSocket socket) {
+        try {
+            Field f_impl = Socket.class.getDeclaredField("impl");
+            f_impl.setAccessible(true);
+            Object socketImpl = f_impl.get(socket);
+            Field f_fd = SocketImpl.class.getDeclaredField("fd");
+            f_fd.setAccessible(true);
+            return (FileDescriptor) f_fd.get(socketImpl);
+        } catch (Exception e) {
+            throw new RuntimeException("Can't get FileDescriptor from socket", e);
+        }
+    }
+
+    static String getCurveName(ECParameterSpec spec) {
+        return spec.getCurveName();
+    }
+
+    static void setCurveName(ECParameterSpec spec, String curveName) {
+        spec.setCurveName(curveName);
+    }
+
+    static void setSocketWriteTimeout(Socket s, long timeoutMillis) throws SocketException {
+        StructTimeval tv = StructTimeval.fromMillis(timeoutMillis);
+        try {
+            Os.setsockoptTimeval(s.getFileDescriptor$(), SOL_SOCKET, SO_SNDTIMEO, tv);
+        } catch (ErrnoException errnoException) {
+            throw errnoException.rethrowAsSocketException();
+        }
+    }
+
+    static void setSSLParameters(
+            SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
+        impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
+        impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
+        List<SNIServerName> serverNames = params.getServerNames();
+        if (serverNames != null) {
+            for (SNIServerName serverName : serverNames) {
+                if (serverName.getType() == StandardConstants.SNI_HOST_NAME) {
+                    socket.setHostname(((SNIHostName) serverName).getAsciiName());
+                    break;
+                }
+            }
+        }
+        impl.setApplicationProtocols(params.getApplicationProtocols());
+    }
+
+    static void getSSLParameters(
+            SSLParameters params, SSLParametersImpl impl, AbstractConscryptSocket socket) {
+        params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
+        params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
+        if (impl.getUseSni() && AddressUtils.isValidSniHostname(socket.getHostname())) {
+            params.setServerNames(Collections.<SNIServerName>singletonList(
+                    new SNIHostName(socket.getHostname())));
+        }
+        params.setApplicationProtocols(impl.getApplicationProtocols());
+    }
+
+    static void setSSLParameters(
+            SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
+        impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
+        impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
+        List<SNIServerName> serverNames = params.getServerNames();
+        if (serverNames != null) {
+            for (SNIServerName serverName : serverNames) {
+                if (serverName.getType() == StandardConstants.SNI_HOST_NAME) {
+                    engine.setHostname(((SNIHostName) serverName).getAsciiName());
+                    break;
+                }
+            }
+        }
+        impl.setApplicationProtocols(params.getApplicationProtocols());
+    }
+
+    static void getSSLParameters(
+            SSLParameters params, SSLParametersImpl impl, ConscryptEngine engine) {
+        params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
+        params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
+        if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getHostname())) {
+            params.setServerNames(Collections.<SNIServerName>singletonList(
+                    new SNIHostName(engine.getHostname())));
+        }
+        params.setApplicationProtocols(impl.getApplicationProtocols());
+    }
+
+    /**
+     * Helper function to unify calls to the different names used for each function taking a
+     * Socket, SSLEngine, or String (legacy Android).
+     */
+    private static boolean checkTrusted(String methodName, X509TrustManager tm,
+            X509Certificate[] chain, String authType, Class<?> argumentClass,
+            Object argumentInstance) throws CertificateException {
+        // Use duck-typing to try and call the hostname-aware method if available.
+        try {
+            Method method = tm.getClass().getMethod(
+                    methodName, X509Certificate[].class, String.class, argumentClass);
+            method.invoke(tm, chain, authType, argumentInstance);
+            return true;
+        } catch (NoSuchMethodException | IllegalAccessException ignored) {
+        } catch (InvocationTargetException e) {
+            if (e.getCause() instanceof CertificateException) {
+                throw(CertificateException) e.getCause();
+            }
+            throw new RuntimeException(e.getCause());
+        }
+        return false;
+    }
+
+    static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
+            AbstractConscryptSocket socket) throws CertificateException {
+        if (tm instanceof X509ExtendedTrustManager) {
+            X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
+            x509etm.checkClientTrusted(chain, authType, socket);
+        } else if (!checkTrusted("checkClientTrusted", tm, chain, authType, Socket.class, socket)
+                && !checkTrusted("checkClientTrusted", tm, chain, authType, String.class,
+                           socket.getHandshakeSession().getPeerHost())) {
+            tm.checkClientTrusted(chain, authType);
+        }
+    }
+
+    static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
+            AbstractConscryptSocket socket) throws CertificateException {
+        if (tm instanceof X509ExtendedTrustManager) {
+            X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
+            x509etm.checkServerTrusted(chain, authType, socket);
+        } else if (!checkTrusted("checkServerTrusted", tm, chain, authType, Socket.class, socket)
+                && !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
+                           socket.getHandshakeSession().getPeerHost())) {
+            tm.checkServerTrusted(chain, authType);
+        }
+    }
+
+    static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
+            ConscryptEngine engine) throws CertificateException {
+        if (tm instanceof X509ExtendedTrustManager) {
+            X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
+            x509etm.checkClientTrusted(chain, authType, engine);
+        } else if (!checkTrusted("checkClientTrusted", tm, chain, authType, SSLEngine.class, engine)
+                && !checkTrusted("checkClientTrusted", tm, chain, authType, String.class,
+                           engine.getHandshakeSession().getPeerHost())) {
+            tm.checkClientTrusted(chain, authType);
+        }
+    }
+
+    static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain, String authType,
+            ConscryptEngine engine) throws CertificateException {
+        if (tm instanceof X509ExtendedTrustManager) {
+            X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
+            x509etm.checkServerTrusted(chain, authType, engine);
+        } else if (!checkTrusted("checkServerTrusted", tm, chain, authType, SSLEngine.class, engine)
+                && !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
+                           engine.getHandshakeSession().getPeerHost())) {
+            tm.checkServerTrusted(chain, authType);
+        }
+    }
+
+    /**
+     * Wraps an old AndroidOpenSSL key instance. This is not needed on platform
+     * builds since we didn't backport, so return null.
+     */
+    static OpenSSLKey wrapRsaKey(PrivateKey key) {
+        return null;
+    }
+
+    /**
+     * Logs to the system EventLog system.
+     */
+    static void logEvent(String message) {
+        try {
+            Class processClass = Class.forName("android.os.Process");
+            Object processInstance = processClass.newInstance();
+            Method myUidMethod = processClass.getMethod("myUid", (Class[]) null);
+            int uid = (Integer) myUidMethod.invoke(processInstance);
+
+            Class eventLogClass = Class.forName("android.util.EventLog");
+            Object eventLogInstance = eventLogClass.newInstance();
+            Method writeEventMethod = eventLogClass.getMethod(
+                    "writeEvent", new Class[] {Integer.TYPE, Object[].class});
+            writeEventMethod.invoke(eventLogInstance, 0x534e4554 /* SNET */,
+                    new Object[] {"conscrypt", uid, message});
+        } catch (Exception e) {
+            // Do not log and fail silently
+        }
+    }
+
+    static SSLEngine wrapEngine(ConscryptEngine engine) {
+        return new Java8EngineWrapper(engine);
+    }
+
+    static SSLEngine unwrapEngine(SSLEngine engine) {
+        return Java8EngineWrapper.getDelegate(engine);
+    }
+
+    static ConscryptEngineSocket createEngineSocket(SSLParametersImpl sslParameters)
+            throws IOException {
+        return new Java8EngineSocket(sslParameters);
+    }
+
+    static ConscryptEngineSocket createEngineSocket(String hostname, int port,
+            SSLParametersImpl sslParameters) throws IOException {
+        return new Java8EngineSocket(hostname, port, sslParameters);
+    }
+
+    static ConscryptEngineSocket createEngineSocket(InetAddress address, int port,
+            SSLParametersImpl sslParameters) throws IOException {
+        return new Java8EngineSocket(address, port, sslParameters);
+    }
+
+    static ConscryptEngineSocket createEngineSocket(String hostname, int port,
+            InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
+            throws IOException {
+        return new Java8EngineSocket(hostname, port, clientAddress, clientPort, sslParameters);
+    }
+
+    static ConscryptEngineSocket createEngineSocket(InetAddress address, int port,
+            InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
+            throws IOException {
+        return new Java8EngineSocket(address, port, clientAddress, clientPort, sslParameters);
+    }
+
+    static ConscryptEngineSocket createEngineSocket(Socket socket, String hostname, int port,
+            boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
+        return new Java8EngineSocket(socket, hostname, port, autoClose, sslParameters);
+    }
+
+    static ConscryptFileDescriptorSocket createFileDescriptorSocket(SSLParametersImpl sslParameters)
+            throws IOException {
+        return new Java8FileDescriptorSocket(sslParameters);
+    }
+
+    static ConscryptFileDescriptorSocket createFileDescriptorSocket(String hostname, int port,
+            SSLParametersImpl sslParameters) throws IOException {
+        return new Java8FileDescriptorSocket(hostname, port, sslParameters);
+    }
+
+    static ConscryptFileDescriptorSocket createFileDescriptorSocket(InetAddress address, int port,
+            SSLParametersImpl sslParameters) throws IOException {
+        return new Java8FileDescriptorSocket(address, port, sslParameters);
+    }
+
+    static ConscryptFileDescriptorSocket createFileDescriptorSocket(String hostname, int port,
+            InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
+            throws IOException {
+        return new Java8FileDescriptorSocket(
+                hostname, port, clientAddress, clientPort, sslParameters);
+    }
+
+    static ConscryptFileDescriptorSocket createFileDescriptorSocket(InetAddress address, int port,
+            InetAddress clientAddress, int clientPort, SSLParametersImpl sslParameters)
+            throws IOException {
+        return new Java8FileDescriptorSocket(
+                address, port, clientAddress, clientPort, sslParameters);
+    }
+
+    static ConscryptFileDescriptorSocket createFileDescriptorSocket(Socket socket, String hostname,
+            int port, boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
+        return new Java8FileDescriptorSocket(socket, hostname, port, autoClose, sslParameters);
+    }
+
+    /**
+     * Wrap the SocketFactory with the platform wrapper if needed for compatability.
+     * For the platform-bundled library we never need to wrap.
+     */
+    static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) {
+        return factory;
+    }
+
+    /**
+     * Convert from platform's GCMParameterSpec to our internal version.
+     */
+    static GCMParameters fromGCMParameterSpec(AlgorithmParameterSpec params) {
+        if (params instanceof GCMParameterSpec) {
+            GCMParameterSpec gcmParams = (GCMParameterSpec) params;
+            return new GCMParameters(gcmParams.getTLen(), gcmParams.getIV());
+        }
+        return null;
+    }
+
+    /**
+     * Convert from an opaque AlgorithmParameters to the platform's GCMParameterSpec.
+     */
+    static AlgorithmParameterSpec fromGCMParameters(AlgorithmParameters params) {
+        try {
+            return params.getParameterSpec(GCMParameterSpec.class);
+        } catch (InvalidParameterSpecException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Creates a platform version of {@code GCMParameterSpec}.
+     */
+    static AlgorithmParameterSpec toGCMParameterSpec(int tagLenInBits, byte[] iv) {
+        return new GCMParameterSpec(tagLenInBits, iv);
+    }
+
+    /*
+     * CloseGuard functions.
+     */
+
+    static CloseGuard closeGuardGet() {
+        return CloseGuard.get();
+    }
+
+    static void closeGuardOpen(Object guardObj, String message) {
+        CloseGuard guard = (CloseGuard) guardObj;
+        guard.open(message);
+    }
+
+    static void closeGuardClose(Object guardObj) {
+        CloseGuard guard = (CloseGuard) guardObj;
+        guard.close();
+    }
+
+    static void closeGuardWarnIfOpen(Object guardObj) {
+        CloseGuard guard = (CloseGuard) guardObj;
+        guard.warnIfOpen();
+    }
+
+    /*
+     * BlockGuard functions.
+     */
+
+    static void blockGuardOnNetwork() {
+        BlockGuard.getThreadPolicy().onNetwork();
+    }
+
+    /**
+     * OID to Algorithm Name mapping.
+     */
+    static String oidToAlgorithmName(String oid) {
+        try {
+            return AlgorithmId.get(oid).getName();
+        } catch (NoSuchAlgorithmException e) {
+            return oid;
+        }
+    }
+
+    /**
+     * Provides extended capabilities for the session if supported by the platform.
+     */
+    static SSLSession wrapSSLSession(ExternalSession sslSession) {
+        return new Java8ExtendedSSLSession(sslSession);
+    }
+
+    public static String getOriginalHostNameFromInetAddress(InetAddress addr) {
+        try {
+            Method getHolder = InetAddress.class.getDeclaredMethod("holder");
+            getHolder.setAccessible(true);
+
+            Method getOriginalHostName = Class.forName("java.net.InetAddress$InetAddressHolder")
+                                                 .getDeclaredMethod("getOriginalHostName");
+            getOriginalHostName.setAccessible(true);
+
+            String originalHostName = (String) getOriginalHostName.invoke(getHolder.invoke(addr));
+            if (originalHostName == null) {
+                return addr.getHostAddress();
+            }
+            return originalHostName;
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException("Failed to get originalHostName", e);
+        } catch (ClassNotFoundException ignore) {
+            // passthrough and return addr.getHostAddress()
+        } catch (IllegalAccessException ignore) {
+        } catch (NoSuchMethodException ignore) {
+        }
+        return addr.getHostAddress();
+    }
+
+    /*
+     * Pre-Java-7 backward compatibility.
+     */
+
+    static String getHostStringFromInetSocketAddress(InetSocketAddress addr) {
+        return addr.getHostString();
+    }
+
+    // The platform always has X509ExtendedTrustManager
+    static boolean supportsX509ExtendedTrustManager() {
+        return true;
+    }
+
+    static boolean isCTVerificationRequired(String hostname) {
+        return NetworkSecurityPolicy.getInstance().isCertificateTransparencyVerificationRequired(
+                hostname);
+    }
+
+    static boolean supportsConscryptCertStore() {
+        return true;
+    }
+
+    static KeyStore getDefaultCertKeyStore() throws KeyStoreException {
+        KeyStore keyStore = KeyStore.getInstance("AndroidCAStore");
+        try {
+            keyStore.load(null, null);
+        } catch (IOException | CertificateException | NoSuchAlgorithmException e) {
+            throw new KeyStoreException(e);
+        }
+        return keyStore;
+    }
+
+    static ConscryptCertStore newDefaultCertStore() {
+        return new TrustedCertificateStore();
+    }
+
+    static CertBlacklist newDefaultBlacklist() {
+        return CertBlacklistImpl.getDefault();
+    }
+
+    static CTLogStore newDefaultLogStore() {
+        return new CTLogStoreImpl();
+    }
+
+    static CTPolicy newDefaultPolicy(CTLogStore logStore) {
+        return new CTPolicyImpl(logStore, 2);
+    }
+}
diff --git a/repackaged/platform/src/main/java/com/android/org/conscrypt/TrustedCertificateKeyStoreSpi.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/TrustedCertificateKeyStoreSpi.java
new file mode 100644
index 0000000..6bfdbbd
--- /dev/null
+++ b/repackaged/platform/src/main/java/com/android/org/conscrypt/TrustedCertificateKeyStoreSpi.java
@@ -0,0 +1,132 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.KeyStoreSpi;
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+
+/**
+ * A KeyStoreSpi wrapper for the TrustedCertificateStore.
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public final class TrustedCertificateKeyStoreSpi extends KeyStoreSpi {
+
+    private final TrustedCertificateStore store = new TrustedCertificateStore();
+
+    @Override
+    public Key engineGetKey(String alias, char[] password) {
+        if (alias == null) {
+            throw new NullPointerException("alias == null");
+        }
+        return null;
+    }
+
+    @Override
+    public Certificate[] engineGetCertificateChain(String alias) {
+        if (alias == null) {
+            throw new NullPointerException("alias == null");
+        }
+        return null;
+    }
+
+    @Override
+    public Certificate engineGetCertificate(String alias) {
+        return store.getCertificate(alias);
+    }
+
+    @Override
+    public Date engineGetCreationDate(String alias) {
+        return store.getCreationDate(alias);
+    }
+
+    @Override
+    public void engineSetKeyEntry(
+            String alias, Key key, char[] password, Certificate[] chain) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void engineSetCertificateEntry(String alias, Certificate cert) {
+        if (alias == null) {
+            throw new NullPointerException("alias == null");
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void engineDeleteEntry(String alias) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Enumeration<String> engineAliases() {
+        return Collections.enumeration(store.aliases());
+    }
+
+    @Override
+    public boolean engineContainsAlias(String alias) {
+        return store.containsAlias(alias);
+    }
+
+    @Override
+    public int engineSize() {
+        return store.aliases().size();
+    }
+
+    @Override
+    public boolean engineIsKeyEntry(String alias) {
+        if (alias == null) {
+            throw new NullPointerException("alias == null");
+        }
+        return false;
+    }
+
+    @Override
+    public boolean engineIsCertificateEntry(String alias) {
+        return engineContainsAlias(alias);
+    }
+
+    @Override
+    public String engineGetCertificateAlias(Certificate c) {
+        return store.getCertificateAlias(c);
+    }
+
+    @Override
+    public void engineStore(OutputStream stream, char[] password) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void engineLoad(InputStream stream, char[] password) {
+        if (stream != null) {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/repackaged/platform/src/main/java/com/android/org/conscrypt/TrustedCertificateStore.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/TrustedCertificateStore.java
new file mode 100644
index 0000000..7296890
--- /dev/null
+++ b/repackaged/platform/src/main/java/com/android/org/conscrypt/TrustedCertificateStore.java
@@ -0,0 +1,691 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import javax.security.auth.x500.X500Principal;
+import com.android.org.conscrypt.io.IoUtils;
+
+/**
+ * A source for trusted root certificate authority (CA) certificates
+ * supporting an immutable system CA directory along with mutable
+ * directories allowing the user addition of custom CAs and user
+ * removal of system CAs. This store supports the {@code
+ * TrustedCertificateKeyStoreSpi} wrapper to allow a traditional
+ * KeyStore interface for use with {@link
+ * javax.net.ssl.TrustManagerFactory.init}.
+ *
+ * <p>The CAs are accessed via {@code KeyStore} style aliases. Aliases
+ * are made up of a prefix identifying the source ("system:" vs
+ * "user:") and a suffix based on the OpenSSL X509_NAME_hash_old
+ * function of the CA's subject name. For example, the system CA for
+ * "C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification
+ * Authority" could be represented as "system:7651b327.0". By using
+ * the subject hash, operations such as {@link #getCertificateAlias
+ * getCertificateAlias} can be implemented efficiently without
+ * scanning the entire store.
+ *
+ * <p>In addition to supporting the {@code
+ * TrustedCertificateKeyStoreSpi} implementation, {@code
+ * TrustedCertificateStore} also provides the additional public
+ * methods {@link #isTrustAnchor} and {@link #findIssuer} to allow
+ * efficient lookup operations for CAs again based on the file naming
+ * convention.
+ *
+ * <p>The KeyChainService users the {@link installCertificate} and
+ * {@link #deleteCertificateEntry} to install user CAs as well as
+ * delete those user CAs as well as system CAs. The deletion of system
+ * CAs is performed by placing an exact copy of that CA in the deleted
+ * directory. Such deletions are intended to persist across upgrades
+ * but not intended to mask a CA with a matching name or public key
+ * but is otherwise reissued in a system update. Reinstalling a
+ * deleted system certificate simply removes the copy from the deleted
+ * directory, reenabling the original in the system directory.
+ *
+ * <p>Note that the default mutable directory is created by init via
+ * configuration in the system/core/rootdir/init.rc file. The
+ * directive "mkdir /data/misc/keychain 0775 system system"
+ * ensures that its owner and group are the system uid and system
+ * gid and that it is world readable but only writable by the system
+ * user.
+ * @hide This class is not part of the Android public SDK API
+ */
+@libcore.api.CorePlatformApi
+@Internal
+public class TrustedCertificateStore implements ConscryptCertStore {
+
+    private static final String PREFIX_SYSTEM = "system:";
+    private static final String PREFIX_USER = "user:";
+
+    public static final boolean isSystem(String alias) {
+        return alias.startsWith(PREFIX_SYSTEM);
+    }
+    @libcore.api.CorePlatformApi
+    public static final boolean isUser(String alias) {
+        return alias.startsWith(PREFIX_USER);
+    }
+
+    private static class PreloadHolder {
+        private static File defaultCaCertsSystemDir;
+        private static File defaultCaCertsAddedDir;
+        private static File defaultCaCertsDeletedDir;
+
+        static {
+            String ANDROID_ROOT = System.getenv("ANDROID_ROOT");
+            String ANDROID_DATA = System.getenv("ANDROID_DATA");
+            defaultCaCertsSystemDir = new File(ANDROID_ROOT + "/etc/security/cacerts");
+            setDefaultUserDirectory(new File(ANDROID_DATA + "/misc/keychain"));
+        }
+    }
+
+    private static final CertificateFactory CERT_FACTORY;
+    static {
+        try {
+            CERT_FACTORY = CertificateFactory.getInstance("X509");
+        } catch (CertificateException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    @libcore.api.CorePlatformApi
+    public static void setDefaultUserDirectory(File root) {
+        PreloadHolder.defaultCaCertsAddedDir = new File(root, "cacerts-added");
+        PreloadHolder.defaultCaCertsDeletedDir = new File(root, "cacerts-removed");
+    }
+
+    private final File systemDir;
+    private final File addedDir;
+    private final File deletedDir;
+
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    public TrustedCertificateStore() {
+        this(PreloadHolder.defaultCaCertsSystemDir, PreloadHolder.defaultCaCertsAddedDir,
+                PreloadHolder.defaultCaCertsDeletedDir);
+    }
+
+    public TrustedCertificateStore(File systemDir, File addedDir, File deletedDir) {
+        this.systemDir = systemDir;
+        this.addedDir = addedDir;
+        this.deletedDir = deletedDir;
+    }
+
+    @libcore.api.CorePlatformApi
+    public Certificate getCertificate(String alias) {
+        return getCertificate(alias, false);
+    }
+
+    @libcore.api.CorePlatformApi
+    public Certificate getCertificate(String alias, boolean includeDeletedSystem) {
+
+        File file = fileForAlias(alias);
+        if (file == null || (isUser(alias) && isTombstone(file))) {
+            return null;
+        }
+        X509Certificate cert = readCertificate(file);
+        if (cert == null || (isSystem(alias)
+                             && !includeDeletedSystem
+                             && isDeletedSystemCertificate(cert))) {
+            // skip malformed certs as well as deleted system ones
+            return null;
+        }
+        return cert;
+    }
+
+    private File fileForAlias(String alias) {
+        if (alias == null) {
+            throw new NullPointerException("alias == null");
+        }
+        File file;
+        if (isSystem(alias)) {
+            file = new File(systemDir, alias.substring(PREFIX_SYSTEM.length()));
+        } else if (isUser(alias)) {
+            file = new File(addedDir, alias.substring(PREFIX_USER.length()));
+        } else {
+            return null;
+        }
+        if (!file.exists() || isTombstone(file)) {
+            // silently elide tombstones
+            return null;
+        }
+        return file;
+    }
+
+    private boolean isTombstone(File file) {
+        return file.length() == 0;
+    }
+
+    private X509Certificate readCertificate(File file) {
+        if (!file.isFile()) {
+            return null;
+        }
+        InputStream is = null;
+        try {
+            is = new BufferedInputStream(new FileInputStream(file));
+            return (X509Certificate) CERT_FACTORY.generateCertificate(is);
+        } catch (IOException e) {
+            return null;
+        } catch (CertificateException e) {
+            // reading a cert while its being installed can lead to this.
+            // just pretend like its not available yet.
+            return null;
+        } finally {
+            IoUtils.closeQuietly(is);
+        }
+    }
+
+    private void writeCertificate(File file, X509Certificate cert)
+            throws IOException, CertificateException {
+        File dir = file.getParentFile();
+        dir.mkdirs();
+        dir.setReadable(true, false);
+        dir.setExecutable(true, false);
+        OutputStream os = null;
+        try {
+            os = new FileOutputStream(file);
+            os.write(cert.getEncoded());
+        } finally {
+            IoUtils.closeQuietly(os);
+        }
+        file.setReadable(true, false);
+    }
+
+    private boolean isDeletedSystemCertificate(X509Certificate x) {
+        return getCertificateFile(deletedDir, x).exists();
+    }
+
+    @libcore.api.CorePlatformApi
+    public Date getCreationDate(String alias) {
+        // containsAlias check ensures the later fileForAlias result
+        // was not a deleted system cert.
+        if (!containsAlias(alias)) {
+            return null;
+        }
+        File file = fileForAlias(alias);
+        if (file == null) {
+            return null;
+        }
+        long time = file.lastModified();
+        if (time == 0) {
+            return null;
+        }
+        return new Date(time);
+    }
+
+    @libcore.api.CorePlatformApi
+    public Set<String> aliases() {
+        Set<String> result = new HashSet<String>();
+        addAliases(result, PREFIX_USER, addedDir);
+        addAliases(result, PREFIX_SYSTEM, systemDir);
+        return result;
+    }
+
+    @libcore.api.CorePlatformApi
+    public Set<String> userAliases() {
+        Set<String> result = new HashSet<String>();
+        addAliases(result, PREFIX_USER, addedDir);
+        return result;
+    }
+
+    private void addAliases(Set<String> result, String prefix, File dir) {
+        String[] files = dir.list();
+        if (files == null) {
+            return;
+        }
+        for (String filename : files) {
+            String alias = prefix + filename;
+            if (containsAlias(alias)) {
+                result.add(alias);
+            }
+        }
+    }
+
+    @libcore.api.CorePlatformApi
+    public Set<String> allSystemAliases() {
+        Set<String> result = new HashSet<String>();
+        String[] files = systemDir.list();
+        if (files == null) {
+            return result;
+        }
+        for (String filename : files) {
+            String alias = PREFIX_SYSTEM + filename;
+            if (containsAlias(alias, true)) {
+                result.add(alias);
+            }
+        }
+        return result;
+    }
+
+    @libcore.api.CorePlatformApi
+    public boolean containsAlias(String alias) {
+        return containsAlias(alias, false);
+    }
+
+    private boolean containsAlias(String alias, boolean includeDeletedSystem) {
+        return getCertificate(alias, includeDeletedSystem) != null;
+    }
+
+    @libcore.api.CorePlatformApi
+    public String getCertificateAlias(Certificate c) {
+        return getCertificateAlias(c, false);
+    }
+
+    @libcore.api.CorePlatformApi
+    public String getCertificateAlias(Certificate c, boolean includeDeletedSystem) {
+        if (c == null || !(c instanceof X509Certificate)) {
+            return null;
+        }
+        X509Certificate x = (X509Certificate) c;
+        File user = getCertificateFile(addedDir, x);
+        if (user.exists()) {
+            return PREFIX_USER + user.getName();
+        }
+        if (!includeDeletedSystem && isDeletedSystemCertificate(x)) {
+            return null;
+        }
+        File system = getCertificateFile(systemDir, x);
+        if (system.exists()) {
+            return PREFIX_SYSTEM + system.getName();
+        }
+        return null;
+    }
+
+    /**
+     * Returns true to indicate that the certificate was added by the
+     * user, false otherwise.
+     */
+    @libcore.api.CorePlatformApi
+    public boolean isUserAddedCertificate(X509Certificate cert) {
+        return getCertificateFile(addedDir, cert).exists();
+    }
+
+    /**
+     * Returns a File for where the certificate is found if it exists
+     * or where it should be installed if it does not exist. The
+     * caller can disambiguate these cases by calling {@code
+     * File.exists()} on the result.
+     *
+     * @VisibleForTesting
+     */
+    @libcore.api.CorePlatformApi
+    public File getCertificateFile(File dir, final X509Certificate x) {
+        // compare X509Certificate.getEncoded values
+        CertSelector selector = new CertSelector() {
+            @Override
+            public boolean match(X509Certificate cert) {
+                return cert.equals(x);
+            }
+        };
+        return findCert(dir, x.getSubjectX500Principal(), selector, File.class);
+    }
+
+    /**
+     * This non-{@code KeyStoreSpi} public interface is used by {@code
+     * TrustManagerImpl} to locate a CA certificate with the same name
+     * and public key as the provided {@code X509Certificate}. We
+     * match on the name and public key and not the entire certificate
+     * since a CA may be reissued with the same name and PublicKey but
+     * with other differences (for example when switching signature
+     * from md2WithRSAEncryption to SHA1withRSA)
+     */
+    @libcore.api.CorePlatformApi
+    @Override
+    public X509Certificate getTrustAnchor(final X509Certificate c) {
+        // compare X509Certificate.getPublicKey values
+        CertSelector selector = new CertSelector() {
+            @Override
+            public boolean match(X509Certificate ca) {
+                return ca.getPublicKey().equals(c.getPublicKey());
+            }
+        };
+        X509Certificate user = findCert(addedDir,
+                                        c.getSubjectX500Principal(),
+                                        selector,
+                                        X509Certificate.class);
+        if (user != null) {
+            return user;
+        }
+        X509Certificate system = findCert(systemDir,
+                                          c.getSubjectX500Principal(),
+                                          selector,
+                                          X509Certificate.class);
+        if (system != null && !isDeletedSystemCertificate(system)) {
+            return system;
+        }
+        return null;
+    }
+
+    /**
+     * This non-{@code KeyStoreSpi} public interface is used by {@code
+     * TrustManagerImpl} to locate the CA certificate that signed the
+     * provided {@code X509Certificate}.
+     */
+    @libcore.api.CorePlatformApi
+    public X509Certificate findIssuer(final X509Certificate c) {
+        // match on verified issuer of Certificate
+        CertSelector selector = new CertSelector() {
+            @Override
+            public boolean match(X509Certificate ca) {
+                try {
+                    c.verify(ca.getPublicKey());
+                    return true;
+                } catch (Exception e) {
+                    return false;
+                }
+            }
+        };
+        X500Principal issuer = c.getIssuerX500Principal();
+        X509Certificate user = findCert(addedDir, issuer, selector, X509Certificate.class);
+        if (user != null) {
+            return user;
+        }
+        X509Certificate system = findCert(systemDir, issuer, selector, X509Certificate.class);
+        if (system != null && !isDeletedSystemCertificate(system)) {
+            return system;
+        }
+        return null;
+    }
+
+    @libcore.api.CorePlatformApi
+    @Override
+    public Set<X509Certificate> findAllIssuers(final X509Certificate c) {
+        Set<X509Certificate> issuers = null;
+        CertSelector selector = new CertSelector() {
+            @Override
+            public boolean match(X509Certificate ca) {
+                try {
+                    c.verify(ca.getPublicKey());
+                    return true;
+                } catch (Exception e) {
+                    return false;
+                }
+            }
+        };
+        X500Principal issuer = c.getIssuerX500Principal();
+        Set<X509Certificate> userAddedCerts = findCert(addedDir, issuer, selector, Set.class);
+        if (userAddedCerts != null) {
+            issuers = userAddedCerts;
+        }
+        selector = new CertSelector() {
+            @Override
+            public boolean match(X509Certificate ca) {
+                try {
+                    if (isDeletedSystemCertificate(ca)) {
+                        return false;
+                    }
+                    c.verify(ca.getPublicKey());
+                    return true;
+                } catch (Exception e) {
+                    return false;
+                }
+            }
+        };
+        Set<X509Certificate> systemCerts = findCert(systemDir, issuer, selector, Set.class);
+        if (systemCerts != null) {
+            if (issuers != null) {
+                issuers.addAll(systemCerts);
+            } else {
+                issuers = systemCerts;
+            }
+        }
+        return (issuers != null) ? issuers : Collections.<X509Certificate>emptySet();
+    }
+
+    private static boolean isSelfIssuedCertificate(OpenSSLX509Certificate cert) {
+        final long ctx = cert.getContext();
+        return NativeCrypto.X509_check_issued(ctx, cert, ctx, cert) == 0;
+    }
+
+    /**
+     * Converts the {@code cert} to the internal OpenSSL X.509 format so we can
+     * run {@link NativeCrypto} methods on it.
+     */
+    private static OpenSSLX509Certificate convertToOpenSSLIfNeeded(X509Certificate cert)
+            throws CertificateException {
+        if (cert == null) {
+            return null;
+        }
+
+        if (cert instanceof OpenSSLX509Certificate) {
+            return (OpenSSLX509Certificate) cert;
+        }
+
+        try {
+            return OpenSSLX509Certificate.fromX509Der(cert.getEncoded());
+        } catch (Exception e) {
+            throw new CertificateException(e);
+        }
+    }
+
+    /**
+     * Attempt to build a certificate chain from the supplied {@code leaf}
+     * argument through the chain of issuers as high up as known. If the chain
+     * can't be completed, the most complete chain available will be returned.
+     * This means that a list with only the {@code leaf} certificate is returned
+     * if no issuer certificates could be found.
+     *
+     * @throws CertificateException if there was a problem parsing the
+     *             certificates
+     */
+    @dalvik.annotation.compat.UnsupportedAppUsage
+    @libcore.api.CorePlatformApi
+    public List<X509Certificate> getCertificateChain(X509Certificate leaf)
+            throws CertificateException {
+        final LinkedHashSet<OpenSSLX509Certificate> chain
+                = new LinkedHashSet<OpenSSLX509Certificate>();
+        OpenSSLX509Certificate cert = convertToOpenSSLIfNeeded(leaf);
+        chain.add(cert);
+
+        while (true) {
+            if (isSelfIssuedCertificate(cert)) {
+                break;
+            }
+            cert = convertToOpenSSLIfNeeded(findIssuer(cert));
+            if (cert == null || chain.contains(cert)) {
+                break;
+            }
+            chain.add(cert);
+        }
+
+        return new ArrayList<X509Certificate>(chain);
+    }
+
+    // like java.security.cert.CertSelector but with X509Certificate and without cloning
+    private static interface CertSelector {
+        public boolean match(X509Certificate cert);
+    }
+
+    private <T> T findCert(
+            File dir, X500Principal subject, CertSelector selector, Class<T> desiredReturnType) {
+
+        Set<X509Certificate> certs = null;
+        String hash = hash(subject);
+        for (int index = 0; true; index++) {
+            File file = file(dir, hash, index);
+            if (!file.isFile()) {
+                // could not find a match, no file exists, bail
+                if (desiredReturnType == Boolean.class) {
+                    return (T) Boolean.FALSE;
+                }
+                if (desiredReturnType == File.class) {
+                    // we return file so that caller that wants to
+                    // write knows what the next available has
+                    // location is
+                    return (T) file;
+                }
+                if (desiredReturnType == Set.class) {
+                    return (T) certs;
+                }
+                return null;
+            }
+            if (isTombstone(file)) {
+                continue;
+            }
+            X509Certificate cert = readCertificate(file);
+            if (cert == null) {
+                // skip problem certificates
+                continue;
+            }
+            if (selector.match(cert)) {
+                if (desiredReturnType == X509Certificate.class) {
+                    return (T) cert;
+                } else if (desiredReturnType == Boolean.class) {
+                    return (T) Boolean.TRUE;
+                } else if (desiredReturnType == File.class) {
+                    return (T) file;
+                } else if (desiredReturnType == Set.class) {
+                    if (certs == null) {
+                        certs = new HashSet<X509Certificate>();
+                    }
+                    certs.add((X509Certificate) cert);
+                } else {
+                    throw new AssertionError();
+                }
+            }
+        }
+    }
+
+    private String hash(X500Principal name) {
+        int hash = NativeCrypto.X509_NAME_hash_old(name);
+        return Hex.intToHexString(hash, 8);
+    }
+
+    private File file(File dir, String hash, int index) {
+        return new File(dir, hash + '.' + index);
+    }
+
+    /**
+     * This non-{@code KeyStoreSpi} public interface is used by the
+     * {@code KeyChainService} to install new CA certificates. It
+     * silently ignores the certificate if it already exists in the
+     * store.
+     */
+    @libcore.api.CorePlatformApi
+    public void installCertificate(X509Certificate cert) throws IOException, CertificateException {
+        if (cert == null) {
+            throw new NullPointerException("cert == null");
+        }
+        File system = getCertificateFile(systemDir, cert);
+        if (system.exists()) {
+            File deleted = getCertificateFile(deletedDir, cert);
+            if (deleted.exists()) {
+                // we have a system cert that was marked deleted.
+                // remove the deleted marker to expose the original
+                if (!deleted.delete()) {
+                    throw new IOException("Could not remove " + deleted);
+                }
+                return;
+            }
+            // otherwise we just have a dup of an existing system cert.
+            // return taking no further action.
+            return;
+        }
+        File user = getCertificateFile(addedDir, cert);
+        if (user.exists()) {
+            // we have an already installed user cert, bail.
+            return;
+        }
+        // install the user cert
+        writeCertificate(user, cert);
+    }
+
+    /**
+     * This could be considered the implementation of {@code
+     * TrustedCertificateKeyStoreSpi.engineDeleteEntry} but we
+     * consider {@code TrustedCertificateKeyStoreSpi} to be read
+     * only. Instead, this is used by the {@code KeyChainService} to
+     * delete CA certificates.
+     */
+    @libcore.api.CorePlatformApi
+    public void deleteCertificateEntry(String alias) throws IOException, CertificateException {
+        if (alias == null) {
+            return;
+        }
+        File file = fileForAlias(alias);
+        if (file == null) {
+            return;
+        }
+        if (isSystem(alias)) {
+            X509Certificate cert = readCertificate(file);
+            if (cert == null) {
+                // skip problem certificates
+                return;
+            }
+            File deleted = getCertificateFile(deletedDir, cert);
+            if (deleted.exists()) {
+                // already deleted system certificate
+                return;
+            }
+            // write copy of system cert to marked as deleted
+            writeCertificate(deleted, cert);
+            return;
+        }
+        if (isUser(alias)) {
+            // truncate the file to make a tombstone by opening and closing.
+            // we need ensure that we don't leave a gap before a valid cert.
+            new FileOutputStream(file).close();
+            removeUnnecessaryTombstones(alias);
+            return;
+        }
+        // non-existant user cert, nothing to delete
+    }
+
+    private void removeUnnecessaryTombstones(String alias) throws IOException {
+        if (!isUser(alias)) {
+            throw new AssertionError(alias);
+        }
+        int dotIndex = alias.lastIndexOf('.');
+        if (dotIndex == -1) {
+            throw new AssertionError(alias);
+        }
+
+        String hash = alias.substring(PREFIX_USER.length(), dotIndex);
+        int lastTombstoneIndex = Integer.parseInt(alias.substring(dotIndex + 1));
+
+        if (file(addedDir, hash, lastTombstoneIndex + 1).exists()) {
+            return;
+        }
+        while (lastTombstoneIndex >= 0) {
+            File file = file(addedDir, hash, lastTombstoneIndex);
+            if (!isTombstone(file)) {
+                break;
+            }
+            if (!file.delete()) {
+                throw new IOException("Could not remove " + file);
+            }
+            lastTombstoneIndex--;
+        }
+    }
+}
diff --git a/repackaged/platform/src/main/java/com/android/org/conscrypt/ct/CTLogStoreImpl.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/ct/CTLogStoreImpl.java
new file mode 100644
index 0000000..37c84c8
--- /dev/null
+++ b/repackaged/platform/src/main/java/com/android/org/conscrypt/ct/CTLogStoreImpl.java
@@ -0,0 +1,257 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Scanner;
+import java.util.Set;
+import com.android.org.conscrypt.Internal;
+import com.android.org.conscrypt.InternalUtil;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public class CTLogStoreImpl implements CTLogStore {
+    private static final Charset US_ASCII = Charset.forName("US-ASCII");
+
+    /**
+     * Thrown when parsing of a log file fails.
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class InvalidLogFileException extends Exception {
+        public InvalidLogFileException() {
+        }
+
+        public InvalidLogFileException(String message) {
+            super(message);
+        }
+
+        public InvalidLogFileException(String message, Throwable cause) {
+            super(message, cause);
+        }
+
+        public InvalidLogFileException(Throwable cause) {
+            super(cause);
+        }
+    }
+
+    private static final File defaultUserLogDir;
+    private static final File defaultSystemLogDir;
+    // Lazy loaded by CTLogStoreImpl()
+    private static volatile CTLogInfo[] defaultFallbackLogs = null;
+    static {
+        String ANDROID_DATA = System.getenv("ANDROID_DATA");
+        String ANDROID_ROOT = System.getenv("ANDROID_ROOT");
+        defaultUserLogDir = new File(ANDROID_DATA + "/misc/keychain/trusted_ct_logs/current/");
+        defaultSystemLogDir = new File(ANDROID_ROOT + "/etc/security/ct_known_logs/");
+    }
+
+    private File userLogDir;
+    private File systemLogDir;
+    private CTLogInfo[] fallbackLogs;
+
+    private HashMap<ByteBuffer, CTLogInfo> logCache = new HashMap<>();
+    private Set<ByteBuffer> missingLogCache = Collections.synchronizedSet(new HashSet<ByteBuffer>());
+
+    public CTLogStoreImpl() {
+        this(defaultUserLogDir,
+             defaultSystemLogDir,
+             getDefaultFallbackLogs());
+    }
+
+    public CTLogStoreImpl(File userLogDir, File systemLogDir, CTLogInfo[] fallbackLogs) {
+        this.userLogDir = userLogDir;
+        this.systemLogDir = systemLogDir;
+        this.fallbackLogs = fallbackLogs;
+    }
+
+    @Override
+    public CTLogInfo getKnownLog(byte[] logId) {
+        ByteBuffer buf = ByteBuffer.wrap(logId);
+        CTLogInfo log = logCache.get(buf);
+        if (log != null) {
+            return log;
+        }
+        if (missingLogCache.contains(buf)) {
+            return null;
+        }
+
+        log = findKnownLog(logId);
+        if (log != null) {
+            logCache.put(buf, log);
+        } else {
+            missingLogCache.add(buf);
+        }
+
+        return log;
+    }
+
+    private CTLogInfo findKnownLog(byte[] logId) {
+        String filename = hexEncode(logId);
+        try {
+            return loadLog(new File(userLogDir, filename));
+        } catch (InvalidLogFileException e) {
+            return null;
+        } catch (FileNotFoundException e) {}
+
+        try {
+            return loadLog(new File(systemLogDir, filename));
+        } catch (InvalidLogFileException e) {
+            return null;
+        } catch (FileNotFoundException e) {}
+
+        // If the updateable logs dont exist then use the fallback logs.
+        if (!userLogDir.exists()) {
+            for (CTLogInfo log: fallbackLogs) {
+                if (Arrays.equals(logId, log.getID())) {
+                    return log;
+                }
+            }
+        }
+        return null;
+    }
+
+    public static CTLogInfo[] getDefaultFallbackLogs() {
+        CTLogInfo[] result = defaultFallbackLogs;
+        if (result == null) {
+            // single-check idiom
+            defaultFallbackLogs = result = createDefaultFallbackLogs();
+        }
+        return result;
+    }
+
+    private static CTLogInfo[] createDefaultFallbackLogs() {
+        CTLogInfo[] logs = new CTLogInfo[KnownLogs.LOG_COUNT];
+        for (int i = 0; i < KnownLogs.LOG_COUNT; i++) {
+            try {
+                PublicKey key = InternalUtil.logKeyToPublicKey(KnownLogs.LOG_KEYS[i]);
+
+                logs[i] = new CTLogInfo(key,
+                                        KnownLogs.LOG_DESCRIPTIONS[i],
+                                        KnownLogs.LOG_URLS[i]);
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        defaultFallbackLogs = logs;
+        return logs;
+    }
+
+    /**
+     * Load a CTLogInfo from a file.
+     * @throws FileNotFoundException if the file does not exist
+     * @throws InvalidLogFileException if the file could not be parsed properly
+     * @return a CTLogInfo or null if the file is empty
+     */
+    public static CTLogInfo loadLog(File file) throws FileNotFoundException,
+                                                      InvalidLogFileException {
+        return loadLog(new FileInputStream(file));
+    }
+
+    /**
+     * Load a CTLogInfo from a textual representation. Closes {@code input} upon completion
+     * of loading.
+     *
+     * @throws InvalidLogFileException if the input could not be parsed properly
+     * @return a CTLogInfo or null if the input is empty
+     */
+    public static CTLogInfo loadLog(InputStream input) throws InvalidLogFileException {
+        final Scanner scan = new Scanner(input, "UTF-8");
+        scan.useDelimiter("\n");
+
+        String description = null;
+        String url = null;
+        String key = null;
+        try {
+            // If the scanner can't even read one token then the file must be empty/blank
+            if (!scan.hasNext()) {
+                return null;
+            }
+
+            while (scan.hasNext()) {
+                String[] parts = scan.next().split(":", 2);
+                if (parts.length < 2) {
+                    continue;
+                }
+
+                String name = parts[0];
+                String value = parts[1];
+                switch (name) {
+                    case "description":
+                        description = value;
+                        break;
+                    case "url":
+                        url = value;
+                        break;
+                    case "key":
+                        key = value;
+                        break;
+                }
+            }
+        } finally {
+            scan.close();
+        }
+
+        if (description == null || url == null || key == null) {
+            throw new InvalidLogFileException("Missing one of 'description', 'url' or 'key'");
+        }
+
+        PublicKey pubkey;
+        try {
+            pubkey = InternalUtil.readPublicKeyPem(new ByteArrayInputStream(
+                    ("-----BEGIN PUBLIC KEY-----\n" +
+                        key + "\n" +
+                        "-----END PUBLIC KEY-----").getBytes(US_ASCII)));
+        } catch (InvalidKeyException e) {
+            throw new InvalidLogFileException(e);
+        } catch (NoSuchAlgorithmException e) {
+            throw new InvalidLogFileException(e);
+        }
+
+        return new CTLogInfo(pubkey, description, url);
+    }
+
+    private final static char[] HEX_DIGITS = new char[] {
+        '0', '1', '2', '3', '4', '5', '6', '7',
+        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+    };
+
+    private static String hexEncode(byte[] data) {
+        StringBuilder sb = new StringBuilder(data.length * 2);
+        for (byte b: data) {
+            sb.append(HEX_DIGITS[(b >> 4) & 0x0f]);
+            sb.append(HEX_DIGITS[b & 0x0f]);
+        }
+        return sb.toString();
+    }
+}
diff --git a/repackaged/platform/src/main/java/com/android/org/conscrypt/ct/CTPolicyImpl.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/ct/CTPolicyImpl.java
new file mode 100644
index 0000000..4e19318
--- /dev/null
+++ b/repackaged/platform/src/main/java/com/android/org/conscrypt/ct/CTPolicyImpl.java
@@ -0,0 +1,51 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import java.security.cert.X509Certificate;
+import java.util.HashSet;
+import java.util.Set;
+import com.android.org.conscrypt.Internal;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public class CTPolicyImpl implements CTPolicy {
+    private final CTLogStore logStore;
+    private final int minimumLogCount;
+
+    public CTPolicyImpl(CTLogStore logStore, int minimumLogCount) {
+        this.logStore = logStore;
+        this.minimumLogCount = minimumLogCount;
+    }
+
+    @Override
+    public boolean doesResultConformToPolicy(CTVerificationResult result, String hostname,
+                                             X509Certificate[] chain) {
+        Set<CTLogInfo> logSet = new HashSet();
+        for (VerifiedSCT verifiedSCT: result.getValidSCTs()) {
+            CTLogInfo log = logStore.getKnownLog(verifiedSCT.sct.getLogID());
+            if (log != null) {
+                logSet.add(log);
+            }
+        }
+
+        return logSet.size() >= minimumLogCount;
+    }
+}
diff --git a/repackaged/platform/src/main/java/com/android/org/conscrypt/ct/KnownLogs.java b/repackaged/platform/src/main/java/com/android/org/conscrypt/ct/KnownLogs.java
new file mode 100644
index 0000000..7d6dca1
--- /dev/null
+++ b/repackaged/platform/src/main/java/com/android/org/conscrypt/ct/KnownLogs.java
@@ -0,0 +1,138 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file is generated by print_log_list.py
+ * https://github.com/google/certificate-transparency/blob/master/python/utilities/log_list/print_log_list.py */
+
+package com.android.org.conscrypt.ct;
+
+import com.android.org.conscrypt.Internal;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+@Internal
+public final class KnownLogs {
+    public static final int LOG_COUNT = 8;
+    public static final String[] LOG_DESCRIPTIONS = new String[] {
+        "Google 'Pilot' log",
+        "Google 'Aviator' log",
+        "DigiCert Log Server",
+        "Google 'Rocketeer' log",
+        "Certly.IO log",
+        "Izenpe log",
+        "Symantec log",
+        "Venafi log",
+    };
+    public static final String[] LOG_URLS = new String[] {
+        "ct.googleapis.com/pilot",
+        "ct.googleapis.com/aviator",
+        "ct1.digicert-ct.com/log",
+        "ct.googleapis.com/rocketeer",
+        "log.certly.io",
+        "ct.izenpe.com",
+        "ct.ws.symantec.com",
+        "ctlog.api.venafi.com",
+    };
+    public static final byte[][] LOG_KEYS = new byte[][] {
+        // Google 'Pilot' log
+        new byte[] {
+            48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72,
+            -50, 61, 3, 1, 7, 3, 66, 0, 4, 125, -88, 75, 18, 41, -128, -93, 61, -83,
+            -45, 90, 119, -72, -52, -30, -120, -77, -91, -3, -15, -45, 12, -51, 24,
+            12, -24, 65, 70, -24, -127, 1, 27, 21, -31, 75, -15, 27, 98, -35, 54, 10,
+            8, 24, -70, -19, 11, 53, -124, -48, -98, 64, 60, 45, -98, -101, -126,
+            101, -67, 31, 4, 16, 65, 76, -96
+        },
+        // Google 'Aviator' log
+        new byte[] {
+            48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72,
+            -50, 61, 3, 1, 7, 3, 66, 0, 4, -41, -12, -52, 105, -78, -28, 14, -112,
+            -93, -118, -22, 90, 112, 9, 79, -17, 19, 98, -48, -115, 73, 96, -1, 27,
+            64, 80, 7, 12, 109, 113, -122, -38, 37, 73, -115, 101, -31, 8, 13, 71,
+            52, 107, -67, 39, -68, -106, 33, 62, 52, -11, -121, 118, 49, -79, 127,
+            29, -55, -123, 59, 13, -9, 31, 63, -23
+        },
+        // DigiCert Log Server
+        new byte[] {
+            48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72,
+            -50, 61, 3, 1, 7, 3, 66, 0, 4, 2, 70, -59, -66, 27, -69, -126, 64, 22,
+            -24, -63, -46, -84, 25, 105, 19, 89, -8, -8, 112, -123, 70, 64, -71, 56,
+            -80, 35, -126, -88, 100, 76, 127, -65, -69, 52, -97, 74, 95, 40, -118,
+            -49, 25, -60, 0, -10, 54, 6, -109, 101, -19, 76, -11, -87, 33, 98, 90,
+            -40, -111, -21, 56, 36, 64, -84, -24
+        },
+        // Google 'Rocketeer' log
+        new byte[] {
+            48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72,
+            -50, 61, 3, 1, 7, 3, 66, 0, 4, 32, 91, 24, -56, 60, -63, -117, -77, 49,
+            8, 0, -65, -96, -112, 87, 43, -73, 71, -116, 111, -75, 104, -80, -114,
+            -112, 120, -23, -96, 115, -22, 79, 40, 33, 46, -100, -64, -12, 22, 27,
+            -86, -7, -43, -41, -87, -128, -61, 78, 47, 82, 60, -104, 1, 37, 70, 36,
+            37, 40, 35, 119, 45, 5, -62, 64, 122
+        },
+        // Certly.IO log
+        new byte[] {
+            48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72,
+            -50, 61, 3, 1, 7, 3, 66, 0, 4, 11, 35, -53, -123, 98, -104, 97, 72, 4,
+            115, -21, 84, 93, -13, -48, 7, -116, 45, 25, 45, -116, 54, -11, -21,
+            -113, 1, 66, 10, 124, -104, 38, 39, -63, -75, -35, -110, -109, -80, -82,
+            -8, -101, 61, 12, -40, 76, 78, 29, -7, 21, -5, 71, 104, 123, -70, 102,
+            -73, 37, -100, -48, 74, -62, 102, -37, 72
+        },
+        // Izenpe log
+        new byte[] {
+            48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72,
+            -50, 61, 3, 1, 7, 3, 66, 0, 4, 39, 100, 57, 12, 45, -36, 80, 24, -8, 33,
+            0, -94, 14, -19, 44, -22, 62, 117, -70, -97, -109, 100, 9, 0, 17, -60,
+            17, 23, -85, 92, -49, 15, 116, -84, -75, -105, -112, -109, 0, 91, -72,
+            -21, -9, 39, 61, -39, -78, 10, -127, 95, 47, 13, 117, 56, -108, 55, -103,
+            30, -10, 7, 118, -32, -18, -66
+        },
+        // Symantec log
+        new byte[] {
+            48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72,
+            -50, 61, 3, 1, 7, 3, 66, 0, 4, -106, -22, -84, 28, 70, 12, 27, 85, -36,
+            13, -4, -75, -108, 39, 70, 87, 66, 112, 58, 105, 24, -30, -65, 59, -60,
+            -37, -85, -96, -12, -74, 108, -64, 83, 63, 77, 66, 16, 51, -16, 88, -105,
+            -113, 107, -66, 114, -12, 42, -20, 28, 66, -86, 3, 47, 26, 126, 40, 53,
+            118, -103, 8, 61, 33, 20, -122
+        },
+        // Venafi log
+        new byte[] {
+            48, -126, 1, 34, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0,
+            3, -126, 1, 15, 0, 48, -126, 1, 10, 2, -126, 1, 1, 0, -94, 90, 72, 31,
+            23, 82, -107, 53, -53, -93, 91, 58, 31, 83, -126, 118, -108, -93, -1,
+            -128, -14, 28, 55, 60, -64, -79, -67, -63, 89, -117, -85, 45, 101, -109,
+            -41, -13, -32, 4, -43, -102, 111, -65, -42, 35, 118, 54, 79, 35, -103,
+            -53, 84, 40, -83, -116, 21, 75, 101, 89, 118, 65, 74, -100, -90, -9, -77,
+            59, 126, -79, -91, 73, -92, 23, 81, 108, -128, -36, 42, -112, 80, 75,
+            -120, 36, -23, -91, 18, 50, -109, 4, 72, -112, 2, -6, 95, 14, 48, -121,
+            -114, 85, 118, 5, -18, 42, 76, -50, -93, 106, 105, 9, 110, 37, -83, -126,
+            118, 15, -124, -110, -6, 56, -42, -122, 78, 36, -113, -101, -80, 114,
+            -53, -98, -30, 107, 63, -31, 109, -55, 37, 117, 35, -120, -95, 24, 88, 6,
+            35, 51, 120, -38, 0, -48, 56, -111, 103, -46, -90, 125, 39, -105, 103,
+            90, -63, -13, 47, 23, -26, -22, -46, 91, -24, -127, -51, -3, -110, 104,
+            -25, -13, 6, -16, -23, 114, -124, -18, 1, -91, -79, -40, 51, -38, -50,
+            -125, -91, -37, -57, -49, -42, 22, 126, -112, 117, 24, -65, 22, -36, 50,
+            59, 109, -115, -85, -126, 23, 31, -119, 32, -115, 29, -102, -26, 77, 35,
+            8, -33, 120, 111, -58, 5, -65, 95, -82, -108, -105, -37, 95, 100, -44,
+            -18, 22, -117, -93, -124, 108, 113, 43, -15, -85, 127, 93, 13, 50, -18,
+            4, -30, -112, -20, 65, -97, -5, 57, -63, 2, 3, 1, 0, 1
+        },
+    };
+}
diff --git a/repackaged/platform/src/test/java/com/android/org/conscrypt/CertBlacklistTest.java b/repackaged/platform/src/test/java/com/android/org/conscrypt/CertBlacklistTest.java
new file mode 100644
index 0000000..454ac30
--- /dev/null
+++ b/repackaged/platform/src/test/java/com/android/org/conscrypt/CertBlacklistTest.java
@@ -0,0 +1,127 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import javax.net.ssl.X509TrustManager;
+import junit.framework.TestCase;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class CertBlacklistTest extends TestCase {
+
+    private static final String BLACKLIST_CA = "test_blacklist_ca.pem";
+    private static final String BLACKLISTED_CHAIN = "blacklist_test_chain.pem";
+    private static final String BLACKLIST_FALLBACK_VALID_CA = "blacklist_test_valid_ca.pem";
+    private static final String BLACKLISTED_VALID_CHAIN = "blacklist_test_valid_chain.pem";
+
+    /**
+     * Ensure that the test blacklisted CA is actually blacklisted by default.
+     */
+    public void testBlacklistedPublicKey() throws Exception {
+        X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA);
+        CertBlacklist blacklist = CertBlacklistImpl.getDefault();
+        assertTrue(blacklist.isPublicKeyBlackListed(blacklistedCa.getPublicKey()));
+    }
+
+    /**
+     * Check that the blacklisted CA is rejected even if it used as a root of trust
+     */
+    public void testBlacklistedCaUntrusted() throws Exception {
+        X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA);
+        assertUntrusted(new X509Certificate[] {blacklistedCa}, getTrustManager(blacklistedCa));
+    }
+
+    /**
+     * Check that a chain that is rooted in a blacklisted trusted CA is rejected.
+     */
+    public void testBlacklistedRootOfTrust() throws Exception {
+        // Chain is leaf -> blacklisted
+        X509Certificate[] chain = loadCertificates(BLACKLISTED_CHAIN);
+        X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA);
+        assertUntrusted(chain, getTrustManager(blacklistedCa));
+    }
+
+    /** Test that the path building correctly routes around a blacklisted cert where there are
+     * other valid paths available. This prevents breakage where a cert was cross signed by a
+     * blacklisted CA but is still valid due to also being cross signed by CAs that remain trusted.
+     * Path:
+     *
+     * leaf -> intermediate -> blacklisted_ca
+     *               \
+     *                -------> trusted_ca
+     */
+    public void testBlacklistedIntermediateFallback() throws Exception {
+        X509Certificate[] chain = loadCertificates(BLACKLISTED_VALID_CHAIN);
+        X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA);
+        X509Certificate validCa = loadCertificate(BLACKLIST_FALLBACK_VALID_CA);
+        assertTrusted(chain, getTrustManager(blacklistedCa, validCa));
+        // Check that without the trusted_ca the chain is invalid (since it only chains to a
+        // blacklisted ca)
+        assertUntrusted(chain, getTrustManager(blacklistedCa));
+    }
+
+    private static X509Certificate loadCertificate(String file) throws Exception {
+        return loadCertificates(file)[0];
+    }
+
+    private static X509Certificate[] loadCertificates(String file) throws Exception {
+        CertificateFactory factory = CertificateFactory.getInstance("X.509");
+        try (InputStream is = TestUtils.openTestFile(file)) {
+            Collection<? extends Certificate> collection = factory.generateCertificates(is);
+            is.close();
+            X509Certificate[] certs = new X509Certificate[collection.size()];
+            int i = 0;
+            for (Certificate cert : collection) {
+                certs[i++] = (X509Certificate) cert;
+            }
+            return certs;
+        }
+    }
+
+    private static TrustManagerImpl getTrustManager(X509Certificate... trustedCas)
+            throws Exception {
+        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+        ks.load(null);
+        int i = 0;
+        for (X509Certificate ca : trustedCas) {
+            ks.setCertificateEntry(String.valueOf(i++), ca);
+        }
+        return new TrustManagerImpl(ks);
+    }
+
+    private static void assertTrusted(X509Certificate[] certs, X509TrustManager tm)
+            throws Exception {
+        tm.checkServerTrusted(certs, "RSA");
+    }
+
+    private static void assertUntrusted(X509Certificate[] certs, X509TrustManager tm) {
+        try {
+            tm.checkServerTrusted(certs, "RSA");
+            fail();
+        } catch (CertificateException expected) {
+        }
+    }
+}
diff --git a/repackaged/platform/src/test/java/com/android/org/conscrypt/TrustedCertificateStoreTest.java b/repackaged/platform/src/test/java/com/android/org/conscrypt/TrustedCertificateStoreTest.java
new file mode 100644
index 0000000..6ca624f
--- /dev/null
+++ b/repackaged/platform/src/test/java/com/android/org/conscrypt/TrustedCertificateStoreTest.java
@@ -0,0 +1,987 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.KeyStore;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStore.TrustedCertificateEntry;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import javax.security.auth.x500.X500Principal;
+import junit.framework.TestCase;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class TrustedCertificateStoreTest extends TestCase {
+    private static final Random tempFileRandom = new Random();
+
+    private final File dirTest = new File(System.getProperty("java.io.tmpdir", "."),
+            "cert-store-test" + tempFileRandom.nextInt());
+    private final File dirSystem = new File(dirTest, "system");
+    private final File dirAdded = new File(dirTest, "added");
+    private final File dirDeleted = new File(dirTest, "removed");
+
+    private static X509Certificate CA1;
+    private static X509Certificate CA2;
+
+    private static KeyStore.PrivateKeyEntry PRIVATE;
+    private static X509Certificate[] CHAIN;
+
+    private static X509Certificate CA3_WITH_CA1_SUBJECT;
+    private static String ALIAS_SYSTEM_CA1;
+    private static String ALIAS_SYSTEM_CA2;
+    private static String ALIAS_USER_CA1;
+    private static String ALIAS_USER_CA2;
+
+    private static String ALIAS_SYSTEM_CHAIN0;
+    private static String ALIAS_SYSTEM_CHAIN1;
+    private static String ALIAS_SYSTEM_CHAIN2;
+    private static String ALIAS_USER_CHAIN0;
+    private static String ALIAS_USER_CHAIN1;
+    private static String ALIAS_USER_CHAIN2;
+
+    private static String ALIAS_SYSTEM_CA3;
+    private static String ALIAS_SYSTEM_CA3_COLLISION;
+    private static String ALIAS_USER_CA3;
+    private static String ALIAS_USER_CA3_COLLISION;
+
+    private static X509Certificate CERTLOOP_EE;
+    private static X509Certificate CERTLOOP_CA1;
+    private static X509Certificate CERTLOOP_CA2;
+    private static String ALIAS_USER_CERTLOOP_EE;
+    private static String ALIAS_USER_CERTLOOP_CA1;
+    private static String ALIAS_USER_CERTLOOP_CA2;
+
+    private static X509Certificate MULTIPLE_ISSUERS_CA1;
+    private static X509Certificate MULTIPLE_ISSUERS_CA1_CROSS;
+    private static X509Certificate MULTIPLE_ISSUERS_CA2;
+    private static X509Certificate MULTIPLE_ISSUERS_EE;
+    private static String ALIAS_MULTIPLE_ISSUERS_CA1;
+    private static String ALIAS_MULTIPLE_ISSUERS_CA1_CROSS;
+    private static String ALIAS_MULTIPLE_ISSUERS_CA2;
+    private static String ALIAS_MULTIPLE_ISSUERS_EE;
+
+    private static X509Certificate getCa1() {
+        initCerts();
+        return CA1;
+    }
+    private static X509Certificate getCa2() {
+        initCerts();
+        return CA2;
+    }
+
+    private static KeyStore.PrivateKeyEntry getPrivate() {
+        initCerts();
+        return PRIVATE;
+    }
+    private static X509Certificate[] getChain() {
+        initCerts();
+        return CHAIN;
+    }
+
+    private static X509Certificate getCa3WithCa1Subject() {
+        initCerts();
+        return CA3_WITH_CA1_SUBJECT;
+    }
+
+    private static String getAliasSystemCa1() {
+        initCerts();
+        return ALIAS_SYSTEM_CA1;
+    }
+    private static String getAliasSystemCa2() {
+        initCerts();
+        return ALIAS_SYSTEM_CA2;
+    }
+    private static String getAliasUserCa1() {
+        initCerts();
+        return ALIAS_USER_CA1;
+    }
+    private static String getAliasUserCa2() {
+        initCerts();
+        return ALIAS_USER_CA2;
+    }
+
+    private static String getAliasSystemChain0() {
+        initCerts();
+        return ALIAS_SYSTEM_CHAIN0;
+    }
+    private static String getAliasSystemChain1() {
+        initCerts();
+        return ALIAS_SYSTEM_CHAIN1;
+    }
+    private static String getAliasSystemChain2() {
+        initCerts();
+        return ALIAS_SYSTEM_CHAIN2;
+    }
+    private static String getAliasUserChain0() {
+        initCerts();
+        return ALIAS_USER_CHAIN0;
+    }
+    private static String getAliasUserChain1() {
+        initCerts();
+        return ALIAS_USER_CHAIN1;
+    }
+    private static String getAliasUserChain2() {
+        initCerts();
+        return ALIAS_USER_CHAIN2;
+    }
+
+    private static String getAliasSystemCa3() {
+        initCerts();
+        return ALIAS_SYSTEM_CA3;
+    }
+    private static String getAliasSystemCa3Collision() {
+        initCerts();
+        return ALIAS_SYSTEM_CA3_COLLISION;
+    }
+    private static String getAliasUserCa3() {
+        initCerts();
+        return ALIAS_USER_CA3;
+    }
+    private static String getAliasUserCa3Collision() {
+        initCerts();
+        return ALIAS_USER_CA3_COLLISION;
+    }
+    private static X509Certificate getCertLoopEe() {
+        initCerts();
+        return CERTLOOP_EE;
+    }
+    private static X509Certificate getCertLoopCa1() {
+        initCerts();
+        return CERTLOOP_CA1;
+    }
+    private static X509Certificate getCertLoopCa2() {
+        initCerts();
+        return CERTLOOP_CA2;
+    }
+    private static String getAliasCertLoopEe() {
+        initCerts();
+        return ALIAS_USER_CERTLOOP_EE;
+    }
+    private static String getAliasCertLoopCa1() {
+        initCerts();
+        return ALIAS_USER_CERTLOOP_CA1;
+    }
+    private static String getAliasCertLoopCa2() {
+        initCerts();
+        return ALIAS_USER_CERTLOOP_CA2;
+    }
+    private static String getAliasMultipleIssuersCa1() {
+        initCerts();
+        return ALIAS_MULTIPLE_ISSUERS_CA1;
+    }
+    private static String getAliasMultipleIssuersCa2() {
+        initCerts();
+        return ALIAS_MULTIPLE_ISSUERS_CA2;
+    }
+    private static String getAliasMultipleIssuersCa1Cross() {
+        initCerts();
+        return ALIAS_MULTIPLE_ISSUERS_CA1_CROSS;
+    }
+    private static String getAliasMultipleIssuersEe() {
+        initCerts();
+        return ALIAS_MULTIPLE_ISSUERS_EE;
+    }
+    private static X509Certificate getMultipleIssuersCa1() {
+        initCerts();
+        return MULTIPLE_ISSUERS_CA1;
+    }
+    private static X509Certificate getMultipleIssuersCa2() {
+        initCerts();
+        return MULTIPLE_ISSUERS_CA2;
+    }
+    private static X509Certificate getMultipleIssuersCa1Cross() {
+        initCerts();
+        return MULTIPLE_ISSUERS_CA1_CROSS;
+    }
+    private static X509Certificate getMultipleIssuersEe() {
+        initCerts();
+        return MULTIPLE_ISSUERS_EE;
+    }
+
+    /**
+     * Lazily create shared test certificates.
+     */
+    private static synchronized void initCerts() {
+        if (CA1 != null) {
+            return;
+        }
+        try {
+            CA1 = TestKeyStore.getClient().getRootCertificate("RSA");
+            CA2 = TestKeyStore.getClientCA2().getRootCertificate("RSA");
+            PRIVATE = TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
+            CHAIN = (X509Certificate[]) PRIVATE.getCertificateChain();
+            CA3_WITH_CA1_SUBJECT = new TestKeyStore.Builder()
+                    .aliasPrefix("unused")
+                    .subject(CA1.getSubjectX500Principal())
+                    .ca(true)
+                    .build().getRootCertificate("RSA");
+
+
+            ALIAS_SYSTEM_CA1 = alias(false, CA1, 0);
+            ALIAS_SYSTEM_CA2 = alias(false, CA2, 0);
+            ALIAS_USER_CA1 = alias(true, CA1, 0);
+            ALIAS_USER_CA2 = alias(true, CA2, 0);
+
+            ALIAS_SYSTEM_CHAIN0 = alias(false, getChain()[0], 0);
+            ALIAS_SYSTEM_CHAIN1 = alias(false, getChain()[1], 0);
+            ALIAS_SYSTEM_CHAIN2 = alias(false, getChain()[2], 0);
+            ALIAS_USER_CHAIN0 = alias(true, getChain()[0], 0);
+            ALIAS_USER_CHAIN1 = alias(true, getChain()[1], 0);
+            ALIAS_USER_CHAIN2 = alias(true, getChain()[2], 0);
+
+            ALIAS_SYSTEM_CA3 = alias(false, CA3_WITH_CA1_SUBJECT, 0);
+            ALIAS_SYSTEM_CA3_COLLISION = alias(false, CA3_WITH_CA1_SUBJECT, 1);
+            ALIAS_USER_CA3 = alias(true, CA3_WITH_CA1_SUBJECT, 0);
+            ALIAS_USER_CA3_COLLISION = alias(true, CA3_WITH_CA1_SUBJECT, 1);
+
+            /*
+             * The construction below is to build a certificate chain that has a loop
+             * in it:
+             *
+             *   EE ---> CA1 ---> CA2 ---+
+             *            ^              |
+             *            |              |
+             *            +--------------+
+             */
+            TestKeyStore certLoopTempCa1 = new TestKeyStore.Builder()
+                    .keyAlgorithms("RSA")
+                    .aliasPrefix("certloop-ca1")
+                    .subject("CN=certloop-ca1")
+                    .ca(true)
+                    .build();
+            Certificate certLoopTempCaCert1 = ((TrustedCertificateEntry) certLoopTempCa1
+                    .getEntryByAlias("certloop-ca1-public-RSA")).getTrustedCertificate();
+            PrivateKeyEntry certLoopCaKey1 = (PrivateKeyEntry) certLoopTempCa1
+                    .getEntryByAlias("certloop-ca1-private-RSA");
+
+            TestKeyStore certLoopCa2 = new TestKeyStore.Builder()
+                    .keyAlgorithms("RSA")
+                    .aliasPrefix("certloop-ca2")
+                    .subject("CN=certloop-ca2")
+                    .rootCa(certLoopTempCaCert1)
+                    .signer(certLoopCaKey1)
+                    .ca(true)
+                    .build();
+            CERTLOOP_CA2 = (X509Certificate) ((TrustedCertificateEntry) certLoopCa2
+                    .getEntryByAlias("certloop-ca2-public-RSA")).getTrustedCertificate();
+            ALIAS_USER_CERTLOOP_CA2 = alias(true, CERTLOOP_CA2, 0);
+            PrivateKeyEntry certLoopCaKey2 = (PrivateKeyEntry) certLoopCa2
+                    .getEntryByAlias("certloop-ca2-private-RSA");
+
+            TestKeyStore certLoopCa1 = new TestKeyStore.Builder()
+                    .keyAlgorithms("RSA")
+                    .aliasPrefix("certloop-ca1")
+                    .subject("CN=certloop-ca1")
+                    .privateEntry(certLoopCaKey1)
+                    .rootCa(CERTLOOP_CA2)
+                    .signer(certLoopCaKey2)
+                    .ca(true)
+                    .build();
+            CERTLOOP_CA1 = (X509Certificate) ((TrustedCertificateEntry) certLoopCa1
+                    .getEntryByAlias("certloop-ca1-public-RSA")).getTrustedCertificate();
+            ALIAS_USER_CERTLOOP_CA1 = alias(true, CERTLOOP_CA1, 0);
+
+            TestKeyStore certLoopEe = new TestKeyStore.Builder()
+                    .keyAlgorithms("RSA")
+                    .aliasPrefix("certloop-ee")
+                    .subject("CN=certloop-ee")
+                    .rootCa(CERTLOOP_CA1)
+                    .signer(certLoopCaKey1)
+                    .build();
+            CERTLOOP_EE = (X509Certificate) ((TrustedCertificateEntry) certLoopEe
+                    .getEntryByAlias("certloop-ee-public-RSA")).getTrustedCertificate();
+            ALIAS_USER_CERTLOOP_EE = alias(true, CERTLOOP_EE, 0);
+
+            /*
+             * The construction below creates a certificate with multiple possible issuer certs.
+             *
+             *    EE ----> CA1 ---> CA2
+             *
+             *    Where CA1 also exists in a self-issued form.
+             */
+            TestKeyStore multipleIssuersCa1 = new TestKeyStore.Builder()
+                    .keyAlgorithms("RSA")
+                    .aliasPrefix("multiple-issuers-ca1")
+                    .subject("CN=multiple-issuers-ca1")
+                    .ca(true)
+                    .build();
+            MULTIPLE_ISSUERS_CA1 = (X509Certificate) ((TrustedCertificateEntry) multipleIssuersCa1
+                    .getEntryByAlias("multiple-issuers-ca1-public-RSA")).getTrustedCertificate();
+            ALIAS_MULTIPLE_ISSUERS_CA1 = alias(false, MULTIPLE_ISSUERS_CA1, 0);
+            PrivateKeyEntry multipleIssuersCa1Key = (PrivateKeyEntry) multipleIssuersCa1
+                    .getEntryByAlias("multiple-issuers-ca1-private-RSA");
+
+            TestKeyStore multipleIssuersCa2 = new TestKeyStore.Builder()
+                    .keyAlgorithms("RSA")
+                    .aliasPrefix("multiple-issuers-ca2")
+                    .subject("CN=multiple-issuers-ca2")
+                    .ca(true)
+                    .build();
+            MULTIPLE_ISSUERS_CA2 = (X509Certificate) ((TrustedCertificateEntry) multipleIssuersCa2
+                    .getEntryByAlias("multiple-issuers-ca2-public-RSA")).getTrustedCertificate();
+            ALIAS_MULTIPLE_ISSUERS_CA2 = alias(false, MULTIPLE_ISSUERS_CA2, 0);
+            PrivateKeyEntry multipleIssuersCa2Key = (PrivateKeyEntry) multipleIssuersCa2
+                    .getEntryByAlias("multiple-issuers-ca2-private-RSA");
+
+            TestKeyStore multipleIssuersCa1SignedByCa2 = new TestKeyStore.Builder()
+                    .keyAlgorithms("RSA")
+                    .aliasPrefix("multiple-issuers-ca1")
+                    .subject("CN=multiple-issuers-ca1")
+                    .privateEntry(multipleIssuersCa1Key)
+                    .rootCa(MULTIPLE_ISSUERS_CA2)
+                    .signer(multipleIssuersCa2Key)
+                    .ca(true)
+                    .build();
+            MULTIPLE_ISSUERS_CA1_CROSS =
+                    (X509Certificate) ((TrustedCertificateEntry) multipleIssuersCa1SignedByCa2
+                            .getEntryByAlias("multiple-issuers-ca1-public-RSA"))
+                    .getTrustedCertificate();
+            ALIAS_MULTIPLE_ISSUERS_CA1_CROSS = alias(false, MULTIPLE_ISSUERS_CA1_CROSS, 1);
+
+            TestKeyStore multipleIssuersEe = new TestKeyStore.Builder()
+                    .keyAlgorithms("RSA")
+                    .aliasPrefix("multiple-issuers-ee")
+                    .subject("CN=multiple-issuers-ee")
+                    .rootCa(MULTIPLE_ISSUERS_CA1)
+                    .signer(multipleIssuersCa1Key)
+                    .build();
+            MULTIPLE_ISSUERS_EE = (X509Certificate) ((TrustedCertificateEntry) multipleIssuersEe
+                    .getEntryByAlias("multiple-issuers-ee-public-RSA")).getTrustedCertificate();
+            ALIAS_MULTIPLE_ISSUERS_EE = alias(false, MULTIPLE_ISSUERS_EE, 0);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private TrustedCertificateStore store;
+
+    @Override protected void setUp() {
+        setupStore();
+    }
+
+    private void setupStore() {
+        dirSystem.mkdirs();
+        cleanStore();
+        createStore();
+    }
+
+    private void createStore() {
+        store = new TrustedCertificateStore(dirSystem, dirAdded, dirDeleted);
+    }
+
+    @Override protected void tearDown() {
+        cleanStore();
+    }
+
+    private void cleanStore() {
+        for (File dir : new File[] { dirSystem, dirAdded, dirDeleted, dirTest }) {
+            File[] files = dir.listFiles();
+            if (files == null) {
+                continue;
+            }
+            for (File file : files) {
+                assertTrue("Should delete " + file.getPath(), file.delete());
+            }
+        }
+        store = null;
+    }
+
+    private void resetStore() {
+        cleanStore();
+        setupStore();
+    }
+
+    public void testEmptyDirectories() throws Exception {
+        assertEmpty();
+    }
+
+    public void testOneSystemOneDeleted() throws Exception {
+        install(getCa1(), getAliasSystemCa1());
+        store.deleteCertificateEntry(getAliasSystemCa1());
+        assertEmpty();
+        assertDeleted(getCa1(), getAliasSystemCa1());
+    }
+
+    public void testTwoSystemTwoDeleted() throws Exception {
+        install(getCa1(), getAliasSystemCa1());
+        store.deleteCertificateEntry(getAliasSystemCa1());
+        install(getCa2(), getAliasSystemCa2());
+        store.deleteCertificateEntry(getAliasSystemCa2());
+        assertEmpty();
+        assertDeleted(getCa1(), getAliasSystemCa1());
+        assertDeleted(getCa2(), getAliasSystemCa2());
+    }
+
+    public void testPartialFileIsIgnored() throws Exception {
+        File file = file(getAliasSystemCa1());
+        file.getParentFile().mkdirs();
+        OutputStream os = new FileOutputStream(file);
+        os.write(0);
+        os.close();
+        assertTrue(file.exists());
+        assertEmpty();
+        assertTrue(file.exists());
+    }
+
+    private void assertEmpty() throws Exception {
+        try {
+            store.getCertificate(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+        assertNull(store.getCertificate(""));
+
+        try {
+            store.getCreationDate(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+        assertNull(store.getCreationDate(""));
+
+        Set<String> s = store.aliases();
+        assertNotNull(s);
+        assertTrue(s.isEmpty());
+        assertAliases();
+
+        Set<String> u = store.userAliases();
+        assertNotNull(u);
+        assertTrue(u.isEmpty());
+
+        try {
+            store.containsAlias(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+        assertFalse(store.containsAlias(""));
+
+        assertNull(store.getCertificateAlias(null));
+        assertNull(store.getCertificateAlias(getCa1()));
+
+        try {
+            store.getTrustAnchor(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+        assertNull(store.getTrustAnchor(getCa1()));
+
+        try {
+            store.findIssuer(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+        assertNull(store.findIssuer(getCa1()));
+
+        try {
+            store.installCertificate(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        store.deleteCertificateEntry(null);
+        store.deleteCertificateEntry("");
+
+        String[] userFiles = dirAdded.list();
+        assertTrue(userFiles == null || userFiles.length == 0);
+    }
+
+    public void testTwoSystem() throws Exception {
+        testTwo(getCa1(), getAliasSystemCa1(),
+                getCa2(), getAliasSystemCa2());
+    }
+
+    public void testTwoUser() throws Exception {
+        testTwo(getCa1(), getAliasUserCa1(),
+                getCa2(), getAliasUserCa2());
+    }
+
+    public void testOneSystemOneUser() throws Exception {
+        testTwo(getCa1(), getAliasSystemCa1(),
+                getCa2(), getAliasUserCa2());
+    }
+
+    public void testTwoSystemSameSubject() throws Exception {
+        testTwo(getCa1(), getAliasSystemCa1(),
+                getCa3WithCa1Subject(), getAliasSystemCa3Collision());
+    }
+
+    public void testTwoUserSameSubject() throws Exception {
+        testTwo(getCa1(), getAliasUserCa1(),
+                getCa3WithCa1Subject(), getAliasUserCa3Collision());
+
+        store.deleteCertificateEntry(getAliasUserCa1());
+        assertDeleted(getCa1(), getAliasUserCa1());
+        assertTombstone(getAliasUserCa1());
+        assertRootCa(getCa3WithCa1Subject(), getAliasUserCa3Collision());
+        assertAliases(getAliasUserCa3Collision());
+
+        store.deleteCertificateEntry(getAliasUserCa3Collision());
+        assertDeleted(getCa3WithCa1Subject(), getAliasUserCa3Collision());
+        assertNoTombstone(getAliasUserCa3Collision());
+        assertNoTombstone(getAliasUserCa1());
+        assertEmpty();
+    }
+
+    public void testOneSystemOneUserSameSubject() throws Exception {
+        testTwo(getCa1(), getAliasSystemCa1(),
+                getCa3WithCa1Subject(), getAliasUserCa3());
+        testTwo(getCa1(), getAliasUserCa1(),
+                getCa3WithCa1Subject(), getAliasSystemCa3());
+    }
+
+    private void testTwo(X509Certificate x1, String alias1,
+                         X509Certificate x2, String alias2) {
+        install(x1, alias1);
+        install(x2, alias2);
+        assertRootCa(x1, alias1);
+        assertRootCa(x2, alias2);
+        assertAliases(alias1, alias2);
+    }
+
+
+    public void testOneSystemOneUserOneDeleted() throws Exception {
+        install(getCa1(), getAliasSystemCa1());
+        store.installCertificate(getCa2());
+        store.deleteCertificateEntry(getAliasSystemCa1());
+        assertDeleted(getCa1(), getAliasSystemCa1());
+        assertRootCa(getCa2(), getAliasUserCa2());
+        assertAliases(getAliasUserCa2());
+    }
+
+    public void testOneSystemOneUserOneDeletedSameSubject() throws Exception {
+        install(getCa1(), getAliasSystemCa1());
+        store.installCertificate(getCa3WithCa1Subject());
+        store.deleteCertificateEntry(getAliasSystemCa1());
+        assertDeleted(getCa1(), getAliasSystemCa1());
+        assertRootCa(getCa3WithCa1Subject(), getAliasUserCa3());
+        assertAliases(getAliasUserCa3());
+    }
+
+    public void testUserMaskingSystem() throws Exception {
+        install(getCa1(), getAliasSystemCa1());
+        install(getCa1(), getAliasUserCa1());
+        assertMasked(getCa1(), getAliasSystemCa1());
+        assertRootCa(getCa1(), getAliasUserCa1());
+        assertAliases(getAliasSystemCa1(), getAliasUserCa1());
+    }
+
+    public void testChain() throws Exception {
+        testChain(getAliasSystemChain1(), getAliasSystemChain2());
+        testChain(getAliasSystemChain1(), getAliasUserChain2());
+        testChain(getAliasUserChain1(), getAliasSystemCa1());
+        testChain(getAliasUserChain1(), getAliasUserChain2());
+    }
+
+    private void testChain(String alias1, String alias2) throws Exception {
+        install(getChain()[1], alias1);
+        install(getChain()[2], alias2);
+        assertIntermediateCa(getChain()[1], alias1);
+        assertRootCa(getChain()[2], alias2);
+        assertAliases(alias1, alias2);
+        assertEquals(getChain()[2], store.findIssuer(getChain()[1]));
+        assertEquals(getChain()[1], store.findIssuer(getChain()[0]));
+
+        X509Certificate[] expected = getChain();
+        List<X509Certificate> actualList = store.getCertificateChain(expected[0]);
+
+        assertEquals("Generated CA list should be same length", expected.length, actualList.size());
+        for (int i = 0; i < expected.length; i++) {
+            assertEquals("Chain value should be the same for position " + i, expected[i],
+                    actualList.get(i));
+        }
+        resetStore();
+    }
+
+    public void testMissingSystemDirectory() throws Exception {
+        cleanStore();
+        createStore();
+        assertEmpty();
+    }
+
+    public void testWithExistingUserDirectories() throws Exception {
+        dirAdded.mkdirs();
+        dirDeleted.mkdirs();
+        install(getCa1(), getAliasSystemCa1());
+        assertRootCa(getCa1(), getAliasSystemCa1());
+        assertAliases(getAliasSystemCa1());
+    }
+
+    public void testIsTrustAnchorWithReissuedgetCa() throws Exception {
+        PublicKey publicKey = getPrivate().getCertificate().getPublicKey();
+        PrivateKey privateKey = getPrivate().getPrivateKey();
+        String name = "CN=CA4";
+        X509Certificate ca1 = TestKeyStore.createCa(publicKey, privateKey, name);
+        Thread.sleep(1 * 1000); // wait to ensure CAs vary by expiration
+        X509Certificate ca2 = TestKeyStore.createCa(publicKey, privateKey, name);
+        assertFalse(ca1.equals(ca2));
+
+        String systemAlias = alias(false, ca1, 0);
+        install(ca1, systemAlias);
+        assertRootCa(ca1, systemAlias);
+        assertEquals(ca1, store.getTrustAnchor(ca2));
+        assertEquals(ca1, store.findIssuer(ca2));
+        resetStore();
+
+        String userAlias = alias(true, ca1, 0);
+        store.installCertificate(ca1);
+        assertRootCa(ca1, userAlias);
+        assertNotNull(store.getTrustAnchor(ca2));
+        assertEquals(ca1, store.findIssuer(ca2));
+        resetStore();
+    }
+
+    public void testInstallEmpty() throws Exception {
+        store.installCertificate(getCa1());
+        assertRootCa(getCa1(), getAliasUserCa1());
+        assertAliases(getAliasUserCa1());
+
+        // reinstalling should not change anything
+        store.installCertificate(getCa1());
+        assertRootCa(getCa1(), getAliasUserCa1());
+        assertAliases(getAliasUserCa1());
+    }
+
+    public void testInstallEmptySystemExists() throws Exception {
+        install(getCa1(), getAliasSystemCa1());
+        assertRootCa(getCa1(), getAliasSystemCa1());
+        assertAliases(getAliasSystemCa1());
+
+        // reinstalling should not affect system CA
+        store.installCertificate(getCa1());
+        assertRootCa(getCa1(), getAliasSystemCa1());
+        assertAliases(getAliasSystemCa1());
+
+    }
+
+    public void testInstallEmptyDeletedSystemExists() throws Exception {
+        install(getCa1(), getAliasSystemCa1());
+        store.deleteCertificateEntry(getAliasSystemCa1());
+        assertEmpty();
+        assertDeleted(getCa1(), getAliasSystemCa1());
+
+        // installing should restore deleted system CA
+        store.installCertificate(getCa1());
+        assertRootCa(getCa1(), getAliasSystemCa1());
+        assertAliases(getAliasSystemCa1());
+    }
+
+    public void testDeleteEmpty() throws Exception {
+        store.deleteCertificateEntry(getAliasSystemCa1());
+        assertEmpty();
+        assertDeleted(getCa1(), getAliasSystemCa1());
+    }
+
+    public void testDeleteUser() throws Exception {
+        store.installCertificate(getCa1());
+        assertRootCa(getCa1(), getAliasUserCa1());
+        assertAliases(getAliasUserCa1());
+
+        store.deleteCertificateEntry(getAliasUserCa1());
+        assertEmpty();
+        assertDeleted(getCa1(), getAliasUserCa1());
+        assertNoTombstone(getAliasUserCa1());
+    }
+
+    public void testDeleteSystem() throws Exception {
+        install(getCa1(), getAliasSystemCa1());
+        assertRootCa(getCa1(), getAliasSystemCa1());
+        assertAliases(getAliasSystemCa1());
+
+        store.deleteCertificateEntry(getAliasSystemCa1());
+        assertEmpty();
+        assertDeleted(getCa1(), getAliasSystemCa1());
+
+        // deleting again should not change anything
+        store.deleteCertificateEntry(getAliasSystemCa1());
+        assertEmpty();
+        assertDeleted(getCa1(), getAliasSystemCa1());
+    }
+
+    public void testGetLoopedCert() throws Exception {
+        install(getCertLoopEe(), getAliasCertLoopEe());
+        install(getCertLoopCa1(), getAliasCertLoopCa1());
+        install(getCertLoopCa2(), getAliasCertLoopCa2());
+
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        Future<List<X509Certificate>> future = executor
+                .submit(new Callable<List<X509Certificate>>() {
+                    @Override
+                    public List<X509Certificate> call() throws Exception {
+                        return store.getCertificateChain(getCertLoopEe());
+                    }
+                });
+        executor.shutdown();
+        final List<X509Certificate> certs;
+        try {
+            certs = future.get(10, TimeUnit.SECONDS);
+        } catch (TimeoutException e) {
+            fail("Could not finish building chain; possibly confused by loops");
+            return; // Not actually reached.
+        }
+        assertEquals(3, certs.size());
+        assertEquals(getCertLoopEe(), certs.get(0));
+        assertEquals(getCertLoopCa1(), certs.get(1));
+        assertEquals(getCertLoopCa2(), certs.get(2));
+    }
+
+    public void testIsUserAddedCertificate() throws Exception {
+        assertFalse(store.isUserAddedCertificate(getCa1()));
+        assertFalse(store.isUserAddedCertificate(getCa2()));
+        install(getCa1(), getAliasSystemCa1());
+        assertFalse(store.isUserAddedCertificate(getCa1()));
+        assertFalse(store.isUserAddedCertificate(getCa2()));
+        install(getCa1(), getAliasUserCa1());
+        assertTrue(store.isUserAddedCertificate(getCa1()));
+        assertFalse(store.isUserAddedCertificate(getCa2()));
+        install(getCa2(), getAliasUserCa2());
+        assertTrue(store.isUserAddedCertificate(getCa1()));
+        assertTrue(store.isUserAddedCertificate(getCa2()));
+        store.deleteCertificateEntry(getAliasUserCa1());
+        assertFalse(store.isUserAddedCertificate(getCa1()));
+        assertTrue(store.isUserAddedCertificate(getCa2()));
+        store.deleteCertificateEntry(getAliasUserCa2());
+        assertFalse(store.isUserAddedCertificate(getCa1()));
+        assertFalse(store.isUserAddedCertificate(getCa2()));
+    }
+
+    public void testSystemCaCertsUseCorrectFileNames() throws Exception {
+        TrustedCertificateStore store = new TrustedCertificateStore();
+
+        // Assert that all the certificates in the system cacerts directory are stored in files with
+        // expected names.
+        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+        File dir = new File(System.getenv("ANDROID_ROOT") + "/etc/security/cacerts");
+        int systemCertFileCount = 0;
+        for (File actualFile : listFilesNoNull(dir)) {
+            if (!actualFile.isFile()) {
+                continue;
+            }
+            systemCertFileCount++;
+            X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(
+                    new ByteArrayInputStream(readFully(actualFile)));
+
+            File expectedFile = store.getCertificateFile(dir, cert);
+            assertEquals("System certificate stored in the wrong file",
+                    expectedFile.getAbsolutePath(), actualFile.getAbsolutePath());
+
+            // The two statements below indirectly assert that the certificate can be looked up
+            // from a file (hopefully the same one as the expectedFile above). As opposed to
+            // getCertifiacteFile above, these are the actual methods used when verifying chain of
+            // trust. Thus, we assert that they work as expected for all system certificates.
+            assertNotNull("Issuer certificate not found for system certificate " + actualFile,
+                    store.findIssuer(cert));
+            assertNotNull("Trust anchor not found for system certificate " + actualFile,
+                    store.getTrustAnchor(cert));
+        }
+
+        // Assert that all files corresponding to all system certs/aliases known to the store are
+        // present.
+        int systemCertAliasCount = 0;
+        for (String alias : store.aliases()) {
+            if (!TrustedCertificateStore.isSystem(alias)) {
+                continue;
+            }
+            systemCertAliasCount++;
+            // Checking that the certificate is stored in a file is extraneous given the current
+            // implementation of the class under test. We do it just in case the implementation
+            // changes.
+            X509Certificate cert = (X509Certificate) store.getCertificate(alias);
+            File expectedFile = store.getCertificateFile(dir, cert);
+            if (!expectedFile.isFile()) {
+                fail("Missing certificate file for alias " + alias
+                        + ": " + expectedFile.getAbsolutePath());
+            }
+        }
+
+        assertEquals("Number of system cert files and aliases doesn't match",
+                systemCertFileCount, systemCertAliasCount);
+    }
+
+    public void testMultipleIssuers() throws Exception {
+        Set<X509Certificate> result;
+        install(getMultipleIssuersCa1(), getAliasMultipleIssuersCa1());
+        result = store.findAllIssuers(getMultipleIssuersEe());
+        assertEquals("Unexpected number of issuers found", 1, result.size());
+        assertTrue("findAllIssuers does not contain expected issuer",
+                result.contains(getMultipleIssuersCa1()));
+        install(getMultipleIssuersCa1Cross(), getAliasMultipleIssuersCa1Cross());
+        result = store.findAllIssuers(getMultipleIssuersEe());
+        assertEquals("findAllIssuers did not return all issuers", 2, result.size());
+        assertTrue("findAllIssuers does not contain CA1",
+                result.contains(getMultipleIssuersCa1()));
+        assertTrue("findAllIssuers does not contain CA1 signed by CA2",
+                result.contains(getMultipleIssuersCa1Cross()));
+    }
+
+    private static File[] listFilesNoNull(File dir) {
+        File[] files = dir.listFiles();
+        return (files != null) ? files : new File[0];
+    }
+
+    private static byte[] readFully(File file) throws IOException {
+        InputStream in = null;
+        try {
+            in = new FileInputStream(file);
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            byte[] buf = new byte[16384];
+            int chunkSize;
+            while ((chunkSize = in.read(buf)) != -1) {
+                out.write(buf, 0, chunkSize);
+            }
+            return out.toByteArray();
+        } finally {
+            if (in != null) {
+                in.close();
+            }
+        }
+    }
+
+    private void assertRootCa(X509Certificate x, String alias) {
+        assertIntermediateCa(x, alias);
+        assertEquals(x, store.findIssuer(x));
+    }
+
+    private void assertTrusted(X509Certificate x, String alias) {
+        assertEquals(x, store.getCertificate(alias));
+        assertEquals(file(alias).lastModified(), store.getCreationDate(alias).getTime());
+        assertTrue(store.containsAlias(alias));
+        assertEquals(x, store.getTrustAnchor(x));
+    }
+
+    private void assertIntermediateCa(X509Certificate x, String alias) {
+        assertTrusted(x, alias);
+        assertEquals(alias, store.getCertificateAlias(x));
+    }
+
+    private void assertMasked(X509Certificate x, String alias) {
+        assertTrusted(x, alias);
+        assertFalse(alias.equals(store.getCertificateAlias(x)));
+    }
+
+    private void assertDeleted(X509Certificate x, String alias) {
+        assertNull(store.getCertificate(alias));
+        assertFalse(store.containsAlias(alias));
+        assertNull(store.getCertificateAlias(x));
+        assertNull(store.getTrustAnchor(x));
+        assertEquals(store.allSystemAliases().contains(alias),
+                     store.getCertificate(alias, true) != null);
+    }
+
+    private void assertTombstone(String alias) {
+        assertTrue(TrustedCertificateStore.isUser(alias));
+        File file = file(alias);
+        assertTrue(file.exists());
+        assertEquals(0, file.length());
+    }
+
+    private void assertNoTombstone(String alias) {
+        assertTrue(TrustedCertificateStore.isUser(alias));
+        assertFalse(file(alias).exists());
+    }
+
+    private void assertAliases(String... aliases) {
+        Set<String> expected = new HashSet<String>(Arrays.asList(aliases));
+        Set<String> actual = new HashSet<String>();
+        for (String alias : store.aliases()) {
+            boolean system = TrustedCertificateStore.isSystem(alias);
+            boolean user = TrustedCertificateStore.isUser(alias);
+            if (system || user) {
+                assertEquals(system, store.allSystemAliases().contains(alias));
+                assertEquals(user, store.userAliases().contains(alias));
+                actual.add(alias);
+            } else {
+                throw new AssertionError(alias);
+            }
+        }
+        assertEquals(expected, actual);
+    }
+
+    /**
+     * format a certificate alias
+     */
+    private static String alias(boolean user, X509Certificate x, int index) {
+        String prefix = user ? "user:" : "system:";
+
+        X500Principal subject = x.getSubjectX500Principal();
+        int intHash = NativeCrypto.X509_NAME_hash_old(subject);
+        String strHash = Hex.intToHexString(intHash, 8);
+
+        return prefix + strHash + '.' + index;
+    }
+
+    /**
+     * Install certificate under specified alias
+     */
+    private void install(X509Certificate x, String alias) {
+        try {
+            File file = file(alias);
+            file.getParentFile().mkdirs();
+            OutputStream out = new FileOutputStream(file);
+            out.write(x.getEncoded());
+            out.close();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Compute file for an alias
+     */
+    private File file(String alias) {
+        File dir;
+        if (TrustedCertificateStore.isSystem(alias)) {
+            dir = dirSystem;
+        } else if (TrustedCertificateStore.isUser(alias)) {
+            dir = dirAdded;
+        } else {
+            throw new IllegalArgumentException(alias);
+        }
+
+        int index = alias.lastIndexOf(":");
+        if (index == -1) {
+            throw new IllegalArgumentException(alias);
+        }
+        String filename = alias.substring(index+1);
+
+        return new File(dir, filename);
+    }
+}
diff --git a/repackaged/platform/src/test/java/com/android/org/conscrypt/ct/CTLogStoreImplTest.java b/repackaged/platform/src/test/java/com/android/org/conscrypt/ct/CTLogStoreImplTest.java
new file mode 100644
index 0000000..606c099
--- /dev/null
+++ b/repackaged/platform/src/test/java/com/android/org/conscrypt/ct/CTLogStoreImplTest.java
@@ -0,0 +1,205 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.ct;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringBufferInputStream;
+import java.security.PublicKey;
+import junit.framework.TestCase;
+import com.android.org.conscrypt.InternalUtil;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class CTLogStoreImplTest extends TestCase {
+    private static final String[] LOG_KEYS = new String[] {
+        "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmXg8sUUzwBYaWrRb+V0IopzQ6o3U" +
+        "yEJ04r5ZrRXGdpYM8K+hB0pXrGRLI0eeWz+3skXrS0IO83AhA3GpRL6s6w==",
+
+        "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAErEULmlBnX9L/+AK20hLYzPMFozYx" +
+        "pP0Wm1ylqGkPEwuDKn9DSpNSOym49SN77BLGuAXu9twOW/qT+ddIYVBEIw==",
+
+        "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEP6PGcXmjlyCBz2ZFUuUjrgbZLaEF" +
+        "gfLUkt2cEqlSbb4vTuB6WWmgC9h0L6PN6JF0CPcajpBKGlTI15242a8d4g==",
+
+        "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAER3qB0NADsP1szXxe4EagrD/ryPVh" +
+        "Y/azWbKyXcK12zhXnO8WH2U4QROVUMctFXLflIzw0EivdRN9t7UH1Od30w==",
+
+        "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY0ww9JqeJvzVtKNTPVb3JZa7s0ZV" +
+        "duH3PpshpMS5XVoPRSjSQCph6f3HjUcM3c4N2hpa8OFbrFFy37ttUrgD+A=="
+    };
+    private static final String[] LOG_FILENAMES = new String[] {
+        "df1c2ec11500945247a96168325ddc5c7959e8f7c6d388fc002e0bbd3f74d764",
+        "84f8ae3f613b13407a75fa2893b93ab03b18d86c455fe7c241ae020033216446",
+        "89baa01a445100009d8f9a238947115b30702275aafee675a7d94b6b09287619",
+        "57456bffe268e49a190dce4318456034c2b4958f3c0201bed5a366737d1e74ca",
+        "896c898ced4b8e6547fa351266caae4ca304f1c1ec2b623c2ee259c5452147b0"
+    };
+
+    private static final CTLogInfo[] LOGS;
+    private static final String[] LOGS_SERIALIZED;
+
+    static {
+        try {
+            int logCount = LOG_KEYS.length;
+            LOGS = new CTLogInfo[logCount];
+            LOGS_SERIALIZED = new String[logCount];
+            for (int i = 0; i < logCount; i++) {
+                PublicKey key = InternalUtil.readPublicKeyPem(new StringBufferInputStream(
+                    "-----BEGIN PUBLIC KEY-----\n" +
+                    LOG_KEYS[i] + "\n" +
+                    "-----END PUBLIC KEY-----\n"));
+                String description = String.format("Test Log %d", i);
+                String url = String.format("log%d.example.com", i);
+                LOGS[i] = new CTLogInfo(key, description, url);
+                LOGS_SERIALIZED[i] = String.format("description:%s\nurl:%s\nkey:%s",
+                    description, url, LOG_KEYS[i]);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /* CTLogStoreImpl loads the list of logs lazily when they are first needed
+     * to avoid any overhead when CT is disabled.
+     * This test simply forces the logs to be loaded to make sure it doesn't
+     * fail, as all of the other tests use a different log store.
+     */
+    public void test_getDefaultFallbackLogs() {
+        CTLogInfo[] knownLogs = CTLogStoreImpl.getDefaultFallbackLogs();
+        assertEquals(KnownLogs.LOG_COUNT, knownLogs.length);
+    }
+
+    public void test_loadLog() throws Exception {
+        CTLogInfo log = CTLogStoreImpl.loadLog(new StringBufferInputStream(LOGS_SERIALIZED[0]));
+        assertEquals(LOGS[0], log);
+
+        File testFile = writeFile(LOGS_SERIALIZED[0]);
+        log = CTLogStoreImpl.loadLog(testFile);
+        assertEquals(LOGS[0], log);
+
+        // Empty log file, used to mask fallback logs
+        assertEquals(null, CTLogStoreImpl.loadLog(new StringBufferInputStream("")));
+        try {
+            CTLogStoreImpl.loadLog(new StringBufferInputStream("randomgarbage"));
+            fail("InvalidLogFileException not thrown");
+        } catch (CTLogStoreImpl.InvalidLogFileException e) {}
+
+        try {
+            CTLogStoreImpl.loadLog(new File("/nonexistent"));
+            fail("FileNotFoundException not thrown");
+        } catch (FileNotFoundException e) {}
+    }
+
+    public void test_getKnownLog() throws Exception {
+        File userDir = createTempDirectory();
+        userDir.deleteOnExit();
+
+        File systemDir = createTempDirectory();
+        systemDir.deleteOnExit();
+
+        CTLogInfo[] fallback = new CTLogInfo[] { LOGS[2], LOGS[3] };
+
+        CTLogStore store = new CTLogStoreImpl(userDir, systemDir, fallback);
+
+        /* Add logs 0 and 1 to the user and system directories respectively
+         * Log 2 & 3 are part of the fallbacks
+         * But mask log 3 with an empty file in the user directory.
+         * Log 4 is not in the store
+         */
+        File log0File = new File(userDir, LOG_FILENAMES[0]);
+        File log1File = new File(systemDir, LOG_FILENAMES[1]);
+        File log3File = new File(userDir, LOG_FILENAMES[3]);
+        File log4File = new File(userDir, LOG_FILENAMES[4]);
+
+        writeFile(log0File, LOGS_SERIALIZED[0]);
+        writeFile(log1File, LOGS_SERIALIZED[1]);
+        writeFile(log3File, "");
+
+        // Logs 01 are present, log 2 is in the fallback and unused, log 3 is present but masked,
+        // log 4 is missing
+        assertEquals(LOGS[0], store.getKnownLog(LOGS[0].getID()));
+        assertEquals(LOGS[1], store.getKnownLog(LOGS[1].getID()));
+        // Fallback logs are not used if the userDir is present.
+        assertEquals(null, store.getKnownLog(LOGS[2].getID()));
+        assertEquals(null, store.getKnownLog(LOGS[3].getID()));
+        assertEquals(null, store.getKnownLog(LOGS[4].getID()));
+
+        /* Test whether CTLogStoreImpl caches properly
+         * Modify the files on the disk, the result of the store should not change
+         * Delete log 0, mask log 1, add log 4
+         */
+        log0File.delete();
+        writeFile(log1File, "");
+        writeFile(log4File, LOGS_SERIALIZED[4]);
+
+        assertEquals(LOGS[0], store.getKnownLog(LOGS[0].getID()));
+        assertEquals(LOGS[1], store.getKnownLog(LOGS[1].getID()));
+        assertEquals(null, store.getKnownLog(LOGS[4].getID()));
+
+        // Test that fallback logs are used when the userDir doesn't exist.
+        File doesntExist = new File("/doesnt/exist/");
+        store = new CTLogStoreImpl(doesntExist, doesntExist, fallback);
+        assertEquals(LOGS[2], store.getKnownLog(LOGS[2].getID()));
+        assertEquals(LOGS[3], store.getKnownLog(LOGS[3].getID()));
+    }
+
+    /**
+     * Create a temporary file and write to it.
+     * The file will be deleted on exit.
+     * @param contents The data to be written to the file
+     * @return A reference to the temporary file
+     */
+    private File writeFile(String contents) throws IOException {
+        File file = File.createTempFile("test", null);
+        file.deleteOnExit();
+        writeFile(file, contents);
+        return file;
+    }
+
+    private static void writeFile(File file, String contents) throws FileNotFoundException {
+        PrintWriter writer = new PrintWriter(
+                new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), UTF_8)),
+                false);
+        try {
+            writer.write(contents);
+        } finally {
+            writer.close();
+        }
+    }
+
+    /*
+     * This is NOT safe, as another process could create a file between delete() and mkdir()
+     * It should be fine for tests though
+     */
+    private static File createTempDirectory() throws IOException {
+        File folder = File.createTempFile("test", "");
+        folder.delete();
+        folder.mkdir();
+        return folder;
+    }
+}
+
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/ChannelType.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/ChannelType.java
new file mode 100644
index 0000000..a1a77c1
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/ChannelType.java
@@ -0,0 +1,132 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import javax.net.ServerSocketFactory;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * The type of socket to be wrapped by the Conscrypt socket.
+ * @hide This class is not part of the Android public SDK API
+ */
+@SuppressWarnings("unused")
+public enum ChannelType {
+    NONE {
+        @Override
+        SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
+                throws IOException {
+            return clientMode(factory.createSocket(address, port));
+        }
+
+        @Override
+        ServerSocket newServerSocket(SSLServerSocketFactory factory) throws IOException {
+            return factory.createServerSocket(0, 50, InetAddress.getLoopbackAddress());
+        }
+
+        @Override
+        SSLSocket accept(ServerSocket socket, SSLSocketFactory unused) throws IOException {
+            return serverMode(socket.accept());
+        }
+    },
+    NO_CHANNEL {
+        @Override
+        SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
+                throws IOException {
+            Socket wrapped = new Socket(address, port);
+            assertNull(wrapped.getChannel());
+
+            return clientMode(factory.createSocket(wrapped, address.getHostName(), port, true));
+        }
+
+        @Override
+        ServerSocket newServerSocket(SSLServerSocketFactory unused) throws IOException {
+            return ServerSocketFactory.getDefault().createServerSocket(
+                    0, 50, InetAddress.getLoopbackAddress());
+        }
+
+        @Override
+        SSLSocket accept(ServerSocket serverSocket, SSLSocketFactory factory) throws IOException {
+            assertFalse(serverSocket instanceof SSLServerSocket);
+            Socket wrapped = serverSocket.accept();
+            assertNull(wrapped.getChannel());
+
+            return serverMode(factory.createSocket(
+                    wrapped, wrapped.getInetAddress().getHostAddress(), wrapped.getPort(), true));
+        }
+    },
+    CHANNEL {
+        @Override
+        SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
+                throws IOException {
+            Socket wrapped = SocketChannel.open(new InetSocketAddress(address, port)).socket();
+            return clientMode(factory.createSocket(wrapped, address.getHostName(), port, true));
+        }
+
+        @Override
+        ServerSocket newServerSocket(SSLServerSocketFactory unused) throws IOException {
+            return ServerSocketChannel.open()
+                    .bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0))
+                    .socket();
+        }
+
+        @Override
+        SSLSocket accept(ServerSocket serverSocket, SSLSocketFactory factory) throws IOException {
+            assertFalse(serverSocket instanceof SSLServerSocket);
+            ServerSocketChannel serverChannel = serverSocket.getChannel();
+
+            // Just loop until the accept completes.
+            SocketChannel channel;
+            do {
+                channel = serverChannel.accept();
+            } while (channel == null);
+
+            Socket wrapped = channel.socket();
+            return serverMode(factory.createSocket(
+                    wrapped, wrapped.getInetAddress().getHostAddress(), wrapped.getPort(), true));
+        }
+    };
+
+    abstract SSLSocket newClientSocket(SSLSocketFactory factory, InetAddress address, int port)
+            throws IOException;
+    abstract ServerSocket newServerSocket(SSLServerSocketFactory factory) throws IOException;
+    abstract SSLSocket accept(ServerSocket socket, SSLSocketFactory factory) throws IOException;
+
+    private static SSLSocket clientMode(Socket socket) {
+        SSLSocket sslSocket = (SSLSocket) socket;
+        sslSocket.setUseClientMode(true);
+        return sslSocket;
+    }
+
+    private static SSLSocket serverMode(Socket socket) {
+        SSLSocket sslSocket = (SSLSocket) socket;
+        sslSocket.setUseClientMode(false);
+        return sslSocket;
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/TestUtils.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/TestUtils.java
new file mode 100644
index 0000000..0545d77
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/TestUtils.java
@@ -0,0 +1,704 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.Signature;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import com.android.org.conscrypt.java.security.StandardNames;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+import com.android.org.conscrypt.testing.Streams;
+import org.junit.Assume;
+
+/**
+ * Utility methods to support testing.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class TestUtils {
+    public static final Charset UTF_8 = Charset.forName("UTF-8");
+    private static final String PROTOCOL_TLS_V1_2 = "TLSv1.2";
+    private static final String PROTOCOL_TLS_V1_1 = "TLSv1.1";
+    private static final String PROTOCOL_TLS_V1 = "TLSv1";
+    private static final String[] DESIRED_PROTOCOLS =
+        new String[] {PROTOCOL_TLS_V1_2, PROTOCOL_TLS_V1_1, /* For Java 6 */ PROTOCOL_TLS_V1};
+    private static final Provider JDK_PROVIDER = getDefaultTlsProvider();
+    private static final byte[] CHARS =
+            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".getBytes(UTF_8);
+    private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocateDirect(0);
+    private static final String[] PROTOCOLS = getProtocolsInternal();
+
+    static final String TEST_CIPHER = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256";
+
+    private TestUtils() {}
+
+    private static Provider getDefaultTlsProvider() {
+        for (String protocol : DESIRED_PROTOCOLS) {
+            for (Provider p : Security.getProviders()) {
+                if (hasProtocol(p, protocol)) {
+                    return p;
+                }
+            }
+        }
+        // For Java 1.6 testing
+        return new BouncyCastleProvider();
+    }
+
+    private static boolean hasProtocol(Provider p, String protocol) {
+        return p.get("SSLContext." + protocol) != null;
+    }
+
+    static Provider getJdkProvider() {
+        return JDK_PROVIDER;
+    }
+
+    public static boolean isClassAvailable(String classname) {
+        try {
+            Class.forName(classname);
+            return true;
+        } catch (ClassNotFoundException ignore) {
+            // Ignored
+        }
+        return false;
+    }
+
+    private static void assumeClassAvailable(String classname) {
+        Assume.assumeTrue("Skipping test: " + classname + " unavailable",
+                isClassAvailable(classname));
+    }
+
+    public static void assumeSNIHostnameAvailable() {
+        assumeClassAvailable("javax.net.ssl.SNIHostName");
+    }
+
+    public static void assumeExtendedTrustManagerAvailable() {
+        assumeClassAvailable("javax.net.ssl.X509ExtendedTrustManager");
+    }
+
+    public static void assumeSetEndpointIdentificationAlgorithmAvailable() {
+        boolean supported = false;
+        try {
+            SSLParameters.class.getMethod("setEndpointIdentificationAlgorithm", String.class);
+            supported = true;
+        } catch (NoSuchMethodException ignore) {
+            // Ignored
+        }
+        Assume.assumeTrue("Skipping test: "
+                + "SSLParameters.setEndpointIdentificationAlgorithm unavailable", supported);
+    }
+
+    public static void assumeAEADAvailable() {
+        assumeClassAvailable("javax.crypto.AEADBadTagException");
+    }
+
+    private static boolean isAndroid() {
+        try {
+            Class.forName("android.app.Application", false, ClassLoader.getSystemClassLoader());
+            return true;
+        } catch (Throwable ignored) {
+            // Failed to load the class uniquely available in Android.
+            return false;
+        }
+    }
+
+    public static void assumeAndroid() {
+        Assume.assumeTrue(isAndroid());
+    }
+
+    public static void assumeAllowsUnsignedCrypto() {
+        // The Oracle JRE disallows loading crypto providers from unsigned jars
+        Assume.assumeTrue(isAndroid()
+                || !System.getProperty("java.vm.name").contains("HotSpot"));
+    }
+
+    public static void assumeSHA2WithDSAAvailable() {
+        boolean available;
+        try {
+            Signature.getInstance("SHA256withDSA");
+            available = true;
+        } catch (NoSuchAlgorithmException e) {
+            available = false;
+        }
+        Assume.assumeTrue("SHA2 with DSA signatures not available", available);
+    }
+
+    public static InetAddress getLoopbackAddress() {
+        try {
+            Method method = InetAddress.class.getMethod("getLoopbackAddress");
+            return (InetAddress) method.invoke(null);
+        } catch (Exception ignore) {
+            // Ignored.
+        }
+        try {
+            return InetAddress.getLocalHost();
+        } catch (UnknownHostException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static Provider getConscryptProvider() {
+        try {
+            String defaultName = (String) conscryptClass("Platform")
+                .getDeclaredMethod("getDefaultProviderName")
+                .invoke(null);
+            Constructor<?> c = conscryptClass("OpenSSLProvider")
+                .getDeclaredConstructor(String.class, Boolean.TYPE);
+
+            if (!isClassAvailable("javax.net.ssl.X509ExtendedTrustManager")) {
+                return (Provider) c.newInstance(defaultName, false);
+            } else {
+                return (Provider) c.newInstance(defaultName, true);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static synchronized boolean installConscryptIfNotPresent() {
+        Provider conscryptProvider = getConscryptProvider();
+        if (Security.getProvider(conscryptProvider.getName()) == null) {
+            Security.insertProviderAt(conscryptProvider, 1);
+            return true;
+        }
+        return false;
+    }
+
+    public static synchronized void installConscryptAsDefaultProvider() {
+        final Provider conscryptProvider = getConscryptProvider();
+        Provider[] providers = Security.getProviders();
+        if (providers.length == 0 || !providers[0].equals(conscryptProvider)) {
+            Security.insertProviderAt(conscryptProvider, 1);
+        }
+    }
+
+    public static InputStream openTestFile(String name) throws FileNotFoundException {
+        InputStream is = TestUtils.class.getResourceAsStream("/" + name);
+        if (is == null) {
+            throw new FileNotFoundException(name);
+        }
+        return is;
+    }
+
+    public static byte[] readTestFile(String name) throws IOException {
+        return Streams.readFully(openTestFile(name));
+    }
+
+    public static PublicKey readPublicKeyPemFile(String name)
+            throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
+        String keyData = new String(readTestFile(name), "US-ASCII");
+        keyData = keyData.replace("-----BEGIN PUBLIC KEY-----", "");
+        keyData = keyData.replace("-----END PUBLIC KEY-----", "");
+        keyData = keyData.replace("\r", "");
+        keyData = keyData.replace("\n", "");
+        return KeyFactory.getInstance("EC").generatePublic(
+                new X509EncodedKeySpec(decodeBase64(keyData)));
+    }
+
+    /**
+     * Looks up the conscrypt class for the given simple name (i.e. no package prefix).
+     */
+    public static Class<?> conscryptClass(String simpleName) throws ClassNotFoundException {
+        ClassNotFoundException ex = null;
+        for (String packageName : new String[] {"com.android.org.conscrypt", "com.android.com.android.org.conscrypt"}) {
+            String name = packageName + "." + simpleName;
+            try {
+                return Class.forName(name);
+            } catch (ClassNotFoundException e) {
+                ex = e;
+            }
+        }
+        throw ex;
+    }
+
+    /**
+     * Returns an array containing only {@link #PROTOCOL_TLS_V1_2}.
+     */
+    public static String[] getProtocols() {
+        return PROTOCOLS;
+    }
+
+    private static String[] getProtocolsInternal() {
+        List<String> protocols = new ArrayList<String>();
+        for (String protocol : DESIRED_PROTOCOLS) {
+            if (hasProtocol(getJdkProvider(), protocol)) {
+                protocols.add(protocol);
+            }
+        }
+        return protocols.toArray(new String[protocols.size()]);
+    }
+
+    public static SSLSocketFactory getJdkSocketFactory() {
+        return getSocketFactory(JDK_PROVIDER);
+    }
+
+    public static SSLServerSocketFactory getJdkServerSocketFactory() {
+        return getServerSocketFactory(JDK_PROVIDER);
+    }
+
+    static SSLSocketFactory setUseEngineSocket(
+            SSLSocketFactory conscryptFactory, boolean useEngineSocket) {
+        try {
+            Class<?> clazz = conscryptClass("Conscrypt");
+            Method method =
+                    clazz.getMethod("setUseEngineSocket", SSLSocketFactory.class, boolean.class);
+            method.invoke(null, conscryptFactory, useEngineSocket);
+            return conscryptFactory;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    static SSLServerSocketFactory setUseEngineSocket(
+            SSLServerSocketFactory conscryptFactory, boolean useEngineSocket) {
+        try {
+            Class<?> clazz = conscryptClass("Conscrypt");
+            Method method = clazz.getMethod(
+                    "setUseEngineSocket", SSLServerSocketFactory.class, boolean.class);
+            method.invoke(null, conscryptFactory, useEngineSocket);
+            return conscryptFactory;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void setUseSessionTickets(SSLSocket socket, boolean useTickets) {
+        try {
+            Class<?> clazz = conscryptClass("Conscrypt");
+            Method method = clazz.getMethod("setUseSessionTickets", SSLSocket.class, boolean.class);
+            method.invoke(null, socket, useTickets);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static SSLSocketFactory getConscryptSocketFactory(boolean useEngineSocket) {
+        return setUseEngineSocket(getSocketFactory(getConscryptProvider()), useEngineSocket);
+    }
+
+    public static SSLServerSocketFactory getConscryptServerSocketFactory(boolean useEngineSocket) {
+        return setUseEngineSocket(getServerSocketFactory(getConscryptProvider()), useEngineSocket);
+    }
+
+    private static SSLSocketFactory getSocketFactory(Provider provider) {
+        SSLContext clientContext = initClientSslContext(newContext(provider));
+        return clientContext.getSocketFactory();
+    }
+
+    private static SSLServerSocketFactory getServerSocketFactory(Provider provider) {
+        SSLContext serverContext = initServerSslContext(newContext(provider));
+        return serverContext.getServerSocketFactory();
+    }
+
+    static SSLContext newContext(Provider provider) {
+        try {
+            return SSLContext.getInstance("TLS", provider);
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    static String[] getCommonCipherSuites() {
+        SSLContext jdkContext =
+                TestUtils.initSslContext(newContext(getJdkProvider()), TestKeyStore.getClient());
+        SSLContext conscryptContext = TestUtils.initSslContext(
+                newContext(getConscryptProvider()), TestKeyStore.getClient());
+        Set<String> supported = new LinkedHashSet<String>();
+        supported.addAll(supportedCiphers(jdkContext));
+        supported.retainAll(supportedCiphers(conscryptContext));
+        filterCiphers(supported);
+
+        return supported.toArray(new String[supported.size()]);
+    }
+
+    private static List<String> supportedCiphers(SSLContext ctx) {
+        return Arrays.asList(ctx.getDefaultSSLParameters().getCipherSuites());
+    }
+
+    private static void filterCiphers(Iterable<String> ciphers) {
+        // Filter all non-TLS ciphers.
+        Iterator<String> iter = ciphers.iterator();
+        while (iter.hasNext()) {
+            String cipher = iter.next();
+            if (cipher.startsWith("SSL_") || cipher.startsWith("TLS_EMPTY")
+                    || cipher.contains("_RC4_")) {
+                iter.remove();
+            }
+        }
+    }
+
+    /**
+     * Picks a port that is not used right at this moment.
+     * Warning: Not thread safe. May see "BindException: Address already in use: bind" if using the
+     * returned port to create a new server socket when other threads/processes are concurrently
+     * creating new sockets without a specific port.
+     */
+    public static int pickUnusedPort() {
+        try {
+            ServerSocket serverSocket = new ServerSocket(0);
+            int port = serverSocket.getLocalPort();
+            serverSocket.close();
+            return port;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Creates a text message of the given length.
+     */
+    public static byte[] newTextMessage(int length) {
+        byte[] msg = new byte[length];
+        for (int msgIndex = 0; msgIndex < length;) {
+            int remaining = length - msgIndex;
+            int numChars = Math.min(remaining, CHARS.length);
+            System.arraycopy(CHARS, 0, msg, msgIndex, numChars);
+            msgIndex += numChars;
+        }
+        return msg;
+    }
+
+    static SSLContext newClientSslContext(Provider provider) {
+        SSLContext context = newContext(provider);
+        return initClientSslContext(context);
+    }
+
+    static SSLContext newServerSslContext(Provider provider) {
+        SSLContext context = newContext(provider);
+        return initServerSslContext(context);
+    }
+
+    /**
+     * Initializes the given client-side {@code context} with a default cert.
+     */
+    public static SSLContext initClientSslContext(SSLContext context) {
+        return initSslContext(context, TestKeyStore.getClient());
+    }
+
+    /**
+     * Initializes the given server-side {@code context} with the given cert chain and private key.
+     */
+    public static SSLContext initServerSslContext(SSLContext context) {
+        return initSslContext(context, TestKeyStore.getServer());
+    }
+
+    /**
+     * Initializes the given {@code context} from the {@code keyStore}.
+     */
+    static SSLContext initSslContext(SSLContext context, TestKeyStore keyStore) {
+        try {
+            context.init(keyStore.keyManagers, keyStore.trustManagers, null);
+            return context;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Performs the intial TLS handshake between the two {@link SSLEngine} instances.
+     */
+    public static void doEngineHandshake(SSLEngine clientEngine, SSLEngine serverEngine,
+        ByteBuffer clientAppBuffer, ByteBuffer clientPacketBuffer, ByteBuffer serverAppBuffer,
+        ByteBuffer serverPacketBuffer, boolean beginHandshake) throws SSLException {
+        if (beginHandshake) {
+            clientEngine.beginHandshake();
+            serverEngine.beginHandshake();
+        }
+
+        SSLEngineResult clientResult;
+        SSLEngineResult serverResult;
+
+        boolean clientHandshakeFinished = false;
+        boolean serverHandshakeFinished = false;
+
+        do {
+            int cTOsPos = clientPacketBuffer.position();
+            int sTOcPos = serverPacketBuffer.position();
+
+            clientResult = clientEngine.wrap(EMPTY_BUFFER, clientPacketBuffer);
+            runDelegatedTasks(clientResult, clientEngine);
+            serverResult = serverEngine.wrap(EMPTY_BUFFER, serverPacketBuffer);
+            runDelegatedTasks(serverResult, serverEngine);
+
+            // Verify that the consumed and produced number match what is in the buffers now.
+            assertEquals(0, clientResult.bytesConsumed());
+            assertEquals(0, serverResult.bytesConsumed());
+            assertEquals(clientPacketBuffer.position() - cTOsPos, clientResult.bytesProduced());
+            assertEquals(serverPacketBuffer.position() - sTOcPos, serverResult.bytesProduced());
+
+            clientPacketBuffer.flip();
+            serverPacketBuffer.flip();
+
+            // Verify that we only had one SSLEngineResult.HandshakeStatus.FINISHED
+            if (isHandshakeFinished(clientResult)) {
+                assertFalse(clientHandshakeFinished);
+                clientHandshakeFinished = true;
+            }
+            if (isHandshakeFinished(serverResult)) {
+                assertFalse(serverHandshakeFinished);
+                serverHandshakeFinished = true;
+            }
+
+            cTOsPos = clientPacketBuffer.position();
+            sTOcPos = serverPacketBuffer.position();
+
+            int clientAppReadBufferPos = clientAppBuffer.position();
+            int serverAppReadBufferPos = serverAppBuffer.position();
+
+            clientResult = clientEngine.unwrap(serverPacketBuffer, clientAppBuffer);
+            runDelegatedTasks(clientResult, clientEngine);
+            serverResult = serverEngine.unwrap(clientPacketBuffer, serverAppBuffer);
+            runDelegatedTasks(serverResult, serverEngine);
+
+            // Verify that the consumed and produced number match what is in the buffers now.
+            assertEquals(serverPacketBuffer.position() - sTOcPos, clientResult.bytesConsumed());
+            assertEquals(clientPacketBuffer.position() - cTOsPos, serverResult.bytesConsumed());
+            assertEquals(clientAppBuffer.position() - clientAppReadBufferPos,
+                clientResult.bytesProduced());
+            assertEquals(serverAppBuffer.position() - serverAppReadBufferPos,
+                serverResult.bytesProduced());
+
+            clientPacketBuffer.compact();
+            serverPacketBuffer.compact();
+
+            // Verify that we only had one SSLEngineResult.HandshakeStatus.FINISHED
+            if (isHandshakeFinished(clientResult)) {
+                assertFalse(clientHandshakeFinished);
+                clientHandshakeFinished = true;
+            }
+            if (isHandshakeFinished(serverResult)) {
+                assertFalse(serverHandshakeFinished);
+                serverHandshakeFinished = true;
+            }
+        } while (!clientHandshakeFinished || !serverHandshakeFinished);
+    }
+
+    private static boolean isHandshakeFinished(SSLEngineResult result) {
+        return result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED;
+    }
+
+    private static void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) {
+        if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+            for (;;) {
+                Runnable task = engine.getDelegatedTask();
+                if (task == null) {
+                    break;
+                }
+                task.run();
+            }
+        }
+    }
+
+    public static String pickArbitraryNonTls13Suite(String[] cipherSuites) {
+        return pickArbitraryNonTls13Suite(Arrays.asList(cipherSuites));
+    }
+
+    public static String pickArbitraryNonTls13Suite(Iterable<String> cipherSuites) {
+        for (String cipherSuite : cipherSuites) {
+            if (!StandardNames.CIPHER_SUITES_TLS13.contains(cipherSuite)) {
+                return cipherSuite;
+            }
+        }
+        fail("No non-TLSv1.3 cipher suite available.");
+        return null;
+    }
+
+    /**
+     * Decodes the provided hexadecimal string into a byte array.  Odd-length inputs
+     * are not allowed.
+     *
+     * Throws an {@code IllegalArgumentException} if the input is malformed.
+     */
+    public static byte[] decodeHex(String encoded) throws IllegalArgumentException {
+        return decodeHex(encoded.toCharArray());
+    }
+
+    /**
+     * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar}
+     * is {@code true} odd-length inputs are allowed and the first character is interpreted
+     * as the lower bits of the first result byte.
+     *
+     * Throws an {@code IllegalArgumentException} if the input is malformed.
+     */
+    public static byte[] decodeHex(String encoded, boolean allowSingleChar) throws IllegalArgumentException {
+        return decodeHex(encoded.toCharArray(), allowSingleChar);
+    }
+
+    /**
+     * Decodes the provided hexadecimal string into a byte array.  Odd-length inputs
+     * are not allowed.
+     *
+     * Throws an {@code IllegalArgumentException} if the input is malformed.
+     */
+    public static byte[] decodeHex(char[] encoded) throws IllegalArgumentException {
+        return decodeHex(encoded, false);
+    }
+
+    /**
+     * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar}
+     * is {@code true} odd-length inputs are allowed and the first character is interpreted
+     * as the lower bits of the first result byte.
+     *
+     * Throws an {@code IllegalArgumentException} if the input is malformed.
+     */
+    public static byte[] decodeHex(char[] encoded, boolean allowSingleChar) throws IllegalArgumentException {
+        int resultLengthBytes = (encoded.length + 1) / 2;
+        byte[] result = new byte[resultLengthBytes];
+
+        int resultOffset = 0;
+        int i = 0;
+        if (allowSingleChar) {
+            if ((encoded.length % 2) != 0) {
+                // Odd number of digits -- the first digit is the lower 4 bits of the first result byte.
+                result[resultOffset++] = (byte) toDigit(encoded, i);
+                i++;
+            }
+        } else {
+            if ((encoded.length % 2) != 0) {
+                throw new IllegalArgumentException("Invalid input length: " + encoded.length);
+            }
+        }
+
+        for (int len = encoded.length; i < len; i += 2) {
+            result[resultOffset++] = (byte) ((toDigit(encoded, i) << 4) | toDigit(encoded, i + 1));
+        }
+
+        return result;
+    }
+
+
+    private static int toDigit(char[] str, int offset) throws IllegalArgumentException {
+        // NOTE: that this isn't really a code point in the traditional sense, since we're
+        // just rejecting surrogate pairs outright.
+        int pseudoCodePoint = str[offset];
+
+        if ('0' <= pseudoCodePoint && pseudoCodePoint <= '9') {
+            return pseudoCodePoint - '0';
+        } else if ('a' <= pseudoCodePoint && pseudoCodePoint <= 'f') {
+            return 10 + (pseudoCodePoint - 'a');
+        } else if ('A' <= pseudoCodePoint && pseudoCodePoint <= 'F') {
+            return 10 + (pseudoCodePoint - 'A');
+        }
+
+        throw new IllegalArgumentException("Illegal char: " + str[offset] +
+                " at offset " + offset);
+    }
+
+    private static final String BASE64_ALPHABET =
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+    public static String encodeBase64(byte[] data) {
+        // Base64 was introduced in Java 8, so if it's not available we can use a hacky
+        // solution that works in previous versions
+        if (isClassAvailable("java.util.Base64")) {
+            return Base64.getEncoder().encodeToString(data);
+        } else {
+            StringBuilder builder = new StringBuilder();
+            for (int i = 0; i < data.length; i += 3) {
+                int padding = (i + 2 < data.length) ? 0 : (i + 3 - data.length);
+                byte b1 = data[i];
+                byte b2 = padding >= 2 ? 0 : data[i+1];
+                byte b3 = padding >= 1 ? 0 : data[i+2];
+
+                char c1 = BASE64_ALPHABET.charAt((b1 & 0xFF) >>> 2);
+                char c2 = BASE64_ALPHABET.charAt(((b1 & 0x03) << 4) | ((b2 & 0xFF) >>> 4));
+                char c3 = BASE64_ALPHABET.charAt(((b2 & 0x0F) << 2) | ((b3 & 0xFF) >>> 6));
+                char c4 = BASE64_ALPHABET.charAt(b3 & 0x3F);
+
+                if (padding >= 1) {
+                    c4 = '=';
+                }
+                if (padding >= 2) {
+                    c3 = '=';
+                }
+                builder.append(c1).append(c2).append(c3).append(c4);
+            }
+            return builder.toString();
+        }
+    }
+
+    public static byte[] decodeBase64(String data) {
+        // Base64 was introduced in Java 8, so if it's not available we can use a hacky
+        // solution that works in previous versions
+        if (isClassAvailable("java.util.Base64")) {
+            return Base64.getDecoder().decode(data);
+        } else {
+            while (data.endsWith("=")) {
+                data = data.substring(0, data.length() - 1);
+            }
+            int padding = (data.length() % 4 == 0) ? 0 : 4 - (data.length() % 4);
+            byte[] output = new byte[((data.length() - 1) / 4) * 3 + 3 - padding];
+            int outputindex = 0;
+            for (int i = 0; i < data.length(); i += 4) {
+                char c1 = data.charAt(i);
+                char c2 = data.charAt(i+1);
+                char c3 = (i+2 < data.length()) ? data.charAt(i+2) : 'A';
+                char c4 = (i+3 < data.length()) ? data.charAt(i+3) : 'A';
+
+                byte b1 = (byte)
+                        (BASE64_ALPHABET.indexOf(c1) << 2 | BASE64_ALPHABET.indexOf(c2) >>> 4);
+                byte b2 = (byte)
+                        ((BASE64_ALPHABET.indexOf(c2) & 0x0F) << 4 | BASE64_ALPHABET.indexOf(c3) >>> 2);
+                byte b3 = (byte)
+                        ((BASE64_ALPHABET.indexOf(c3) & 0x03) << 6 | BASE64_ALPHABET.indexOf(c4));
+
+                output[outputindex++] = b1;
+                if (outputindex < output.length) {
+                    output[outputindex++] = b2;
+                }
+                if (outputindex < output.length) {
+                    output[outputindex++] = b3;
+                }
+            }
+            return output;
+        }
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractAlgorithmParameterGeneratorTest.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractAlgorithmParameterGeneratorTest.java
new file mode 100644
index 0000000..cdbd7e8
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractAlgorithmParameterGeneratorTest.java
@@ -0,0 +1,47 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.security.AlgorithmParameterGenerator;
+import java.security.AlgorithmParameters;
+import org.junit.Test;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class AbstractAlgorithmParameterGeneratorTest {
+
+    private final String algorithmName;
+    private final TestHelper<AlgorithmParameters> helper;
+
+    protected AbstractAlgorithmParameterGeneratorTest(String algorithmName, TestHelper<AlgorithmParameters> helper) {
+        this.algorithmName = algorithmName;
+        this.helper = helper;
+    }
+
+    @Test
+    public void testAlgorithmParameterGenerator() throws Exception {
+        AlgorithmParameterGenerator generator = AlgorithmParameterGenerator.getInstance(algorithmName);
+        generator.init(1024);
+
+        AlgorithmParameters parameters = generator.generateParameters();
+        assertNotNull("generated parameters are null", parameters);
+        helper.test(parameters);
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractAlgorithmParametersTest.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractAlgorithmParametersTest.java
new file mode 100644
index 0000000..9f2ea86
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractAlgorithmParametersTest.java
@@ -0,0 +1,45 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import java.security.AlgorithmParameters;
+import java.security.spec.AlgorithmParameterSpec;
+import org.junit.Test;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class AbstractAlgorithmParametersTest {
+
+    private final String algorithmName;
+    private final TestHelper<AlgorithmParameters> helper;
+    private final AlgorithmParameterSpec parameterData;
+
+    public AbstractAlgorithmParametersTest(String algorithmName,
+            TestHelper<AlgorithmParameters> helper, AlgorithmParameterSpec parameterData) {
+        this.algorithmName = algorithmName;
+        this.helper = helper;
+        this.parameterData = parameterData;
+    }
+
+    @Test
+    public void testAlgorithmParameters() throws Exception {
+        AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance(algorithmName);
+        algorithmParameters.init(parameterData);
+        helper.test(algorithmParameters);
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractKeyFactoryTest.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractKeyFactoryTest.java
new file mode 100644
index 0000000..2f851c8
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractKeyFactoryTest.java
@@ -0,0 +1,66 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.KeySpec;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class AbstractKeyFactoryTest<PublicKeySpec extends KeySpec, PrivateKeySpec extends KeySpec> {
+
+    private final String algorithmName;
+    private final Class<PublicKeySpec> publicKeySpecClass;
+    private final Class<PrivateKeySpec> privateKeySpecClass;
+    private KeyFactory factory;
+
+    public AbstractKeyFactoryTest(String algorithmName,
+            Class<PublicKeySpec> publicKeySpecClass,
+            Class<PrivateKeySpec> privateKeySpecClass) {
+        this.algorithmName = algorithmName;
+        this.publicKeySpecClass = publicKeySpecClass;
+        this.privateKeySpecClass = privateKeySpecClass;
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        factory = getFactory();
+    }
+
+    private KeyFactory getFactory() throws Exception {
+        return KeyFactory.getInstance(algorithmName);
+    }
+
+    @Test
+    public void testKeyFactory() throws Exception {
+        PrivateKeySpec privateKeySpec = factory.getKeySpec(DefaultKeys.getPrivateKey(algorithmName),
+                                                           privateKeySpecClass);
+        PrivateKey privateKey =  factory.generatePrivate(privateKeySpec);
+        PublicKeySpec publicKeySpec = factory.getKeySpec(DefaultKeys.getPublicKey(algorithmName),
+                                                         publicKeySpecClass);
+        PublicKey publicKey = factory.generatePublic(publicKeySpec);
+        check(new KeyPair(publicKey, privateKey));
+    }
+
+    protected void check(KeyPair keyPair) throws Exception {}
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractKeyPairGeneratorTest.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractKeyPairGeneratorTest.java
new file mode 100644
index 0000000..f9ea7cf
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AbstractKeyPairGeneratorTest.java
@@ -0,0 +1,59 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class AbstractKeyPairGeneratorTest {
+
+    private final String algorithmName;
+    private final TestHelper<KeyPair> helper;
+
+    private KeyPairGenerator generator;
+
+    protected AbstractKeyPairGeneratorTest(String algorithmName, TestHelper<KeyPair> helper) {
+        this.algorithmName = algorithmName;
+        this.helper = helper;
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        generator = KeyPairGenerator.getInstance(algorithmName);
+    }
+
+    @Test
+    public void testKeyPairGenerator() throws Exception {
+        generator.initialize(1024);
+
+        KeyPair keyPair = generator.generateKeyPair();
+
+        assertNotNull("no keypair generated", keyPair);
+        assertNotNull("no public key generated", keyPair.getPublic());
+        assertNotNull("no private key generated", keyPair.getPrivate());
+
+        helper.test(keyPair);
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AlgorithmParameterAsymmetricHelper.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AlgorithmParameterAsymmetricHelper.java
new file mode 100644
index 0000000..bbc6bd4
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AlgorithmParameterAsymmetricHelper.java
@@ -0,0 +1,60 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertTrue;
+
+import java.security.AlgorithmParameters;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.util.Arrays;
+import javax.crypto.Cipher;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AlgorithmParameterAsymmetricHelper extends TestHelper<AlgorithmParameters> {
+
+    private static final String plainData = "some data to encrypt and decrypt";
+    private final String algorithmName;
+
+    public AlgorithmParameterAsymmetricHelper(String algorithmName) {
+        this.algorithmName = algorithmName;
+    }
+
+    private String baseName() {
+        return algorithmName.contains("/")
+                ? algorithmName.substring(0, algorithmName.indexOf('/'))
+                : algorithmName;
+    }
+
+    @Override
+    public void test(AlgorithmParameters parameters) throws Exception {
+        KeyPairGenerator generator = KeyPairGenerator.getInstance(baseName());
+        generator.initialize(1024);
+        KeyPair keyPair = generator.generateKeyPair();
+
+        Cipher cipher = Cipher.getInstance(algorithmName);
+        cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic(), parameters);
+        byte[] bs = cipher.doFinal(plainData.getBytes("UTF-8"));
+
+        cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate(), parameters);
+        byte[] decrypted = cipher.doFinal(bs);
+        assertTrue(Arrays.equals(plainData.getBytes("UTF-8"), decrypted));
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AlgorithmParameterKeyAgreementHelper.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AlgorithmParameterKeyAgreementHelper.java
new file mode 100644
index 0000000..5eba48e
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AlgorithmParameterKeyAgreementHelper.java
@@ -0,0 +1,49 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.security.AlgorithmParameters;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import javax.crypto.KeyAgreement;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AlgorithmParameterKeyAgreementHelper extends TestHelper<AlgorithmParameters> {
+
+    private final String algorithmName;
+
+    public AlgorithmParameterKeyAgreementHelper(String algorithmName) {
+        this.algorithmName = algorithmName;
+    }
+
+    @Override
+    public void test(AlgorithmParameters parameters) throws Exception {
+        KeyPairGenerator generator = KeyPairGenerator.getInstance(algorithmName);
+        generator.initialize(1024);
+
+        KeyPair keyPair = generator.generateKeyPair();
+        KeyAgreement keyAgreement = KeyAgreement.getInstance(algorithmName);
+        keyAgreement.init(keyPair.getPrivate());
+        keyAgreement.doPhase(keyPair.getPublic(), true);
+        assertNotNull("generated secret is null", keyAgreement.generateSecret());
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AlgorithmParameterSignatureHelper.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AlgorithmParameterSignatureHelper.java
new file mode 100644
index 0000000..f9d0898
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AlgorithmParameterSignatureHelper.java
@@ -0,0 +1,60 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertTrue;
+
+import java.security.AlgorithmParameters;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Signature;
+import java.security.spec.AlgorithmParameterSpec;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AlgorithmParameterSignatureHelper<T extends AlgorithmParameterSpec>
+        extends TestHelper<AlgorithmParameters> {
+
+    private final String algorithmName;
+    private final String plainData = "some data do sign and verify";
+    private final Class<T> parameterSpecClass;
+
+    public AlgorithmParameterSignatureHelper(String algorithmName, Class<T> parameterSpecCla1ss) {
+        this.algorithmName = algorithmName;
+        this.parameterSpecClass = parameterSpecCla1ss;
+    }
+
+    @Override
+    public void test(AlgorithmParameters parameters) throws Exception {
+        Signature signature = Signature.getInstance(algorithmName);
+        T parameterSpec = parameters.getParameterSpec(parameterSpecClass);
+        KeyPairGenerator generator = KeyPairGenerator.getInstance(algorithmName);
+
+        generator.initialize(parameterSpec);
+        KeyPair keyPair = generator.genKeyPair();
+
+        signature.initSign(keyPair.getPrivate());
+        signature.update(plainData.getBytes("UTF-8"));
+        byte[] signed = signature.sign();
+
+        signature.initVerify(keyPair.getPublic());
+        signature.update(plainData.getBytes("UTF-8"));
+        assertTrue("signature should verify", signature.verify(signed));
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AlgorithmParameterSymmetricHelper.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AlgorithmParameterSymmetricHelper.java
new file mode 100644
index 0000000..8791ecb
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/AlgorithmParameterSymmetricHelper.java
@@ -0,0 +1,69 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertTrue;
+
+import java.security.AlgorithmParameters;
+import java.security.Key;
+import java.util.Arrays;
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AlgorithmParameterSymmetricHelper extends TestHelper<AlgorithmParameters> {
+
+    private static final String plainData = "some data to encrypt and decrypt";
+    private final String algorithmName;
+    private final int keySize;
+    private String blockmode;
+
+    public AlgorithmParameterSymmetricHelper(String algorithmName, int keySize) {
+        this.algorithmName = algorithmName;
+        this.keySize = keySize;
+    }
+
+    public AlgorithmParameterSymmetricHelper(String algorithmName, String blockmode, int keySize) {
+        this(algorithmName, keySize);
+        this.blockmode = blockmode;
+    }
+
+    @Override
+    public void test(AlgorithmParameters parameters) throws Exception {
+        KeyGenerator generator = KeyGenerator.getInstance(algorithmName);
+        generator.init(keySize);
+
+        Key key = generator.generateKey();
+        String transformation = algorithmName;
+        if (blockmode != null)
+        {
+            transformation += "/" + blockmode;
+        }
+
+        Cipher cipher = Cipher.getInstance(transformation);
+        cipher.init(Cipher.ENCRYPT_MODE, key, parameters);
+        byte[] bs = cipher.doFinal(plainData.getBytes("UTF-8"));
+
+        cipher.init(Cipher.DECRYPT_MODE, key, parameters);
+        byte[] decrypted = cipher.doFinal(bs);
+
+        assertTrue(Arrays.equals(plainData.getBytes("UTF-8"), decrypted));
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CipherAsymmetricCryptHelper.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CipherAsymmetricCryptHelper.java
new file mode 100644
index 0000000..049f609
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CipherAsymmetricCryptHelper.java
@@ -0,0 +1,39 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import java.security.KeyPair;
+import javax.crypto.Cipher;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class CipherAsymmetricCryptHelper extends CipherHelper<KeyPair> {
+
+    private static final String plainData = "some data to encrypt and decrypt test";
+
+    public CipherAsymmetricCryptHelper(String algorithmName) {
+        super(algorithmName, plainData, Cipher.ENCRYPT_MODE,
+                Cipher.DECRYPT_MODE);
+    }
+
+    @Override
+    public void test(KeyPair keyPair) throws Exception {
+        test(keyPair.getPrivate(), keyPair.getPublic());
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CipherHelper.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CipherHelper.java
new file mode 100644
index 0000000..73f59f7
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CipherHelper.java
@@ -0,0 +1,53 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertEquals;
+
+import java.security.Key;
+import javax.crypto.Cipher;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class CipherHelper<T> extends TestHelper<T> {
+
+    private final String algorithmName;
+    private final String plainData;
+    private final int mode1;
+    private final int mode2;
+
+    public CipherHelper(String algorithmName, String plainData, int mode1, int mode2) {
+        this.algorithmName = algorithmName;
+        this.plainData = plainData;
+        this.mode1 = mode1;
+        this.mode2 = mode2;
+    }
+
+    public void test(Key encryptKey, Key decryptKey) throws Exception {
+        Cipher cipher = Cipher.getInstance(algorithmName);
+        cipher.init(mode1, encryptKey);
+        byte[] encrypted = cipher.doFinal(plainData.getBytes("UTF-8"));
+
+        cipher.init(mode2, decryptKey);
+        byte[] decrypted = cipher.doFinal(encrypted);
+        String decryptedString = new String(decrypted, "UTF-8");
+
+        assertEquals("transformed data does not match", plainData, decryptedString);
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CpuFeatures.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CpuFeatures.java
new file mode 100644
index 0000000..f75fb94
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/CpuFeatures.java
@@ -0,0 +1,151 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class CpuFeatures {
+    private CpuFeatures() {}
+
+    public static boolean isAESHardwareAccelerated() {
+        List<String> features = getListFromCpuinfo("Features");
+        if (features != null && features.contains("aes")) {
+            return true;
+        }
+
+        List<String> flags = getListFromCpuinfo("flags");
+        if (flags != null && flags.contains("aes")) {
+            return true;
+        }
+
+        features = getCpuFeaturesMac();
+        if (features != null && features.contains("aes")) {
+            return true;
+        }
+
+        // If we're in an emulated ABI, Conscrypt's NativeCrypto might bridge to
+        // a library that has accelerated AES instructions. See if Conscrypt
+        // detects that condition.
+        Class<?> nativeCrypto = findNativeCrypto();
+        if (nativeCrypto != null) {
+            try {
+                Method EVP_has_aes_hardware =
+                        nativeCrypto.getDeclaredMethod("EVP_has_aes_hardware");
+                EVP_has_aes_hardware.setAccessible(true);
+                return ((Integer) EVP_has_aes_hardware.invoke(null)) == 1;
+            } catch (NoSuchMethodException ignored) {
+            } catch (SecurityException ignored) {
+            } catch (IllegalAccessException ignored) {
+            } catch (IllegalArgumentException ignored) {
+            } catch (InvocationTargetException e) {
+                throw new IllegalArgumentException(e);
+            }
+        }
+
+        return false;
+    }
+
+    private static Class<?> findNativeCrypto() {
+        for (String packageName : new String[]{"com.android.com.android.org.conscrypt", "com.android.org.conscrypt"}) {
+            String name = packageName + ".NativeCrypto";
+            try {
+                return Class.forName(name);
+            } catch (ClassNotFoundException e) {
+                // Try the next one.
+            }
+        }
+        return null;
+    }
+
+    private static String getFieldFromCpuinfo(String field) {
+        try {
+            @SuppressWarnings("DefaultCharset")
+            BufferedReader br = new BufferedReader(new FileReader("/proc/cpuinfo"));
+            Pattern p = Pattern.compile(field + "\\s*:\\s*(.*)");
+
+            try {
+                String line;
+                while ((line = br.readLine()) != null) {
+                    Matcher m = p.matcher(line);
+                    if (m.matches()) {
+                        return m.group(1);
+                    }
+                }
+            } finally {
+                br.close();
+            }
+        } catch (IOException ignored) {
+            // Ignored.
+        }
+
+        return null;
+    }
+
+    private static List<String> getListFromCpuinfo(String fieldName) {
+        String features = getFieldFromCpuinfo(fieldName);
+        if (features == null)
+            return null;
+
+        return Arrays.asList(features.split("\\s"));
+    }
+
+    private static List<String> getCpuFeaturesMac() {
+        try {
+            StringBuilder output = new StringBuilder();
+            Process proc = Runtime.getRuntime().exec("sysctl -a");
+            if (proc.waitFor() == 0) {
+                BufferedReader reader =
+                        new BufferedReader(new InputStreamReader(proc.getInputStream(), UTF_8));
+
+                final String linePrefix = "machdep.cpu.features:";
+
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    line = line.toLowerCase();
+                    if (line.startsWith(linePrefix)) {
+                        // Strip the line prefix from the results.
+                        output.append(line.substring(linePrefix.length())).append(' ');
+                    }
+                }
+                if (output.length() > 0) {
+                    String outputString = output.toString();
+                    String[] parts = outputString.split("\\s+");
+                    return Arrays.asList(parts);
+                }
+            }
+        } catch (Exception ignored) {
+            // Ignored.
+        }
+
+        return null;
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/DefaultKeys.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/DefaultKeys.java
new file mode 100644
index 0000000..5a89f3d
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/DefaultKeys.java
@@ -0,0 +1,210 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.java.security;
+
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.HashMap;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DefaultKeys {
+    private static final byte[] RSA_private = new byte[] {
+        (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x75, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D,
+        (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x82, (byte) 0x02, (byte) 0x5F, (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x5B, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02,
+        (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x99, (byte) 0xA5, (byte) 0x96, (byte) 0x72, (byte) 0xAE, (byte) 0xBB, (byte) 0x59, (byte) 0x36, (byte) 0xA8, (byte) 0x12, (byte) 0x17, (byte) 0x05, (byte) 0x4C, (byte) 0x63,
+        (byte) 0x9E, (byte) 0xB8, (byte) 0x85, (byte) 0xD4, (byte) 0x2D, (byte) 0x71, (byte) 0xD7, (byte) 0x29, (byte) 0xB9, (byte) 0x05, (byte) 0x0F, (byte) 0xB4, (byte) 0x57, (byte) 0xFB, (byte) 0xD3, (byte) 0x95, (byte) 0x5C,
+        (byte) 0x21, (byte) 0xEC, (byte) 0xB5, (byte) 0xEB, (byte) 0x67, (byte) 0xA2, (byte) 0x4F, (byte) 0xC1, (byte) 0x93, (byte) 0xEF, (byte) 0x96, (byte) 0x41, (byte) 0x05, (byte) 0x3D, (byte) 0xC5, (byte) 0x3E, (byte) 0x04,
+        (byte) 0x4D, (byte) 0xC6, (byte) 0xCF, (byte) 0x32, (byte) 0x7C, (byte) 0x1F, (byte) 0x66, (byte) 0xA3, (byte) 0xC5, (byte) 0x27, (byte) 0x79, (byte) 0xEC, (byte) 0x2E, (byte) 0x67, (byte) 0xFA, (byte) 0x19, (byte) 0x5B,
+        (byte) 0xE3, (byte) 0xB1, (byte) 0x69, (byte) 0xDA, (byte) 0x63, (byte) 0xBC, (byte) 0xDA, (byte) 0xD3, (byte) 0xBB, (byte) 0xAD, (byte) 0x8C, (byte) 0x38, (byte) 0x7B, (byte) 0x4A, (byte) 0x9C, (byte) 0xD4, (byte) 0x4D,
+        (byte) 0xD2, (byte) 0x33, (byte) 0xB7, (byte) 0x4E, (byte) 0x04, (byte) 0xB6, (byte) 0xDF, (byte) 0x62, (byte) 0x55, (byte) 0x48, (byte) 0x5C, (byte) 0x94, (byte) 0x02, (byte) 0xF7, (byte) 0x84, (byte) 0xE6, (byte) 0x9B,
+        (byte) 0x57, (byte) 0xFF, (byte) 0x17, (byte) 0x2A, (byte) 0xA1, (byte) 0x74, (byte) 0x8D, (byte) 0x07, (byte) 0xD8, (byte) 0xCE, (byte) 0xF7, (byte) 0x0B, (byte) 0x59, (byte) 0xFB, (byte) 0x13, (byte) 0x6E, (byte) 0xF1,
+        (byte) 0xC3, (byte) 0xAB, (byte) 0x3E, (byte) 0x72, (byte) 0x1B, (byte) 0x62, (byte) 0x09, (byte) 0xE8, (byte) 0xD8, (byte) 0x41, (byte) 0x69, (byte) 0xE1, (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01,
+        (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x57, (byte) 0xD6, (byte) 0x1C, (byte) 0x2E, (byte) 0x2F, (byte) 0xCA, (byte) 0x16, (byte) 0xF4, (byte) 0x72, (byte) 0x1C, (byte) 0xF5, (byte) 0x60, (byte) 0x28, (byte) 0x0D,
+        (byte) 0x83, (byte) 0x7D, (byte) 0x85, (byte) 0xB4, (byte) 0x88, (byte) 0xCE, (byte) 0x5D, (byte) 0xED, (byte) 0x12, (byte) 0x42, (byte) 0xDC, (byte) 0x79, (byte) 0x83, (byte) 0x1B, (byte) 0x0A, (byte) 0x18, (byte) 0x86,
+        (byte) 0xF5, (byte) 0x35, (byte) 0xF7, (byte) 0xC2, (byte) 0x3E, (byte) 0x1A, (byte) 0xC2, (byte) 0x71, (byte) 0xAD, (byte) 0xFA, (byte) 0xF7, (byte) 0xF0, (byte) 0xEF, (byte) 0xE8, (byte) 0x22, (byte) 0x4C, (byte) 0x93,
+        (byte) 0xF5, (byte) 0x4A, (byte) 0xC4, (byte) 0xC4, (byte) 0xDD, (byte) 0xC4, (byte) 0xAD, (byte) 0xCE, (byte) 0xCE, (byte) 0x35, (byte) 0x05, (byte) 0x34, (byte) 0x8A, (byte) 0x4B, (byte) 0x12, (byte) 0xE4, (byte) 0x69,
+        (byte) 0xE6, (byte) 0xDA, (byte) 0x07, (byte) 0x1A, (byte) 0x77, (byte) 0x5C, (byte) 0xA7, (byte) 0x21, (byte) 0x41, (byte) 0x89, (byte) 0x8C, (byte) 0x95, (byte) 0x6A, (byte) 0x5D, (byte) 0x9C, (byte) 0x3C, (byte) 0xAE,
+        (byte) 0xC3, (byte) 0xE4, (byte) 0x64, (byte) 0x54, (byte) 0xDA, (byte) 0xFB, (byte) 0xBA, (byte) 0xA6, (byte) 0xE5, (byte) 0x8A, (byte) 0x7F, (byte) 0xFA, (byte) 0x1A, (byte) 0x3F, (byte) 0x9B, (byte) 0xAB, (byte) 0xDA,
+        (byte) 0x3D, (byte) 0x3B, (byte) 0x43, (byte) 0xF0, (byte) 0x0C, (byte) 0x06, (byte) 0x57, (byte) 0x43, (byte) 0x45, (byte) 0xEE, (byte) 0x8C, (byte) 0x27, (byte) 0x05, (byte) 0xAF, (byte) 0xCD, (byte) 0x5A, (byte) 0x47,
+        (byte) 0xB9, (byte) 0xEA, (byte) 0xD9, (byte) 0xAA, (byte) 0x66, (byte) 0xDB, (byte) 0xE3, (byte) 0xDC, (byte) 0x54, (byte) 0x47, (byte) 0x60, (byte) 0x01, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xED, (byte) 0xE9,
+        (byte) 0xBD, (byte) 0xD5, (byte) 0x02, (byte) 0x6D, (byte) 0x44, (byte) 0x0E, (byte) 0x3F, (byte) 0x74, (byte) 0x0C, (byte) 0x45, (byte) 0x54, (byte) 0x88, (byte) 0xFE, (byte) 0x5C, (byte) 0xFC, (byte) 0xF2, (byte) 0x31,
+        (byte) 0x7B, (byte) 0xAF, (byte) 0x15, (byte) 0x77, (byte) 0x7A, (byte) 0xDC, (byte) 0xC6, (byte) 0x9E, (byte) 0x7E, (byte) 0xC1, (byte) 0xCA, (byte) 0x84, (byte) 0xC7, (byte) 0x4B, (byte) 0xC4, (byte) 0x41, (byte) 0xE1,
+        (byte) 0x85, (byte) 0xE4, (byte) 0x5A, (byte) 0xA7, (byte) 0x3D, (byte) 0x54, (byte) 0x87, (byte) 0x0D, (byte) 0x7A, (byte) 0xC5, (byte) 0x47, (byte) 0x5C, (byte) 0xF2, (byte) 0xAD, (byte) 0x14, (byte) 0x4D, (byte) 0x63,
+        (byte) 0xB0, (byte) 0xDC, (byte) 0x34, (byte) 0xB5, (byte) 0xDA, (byte) 0x17, (byte) 0x0D, (byte) 0x4E, (byte) 0x2B, (byte) 0x9E, (byte) 0x81, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xA5, (byte) 0x53, (byte) 0xDB,
+        (byte) 0xD8, (byte) 0x28, (byte) 0x57, (byte) 0x65, (byte) 0x2B, (byte) 0xFA, (byte) 0xF2, (byte) 0x21, (byte) 0xB8, (byte) 0x60, (byte) 0xAE, (byte) 0x43, (byte) 0x4B, (byte) 0x51, (byte) 0x85, (byte) 0xCB, (byte) 0xDA,
+        (byte) 0x89, (byte) 0x5A, (byte) 0x7D, (byte) 0x05, (byte) 0xDA, (byte) 0xFC, (byte) 0xAF, (byte) 0x46, (byte) 0x86, (byte) 0xBC, (byte) 0x3F, (byte) 0xD1, (byte) 0xEA, (byte) 0xA4, (byte) 0x56, (byte) 0xA3, (byte) 0xE6,
+        (byte) 0xD4, (byte) 0xA2, (byte) 0x08, (byte) 0x93, (byte) 0x63, (byte) 0x21, (byte) 0x0E, (byte) 0xC5, (byte) 0x3C, (byte) 0x97, (byte) 0x7E, (byte) 0x71, (byte) 0x0B, (byte) 0x79, (byte) 0xF8, (byte) 0x60, (byte) 0x73,
+        (byte) 0xD1, (byte) 0xF9, (byte) 0xD4, (byte) 0x66, (byte) 0x29, (byte) 0x7D, (byte) 0xDC, (byte) 0x22, (byte) 0xDB, (byte) 0x61, (byte) 0x02, (byte) 0x40, (byte) 0x5D, (byte) 0x3D, (byte) 0xEF, (byte) 0x85, (byte) 0x4D,
+        (byte) 0x27, (byte) 0x2F, (byte) 0xB5, (byte) 0xF9, (byte) 0xCE, (byte) 0x6C, (byte) 0x84, (byte) 0xBB, (byte) 0x85, (byte) 0xD9, (byte) 0x52, (byte) 0xEE, (byte) 0x5B, (byte) 0xA9, (byte) 0x63, (byte) 0x15, (byte) 0x12,
+        (byte) 0x6F, (byte) 0xBA, (byte) 0x3A, (byte) 0x4E, (byte) 0xA9, (byte) 0x8D, (byte) 0x7A, (byte) 0x3B, (byte) 0xF9, (byte) 0xDF, (byte) 0xF5, (byte) 0xE4, (byte) 0xDC, (byte) 0x01, (byte) 0x1C, (byte) 0x2D, (byte) 0x8C,
+        (byte) 0x0D, (byte) 0xE1, (byte) 0x6E, (byte) 0x80, (byte) 0x63, (byte) 0x9B, (byte) 0x0B, (byte) 0x38, (byte) 0x55, (byte) 0xC8, (byte) 0x52, (byte) 0x67, (byte) 0x13, (byte) 0x91, (byte) 0x8F, (byte) 0x9E, (byte) 0x2E,
+        (byte) 0x16, (byte) 0x5B, (byte) 0x7C, (byte) 0x0F, (byte) 0x5D, (byte) 0xE4, (byte) 0xA0, (byte) 0x81, (byte) 0x02, (byte) 0x40, (byte) 0x20, (byte) 0x12, (byte) 0x11, (byte) 0x5E, (byte) 0x70, (byte) 0x0C, (byte) 0xEC,
+        (byte) 0x02, (byte) 0x49, (byte) 0x0E, (byte) 0xB9, (byte) 0x3D, (byte) 0xD3, (byte) 0xFB, (byte) 0x59, (byte) 0xF0, (byte) 0x7D, (byte) 0x62, (byte) 0xEF, (byte) 0xF5, (byte) 0x77, (byte) 0x99, (byte) 0x87, (byte) 0x11,
+        (byte) 0x20, (byte) 0xB6, (byte) 0xCD, (byte) 0xA5, (byte) 0x67, (byte) 0xB3, (byte) 0x92, (byte) 0xC9, (byte) 0xBC, (byte) 0xB3, (byte) 0x9E, (byte) 0x5E, (byte) 0xF3, (byte) 0x03, (byte) 0x22, (byte) 0x5F, (byte) 0x79,
+        (byte) 0x7F, (byte) 0xCC, (byte) 0x44, (byte) 0xDA, (byte) 0x3B, (byte) 0xF3, (byte) 0xC3, (byte) 0x42, (byte) 0x58, (byte) 0x90, (byte) 0x93, (byte) 0x7E, (byte) 0xDA, (byte) 0x58, (byte) 0xCC, (byte) 0x16, (byte) 0xC8,
+        (byte) 0xAE, (byte) 0x99, (byte) 0xCC, (byte) 0x9F, (byte) 0x32, (byte) 0x61, (byte) 0x02, (byte) 0x40, (byte) 0x02, (byte) 0x29, (byte) 0xDB, (byte) 0x00, (byte) 0x0F, (byte) 0x0A, (byte) 0x17, (byte) 0x33, (byte) 0x7E,
+        (byte) 0xC5, (byte) 0xEC, (byte) 0x21, (byte) 0x47, (byte) 0x65, (byte) 0xDC, (byte) 0xE5, (byte) 0xC2, (byte) 0x0D, (byte) 0x42, (byte) 0x28, (byte) 0xE1, (byte) 0x17, (byte) 0x1A, (byte) 0x00, (byte) 0xBD, (byte) 0xBE,
+        (byte) 0x1C, (byte) 0x7D, (byte) 0xCA, (byte) 0x93, (byte) 0x67, (byte) 0x8F, (byte) 0x28, (byte) 0xB7, (byte) 0x60, (byte) 0x8E, (byte) 0xF0, (byte) 0x5D, (byte) 0xCD, (byte) 0xFA, (byte) 0xDD, (byte) 0x6B, (byte) 0x72,
+        (byte) 0xF7, (byte) 0x48, (byte) 0xD9, (byte) 0x3C, (byte) 0x40, (byte) 0x7C, (byte) 0xB0, (byte) 0xD7, (byte) 0x58, (byte) 0xC2, (byte) 0x53, (byte) 0xAD, (byte) 0x04, (byte) 0xF6, (byte) 0x0B, (byte) 0x35, (byte) 0x51,
+        (byte) 0x45, (byte) 0xB9, (byte) 0x4F, (byte) 0x49  };
+        private static final byte[] RSA_public = new byte[] {
+        (byte) 0x30, (byte) 0x81, (byte) 0x9F, (byte) 0x30, (byte) 0x0D, (byte) 0x06, (byte) 0x09, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05,
+        (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x8D, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x99, (byte) 0xA5, (byte) 0x96, (byte) 0x72, (byte) 0xAE,
+        (byte) 0xBB, (byte) 0x59, (byte) 0x36, (byte) 0xA8, (byte) 0x12, (byte) 0x17, (byte) 0x05, (byte) 0x4C, (byte) 0x63, (byte) 0x9E, (byte) 0xB8, (byte) 0x85, (byte) 0xD4, (byte) 0x2D, (byte) 0x71, (byte) 0xD7, (byte) 0x29,
+        (byte) 0xB9, (byte) 0x05, (byte) 0x0F, (byte) 0xB4, (byte) 0x57, (byte) 0xFB, (byte) 0xD3, (byte) 0x95, (byte) 0x5C, (byte) 0x21, (byte) 0xEC, (byte) 0xB5, (byte) 0xEB, (byte) 0x67, (byte) 0xA2, (byte) 0x4F, (byte) 0xC1,
+        (byte) 0x93, (byte) 0xEF, (byte) 0x96, (byte) 0x41, (byte) 0x05, (byte) 0x3D, (byte) 0xC5, (byte) 0x3E, (byte) 0x04, (byte) 0x4D, (byte) 0xC6, (byte) 0xCF, (byte) 0x32, (byte) 0x7C, (byte) 0x1F, (byte) 0x66, (byte) 0xA3,
+        (byte) 0xC5, (byte) 0x27, (byte) 0x79, (byte) 0xEC, (byte) 0x2E, (byte) 0x67, (byte) 0xFA, (byte) 0x19, (byte) 0x5B, (byte) 0xE3, (byte) 0xB1, (byte) 0x69, (byte) 0xDA, (byte) 0x63, (byte) 0xBC, (byte) 0xDA, (byte) 0xD3,
+        (byte) 0xBB, (byte) 0xAD, (byte) 0x8C, (byte) 0x38, (byte) 0x7B, (byte) 0x4A, (byte) 0x9C, (byte) 0xD4, (byte) 0x4D, (byte) 0xD2, (byte) 0x33, (byte) 0xB7, (byte) 0x4E, (byte) 0x04, (byte) 0xB6, (byte) 0xDF, (byte) 0x62,
+        (byte) 0x55, (byte) 0x48, (byte) 0x5C, (byte) 0x94, (byte) 0x02, (byte) 0xF7, (byte) 0x84, (byte) 0xE6, (byte) 0x9B, (byte) 0x57, (byte) 0xFF, (byte) 0x17, (byte) 0x2A, (byte) 0xA1, (byte) 0x74, (byte) 0x8D, (byte) 0x07,
+        (byte) 0xD8, (byte) 0xCE, (byte) 0xF7, (byte) 0x0B, (byte) 0x59, (byte) 0xFB, (byte) 0x13, (byte) 0x6E, (byte) 0xF1, (byte) 0xC3, (byte) 0xAB, (byte) 0x3E, (byte) 0x72, (byte) 0x1B, (byte) 0x62, (byte) 0x09, (byte) 0xE8,
+        (byte) 0xD8, (byte) 0x41, (byte) 0x69, (byte) 0xE1, (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01     };
+        private static final byte[] DSA_private = new byte[] {
+        (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x4B, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x2C, (byte) 0x06, (byte) 0x07, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0xCE,
+        (byte) 0x38, (byte) 0x04, (byte) 0x01, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x1F, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xFD, (byte) 0x7F, (byte) 0x53, (byte) 0x81, (byte) 0x1D, (byte) 0x75,
+        (byte) 0x12, (byte) 0x29, (byte) 0x52, (byte) 0xDF, (byte) 0x4A, (byte) 0x9C, (byte) 0x2E, (byte) 0xEC, (byte) 0xE4, (byte) 0xE7, (byte) 0xF6, (byte) 0x11, (byte) 0xB7, (byte) 0x52, (byte) 0x3C, (byte) 0xEF, (byte) 0x44,
+        (byte) 0x00, (byte) 0xC3, (byte) 0x1E, (byte) 0x3F, (byte) 0x80, (byte) 0xB6, (byte) 0x51, (byte) 0x26, (byte) 0x69, (byte) 0x45, (byte) 0x5D, (byte) 0x40, (byte) 0x22, (byte) 0x51, (byte) 0xFB, (byte) 0x59, (byte) 0x3D,
+        (byte) 0x8D, (byte) 0x58, (byte) 0xFA, (byte) 0xBF, (byte) 0xC5, (byte) 0xF5, (byte) 0xBA, (byte) 0x30, (byte) 0xF6, (byte) 0xCB, (byte) 0x9B, (byte) 0x55, (byte) 0x6C, (byte) 0xD7, (byte) 0x81, (byte) 0x3B, (byte) 0x80,
+        (byte) 0x1D, (byte) 0x34, (byte) 0x6F, (byte) 0xF2, (byte) 0x66, (byte) 0x60, (byte) 0xB7, (byte) 0x6B, (byte) 0x99, (byte) 0x50, (byte) 0xA5, (byte) 0xA4, (byte) 0x9F, (byte) 0x9F, (byte) 0xE8, (byte) 0x04, (byte) 0x7B,
+        (byte) 0x10, (byte) 0x22, (byte) 0xC2, (byte) 0x4F, (byte) 0xBB, (byte) 0xA9, (byte) 0xD7, (byte) 0xFE, (byte) 0xB7, (byte) 0xC6, (byte) 0x1B, (byte) 0xF8, (byte) 0x3B, (byte) 0x57, (byte) 0xE7, (byte) 0xC6, (byte) 0xA8,
+        (byte) 0xA6, (byte) 0x15, (byte) 0x0F, (byte) 0x04, (byte) 0xFB, (byte) 0x83, (byte) 0xF6, (byte) 0xD3, (byte) 0xC5, (byte) 0x1E, (byte) 0xC3, (byte) 0x02, (byte) 0x35, (byte) 0x54, (byte) 0x13, (byte) 0x5A, (byte) 0x16,
+        (byte) 0x91, (byte) 0x32, (byte) 0xF6, (byte) 0x75, (byte) 0xF3, (byte) 0xAE, (byte) 0x2B, (byte) 0x61, (byte) 0xD7, (byte) 0x2A, (byte) 0xEF, (byte) 0xF2, (byte) 0x22, (byte) 0x03, (byte) 0x19, (byte) 0x9D, (byte) 0xD1,
+        (byte) 0x48, (byte) 0x01, (byte) 0xC7, (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0x97, (byte) 0x60, (byte) 0x50, (byte) 0x8F, (byte) 0x15, (byte) 0x23, (byte) 0x0B, (byte) 0xCC, (byte) 0xB2, (byte) 0x92, (byte) 0xB9,
+        (byte) 0x82, (byte) 0xA2, (byte) 0xEB, (byte) 0x84, (byte) 0x0B, (byte) 0xF0, (byte) 0x58, (byte) 0x1C, (byte) 0xF5, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xF7, (byte) 0xE1, (byte) 0xA0, (byte) 0x85,
+        (byte) 0xD6, (byte) 0x9B, (byte) 0x3D, (byte) 0xDE, (byte) 0xCB, (byte) 0xBC, (byte) 0xAB, (byte) 0x5C, (byte) 0x36, (byte) 0xB8, (byte) 0x57, (byte) 0xB9, (byte) 0x79, (byte) 0x94, (byte) 0xAF, (byte) 0xBB, (byte) 0xFA,
+        (byte) 0x3A, (byte) 0xEA, (byte) 0x82, (byte) 0xF9, (byte) 0x57, (byte) 0x4C, (byte) 0x0B, (byte) 0x3D, (byte) 0x07, (byte) 0x82, (byte) 0x67, (byte) 0x51, (byte) 0x59, (byte) 0x57, (byte) 0x8E, (byte) 0xBA, (byte) 0xD4,
+        (byte) 0x59, (byte) 0x4F, (byte) 0xE6, (byte) 0x71, (byte) 0x07, (byte) 0x10, (byte) 0x81, (byte) 0x80, (byte) 0xB4, (byte) 0x49, (byte) 0x16, (byte) 0x71, (byte) 0x23, (byte) 0xE8, (byte) 0x4C, (byte) 0x28, (byte) 0x16,
+        (byte) 0x13, (byte) 0xB7, (byte) 0xCF, (byte) 0x09, (byte) 0x32, (byte) 0x8C, (byte) 0xC8, (byte) 0xA6, (byte) 0xE1, (byte) 0x3C, (byte) 0x16, (byte) 0x7A, (byte) 0x8B, (byte) 0x54, (byte) 0x7C, (byte) 0x8D, (byte) 0x28,
+        (byte) 0xE0, (byte) 0xA3, (byte) 0xAE, (byte) 0x1E, (byte) 0x2B, (byte) 0xB3, (byte) 0xA6, (byte) 0x75, (byte) 0x91, (byte) 0x6E, (byte) 0xA3, (byte) 0x7F, (byte) 0x0B, (byte) 0xFA, (byte) 0x21, (byte) 0x35, (byte) 0x62,
+        (byte) 0xF1, (byte) 0xFB, (byte) 0x62, (byte) 0x7A, (byte) 0x01, (byte) 0x24, (byte) 0x3B, (byte) 0xCC, (byte) 0xA4, (byte) 0xF1, (byte) 0xBE, (byte) 0xA8, (byte) 0x51, (byte) 0x90, (byte) 0x89, (byte) 0xA8, (byte) 0x83,
+        (byte) 0xDF, (byte) 0xE1, (byte) 0x5A, (byte) 0xE5, (byte) 0x9F, (byte) 0x06, (byte) 0x92, (byte) 0x8B, (byte) 0x66, (byte) 0x5E, (byte) 0x80, (byte) 0x7B, (byte) 0x55, (byte) 0x25, (byte) 0x64, (byte) 0x01, (byte) 0x4C,
+        (byte) 0x3B, (byte) 0xFE, (byte) 0xCF, (byte) 0x49, (byte) 0x2A, (byte) 0x04, (byte) 0x16, (byte) 0x02, (byte) 0x14, (byte) 0x0E, (byte) 0x90, (byte) 0xB7, (byte) 0x92, (byte) 0x01, (byte) 0x98, (byte) 0xCD, (byte) 0x85,
+        (byte) 0x87, (byte) 0x77, (byte) 0x2F, (byte) 0xB4, (byte) 0x31, (byte) 0xFD, (byte) 0xDE, (byte) 0xFA, (byte) 0x08, (byte) 0x6D, (byte) 0x0C, (byte) 0xE3  };
+        private static final byte[] DSA_public = new byte[] {
+        (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0xB8, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x2C, (byte) 0x06, (byte) 0x07, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0xCE, (byte) 0x38, (byte) 0x04, (byte) 0x01,
+        (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x1F, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xFD, (byte) 0x7F, (byte) 0x53, (byte) 0x81, (byte) 0x1D, (byte) 0x75, (byte) 0x12, (byte) 0x29, (byte) 0x52,
+        (byte) 0xDF, (byte) 0x4A, (byte) 0x9C, (byte) 0x2E, (byte) 0xEC, (byte) 0xE4, (byte) 0xE7, (byte) 0xF6, (byte) 0x11, (byte) 0xB7, (byte) 0x52, (byte) 0x3C, (byte) 0xEF, (byte) 0x44, (byte) 0x00, (byte) 0xC3, (byte) 0x1E,
+        (byte) 0x3F, (byte) 0x80, (byte) 0xB6, (byte) 0x51, (byte) 0x26, (byte) 0x69, (byte) 0x45, (byte) 0x5D, (byte) 0x40, (byte) 0x22, (byte) 0x51, (byte) 0xFB, (byte) 0x59, (byte) 0x3D, (byte) 0x8D, (byte) 0x58, (byte) 0xFA,
+        (byte) 0xBF, (byte) 0xC5, (byte) 0xF5, (byte) 0xBA, (byte) 0x30, (byte) 0xF6, (byte) 0xCB, (byte) 0x9B, (byte) 0x55, (byte) 0x6C, (byte) 0xD7, (byte) 0x81, (byte) 0x3B, (byte) 0x80, (byte) 0x1D, (byte) 0x34, (byte) 0x6F,
+        (byte) 0xF2, (byte) 0x66, (byte) 0x60, (byte) 0xB7, (byte) 0x6B, (byte) 0x99, (byte) 0x50, (byte) 0xA5, (byte) 0xA4, (byte) 0x9F, (byte) 0x9F, (byte) 0xE8, (byte) 0x04, (byte) 0x7B, (byte) 0x10, (byte) 0x22, (byte) 0xC2,
+        (byte) 0x4F, (byte) 0xBB, (byte) 0xA9, (byte) 0xD7, (byte) 0xFE, (byte) 0xB7, (byte) 0xC6, (byte) 0x1B, (byte) 0xF8, (byte) 0x3B, (byte) 0x57, (byte) 0xE7, (byte) 0xC6, (byte) 0xA8, (byte) 0xA6, (byte) 0x15, (byte) 0x0F,
+        (byte) 0x04, (byte) 0xFB, (byte) 0x83, (byte) 0xF6, (byte) 0xD3, (byte) 0xC5, (byte) 0x1E, (byte) 0xC3, (byte) 0x02, (byte) 0x35, (byte) 0x54, (byte) 0x13, (byte) 0x5A, (byte) 0x16, (byte) 0x91, (byte) 0x32, (byte) 0xF6,
+        (byte) 0x75, (byte) 0xF3, (byte) 0xAE, (byte) 0x2B, (byte) 0x61, (byte) 0xD7, (byte) 0x2A, (byte) 0xEF, (byte) 0xF2, (byte) 0x22, (byte) 0x03, (byte) 0x19, (byte) 0x9D, (byte) 0xD1, (byte) 0x48, (byte) 0x01, (byte) 0xC7,
+        (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0x97, (byte) 0x60, (byte) 0x50, (byte) 0x8F, (byte) 0x15, (byte) 0x23, (byte) 0x0B, (byte) 0xCC, (byte) 0xB2, (byte) 0x92, (byte) 0xB9, (byte) 0x82, (byte) 0xA2, (byte) 0xEB,
+        (byte) 0x84, (byte) 0x0B, (byte) 0xF0, (byte) 0x58, (byte) 0x1C, (byte) 0xF5, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xF7, (byte) 0xE1, (byte) 0xA0, (byte) 0x85, (byte) 0xD6, (byte) 0x9B, (byte) 0x3D,
+        (byte) 0xDE, (byte) 0xCB, (byte) 0xBC, (byte) 0xAB, (byte) 0x5C, (byte) 0x36, (byte) 0xB8, (byte) 0x57, (byte) 0xB9, (byte) 0x79, (byte) 0x94, (byte) 0xAF, (byte) 0xBB, (byte) 0xFA, (byte) 0x3A, (byte) 0xEA, (byte) 0x82,
+        (byte) 0xF9, (byte) 0x57, (byte) 0x4C, (byte) 0x0B, (byte) 0x3D, (byte) 0x07, (byte) 0x82, (byte) 0x67, (byte) 0x51, (byte) 0x59, (byte) 0x57, (byte) 0x8E, (byte) 0xBA, (byte) 0xD4, (byte) 0x59, (byte) 0x4F, (byte) 0xE6,
+        (byte) 0x71, (byte) 0x07, (byte) 0x10, (byte) 0x81, (byte) 0x80, (byte) 0xB4, (byte) 0x49, (byte) 0x16, (byte) 0x71, (byte) 0x23, (byte) 0xE8, (byte) 0x4C, (byte) 0x28, (byte) 0x16, (byte) 0x13, (byte) 0xB7, (byte) 0xCF,
+        (byte) 0x09, (byte) 0x32, (byte) 0x8C, (byte) 0xC8, (byte) 0xA6, (byte) 0xE1, (byte) 0x3C, (byte) 0x16, (byte) 0x7A, (byte) 0x8B, (byte) 0x54, (byte) 0x7C, (byte) 0x8D, (byte) 0x28, (byte) 0xE0, (byte) 0xA3, (byte) 0xAE,
+        (byte) 0x1E, (byte) 0x2B, (byte) 0xB3, (byte) 0xA6, (byte) 0x75, (byte) 0x91, (byte) 0x6E, (byte) 0xA3, (byte) 0x7F, (byte) 0x0B, (byte) 0xFA, (byte) 0x21, (byte) 0x35, (byte) 0x62, (byte) 0xF1, (byte) 0xFB, (byte) 0x62,
+        (byte) 0x7A, (byte) 0x01, (byte) 0x24, (byte) 0x3B, (byte) 0xCC, (byte) 0xA4, (byte) 0xF1, (byte) 0xBE, (byte) 0xA8, (byte) 0x51, (byte) 0x90, (byte) 0x89, (byte) 0xA8, (byte) 0x83, (byte) 0xDF, (byte) 0xE1, (byte) 0x5A,
+        (byte) 0xE5, (byte) 0x9F, (byte) 0x06, (byte) 0x92, (byte) 0x8B, (byte) 0x66, (byte) 0x5E, (byte) 0x80, (byte) 0x7B, (byte) 0x55, (byte) 0x25, (byte) 0x64, (byte) 0x01, (byte) 0x4C, (byte) 0x3B, (byte) 0xFE, (byte) 0xCF,
+        (byte) 0x49, (byte) 0x2A, (byte) 0x03, (byte) 0x81, (byte) 0x85, (byte) 0x00, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x98, (byte) 0x33, (byte) 0x90, (byte) 0x14, (byte) 0x79, (byte) 0xC7, (byte) 0xC8,
+        (byte) 0x57, (byte) 0xE1, (byte) 0x82, (byte) 0x53, (byte) 0x5B, (byte) 0x6E, (byte) 0x01, (byte) 0x07, (byte) 0x1E, (byte) 0xA5, (byte) 0x98, (byte) 0xC4, (byte) 0x57, (byte) 0x5D, (byte) 0x23, (byte) 0xAB, (byte) 0x72,
+        (byte) 0x9A, (byte) 0xB3, (byte) 0x2F, (byte) 0x39, (byte) 0xCB, (byte) 0xC5, (byte) 0xD0, (byte) 0x97, (byte) 0xD5, (byte) 0x62, (byte) 0x8C, (byte) 0xD9, (byte) 0xE6, (byte) 0xE2, (byte) 0x05, (byte) 0xC6, (byte) 0x05,
+        (byte) 0x71, (byte) 0x16, (byte) 0xE3, (byte) 0xE8, (byte) 0x04, (byte) 0xE8, (byte) 0x46, (byte) 0x12, (byte) 0x0C, (byte) 0xF8, (byte) 0xFC, (byte) 0x8E, (byte) 0x15, (byte) 0x26, (byte) 0x32, (byte) 0xF7, (byte) 0x8C,
+        (byte) 0x04, (byte) 0x3B, (byte) 0x53, (byte) 0x68, (byte) 0x9A, (byte) 0xA3, (byte) 0xB7, (byte) 0x4D, (byte) 0x13, (byte) 0x40, (byte) 0x0F, (byte) 0xBE, (byte) 0x03, (byte) 0x87, (byte) 0xD8, (byte) 0xF1, (byte) 0xFE,
+        (byte) 0x4B, (byte) 0xF5, (byte) 0x44, (byte) 0x19, (byte) 0x29, (byte) 0xBB, (byte) 0x0D, (byte) 0x0C, (byte) 0x52, (byte) 0xC6, (byte) 0x84, (byte) 0x33, (byte) 0x62, (byte) 0x73, (byte) 0x5D, (byte) 0x03, (byte) 0xFF,
+        (byte) 0x6F, (byte) 0x0A, (byte) 0x5A, (byte) 0xF3, (byte) 0x9E, (byte) 0x52, (byte) 0xF2, (byte) 0xC2, (byte) 0xC8, (byte) 0x00, (byte) 0x52, (byte) 0xC7, (byte) 0x75, (byte) 0xA4, (byte) 0xFD, (byte) 0x71, (byte) 0x2D,
+        (byte) 0x7B, (byte) 0x7A, (byte) 0x31, (byte) 0x27, (byte) 0x6E, (byte) 0xAC, (byte) 0xB6, (byte) 0x40, (byte) 0x14, (byte) 0x4E, (byte) 0xB4, (byte) 0xBB, (byte) 0xB1, (byte) 0x51, (byte) 0x63, (byte) 0x29, (byte) 0x81,
+        (byte) 0x06, (byte) 0xF9    };
+        private static final byte[] DH_private = new byte[] {
+        (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0xA8, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x1B, (byte) 0x06, (byte) 0x09, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86,
+        (byte) 0xF7, (byte) 0x0D, (byte) 0x01, (byte) 0x03, (byte) 0x01, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x0C, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xFD, (byte) 0x7F, (byte) 0x53, (byte) 0x81,
+        (byte) 0x1D, (byte) 0x75, (byte) 0x12, (byte) 0x29, (byte) 0x52, (byte) 0xDF, (byte) 0x4A, (byte) 0x9C, (byte) 0x2E, (byte) 0xEC, (byte) 0xE4, (byte) 0xE7, (byte) 0xF6, (byte) 0x11, (byte) 0xB7, (byte) 0x52, (byte) 0x3C,
+        (byte) 0xEF, (byte) 0x44, (byte) 0x00, (byte) 0xC3, (byte) 0x1E, (byte) 0x3F, (byte) 0x80, (byte) 0xB6, (byte) 0x51, (byte) 0x26, (byte) 0x69, (byte) 0x45, (byte) 0x5D, (byte) 0x40, (byte) 0x22, (byte) 0x51, (byte) 0xFB,
+        (byte) 0x59, (byte) 0x3D, (byte) 0x8D, (byte) 0x58, (byte) 0xFA, (byte) 0xBF, (byte) 0xC5, (byte) 0xF5, (byte) 0xBA, (byte) 0x30, (byte) 0xF6, (byte) 0xCB, (byte) 0x9B, (byte) 0x55, (byte) 0x6C, (byte) 0xD7, (byte) 0x81,
+        (byte) 0x3B, (byte) 0x80, (byte) 0x1D, (byte) 0x34, (byte) 0x6F, (byte) 0xF2, (byte) 0x66, (byte) 0x60, (byte) 0xB7, (byte) 0x6B, (byte) 0x99, (byte) 0x50, (byte) 0xA5, (byte) 0xA4, (byte) 0x9F, (byte) 0x9F, (byte) 0xE8,
+        (byte) 0x04, (byte) 0x7B, (byte) 0x10, (byte) 0x22, (byte) 0xC2, (byte) 0x4F, (byte) 0xBB, (byte) 0xA9, (byte) 0xD7, (byte) 0xFE, (byte) 0xB7, (byte) 0xC6, (byte) 0x1B, (byte) 0xF8, (byte) 0x3B, (byte) 0x57, (byte) 0xE7,
+        (byte) 0xC6, (byte) 0xA8, (byte) 0xA6, (byte) 0x15, (byte) 0x0F, (byte) 0x04, (byte) 0xFB, (byte) 0x83, (byte) 0xF6, (byte) 0xD3, (byte) 0xC5, (byte) 0x1E, (byte) 0xC3, (byte) 0x02, (byte) 0x35, (byte) 0x54, (byte) 0x13,
+        (byte) 0x5A, (byte) 0x16, (byte) 0x91, (byte) 0x32, (byte) 0xF6, (byte) 0x75, (byte) 0xF3, (byte) 0xAE, (byte) 0x2B, (byte) 0x61, (byte) 0xD7, (byte) 0x2A, (byte) 0xEF, (byte) 0xF2, (byte) 0x22, (byte) 0x03, (byte) 0x19,
+        (byte) 0x9D, (byte) 0xD1, (byte) 0x48, (byte) 0x01, (byte) 0xC7, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xF7, (byte) 0xE1, (byte) 0xA0, (byte) 0x85, (byte) 0xD6, (byte) 0x9B, (byte) 0x3D, (byte) 0xDE,
+        (byte) 0xCB, (byte) 0xBC, (byte) 0xAB, (byte) 0x5C, (byte) 0x36, (byte) 0xB8, (byte) 0x57, (byte) 0xB9, (byte) 0x79, (byte) 0x94, (byte) 0xAF, (byte) 0xBB, (byte) 0xFA, (byte) 0x3A, (byte) 0xEA, (byte) 0x82, (byte) 0xF9,
+        (byte) 0x57, (byte) 0x4C, (byte) 0x0B, (byte) 0x3D, (byte) 0x07, (byte) 0x82, (byte) 0x67, (byte) 0x51, (byte) 0x59, (byte) 0x57, (byte) 0x8E, (byte) 0xBA, (byte) 0xD4, (byte) 0x59, (byte) 0x4F, (byte) 0xE6, (byte) 0x71,
+        (byte) 0x07, (byte) 0x10, (byte) 0x81, (byte) 0x80, (byte) 0xB4, (byte) 0x49, (byte) 0x16, (byte) 0x71, (byte) 0x23, (byte) 0xE8, (byte) 0x4C, (byte) 0x28, (byte) 0x16, (byte) 0x13, (byte) 0xB7, (byte) 0xCF, (byte) 0x09,
+        (byte) 0x32, (byte) 0x8C, (byte) 0xC8, (byte) 0xA6, (byte) 0xE1, (byte) 0x3C, (byte) 0x16, (byte) 0x7A, (byte) 0x8B, (byte) 0x54, (byte) 0x7C, (byte) 0x8D, (byte) 0x28, (byte) 0xE0, (byte) 0xA3, (byte) 0xAE, (byte) 0x1E,
+        (byte) 0x2B, (byte) 0xB3, (byte) 0xA6, (byte) 0x75, (byte) 0x91, (byte) 0x6E, (byte) 0xA3, (byte) 0x7F, (byte) 0x0B, (byte) 0xFA, (byte) 0x21, (byte) 0x35, (byte) 0x62, (byte) 0xF1, (byte) 0xFB, (byte) 0x62, (byte) 0x7A,
+        (byte) 0x01, (byte) 0x24, (byte) 0x3B, (byte) 0xCC, (byte) 0xA4, (byte) 0xF1, (byte) 0xBE, (byte) 0xA8, (byte) 0x51, (byte) 0x90, (byte) 0x89, (byte) 0xA8, (byte) 0x83, (byte) 0xDF, (byte) 0xE1, (byte) 0x5A, (byte) 0xE5,
+        (byte) 0x9F, (byte) 0x06, (byte) 0x92, (byte) 0x8B, (byte) 0x66, (byte) 0x5E, (byte) 0x80, (byte) 0x7B, (byte) 0x55, (byte) 0x25, (byte) 0x64, (byte) 0x01, (byte) 0x4C, (byte) 0x3B, (byte) 0xFE, (byte) 0xCF, (byte) 0x49,
+        (byte) 0x2A, (byte) 0x02, (byte) 0x02, (byte) 0x03, (byte) 0xFE, (byte) 0x04, (byte) 0x81, (byte) 0x83, (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x35, (byte) 0xFE, (byte) 0x44, (byte) 0x0A, (byte) 0xA3, (byte) 0xA0,
+        (byte) 0xCB, (byte) 0x52, (byte) 0xC2, (byte) 0x32, (byte) 0xCA, (byte) 0x38, (byte) 0x1F, (byte) 0x18, (byte) 0xEB, (byte) 0x27, (byte) 0x6E, (byte) 0x77, (byte) 0x25, (byte) 0x40, (byte) 0x1F, (byte) 0x64, (byte) 0x5D,
+        (byte) 0x4B, (byte) 0x59, (byte) 0x41, (byte) 0xB6, (byte) 0xCB, (byte) 0xDF, (byte) 0x73, (byte) 0xE0, (byte) 0x01, (byte) 0x5A, (byte) 0x79, (byte) 0x0D, (byte) 0x8D, (byte) 0x08, (byte) 0xE6, (byte) 0x7F, (byte) 0x86,
+        (byte) 0x58, (byte) 0xCF, (byte) 0x7F, (byte) 0x4B, (byte) 0x2E, (byte) 0xDB, (byte) 0x4C, (byte) 0xDF, (byte) 0x75, (byte) 0xB5, (byte) 0x16, (byte) 0xC4, (byte) 0xA9, (byte) 0x49, (byte) 0xEE, (byte) 0x00, (byte) 0x56,
+        (byte) 0xA0, (byte) 0x60, (byte) 0x08, (byte) 0x8E, (byte) 0x0D, (byte) 0xC7, (byte) 0xC9, (byte) 0x45, (byte) 0x0C, (byte) 0x5D, (byte) 0xB7, (byte) 0x4C, (byte) 0xC4, (byte) 0x7E, (byte) 0xAB, (byte) 0x1F, (byte) 0xEA,
+        (byte) 0xCF, (byte) 0x08, (byte) 0x6D, (byte) 0x05, (byte) 0xA1, (byte) 0x7F, (byte) 0x63, (byte) 0x6F, (byte) 0xB3, (byte) 0x91, (byte) 0xA3, (byte) 0xE1, (byte) 0xB0, (byte) 0x36, (byte) 0x02, (byte) 0x3F, (byte) 0x55,
+        (byte) 0x71, (byte) 0x38, (byte) 0x37, (byte) 0x9A, (byte) 0x19, (byte) 0xA3, (byte) 0xAF, (byte) 0xC8, (byte) 0xD5, (byte) 0x22, (byte) 0xDD, (byte) 0x00, (byte) 0x81, (byte) 0x19, (byte) 0xB6, (byte) 0x3C, (byte) 0x5F,
+        (byte) 0xD9, (byte) 0xDF, (byte) 0xFD, (byte) 0x58, (byte) 0xB1, (byte) 0xE6, (byte) 0xD1, (byte) 0xD2, (byte) 0x58, (byte) 0xEF, (byte) 0x44, (byte) 0x6E, (byte) 0x66, (byte) 0x92, (byte) 0x1E, (byte) 0x30, (byte) 0x0B,
+        (byte) 0x90, (byte) 0x8E, (byte) 0x29   };
+        private static final byte[] DH_public = new byte[] {
+        (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0xA7, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x1B, (byte) 0x06, (byte) 0x09, (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01,
+        (byte) 0x03, (byte) 0x01, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x0C, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xFD, (byte) 0x7F, (byte) 0x53, (byte) 0x81, (byte) 0x1D, (byte) 0x75, (byte) 0x12,
+        (byte) 0x29, (byte) 0x52, (byte) 0xDF, (byte) 0x4A, (byte) 0x9C, (byte) 0x2E, (byte) 0xEC, (byte) 0xE4, (byte) 0xE7, (byte) 0xF6, (byte) 0x11, (byte) 0xB7, (byte) 0x52, (byte) 0x3C, (byte) 0xEF, (byte) 0x44, (byte) 0x00,
+        (byte) 0xC3, (byte) 0x1E, (byte) 0x3F, (byte) 0x80, (byte) 0xB6, (byte) 0x51, (byte) 0x26, (byte) 0x69, (byte) 0x45, (byte) 0x5D, (byte) 0x40, (byte) 0x22, (byte) 0x51, (byte) 0xFB, (byte) 0x59, (byte) 0x3D, (byte) 0x8D,
+        (byte) 0x58, (byte) 0xFA, (byte) 0xBF, (byte) 0xC5, (byte) 0xF5, (byte) 0xBA, (byte) 0x30, (byte) 0xF6, (byte) 0xCB, (byte) 0x9B, (byte) 0x55, (byte) 0x6C, (byte) 0xD7, (byte) 0x81, (byte) 0x3B, (byte) 0x80, (byte) 0x1D,
+        (byte) 0x34, (byte) 0x6F, (byte) 0xF2, (byte) 0x66, (byte) 0x60, (byte) 0xB7, (byte) 0x6B, (byte) 0x99, (byte) 0x50, (byte) 0xA5, (byte) 0xA4, (byte) 0x9F, (byte) 0x9F, (byte) 0xE8, (byte) 0x04, (byte) 0x7B, (byte) 0x10,
+        (byte) 0x22, (byte) 0xC2, (byte) 0x4F, (byte) 0xBB, (byte) 0xA9, (byte) 0xD7, (byte) 0xFE, (byte) 0xB7, (byte) 0xC6, (byte) 0x1B, (byte) 0xF8, (byte) 0x3B, (byte) 0x57, (byte) 0xE7, (byte) 0xC6, (byte) 0xA8, (byte) 0xA6,
+        (byte) 0x15, (byte) 0x0F, (byte) 0x04, (byte) 0xFB, (byte) 0x83, (byte) 0xF6, (byte) 0xD3, (byte) 0xC5, (byte) 0x1E, (byte) 0xC3, (byte) 0x02, (byte) 0x35, (byte) 0x54, (byte) 0x13, (byte) 0x5A, (byte) 0x16, (byte) 0x91,
+        (byte) 0x32, (byte) 0xF6, (byte) 0x75, (byte) 0xF3, (byte) 0xAE, (byte) 0x2B, (byte) 0x61, (byte) 0xD7, (byte) 0x2A, (byte) 0xEF, (byte) 0xF2, (byte) 0x22, (byte) 0x03, (byte) 0x19, (byte) 0x9D, (byte) 0xD1, (byte) 0x48,
+        (byte) 0x01, (byte) 0xC7, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xF7, (byte) 0xE1, (byte) 0xA0, (byte) 0x85, (byte) 0xD6, (byte) 0x9B, (byte) 0x3D, (byte) 0xDE, (byte) 0xCB, (byte) 0xBC, (byte) 0xAB,
+        (byte) 0x5C, (byte) 0x36, (byte) 0xB8, (byte) 0x57, (byte) 0xB9, (byte) 0x79, (byte) 0x94, (byte) 0xAF, (byte) 0xBB, (byte) 0xFA, (byte) 0x3A, (byte) 0xEA, (byte) 0x82, (byte) 0xF9, (byte) 0x57, (byte) 0x4C, (byte) 0x0B,
+        (byte) 0x3D, (byte) 0x07, (byte) 0x82, (byte) 0x67, (byte) 0x51, (byte) 0x59, (byte) 0x57, (byte) 0x8E, (byte) 0xBA, (byte) 0xD4, (byte) 0x59, (byte) 0x4F, (byte) 0xE6, (byte) 0x71, (byte) 0x07, (byte) 0x10, (byte) 0x81,
+        (byte) 0x80, (byte) 0xB4, (byte) 0x49, (byte) 0x16, (byte) 0x71, (byte) 0x23, (byte) 0xE8, (byte) 0x4C, (byte) 0x28, (byte) 0x16, (byte) 0x13, (byte) 0xB7, (byte) 0xCF, (byte) 0x09, (byte) 0x32, (byte) 0x8C, (byte) 0xC8,
+        (byte) 0xA6, (byte) 0xE1, (byte) 0x3C, (byte) 0x16, (byte) 0x7A, (byte) 0x8B, (byte) 0x54, (byte) 0x7C, (byte) 0x8D, (byte) 0x28, (byte) 0xE0, (byte) 0xA3, (byte) 0xAE, (byte) 0x1E, (byte) 0x2B, (byte) 0xB3, (byte) 0xA6,
+        (byte) 0x75, (byte) 0x91, (byte) 0x6E, (byte) 0xA3, (byte) 0x7F, (byte) 0x0B, (byte) 0xFA, (byte) 0x21, (byte) 0x35, (byte) 0x62, (byte) 0xF1, (byte) 0xFB, (byte) 0x62, (byte) 0x7A, (byte) 0x01, (byte) 0x24, (byte) 0x3B,
+        (byte) 0xCC, (byte) 0xA4, (byte) 0xF1, (byte) 0xBE, (byte) 0xA8, (byte) 0x51, (byte) 0x90, (byte) 0x89, (byte) 0xA8, (byte) 0x83, (byte) 0xDF, (byte) 0xE1, (byte) 0x5A, (byte) 0xE5, (byte) 0x9F, (byte) 0x06, (byte) 0x92,
+        (byte) 0x8B, (byte) 0x66, (byte) 0x5E, (byte) 0x80, (byte) 0x7B, (byte) 0x55, (byte) 0x25, (byte) 0x64, (byte) 0x01, (byte) 0x4C, (byte) 0x3B, (byte) 0xFE, (byte) 0xCF, (byte) 0x49, (byte) 0x2A, (byte) 0x02, (byte) 0x02,
+        (byte) 0x03, (byte) 0xFE, (byte) 0x03, (byte) 0x81, (byte) 0x85, (byte) 0x00, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xD4, (byte) 0xC2, (byte) 0xC2, (byte) 0x84, (byte) 0xEB, (byte) 0xEC, (byte) 0xB6,
+        (byte) 0xF1, (byte) 0x29, (byte) 0x2B, (byte) 0xAB, (byte) 0x8F, (byte) 0xC1, (byte) 0x48, (byte) 0x4C, (byte) 0x47, (byte) 0xCE, (byte) 0x0B, (byte) 0x97, (byte) 0x4C, (byte) 0xFC, (byte) 0x27, (byte) 0x10, (byte) 0x0A,
+        (byte) 0x5F, (byte) 0x3E, (byte) 0xE6, (byte) 0xF9, (byte) 0x9B, (byte) 0x15, (byte) 0xDF, (byte) 0x83, (byte) 0x01, (byte) 0xFA, (byte) 0x69, (byte) 0x57, (byte) 0xEC, (byte) 0x6B, (byte) 0x68, (byte) 0xC7, (byte) 0x96,
+        (byte) 0x33, (byte) 0x98, (byte) 0xA4, (byte) 0xB0, (byte) 0xA3, (byte) 0x18, (byte) 0x01, (byte) 0x66, (byte) 0x7A, (byte) 0x4A, (byte) 0xF3, (byte) 0x3C, (byte) 0xD9, (byte) 0x2A, (byte) 0x48, (byte) 0xFD, (byte) 0x3A,
+        (byte) 0x31, (byte) 0xFC, (byte) 0x97, (byte) 0x52, (byte) 0x36, (byte) 0x20, (byte) 0x0E, (byte) 0x69, (byte) 0xB6, (byte) 0x32, (byte) 0x8B, (byte) 0x4E, (byte) 0xDA, (byte) 0x8B, (byte) 0x04, (byte) 0x88, (byte) 0xF8,
+        (byte) 0x30, (byte) 0xA9, (byte) 0x65, (byte) 0x68, (byte) 0x47, (byte) 0xBB, (byte) 0xA1, (byte) 0xF6, (byte) 0xD6, (byte) 0x18, (byte) 0x11, (byte) 0x48, (byte) 0x8D, (byte) 0x8F, (byte) 0x4B, (byte) 0xC1, (byte) 0xE1,
+        (byte) 0xA4, (byte) 0x43, (byte) 0x83, (byte) 0x1F, (byte) 0x6B, (byte) 0x6D, (byte) 0xEE, (byte) 0xA7, (byte) 0xA3, (byte) 0x5F, (byte) 0xD2, (byte) 0x95, (byte) 0x09, (byte) 0xD4, (byte) 0xEA, (byte) 0x85, (byte) 0x0C,
+        (byte) 0xA5, (byte) 0xC9, (byte) 0x93, (byte) 0xCE, (byte) 0xC1, (byte) 0x1D, (byte) 0x30, (byte) 0x73, (byte) 0xA3, (byte) 0xE1, (byte) 0x69, (byte) 0xA8, (byte) 0x11, (byte) 0x98, (byte) 0x78, (byte) 0xF3, (byte) 0xF9,
+        (byte) 0x8F, (byte) 0x04    };
+
+
+
+    private static final HashMap<String, KeySpec> keys = new HashMap<String, KeySpec>();
+    static {
+        keys.put("DH_public", new X509EncodedKeySpec(DH_public));
+        keys.put("DH_private", new PKCS8EncodedKeySpec(DH_private));
+        keys.put("DSA_public", new X509EncodedKeySpec(DSA_public));
+        keys.put("DSA_private", new PKCS8EncodedKeySpec(DSA_private));
+        keys.put("RSA_public", new X509EncodedKeySpec(RSA_public));
+        keys.put("RSA_private", new PKCS8EncodedKeySpec(RSA_private));
+    }
+
+    public static PrivateKey getPrivateKey(String algorithmName) throws NoSuchAlgorithmException, InvalidKeySpecException
+    {
+        KeyFactory factory = KeyFactory.getInstance(algorithmName);
+        return factory.generatePrivate(keys.get(algorithmName + "_private"));
+    }
+
+    public static PublicKey getPublicKey(String algorithmName) throws NoSuchAlgorithmException, InvalidKeySpecException
+    {
+        KeyFactory factory = KeyFactory.getInstance(algorithmName);
+        return factory.generatePublic(keys.get(algorithmName + "_public"));
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/KeyAgreementHelper.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/KeyAgreementHelper.java
new file mode 100644
index 0000000..7580aea
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/KeyAgreementHelper.java
@@ -0,0 +1,48 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import javax.crypto.KeyAgreement;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class KeyAgreementHelper extends TestHelper<KeyPair> {
+
+    private final String algorithmName;
+
+    public KeyAgreementHelper(String algorithmName) {
+        this.algorithmName = algorithmName;
+    }
+
+    @Override public void test(KeyPair keyPair) throws Exception {
+        test(keyPair.getPrivate(), keyPair.getPublic());
+    }
+
+    void test(PrivateKey encryptKey, PublicKey decryptKey) throws Exception {
+        KeyAgreement keyAgreement = KeyAgreement.getInstance(algorithmName);
+        keyAgreement.init(encryptKey);
+        keyAgreement.doPhase(decryptKey, true);
+        assertNotNull("generated secret is null", keyAgreement.generateSecret());
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/SignatureHelper.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/SignatureHelper.java
new file mode 100644
index 0000000..9717a58
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/SignatureHelper.java
@@ -0,0 +1,54 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertTrue;
+
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class SignatureHelper extends TestHelper<KeyPair> {
+
+    private final String algorithmName;
+    private final String plainData = "some data do sign and verify";
+
+    public SignatureHelper(String algorithmName) {
+        this.algorithmName = algorithmName;
+    }
+
+    @Override
+    public void test(KeyPair keyPair) throws Exception {
+        test(keyPair.getPrivate(), keyPair.getPublic());
+    }
+
+    public void test(PrivateKey encryptKey, PublicKey decryptKey) throws Exception {
+        Signature signature = Signature.getInstance(algorithmName);
+        signature.initSign(encryptKey);
+        signature.update(plainData.getBytes("UTF-8"));
+        byte[] signed = signature.sign();
+
+        signature.initVerify(decryptKey);
+        signature.update(plainData.getBytes("UTF-8"));
+        assertTrue("signature could not be verified", signature.verify(signed));
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/StandardNames.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/StandardNames.java
new file mode 100644
index 0000000..917df97
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/StandardNames.java
@@ -0,0 +1,455 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * This class defines expected string names for protocols, key types,
+ * client and server auth types, cipher suites.
+ *
+ * Initially based on "Appendix A: Standard Names" of
+ * <a href="http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA">
+ * Java &trade; Secure Socket Extension (JSSE) Reference Guide
+ * for the Java &trade; 2 Platform Standard Edition 5
+ * </a>.
+ *
+ * Updated based on the
+ * <a href="http://download.java.net/jdk8/docs/technotes/guides/security/SunProviders.html">
+ * Java &trade; Cryptography Architecture Oracle Providers Documentation
+ * for Java &trade; Platform Standard Edition 7
+ * </a>.
+ * See also the
+ * <a href="http://download.java.net/jdk8/docs/technotes/guides/security/StandardNames.html">
+ * Java &trade; Cryptography Architecture Standard Algorithm Name Documentation
+ * </a>.
+ *
+ * Further updates based on the
+ * <a href=http://java.sun.com/javase/6/docs/technotes/guides/security/p11guide.html">
+ * Java &trade; PKCS#11 Reference Guide
+ * </a>.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class StandardNames {
+    public static final boolean IS_RI =
+            !"Dalvik Core Library".equals(System.getProperty("java.specification.name"));
+    public static final String JSSE_PROVIDER_NAME = IS_RI ? "Conscrypt" : "AndroidOpenSSL";
+
+    public static final String KEY_MANAGER_FACTORY_DEFAULT = IS_RI ? "SunX509" : "PKIX";
+    public static final String TRUST_MANAGER_FACTORY_DEFAULT = "PKIX";
+
+    public static final String KEY_STORE_ALGORITHM = IS_RI ? "JKS" : "BKS";
+
+    /**
+     * RFC 5746's Signaling Cipher Suite Value to indicate a request for secure renegotiation
+     */
+    public static final String CIPHER_SUITE_SECURE_RENEGOTIATION =
+            "TLS_EMPTY_RENEGOTIATION_INFO_SCSV";
+
+    /**
+     * From https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00 it is a
+     * signaling cipher suite value (SCSV) to indicate that this request is a
+     * protocol fallback (e.g., TLS 1.0 -> SSL 3.0) because the server didn't respond
+     * to the first request.
+     */
+    public static final String CIPHER_SUITE_FALLBACK = "TLS_FALLBACK_SCSV";
+
+    private static final HashMap<String, HashSet<String>> CIPHER_MODES =
+            new HashMap<String, HashSet<String>>();
+
+    private static final HashMap<String, HashSet<String>> CIPHER_PADDINGS =
+            new HashMap<String, HashSet<String>>();
+
+    private static final HashMap<String, String[]> SSL_CONTEXT_PROTOCOLS_ENABLED =
+            new HashMap<String, String[]>();
+
+    private static void provideCipherModes(String algorithm, String newModes[]) {
+        HashSet<String> modes = CIPHER_MODES.get(algorithm);
+        if (modes == null) {
+            modes = new HashSet<String>();
+            CIPHER_MODES.put(algorithm, modes);
+        }
+        modes.addAll(Arrays.asList(newModes));
+    }
+    private static void provideCipherPaddings(String algorithm, String newPaddings[]) {
+        HashSet<String> paddings = CIPHER_PADDINGS.get(algorithm);
+        if (paddings == null) {
+            paddings = new HashSet<String>();
+            CIPHER_PADDINGS.put(algorithm, paddings);
+        }
+        paddings.addAll(Arrays.asList(newPaddings));
+    }
+    private static void provideSslContextEnabledProtocols(
+            String algorithm, TLSVersion minimum, TLSVersion maximum) {
+        if (minimum.ordinal() > maximum.ordinal()) {
+            throw new RuntimeException("TLS version: minimum > maximum");
+        }
+        int versionsLength = maximum.ordinal() - minimum.ordinal() + 1;
+        String[] versionNames = new String[versionsLength];
+        for (int i = 0; i < versionsLength; i++) {
+            versionNames[i] = TLSVersion.values()[i + minimum.ordinal()].name;
+        }
+        SSL_CONTEXT_PROTOCOLS_ENABLED.put(algorithm, versionNames);
+    }
+    static {
+        // TODO: provideCipherModes and provideCipherPaddings for other Ciphers
+        provideCipherModes("AES", new String[] {"CBC", "CFB", "CTR", "CTS", "ECB", "OFB"});
+        provideCipherPaddings("AES", new String[] {"NoPadding", "PKCS5Padding"});
+        // TODO: None?
+        provideCipherModes("RSA", new String[] {"ECB"});
+        // TODO: OAEPPadding
+        provideCipherPaddings("RSA", new String[] {"NoPadding", "PKCS1Padding"});
+
+        // Fixups for dalvik
+        if (!IS_RI) {
+            provideCipherPaddings("AES", new String[] {"PKCS7Padding"});
+        }
+
+        provideSslContextEnabledProtocols("TLS", TLSVersion.TLSv1, TLSVersion.TLSv13);
+        provideSslContextEnabledProtocols("TLSv1", TLSVersion.TLSv1, TLSVersion.TLSv12);
+        provideSslContextEnabledProtocols("TLSv1.1", TLSVersion.TLSv1, TLSVersion.TLSv12);
+        provideSslContextEnabledProtocols("TLSv1.2", TLSVersion.TLSv1, TLSVersion.TLSv12);
+        provideSslContextEnabledProtocols("TLSv1.3", TLSVersion.TLSv1, TLSVersion.TLSv13);
+        provideSslContextEnabledProtocols("Default", TLSVersion.TLSv1, TLSVersion.TLSv13);
+    }
+
+    public static final String SSL_CONTEXT_PROTOCOLS_DEFAULT = "Default";
+    public static final Set<String> SSL_CONTEXT_PROTOCOLS = new HashSet<String>(
+            Arrays.asList(SSL_CONTEXT_PROTOCOLS_DEFAULT, "TLS", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"));
+    public static final Set<String> SSL_CONTEXT_PROTOCOLS_WITH_DEFAULT_CONFIG = new HashSet<String>(
+            Arrays.asList(SSL_CONTEXT_PROTOCOLS_DEFAULT, "TLS", "TLSv1.3"));
+
+    public static final Set<String> KEY_TYPES = new HashSet<String>(
+            Arrays.asList("RSA", "DSA", "DH_RSA", "DH_DSA", "EC", "EC_EC", "EC_RSA"));
+    static {
+        if (IS_RI) {
+            // DH_* are specified by standard names, but do not seem to be supported by RI
+            KEY_TYPES.remove("DH_RSA");
+            KEY_TYPES.remove("DH_DSA");
+        }
+    }
+
+    public static final Set<String> SSL_SOCKET_PROTOCOLS =
+            new HashSet<String>(Arrays.asList("TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"));
+
+    private enum TLSVersion {
+        SSLv3("SSLv3"),
+        TLSv1("TLSv1"),
+        TLSv11("TLSv1.1"),
+        TLSv12("TLSv1.2"),
+        TLSv13("TLSv1.3"),
+        ;
+
+        private final String name;
+
+        TLSVersion(String name) {
+            this.name = name;
+        }
+    }
+
+    /**
+     * Valid values for X509TrustManager.checkClientTrusted authType,
+     * either the algorithm of the public key or UNKNOWN.
+     */
+    public static final Set<String> CLIENT_AUTH_TYPES =
+            new HashSet<String>(Arrays.asList("RSA", "DSA", "EC", "UNKNOWN"));
+
+    /**
+     * Valid values for X509TrustManager.checkServerTrusted authType,
+     * either key exchange algorithm part of the cipher suite, UNKNOWN,
+     * or GENERIC (for TLS 1.3 cipher suites that don't imply a specific
+     * key exchange method).
+     */
+    public static final Set<String> SERVER_AUTH_TYPES = new HashSet<String>(Arrays.asList("DHE_DSS",
+            "DHE_DSS_EXPORT", "DHE_RSA", "DHE_RSA_EXPORT", "DH_DSS_EXPORT", "DH_RSA_EXPORT",
+            "DH_anon", "DH_anon_EXPORT", "KRB5", "KRB5_EXPORT", "RSA", "RSA_EXPORT",
+            "RSA_EXPORT1024", "ECDH_ECDSA", "ECDH_RSA", "ECDHE_ECDSA", "ECDHE_RSA", "UNKNOWN",
+            "GENERIC"));
+
+    public static final String CIPHER_SUITE_INVALID = "SSL_NULL_WITH_NULL_NULL";
+
+    private static final Set<String> CIPHER_SUITES = new LinkedHashSet<String>();
+
+    private static void addOpenSsl(String cipherSuite) {
+        CIPHER_SUITES.add(cipherSuite);
+    }
+
+    static {
+        addOpenSsl("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA");
+        addOpenSsl("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA");
+        addOpenSsl("TLS_RSA_WITH_AES_256_CBC_SHA");
+        addOpenSsl("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA");
+        addOpenSsl("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
+        addOpenSsl("TLS_RSA_WITH_AES_128_CBC_SHA");
+        addOpenSsl("SSL_RSA_WITH_3DES_EDE_CBC_SHA");
+
+        // TLSv1.2 cipher suites
+        addOpenSsl("TLS_RSA_WITH_AES_128_GCM_SHA256");
+        addOpenSsl("TLS_RSA_WITH_AES_256_GCM_SHA384");
+        addOpenSsl("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
+        addOpenSsl("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384");
+        addOpenSsl("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");
+        addOpenSsl("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384");
+        addOpenSsl("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256");
+        addOpenSsl("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256");
+
+        // Pre-Shared Key (PSK) cipher suites
+        addOpenSsl("TLS_PSK_WITH_AES_128_CBC_SHA");
+        addOpenSsl("TLS_PSK_WITH_AES_256_CBC_SHA");
+        addOpenSsl("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA");
+        addOpenSsl("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA");
+        addOpenSsl("TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256");
+
+        // TLS 1.3 cipher suites
+        addOpenSsl("TLS_AES_128_GCM_SHA256");
+        addOpenSsl("TLS_AES_256_GCM_SHA384");
+        addOpenSsl("TLS_CHACHA20_POLY1305_SHA256");
+
+        // RFC 5746's Signaling Cipher Suite Value to indicate a request for secure renegotiation
+        addOpenSsl(CIPHER_SUITE_SECURE_RENEGOTIATION);
+
+        // From https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00 to indicate
+        // TLS fallback request
+        addOpenSsl(CIPHER_SUITE_FALLBACK);
+    }
+
+    /**
+     * Cipher suites that are not negotiated when TLSv1.2 is selected on the RI.
+     */
+    public static final List<String> CIPHER_SUITES_OBSOLETE_TLS12 = Arrays.asList(
+            "SSL_RSA_WITH_DES_CBC_SHA",
+            "SSL_DHE_RSA_WITH_DES_CBC_SHA",
+            "SSL_DHE_DSS_WITH_DES_CBC_SHA",
+            "SSL_DH_anon_WITH_DES_CBC_SHA",
+            "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
+            "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
+            "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
+            "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+            "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
+            "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA");
+
+    /**
+     * Cipher suites that are only supported with TLS 1.3.
+     */
+    public static final List<String> CIPHER_SUITES_TLS13 = Arrays.asList(
+            "TLS_AES_128_GCM_SHA256",
+            "TLS_AES_256_GCM_SHA384",
+            "TLS_CHACHA20_POLY1305_SHA256");
+
+    // NOTE: This list needs to be kept in sync with Javadoc of javax.net.ssl.SSLSocket and
+    // javax.net.ssl.SSLEngine.
+    private static final List<String> CIPHER_SUITES_AES_HARDWARE = Arrays.asList(
+            "TLS_AES_128_GCM_SHA256",
+            "TLS_AES_256_GCM_SHA384",
+            "TLS_CHACHA20_POLY1305_SHA256",
+            "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+            "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+            "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
+            "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+            "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
+            "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+            "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
+            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
+            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
+            "TLS_RSA_WITH_AES_128_GCM_SHA256",
+            "TLS_RSA_WITH_AES_256_GCM_SHA384",
+            "TLS_RSA_WITH_AES_128_CBC_SHA",
+            "TLS_RSA_WITH_AES_256_CBC_SHA",
+            CIPHER_SUITE_SECURE_RENEGOTIATION);
+
+    // NOTE: This list needs to be kept in sync with Javadoc of javax.net.ssl.SSLSocket and
+    // javax.net.ssl.SSLEngine.
+    private static final List<String> CIPHER_SUITES_SOFTWARE = Arrays.asList(
+            "TLS_AES_128_GCM_SHA256",
+            "TLS_AES_256_GCM_SHA384",
+            "TLS_CHACHA20_POLY1305_SHA256",
+            "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
+            "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+            "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+            "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
+            "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+            "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+            "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
+            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
+            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
+            "TLS_RSA_WITH_AES_128_GCM_SHA256",
+            "TLS_RSA_WITH_AES_256_GCM_SHA384",
+            "TLS_RSA_WITH_AES_128_CBC_SHA",
+            "TLS_RSA_WITH_AES_256_CBC_SHA",
+            CIPHER_SUITE_SECURE_RENEGOTIATION);
+
+    // NOTE: This list needs to be kept in sync with Javadoc of javax.net.ssl.SSLSocket and
+    // javax.net.ssl.SSLEngine.
+    public static final List<String> CIPHER_SUITES_DEFAULT = CpuFeatures.isAESHardwareAccelerated()
+            ? CIPHER_SUITES_AES_HARDWARE
+            : CIPHER_SUITES_SOFTWARE;
+
+    // NOTE: This list needs to be kept in sync with Javadoc of javax.net.ssl.SSLSocket and
+    // javax.net.ssl.SSLEngine.
+    public static final List<String> CIPHER_SUITES_DEFAULT_PSK = Arrays.asList(
+            "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
+            "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
+            "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
+            "TLS_PSK_WITH_AES_128_CBC_SHA",
+            "TLS_PSK_WITH_AES_256_CBC_SHA");
+
+    // Should be updated to match BoringSSL's defaults when they change.
+    // https://boringssl.googlesource.com/boringssl/+/master/ssl/t1_lib.cc#289
+    private static final List<String> ELLIPTIC_CURVES_DEFAULT =
+            Arrays.asList("x25519 (29)", "secp256r1 (23)", "secp384r1 (24)");
+
+    /**
+     * Asserts that the cipher suites array is non-null and that it
+     * all of its contents are cipher suites known to this
+     * implementation. As a convenience, returns any unenabled cipher
+     * suites in a test for those that want to verify separately that
+     * all cipher suites were included.
+     */
+    private static Set<String> assertValidCipherSuites(
+            Set<String> expected, String[] cipherSuites) {
+        assertNotNull(cipherSuites);
+        assertTrue(cipherSuites.length != 0);
+
+        // Make sure all cipherSuites names are expected
+        HashSet<String> remainingCipherSuites = new HashSet<String>(expected);
+        HashSet<String> unknownCipherSuites = new HashSet<String>();
+        for (String cipherSuite : cipherSuites) {
+            boolean removed = remainingCipherSuites.remove(cipherSuite);
+            if (!removed) {
+                unknownCipherSuites.add(cipherSuite);
+            }
+        }
+        assertEquals("Unknown cipher suites", Collections.EMPTY_SET, unknownCipherSuites);
+        return remainingCipherSuites;
+    }
+
+    /**
+     * After using assertValidCipherSuites on cipherSuites,
+     * assertSupportedCipherSuites additionally verifies that all
+     * supported cipher suites where in the input array.
+     */
+    private static void assertSupportedCipherSuites(Set<String> expected, String[] cipherSuites) {
+        Set<String> remainingCipherSuites = assertValidCipherSuites(expected, cipherSuites);
+        assertEquals("Missing cipher suites", Collections.EMPTY_SET, remainingCipherSuites);
+        assertEquals(expected.size(), cipherSuites.length);
+    }
+
+    /**
+     * Asserts that the protocols array is non-null and that it all of
+     * its contents are protocols known to this implementation. As a
+     * convenience, returns any unenabled protocols in a test for
+     * those that want to verify separately that all protocols were
+     * included.
+     */
+    private static Set<String> assertValidProtocols(Set<String> expected, String[] protocols) {
+        assertNotNull(protocols);
+        assertTrue(protocols.length != 0);
+
+        // Make sure all protocols names are expected
+        HashSet<String> remainingProtocols = new HashSet<String>(expected);
+        HashSet<String> unknownProtocols = new HashSet<String>();
+        for (String protocol : protocols) {
+            if (!remainingProtocols.remove(protocol)) {
+                unknownProtocols.add(protocol);
+            }
+        }
+        assertEquals("Unknown protocols", Collections.EMPTY_SET, unknownProtocols);
+        return remainingProtocols;
+    }
+
+    /**
+     * After using assertValidProtocols on protocols,
+     * assertSupportedProtocols additionally verifies that all
+     * supported protocols where in the input array.
+     */
+    private static void assertSupportedProtocols(Set<String> expected, String[] protocols) {
+        Set<String> remainingProtocols = assertValidProtocols(expected, protocols);
+        assertEquals("Missing protocols", Collections.EMPTY_SET, remainingProtocols);
+        assertEquals(expected.size(), protocols.length);
+    }
+
+    /**
+     * Asserts that the provided list of protocols matches the supported list of protocols.
+     */
+    public static void assertSupportedProtocols(String[] protocols) {
+        assertSupportedProtocols(SSL_SOCKET_PROTOCOLS, protocols);
+    }
+
+    /**
+     * Assert that the provided list of cipher suites contains only the supported cipher suites.
+     */
+    public static void assertValidCipherSuites(String[] cipherSuites) {
+        assertValidCipherSuites(CIPHER_SUITES, cipherSuites);
+    }
+
+    /**
+     * Assert that the provided list of cipher suites matches the supported list.
+     */
+    public static void assertSupportedCipherSuites(String[] cipherSuites) {
+        assertSupportedCipherSuites(CIPHER_SUITES, cipherSuites);
+    }
+
+    /**
+     * Assert cipher suites match the default list in content and priority order and contain
+     * only cipher suites permitted by default.
+     */
+    public static void assertDefaultCipherSuites(String[] cipherSuites) {
+        assertValidCipherSuites(cipherSuites);
+
+        Set<String> expected = new TreeSet<String>(CIPHER_SUITES_DEFAULT);
+        Set<String> actual = new TreeSet<String>(Arrays.asList(cipherSuites));
+        assertEquals(expected, actual);
+    }
+
+    public static void assertDefaultEllipticCurves(String[] curves) {
+        assertEquals(ELLIPTIC_CURVES_DEFAULT, Arrays.asList(curves));
+    }
+
+    public static void assertSSLContextEnabledProtocols(String version, String[] protocols) {
+        assertEquals("For protocol \"" + version + "\"",
+                Arrays.toString(SSL_CONTEXT_PROTOCOLS_ENABLED.get(version)),
+                Arrays.toString(protocols));
+    }
+
+    /**
+     * Get all supported mode names for the given cipher.
+     */
+    public static Set<String> getModesForCipher(String cipher) {
+        return CIPHER_MODES.get(cipher);
+    }
+
+    /**
+     * Get all supported padding names for the given cipher.
+     */
+    public static Set<String> getPaddingsForCipher(String cipher) {
+        return CIPHER_PADDINGS.get(cipher);
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/TestHelper.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/TestHelper.java
new file mode 100644
index 0000000..92f3120
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/TestHelper.java
@@ -0,0 +1,25 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class TestHelper<T> {
+    public abstract void test(T testObject) throws Exception;
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/TestKeyStore.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/TestKeyStore.java
new file mode 100644
index 0000000..86568a3
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/java/security/TestKeyStore.java
@@ -0,0 +1,1155 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.java.security;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStore.PasswordProtection;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStore.TrustedCertificateEntry;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.UnrecoverableEntryException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import javax.crypto.spec.DHParameterSpec;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.security.auth.x500.X500Principal;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.CRLReason;
+import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.GeneralSubtree;
+import org.bouncycastle.asn1.x509.KeyPurposeId;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.NameConstraints;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.cert.ocsp.BasicOCSPResp;
+import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
+import org.bouncycastle.cert.ocsp.CertificateID;
+import org.bouncycastle.cert.ocsp.CertificateStatus;
+import org.bouncycastle.cert.ocsp.OCSPResp;
+import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
+import org.bouncycastle.cert.ocsp.RevokedStatus;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import com.android.org.conscrypt.javax.net.ssl.TestKeyManager;
+import com.android.org.conscrypt.javax.net.ssl.TestTrustManager;
+
+/**
+ * TestKeyStore is a convenience class for other tests that
+ * want a canned KeyStore with a variety of key pairs.
+ *
+ * Creating a key store is relatively slow, so a singleton instance is
+ * accessible via TestKeyStore.get().
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class TestKeyStore {
+    /** Size of DSA keys to generate for testing. */
+    private static final int DSA_KEY_SIZE_BITS = 1024;
+
+    /** Size of EC keys to generate for testing. */
+    private static final int EC_KEY_SIZE_BITS = 256;
+
+    /** Size of RSA keys to generate for testing. */
+    private static final int RSA_KEY_SIZE_BITS = 1024;
+
+    // Generated with: openssl dhparam -C 1024
+    private static final BigInteger DH_PARAMS_P = new BigInteger(1,
+            new byte[] {
+                    (byte) 0xA2, (byte) 0x31, (byte) 0xB4, (byte) 0xB3, (byte) 0x6D, (byte) 0x9B,
+                    (byte) 0x7E, (byte) 0xF4, (byte) 0xE7, (byte) 0x21, (byte) 0x51, (byte) 0x40,
+                    (byte) 0xEB, (byte) 0xC6, (byte) 0xB6, (byte) 0xD6, (byte) 0x54, (byte) 0x56,
+                    (byte) 0x72, (byte) 0xBE, (byte) 0x43, (byte) 0x18, (byte) 0x30, (byte) 0x5C,
+                    (byte) 0x15, (byte) 0x5A, (byte) 0xF9, (byte) 0x19, (byte) 0x62, (byte) 0xAD,
+                    (byte) 0xF4, (byte) 0x29, (byte) 0xCB, (byte) 0xC6, (byte) 0xF6, (byte) 0x64,
+                    (byte) 0x0B, (byte) 0x9D, (byte) 0x23, (byte) 0x80, (byte) 0xF9, (byte) 0x5B,
+                    (byte) 0x1C, (byte) 0x1C, (byte) 0x6A, (byte) 0xB4, (byte) 0xEA, (byte) 0xB9,
+                    (byte) 0x80, (byte) 0x98, (byte) 0x8B, (byte) 0xAF, (byte) 0x15, (byte) 0xA8,
+                    (byte) 0x5C, (byte) 0xC4, (byte) 0xB0, (byte) 0x41, (byte) 0x29, (byte) 0x66,
+                    (byte) 0x9F, (byte) 0x9F, (byte) 0x1F, (byte) 0x88, (byte) 0x50, (byte) 0x97,
+                    (byte) 0x38, (byte) 0x0B, (byte) 0x01, (byte) 0x16, (byte) 0xD6, (byte) 0x84,
+                    (byte) 0x1D, (byte) 0x48, (byte) 0x6F, (byte) 0x7C, (byte) 0x06, (byte) 0x8C,
+                    (byte) 0x6E, (byte) 0x68, (byte) 0xCD, (byte) 0x38, (byte) 0xE6, (byte) 0x22,
+                    (byte) 0x30, (byte) 0x61, (byte) 0x37, (byte) 0x02, (byte) 0x3D, (byte) 0x47,
+                    (byte) 0x62, (byte) 0xCE, (byte) 0xB9, (byte) 0x1A, (byte) 0x69, (byte) 0x9D,
+                    (byte) 0xA1, (byte) 0x9F, (byte) 0x10, (byte) 0xA1, (byte) 0xAA, (byte) 0x70,
+                    (byte) 0xF7, (byte) 0x27, (byte) 0x9C, (byte) 0xD4, (byte) 0xA5, (byte) 0x15,
+                    (byte) 0xE2, (byte) 0x15, (byte) 0x0C, (byte) 0x20, (byte) 0x90, (byte) 0x08,
+                    (byte) 0xB6, (byte) 0xF5, (byte) 0xDF, (byte) 0x1C, (byte) 0xCB, (byte) 0x82,
+                    (byte) 0x6D, (byte) 0xC0, (byte) 0xE1, (byte) 0xBD, (byte) 0xCC, (byte) 0x4A,
+                    (byte) 0x76, (byte) 0xE3,
+            });
+
+    // generator of 2
+    private static final BigInteger DH_PARAMS_G = BigInteger.valueOf(2);
+
+    private static TestKeyStore ROOT_CA;
+    private static TestKeyStore INTERMEDIATE_CA;
+    private static TestKeyStore INTERMEDIATE_CA_2;
+    private static TestKeyStore INTERMEDIATE_CA_EC;
+
+    private static TestKeyStore SERVER;
+    private static TestKeyStore SERVER_HOSTNAME;
+    private static TestKeyStore CLIENT;
+    private static TestKeyStore CLIENT_CERTIFICATE;
+    private static TestKeyStore CLIENT_EC_RSA_CERTIFICATE;
+    private static TestKeyStore CLIENT_EC_EC_CERTIFICATE;
+
+    private static TestKeyStore CLIENT_2;
+
+    static {
+        if (!StandardNames.IS_RI
+                && !BouncyCastleProvider.class.getName().startsWith("com.android")) {
+            // If we run outside of the Android system, we need to make sure
+            // that the BouncyCastleProvider's static field keyInfoConverters
+            // is initialized. This happens in the default constructor only.
+            new BouncyCastleProvider();
+        }
+    }
+
+    private static final byte[] LOCAL_HOST_ADDRESS = {127, 0, 0, 1};
+    private static final String LOCAL_HOST_NAME = "localhost";
+    private static final String LOCAL_HOST_NAME_IPV6 = "ip6-localhost";
+    public static final String CERT_HOSTNAME = "example.com";
+
+    public final KeyStore keyStore;
+    public final char[] storePassword;
+    public final char[] keyPassword;
+    public final KeyManager[] keyManagers;
+    public final TrustManager[] trustManagers;
+    public final TrustManager trustManager;
+
+    private TestKeyStore(KeyStore keyStore, char[] storePassword, char[] keyPassword) {
+        this.keyStore = keyStore;
+        this.storePassword = storePassword;
+        this.keyPassword = keyPassword;
+        this.keyManagers = createKeyManagers(keyStore, storePassword);
+        this.trustManagers = createTrustManagers(keyStore);
+        this.trustManager = trustManagers[0];
+    }
+
+    public static KeyManager[] createKeyManagers(KeyStore keyStore, char[] storePassword) {
+        try {
+            String kmfa = KeyManagerFactory.getDefaultAlgorithm();
+            KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfa);
+            kmf.init(keyStore, storePassword);
+            return TestKeyManager.wrap(kmf.getKeyManagers());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static TrustManager[] createTrustManagers(final KeyStore keyStore) {
+        try {
+            String tmfa = TrustManagerFactory.getDefaultAlgorithm();
+            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfa);
+            tmf.init(keyStore);
+            return TestTrustManager.wrap(tmf.getTrustManagers());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Lazily create shared test certificates.
+     */
+    private static synchronized void initCerts() {
+        if (ROOT_CA != null) {
+            return;
+        }
+        ROOT_CA = new Builder()
+                          .aliasPrefix("RootCA")
+                          .subject("CN=Test Root Certificate Authority")
+                          .ca(true)
+                          .certificateSerialNumber(BigInteger.valueOf(1))
+                          .build();
+        INTERMEDIATE_CA_EC = new Builder()
+                                     .aliasPrefix("IntermediateCA-EC")
+                                     .keyAlgorithms("EC")
+                                     .subject("CN=Test Intermediate Certificate Authority ECDSA")
+                                     .ca(true)
+                                     .signer(ROOT_CA.getPrivateKey("RSA", "RSA"))
+                                     .rootCa(ROOT_CA.getRootCertificate("RSA"))
+                                     .certificateSerialNumber(BigInteger.valueOf(2))
+                                     .build();
+        INTERMEDIATE_CA = new Builder()
+                                  .aliasPrefix("IntermediateCA")
+                                  .subject("CN=Test Intermediate Certificate Authority")
+                                  .ca(true)
+                                  .signer(ROOT_CA.getPrivateKey("RSA", "RSA"))
+                                  .rootCa(ROOT_CA.getRootCertificate("RSA"))
+                                  .certificateSerialNumber(BigInteger.valueOf(2))
+                                  .build();
+        SERVER = new Builder()
+                         .aliasPrefix("server")
+                         .signer(INTERMEDIATE_CA.getPrivateKey("RSA", "RSA"))
+                         .rootCa(INTERMEDIATE_CA.getRootCertificate("RSA"))
+                         .addSubjectAltNameIpAddress(LOCAL_HOST_ADDRESS)
+                         .certificateSerialNumber(BigInteger.valueOf(3))
+                         .build();
+        SERVER_HOSTNAME = new Builder()
+                                  .aliasPrefix("server-hostname")
+                                  .signer(INTERMEDIATE_CA.getPrivateKey("RSA", "RSA"))
+                                  .rootCa(INTERMEDIATE_CA.getRootCertificate("RSA"))
+                                  .addSubjectAltNameDnsName(CERT_HOSTNAME)
+                                  .certificateSerialNumber(BigInteger.valueOf(4))
+                                  .build();
+        CLIENT = new TestKeyStore(createClient(INTERMEDIATE_CA.keyStore), null, null);
+        CLIENT_EC_RSA_CERTIFICATE = new Builder()
+                                            .aliasPrefix("client-ec")
+                                            .keyAlgorithms("EC")
+                                            .subject("emailAddress=test-ec@user")
+                                            .signer(INTERMEDIATE_CA.getPrivateKey("RSA", "RSA"))
+                                            .rootCa(INTERMEDIATE_CA.getRootCertificate("RSA"))
+                                            .build();
+        CLIENT_EC_EC_CERTIFICATE = new Builder()
+                                           .aliasPrefix("client-ec")
+                                           .keyAlgorithms("EC")
+                                           .subject("emailAddress=test-ec@user")
+                                           .signer(INTERMEDIATE_CA_EC.getPrivateKey("EC", "RSA"))
+                                           .rootCa(INTERMEDIATE_CA_EC.getRootCertificate("RSA"))
+                                           .build();
+        CLIENT_CERTIFICATE = new Builder()
+                                     .aliasPrefix("client")
+                                     .subject("emailAddress=test@user")
+                                     .signer(INTERMEDIATE_CA.getPrivateKey("RSA", "RSA"))
+                                     .rootCa(INTERMEDIATE_CA.getRootCertificate("RSA"))
+                                     .build();
+        TestKeyStore rootCa2 = new Builder()
+                                       .aliasPrefix("RootCA2")
+                                       .subject("CN=Test Root Certificate Authority 2")
+                                       .ca(true)
+                                       .build();
+        INTERMEDIATE_CA_2 = new Builder()
+                                    .aliasPrefix("IntermediateCA")
+                                    .subject("CN=Test Intermediate Certificate Authority")
+                                    .ca(true)
+                                    .signer(rootCa2.getPrivateKey("RSA", "RSA"))
+                                    .rootCa(rootCa2.getRootCertificate("RSA"))
+                                    .build();
+        CLIENT_2 = new TestKeyStore(createClient(rootCa2.keyStore), null, null);
+    }
+
+    /**
+     * Return an root CA that can be used to issue new certificates.
+     */
+    public static TestKeyStore getRootCa() {
+        initCerts();
+        return ROOT_CA;
+    }
+
+    /**
+     * Return an intermediate CA that can be used to issue new certificates.
+     */
+    public static TestKeyStore getIntermediateCa() {
+        initCerts();
+        return INTERMEDIATE_CA;
+    }
+
+    /**
+     * Return an intermediate CA that can be used to issue new certificates.
+     */
+    public static TestKeyStore getIntermediateCa2() {
+        initCerts();
+        return INTERMEDIATE_CA_2;
+    }
+
+    /**
+     * Return a server keystore with a matched RSA certificate and
+     * private key as well as a CA certificate.
+     */
+    public static TestKeyStore getServer() {
+        initCerts();
+        return SERVER;
+    }
+
+    /**
+     * Return a server keystore with a matched RSA certificate with SAN hostname and private key
+     * as well as a CA certificate.
+     */
+    public static TestKeyStore getServerHostname() {
+        initCerts();
+        return SERVER_HOSTNAME;
+    }
+
+    /**
+     * Return a keystore with a CA certificate
+     */
+    public static TestKeyStore getClient() {
+        initCerts();
+        return CLIENT;
+    }
+
+    /**
+     * Return a client keystore with a matched RSA certificate and
+     * private key as well as a CA certificate.
+     */
+    public static TestKeyStore getClientCertificate() {
+        initCerts();
+        return CLIENT_CERTIFICATE;
+    }
+
+    /**
+     * Return a client keystore with a matched RSA certificate and
+     * private key as well as a CA certificate.
+     */
+    public static TestKeyStore getClientEcRsaCertificate() {
+        initCerts();
+        return CLIENT_EC_RSA_CERTIFICATE;
+    }
+
+    /**
+     * Return a client keystore with a matched RSA certificate and
+     * private key as well as a CA certificate.
+     */
+    public static TestKeyStore getClientEcEcCertificate() {
+        initCerts();
+        return CLIENT_EC_EC_CERTIFICATE;
+    }
+
+    /**
+     * Return a keystore with a second CA certificate that does not
+     * trust the server certificate returned by getServer for negative
+     * testing.
+     */
+    public static TestKeyStore getClientCA2() {
+        initCerts();
+        return CLIENT_2;
+    }
+
+    /**
+     * Creates KeyStores containing the requested key types. Since key
+     * generation can be expensive, most tests should reuse the RSA-only
+     * singleton instance returned by TestKeyStore.get.
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class Builder {
+        private String[] keyAlgorithms = {"RSA"};
+        private char[] storePassword;
+        private char[] keyPassword;
+        private String aliasPrefix;
+        private X500Principal subject;
+        private int keyUsage;
+        private boolean ca;
+        private PrivateKeyEntry privateEntry;
+        private PrivateKeyEntry signer;
+        private Certificate rootCa;
+        private final List<KeyPurposeId> extendedKeyUsages = new ArrayList<KeyPurposeId>();
+        private final List<Boolean> criticalExtendedKeyUsages = new ArrayList<Boolean>();
+        private final List<GeneralName> subjectAltNames = new ArrayList<GeneralName>();
+        private final List<GeneralSubtree> permittedNameConstraints =
+                new ArrayList<GeneralSubtree>();
+        private final List<GeneralSubtree> excludedNameConstraints =
+                new ArrayList<GeneralSubtree>();
+        // Generated randomly if not set
+        private BigInteger certificateSerialNumber = null;
+
+        public Builder() {
+        }
+
+        /**
+         * Sets the requested key types to generate and include. The default is
+         * RSA only.
+         */
+        public Builder keyAlgorithms(String... keyAlgorithms) {
+            this.keyAlgorithms = keyAlgorithms;
+            return this;
+        }
+
+        /** A unique prefix to identify the key aliases */
+        public Builder aliasPrefix(String aliasPrefix) {
+            this.aliasPrefix = aliasPrefix;
+            return this;
+        }
+
+        /**
+         * Sets the subject common name. The default is the local host's
+         * canonical name.
+         */
+        public Builder subject(X500Principal subject) {
+            this.subject = subject;
+            return this;
+        }
+
+        public Builder subject(String commonName) {
+            return subject(new X500Principal(commonName));
+        }
+
+        /** {@link KeyUsage} bit mask for 2.5.29.15 extension */
+        public Builder keyUsage(int keyUsage) {
+            this.keyUsage = keyUsage;
+            return this;
+        }
+
+        /** true If the keys being created are for a CA */
+        public Builder ca(boolean ca) {
+            this.ca = ca;
+            return this;
+        }
+
+        /** a private key entry to use for the generation of the certificate */
+        public Builder privateEntry(PrivateKeyEntry privateEntry) {
+            this.privateEntry = privateEntry;
+            return this;
+        }
+
+        /** a private key entry to be used for signing, otherwise self-sign */
+        public Builder signer(PrivateKeyEntry signer) {
+            this.signer = signer;
+            return this;
+        }
+
+        /** a root CA to include in the final store */
+        public Builder rootCa(Certificate rootCa) {
+            this.rootCa = rootCa;
+            return this;
+        }
+
+        public Builder addExtendedKeyUsage(KeyPurposeId keyPurposeId, boolean critical) {
+            extendedKeyUsages.add(keyPurposeId);
+            criticalExtendedKeyUsages.add(critical);
+            return this;
+        }
+
+        public Builder addSubjectAltName(GeneralName generalName) {
+            subjectAltNames.add(generalName);
+            return this;
+        }
+
+        public Builder addSubjectAltNameDnsName(String dnsName) {
+            return addSubjectAltName(
+                    new GeneralName(GeneralName.dNSName, dnsName));
+        }
+
+        public Builder addSubjectAltNameIpAddress(byte[] ipAddress) {
+            return addSubjectAltName(
+                    new GeneralName(GeneralName.iPAddress, new DEROctetString(ipAddress)));
+        }
+
+        private Builder addNameConstraint(boolean permitted, GeneralName generalName) {
+            if (permitted) {
+                permittedNameConstraints.add(new GeneralSubtree(generalName));
+            } else {
+                excludedNameConstraints.add(new GeneralSubtree(generalName));
+            }
+            return this;
+        }
+
+        public Builder addNameConstraint(boolean permitted, byte[] ipAddress) {
+            return addNameConstraint(permitted,
+                    new GeneralName(GeneralName.iPAddress, new DEROctetString(ipAddress)));
+        }
+
+        public Builder certificateSerialNumber(BigInteger certificateSerialNumber) {
+            this.certificateSerialNumber = certificateSerialNumber;
+            return this;
+        }
+
+        public TestKeyStore build() {
+            try {
+                if (StandardNames.IS_RI) {
+                    // JKS does not allow null password
+                    if (storePassword == null) {
+                        storePassword = "password".toCharArray();
+                    }
+                    if (keyPassword == null) {
+                        keyPassword = "password".toCharArray();
+                    }
+                }
+
+                /*
+                 * This is not implemented for other key types because the logic
+                 * would be long to write and it's not needed currently.
+                 */
+                if (privateEntry != null
+                        && (keyAlgorithms.length != 1 || !"RSA".equals(keyAlgorithms[0]))) {
+                    throw new IllegalStateException(
+                            "Only reusing an existing key is implemented for RSA");
+                }
+
+                KeyStore keyStore = createKeyStore();
+                for (String keyAlgorithm : keyAlgorithms) {
+                    String publicAlias = aliasPrefix + "-public-" + keyAlgorithm;
+                    String privateAlias = aliasPrefix + "-private-" + keyAlgorithm;
+                    if ((keyAlgorithm.equals("EC_RSA") || keyAlgorithm.equals("DH_RSA"))
+                            && signer == null && rootCa == null) {
+                        createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, null,
+                                privateKey(keyStore, keyPassword, "RSA", "RSA"));
+                        continue;
+                    } else if (keyAlgorithm.equals("DH_DSA") && signer == null && rootCa == null) {
+                        createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, null,
+                                privateKey(keyStore, keyPassword, "DSA", "DSA"));
+                        continue;
+                    }
+                    createKeys(keyStore, keyAlgorithm, publicAlias, privateAlias, privateEntry,
+                            signer);
+                }
+                if (rootCa != null) {
+                    keyStore.setCertificateEntry(
+                            aliasPrefix + "-root-ca-" + rootCa.getPublicKey().getAlgorithm(),
+                            rootCa);
+                }
+                return new TestKeyStore(keyStore, storePassword, keyPassword);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        /**
+         * Add newly generated keys of a given key type to an existing
+         * KeyStore. The PrivateKey will be stored under the specified
+         * private alias name. The X509Certificate will be stored on the
+         * public alias name and have the given subject distinguished
+         * name.
+         *
+         * If a CA is provided, it will be used to sign the generated
+         * certificate and OCSP responses. Otherwise, the certificate
+         * will be self signed. The certificate will be valid for one
+         * day before and one day after the time of creation.
+         *
+         * Based on:
+         * org.bouncycastle.jce.provider.test.SigTest
+         * org.bouncycastle.jce.provider.test.CertTest
+         */
+        private KeyStore createKeys(KeyStore keyStore, String keyAlgorithm, String publicAlias,
+                String privateAlias, PrivateKeyEntry privateEntry, PrivateKeyEntry signer)
+                throws Exception {
+            PrivateKey caKey;
+            X509Certificate caCert;
+            X509Certificate[] caCertChain;
+            if (signer == null) {
+                caKey = null;
+                caCert = null;
+                caCertChain = null;
+            } else {
+                caKey = signer.getPrivateKey();
+                caCert = (X509Certificate) signer.getCertificate();
+                caCertChain = (X509Certificate[]) signer.getCertificateChain();
+            }
+
+            // Default to localhost if nothing was specified.
+            if (subject == null) {
+                subject = localhost();
+                addSubjectAltNameDnsName(LOCAL_HOST_NAME);
+                addSubjectAltNameDnsName(LOCAL_HOST_NAME_IPV6);
+            }
+
+            final PrivateKey privateKey;
+            final PublicKey publicKey;
+            X509Certificate x509c;
+            if (publicAlias == null && privateAlias == null) {
+                // don't want anything apparently
+                privateKey = null;
+                publicKey = null;
+                x509c = null;
+            } else {
+                if (privateEntry == null) {
+                    // 1a.) we make the keys
+                    int keySize = -1;
+                    AlgorithmParameterSpec spec = null;
+                    if (keyAlgorithm.equals("RSA")) {
+                        keySize = RSA_KEY_SIZE_BITS;
+                    } else if (keyAlgorithm.equals("DH_RSA")) {
+                        spec = new DHParameterSpec(DH_PARAMS_P, DH_PARAMS_G);
+                        keyAlgorithm = "DH";
+                    } else if (keyAlgorithm.equals("DSA")) {
+                        keySize = DSA_KEY_SIZE_BITS;
+                    } else if (keyAlgorithm.equals("DH_DSA")) {
+                        spec = new DHParameterSpec(DH_PARAMS_P, DH_PARAMS_G);
+                        keyAlgorithm = "DH";
+                    } else if (keyAlgorithm.equals("EC")) {
+                        keySize = EC_KEY_SIZE_BITS;
+                    } else if (keyAlgorithm.equals("EC_RSA")) {
+                        keySize = EC_KEY_SIZE_BITS;
+                        keyAlgorithm = "EC";
+                    } else {
+                        throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm);
+                    }
+
+                    KeyPairGenerator kpg = KeyPairGenerator.getInstance(keyAlgorithm);
+                    if (spec != null) {
+                        kpg.initialize(spec);
+                    } else if (keySize != -1) {
+                        kpg.initialize(keySize);
+                    } else {
+                        throw new AssertionError(
+                                "Must either have set algorithm parameters or key size!");
+                    }
+
+                    KeyPair kp = kpg.generateKeyPair();
+                    privateKey = kp.getPrivate();
+                    publicKey = kp.getPublic();
+                } else {
+                    // 1b.) we use the previous keys
+                    privateKey = privateEntry.getPrivateKey();
+                    publicKey = privateEntry.getCertificate().getPublicKey();
+                }
+
+                // 2.) use keys to make certificate
+                X500Principal issuer =
+                        ((caCert != null) ? caCert.getSubjectX500Principal() : subject);
+                PrivateKey signingKey = (caKey == null) ? privateKey : caKey;
+                x509c = createCertificate(publicKey, signingKey, subject, issuer, keyUsage, ca,
+                        extendedKeyUsages, criticalExtendedKeyUsages, subjectAltNames,
+                        permittedNameConstraints, excludedNameConstraints, certificateSerialNumber);
+            }
+
+            X509Certificate[] x509cc;
+            if (privateAlias == null) {
+                // don't need certificate chain
+                x509cc = null;
+            } else if (caCertChain == null) {
+                x509cc = new X509Certificate[] {x509c};
+            } else {
+                x509cc = new X509Certificate[caCertChain.length + 1];
+                x509cc[0] = x509c;
+                System.arraycopy(caCertChain, 0, x509cc, 1, caCertChain.length);
+            }
+
+            // 3.) put certificate and private key into the key store
+            if (privateAlias != null) {
+                keyStore.setKeyEntry(privateAlias, privateKey, keyPassword, x509cc);
+            }
+            if (publicAlias != null) {
+                keyStore.setCertificateEntry(publicAlias, x509c);
+            }
+            return keyStore;
+        }
+
+        private X500Principal localhost() {
+            return new X500Principal("CN=" + LOCAL_HOST_NAME);
+        }
+    }
+
+    public static X509Certificate createCa(
+            PublicKey publicKey, PrivateKey privateKey, String subject) {
+        try {
+            X500Principal principal = new X500Principal(subject);
+            return createCertificate(publicKey, privateKey, principal, principal, 0, true,
+                    new ArrayList<KeyPurposeId>(), new ArrayList<Boolean>(),
+                    new ArrayList<GeneralName>(), new ArrayList<GeneralSubtree>(),
+                    new ArrayList<GeneralSubtree>(), null /* serialNumber, generated randomly */);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static X509Certificate createCertificate(PublicKey publicKey, PrivateKey privateKey,
+            X500Principal subject, X500Principal issuer, int keyUsage, boolean ca,
+            List<KeyPurposeId> extendedKeyUsages, List<Boolean> criticalExtendedKeyUsages,
+            List<GeneralName> subjectAltNames, List<GeneralSubtree> permittedNameConstraints,
+            List<GeneralSubtree> excludedNameConstraints, BigInteger serialNumber)
+            throws Exception {
+        // Note that there is no way to programmatically make a
+        // Certificate using java.* or javax.* APIs. The
+        // CertificateFactory interface assumes you want to read
+        // in a stream of bytes, typically the X.509 factory would
+        // allow ASN.1 DER encoded bytes and optionally some PEM
+        // formats. Here we use Bouncy Castle's
+        // X509V3CertificateGenerator and related classes.
+
+        long millisPerDay = 24 * 60 * 60 * 1000;
+        long now = System.currentTimeMillis();
+        Date start = new Date(now - millisPerDay);
+        Date end = new Date(now + millisPerDay);
+
+        String keyAlgorithm = privateKey.getAlgorithm();
+        String signatureAlgorithm;
+        if (keyAlgorithm.equals("RSA")) {
+            signatureAlgorithm = "sha256WithRSA";
+        } else if (keyAlgorithm.equals("DSA")) {
+            signatureAlgorithm = "sha256WithDSA";
+        } else if (keyAlgorithm.equals("EC")) {
+            signatureAlgorithm = "sha256WithECDSA";
+        } else if (keyAlgorithm.equals("EC_RSA")) {
+            signatureAlgorithm = "sha256WithRSA";
+        } else {
+            throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm);
+        }
+
+        if (serialNumber == null) {
+            byte[] serialBytes = new byte[16];
+            new SecureRandom().nextBytes(serialBytes);
+            serialNumber = new BigInteger(1, serialBytes);
+        }
+
+        X509v3CertificateBuilder x509cg =
+                new X509v3CertificateBuilder(X500Name.getInstance(issuer.getEncoded()),
+                        serialNumber, start, end, X500Name.getInstance(subject.getEncoded()),
+                        SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));
+        if (keyUsage != 0) {
+            x509cg.addExtension(Extension.keyUsage, true, new KeyUsage(keyUsage));
+        }
+        if (ca) {
+            x509cg.addExtension(Extension.basicConstraints, true, new BasicConstraints(true));
+        }
+        for (int i = 0; i < extendedKeyUsages.size(); i++) {
+            KeyPurposeId keyPurposeId = extendedKeyUsages.get(i);
+            boolean critical = criticalExtendedKeyUsages.get(i);
+            x509cg.addExtension(
+                    Extension.extendedKeyUsage, critical, new ExtendedKeyUsage(keyPurposeId));
+        }
+        if (!subjectAltNames.isEmpty()) {
+            x509cg.addExtension(Extension.subjectAlternativeName, false,
+                    new GeneralNames(subjectAltNames.toArray(new GeneralName[0])).getEncoded());
+        }
+        if (!permittedNameConstraints.isEmpty() || !excludedNameConstraints.isEmpty()) {
+            x509cg.addExtension(Extension.nameConstraints, true,
+                    new NameConstraints(
+                            permittedNameConstraints.toArray(
+                                    new GeneralSubtree[permittedNameConstraints.size()]),
+                            excludedNameConstraints.toArray(
+                                    new GeneralSubtree[excludedNameConstraints.size()])));
+        }
+
+        X509CertificateHolder x509holder =
+                x509cg.build(new JcaContentSignerBuilder(signatureAlgorithm).build(privateKey));
+        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+        X509Certificate x509c = (X509Certificate) certFactory.generateCertificate(
+                new ByteArrayInputStream(x509holder.getEncoded()));
+        if (StandardNames.IS_RI) {
+            /*
+             * The RI can't handle the BC EC signature algorithm
+             * string of "ECDSA", since it expects "...WITHEC...",
+             * so convert from BC to RI X509Certificate
+             * implementation via bytes.
+             */
+            CertificateFactory cf = CertificateFactory.getInstance("X.509");
+            ByteArrayInputStream bais = new ByteArrayInputStream(x509c.getEncoded());
+            Certificate c = cf.generateCertificate(bais);
+            x509c = (X509Certificate) c;
+        }
+        return x509c;
+    }
+
+    /**
+     * Return the key algorithm for a possible compound algorithm
+     * identifier containing an underscore. If not underscore is
+     * present, the argument is returned unmodified. However for an
+     * algorithm such as EC_RSA, return EC.
+     */
+    public static String keyAlgorithm(String algorithm) {
+        int index = algorithm.indexOf('_');
+        if (index == -1) {
+            return algorithm;
+        }
+        return algorithm.substring(0, index);
+    }
+
+    /**
+     * Return the signature algorithm for a possible compound
+     * algorithm identifier containing an underscore. If not
+     * underscore is present, the argument is returned
+     * unmodified. However for an algorithm such as EC_RSA, return
+     * RSA.
+     */
+    public static String signatureAlgorithm(String algorithm) {
+        int index = algorithm.indexOf('_');
+        if (index == -1) {
+            return algorithm;
+        }
+        return algorithm.substring(index + 1, algorithm.length());
+    }
+
+    /**
+     * Create an empty KeyStore
+     */
+    public static KeyStore createKeyStore() {
+        try {
+            KeyStore keyStore = KeyStore.getInstance(StandardNames.KEY_STORE_ALGORITHM);
+            keyStore.load(null, null);
+            return keyStore;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Return the only private key in a TestKeyStore for the given
+     * algorithms. Throws IllegalStateException if there are are more
+     * or less than one.
+     */
+    public PrivateKeyEntry getPrivateKey(String keyAlgorithm, String signatureAlgorithm) {
+        return privateKey(keyStore, keyPassword, keyAlgorithm, signatureAlgorithm);
+    }
+
+    /**
+     * Return the only private key in a keystore for the given
+     * algorithms. Throws IllegalStateException if there are are more
+     * or less than one.
+     */
+    public static PrivateKeyEntry privateKey(
+            KeyStore keyStore, char[] keyPassword, String keyAlgorithm, String signatureAlgorithm) {
+        try {
+            PrivateKeyEntry found = null;
+            PasswordProtection password = new PasswordProtection(keyPassword);
+            for (String alias : Collections.list(keyStore.aliases())) {
+                if (!keyStore.entryInstanceOf(alias, PrivateKeyEntry.class)) {
+                    continue;
+                }
+                PrivateKeyEntry privateKey = (PrivateKeyEntry) keyStore.getEntry(alias, password);
+                if (!privateKey.getPrivateKey().getAlgorithm().equals(keyAlgorithm)) {
+                    continue;
+                }
+                X509Certificate certificate = (X509Certificate) privateKey.getCertificate();
+                if (!certificate.getSigAlgName().contains(signatureAlgorithm)) {
+                    continue;
+                }
+                if (found != null) {
+                    throw new IllegalStateException("KeyStore has more than one private key for"
+                            + " keyAlgorithm: " + keyAlgorithm + " signatureAlgorithm: "
+                            + signatureAlgorithm + "\nfirst: " + found.getPrivateKey()
+                            + "\nsecond: " + privateKey.getPrivateKey());
+                }
+                found = privateKey;
+            }
+            if (found == null) {
+                throw new IllegalStateException("KeyStore contained no private key for"
+                        + " keyAlgorithm: " + keyAlgorithm
+                        + " signatureAlgorithm: " + signatureAlgorithm);
+            }
+            return found;
+        } catch (Exception e) {
+            throw new RuntimeException("Problem getting key for " + keyAlgorithm + " and signature "
+                            + signatureAlgorithm,
+                    e);
+        }
+    }
+
+    /**
+     * Return the issuing CA certificate of the given
+     * certificate. Throws IllegalStateException if there are are more
+     * or less than one.
+     */
+    public Certificate getIssuer(Certificate cert) throws Exception {
+        return issuer(keyStore, cert);
+    }
+
+    /**
+     * Return the issuing CA certificate of the given
+     * certificate. Throws IllegalStateException if there are are more
+     * or less than one.
+     */
+    public static Certificate issuer(KeyStore keyStore, Certificate c) throws Exception {
+        if (!(c instanceof X509Certificate)) {
+            throw new IllegalStateException("issuer requires an X509Certificate, found " + c);
+        }
+        X509Certificate cert = (X509Certificate) c;
+
+        Certificate found = null;
+        for (String alias : Collections.list(keyStore.aliases())) {
+            if (!keyStore.entryInstanceOf(alias, TrustedCertificateEntry.class)) {
+                continue;
+            }
+            TrustedCertificateEntry certificateEntry =
+                    (TrustedCertificateEntry) keyStore.getEntry(alias, null);
+            Certificate certificate = certificateEntry.getTrustedCertificate();
+            if (!(certificate instanceof X509Certificate)) {
+                continue;
+            }
+            X509Certificate x = (X509Certificate) certificate;
+            if (!cert.getIssuerDN().equals(x.getSubjectDN())) {
+                continue;
+            }
+            if (found != null) {
+                throw new IllegalStateException("KeyStore has more than one issuing CA for " + cert
+                        + "\nfirst: " + found + "\nsecond: " + certificate);
+            }
+            found = certificate;
+        }
+        if (found == null) {
+            throw new IllegalStateException("KeyStore contained no issuing CA for " + cert);
+        }
+        return found;
+    }
+
+    /**
+     * Return the only self-signed root certificate in a TestKeyStore
+     * for the given algorithm. Throws IllegalStateException if there
+     * are are more or less than one.
+     */
+    public X509Certificate getRootCertificate(String algorithm) {
+        return rootCertificate(keyStore, algorithm);
+    }
+
+    private static OCSPResp generateOCSPResponse(PrivateKeyEntry server, PrivateKeyEntry issuer,
+            CertificateStatus status) throws CertificateException {
+        try {
+            X509Certificate serverCertJca = (X509Certificate) server.getCertificate();
+            X509Certificate caCertJca = (X509Certificate) issuer.getCertificate();
+
+            X509CertificateHolder caCert = new JcaX509CertificateHolder(caCertJca);
+
+            DigestCalculatorProvider digCalcProv = new BcDigestCalculatorProvider();
+            BasicOCSPRespBuilder basicBuilder = new BasicOCSPRespBuilder(
+                    SubjectPublicKeyInfo.getInstance(caCertJca.getPublicKey().getEncoded()),
+                    digCalcProv.get(CertificateID.HASH_SHA1));
+
+            CertificateID certId = new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1),
+                    caCert, serverCertJca.getSerialNumber());
+
+            basicBuilder.addResponse(certId, status);
+
+            BasicOCSPResp resp = basicBuilder.build(
+                    new JcaContentSignerBuilder("SHA256withRSA").build(issuer.getPrivateKey()),
+                    null, new Date());
+
+            OCSPRespBuilder builder = new OCSPRespBuilder();
+            return builder.build(OCSPRespBuilder.SUCCESSFUL, resp);
+        } catch (Exception e) {
+            throw new CertificateException("cannot generate OCSP response", e);
+        }
+    }
+
+    public static byte[] getOCSPResponseForGood(PrivateKeyEntry server, PrivateKeyEntry issuer)
+            throws CertificateException {
+        try {
+            return generateOCSPResponse(server, issuer, CertificateStatus.GOOD).getEncoded();
+        } catch (IOException e) {
+            throw new CertificateException(e);
+        }
+    }
+
+    public static byte[] getOCSPResponseForRevoked(PrivateKeyEntry server, PrivateKeyEntry issuer)
+            throws CertificateException {
+        try {
+            return generateOCSPResponse(
+                    server, issuer, new RevokedStatus(new Date(), CRLReason.keyCompromise))
+                    .getEncoded();
+        } catch (IOException e) {
+            throw new CertificateException(e);
+        }
+    }
+
+    /**
+     * Return the only self-signed root certificate in a keystore for
+     * the given algorithm. Throws IllegalStateException if there are
+     * are more or less than one.
+     */
+    public static X509Certificate rootCertificate(KeyStore keyStore, String algorithm) {
+        try {
+            X509Certificate found = null;
+            for (String alias : Collections.list(keyStore.aliases())) {
+                if (!keyStore.entryInstanceOf(alias, TrustedCertificateEntry.class)) {
+                    continue;
+                }
+                TrustedCertificateEntry certificateEntry =
+                        (TrustedCertificateEntry) keyStore.getEntry(alias, null);
+                Certificate certificate = certificateEntry.getTrustedCertificate();
+                if (!certificate.getPublicKey().getAlgorithm().equals(algorithm)) {
+                    continue;
+                }
+                if (!(certificate instanceof X509Certificate)) {
+                    continue;
+                }
+                X509Certificate x = (X509Certificate) certificate;
+                if (!x.getIssuerDN().equals(x.getSubjectDN())) {
+                    continue;
+                }
+                if (found != null) {
+                    throw new IllegalStateException("KeyStore has more than one root CA for "
+                            + algorithm + "\nfirst: " + found + "\nsecond: " + certificate);
+                }
+                found = x;
+            }
+            if (found == null) {
+                throw new IllegalStateException("KeyStore contained no root CA for " + algorithm);
+            }
+            return found;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Return an {@code X509Certificate} that matches the given {@code alias}.
+     */
+    public KeyStore.Entry getEntryByAlias(String alias) {
+        return entryByAlias(keyStore, alias);
+    }
+
+    /**
+     * Finds an entry in the keystore by the given alias.
+     */
+    public static KeyStore.Entry entryByAlias(KeyStore keyStore, String alias) {
+        try {
+            return keyStore.getEntry(alias, null);
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        } catch (UnrecoverableEntryException e) {
+            throw new RuntimeException(e);
+        } catch (KeyStoreException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Create a client key store that only contains self-signed certificates but no private keys
+     */
+    public static KeyStore createClient(KeyStore caKeyStore) {
+        KeyStore clientKeyStore = createKeyStore();
+        copySelfSignedCertificates(clientKeyStore, caKeyStore);
+        return clientKeyStore;
+    }
+
+    /**
+     * Copy self-signed certificates from one key store to another.
+     * Returns true if successful, false if no match found.
+     */
+    public static boolean copySelfSignedCertificates(KeyStore dst, KeyStore src) {
+        try {
+            boolean copied = false;
+            for (String alias : Collections.list(src.aliases())) {
+                if (!src.isCertificateEntry(alias)) {
+                    continue;
+                }
+                X509Certificate cert = (X509Certificate) src.getCertificate(alias);
+                if (!cert.getSubjectDN().equals(cert.getIssuerDN())) {
+                    continue;
+                }
+                dst.setCertificateEntry(alias, cert);
+                copied = true;
+            }
+            return copied;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Copy named certificates from one key store to another.
+     * Returns true if successful, false if no match found.
+     */
+    public static boolean copyCertificate(Principal subject, KeyStore dst, KeyStore src)
+            throws Exception {
+        for (String alias : Collections.list(src.aliases())) {
+            if (!src.isCertificateEntry(alias)) {
+                continue;
+            }
+            X509Certificate cert = (X509Certificate) src.getCertificate(alias);
+            if (!cert.getSubjectDN().equals(subject)) {
+                continue;
+            }
+            dst.setCertificateEntry(alias, cert);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Dump a key store for debugging.
+     */
+    public void dump(String context) throws KeyStoreException, NoSuchAlgorithmException {
+        dump(context, keyStore, keyPassword);
+    }
+
+    /**
+     * Dump a key store for debugging.
+     */
+    public static void dump(String context, KeyStore keyStore, char[] keyPassword)
+            throws KeyStoreException, NoSuchAlgorithmException {
+        PrintStream out = System.out;
+        out.println("context=" + context);
+        out.println("\tkeyStore=" + keyStore);
+        out.println("\tkeyStore.type=" + keyStore.getType());
+        out.println("\tkeyStore.provider=" + keyStore.getProvider());
+        out.println("\tkeyPassword=" + ((keyPassword == null) ? null : new String(keyPassword)));
+        out.println("\tsize=" + keyStore.size());
+        for (String alias : Collections.list(keyStore.aliases())) {
+            out.println("alias=" + alias);
+            out.println("\tcreationDate=" + keyStore.getCreationDate(alias));
+            if (keyStore.isCertificateEntry(alias)) {
+                out.println("\tcertificate:");
+                out.println("==========================================");
+                out.println(keyStore.getCertificate(alias));
+                out.println("==========================================");
+                continue;
+            }
+            if (keyStore.isKeyEntry(alias)) {
+                out.println("\tkey:");
+                out.println("==========================================");
+                String key;
+                try {
+                    key = ("Key retrieved using password\n" + keyStore.getKey(alias, keyPassword));
+                } catch (UnrecoverableKeyException e1) {
+                    try {
+                        key = ("Key retrieved without password\n" + keyStore.getKey(alias, null));
+                    } catch (UnrecoverableKeyException e2) {
+                        key = "Key could not be retrieved";
+                    }
+                }
+                out.println(key);
+                out.println("==========================================");
+                Certificate[] chain = keyStore.getCertificateChain(alias);
+                if (chain == null) {
+                    out.println("No certificate chain associated with key");
+                    out.println("==========================================");
+                } else {
+                    for (int i = 0; i < chain.length; i++) {
+                        out.println("Certificate chain element #" + i);
+                        out.println(chain[i]);
+                        out.println("==========================================");
+                    }
+                }
+                continue;
+            }
+            out.println("\tunknown entry type");
+        }
+    }
+
+    public static void assertChainLength(Object[] chain) {
+        /*
+         * Note chain is Object[] to support both
+         * java.security.cert.X509Certificate and
+         * javax.security.cert.X509Certificate
+         */
+        assertEquals(3, chain.length);
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/FakeSSLSession.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/FakeSSLSession.java
new file mode 100644
index 0000000..ecd8b24
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/FakeSSLSession.java
@@ -0,0 +1,141 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import java.nio.charset.Charset;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class FakeSSLSession implements SSLSession {
+    private static final Charset UTF_8 = Charset.forName("UTF-8");
+    final String host;
+
+    public FakeSSLSession(String host) {
+        this.host = host;
+    }
+
+    @Override
+    public int getApplicationBufferSize() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getCipherSuite() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public long getCreationTime() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public byte[] getId() {
+        return host.getBytes(UTF_8);
+    }
+
+    @Override
+    public long getLastAccessedTime() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Certificate[] getLocalCertificates() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Principal getLocalPrincipal() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getPacketBufferSize() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public javax.security.cert.X509Certificate[] getPeerCertificateChain() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Certificate[] getPeerCertificates() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getPeerHost() {
+        return host;
+    }
+
+    @Override
+    public int getPeerPort() {
+        return 443;
+    }
+
+    @Override
+    public Principal getPeerPrincipal() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getProtocol() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public SSLSessionContext getSessionContext() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Object getValue(String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String[] getValueNames() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void invalidate() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isValid() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void putValue(String name, Object value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeValue(String name) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/ForwardingX509ExtendedKeyManager.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/ForwardingX509ExtendedKeyManager.java
new file mode 100644
index 0000000..9ad3db5
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/ForwardingX509ExtendedKeyManager.java
@@ -0,0 +1,66 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.javax.net.ssl;
+import java.net.Socket;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.X509ExtendedKeyManager;
+/**
+ * {@link X509ExtendedKeyManager} which delegates all calls to the provided
+ * {@code X509ExtendedKeyManager} instance.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ForwardingX509ExtendedKeyManager extends X509ExtendedKeyManager {
+    private final X509ExtendedKeyManager delegate;
+    public ForwardingX509ExtendedKeyManager(X509ExtendedKeyManager delegate) {
+        this.delegate = delegate;
+    }
+    @Override
+    public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
+        return delegate.chooseClientAlias(keyType, issuers, socket);
+    }
+    @Override
+    public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
+        return delegate.chooseServerAlias(keyType, issuers, socket);
+    }
+    @Override
+    public X509Certificate[] getCertificateChain(String alias) {
+        return delegate.getCertificateChain(alias);
+    }
+    @Override
+    public String[] getClientAliases(String keyType, Principal[] issuers) {
+        return delegate.getClientAliases(keyType, issuers);
+    }
+    @Override
+    public String[] getServerAliases(String keyType, Principal[] issuers) {
+        return delegate.getServerAliases(keyType, issuers);
+    }
+    @Override
+    public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) {
+        return delegate.chooseEngineClientAlias(keyType, issuers, engine);
+    }
+    @Override
+    public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) {
+        return delegate.chooseEngineServerAlias(keyType, issuers, engine);
+    }
+    @Override
+    public PrivateKey getPrivateKey(String alias) {
+        return delegate.getPrivateKey(alias);
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/PSKKeyManagerProxy.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/PSKKeyManagerProxy.java
new file mode 100644
index 0000000..d4275c6
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/PSKKeyManagerProxy.java
@@ -0,0 +1,97 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.javax.net.ssl;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.Socket;
+import javax.crypto.SecretKey;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLEngine;
+import com.android.org.conscrypt.TestUtils;
+
+/**
+ * Reflection-based implementation of {@code PSKKeyManager} from Conscrypt on which these tests
+ * cannot depend directly.
+ */
+class PSKKeyManagerProxy implements InvocationHandler {
+    static KeyManager getConscryptPSKKeyManager(PSKKeyManagerProxy delegate) {
+        Class<?> pskKeyManagerInterface;
+        try {
+            pskKeyManagerInterface = TestUtils.conscryptClass("PSKKeyManager");
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+        return (KeyManager) Proxy.newProxyInstance(
+                PSKKeyManagerProxy.class.getClassLoader(),
+                new Class<?>[] {pskKeyManagerInterface},
+                delegate);
+    }
+    @SuppressWarnings("unused")
+    protected SecretKey getKey(String identityHint, String identity, Socket socket) {
+        return null;
+    }
+    @SuppressWarnings("unused")
+    protected SecretKey getKey(String identityHint, String identity, SSLEngine engine) {
+        return null;
+    }
+    @SuppressWarnings("unused")
+    protected String chooseServerKeyIdentityHint(Socket socket) {
+        return null;
+    }
+    @SuppressWarnings("unused")
+    protected String chooseServerKeyIdentityHint(SSLEngine engine) {
+        return null;
+    }
+    @SuppressWarnings("unused")
+    protected String chooseClientKeyIdentity(String identityHint, Socket socket) {
+        return null;
+    }
+    @SuppressWarnings("unused")
+    protected String chooseClientKeyIdentity(String identityHint, SSLEngine engine) {
+        return null;
+    }
+    @Override
+    public final Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        String methodName = method.getName();
+        Class<?>[] parameterTypes = method.getParameterTypes();
+        boolean sslEngineVariant = (parameterTypes.length > 0)
+                && SSLEngine.class.equals(parameterTypes[parameterTypes.length - 1]);
+        if ("getKey".equals(methodName)) {
+            if (sslEngineVariant) {
+                return getKey((String) args[0], (String) args[1], (SSLEngine) args[2]);
+            } else {
+                return getKey((String) args[0], (String) args[1], (Socket) args[2]);
+            }
+        } else if ("chooseServerKeyIdentityHint".equals(methodName)) {
+            if (sslEngineVariant) {
+                return chooseServerKeyIdentityHint((SSLEngine) args[0]);
+            } else {
+                return chooseServerKeyIdentityHint((Socket) args[0]);
+            }
+        } else if ("chooseClientKeyIdentity".equals(methodName)) {
+            if (sslEngineVariant) {
+                return chooseClientKeyIdentity((String) args[0], (SSLEngine) args[1]);
+            } else {
+                return chooseClientKeyIdentity((String) args[0], (Socket) args[1]);
+            }
+        } else {
+            throw new IllegalArgumentException("Unexpected method: " + method);
+        }
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/RandomPrivateKeyX509ExtendedKeyManager.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/RandomPrivateKeyX509ExtendedKeyManager.java
new file mode 100644
index 0000000..efe5d6c
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/RandomPrivateKeyX509ExtendedKeyManager.java
@@ -0,0 +1,87 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.fail;
+
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.spec.DSAParameterSpec;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.util.HashMap;
+import java.util.Map;
+import javax.net.ssl.X509ExtendedKeyManager;
+
+/**
+ * {@link X509ExtendedKeyManager} which forwards all calls to a delegate while substituting
+ * the returned private key with its own randomly generated keys of the same type (and parameters).
+ * @hide This class is not part of the Android public SDK API
+ */
+public class RandomPrivateKeyX509ExtendedKeyManager extends ForwardingX509ExtendedKeyManager {
+    private final Map<String, PrivateKey> cachedKeys = new HashMap<String, PrivateKey>();
+    public RandomPrivateKeyX509ExtendedKeyManager(X509ExtendedKeyManager delegate) {
+        super(delegate);
+    }
+    @Override
+    public PrivateKey getPrivateKey(String alias) {
+        PrivateKey originalPrivateKey = super.getPrivateKey(alias);
+        if (originalPrivateKey == null) {
+            return null;
+        }
+        PrivateKey result;
+        String keyAlgorithm = originalPrivateKey.getAlgorithm();
+        try {
+            KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
+            if ("RSA".equals(keyAlgorithm)) {
+                RSAPrivateKeySpec originalKeySpec =
+                        keyFactory.getKeySpec(originalPrivateKey, RSAPrivateKeySpec.class);
+                int keyLengthBits = originalKeySpec.getModulus().bitLength();
+                // Use a cache because RSA key generation is slow.
+                String cacheKey = keyAlgorithm + "-" + keyLengthBits;
+                result = cachedKeys.get(cacheKey);
+                if (result == null) {
+                    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyAlgorithm);
+                    keyPairGenerator.initialize(keyLengthBits);
+                    result = keyPairGenerator.generateKeyPair().getPrivate();
+                    cachedKeys.put(cacheKey, result);
+                }
+            } else if ("DSA".equals(keyAlgorithm)) {
+                DSAPrivateKeySpec originalKeySpec =
+                        keyFactory.getKeySpec(originalPrivateKey, DSAPrivateKeySpec.class);
+                KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyAlgorithm);
+                keyPairGenerator.initialize(new DSAParameterSpec(
+                        originalKeySpec.getP(), originalKeySpec.getQ(), originalKeySpec.getG()));
+                result = keyPairGenerator.generateKeyPair().getPrivate();
+            } else if ("EC".equals(keyAlgorithm)) {
+                KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyAlgorithm);
+                keyPairGenerator.initialize(((ECPrivateKey) originalPrivateKey).getParams());
+                result = keyPairGenerator.generateKeyPair().getPrivate();
+            } else {
+                fail("Unsupported key algorithm: " + originalPrivateKey.getAlgorithm());
+                result = null;
+            }
+        } catch (GeneralSecurityException e) {
+            fail("Failed to generate private key: " + e);
+            result = null;
+        }
+        return result;
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/SSLConfigurationAsserts.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/SSLConfigurationAsserts.java
new file mode 100644
index 0000000..a415292
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/SSLConfigurationAsserts.java
@@ -0,0 +1,236 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import com.android.org.conscrypt.java.security.StandardNames;
+
+/**
+ * Assertions about the configuration of TLS/SSL primitives.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class SSLConfigurationAsserts {
+    /** Hidden constructor to prevent instantiation. */
+    private SSLConfigurationAsserts() {}
+    /**
+     * Asserts that the provided {@link SSLContext} has the expected default configuration, and that
+     * {@link SSLSocketFactory}, {@link SSLServerSocketFactory}, {@link SSLSocket},
+     * {@link SSLServerSocket} and {@link SSLEngine} instances created from the context match the
+     * configuration.
+     */
+    public static void assertSSLContextDefaultConfiguration(SSLContext sslContext)
+            throws IOException {
+        SSLParameters defaultParameters = sslContext.getDefaultSSLParameters();
+        StandardNames.assertSSLContextEnabledProtocols(
+                sslContext.getProtocol(), defaultParameters.getProtocols());
+        StandardNames.assertDefaultCipherSuites(defaultParameters.getCipherSuites());
+        assertFalse(defaultParameters.getWantClientAuth());
+        assertFalse(defaultParameters.getNeedClientAuth());
+        SSLParameters supportedParameters = sslContext.getSupportedSSLParameters();
+        StandardNames.assertSupportedCipherSuites(supportedParameters.getCipherSuites());
+        StandardNames.assertSupportedProtocols(supportedParameters.getProtocols());
+        assertFalse(supportedParameters.getWantClientAuth());
+        assertFalse(supportedParameters.getNeedClientAuth());
+        assertContainsAll("Unsupported enabled cipher suites",
+                supportedParameters.getCipherSuites(), defaultParameters.getCipherSuites());
+        assertContainsAll("Unsupported enabled protocols", supportedParameters.getProtocols(),
+                defaultParameters.getProtocols());
+        assertSSLSocketFactoryConfigSameAsSSLContext(sslContext.getSocketFactory(), sslContext);
+        assertSSLServerSocketFactoryConfigSameAsSSLContext(
+                sslContext.getServerSocketFactory(), sslContext);
+        SSLEngine sslEngine = sslContext.createSSLEngine();
+        assertFalse(sslEngine.getUseClientMode());
+        assertSSLEngineConfigSameAsSSLContext(sslEngine, sslContext);
+    }
+    /**
+     * Asserts that the provided {@link SSLSocketFactory} has the expected default configuration and
+     * that {@link SSLSocket} instances created by the factory match the configuration.
+     */
+    public static void assertSSLSocketFactoryDefaultConfiguration(SSLSocketFactory sslSocketFactory)
+            throws Exception {
+        assertSSLSocketFactoryConfigSameAsSSLContext(sslSocketFactory, SSLContext.getDefault());
+    }
+    /**
+     * Asserts that {@link SSLSocketFactory}'s configuration matches {@code SSLContext}'s
+     * configuration, and that {@link SSLSocket} instances obtained from the factory match this
+     * configuration as well.
+     */
+    private static void assertSSLSocketFactoryConfigSameAsSSLContext(
+            SSLSocketFactory sslSocketFactory, SSLContext sslContext) throws IOException {
+        assertCipherSuitesEqual(sslContext.getDefaultSSLParameters().getCipherSuites(),
+                sslSocketFactory.getDefaultCipherSuites());
+        assertCipherSuitesEqual(sslContext.getSupportedSSLParameters().getCipherSuites(),
+                sslSocketFactory.getSupportedCipherSuites());
+        SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket();
+        try {
+            assertTrue(sslSocket.getUseClientMode());
+            assertTrue(sslSocket.getEnableSessionCreation());
+            assertSSLSocketConfigSameAsSSLContext(sslSocket, sslContext);
+        } finally {
+            sslSocket.close();
+        }
+    }
+    /**
+     * Asserts that the provided {@link SSLSocket} has the expected default configuration.
+     */
+    public static void assertSSLSocketDefaultConfiguration(SSLSocket sslSocket) throws Exception {
+        assertTrue(sslSocket.getUseClientMode());
+        assertTrue(sslSocket.getEnableSessionCreation());
+        assertSSLSocketConfigSameAsSSLContext(sslSocket, SSLContext.getDefault());
+    }
+    /**
+     * Asserts that {@link SSLSocket}'s configuration matches {@code SSLContext's} configuration.
+     */
+    private static void assertSSLSocketConfigSameAsSSLContext(
+            SSLSocket sslSocket, SSLContext sslContext) {
+        assertSSLParametersEqual(
+                sslSocket.getSSLParameters(), sslContext.getDefaultSSLParameters());
+        assertCipherSuitesEqual(sslSocket.getEnabledCipherSuites(),
+                sslContext.getDefaultSSLParameters().getCipherSuites());
+        assertProtocolsEqual(sslSocket.getEnabledProtocols(),
+                sslContext.getDefaultSSLParameters().getProtocols());
+        assertCipherSuitesEqual(sslSocket.getSupportedCipherSuites(),
+                sslContext.getSupportedSSLParameters().getCipherSuites());
+        assertProtocolsEqual(sslSocket.getSupportedProtocols(),
+                sslContext.getSupportedSSLParameters().getProtocols());
+    }
+    /**
+     * Asserts that the provided {@link SSLServerSocketFactory} has the expected default
+     * configuration, and that {@link SSLServerSocket} instances created by the factory match the
+     * configuration.
+     */
+    public static void assertSSLServerSocketFactoryDefaultConfiguration(
+            SSLServerSocketFactory sslServerSocketFactory) throws Exception {
+        assertSSLServerSocketFactoryConfigSameAsSSLContext(
+                sslServerSocketFactory, SSLContext.getDefault());
+    }
+    /**
+     * Asserts that {@link SSLServerSocketFactory}'s configuration matches {@code SSLContext}'s
+     * configuration, and that {@link SSLServerSocket} instances obtained from the factory match
+     * this
+     * configuration as well.
+     */
+    private static void assertSSLServerSocketFactoryConfigSameAsSSLContext(
+            SSLServerSocketFactory sslServerSocketFactory, SSLContext sslContext)
+            throws IOException {
+        assertCipherSuitesEqual(sslContext.getDefaultSSLParameters().getCipherSuites(),
+                sslServerSocketFactory.getDefaultCipherSuites());
+        assertCipherSuitesEqual(sslContext.getSupportedSSLParameters().getCipherSuites(),
+                sslServerSocketFactory.getSupportedCipherSuites());
+        SSLServerSocket sslServerSocket =
+            (SSLServerSocket) sslServerSocketFactory.createServerSocket();
+        try {
+            assertFalse(sslServerSocket.getUseClientMode());
+            assertTrue(sslServerSocket.getEnableSessionCreation());
+            assertSSLServerSocketConfigSameAsSSLContext(sslServerSocket, sslContext);
+        } finally {
+            sslServerSocket.close();
+        }
+    }
+    /**
+     * Asserts that the provided {@link SSLServerSocket} has the expected default configuration.
+     */
+    public static void assertSSLServerSocketDefaultConfiguration(SSLServerSocket sslServerSocket)
+            throws Exception {
+        assertFalse(sslServerSocket.getUseClientMode());
+        assertTrue(sslServerSocket.getEnableSessionCreation());
+        assertSSLServerSocketConfigSameAsSSLContext(sslServerSocket, SSLContext.getDefault());
+        // TODO: Check SSLParameters when supported by SSLServerSocket API
+    }
+    /**
+     * Asserts that {@link SSLServerSocket}'s configuration matches {@code SSLContext's}
+     * configuration.
+     */
+    private static void assertSSLServerSocketConfigSameAsSSLContext(
+            SSLServerSocket sslServerSocket, SSLContext sslContext) {
+        assertCipherSuitesEqual(sslServerSocket.getEnabledCipherSuites(),
+                sslContext.getDefaultSSLParameters().getCipherSuites());
+        assertProtocolsEqual(sslServerSocket.getEnabledProtocols(),
+                sslContext.getDefaultSSLParameters().getProtocols());
+        assertCipherSuitesEqual(sslServerSocket.getSupportedCipherSuites(),
+                sslContext.getSupportedSSLParameters().getCipherSuites());
+        assertProtocolsEqual(sslServerSocket.getSupportedProtocols(),
+                sslContext.getSupportedSSLParameters().getProtocols());
+        assertEquals(sslServerSocket.getNeedClientAuth(),
+                sslContext.getDefaultSSLParameters().getNeedClientAuth());
+        assertEquals(sslServerSocket.getWantClientAuth(),
+                sslContext.getDefaultSSLParameters().getWantClientAuth());
+    }
+    /**
+     * Asserts that the provided {@link SSLEngine} has the expected default configuration.
+     */
+    public static void assertSSLEngineDefaultConfiguration(SSLEngine sslEngine) throws Exception {
+        assertFalse(sslEngine.getUseClientMode());
+        assertTrue(sslEngine.getEnableSessionCreation());
+        assertSSLEngineConfigSameAsSSLContext(sslEngine, SSLContext.getDefault());
+    }
+    /**
+     * Asserts that {@link SSLEngine}'s configuration matches {@code SSLContext's} configuration.
+     */
+    private static void assertSSLEngineConfigSameAsSSLContext(
+            SSLEngine sslEngine, SSLContext sslContext) {
+        assertSSLParametersEqual(
+                sslEngine.getSSLParameters(), sslContext.getDefaultSSLParameters());
+        assertCipherSuitesEqual(sslEngine.getEnabledCipherSuites(),
+                sslContext.getDefaultSSLParameters().getCipherSuites());
+        assertProtocolsEqual(sslEngine.getEnabledProtocols(),
+                sslContext.getDefaultSSLParameters().getProtocols());
+        assertCipherSuitesEqual(sslEngine.getSupportedCipherSuites(),
+                sslContext.getSupportedSSLParameters().getCipherSuites());
+        assertProtocolsEqual(sslEngine.getSupportedProtocols(),
+                sslContext.getSupportedSSLParameters().getProtocols());
+    }
+    private static void assertSSLParametersEqual(SSLParameters expected, SSLParameters actual) {
+        assertCipherSuitesEqual(expected.getCipherSuites(), actual.getCipherSuites());
+        assertProtocolsEqual(expected.getProtocols(), actual.getProtocols());
+        assertEquals(expected.getNeedClientAuth(), actual.getNeedClientAuth());
+        assertEquals(expected.getWantClientAuth(), actual.getWantClientAuth());
+    }
+    private static void assertCipherSuitesEqual(String[] expected, String[] actual) {
+        assertEquals(Arrays.asList(expected), Arrays.asList(actual));
+    }
+    private static void assertProtocolsEqual(String[] expected, String[] actual) {
+        // IMPLEMENTATION NOTE: The order of protocols versions does not matter. Similarly, it only
+        // matters whether a protocol version is present or absent in the array. These arrays are
+        // supposed to represent sets of protocol versions. Thus, we treat them as such.
+        assertEquals(new HashSet<String>(Arrays.asList(expected)),
+                new HashSet<String>(Arrays.asList(actual)));
+    }
+    /**
+     * Asserts that the {@code container} contains all the {@code elements}.
+     */
+    private static void assertContainsAll(String message, String[] container, String[] elements) {
+        Set<String> elementsNotInContainer = new HashSet<String>(Arrays.asList(elements));
+        elementsNotInContainer.removeAll(Arrays.asList(container));
+        assertEquals(message, Collections.EMPTY_SET, elementsNotInContainer);
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestHostnameVerifier.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestHostnameVerifier.java
new file mode 100644
index 0000000..ca6e225
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestHostnameVerifier.java
@@ -0,0 +1,57 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.conscrypt.javax.net.ssl;
+
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+
+/**
+ * This class implements the simplest possible HostnameVerifier.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class TestHostnameVerifier implements HostnameVerifier {
+    @Override
+    public boolean verify(String hostname, SSLSession sslSession) {
+        try {
+            return verify(hostname, (X509Certificate) sslSession.getPeerCertificates()[0]);
+        } catch (SSLException e) {
+            return false;
+        }
+    }
+
+    private boolean verify(String hostname, X509Certificate cert) {
+        for (String certHost : getHostnames(cert)) {
+            if (certHost.equals(hostname)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static final int DNS_NAME_TYPE = 2;
+
+    private List<String> getHostnames(X509Certificate cert) {
+        List<String> result = new ArrayList<String>();
+        try {
+            Collection<List<?>> altNamePairs = cert.getSubjectAlternativeNames();
+            if (altNamePairs != null) {
+                for (List<?> altNamePair : altNamePairs) {
+                    // altNames are returned as effectively Pair<Integer, String> instances,
+                    // where the first member is the type of altName and the second is the name.
+                    if (altNamePair.get(0).equals(DNS_NAME_TYPE)) {
+                        result.add((String) altNamePair.get(1));
+                    }
+                }
+            }
+            return result;
+        } catch (CertificateParsingException e) {
+            return Collections.emptyList();
+        }
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestKeyManager.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestKeyManager.java
new file mode 100644
index 0000000..10e2266
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestKeyManager.java
@@ -0,0 +1,213 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import java.io.PrintStream;
+import java.net.Socket;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.X509ExtendedKeyManager;
+import com.android.org.conscrypt.java.security.StandardNames;
+import com.android.org.conscrypt.testing.NullPrintStream;
+
+/**
+ * TestKeyManager is a simple proxy class that wraps an existing
+ * X509ExtendedKeyManager to provide debug logging and recording of
+ * values.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class TestKeyManager extends X509ExtendedKeyManager {
+    private static final boolean LOG = false;
+    private static final PrintStream out = LOG ? System.out : new NullPrintStream();
+
+    private final X509ExtendedKeyManager keyManager;
+
+    public static KeyManager[] wrap(KeyManager[] keyManagers) {
+        KeyManager[] result = keyManagers.clone();
+        for (int i = 0; i < result.length; i++) {
+            result[i] = wrap(result[i]);
+        }
+        return result;
+    }
+
+    public static KeyManager wrap(KeyManager keyManager) {
+        if (!(keyManager instanceof X509ExtendedKeyManager)) {
+            return keyManager;
+        }
+        return new TestKeyManager((X509ExtendedKeyManager) keyManager);
+    }
+
+    public TestKeyManager(X509ExtendedKeyManager keyManager) {
+        out.println("TestKeyManager.<init> keyManager=" + keyManager);
+        this.keyManager = keyManager;
+    }
+
+    @Override
+    public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
+        out.print("TestKeyManager.chooseClientAlias");
+        out.print(" | keyTypes: ");
+        for (String keyType : keyTypes) {
+            out.print(keyType);
+            out.print(' ');
+        }
+        dumpIssuers(issuers);
+        dumpSocket(socket);
+        assertKeyTypes(keyTypes);
+        return dumpAlias(keyManager.chooseClientAlias(keyTypes, issuers, socket));
+    }
+
+    private void assertKeyTypes(String[] keyTypes) {
+        for (String keyType : keyTypes) {
+            assertKeyType(keyType);
+        }
+    }
+
+    private void assertKeyType(String keyType) {
+        if (!StandardNames.KEY_TYPES.contains(keyType)) {
+            throw new AssertionError("Unexpected key type " + keyType);
+        }
+    }
+
+    @Override
+    public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
+        out.print("TestKeyManager.chooseServerAlias");
+        out.print(" | keyType: ");
+        out.print(keyType);
+        out.print(' ');
+        dumpIssuers(issuers);
+        dumpSocket(socket);
+        assertKeyType(keyType);
+        return dumpAlias(keyManager.chooseServerAlias(keyType, issuers, socket));
+    }
+
+    private void dumpSocket(Socket socket) {
+        out.print(" | socket: ");
+        out.print(String.valueOf(socket));
+    }
+
+    private void dumpIssuers(Principal[] issuers) {
+        out.print(" | issuers: ");
+        if (issuers == null) {
+            out.print("null");
+            return;
+        }
+        for (Principal issuer : issuers) {
+            out.print(issuer);
+            out.print(' ');
+        }
+    }
+
+    private String dumpAlias(String alias) {
+        out.print(" => ");
+        out.println(alias);
+        return alias;
+    }
+
+    @Override
+    public X509Certificate[] getCertificateChain(String alias) {
+        out.print("TestKeyManager.getCertificateChain");
+        out.print(" | alias: ");
+        out.print(alias);
+        return dumpCerts(keyManager.getCertificateChain(alias));
+    }
+
+    private X509Certificate[] dumpCerts(X509Certificate[] certs) {
+        out.print(" => ");
+        for (X509Certificate cert : certs) {
+            out.print(cert.getSubjectDN());
+            out.print(' ');
+        }
+        out.println();
+        return certs;
+    }
+
+    @Override
+    public String[] getClientAliases(String keyType, Principal[] issuers) {
+        out.print("TestKeyManager.getClientAliases");
+        out.print(" | keyType: ");
+        out.print(keyType);
+        dumpIssuers(issuers);
+        assertKeyType(keyType);
+        return dumpAliases(keyManager.getClientAliases(keyType, issuers));
+    }
+
+    @Override
+    public String[] getServerAliases(String keyType, Principal[] issuers) {
+        out.print("TestKeyManager.getServerAliases");
+        out.print(" | keyType: ");
+        out.print(keyType);
+        dumpIssuers(issuers);
+        assertKeyType(keyType);
+        return dumpAliases(keyManager.getServerAliases(keyType, issuers));
+    }
+
+    private String[] dumpAliases(String[] aliases) {
+        out.print(" => ");
+        for (String alias : aliases) {
+            out.print(alias);
+            out.print(' ');
+        }
+        out.println();
+        return aliases;
+    }
+
+    @Override
+    public PrivateKey getPrivateKey(String alias) {
+        out.print("TestKeyManager.getPrivateKey");
+        out.print(" | alias: ");
+        out.print(alias);
+        PrivateKey pk = keyManager.getPrivateKey(alias);
+        out.print(" => ");
+        out.println(String.valueOf(pk));
+        return pk;
+    }
+
+    @Override
+    public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine e) {
+        out.print("TestKeyManager.chooseEngineClientAlias");
+        out.print(" | keyTypes: ");
+        for (String keyType : keyTypes) {
+            out.print(keyType);
+            out.print(' ');
+        }
+        dumpIssuers(issuers);
+        dumpEngine(e);
+        assertKeyTypes(keyTypes);
+        return dumpAlias(keyManager.chooseEngineClientAlias(keyTypes, issuers, e));
+    }
+
+    @Override
+    public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine e) {
+        out.print("TestKeyManager.chooseEngineServerAlias");
+        out.print(" | keyType: ");
+        out.print(keyType);
+        out.print(' ');
+        dumpIssuers(issuers);
+        dumpEngine(e);
+        assertKeyType(keyType);
+        return dumpAlias(keyManager.chooseEngineServerAlias(keyType, issuers, e));
+    }
+
+    private void dumpEngine(SSLEngine engine) {
+        out.print(" | engine: ");
+        out.print(String.valueOf(engine));
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestSSLContext.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestSSLContext.java
new file mode 100644
index 0000000..84f377b
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestSSLContext.java
@@ -0,0 +1,490 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.security.KeyStore;
+import java.security.Principal;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import com.android.org.conscrypt.TestUtils;
+import com.android.org.conscrypt.java.security.TestKeyStore;
+
+/**
+ * TestSSLContext is a convenience class for other tests that
+ * want a canned SSLContext and related state for testing so they
+ * don't have to duplicate the logic.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class TestSSLContext {
+    /**
+     * The Android SSLSocket and SSLServerSocket implementations are
+     * based on a version of OpenSSL which includes support for RFC
+     * 4507 session tickets. When using session tickets, the server
+     * does not need to keep a cache mapping session IDs to SSL
+     * sessions for reuse. Instead, the client presents the server
+     * with a session ticket it received from the server earlier,
+     * which is an SSL session encrypted by the server's secret
+     * key. Since in this case the server does not need to keep a
+     * cache, some tests may find different results depending on
+     * whether or not the session tickets are in use. These tests can
+     * use this function to determine if loopback SSL connections are
+     * expected to use session tickets and conditionalize their
+     * results appropriately.
+     */
+    public static boolean sslServerSocketSupportsSessionTickets() {
+        // Disabled session tickets for better compatability b/2682876
+        // return !IS_RI;
+        return true;
+    }
+    public final KeyStore clientKeyStore;
+    public final char[] clientStorePassword;
+    public final KeyStore serverKeyStore;
+    public final char[] serverStorePassword;
+    public final KeyManager[] clientKeyManagers;
+    public final KeyManager[] serverKeyManagers;
+    public final X509TrustManager clientTrustManager;
+    public final X509TrustManager serverTrustManager;
+    public final SSLContext clientContext;
+    public final SSLContext serverContext;
+    public final SSLServerSocket serverSocket;
+    public final InetAddress host;
+    public final int port;
+    /**
+     * Used for replacing the hostname in an InetSocketAddress object during
+     * serialization.
+     */
+    private static class HostnameRewritingObjectOutputStream extends ObjectOutputStream {
+        private final String hostname;
+        public HostnameRewritingObjectOutputStream(OutputStream out, String hostname)
+                throws IOException {
+            super(out);
+            this.hostname = hostname;
+        }
+        @Override
+        public PutField putFields() throws IOException {
+            return new PutFieldProxy(super.putFields(), hostname);
+        }
+        private static class PutFieldProxy extends ObjectOutputStream.PutField {
+            private final PutField delegate;
+            private final String hostname;
+            public PutFieldProxy(ObjectOutputStream.PutField delegate, String hostname) {
+                this.delegate = delegate;
+                this.hostname = hostname;
+            }
+            @Override
+            public void put(String name, boolean val) {
+                delegate.put(name, val);
+            }
+            @Override
+            public void put(String name, byte val) {
+                delegate.put(name, val);
+            }
+            @Override
+            public void put(String name, char val) {
+                delegate.put(name, val);
+            }
+            @Override
+            public void put(String name, short val) {
+                delegate.put(name, val);
+            }
+            @Override
+            public void put(String name, int val) {
+                delegate.put(name, val);
+            }
+            @Override
+            public void put(String name, long val) {
+                delegate.put(name, val);
+            }
+            @Override
+            public void put(String name, float val) {
+                delegate.put(name, val);
+            }
+            @Override
+            public void put(String name, double val) {
+                delegate.put(name, val);
+            }
+            @Override
+            public void put(String name, Object val) {
+                if ("hostname".equals(name)) {
+                    delegate.put(name, hostname);
+                } else {
+                    delegate.put(name, val);
+                }
+            }
+            @SuppressWarnings("deprecation")
+            @Override
+            public void write(ObjectOutput out) throws IOException {
+                delegate.write(out);
+            }
+        }
+    }
+    /**
+     * Creates an InetSocketAddress where the hostname points to an arbitrary
+     * hostname, but the address points to the loopback address. Useful for
+     * testing SNI where both "localhost" and IP addresses are not allowed.
+     */
+    public InetSocketAddress getLoopbackAsHostname(String hostname, int port)
+            throws IOException, ClassNotFoundException {
+        InetSocketAddress addr = new InetSocketAddress(TestUtils.getLoopbackAddress(), port);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        HostnameRewritingObjectOutputStream oos =
+                new HostnameRewritingObjectOutputStream(baos, hostname);
+        oos.writeObject(addr);
+        oos.close();
+        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
+        return (InetSocketAddress) ois.readObject();
+    }
+    private TestSSLContext(KeyStore clientKeyStore, char[] clientStorePassword,
+            KeyStore serverKeyStore, char[] serverStorePassword, KeyManager[] clientKeyManagers,
+            KeyManager[] serverKeyManagers, X509TrustManager clientTrustManager,
+            X509TrustManager serverTrustManager, SSLContext clientContext,
+            SSLContext serverContext, SSLServerSocket serverSocket, InetAddress host, int port) {
+        this.clientKeyStore = clientKeyStore;
+        this.clientStorePassword = clientStorePassword;
+        this.serverKeyStore = serverKeyStore;
+        this.serverStorePassword = serverStorePassword;
+        this.clientKeyManagers = clientKeyManagers;
+        this.serverKeyManagers = serverKeyManagers;
+        this.clientTrustManager = clientTrustManager;
+        this.serverTrustManager = serverTrustManager;
+        this.clientContext = clientContext;
+        this.serverContext = serverContext;
+        this.serverSocket = serverSocket;
+        this.host = host;
+        this.port = port;
+    }
+    public void close() {
+        try {
+            serverSocket.close();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static final class Builder {
+        private TestKeyStore client;
+        private char[] clientStorePassword;
+        private TestKeyStore server;
+        private char[] serverStorePassword;
+        private KeyManager[] additionalClientKeyManagers;
+        private KeyManager[] additionalServerKeyManagers;
+        private TrustManager clientTrustManager;
+        private TrustManager serverTrustManager;
+        private SSLContext clientContext;
+        private SSLContext serverContext;
+        private String clientProtocol = "TLS";
+        private String serverProtocol = "TLS";
+        private int serverReceiveBufferSize;
+        private boolean useDefaults = true;
+
+        public Builder useDefaults(boolean useDefaults) {
+            this.useDefaults = useDefaults;
+            return this;
+        }
+
+        public Builder client(TestKeyStore client) {
+            this.client = client;
+            return this;
+        }
+
+        public Builder clientStorePassword(char[] clientStorePassword) {
+            this.clientStorePassword = clientStorePassword;
+            return this;
+        }
+
+        public Builder server(TestKeyStore server) {
+            this.server = server;
+            return this;
+        }
+
+        public Builder serverStorePassword(char[] serverStorePassword) {
+            this.serverStorePassword = serverStorePassword;
+            return this;
+        }
+
+        public Builder additionalClientKeyManagers(KeyManager[] additionalClientKeyManagers) {
+            this.additionalClientKeyManagers = additionalClientKeyManagers;
+            return this;
+        }
+
+        public Builder additionalServerKeyManagers(KeyManager[] additionalServerKeyManagers) {
+            this.additionalServerKeyManagers = additionalServerKeyManagers;
+            return this;
+        }
+
+        public Builder clientTrustManager(TrustManager clientTrustManager) {
+            this.clientTrustManager = clientTrustManager;
+            return this;
+        }
+
+        public Builder serverTrustManager(TrustManager serverTrustManager) {
+            this.serverTrustManager = serverTrustManager;
+            return this;
+        }
+
+        public Builder clientContext(SSLContext clientContext) {
+            this.clientContext = clientContext;
+            return this;
+        }
+
+        public Builder serverContext(SSLContext serverContext) {
+            this.serverContext = serverContext;
+            return this;
+        }
+
+        public Builder clientProtocol(String clientProtocol) {
+            this.clientProtocol = clientProtocol;
+            return this;
+        }
+
+        public Builder serverProtocol(String serverProtocol) {
+            this.serverProtocol = serverProtocol;
+            return this;
+        }
+
+        public Builder serverReceiveBufferSize(int serverReceiveBufferSize) {
+            this.serverReceiveBufferSize = serverReceiveBufferSize;
+            return this;
+        }
+
+        TestSSLContext build() {
+            // Get the current values for all the things.
+            TestKeyStore client = this.client;
+            TestKeyStore server = this.server;
+            char[] clientStorePassword = this.clientStorePassword;
+            char[] serverStorePassword = this.serverStorePassword;
+            KeyManager[] clientKeyManagers = client != null ? client.keyManagers : null;
+            KeyManager[] serverKeyManagers = server != null ? server.keyManagers : null;
+            TrustManager clientTrustManager = this.clientTrustManager;
+            TrustManager serverTrustManager = this.serverTrustManager;
+            SSLContext clientContext = this.clientContext;
+            SSLContext serverContext = this.serverContext;
+
+            // Apply default values if configured to do so.
+            if (useDefaults) {
+                client = client != null ? client : TestKeyStore.getClient();
+                server = server != null ? server : TestKeyStore.getServer();
+                clientStorePassword =
+                        clientStorePassword != null ? clientStorePassword : client.storePassword;
+                serverStorePassword =
+                        serverStorePassword != null ? serverStorePassword : server.storePassword;
+                clientKeyManagers =
+                        clientKeyManagers != null ? clientKeyManagers : client.keyManagers;
+                serverKeyManagers =
+                        serverKeyManagers != null ? serverKeyManagers : server.keyManagers;
+                clientKeyManagers = concat(clientKeyManagers, additionalClientKeyManagers);
+                serverKeyManagers = concat(serverKeyManagers, additionalServerKeyManagers);
+                clientTrustManager =
+                        clientTrustManager != null ? clientTrustManager : client.trustManagers[0];
+                serverTrustManager =
+                        serverTrustManager != null ? serverTrustManager : server.trustManagers[0];
+
+                clientContext = clientContext != null
+                        ? clientContext
+                        : createSSLContext(clientProtocol, clientKeyManagers,
+                                  new TrustManager[] {clientTrustManager});
+                serverContext = serverContext != null
+                        ? serverContext
+                        : createSSLContext(serverProtocol, serverKeyManagers,
+                                  new TrustManager[] {serverTrustManager});
+            }
+
+            // Create the context.
+            try {
+                SSLServerSocket serverSocket =
+                        (SSLServerSocket) serverContext.getServerSocketFactory()
+                                .createServerSocket();
+                if (serverReceiveBufferSize > 0) {
+                    // The TCP spec says that this should occur before listen.
+                    serverSocket.setReceiveBufferSize(serverReceiveBufferSize);
+                }
+                InetAddress host = TestUtils.getLoopbackAddress();
+                serverSocket.bind(new InetSocketAddress(host, 0));
+                int port = serverSocket.getLocalPort();
+                return new TestSSLContext(client != null ? client.keyStore : null,
+                        clientStorePassword, server != null ? server.keyStore : null,
+                        serverStorePassword, clientKeyManagers, serverKeyManagers,
+                        (X509TrustManager) clientTrustManager,
+                        (X509TrustManager) serverTrustManager, clientContext, serverContext,
+                        serverSocket, host, port);
+            } catch (RuntimeException e) {
+                throw e;
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * Usual TestSSLContext creation method, creates underlying
+     * SSLContext with certificate and key as well as SSLServerSocket
+     * listening provided host and port.
+     */
+    public static TestSSLContext create() {
+        return new Builder().build();
+    }
+
+    /**
+     * TestSSLContext creation method that allows separate creation of server key store
+     */
+    public static TestSSLContext create(TestKeyStore client, TestKeyStore server) {
+        return new Builder().client(client).server(server).build();
+    }
+    /**
+     * Create a SSLContext with a KeyManager using the private key and
+     * certificate chain from the given KeyStore and a TrustManager
+     * using the certificates authorities from the same KeyStore.
+     */
+    public static SSLContext createSSLContext(final String protocol, final KeyManager[] keyManagers,
+            final TrustManager[] trustManagers) {
+        try {
+            SSLContext context = SSLContext.getInstance(protocol);
+            context.init(keyManagers, trustManagers, new SecureRandom());
+            return context;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+    public static void assertCertificateInKeyStore(Principal principal, KeyStore keyStore)
+            throws Exception {
+        String subjectName = principal.getName();
+        boolean found = false;
+        for (String alias : Collections.list(keyStore.aliases())) {
+            if (!keyStore.isCertificateEntry(alias)) {
+                continue;
+            }
+            X509Certificate keyStoreCertificate = (X509Certificate) keyStore.getCertificate(alias);
+            if (subjectName.equals(keyStoreCertificate.getSubjectDN().getName())) {
+                found = true;
+                break;
+            }
+        }
+        assertTrue(found);
+    }
+    public static void assertCertificateInKeyStore(Certificate certificate, KeyStore keyStore)
+            throws Exception {
+        boolean found = false;
+        for (String alias : Collections.list(keyStore.aliases())) {
+            if (!keyStore.isCertificateEntry(alias)) {
+                continue;
+            }
+            Certificate keyStoreCertificate = keyStore.getCertificate(alias);
+            if (certificate.equals(keyStoreCertificate)) {
+                found = true;
+                break;
+            }
+        }
+        assertTrue(found);
+    }
+    public static void assertServerCertificateChain(
+            X509TrustManager trustManager, Certificate[] serverChain) throws CertificateException {
+        X509Certificate[] chain = (X509Certificate[]) serverChain;
+        trustManager.checkServerTrusted(chain, chain[0].getPublicKey().getAlgorithm());
+    }
+    public static void assertClientCertificateChain(
+            X509TrustManager trustManager, Certificate[] clientChain) throws CertificateException {
+        X509Certificate[] chain = (X509Certificate[]) clientChain;
+        trustManager.checkClientTrusted(chain, chain[0].getPublicKey().getAlgorithm());
+    }
+    /**
+     * Returns an SSLSocketFactory that calls setWantClientAuth and
+     * setNeedClientAuth as specified on all returned sockets.
+     */
+    public static SSLSocketFactory clientAuth(
+            final SSLSocketFactory sf, final boolean want, final boolean need) {
+        return new SSLSocketFactory() {
+            private SSLSocket set(Socket socket) {
+                SSLSocket s = (SSLSocket) socket;
+                s.setWantClientAuth(want);
+                s.setNeedClientAuth(need);
+                return s;
+            }
+            @Override
+            public Socket createSocket(String host, int port) throws IOException {
+                return set(sf.createSocket(host, port));
+            }
+            @Override
+            public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
+                    throws IOException {
+                return set(sf.createSocket(host, port, localHost, localPort));
+            }
+            @Override
+            public Socket createSocket(InetAddress host, int port) throws IOException {
+                return set(sf.createSocket(host, port));
+            }
+            @Override
+            public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
+                    int localPort) throws IOException {
+                return set(sf.createSocket(address, port));
+            }
+            @Override
+            public String[] getDefaultCipherSuites() {
+                return sf.getDefaultCipherSuites();
+            }
+            @Override
+            public String[] getSupportedCipherSuites() {
+                return sf.getSupportedCipherSuites();
+            }
+            @Override
+            public Socket createSocket(Socket s, String host, int port, boolean autoClose)
+                    throws IOException {
+                return set(sf.createSocket(s, host, port, autoClose));
+            }
+        };
+    }
+    private static KeyManager[] concat(KeyManager[] a, KeyManager[] b) {
+        if ((a == null) || (a.length == 0)) {
+            return b;
+        }
+        if ((b == null) || (b.length == 0)) {
+            return a;
+        }
+        KeyManager[] result = new KeyManager[a.length + b.length];
+        System.arraycopy(a, 0, result, 0, a.length);
+        System.arraycopy(b, 0, result, a.length, b.length);
+        return result;
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestSSLEnginePair.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestSSLEnginePair.java
new file mode 100644
index 0000000..a2906d3
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestSSLEnginePair.java
@@ -0,0 +1,232 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+
+/**
+ * TestSSLEnginePair is a convenience class for other tests that want
+ * a pair of connected and handshaked client and server SSLEngines for
+ * testing.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class TestSSLEnginePair implements Closeable {
+    public final TestSSLContext c;
+    public final SSLEngine server;
+    public final SSLEngine client;
+
+    private TestSSLEnginePair(TestSSLContext c,
+            SSLEngine server,
+            SSLEngine client) {
+        this.c = c;
+        this.server = server;
+        this.client = client;
+    }
+
+    public static TestSSLEnginePair create() throws IOException {
+        return create((Hooks) null);
+    }
+
+    public static TestSSLEnginePair create(TestSSLContext c) throws IOException {
+        return create(c, null);
+    }
+
+    public static TestSSLEnginePair create(Hooks hooks) throws IOException {
+        return create(TestSSLContext.create(), hooks);
+    }
+
+    public static TestSSLEnginePair create(TestSSLContext c, Hooks hooks) throws IOException {
+        return create(c, hooks, null);
+    }
+
+    public static TestSSLEnginePair create(TestSSLContext c, Hooks hooks, boolean[] finished)
+            throws IOException {
+        SSLEngine[] engines = connect(c, hooks, finished);
+        return new TestSSLEnginePair(c, engines[0], engines[1]);
+    }
+
+    public static SSLEngine[] connect(TestSSLContext c, Hooks hooks) throws IOException {
+        return connect(c, hooks, null);
+    }
+
+    /**
+     * Create a new connected server/client engine pair within a
+     * existing SSLContext.
+     */
+    public static SSLEngine[] connect(final TestSSLContext c,
+            Hooks hooks,
+            boolean finished[]) throws IOException {
+        if (hooks == null) {
+            hooks = new Hooks();
+        }
+
+        // FINISHED state should be returned only once.
+        boolean[] clientFinished = new boolean[1];
+        boolean[] serverFinished = new boolean[1];
+
+        SSLSession session = c.clientContext.createSSLEngine().getSession();
+
+        int packetBufferSize = session.getPacketBufferSize();
+        ByteBuffer clientToServer = ByteBuffer.allocate(packetBufferSize);
+        ByteBuffer serverToClient = ByteBuffer.allocate(packetBufferSize);
+
+        int applicationBufferSize = session.getApplicationBufferSize();
+        ByteBuffer scratch = ByteBuffer.allocate(applicationBufferSize);
+
+        SSLEngine client = c.clientContext.createSSLEngine(c.host.getHostName(), c.port);
+        SSLEngine server = c.serverContext.createSSLEngine();
+        client.setUseClientMode(true);
+        server.setUseClientMode(false);
+        hooks.beforeBeginHandshake(client, server);
+        client.beginHandshake();
+        server.beginHandshake();
+
+        while (true) {
+            boolean clientDone = client.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING;
+            boolean serverDone = server.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING;
+            if (clientDone && serverDone) {
+                break;
+            }
+
+            boolean progress =
+                    handshakeStep(client, clientToServer, serverToClient, scratch, clientFinished);
+            progress |=
+                    handshakeStep(server, serverToClient, clientToServer, scratch, serverFinished);
+            if (!progress) {
+                break;
+            }
+        }
+
+        if (finished != null) {
+            assertEquals(2, finished.length);
+            finished[0] = clientFinished[0];
+            finished[1] = serverFinished[0];
+        }
+        return new SSLEngine[] { server, client };
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public static class Hooks {
+        void beforeBeginHandshake(SSLEngine client, SSLEngine server) {}
+    }
+
+    @Override
+    public void close() throws SSLException {
+        close(new SSLEngine[] { client, server });
+    }
+
+    public static void close(SSLEngine[] engines) {
+        try {
+            for (SSLEngine engine : engines) {
+                if (engine != null) {
+                    engine.closeInbound();
+                    engine.closeOutbound();
+                }
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static boolean handshakeStep(SSLEngine engine, ByteBuffer output, ByteBuffer input,
+            ByteBuffer scratch, boolean[] finished) throws IOException {
+        try {
+            // make the other side's output into our input
+            input.flip();
+
+            HandshakeStatus status = engine.getHandshakeStatus();
+            switch (status) {
+
+                case NEED_TASK: {
+                    boolean progress = false;
+                    while (true) {
+                        Runnable runnable = engine.getDelegatedTask();
+                        if (runnable == null) {
+                            return progress;
+                        }
+                        runnable.run();
+                        progress = true;
+                    }
+                }
+
+                case NOT_HANDSHAKING:
+                    // If we're not handshaking, our peer might still be.  Check if there's
+                    // any input for us to consume.
+                case NEED_UNWRAP: {
+                    // avoid underflow
+                    if (input.remaining() == 0) {
+                        return false;
+                    }
+                    int inputPositionBefore = input.position();
+                    SSLEngineResult unwrapResult = engine.unwrap(input, scratch);
+                    assertEquals(SSLEngineResult.Status.OK, unwrapResult.getStatus());
+                    assertEquals(0, scratch.position());
+                    assertEquals(0, unwrapResult.bytesProduced());
+                    assertEquals(input.position() - inputPositionBefore, unwrapResult.bytesConsumed());
+                    assertFinishedOnce(finished, unwrapResult);
+                    return true;
+                }
+
+                case NEED_WRAP: {
+                    // avoid possible overflow
+                    if (output.remaining() != output.capacity()) {
+                        return false;
+                    }
+                    ByteBuffer emptyByteBuffer = ByteBuffer.allocate(0);
+                    int inputPositionBefore = emptyByteBuffer.position();
+                    int outputPositionBefore = output.position();
+                    SSLEngineResult wrapResult = engine.wrap(emptyByteBuffer, output);
+                    assertEquals(SSLEngineResult.Status.OK, wrapResult.getStatus());
+                    assertEquals(0, wrapResult.bytesConsumed());
+                    assertEquals(inputPositionBefore, emptyByteBuffer.position());
+                    assertEquals(output.position() - outputPositionBefore,
+                            wrapResult.bytesProduced());
+                    assertFinishedOnce(finished, wrapResult);
+                    return true;
+                }
+
+                case FINISHED:
+                    // only returned by wrap/unrap status, not getHandshakeStatus
+                    throw new IllegalStateException("Unexpected HandshakeStatus = " + status);
+                default:
+                    throw new IllegalStateException("Unknown HandshakeStatus = " + status);
+            }
+        } finally {
+            // shift consumed input, restore to output mode
+            input.compact();
+        }
+    }
+
+    private static void assertFinishedOnce(boolean[] finishedOut, SSLEngineResult result) {
+        if (result.getHandshakeStatus() == HandshakeStatus.FINISHED) {
+            assertFalse("should only return FINISHED once", finishedOut[0]);
+            finishedOut[0] = true;
+        }
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestSSLSessions.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestSSLSessions.java
new file mode 100644
index 0000000..518d7d3
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestSSLSessions.java
@@ -0,0 +1,79 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.javax.net.ssl;
+
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+/**
+ * TestSSLSessions is a convenience class for other tests that want
+ * precreated SSLSessions for testing. It contains a connected
+ * client/server pair of SSLSession as well as an invalid SSLSession.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class TestSSLSessions {
+    /**
+     * An invalid session that is not connected
+     */
+    public final SSLSession invalid;
+
+    /**
+     * The server side of a connected session
+     */
+    public final SSLSession server;
+
+    /**
+     * The client side of a connected session
+     */
+    public final SSLSession client;
+
+    /**
+     * The associated SSLSocketTest.Helper that is the source of
+     * the client and server SSLSessions.
+     */
+    public final TestSSLSocketPair s;
+
+    private TestSSLSessions(SSLSession invalid,
+            SSLSession server,
+            SSLSession client,
+            TestSSLSocketPair s) {
+        this.invalid = invalid;
+        this.server = server;
+        this.client = client;
+        this.s = s;
+    }
+
+    public void close() {
+        s.close();
+    }
+
+    public static TestSSLSessions create() {
+        return create(TestSSLContext.create());
+    }
+
+    public static TestSSLSessions create(TestSSLContext context) {
+        try {
+            SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+            SSLSocket ssl = (SSLSocket) sf.createSocket();
+            SSLSession invalid = ssl.getSession();
+            TestSSLSocketPair s = TestSSLSocketPair.create(context).connect();
+            return new TestSSLSessions(invalid, s.server.getSession(), s.client.getSession(), s);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestSSLSocketPair.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestSSLSocketPair.java
new file mode 100644
index 0000000..f6a3039
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestSSLSocketPair.java
@@ -0,0 +1,161 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.javax.net.ssl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLSocket;
+import com.android.org.conscrypt.TestUtils;
+
+/**
+ * TestSSLSocketPair is a convenience class for other tests that want
+ * a pair of connected and handshaked client and server SSLSockets for
+ * testing.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class TestSSLSocketPair {
+    public final TestSSLContext c;
+    public final SSLSocket server;
+    public final SSLSocket client;
+    private TestSSLSocketPair(TestSSLContext c, SSLSocket server, SSLSocket client) {
+        this.c = c;
+        this.server = server;
+        this.client = client;
+    }
+    public void close() {
+        c.close();
+        try {
+            server.close();
+            client.close();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public SSLSocket[] sockets() {
+        return new SSLSocket[] {server, client};
+    }
+
+    public TestSSLSocketPair connect() {
+        return connect(null, null);
+    }
+
+    /**
+     * Create a new connected server/client socket pair within a
+     * existing SSLContext. Optionally specify clientCipherSuites to
+     * allow forcing new SSLSession to test SSLSessionContext
+     * caching. Optionally specify serverCipherSuites for testing
+     * cipher suite negotiation.
+     */
+    public TestSSLSocketPair connect(
+            final String[] clientCipherSuites, final String[] serverCipherSuites) {
+        try {
+            ExecutorService executor = Executors.newFixedThreadPool(2);
+            Future<Void> s = executor.submit(new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    if (serverCipherSuites != null) {
+                        server.setEnabledCipherSuites(serverCipherSuites);
+                    }
+                    TestUtils.setUseSessionTickets(server, true);
+                    server.startHandshake();
+                    return null;
+                }
+            });
+            Future<Void> c = executor.submit(new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    if (clientCipherSuites != null) {
+                        client.setEnabledCipherSuites(clientCipherSuites);
+                    }
+                    TestUtils.setUseSessionTickets(client, true);
+                    client.startHandshake();
+                    return null;
+                }
+            });
+            executor.shutdown();
+            // catch client and server exceptions separately so we can
+            // potentially log both.
+            Exception serverException;
+            try {
+                s.get(30, TimeUnit.SECONDS);
+                serverException = null;
+            } catch (Exception e) {
+                serverException = e;
+                e.printStackTrace();
+            }
+            Exception clientException;
+            try {
+                c.get(30, TimeUnit.SECONDS);
+                clientException = null;
+            } catch (Exception e) {
+                clientException = e;
+                e.printStackTrace();
+            }
+            if (serverException != null) {
+                throw serverException;
+            }
+            if (clientException != null) {
+                throw clientException;
+            }
+            // Ensure that messages can actually be passed and that any NewSessionTicket messages
+            // that come after the handshake have been processed.
+            exchangeMessages();
+            return this;
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private TestSSLSocketPair exchangeMessages() {
+        try {
+            client.getOutputStream().write('A');
+            assertEquals((int) 'A', server.getInputStream().read());
+            server.getOutputStream().write('B');
+            assertEquals((int) 'B', client.getInputStream().read());
+            return this;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static TestSSLSocketPair create() {
+        return create(TestSSLContext.create());
+    }
+
+    /**
+     * based on test_SSLSocket_startHandshake
+     */
+    public static TestSSLSocketPair create(TestSSLContext context) {
+        try {
+            SSLSocket client = (SSLSocket) context.clientContext.getSocketFactory().createSocket(
+                    context.host, context.port);
+            SSLSocket server = (SSLSocket) context.serverSocket.accept();
+            return new TestSSLSocketPair(context, server, client);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestTrustManager.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestTrustManager.java
new file mode 100644
index 0000000..aa3a79b
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/javax/net/ssl/TestTrustManager.java
@@ -0,0 +1,282 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.javax.net.ssl;
+
+import java.io.PrintStream;
+import java.net.Socket;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509ExtendedTrustManager;
+import javax.net.ssl.X509TrustManager;
+import com.android.org.conscrypt.java.security.StandardNames;
+import com.android.org.conscrypt.testing.NullPrintStream;
+
+/**
+ * TestTrustManager is a simple proxy class that wraps an existing
+ * X509ExtendedTrustManager to provide debug logging and recording of
+ * values.
+ * @hide This class is not part of the Android public SDK API
+ */
+public abstract class TestTrustManager {
+    private static final boolean LOG = false;
+    private static final PrintStream out = LOG ? System.out : new NullPrintStream();
+    private static final Class<?> EXTENDED_TRUST_MANAGER_CLASS = getExtendedTrustManagerClass();
+
+    public static TrustManager[] wrap(TrustManager[] trustManagers) {
+        TrustManager[] result = trustManagers.clone();
+        for (int i = 0; i < result.length; i++) {
+            result[i] = wrap(result[i]);
+        }
+        return result;
+    }
+
+    public static TrustManager wrap(TrustManager trustManager) {
+        if (EXTENDED_TRUST_MANAGER_CLASS != null && EXTENDED_TRUST_MANAGER_CLASS.isInstance(trustManager)) {
+            return new ExtendedWrapper((X509ExtendedTrustManager) trustManager);
+        } else if (trustManager instanceof X509TrustManager) {
+            return new Wrapper((X509TrustManager) trustManager);
+        }
+        return trustManager;
+    }
+
+    private static Class<?> getExtendedTrustManagerClass() {
+        try {
+            return Class.forName("javax.net.ssl.X509ExtendedTrustManager");
+        } catch (ClassNotFoundException e) {
+            return null;
+        }
+    }
+
+    private static void assertClientAuthType(String authType) {
+        if (!StandardNames.CLIENT_AUTH_TYPES.contains(authType)) {
+            throw new AssertionError("Unexpected client auth type " + authType);
+        }
+    }
+
+    private static void assertServerAuthType(String authType) {
+        if (!StandardNames.SERVER_AUTH_TYPES.contains(authType)) {
+            throw new AssertionError("Unexpected server auth type " + authType);
+        }
+    }
+
+    private static final class Wrapper implements X509TrustManager {
+        private final X509TrustManager trustManager;
+
+        private Wrapper(X509TrustManager trustManager) {
+            out.println("TestTrustManager.<init> trustManager=" + trustManager);
+            this.trustManager = trustManager;
+        }
+
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType)
+            throws CertificateException {
+            out.print("TestTrustManager.checkClientTrusted "
+                + "chain=" + chain.length + " "
+                + "authType=" + authType + " ");
+            try {
+                assertClientAuthType(authType);
+                trustManager.checkClientTrusted(chain, authType);
+                out.println("OK");
+            } catch (CertificateException e) {
+                e.printStackTrace(out);
+                throw e;
+            }
+        }
+
+
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType)
+            throws CertificateException {
+            out.print("TestTrustManager.checkServerTrusted "
+                + "chain=" + chain.length + " "
+                + "authType=" + authType + " ");
+            try {
+                assertServerAuthType(authType);
+                trustManager.checkServerTrusted(chain, authType);
+                out.println("OK");
+            } catch (CertificateException e) {
+                e.printStackTrace(out);
+                throw e;
+            }
+        }
+
+        /**
+         * Returns the list of certificate issuer authorities which are trusted for
+         * authentication of peers.
+         *
+         * @return the list of certificate issuer authorities which are trusted for
+         *         authentication of peers.
+         */
+        @Override
+        public X509Certificate[] getAcceptedIssuers() {
+            X509Certificate[] result = trustManager.getAcceptedIssuers();
+            out.print("TestTrustManager.getAcceptedIssuers result=" + result.length);
+            return result;
+        }
+    }
+
+    private static final class ExtendedWrapper extends X509ExtendedTrustManager {
+        private final X509ExtendedTrustManager extendedTrustManager;
+        private final X509TrustManager trustManager;
+
+        ExtendedWrapper(X509ExtendedTrustManager trustManager) {
+            out.println("TestTrustManager.<init> extendedTrustManager=" + trustManager);
+            this.extendedTrustManager = trustManager;
+            this.trustManager = trustManager;
+        }
+
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType)
+            throws CertificateException {
+            out.print("TestTrustManager.checkClientTrusted "
+                + "chain=" + chain.length + " "
+                + "authType=" + authType + " ");
+            try {
+                assertClientAuthType(authType);
+                trustManager.checkClientTrusted(chain, authType);
+                out.println("OK");
+            } catch (CertificateException e) {
+                e.printStackTrace(out);
+                throw e;
+            }
+        }
+
+
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType)
+            throws CertificateException {
+            out.print("TestTrustManager.checkServerTrusted "
+                + "chain=" + chain.length + " "
+                + "authType=" + authType + " ");
+            try {
+                assertServerAuthType(authType);
+                trustManager.checkServerTrusted(chain, authType);
+                out.println("OK");
+            } catch (CertificateException e) {
+                e.printStackTrace(out);
+                throw e;
+            }
+        }
+
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
+            throws CertificateException {
+            if (extendedTrustManager == null) {
+                out.print("(fallback to X509TrustManager) ");
+                checkClientTrusted(chain, authType);
+                return;
+            }
+            out.print("TestTrustManager.checkClientTrusted "
+                + "chain=" + chain.length + " "
+                + "authType=" + authType + " "
+                + "socket=" + socket + " ");
+            try {
+                assertClientAuthType(authType);
+                extendedTrustManager.checkClientTrusted(chain, authType, socket);
+                out.println("OK");
+            } catch (CertificateException e) {
+                e.printStackTrace(out);
+                throw e;
+            }
+        }
+
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
+            throws CertificateException {
+            if (extendedTrustManager == null) {
+                out.print("(fallback to X509TrustManager) ");
+                checkClientTrusted(chain, authType);
+                return;
+            }
+            out.print("TestTrustManager.checkClientTrusted "
+                + "chain=" + chain.length + " "
+                + "authType=" + authType + " "
+                + "engine=" + engine + " ");
+            try {
+                assertClientAuthType(authType);
+                extendedTrustManager.checkClientTrusted(chain, authType, engine);
+                out.println("OK");
+            } catch (CertificateException e) {
+                e.printStackTrace(out);
+                throw e;
+            }
+        }
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
+            throws CertificateException {
+            if (extendedTrustManager == null) {
+                out.print("(fallback to X509TrustManager) ");
+                checkServerTrusted(chain, authType);
+                return;
+            }
+            out.print("TestTrustManager.checkServerTrusted "
+                + "chain=" + chain.length + " "
+                + "authType=" + authType + " "
+                + "socket=" + socket.toString() + " ");
+            try {
+                assertServerAuthType(authType);
+                extendedTrustManager.checkServerTrusted(chain, authType, socket);
+                out.println("OK");
+            } catch (CertificateException e) {
+                e.printStackTrace(out);
+                throw e;
+            }
+        }
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
+            throws CertificateException {
+            if (extendedTrustManager == null) {
+                out.print("(fallback to X509TrustManager) ");
+                checkServerTrusted(chain, authType);
+                return;
+            }
+            out.print("TestTrustManager.checkServerTrusted "
+                + "chain=" + chain.length + " "
+                + "authType=" + authType + " "
+                + "engine=" + engine.toString() + " ");
+            try {
+                assertServerAuthType(authType);
+                extendedTrustManager.checkServerTrusted(chain, authType, engine);
+                out.println("OK");
+            } catch (CertificateException e) {
+                e.printStackTrace(out);
+                throw e;
+            }
+        }
+
+        /**
+         * Returns the list of certificate issuer authorities which are trusted for
+         * authentication of peers.
+         *
+         * @return the list of certificate issuer authorities which are trusted for
+         *         authentication of peers.
+         */
+        @Override
+        public X509Certificate[] getAcceptedIssuers() {
+            X509Certificate[] result = trustManager.getAcceptedIssuers();
+            out.print("TestTrustManager.getAcceptedIssuers result=" + result.length);
+            return result;
+        }
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/testing/BrokenProvider.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/testing/BrokenProvider.java
new file mode 100644
index 0000000..e7b11ff
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/testing/BrokenProvider.java
@@ -0,0 +1,81 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.conscrypt.testing;
+
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.SignatureSpi;
+
+/**
+ * A provider that throws UnsupportedOperationException whenever its features are used.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class BrokenProvider extends Provider {
+
+  public static final String NAME = "BrokenProvider";
+
+  public BrokenProvider() {
+    super(NAME, 1.0, "A broken provider");
+    put("Signature.NONEwithECDSA", BrokenSignatureSpi.ECDSA.class.getName());
+  }
+
+  private static class BrokenSignatureSpi extends SignatureSpi {
+
+    BrokenSignatureSpi() { }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public final static class ECDSA extends BrokenSignatureSpi {
+      public ECDSA() {
+        super();
+      }
+    }
+
+    @Override
+    protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+      throw new UnsupportedOperationException("Nope");
+    }
+
+    @Override
+    protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
+      throw new UnsupportedOperationException("Nope");
+    }
+
+    @Override
+    protected void engineUpdate(byte b) throws SignatureException {
+      throw new UnsupportedOperationException("Nope");
+    }
+
+    @Override
+    protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
+      throw new UnsupportedOperationException("Nope");
+    }
+
+    @Override
+    protected byte[] engineSign() throws SignatureException {
+      throw new UnsupportedOperationException("Nope");
+    }
+
+    @Override
+    protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+      throw new UnsupportedOperationException("Nope");
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected void engineSetParameter(String param, Object value)
+        throws InvalidParameterException {
+      throw new UnsupportedOperationException("Nope");
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected Object engineGetParameter(String param) throws InvalidParameterException {
+      throw new UnsupportedOperationException("Nope");
+    }
+  }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/testing/NullPrintStream.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/testing/NullPrintStream.java
new file mode 100644
index 0000000..ca1e70b
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/testing/NullPrintStream.java
@@ -0,0 +1,149 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.testing;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Locale;
+
+/**
+ * A PrintStream that throws away its output.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class NullPrintStream extends PrintStream {
+    public NullPrintStream() {
+        // super class complains if argument is null
+        super((OutputStream) new ByteArrayOutputStream());
+    }
+
+    @Override
+    public boolean checkError() {
+        return false;
+    }
+
+    @Override
+    protected void clearError() {}
+
+    @Override
+    public void close() {}
+
+    @Override
+    public void flush() {}
+
+    @Override
+    public PrintStream format(String format, Object... args) {
+        return this;
+    }
+
+    @Override
+    public PrintStream format(Locale l, String format, Object... args) {
+        return this;
+    }
+
+    @Override
+    public PrintStream printf(String format, Object... args) {
+        return this;
+    }
+
+    @Override
+    public PrintStream printf(Locale l, String format, Object... args) {
+        return this;
+    }
+
+    @Override
+    public void print(char[] charArray) {}
+
+    @Override
+    public void print(char ch) {}
+
+    @Override
+    public void print(double dnum) {}
+
+    @Override
+    public void print(float fnum) {}
+
+    @Override
+    public void print(int inum) {}
+
+    @Override
+    public void print(long lnum) {}
+
+    @Override
+    public void print(Object obj) {}
+
+    @Override
+    public void print(String str) {}
+
+    @Override
+    public void print(boolean bool) {}
+
+    @Override
+    public void println() {}
+
+    @Override
+    public void println(char[] charArray) {}
+
+    @Override
+    public void println(char ch) {}
+
+    @Override
+    public void println(double dnum) {}
+
+    @Override
+    public void println(float fnum) {}
+
+    @Override
+    public void println(int inum) {}
+
+    @Override
+    public void println(long lnum) {}
+
+    @Override
+    public void println(Object obj) {}
+
+    @Override
+    public void println(String str) {}
+
+    @Override
+    public void println(boolean bool) {}
+
+    @Override
+    protected void setError() {}
+
+    @Override
+    public void write(byte[] buffer, int offset, int length) {}
+
+    @Override
+    public void write(int oneByte) {}
+
+    @Override
+    public PrintStream append(char c) {
+        return this;
+    }
+
+    @Override
+    public PrintStream append(CharSequence csq) {
+        return this;
+    }
+
+    @Override
+    public PrintStream append(CharSequence csq, int start, int end) {
+        return this;
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/testing/OpaqueProvider.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/testing/OpaqueProvider.java
new file mode 100644
index 0000000..7ad4b83
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/testing/OpaqueProvider.java
@@ -0,0 +1,353 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.conscrypt.testing;
+
+import static org.junit.Assert.fail;
+
+import java.math.BigInteger;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.SignatureSpi;
+import java.security.interfaces.ECKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.RSAKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECParameterSpec;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+import com.android.org.conscrypt.java.security.StandardNames;
+
+/**
+ * A provider that supplies and can use keys whose keying material is hidden from callers.
+ * @hide This class is not part of the Android public SDK API
+ */
+@SuppressWarnings("serial")
+public class OpaqueProvider extends Provider {
+
+  public static final String NAME = "OpaqueProvider";
+
+  public OpaqueProvider() {
+    super(NAME, 1.0, "test provider");
+    put("Signature.NONEwithECDSA", OpaqueSignatureSpi.ECDSA.class.getName());
+    put("Cipher.RSA/ECB/NoPadding", OpaqueCipherSpi.NoPadding.class.getName());
+    put("Cipher.RSA/ECB/PKCS1Padding", OpaqueCipherSpi.PKCS1Padding.class.getName());
+  }
+
+  /**
+   * Returns an opaque key that wraps the given key.
+   */
+  public static PrivateKey wrapKey(PrivateKey key) {
+    if (key instanceof RSAPrivateKey) {
+      return new OpaqueDelegatingRSAPrivateKey((RSAPrivateKey) key);
+    } else if (key instanceof ECPrivateKey) {
+      return new OpaqueDelegatingECPrivateKey((ECPrivateKey) key);
+    } else {
+      fail("Unknown key type: " + key.getClass().getName());
+      return null;
+    }
+  }
+
+  /**
+   * Returns an opaque key that wraps the given key and is additionally marked with the
+   * appropriate FooPrivateKey interface for that key type.
+   */
+  public static PrivateKey wrapKeyMarked(PrivateKey key) {
+    if (key instanceof RSAPrivateKey) {
+      return new OpaqueDelegatingMarkedRSAPrivateKey((RSAPrivateKey) key);
+    } else if (key instanceof ECPrivateKey) {
+      return new OpaqueDelegatingMarkedECPrivateKey((ECPrivateKey) key);
+    } else {
+      fail("Unknown key type: " + key.getClass().getName());
+      return null;
+    }
+  }
+
+  private static class OpaqueSignatureSpi extends SignatureSpi {
+    private final String algorithm;
+    private Signature delegate;
+
+    OpaqueSignatureSpi(String algorithm) {
+      this.algorithm = algorithm;
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public final static class ECDSA extends OpaqueSignatureSpi {
+      public ECDSA() {
+        super("NONEwithECDSA");
+      }
+    }
+
+    @Override
+    protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+      fail("Cannot verify");
+    }
+
+    @Override
+    protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
+      DelegatingPrivateKey opaqueKey = (DelegatingPrivateKey) privateKey;
+      try {
+        delegate = Signature.getInstance(algorithm);
+      } catch (NoSuchAlgorithmException e) {
+        throw new InvalidKeyException(e);
+      }
+      delegate.initSign(opaqueKey.getDelegate());
+    }
+
+    @Override
+    protected void engineUpdate(byte b) throws SignatureException {
+      delegate.update(b);
+    }
+
+    @Override
+    protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
+      delegate.update(b, off, len);
+    }
+
+    @Override
+    protected byte[] engineSign() throws SignatureException {
+      return delegate.sign();
+    }
+
+    @Override
+    protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+      return delegate.verify(sigBytes);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected void engineSetParameter(String param, Object value)
+        throws InvalidParameterException {
+      delegate.setParameter(param, value);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected Object engineGetParameter(String param) throws InvalidParameterException {
+      return delegate.getParameter(param);
+    }
+  }
+
+  private static class OpaqueCipherSpi extends CipherSpi {
+    private Cipher delegate;
+    private final String algorithm;
+
+    public OpaqueCipherSpi(String algorithm) {
+      this.algorithm = algorithm;
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public final static class NoPadding extends OpaqueCipherSpi {
+      public NoPadding() {
+        super("RSA/ECB/NoPadding");
+      }
+    }
+
+    /**
+     * @hide This class is not part of the Android public SDK API
+     */
+    public final static class PKCS1Padding extends OpaqueCipherSpi {
+      public PKCS1Padding() {
+        super("RSA/ECB/PKCS1Padding");
+      }
+    }
+
+    @Override
+    protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
+      fail();
+    }
+
+    @Override
+    protected void engineSetPadding(String padding) throws NoSuchPaddingException {
+      fail();
+    }
+
+    @Override
+    protected int engineGetBlockSize() {
+      return delegate.getBlockSize();
+    }
+
+    @Override
+    protected int engineGetOutputSize(int inputLen) {
+      return delegate.getOutputSize(inputLen);
+    }
+
+    @Override
+    protected byte[] engineGetIV() {
+      return delegate.getIV();
+    }
+
+    @Override
+    protected AlgorithmParameters engineGetParameters() {
+      return delegate.getParameters();
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, SecureRandom random)
+        throws InvalidKeyException {
+      getCipher();
+      delegate.init(opmode, ((DelegatingPrivateKey) key).getDelegate(), random);
+    }
+
+    void getCipher() throws InvalidKeyException {
+      try {
+        delegate = Cipher.getInstance(algorithm, StandardNames.JSSE_PROVIDER_NAME);
+      } catch (NoSuchAlgorithmException | NoSuchPaddingException | NoSuchProviderException e) {
+        throw new InvalidKeyException(e);
+      }
+    }
+
+    @Override
+    protected void engineInit(
+        int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random)
+        throws InvalidKeyException, InvalidAlgorithmParameterException {
+      getCipher();
+      delegate.init(opmode, ((DelegatingPrivateKey) key).getDelegate(), params, random);
+    }
+
+    @Override
+    protected void engineInit(
+        int opmode, Key key, AlgorithmParameters params, SecureRandom random)
+        throws InvalidKeyException, InvalidAlgorithmParameterException {
+      getCipher();
+      delegate.init(opmode, ((DelegatingPrivateKey) key).getDelegate(), params, random);
+    }
+
+    @Override
+    protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
+      return delegate.update(input, inputOffset, inputLen);
+    }
+
+    @Override
+    protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
+        int outputOffset) throws ShortBufferException {
+      return delegate.update(input, inputOffset, inputLen, output, outputOffset);
+    }
+
+    @Override
+    protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
+        throws IllegalBlockSizeException, BadPaddingException {
+      return delegate.doFinal(input, inputOffset, inputLen);
+    }
+
+    @Override
+    protected int engineDoFinal(
+        byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)
+        throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
+      return delegate.doFinal(input, inputOffset, inputLen, output, outputOffset);
+    }
+  }
+
+  private interface DelegatingPrivateKey { PrivateKey getDelegate(); }
+
+  private static class OpaqueDelegatingECPrivateKey
+      implements ECKey, PrivateKey, DelegatingPrivateKey {
+    private final ECPrivateKey delegate;
+
+    private OpaqueDelegatingECPrivateKey(ECPrivateKey delegate) {
+      this.delegate = delegate;
+    }
+
+    @Override
+    public PrivateKey getDelegate() {
+      return delegate;
+    }
+
+    @Override
+    public String getAlgorithm() {
+      return delegate.getAlgorithm();
+    }
+
+    @Override
+    public String getFormat() {
+      return null;
+    }
+
+    @Override
+    public byte[] getEncoded() {
+      return null;
+    }
+
+    @Override
+    public ECParameterSpec getParams() {
+      return delegate.getParams();
+    }
+  }
+
+  private static class OpaqueDelegatingMarkedECPrivateKey extends OpaqueDelegatingECPrivateKey
+      implements ECPrivateKey {
+    private OpaqueDelegatingMarkedECPrivateKey(ECPrivateKey delegate) {
+      super(delegate);
+    }
+
+    @Override
+    public BigInteger getS() {
+      throw new UnsupportedOperationException("Nope");
+    }
+  }
+
+  private static class OpaqueDelegatingRSAPrivateKey
+      implements RSAKey, PrivateKey, DelegatingPrivateKey {
+
+    private final RSAPrivateKey delegate;
+
+    private OpaqueDelegatingRSAPrivateKey(RSAPrivateKey delegate) {
+      this.delegate = delegate;
+    }
+
+    @Override
+    public String getAlgorithm() {
+      return delegate.getAlgorithm();
+    }
+
+    @Override
+    public String getFormat() {
+      return null;
+    }
+
+    @Override
+    public byte[] getEncoded() {
+      return null;
+    }
+
+    @Override
+    public BigInteger getModulus() {
+      return delegate.getModulus();
+    }
+
+    @Override
+    public PrivateKey getDelegate() {
+      return delegate;
+    }
+  }
+
+  private static class OpaqueDelegatingMarkedRSAPrivateKey extends OpaqueDelegatingRSAPrivateKey
+     implements RSAPrivateKey {
+    private OpaqueDelegatingMarkedRSAPrivateKey(RSAPrivateKey delegate) {
+      super(delegate);
+    }
+
+    @Override
+    public BigInteger getPrivateExponent() {
+      throw new UnsupportedOperationException("Nope");
+    }
+  }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/testing/Streams.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/testing/Streams.java
new file mode 100644
index 0000000..9225df1
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/testing/Streams.java
@@ -0,0 +1,69 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.org.conscrypt.testing;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class Streams {
+    private Streams() {}
+
+    /**
+     * Returns a byte[] containing the remainder of 'in', closing it when done.
+     */
+    public static byte[] readFully(InputStream in) throws IOException {
+        try {
+            return readFullyNoClose(in);
+        } finally {
+            in.close();
+        }
+    }
+
+    /**
+     * Returns a byte[] containing the remainder of 'in'.
+     */
+    private static byte[] readFullyNoClose(InputStream in) throws IOException {
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int count;
+        while ((count = in.read(buffer)) != -1) {
+            bytes.write(buffer, 0, count);
+        }
+        return bytes.toByteArray();
+    }
+
+    /**
+     * Copies all of the bytes from {@code in} to {@code out}. Neither stream is closed.
+     * Returns the total number of bytes transferred.
+     */
+    public static int copy(InputStream in, OutputStream out) throws IOException {
+        int total = 0;
+        byte[] buffer = new byte[8192];
+        int c;
+        while ((c = in.read(buffer)) != -1) {
+            total += c;
+            out.write(buffer, 0, c);
+        }
+        return total;
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/TlsTester.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/TlsTester.java
new file mode 100644
index 0000000..fc8d802
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/TlsTester.java
@@ -0,0 +1,161 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+package com.android.org.conscrypt.tlswire;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Arrays;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import javax.net.ServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import com.android.org.conscrypt.tlswire.handshake.ClientHello;
+import com.android.org.conscrypt.tlswire.handshake.HandshakeMessage;
+import com.android.org.conscrypt.tlswire.record.TlsProtocols;
+import com.android.org.conscrypt.tlswire.record.TlsRecord;
+
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class TlsTester {
+
+    private TlsTester() {}
+
+    public static ClientHello captureTlsHandshakeClientHello(ExecutorService executor,
+            SSLSocketFactory sslSocketFactory) throws Exception {
+        TlsRecord record = captureTlsHandshakeFirstTlsRecord(executor, sslSocketFactory);
+        return parseClientHello(record);
+    }
+
+    public static ClientHello parseClientHello(byte[] data) throws Exception {
+        return parseClientHello(parseRecord(data));
+    }
+
+    private static ClientHello parseClientHello(TlsRecord record) throws Exception {
+        assertEquals("TLS record type", TlsProtocols.HANDSHAKE, record.type);
+        ByteArrayInputStream fragmentIn = new ByteArrayInputStream(record.fragment);
+        HandshakeMessage handshakeMessage = HandshakeMessage.read(new DataInputStream(fragmentIn));
+        assertEquals(
+                "HandshakeMessage type", HandshakeMessage.TYPE_CLIENT_HELLO, handshakeMessage.type);
+        // Assert that the fragment does not contain any more messages
+        assertEquals(0, fragmentIn.available());
+        return (ClientHello) handshakeMessage;
+    }
+
+    public static TlsRecord captureTlsHandshakeFirstTlsRecord(ExecutorService executor,
+            SSLSocketFactory sslSocketFactory) throws Exception {
+        byte[] firstReceivedChunk = captureTlsHandshakeFirstTransmittedChunkBytes(executor, sslSocketFactory);
+        return parseRecord(firstReceivedChunk);
+    }
+
+    public static TlsRecord parseRecord(byte[] data) throws Exception {
+        ByteArrayInputStream firstReceivedChunkIn = new ByteArrayInputStream(data);
+        TlsRecord record = TlsRecord.read(new DataInputStream(firstReceivedChunkIn));
+        // Assert that the chunk does not contain any more data
+        assertEquals(0, firstReceivedChunkIn.available());
+        return record;
+    }
+
+    @SuppressWarnings("FutureReturnValueIgnored")
+    private static byte[] captureTlsHandshakeFirstTransmittedChunkBytes(
+            ExecutorService executor, final SSLSocketFactory sslSocketFactory) throws Exception {
+        // Since there's no straightforward way to obtain a ClientHello from SSLSocket, this test
+        // does the following:
+        // 1. Creates a listening server socket (a plain one rather than a TLS/SSL one).
+        // 2. Creates a client SSLSocket, which connects to the server socket and initiates the
+        //    TLS/SSL handshake.
+        // 3. Makes the server socket accept an incoming connection on the server socket, and reads
+        //    the first chunk of data received. This chunk is assumed to be the ClientHello.
+        // NOTE: Steps 2 and 3 run concurrently.
+        ServerSocket listeningSocket = null;
+        // Some Socket operations are not interruptible via Thread.interrupt for some reason. To
+        // work around, we unblock these sockets using Socket.close.
+        final Socket[] sockets = new Socket[2];
+        try {
+            // 1. Create the listening server socket.
+            listeningSocket = ServerSocketFactory.getDefault().createServerSocket(0);
+            final ServerSocket finalListeningSocket = listeningSocket;
+            // 2. (in background) Wait for an incoming connection and read its first chunk.
+            final Future<byte[]>
+                    readFirstReceivedChunkFuture = executor.submit(new Callable<byte[]>() {
+                @Override
+                public byte[] call() throws Exception {
+                    Socket socket = finalListeningSocket.accept();
+                    sockets[1] = socket;
+                    try {
+                        byte[] buffer = new byte[64 * 1024];
+                        int bytesRead = socket.getInputStream().read(buffer);
+                        if (bytesRead == -1) {
+                            throw new EOFException("Failed to read anything");
+                        }
+                        return Arrays.copyOf(buffer, bytesRead);
+                    } finally {
+                        closeQuietly(socket);
+                    }
+                }
+            });
+            // 3. Create a client socket, connect it to the server socket, and start the TLS/SSL
+            //    handshake.
+            executor.submit(new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    Socket client = new Socket();
+                    sockets[0] = client;
+                    try {
+                        client.connect(finalListeningSocket.getLocalSocketAddress());
+                        // Initiate the TLS/SSL handshake which is expected to fail as soon as the
+                        // server socket receives a ClientHello.
+                        try {
+                            SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(client,
+                                    "localhost.localdomain", finalListeningSocket.getLocalPort(),
+                                    true);
+                            sslSocket.startHandshake();
+                            fail();
+                            return null;
+                        } catch (IOException expected) {
+                            // Ignored.
+                        }
+                        return null;
+                    } finally {
+                        closeQuietly(client);
+                    }
+                }
+            });
+            // Wait for the ClientHello to arrive
+            return readFirstReceivedChunkFuture.get(10, TimeUnit.SECONDS);
+        } finally {
+            closeQuietly(listeningSocket);
+            closeQuietly(sockets[0]);
+            closeQuietly(sockets[1]);
+        }
+    }
+
+    private static void closeQuietly(Socket socket) {
+        if (socket != null) {
+            try {
+                socket.close();
+            } catch (IOException ignored) {
+                // Ignored.
+            }
+        }
+    }
+
+    private static void closeQuietly(ServerSocket serverSocket) {
+        if (serverSocket != null) {
+            try {
+                serverSocket.close();
+            } catch (IOException ignored) {
+                // Ignored.
+            }
+        }
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/AlpnHelloExtension.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/AlpnHelloExtension.java
new file mode 100644
index 0000000..f0709ec
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/AlpnHelloExtension.java
@@ -0,0 +1,53 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.tlswire.handshake;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import com.android.org.conscrypt.tlswire.util.IoUtils;
+
+/**
+ * {@code application_layer_protocol_negotiation} {@link HelloExtension} from RFC 7301 section 3.1.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class AlpnHelloExtension extends HelloExtension {
+
+    public List<String> protocols;
+
+    @Override
+    protected void parseData() throws IOException {
+        byte[] alpnListBytes = IoUtils.readTlsVariableLengthByteVector(
+                new DataInputStream(new ByteArrayInputStream(data)), 0xffff);
+        protocols = new ArrayList<String>();
+        DataInputStream alpnList = new DataInputStream(new ByteArrayInputStream(alpnListBytes));
+        while (alpnList.available() > 0) {
+            byte[] alpnValue = IoUtils.readTlsVariableLengthByteVector(alpnList, 0xff);
+            protocols.add(new String(alpnValue, "US-ASCII"));
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("HelloExtension{type: elliptic_curves, protocols: ");
+        sb.append(protocols);
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/CipherSuite.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/CipherSuite.java
new file mode 100644
index 0000000..89857dd
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/CipherSuite.java
@@ -0,0 +1,464 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.tlswire.handshake;
+import java.util.HashMap;
+import java.util.Map;
+/**
+ * {@code CipherSuite} enum from TLS 1.2 RFC 5246.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class CipherSuite {
+    // The list of cipher suites below is based on IANA registry
+    // https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
+    private static final CipherSuite[] CIPHER_SUITES = new CipherSuite[] {
+            new CipherSuite(0x0000, "TLS_NULL_WITH_NULL_NULL"),
+            new CipherSuite(0x0001, "TLS_RSA_WITH_NULL_MD5", "SSL_RSA_WITH_NULL_MD5"),
+            new CipherSuite(0x0002, "TLS_RSA_WITH_NULL_SHA", "SSL_RSA_WITH_NULL_SHA"),
+            new CipherSuite(
+                    0x0003, "TLS_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_RSA_EXPORT_WITH_RC4_40_MD5"),
+            new CipherSuite(0x0004, "TLS_RSA_WITH_RC4_128_MD5", "SSL_RSA_WITH_RC4_128_MD5"),
+            new CipherSuite(0x0005, "TLS_RSA_WITH_RC4_128_SHA", "SSL_RSA_WITH_RC4_128_SHA"),
+            new CipherSuite(0x0006, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"),
+            new CipherSuite(0x0007, "TLS_RSA_WITH_IDEA_CBC_SHA"),
+            new CipherSuite(0x0008, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA",
+                    "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA"),
+            new CipherSuite(0x0009, "TLS_RSA_WITH_DES_CBC_SHA", "SSL_RSA_WITH_DES_CBC_SHA"),
+            new CipherSuite(
+                    0x000a, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0x000b, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"),
+            new CipherSuite(0x000c, "TLS_DH_DSS_WITH_DES_CBC_SHA"),
+            new CipherSuite(0x000d, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0x000e, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"),
+            new CipherSuite(0x000f, "TLS_DH_RSA_WITH_DES_CBC_SHA"),
+            new CipherSuite(0x0010, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0x0011, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
+                    "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"),
+            new CipherSuite(0x0012, "TLS_DHE_DSS_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA"),
+            new CipherSuite(0x0013, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
+                    "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0x0014, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+                    "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"),
+            new CipherSuite(0x0015, "TLS_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_RSA_WITH_DES_CBC_SHA"),
+            new CipherSuite(0x0016, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+                    "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0x0017, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5",
+                    "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5"),
+            new CipherSuite(0x0018, "TLS_DH_anon_WITH_RC4_128_MD5", "SSL_DH_anon_WITH_RC4_128_MD5"),
+            new CipherSuite(0x0019, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
+                    "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA"),
+            new CipherSuite(0x001a, "TLS_DH_anon_WITH_DES_CBC_SHA", "SSL_DH_anon_WITH_DES_CBC_SHA"),
+            new CipherSuite(0x001b, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA",
+                    "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0x001e, "TLS_KRB5_WITH_DES_CBC_SHA"),
+            new CipherSuite(0x001f, "TLS_KRB5_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0x0020, "TLS_KRB5_WITH_RC4_128_SHA"),
+            new CipherSuite(0x0021, "TLS_KRB5_WITH_IDEA_CBC_SHA"),
+            new CipherSuite(0x0022, "TLS_KRB5_WITH_DES_CBC_MD5"),
+            new CipherSuite(0x0023, "TLS_KRB5_WITH_3DES_EDE_CBC_MD5"),
+            new CipherSuite(0x0024, "TLS_KRB5_WITH_RC4_128_MD5"),
+            new CipherSuite(0x0025, "TLS_KRB5_WITH_IDEA_CBC_MD5"),
+            new CipherSuite(0x0026, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA"),
+            new CipherSuite(0x0027, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA"),
+            new CipherSuite(0x0028, "TLS_KRB5_EXPORT_WITH_RC4_40_SHA"),
+            new CipherSuite(0x0029, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5"),
+            new CipherSuite(0x002a, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5"),
+            new CipherSuite(0x002b, "TLS_KRB5_EXPORT_WITH_RC4_40_MD5"),
+            new CipherSuite(0x002c, "TLS_PSK_WITH_NULL_SHA"),
+            new CipherSuite(0x002d, "TLS_DHE_PSK_WITH_NULL_SHA"),
+            new CipherSuite(0x002e, "TLS_RSA_PSK_WITH_NULL_SHA"),
+            new CipherSuite(0x002f, "TLS_RSA_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0x0030, "TLS_DH_DSS_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0x0031, "TLS_DH_RSA_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0x0032, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0x0033, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0x0034, "TLS_DH_anon_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0x0035, "TLS_RSA_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0x0036, "TLS_DH_DSS_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0x0037, "TLS_DH_RSA_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0x0038, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0x0039, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0x003a, "TLS_DH_anon_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0x003b, "TLS_RSA_WITH_NULL_SHA256"),
+            new CipherSuite(0x003c, "TLS_RSA_WITH_AES_128_CBC_SHA256"),
+            new CipherSuite(0x003d, "TLS_RSA_WITH_AES_256_CBC_SHA256"),
+            new CipherSuite(0x003e, "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"),
+            new CipherSuite(0x003f, "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"),
+            new CipherSuite(0x0040, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"),
+            new CipherSuite(0x0041, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA"),
+            new CipherSuite(0x0042, "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA"),
+            new CipherSuite(0x0043, "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA"),
+            new CipherSuite(0x0044, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA"),
+            new CipherSuite(0x0045, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA"),
+            new CipherSuite(0x0046, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA"),
+            new CipherSuite(0x0060, "TLS_RSA_EXPORT1024_WITH_RC4_56_MD5"),
+            new CipherSuite(0x0061, "TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5"),
+            new CipherSuite(0x0062, "TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA"),
+            new CipherSuite(0x0063, "TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA"),
+            new CipherSuite(0x0064, "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA"),
+            new CipherSuite(0x0065, "TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA"),
+            new CipherSuite(0x0066, "TLS_DHE_DSS_WITH_RC4_128_SHA"),
+            new CipherSuite(0x0067, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"),
+            new CipherSuite(0x0068, "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"),
+            new CipherSuite(0x0069, "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"),
+            new CipherSuite(0x006a, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"),
+            new CipherSuite(0x006b, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"),
+            new CipherSuite(0x006c, "TLS_DH_anon_WITH_AES_128_CBC_SHA256"),
+            new CipherSuite(0x006d, "TLS_DH_anon_WITH_AES_256_CBC_SHA256"),
+            new CipherSuite(0x0084, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA"),
+            new CipherSuite(0x0085, "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA"),
+            new CipherSuite(0x0086, "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA"),
+            new CipherSuite(0x0087, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA"),
+            new CipherSuite(0x0088, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA"),
+            new CipherSuite(0x0089, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA"),
+            new CipherSuite(0x008a, "TLS_PSK_WITH_RC4_128_SHA"),
+            new CipherSuite(0x008b, "TLS_PSK_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0x008c, "TLS_PSK_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0x008d, "TLS_PSK_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0x008e, "TLS_DHE_PSK_WITH_RC4_128_SHA"),
+            new CipherSuite(0x008f, "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0x0090, "TLS_DHE_PSK_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0x0091, "TLS_DHE_PSK_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0x0092, "TLS_RSA_PSK_WITH_RC4_128_SHA"),
+            new CipherSuite(0x0093, "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0x0094, "TLS_RSA_PSK_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0x0095, "TLS_RSA_PSK_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0x0096, "TLS_RSA_WITH_SEED_CBC_SHA"),
+            new CipherSuite(0x0097, "TLS_DH_DSS_WITH_SEED_CBC_SHA"),
+            new CipherSuite(0x0098, "TLS_DH_RSA_WITH_SEED_CBC_SHA"),
+            new CipherSuite(0x0099, "TLS_DHE_DSS_WITH_SEED_CBC_SHA"),
+            new CipherSuite(0x009a, "TLS_DHE_RSA_WITH_SEED_CBC_SHA"),
+            new CipherSuite(0x009b, "TLS_DH_anon_WITH_SEED_CBC_SHA"),
+            new CipherSuite(0x009c, "TLS_RSA_WITH_AES_128_GCM_SHA256"),
+            new CipherSuite(0x009d, "TLS_RSA_WITH_AES_256_GCM_SHA384"),
+            new CipherSuite(0x009e, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"),
+            new CipherSuite(0x009f, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"),
+            new CipherSuite(0x00a0, "TLS_DH_RSA_WITH_AES_128_GCM_SHA256"),
+            new CipherSuite(0x00a1, "TLS_DH_RSA_WITH_AES_256_GCM_SHA384"),
+            new CipherSuite(0x00a2, "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256"),
+            new CipherSuite(0x00a3, "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384"),
+            new CipherSuite(0x00a4, "TLS_DH_DSS_WITH_AES_128_GCM_SHA256"),
+            new CipherSuite(0x00a5, "TLS_DH_DSS_WITH_AES_256_GCM_SHA384"),
+            new CipherSuite(0x00a6, "TLS_DH_anon_WITH_AES_128_GCM_SHA256"),
+            new CipherSuite(0x00a7, "TLS_DH_anon_WITH_AES_256_GCM_SHA384"),
+            new CipherSuite(0x00a8, "TLS_PSK_WITH_AES_128_GCM_SHA256"),
+            new CipherSuite(0x00a9, "TLS_PSK_WITH_AES_256_GCM_SHA384"),
+            new CipherSuite(0x00aa, "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256"),
+            new CipherSuite(0x00ab, "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384"),
+            new CipherSuite(0x00ac, "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256"),
+            new CipherSuite(0x00ad, "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384"),
+            new CipherSuite(0x00ae, "TLS_PSK_WITH_AES_128_CBC_SHA256"),
+            new CipherSuite(0x00af, "TLS_PSK_WITH_AES_256_CBC_SHA384"),
+            new CipherSuite(0x00b0, "TLS_PSK_WITH_NULL_SHA256"),
+            new CipherSuite(0x00b1, "TLS_PSK_WITH_NULL_SHA384"),
+            new CipherSuite(0x00b2, "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256"),
+            new CipherSuite(0x00b3, "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384"),
+            new CipherSuite(0x00b4, "TLS_DHE_PSK_WITH_NULL_SHA256"),
+            new CipherSuite(0x00b5, "TLS_DHE_PSK_WITH_NULL_SHA384"),
+            new CipherSuite(0x00b6, "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256"),
+            new CipherSuite(0x00b7, "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384"),
+            new CipherSuite(0x00b8, "TLS_RSA_PSK_WITH_NULL_SHA256"),
+            new CipherSuite(0x00b9, "TLS_RSA_PSK_WITH_NULL_SHA384"),
+            new CipherSuite(0x00ba, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256"),
+            new CipherSuite(0x00bb, "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256"),
+            new CipherSuite(0x00bc, "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256"),
+            new CipherSuite(0x00bd, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256"),
+            new CipherSuite(0x00be, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256"),
+            new CipherSuite(0x00bf, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256"),
+            new CipherSuite(0x00c0, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256"),
+            new CipherSuite(0x00c1, "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256"),
+            new CipherSuite(0x00c2, "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256"),
+            new CipherSuite(0x00c3, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256"),
+            new CipherSuite(0x00c4, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256"),
+            new CipherSuite(0x00c5, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256"),
+            new CipherSuite(0x00ff, "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"),
+            new CipherSuite(0x1301, "TLS_AES_128_GCM_SHA256"),
+            new CipherSuite(0x1302, "TLS_AES_256_GCM_SHA384"),
+            new CipherSuite(0x1303, "TLS_CHACHA20_POLY1305_SHA256"),
+            new CipherSuite(0x1304, "TLS_AES_128_CCM_SHA256"),
+            new CipherSuite(0x1305, "TLS_AES_128_CCM_8_SHA256"),
+            new CipherSuite(0x5600, "TLS_FALLBACK_SCSV"),
+            new CipherSuite(0xc001, "TLS_ECDH_ECDSA_WITH_NULL_SHA"),
+            new CipherSuite(0xc002, "TLS_ECDH_ECDSA_WITH_RC4_128_SHA"),
+            new CipherSuite(0xc003, "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0xc004, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0xc005, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0xc006, "TLS_ECDHE_ECDSA_WITH_NULL_SHA"),
+            new CipherSuite(0xc007, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA"),
+            new CipherSuite(0xc008, "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0xc009, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0xc00a, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0xc00b, "TLS_ECDH_RSA_WITH_NULL_SHA"),
+            new CipherSuite(0xc00c, "TLS_ECDH_RSA_WITH_RC4_128_SHA"),
+            new CipherSuite(0xc00d, "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0xc00e, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0xc00f, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0xc010, "TLS_ECDHE_RSA_WITH_NULL_SHA"),
+            new CipherSuite(0xc011, "TLS_ECDHE_RSA_WITH_RC4_128_SHA"),
+            new CipherSuite(0xc012, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0xc013, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0xc014, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0xc015, "TLS_ECDH_anon_WITH_NULL_SHA"),
+            new CipherSuite(0xc016, "TLS_ECDH_anon_WITH_RC4_128_SHA"),
+            new CipherSuite(0xc017, "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0xc018, "TLS_ECDH_anon_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0xc019, "TLS_ECDH_anon_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0xc01a, "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0xc01b, "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0xc01c, "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0xc01d, "TLS_SRP_SHA_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0xc01e, "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0xc01f, "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0xc020, "TLS_SRP_SHA_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0xc021, "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0xc022, "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0xc023, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"),
+            new CipherSuite(0xc024, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"),
+            new CipherSuite(0xc025, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256"),
+            new CipherSuite(0xc026, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384"),
+            new CipherSuite(0xc027, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"),
+            new CipherSuite(0xc028, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"),
+            new CipherSuite(0xc029, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256"),
+            new CipherSuite(0xc02a, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384"),
+            new CipherSuite(0xc02b, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"),
+            new CipherSuite(0xc02c, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"),
+            new CipherSuite(0xc02d, "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256"),
+            new CipherSuite(0xc02e, "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384"),
+            new CipherSuite(0xc02f, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"),
+            new CipherSuite(0xc030, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"),
+            new CipherSuite(0xc031, "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256"),
+            new CipherSuite(0xc032, "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384"),
+            new CipherSuite(0xc033, "TLS_ECDHE_PSK_WITH_RC4_128_SHA"),
+            new CipherSuite(0xc034, "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA"),
+            new CipherSuite(0xc035, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA"),
+            new CipherSuite(0xc036, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA"),
+            new CipherSuite(0xc037, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256"),
+            new CipherSuite(0xc038, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384"),
+            new CipherSuite(0xc039, "TLS_ECDHE_PSK_WITH_NULL_SHA"),
+            new CipherSuite(0xc03a, "TLS_ECDHE_PSK_WITH_NULL_SHA256"),
+            new CipherSuite(0xc03b, "TLS_ECDHE_PSK_WITH_NULL_SHA384"),
+            new CipherSuite(0xc03c, "TLS_RSA_WITH_ARIA_128_CBC_SHA256"),
+            new CipherSuite(0xc03d, "TLS_RSA_WITH_ARIA_256_CBC_SHA384"),
+            new CipherSuite(0xc03e, "TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256"),
+            new CipherSuite(0xc03f, "TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384"),
+            new CipherSuite(0xc040, "TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256"),
+            new CipherSuite(0xc041, "TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384"),
+            new CipherSuite(0xc042, "TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256"),
+            new CipherSuite(0xc043, "TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384"),
+            new CipherSuite(0xc044, "TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256"),
+            new CipherSuite(0xc045, "TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384"),
+            new CipherSuite(0xc046, "TLS_DH_anon_WITH_ARIA_128_CBC_SHA256"),
+            new CipherSuite(0xc047, "TLS_DH_anon_WITH_ARIA_256_CBC_SHA384"),
+            new CipherSuite(0xc048, "TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256"),
+            new CipherSuite(0xc049, "TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384"),
+            new CipherSuite(0xc04a, "TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256"),
+            new CipherSuite(0xc04b, "TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384"),
+            new CipherSuite(0xc04c, "TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256"),
+            new CipherSuite(0xc04d, "TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384"),
+            new CipherSuite(0xc04e, "TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256"),
+            new CipherSuite(0xc04f, "TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384"),
+            new CipherSuite(0xc050, "TLS_RSA_WITH_ARIA_128_GCM_SHA256"),
+            new CipherSuite(0xc051, "TLS_RSA_WITH_ARIA_256_GCM_SHA384"),
+            new CipherSuite(0xc052, "TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256"),
+            new CipherSuite(0xc053, "TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384"),
+            new CipherSuite(0xc054, "TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256"),
+            new CipherSuite(0xc055, "TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384"),
+            new CipherSuite(0xc056, "TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256"),
+            new CipherSuite(0xc057, "TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384"),
+            new CipherSuite(0xc058, "TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256"),
+            new CipherSuite(0xc059, "TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384"),
+            new CipherSuite(0xc05a, "TLS_DH_anon_WITH_ARIA_128_GCM_SHA256"),
+            new CipherSuite(0xc05b, "TLS_DH_anon_WITH_ARIA_256_GCM_SHA384"),
+            new CipherSuite(0xc05c, "TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256"),
+            new CipherSuite(0xc05d, "TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384"),
+            new CipherSuite(0xc05e, "TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256"),
+            new CipherSuite(0xc05f, "TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384"),
+            new CipherSuite(0xc060, "TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256"),
+            new CipherSuite(0xc061, "TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384"),
+            new CipherSuite(0xc062, "TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256"),
+            new CipherSuite(0xc063, "TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384"),
+            new CipherSuite(0xc064, "TLS_PSK_WITH_ARIA_128_CBC_SHA256"),
+            new CipherSuite(0xc065, "TLS_PSK_WITH_ARIA_256_CBC_SHA384"),
+            new CipherSuite(0xc066, "TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256"),
+            new CipherSuite(0xc067, "TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384"),
+            new CipherSuite(0xc068, "TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256"),
+            new CipherSuite(0xc069, "TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384"),
+            new CipherSuite(0xc06a, "TLS_PSK_WITH_ARIA_128_GCM_SHA256"),
+            new CipherSuite(0xc06b, "TLS_PSK_WITH_ARIA_256_GCM_SHA384"),
+            new CipherSuite(0xc06c, "TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256"),
+            new CipherSuite(0xc06d, "TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384"),
+            new CipherSuite(0xc06e, "TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256"),
+            new CipherSuite(0xc06f, "TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384"),
+            new CipherSuite(0xc070, "TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256"),
+            new CipherSuite(0xc071, "TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384"),
+            new CipherSuite(0xc072, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256"),
+            new CipherSuite(0xc073, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384"),
+            new CipherSuite(0xc074, "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256"),
+            new CipherSuite(0xc075, "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384"),
+            new CipherSuite(0xc076, "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256"),
+            new CipherSuite(0xc077, "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384"),
+            new CipherSuite(0xc078, "TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256"),
+            new CipherSuite(0xc079, "TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384"),
+            new CipherSuite(0xc07a, "TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256"),
+            new CipherSuite(0xc07b, "TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384"),
+            new CipherSuite(0xc07c, "TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256"),
+            new CipherSuite(0xc07d, "TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384"),
+            new CipherSuite(0xc07e, "TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256"),
+            new CipherSuite(0xc07f, "TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384"),
+            new CipherSuite(0xc080, "TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256"),
+            new CipherSuite(0xc081, "TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384"),
+            new CipherSuite(0xc082, "TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256"),
+            new CipherSuite(0xc083, "TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384"),
+            new CipherSuite(0xc084, "TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256"),
+            new CipherSuite(0xc085, "TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384"),
+            new CipherSuite(0xc086, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256"),
+            new CipherSuite(0xc087, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384"),
+            new CipherSuite(0xc088, "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256"),
+            new CipherSuite(0xc089, "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384"),
+            new CipherSuite(0xc08a, "TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256"),
+            new CipherSuite(0xc08b, "TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384"),
+            new CipherSuite(0xc08c, "TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256"),
+            new CipherSuite(0xc08d, "TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384"),
+            new CipherSuite(0xc08e, "TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256"),
+            new CipherSuite(0xc08f, "TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384"),
+            new CipherSuite(0xc090, "TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256"),
+            new CipherSuite(0xc091, "TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384"),
+            new CipherSuite(0xc092, "TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256"),
+            new CipherSuite(0xc093, "TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384"),
+            new CipherSuite(0xc094, "TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256"),
+            new CipherSuite(0xc095, "TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384"),
+            new CipherSuite(0xc096, "TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256"),
+            new CipherSuite(0xc097, "TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384"),
+            new CipherSuite(0xc098, "TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256"),
+            new CipherSuite(0xc099, "TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384"),
+            new CipherSuite(0xc09a, "TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256"),
+            new CipherSuite(0xc09b, "TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384"),
+            new CipherSuite(0xc09c, "TLS_RSA_WITH_AES_128_CCM"),
+            new CipherSuite(0xc09d, "TLS_RSA_WITH_AES_256_CCM"),
+            new CipherSuite(0xc09e, "TLS_DHE_RSA_WITH_AES_128_CCM"),
+            new CipherSuite(0xc09f, "TLS_DHE_RSA_WITH_AES_256_CCM"),
+            new CipherSuite(0xc0a0, "TLS_RSA_WITH_AES_128_CCM_8"),
+            new CipherSuite(0xc0a1, "TLS_RSA_WITH_AES_256_CCM_8"),
+            new CipherSuite(0xc0a2, "TLS_DHE_RSA_WITH_AES_128_CCM_8"),
+            new CipherSuite(0xc0a3, "TLS_DHE_RSA_WITH_AES_256_CCM_8"),
+            new CipherSuite(0xc0a4, "TLS_PSK_WITH_AES_128_CCM"),
+            new CipherSuite(0xc0a5, "TLS_PSK_WITH_AES_256_CCM"),
+            new CipherSuite(0xc0a6, "TLS_DHE_PSK_WITH_AES_128_CCM"),
+            new CipherSuite(0xc0a7, "TLS_DHE_PSK_WITH_AES_256_CCM"),
+            new CipherSuite(0xc0a8, "TLS_PSK_WITH_AES_128_CCM_8"),
+            new CipherSuite(0xc0a9, "TLS_PSK_WITH_AES_256_CCM_8"),
+            new CipherSuite(0xc0aa, "TLS_PSK_DHE_WITH_AES_128_CCM_8"),
+            new CipherSuite(0xc0ab, "TLS_PSK_DHE_WITH_AES_256_CCM_8"),
+            new CipherSuite(0xc0ac, "TLS_ECDHE_ECDSA_WITH_AES_128_CCM"),
+            new CipherSuite(0xc0ad, "TLS_ECDHE_ECDSA_WITH_AES_256_CCM"),
+            new CipherSuite(0xc0ae, "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8"),
+            new CipherSuite(0xc0af, "TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8"),
+            new CipherSuite(0xcc13, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_OLD"),
+            new CipherSuite(0xcc14, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_OLD"),
+            new CipherSuite(0xcc15, "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_OLD"),
+            new CipherSuite(0xcca8, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"),
+            new CipherSuite(0xcca9, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"),
+            new CipherSuite(0xccaa, "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256"),
+            new CipherSuite(0xccab, "TLS_PSK_WITH_CHACHA20_POLY1305_SHA256"),
+            new CipherSuite(0xccac, "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256"),
+            new CipherSuite(0xccad, "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256"),
+            new CipherSuite(0xccae, "TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256"),
+    };
+    private static final Map<Integer, CipherSuite> CODE_TO_CIPHER_SUITE;
+    private static final Map<String, CipherSuite> NAME_TO_CIPHER_SUITE;
+    static {
+        Map<Integer, CipherSuite> byCode = new HashMap<Integer, CipherSuite>();
+        Map<String, CipherSuite> byName = new HashMap<String, CipherSuite>();
+        for (CipherSuite cipherSuite : CIPHER_SUITES) {
+            if (byCode.put(cipherSuite.code, cipherSuite) != null) {
+                throw new RuntimeException(
+                        "Cipher suite multiply defined: " + Integer.toHexString(cipherSuite.code));
+            }
+            String name = cipherSuite.name;
+            if (byName.put(name, cipherSuite) != null) {
+                throw new RuntimeException(
+                        "Cipher suite multiply defined: " + cipherSuite.name);
+            }
+            String androidName = cipherSuite.getAndroidName();
+            if (!name.equals(androidName)) {
+                if (byName.put(androidName, cipherSuite) != null) {
+                    throw new RuntimeException(
+                            "Cipher suite multiply defined: " + cipherSuite.androidName);
+                }
+            }
+        }
+        CODE_TO_CIPHER_SUITE = byCode;
+        NAME_TO_CIPHER_SUITE = byName;
+    }
+    public final int code;
+    public final String name;
+    private final String androidName;
+    private CipherSuite(int code, String name) {
+        this.code = code;
+        this.name = name;
+        this.androidName = null;
+    }
+    private CipherSuite(int code, String name, String androidName) {
+        this.code = code;
+        this.name = name;
+        this.androidName = androidName;
+    }
+    public static CipherSuite valueOf(String name) {
+        CipherSuite result = NAME_TO_CIPHER_SUITE.get(name);
+        if (result != null) {
+            return result;
+        }
+        throw new IllegalArgumentException("Unknown cipher suite: " + name);
+    }
+    public static CipherSuite valueOf(int code) {
+        CipherSuite result = CODE_TO_CIPHER_SUITE.get(code);
+        if (result != null) {
+            return result;
+        }
+        return new CipherSuite(code, Integer.toHexString(code));
+    }
+    public String getAndroidName() {
+        return (androidName != null) ? androidName : name;
+    }
+    @Override
+    public String toString() {
+        return name;
+    }
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + code;
+        return result;
+    }
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        CipherSuite other = (CipherSuite) obj;
+        if (code != other.code) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/ClientHello.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/ClientHello.java
new file mode 100644
index 0000000..55d2575
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/ClientHello.java
@@ -0,0 +1,102 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.tlswire.handshake;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import com.android.org.conscrypt.tlswire.util.IoUtils;
+import com.android.org.conscrypt.tlswire.util.TlsProtocolVersion;
+
+/**
+ * {@link ClientHello} {@link HandshakeMessage} from TLS 1.2 RFC 5246.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ClientHello extends HandshakeMessage {
+    public TlsProtocolVersion clientVersion;
+    public byte[] random;
+    public byte[] sessionId;
+    public List<CipherSuite> cipherSuites;
+    public List<CompressionMethod> compressionMethods;
+    /** Extensions or {@code null} for no extensions. */
+    public List<HelloExtension> extensions;
+    @Override
+    protected void parseBody(DataInput in) throws IOException {
+        clientVersion = TlsProtocolVersion.read(in);
+        random = new byte[32];
+        in.readFully(random);
+        sessionId = IoUtils.readTlsVariableLengthByteVector(in, 32);
+        int[] cipherSuiteCodes = IoUtils.readTlsVariableLengthUnsignedShortVector(in, 0xfffe);
+        cipherSuites = new ArrayList<CipherSuite>(cipherSuiteCodes.length);
+        for (int i = 0; i < cipherSuiteCodes.length; i++) {
+            cipherSuites.add(CipherSuite.valueOf(cipherSuiteCodes[i]));
+        }
+        byte[] compressionMethodCodes = IoUtils.readTlsVariableLengthByteVector(in, 0xff);
+        compressionMethods = new ArrayList<CompressionMethod>(compressionMethodCodes.length);
+        for (int i = 0; i < compressionMethodCodes.length; i++) {
+            int code = compressionMethodCodes[i] & 0xff;
+            compressionMethods.add(CompressionMethod.valueOf(code));
+        }
+        int extensionsSectionSize;
+        try {
+            extensionsSectionSize = in.readUnsignedShort();
+        } catch (EOFException e) {
+            // No extensions present
+            extensionsSectionSize = 0;
+        }
+        if (extensionsSectionSize > 0) {
+            extensions = new ArrayList<HelloExtension>();
+            byte[] extensionsBytes = new byte[extensionsSectionSize];
+            in.readFully(extensionsBytes);
+            ByteArrayInputStream extensionsIn = new ByteArrayInputStream(extensionsBytes);
+            DataInput extensionsDataIn = new DataInputStream(extensionsIn);
+            while (extensionsIn.available() > 0) {
+                try {
+                    extensions.add(HelloExtension.read(extensionsDataIn));
+                } catch (IOException e) {
+                    throw new IOException(
+                            "Failed to read HelloExtension #" + (extensions.size() + 1));
+                }
+            }
+        }
+    }
+    public HelloExtension findExtensionByType(int extensionType) {
+        if (extensions == null) {
+            return null;
+        }
+        for (HelloExtension extension : extensions) {
+            if (extension.type == extensionType) {
+                return extension;
+            }
+        }
+        return null;
+    }
+    @Override
+    public String toString() {
+        return "ClientHello{client version: " + clientVersion + ", random: "
+                + new BigInteger(1, random).toString(16) + ", sessionId: "
+                + new BigInteger(1, sessionId).toString(16) + ", cipher suites: " + cipherSuites
+                + ", compression methods: " + compressionMethods
+                + ((extensions != null) ? (", extensions: " + String.valueOf(extensions)) : "")
+                + "}";
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/CompressionMethod.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/CompressionMethod.java
new file mode 100644
index 0000000..88069d2
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/CompressionMethod.java
@@ -0,0 +1,69 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.tlswire.handshake;
+/**
+ * {@code CompressionMethod} enum from TLS 1.2 RFC 5246.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class CompressionMethod {
+    public static final CompressionMethod NULL = new CompressionMethod(0, "null");
+    public static final CompressionMethod DEFLATE = new CompressionMethod(1, "deflate");
+    public final int type;
+    public final String name;
+    private CompressionMethod(int type, String name) {
+        this.type = type;
+        this.name = name;
+    }
+    public static CompressionMethod valueOf(int type) {
+        switch (type) {
+            case 0:
+                return NULL;
+            case 1:
+                return DEFLATE;
+            default:
+                return new CompressionMethod(type, String.valueOf(type));
+        }
+    }
+    @Override
+    public String toString() {
+        return name;
+    }
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + type;
+        return result;
+    }
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        CompressionMethod other = (CompressionMethod) obj;
+        if (type != other.type) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/EllipticCurve.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/EllipticCurve.java
new file mode 100644
index 0000000..01fc90e
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/EllipticCurve.java
@@ -0,0 +1,79 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.tlswire.handshake;
+/**
+ * {@code EllipticCurve} enum from RFC 4492 section 5.1.1. Curves are assigned
+ * via the
+ * <a href="https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8">IANA registry</a>.
+ * @hide This class is not part of the Android public SDK API
+ */
+public enum EllipticCurve {
+    SECT163K1(1, "sect163k1"),
+    SECT163R1(2, "sect163r1"),
+    SECT163R2(3, "sect163r2"),
+    SECT193R1(4, "sect193r1"),
+    SECT193R2(5, "sect193r2"),
+    SECT233K1(6, "sect233k1"),
+    SECT233R1(7, "sect233r1"),
+    SECT239K1(8, "sect239k1"),
+    SECT283K1(9, "sect283k1"),
+    SECT283R1(10, "sect283r1"),
+    SECT409K1(11, "sect409k1"),
+    SECT409R1(12, "sect409r1"),
+    SECT571K1(13, "sect571k1"),
+    SECT571R1(14, "sect571r1"),
+    SECP160K1(15, "secp160k1"),
+    SECP160R1(16, "secp160r1"),
+    SECP160R2(17, "secp160r2"),
+    SECP192K1(18, "secp192k1"),
+    SECP192R1(19, "secp192r1"),
+    SECP224K1(20, "secp224k1"),
+    SECP224R1(21, "secp224r1"),
+    SECP256K1(22, "secp256k1"),
+    SECP256R1(23, "secp256r1"),
+    SECP384R1(24, "secp384r1"),
+    SECP521R1(25, "secp521r1"),
+    BRAINPOOLP256R1(26, "brainpoolP256r1"),
+    BRAINPOOLP384R1(27, "brainpoolP384r1"),
+    BRAINPOOLP521R1(28, "brainpoolP521r1"),
+    X25519(29, "x25519"),
+    X448(30, "x448"),
+    ARBITRARY_PRIME(0xFF01, "arbitrary_explicit_prime_curves"),
+    ARBITRARY_CHAR2(0xFF02, "arbitrary_explicit_char2_curves");
+    public final int identifier;
+    public final String name;
+    private EllipticCurve(int identifier, String name) {
+        this.identifier = identifier;
+        this.name = name;
+    }
+    public static EllipticCurve fromIdentifier(int identifier) {
+        for (EllipticCurve curve : values()) {
+            if (curve.identifier == identifier) {
+                return curve;
+            }
+        }
+        throw new AssertionError("Unknown curve identifier " + identifier);
+    }
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(name);
+        sb.append(" (");
+        sb.append(identifier);
+        sb.append(')');
+        return sb.toString();
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/EllipticCurvesHelloExtension.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/EllipticCurvesHelloExtension.java
new file mode 100644
index 0000000..56dc55d
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/EllipticCurvesHelloExtension.java
@@ -0,0 +1,56 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.tlswire.handshake;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import com.android.org.conscrypt.tlswire.util.IoUtils;
+
+/**
+ * {@code elliptic_curves} {@link HelloExtension} from RFC 4492 section 5.1.1.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class EllipticCurvesHelloExtension extends HelloExtension {
+    public List<EllipticCurve> supported;
+    public boolean wellFormed;
+    @Override
+    protected void parseData() throws IOException {
+        byte[] ellipticCurvesListBytes = IoUtils.readTlsVariableLengthByteVector(
+                new DataInputStream(new ByteArrayInputStream(data)), 0xffff);
+        ByteArrayInputStream ellipticCurvesListIn =
+                new ByteArrayInputStream(ellipticCurvesListBytes);
+        DataInputStream in = new DataInputStream(ellipticCurvesListIn);
+        wellFormed = (ellipticCurvesListIn.available() % 2) == 0;
+        supported = new ArrayList<EllipticCurve>(ellipticCurvesListIn.available() / 2);
+        while (ellipticCurvesListIn.available() >= 2) {
+            int curve_id = in.readUnsignedShort();
+            supported.add(EllipticCurve.fromIdentifier(curve_id));
+        }
+    }
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("HelloExtension{type: elliptic_curves, wellFormed: ");
+        sb.append(wellFormed);
+        sb.append(", supported: ");
+        sb.append(supported);
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/HandshakeMessage.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/HandshakeMessage.java
new file mode 100644
index 0000000..4e04f2f
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/HandshakeMessage.java
@@ -0,0 +1,60 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.tlswire.handshake;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.IOException;
+import com.android.org.conscrypt.tlswire.util.IoUtils;
+
+/**
+ * Handshake Protocol message from TLS 1.2 RFC 5246.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class HandshakeMessage {
+    public static final int TYPE_CLIENT_HELLO = 1;
+    public int type;
+    public byte[] body;
+    /**
+     * Parses the provided TLS record as a handshake message.
+     */
+    public static HandshakeMessage read(DataInput in) throws IOException {
+        int type = in.readUnsignedByte();
+        HandshakeMessage result;
+        switch (type) {
+            case TYPE_CLIENT_HELLO:
+                result = new ClientHello();
+                break;
+            default:
+                result = new HandshakeMessage();
+                break;
+        }
+        result.type = type;
+        int bodyLength = IoUtils.readUnsignedInt24(in);
+        result.body = new byte[bodyLength];
+        in.readFully(result.body);
+        result.parseBody(new DataInputStream(new ByteArrayInputStream(result.body)));
+        return result;
+    }
+    /**
+     * Parses the provided body. The default implementation does nothing.
+     *
+     * @throws IOException if an I/O error occurs.
+     */
+    protected void parseBody(@SuppressWarnings("unused") DataInput in) throws IOException {}
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/HelloExtension.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/HelloExtension.java
new file mode 100644
index 0000000..298bd52
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/HelloExtension.java
@@ -0,0 +1,105 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.tlswire.handshake;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.Map;
+import com.android.org.conscrypt.tlswire.util.IoUtils;
+
+/**
+ * {@code HelloExtension} struct from TLS 1.2 RFC 5246.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class HelloExtension {
+    public static final int TYPE_SERVER_NAME = 0;
+    public static final int TYPE_ELLIPTIC_CURVES = 10;
+    public static final int TYPE_APPLICATION_LAYER_PROTOCOL_NEGOTIATION = 16;
+    public static final int TYPE_PADDING = 21;
+    public static final int TYPE_SESSION_TICKET = 35;
+    public static final int TYPE_RENEGOTIATION_INFO = 65281;
+    private static final Map<Integer, String> TYPE_TO_NAME = new HashMap<Integer, String>();
+    static {
+        TYPE_TO_NAME.put(TYPE_SERVER_NAME, "server_name");
+        TYPE_TO_NAME.put(1, "max_fragment_length");
+        TYPE_TO_NAME.put(2, "client_certificate_url");
+        TYPE_TO_NAME.put(3, "trusted_ca_keys");
+        TYPE_TO_NAME.put(4, "truncated_hmac");
+        TYPE_TO_NAME.put(5, "status_request");
+        TYPE_TO_NAME.put(6, "user_mapping");
+        TYPE_TO_NAME.put(7, "client_authz");
+        TYPE_TO_NAME.put(8, "server_authz");
+        TYPE_TO_NAME.put(9, "cert_type");
+        TYPE_TO_NAME.put(TYPE_ELLIPTIC_CURVES, "elliptic_curves");
+        TYPE_TO_NAME.put(11, "ec_point_formats");
+        TYPE_TO_NAME.put(12, "srp");
+        TYPE_TO_NAME.put(13, "signature_algorithms");
+        TYPE_TO_NAME.put(14, "use_srtp");
+        TYPE_TO_NAME.put(15, "heartbeat");
+        TYPE_TO_NAME.put(TYPE_APPLICATION_LAYER_PROTOCOL_NEGOTIATION, "application_layer_protocol_negotiation");
+        TYPE_TO_NAME.put(17, "status_request_v2");
+        TYPE_TO_NAME.put(18, "signed_certificate_timestamp");
+        TYPE_TO_NAME.put(19, "client_certificate_type");
+        TYPE_TO_NAME.put(20, "server_certificate_type");
+        TYPE_TO_NAME.put(TYPE_PADDING, "padding");
+        TYPE_TO_NAME.put(TYPE_SESSION_TICKET, "SessionTicket");
+        TYPE_TO_NAME.put(13172, "next_protocol_negotiation");
+        TYPE_TO_NAME.put(30031, "Channel ID (old)");
+        TYPE_TO_NAME.put(30032, "Channel ID (new)");
+        TYPE_TO_NAME.put(TYPE_RENEGOTIATION_INFO, "renegotiation_info");
+    }
+    public int type;
+    public String name;
+    public byte[] data;
+    public static HelloExtension read(DataInput in) throws IOException {
+        int type = in.readUnsignedShort();
+        HelloExtension result;
+        switch (type) {
+            case TYPE_SERVER_NAME:
+                result = new ServerNameHelloExtension();
+                break;
+            case TYPE_ELLIPTIC_CURVES:
+                result = new EllipticCurvesHelloExtension();
+                break;
+            case TYPE_APPLICATION_LAYER_PROTOCOL_NEGOTIATION:
+                result = new AlpnHelloExtension();
+                break;
+            default:
+                result = new HelloExtension();
+                break;
+        }
+        result.type = type;
+        result.name = TYPE_TO_NAME.get(result.type);
+        if (result.name == null) {
+            result.name = String.valueOf(result.type);
+        }
+        result.data = IoUtils.readTlsVariableLengthByteVector(in, 0xffff);
+        result.parseData();
+        return result;
+    }
+    /**
+     * @throws IOException
+     */
+    protected void parseData() throws IOException {}
+    @Override
+    public String toString() {
+        return "HelloExtension{type: " + name + ", data: " + new BigInteger(1, data).toString(16)
+                + "}";
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/ServerNameHelloExtension.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/ServerNameHelloExtension.java
new file mode 100644
index 0000000..27262db
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/handshake/ServerNameHelloExtension.java
@@ -0,0 +1,54 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.tlswire.handshake;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import com.android.org.conscrypt.tlswire.util.IoUtils;
+
+/**
+ * {@code server_name} (SNI) {@link HelloExtension} from TLS 1.2 RFC 5246.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class ServerNameHelloExtension extends HelloExtension {
+    private static final int TYPE_HOST_NAME = 0;
+    public List<String> hostnames;
+    @Override
+    protected void parseData() throws IOException {
+        byte[] serverNameListBytes = IoUtils.readTlsVariableLengthByteVector(
+                new DataInputStream(new ByteArrayInputStream(data)), 0xffff);
+        ByteArrayInputStream serverNameListIn = new ByteArrayInputStream(serverNameListBytes);
+        DataInputStream in = new DataInputStream(serverNameListIn);
+        hostnames = new ArrayList<String>();
+        while (serverNameListIn.available() > 0) {
+            int type = in.readUnsignedByte();
+            if (type != TYPE_HOST_NAME) {
+                throw new IOException("Unsupported ServerName type: " + type);
+            }
+            byte[] hostnameBytes = IoUtils.readTlsVariableLengthByteVector(in, 0xffff);
+            String hostname = new String(hostnameBytes, "US-ASCII");
+            hostnames.add(hostname);
+        }
+    }
+    @Override
+    public String toString() {
+        return "HelloExtension{type: server_name, hostnames: " + hostnames + "}";
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/record/TlsProtocols.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/record/TlsProtocols.java
new file mode 100644
index 0000000..a96afd1
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/record/TlsProtocols.java
@@ -0,0 +1,29 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.tlswire.record;
+/**
+ * Protocols that can run over the TLS Record Protocol from TLS 1.2 RFC 5246.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class TlsProtocols {
+    public static final int CHANGE_CIPHER_SPEC = 20;
+    public static final int ALERT = 21;
+    public static final int HANDSHAKE = 22;
+    public static final int APPLICATION_DATA = 23;
+    public static final int HEARTBEAT = 24;
+    private TlsProtocols() {}
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/record/TlsRecord.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/record/TlsRecord.java
new file mode 100644
index 0000000..c22bb9b
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/record/TlsRecord.java
@@ -0,0 +1,40 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.tlswire.record;
+
+import java.io.DataInput;
+import java.io.IOException;
+import com.android.org.conscrypt.tlswire.util.TlsProtocolVersion;
+
+/**
+ * TLS Record Protocol record from TLS 1.2 RFC 5246.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class TlsRecord {
+    public int type;
+    public TlsProtocolVersion version;
+    public byte[] fragment;
+    public static TlsRecord read(DataInput in) throws IOException {
+        TlsRecord result = new TlsRecord();
+        result.type = in.readUnsignedByte();
+        result.version = TlsProtocolVersion.read(in);
+        int fragmentLength = in.readUnsignedShort();
+        result.fragment = new byte[fragmentLength];
+        in.readFully(result.fragment);
+        return result;
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/util/IoUtils.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/util/IoUtils.java
new file mode 100644
index 0000000..ecbefab
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/util/IoUtils.java
@@ -0,0 +1,56 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.tlswire.util;
+import java.io.DataInput;
+import java.io.IOException;
+/**
+ * @hide This class is not part of the Android public SDK API
+ */
+public class IoUtils {
+    public static int readUnsignedInt24(DataInput in) throws IOException {
+        return (in.readUnsignedByte() << 16) | in.readUnsignedShort();
+    }
+    public static byte[] readTlsVariableLengthByteVector(DataInput in, int maxSizeBytes)
+            throws IOException {
+        int sizeBytes = readTlsVariableLengthVectorSizeBytes(in, maxSizeBytes);
+        byte[] result = new byte[sizeBytes];
+        in.readFully(result);
+        return result;
+    }
+    public static int[] readTlsVariableLengthUnsignedShortVector(DataInput in, int maxSizeBytes)
+            throws IOException {
+        int sizeBytes = readTlsVariableLengthVectorSizeBytes(in, maxSizeBytes);
+        int elementCount = sizeBytes / 2;
+        int[] result = new int[elementCount];
+        for (int i = 0; i < elementCount; i++) {
+            result[i] = in.readUnsignedShort();
+        }
+        return result;
+    }
+    private static int readTlsVariableLengthVectorSizeBytes(DataInput in, int maxSizeBytes)
+            throws IOException {
+        if (maxSizeBytes < 0x100) {
+            return in.readUnsignedByte();
+        } else if (maxSizeBytes < 0x10000) {
+            return in.readUnsignedShort();
+        } else if (maxSizeBytes < 0x1000000) {
+            return readUnsignedInt24(in);
+        } else {
+            return in.readInt();
+        }
+    }
+}
diff --git a/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/util/TlsProtocolVersion.java b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/util/TlsProtocolVersion.java
new file mode 100644
index 0000000..0aa4446
--- /dev/null
+++ b/repackaged/testing/src/main/java/com/android/org/conscrypt/tlswire/util/TlsProtocolVersion.java
@@ -0,0 +1,92 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.org.conscrypt.tlswire.util;
+import java.io.DataInput;
+import java.io.IOException;
+/**
+ * {@code ProtovolVersion} struct from TLS 1.2 RFC 5246.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class TlsProtocolVersion {
+    public static final TlsProtocolVersion SSLV3 = new TlsProtocolVersion(3, 0, "SSLv3");
+    public static final TlsProtocolVersion TLSv1_0 = new TlsProtocolVersion(3, 1, "TLSv1.0");
+    public static final TlsProtocolVersion TLSv1_1 = new TlsProtocolVersion(3, 2, "TLSv1.1");
+    public static final TlsProtocolVersion TLSv1_2 = new TlsProtocolVersion(3, 3, "TLSv1.2");
+    public static final TlsProtocolVersion TLSv1_3 = new TlsProtocolVersion(3, 4, "TLSv1.3");
+    public final int major;
+    public final int minor;
+    public final String name;
+    private TlsProtocolVersion(int major, int minor, String name) {
+        this.major = major;
+        this.minor = minor;
+        this.name = name;
+    }
+    public static TlsProtocolVersion valueOf(int major, int minor) {
+        if (major == 3) {
+            switch (minor) {
+                case 0:
+                    return SSLV3;
+                case 1:
+                    return TLSv1_0;
+                case 2:
+                    return TLSv1_1;
+                case 3:
+                    return TLSv1_2;
+                case 4:
+                    return TLSv1_3;
+            }
+        }
+        return new TlsProtocolVersion(major, minor, major + "." + minor);
+    }
+    public static TlsProtocolVersion read(DataInput in) throws IOException {
+        int major = in.readUnsignedByte();
+        int minor = in.readUnsignedByte();
+        return TlsProtocolVersion.valueOf(major, minor);
+    }
+    @Override
+    public String toString() {
+        return name;
+    }
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + major;
+        result = prime * result + minor;
+        return result;
+    }
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        TlsProtocolVersion other = (TlsProtocolVersion) obj;
+        if (major != other.major) {
+            return false;
+        }
+        if (minor != other.minor) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/repackaged/testing/src/main/java/tests/net/DelegatingSSLSocketFactory.java b/repackaged/testing/src/main/java/tests/net/DelegatingSSLSocketFactory.java
new file mode 100644
index 0000000..f93df76
--- /dev/null
+++ b/repackaged/testing/src/main/java/tests/net/DelegatingSSLSocketFactory.java
@@ -0,0 +1,83 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package tests.net;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+/**
+ * {@link SSLSocketFactory} which delegates all invocations to the provided delegate
+ * {@code SSLSocketFactory}.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DelegatingSSLSocketFactory extends SSLSocketFactory {
+    private final SSLSocketFactory mDelegate;
+    public DelegatingSSLSocketFactory(SSLSocketFactory delegate) {
+        this.mDelegate = delegate;
+    }
+    /**
+     * Invoked after obtaining a socket from the delegate and before returning it to the caller.
+     *
+     * <p>The default implementation does nothing.
+     */
+    protected SSLSocket configureSocket(SSLSocket socket) throws IOException {
+        return socket;
+    }
+    @Override
+    public String[] getDefaultCipherSuites() {
+        return mDelegate.getDefaultCipherSuites();
+    }
+    @Override
+    public String[] getSupportedCipherSuites() {
+        return mDelegate.getSupportedCipherSuites();
+    }
+    @Override
+    public Socket createSocket() throws IOException {
+        SSLSocket socket = (SSLSocket) mDelegate.createSocket();
+        return configureSocket(socket);
+    }
+    @Override
+    public Socket createSocket(Socket s, String host, int port, boolean autoClose)
+            throws IOException {
+        SSLSocket socket = (SSLSocket) mDelegate.createSocket(s, host, port, autoClose);
+        return configureSocket(socket);
+    }
+    @Override
+    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
+        SSLSocket socket = (SSLSocket) mDelegate.createSocket(host, port);
+        return configureSocket(socket);
+    }
+    @Override
+    public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
+            throws IOException, UnknownHostException {
+        SSLSocket socket = (SSLSocket) mDelegate.createSocket(host, port, localHost, localPort);
+        return configureSocket(socket);
+    }
+    @Override
+    public Socket createSocket(InetAddress host, int port) throws IOException {
+        SSLSocket socket = (SSLSocket) mDelegate.createSocket(host, port);
+        return configureSocket(socket);
+    }
+    @Override
+    public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
+            int localPort) throws IOException {
+        SSLSocket socket = (SSLSocket) mDelegate.createSocket(address, port, localAddress, localPort);
+        return configureSocket(socket);
+    }
+}
diff --git a/repackaged/testing/src/main/java/tests/net/DelegatingSocketFactory.java b/repackaged/testing/src/main/java/tests/net/DelegatingSocketFactory.java
new file mode 100644
index 0000000..c412ed4
--- /dev/null
+++ b/repackaged/testing/src/main/java/tests/net/DelegatingSocketFactory.java
@@ -0,0 +1,68 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package tests.net;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import javax.net.SocketFactory;
+/**
+ * {@link SocketFactory} which delegates all invocations to the provided delegate
+ * {@code SocketFactory}.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class DelegatingSocketFactory extends SocketFactory {
+    private final SocketFactory mDelegate;
+    public DelegatingSocketFactory(SocketFactory delegate) {
+        this.mDelegate = delegate;
+    }
+    /**
+     * Invoked after obtaining a socket from the delegate and before returning it to the caller.
+     *
+     * <p>The default implementation does nothing.
+     */
+    protected Socket configureSocket(Socket socket) throws IOException {
+        return socket;
+    }
+    @Override
+    public Socket createSocket() throws IOException {
+        Socket socket = mDelegate.createSocket();
+        return configureSocket(socket);
+    }
+    @Override
+    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
+        Socket socket = mDelegate.createSocket(host, port);
+        return configureSocket(socket);
+    }
+    @Override
+    public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
+            throws IOException, UnknownHostException {
+        Socket socket = mDelegate.createSocket(host, port, localHost, localPort);
+        return configureSocket(socket);
+    }
+    @Override
+    public Socket createSocket(InetAddress host, int port) throws IOException {
+        Socket socket = mDelegate.createSocket(host, port);
+        return configureSocket(socket);
+    }
+    @Override
+    public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
+            int localPort) throws IOException {
+        Socket socket = mDelegate.createSocket(address, port, localAddress, localPort);
+        return configureSocket(socket);
+    }
+}
diff --git a/repackaged/testing/src/main/java/tests/util/ForEachRunner.java b/repackaged/testing/src/main/java/tests/util/ForEachRunner.java
new file mode 100644
index 0000000..614716d
--- /dev/null
+++ b/repackaged/testing/src/main/java/tests/util/ForEachRunner.java
@@ -0,0 +1,52 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package tests.util;
+/**
+ * Runner which executes the provided code under test (via a callback) for each provided input
+ * value.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class ForEachRunner {
+    /**
+     * Callback parameterized with a value.
+     * @hide This class is not part of the Android public SDK API
+     */
+    public interface Callback<T> {
+        /**
+         * Invokes the callback for the provided value.
+         */
+        void run(T value) throws Exception;
+    }
+    private ForEachRunner() {}
+    /**
+     * Invokes the provided callback for each of the provided named values.
+     *
+     * @param namesAndValues named values represented as name-value pairs.
+     *
+     * @param <T> type of value.
+     */
+    public static <T> void runNamed(Callback<T> callback, Iterable<Pair<String, T>> namesAndValues)
+            throws Exception {
+        for (Pair<String, T> nameAndValue : namesAndValues) {
+            try {
+                callback.run(nameAndValue.getSecond());
+            } catch (Throwable e) {
+                throw new Exception("Failed for " + nameAndValue.getFirst() + ": " + e.getMessage(), e);
+            }
+        }
+    }
+}
diff --git a/repackaged/testing/src/main/java/tests/util/Pair.java b/repackaged/testing/src/main/java/tests/util/Pair.java
new file mode 100644
index 0000000..1bf662e
--- /dev/null
+++ b/repackaged/testing/src/main/java/tests/util/Pair.java
@@ -0,0 +1,100 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package tests.util;
+/**
+ * Pair of typed values.
+ *
+ * <p>Pairs are obtained using {@link #of(Object, Object) of}.
+ *
+ * @param <F> type of the first value.
+ * @param <S> type of the second value.
+ * @hide This class is not part of the Android public SDK API
+ */
+public class Pair<F, S> {
+    private final F mFirst;
+    private final S mSecond;
+    private Pair(F first, S second) {
+        mFirst = first;
+        mSecond = second;
+    }
+    /**
+     * Gets the pair consisting of the two provided values.
+     *
+     * @param first first value or {@code null}.
+     * @param second second value or {@code null}.
+     */
+    public static <F, S> Pair<F, S> of(F first, S second) {
+        return new Pair<F, S>(first, second);
+    }
+    /**
+     * Gets the first value from this pair.
+     *
+     * @return value or {@code null}.
+     */
+    public F getFirst() {
+        return mFirst;
+    }
+    /**
+     * Gets the second value from this pair.
+     *
+     * @return value or {@code null}.
+     */
+    public S getSecond() {
+        return mSecond;
+    }
+    @Override
+    public String toString() {
+        return "Pair[" + mFirst + ", " + mSecond + "]";
+    }
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((mFirst == null) ? 0 : mFirst.hashCode());
+        result = prime * result + ((mSecond == null) ? 0 : mSecond.hashCode());
+        return result;
+    }
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        @SuppressWarnings("rawtypes")
+        Pair other = (Pair) obj;
+        if (mFirst == null) {
+            if (other.mFirst != null) {
+                return false;
+            }
+        } else if (!mFirst.equals(other.mFirst)) {
+            return false;
+        }
+        if (mSecond == null) {
+            if (other.mSecond != null) {
+                return false;
+            }
+        } else if (!mSecond.equals(other.mSecond)) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/repackaged/testing/src/main/java/tests/util/ServiceTester.java b/repackaged/testing/src/main/java/tests/util/ServiceTester.java
new file mode 100644
index 0000000..e2e325c
--- /dev/null
+++ b/repackaged/testing/src/main/java/tests/util/ServiceTester.java
@@ -0,0 +1,179 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tests.util;
+
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * A utility for testing all the implementations of a particular service (such as MessageDigest or
+ * KeyGenerator).
+ * <p>
+ * An instance of this class may only be used to run one test.
+ * @hide This class is not part of the Android public SDK API
+ */
+public final class ServiceTester {
+
+  /**
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface Test {
+    /**
+     * Run the test for the given provider and algorithm.  This method should throw an exception
+     * if the test fails or do nothing if it passes.
+     */
+    void test(Provider p, String algorithm) throws Exception;
+  }
+
+  private final String service;
+  private Set<Provider> providers = new LinkedHashSet<>();
+  private Set<Provider> skipProviders = new HashSet<>();
+  private Set<String> algorithms = new LinkedHashSet<>();
+  private Set<String> skipAlgorithms = new HashSet<>();
+
+  private ServiceTester(String service) {
+    this.service = service;
+  }
+
+  /**
+   * Create a new ServiceTester for the given service.
+   */
+  public static ServiceTester test(String service) {
+    if (service.equalsIgnoreCase("Cipher")) {
+      // Cipher is complicated because the parameterized transformations mean that you have
+      // to check for a lot of combinations (eg, a test for AES/CBC/NoPadding might be satisfied by
+      // a provider providing AES, AES/CBC, AES//NoPadding, or AES/CBC/NoPadding).  We don't
+      // really need it, so we haven't implemented it.
+      throw new IllegalArgumentException("ServiceTester doesn't support Cipher");
+    }
+    return new ServiceTester(service);
+  }
+
+  /**
+   * Specifies the list of providers to test.  If this method is called multiple times, the
+   * collections are combined.  If this method is never called, this will test all installed
+   * providers.
+   *
+   * @throws IllegalArgumentException if a named provider is not installed
+   */
+  public ServiceTester withProviders(Collection<String> providers) {
+    for (String name : providers) {
+      Provider p = Security.getProvider(name);
+      if (p == null) {
+        throw new IllegalArgumentException("No such provider: " + name);
+      }
+      this.providers.add(p);
+    }
+    return this;
+  }
+
+  /**
+   * Causes the given provider to be omitted from this instance's testing.  If the given provider
+   * is not installed, does nothing.
+   */
+  public ServiceTester skipProvider(String provider) {
+    Provider p = Security.getProvider(provider);
+    if (p != null) {
+      skipProviders.add(p);
+    }
+    return this;
+  }
+
+  /**
+   * Specifies the algorithm to test.  If this method and/or {@link #withAlgorithms(Collection)}}
+   * are called multiple times, all values are combined.  If neither method is called, this will
+   * test all algorithms supported by any tested provider.
+   */
+  public ServiceTester withAlgorithm(String algorithm) {
+    this.algorithms.add(algorithm);
+    return this;
+  }
+
+  /**
+   * Specifies the algorithms to test.  If this method and/or {@link #withAlgorithm(String)}}
+   * are called multiple times, all values are combined.  If neither method is called, this will
+   * test all algorithms supported by any tested provider.
+   */
+  public ServiceTester withAlgorithms(Collection<String> algorithms) {
+    this.algorithms.addAll(algorithms);
+    return this;
+  }
+
+  /**
+   * Causes the given algorithm to be omitted from this instance's testing.  If no tested provider
+   * provides the given algorithm, does nothing.
+   */
+  public ServiceTester skipAlgorithm(String algorithm) {
+    skipAlgorithms.add(algorithm);
+    return this;
+  }
+
+  /**
+   * Runs the given test against the configured combination of providers and algorithms.  Continues
+   * running all combinations even if some fail.  If any of the test runs fail, this throws
+   * an exception with the details of the failure(s).
+   */
+  public void run(Test test) {
+    if (providers.isEmpty()) {
+      providers.addAll(Arrays.asList(Security.getProviders()));
+    }
+    providers.removeAll(skipProviders);
+    final ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
+    PrintStream errors = new PrintStream(errBuffer);
+    for (Provider p : providers) {
+      if (algorithms.isEmpty()) {
+        for (Provider.Service s : p.getServices()) {
+          if (s.getType().equals(service) && !skipAlgorithms.contains(s.getAlgorithm())) {
+            doTest(test, p, s.getAlgorithm(), errors);
+          }
+        }
+      } else {
+        algorithms.removeAll(skipAlgorithms);
+        for (String algorithm : algorithms) {
+          if (p.getService(service, algorithm) != null) {
+            doTest(test, p, algorithm, errors);
+          }
+        }
+      }
+    }
+    errors.flush();
+    if (errBuffer.size() > 0) {
+      fail("Tests failed:\n\n" + errBuffer.toString());
+    }
+  }
+
+  private void doTest(Test test, Provider p, String algorithm, PrintStream errors) {
+    try {
+      test.test(p, algorithm);
+    } catch (Exception e) {
+      errors.append("Failure testing " + service + ":" + algorithm
+          + " from provider " + p.getName() + ":\n");
+      e.printStackTrace(errors);
+    }
+  }
+
+}
diff --git a/srcgen/core-platform-api.txt b/srcgen/core-platform-api.txt
new file mode 100644
index 0000000..c882063
--- /dev/null
+++ b/srcgen/core-platform-api.txt
@@ -0,0 +1,58 @@
+method:com.android.org.conscrypt.ClientSessionContext#setPersistentCache(SSLClientSessionCache)
+method:com.android.org.conscrypt.Conscrypt#getDefaultX509TrustManager()
+method:com.android.org.conscrypt.FileClientSessionCache#usingDirectory(File)
+method:com.android.org.conscrypt.OpenSSLProvider#OpenSSLProvider()
+method:com.android.org.conscrypt.OpenSSLSocketImpl#getAlpnSelectedProtocol()
+method:com.android.org.conscrypt.OpenSSLSocketImpl#getNpnSelectedProtocol()
+method:com.android.org.conscrypt.OpenSSLSocketImpl#setAlpnProtocols(byte[])
+method:com.android.org.conscrypt.OpenSSLSocketImpl#setChannelIdPrivateKey(PrivateKey)
+method:com.android.org.conscrypt.OpenSSLSocketImpl#setHandshakeTimeout(int)
+method:com.android.org.conscrypt.OpenSSLSocketImpl#setHostname(String)
+method:com.android.org.conscrypt.OpenSSLSocketImpl#setNpnProtocols(byte[])
+method:com.android.org.conscrypt.OpenSSLSocketImpl#setSoWriteTimeout(int)
+method:com.android.org.conscrypt.OpenSSLSocketImpl#setUseSessionTickets(boolean)
+method:com.android.org.conscrypt.TrustedCertificateIndex#findAllByIssuerAndSignature(X509Certificate)
+method:com.android.org.conscrypt.TrustedCertificateIndex#findByIssuerAndSignature(X509Certificate)
+method:com.android.org.conscrypt.TrustedCertificateIndex#findBySubjectAndPublicKey(X509Certificate)
+method:com.android.org.conscrypt.TrustedCertificateIndex#index(X509Certificate)
+method:com.android.org.conscrypt.TrustedCertificateIndex#TrustedCertificateIndex()
+method:com.android.org.conscrypt.TrustedCertificateStore#aliases()
+method:com.android.org.conscrypt.TrustedCertificateStore#allSystemAliases()
+method:com.android.org.conscrypt.TrustedCertificateStore#containsAlias(String)
+method:com.android.org.conscrypt.TrustedCertificateStore#deleteCertificateEntry(String)
+method:com.android.org.conscrypt.TrustedCertificateStore#findAllIssuers(X509Certificate)
+method:com.android.org.conscrypt.TrustedCertificateStore#findIssuer(X509Certificate)
+method:com.android.org.conscrypt.TrustedCertificateStore#getCertificateAlias(Certificate)
+method:com.android.org.conscrypt.TrustedCertificateStore#getCertificateAlias(Certificate,boolean)
+method:com.android.org.conscrypt.TrustedCertificateStore#getCertificateChain(X509Certificate)
+method:com.android.org.conscrypt.TrustedCertificateStore#getCertificateFile(File,X509Certificate)
+method:com.android.org.conscrypt.TrustedCertificateStore#getCertificate(String)
+method:com.android.org.conscrypt.TrustedCertificateStore#getCertificate(String,boolean)
+method:com.android.org.conscrypt.TrustedCertificateStore#getCreationDate(String)
+method:com.android.org.conscrypt.TrustedCertificateStore#getTrustAnchor(X509Certificate)
+method:com.android.org.conscrypt.TrustedCertificateStore#installCertificate(X509Certificate)
+method:com.android.org.conscrypt.TrustedCertificateStore#isUserAddedCertificate(X509Certificate)
+method:com.android.org.conscrypt.TrustedCertificateStore#isUser(String)
+method:com.android.org.conscrypt.TrustedCertificateStore#setDefaultUserDirectory(File)
+method:com.android.org.conscrypt.TrustedCertificateStore#TrustedCertificateStore()
+method:com.android.org.conscrypt.TrustedCertificateStore#userAliases()
+method:com.android.org.conscrypt.TrustManagerImpl#checkClientTrusted(X509Certificate[],String)
+method:com.android.org.conscrypt.TrustManagerImpl#checkClientTrusted(X509Certificate[],String,Socket)
+method:com.android.org.conscrypt.TrustManagerImpl#checkClientTrusted(X509Certificate[],String,SSLEngine)
+method:com.android.org.conscrypt.TrustManagerImpl#checkServerTrusted(X509Certificate[],String,String)
+method:com.android.org.conscrypt.TrustManagerImpl#getTrustedChainForServer(X509Certificate[],String,Socket)
+method:com.android.org.conscrypt.TrustManagerImpl#getTrustedChainForServer(X509Certificate[],String,SSLEngine)
+method:com.android.org.conscrypt.TrustManagerImpl#handleTrustStorageUpdate()
+method:com.android.org.conscrypt.TrustManagerImpl#TrustManagerImpl(KeyStore)
+method:com.android.org.conscrypt.TrustManagerImpl#TrustManagerImpl(KeyStore,CertPinManager,ConscryptCertStore)
+type:com.android.org.conscrypt.CertPinManager
+type:com.android.org.conscrypt.ClientSessionContext
+type:com.android.org.conscrypt.Conscrypt
+type:com.android.org.conscrypt.ConscryptCertStore
+type:com.android.org.conscrypt.FileClientSessionCache
+type:com.android.org.conscrypt.OpenSSLProvider
+type:com.android.org.conscrypt.OpenSSLSocketImpl
+type:com.android.org.conscrypt.SSLClientSessionCache
+type:com.android.org.conscrypt.TrustedCertificateIndex
+type:com.android.org.conscrypt.TrustedCertificateStore
+type:com.android.org.conscrypt.TrustManagerImpl
diff --git a/srcgen/default-constructors.txt b/srcgen/default-constructors.txt
new file mode 100644
index 0000000..70f8a22
--- /dev/null
+++ b/srcgen/default-constructors.txt
@@ -0,0 +1,3 @@
+com.android.org.conscrypt.IvParameters
+com.android.org.conscrypt.OpenSSLRSAKeyFactory
+com.android.org.conscrypt.OpenSSLRSAKeyPairGenerator
diff --git a/srcgen/generate_android_src.sh b/srcgen/generate_android_src.sh
new file mode 100755
index 0000000..70c6d44
--- /dev/null
+++ b/srcgen/generate_android_src.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if [[ -z "${ANDROID_BUILD_TOP}" ]]; then
+    echo "Missing environment variables. Did you run build/envsetup.sh and lunch?" >&2
+    exit 1
+fi
+
+PROJECT_DIR=external/conscrypt
+
+PACKAGE_TRANSFORMATIONS="\
+    org.conscrypt:com.android.org.conscrypt \
+"
+
+MODULE_DIRS="\
+    benchmark-android \
+    benchmark-base \
+    common \
+    openjdk \
+    openjdk-integ-tests \
+    platform \
+    testing \
+"
+DEFAULT_CONSTRUCTORS_FILE=${CONSCRYPT_DIR}/srcgen/default-constructors.txt
+
+SOURCE_DIRS="\
+    src/main/java \
+    src/test/java \
+"
+
+# Repackage the project's source.
+source ${ANDROID_BUILD_TOP}/tools/currysrc/scripts/repackage-common.sh
+
+# Remove some unused test files:
+rm -fr ${REPACKAGED_DIR}/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/ConscryptSuite.java
+rm -fr ${REPACKAGED_DIR}/openjdk-integ-tests/src/test/java/com/android/org/conscrypt/ConscryptJava7Suite.java
+
+echo "Reformatting generated code to adhere to format required by the preupload check"
+cd ${PROJECT_DIR}
+CLANG_STABLE_BIN=${ANDROID_BUILD_TOP}/prebuilts/clang/host/linux-x86/clang-stable/bin
+${ANDROID_BUILD_TOP}/tools/repohooks/tools/clang-format.py --fix \
+  --clang-format ${CLANG_STABLE_BIN}/clang-format \
+  --git-clang-format ${CLANG_STABLE_BIN}/git-clang-format \
+  --style file \
+  --working-tree \
+  $(git diff --name-only HEAD | grep -E "^repackaged/")
diff --git a/srcgen/intra-core-api.txt b/srcgen/intra-core-api.txt
new file mode 100644
index 0000000..656535d
--- /dev/null
+++ b/srcgen/intra-core-api.txt
@@ -0,0 +1,207 @@
+method:com.android.org.conscrypt.DESEDESecretKeyFactory#DESEDESecretKeyFactory()
+method:com.android.org.conscrypt.DefaultSSLContextImpl#DefaultSSLContextImpl()
+method:com.android.org.conscrypt.ECParameters#ECParameters()
+method:com.android.org.conscrypt.GCMParameters#GCMParameters()
+method:com.android.org.conscrypt.IvParameters$AES#AES()
+method:com.android.org.conscrypt.IvParameters$ChaCha20#ChaCha20()
+method:com.android.org.conscrypt.IvParameters$DESEDE#DESEDE()
+method:com.android.org.conscrypt.IvParameters#IvParameters()
+method:com.android.org.conscrypt.KeyGeneratorImpl$AES#AES()
+method:com.android.org.conscrypt.KeyGeneratorImpl$ARC4#ARC4()
+method:com.android.org.conscrypt.KeyGeneratorImpl$ChaCha20#ChaCha20()
+method:com.android.org.conscrypt.KeyGeneratorImpl$DESEDE#DESEDE()
+method:com.android.org.conscrypt.KeyGeneratorImpl$HmacMD5#HmacMD5()
+method:com.android.org.conscrypt.KeyGeneratorImpl$HmacSHA1#HmacSHA1()
+method:com.android.org.conscrypt.KeyGeneratorImpl$HmacSHA224#HmacSHA224()
+method:com.android.org.conscrypt.KeyGeneratorImpl$HmacSHA256#HmacSHA256()
+method:com.android.org.conscrypt.KeyGeneratorImpl$HmacSHA384#HmacSHA384()
+method:com.android.org.conscrypt.KeyGeneratorImpl$HmacSHA512#HmacSHA512()
+method:com.android.org.conscrypt.OAEPParameters#OAEPParameters()
+method:com.android.org.conscrypt.OpenSSLAeadCipherAES$GCM#GCM()
+method:com.android.org.conscrypt.OpenSSLAeadCipherAES$GCM$AES_128#AES_128()
+method:com.android.org.conscrypt.OpenSSLAeadCipherAES$GCM$AES_256#AES_256()
+method:com.android.org.conscrypt.OpenSSLAeadCipherAES$GCM_SIV#GCM_SIV()
+method:com.android.org.conscrypt.OpenSSLAeadCipherAES$GCM_SIV$AES_128#AES_128()
+method:com.android.org.conscrypt.OpenSSLAeadCipherAES$GCM_SIV$AES_256#AES_256()
+method:com.android.org.conscrypt.OpenSSLAeadCipherChaCha20#OpenSSLAeadCipherChaCha20()
+method:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES$CBC$NoPadding#NoPadding()
+method:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES$CBC$PKCS5Padding#PKCS5Padding()
+method:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES$CTR#CTR()
+method:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES$ECB$NoPadding#NoPadding()
+method:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES$ECB$PKCS5Padding#PKCS5Padding()
+method:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_128$CBC$NoPadding#NoPadding()
+method:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_128$CBC$PKCS5Padding#PKCS5Padding()
+method:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_128$ECB$NoPadding#NoPadding()
+method:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_128$ECB$PKCS5Padding#PKCS5Padding()
+method:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_256$CBC$NoPadding#NoPadding()
+method:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_256$CBC$PKCS5Padding#PKCS5Padding()
+method:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_256$ECB$NoPadding#NoPadding()
+method:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_256$ECB$PKCS5Padding#PKCS5Padding()
+method:com.android.org.conscrypt.OpenSSLEvpCipherARC4#OpenSSLEvpCipherARC4()
+method:com.android.org.conscrypt.OpenSSLEvpCipherDESEDE$CBC$NoPadding#NoPadding()
+method:com.android.org.conscrypt.OpenSSLEvpCipherDESEDE$CBC$PKCS5Padding#PKCS5Padding()
+method:com.android.org.conscrypt.OpenSSLCipherChaCha20#OpenSSLCipherChaCha20()
+method:com.android.org.conscrypt.OpenSSLCipherRSA$OAEP$SHA1#SHA1()
+method:com.android.org.conscrypt.OpenSSLCipherRSA$OAEP$SHA224#SHA224()
+method:com.android.org.conscrypt.OpenSSLCipherRSA$OAEP$SHA256#SHA256()
+method:com.android.org.conscrypt.OpenSSLCipherRSA$OAEP$SHA384#SHA384()
+method:com.android.org.conscrypt.OpenSSLCipherRSA$OAEP$SHA512#SHA512()
+method:com.android.org.conscrypt.OpenSSLCipherRSA$PKCS1#PKCS1()
+method:com.android.org.conscrypt.OpenSSLCipherRSA$Raw#Raw()
+method:com.android.org.conscrypt.OpenSSLContextImpl$TLSv1#TLSv1()
+method:com.android.org.conscrypt.OpenSSLContextImpl$TLSv11#TLSv11()
+method:com.android.org.conscrypt.OpenSSLContextImpl$TLSv12#TLSv12()
+method:com.android.org.conscrypt.OpenSSLContextImpl$TLSv13#TLSv13()
+method:com.android.org.conscrypt.OpenSSLECDHKeyAgreement#OpenSSLECDHKeyAgreement()
+method:com.android.org.conscrypt.OpenSSLECKeyFactory#OpenSSLECKeyFactory()
+method:com.android.org.conscrypt.OpenSSLECKeyPairGenerator#OpenSSLECKeyPairGenerator()
+method:com.android.org.conscrypt.OpenSSLMac$HmacMD5#HmacMD5()
+method:com.android.org.conscrypt.OpenSSLMac$HmacSHA1#HmacSHA1()
+method:com.android.org.conscrypt.OpenSSLMac$HmacSHA224#HmacSHA224()
+method:com.android.org.conscrypt.OpenSSLMac$HmacSHA256#HmacSHA256()
+method:com.android.org.conscrypt.OpenSSLMac$HmacSHA384#HmacSHA384()
+method:com.android.org.conscrypt.OpenSSLMac$HmacSHA512#HmacSHA512()
+method:com.android.org.conscrypt.OpenSSLMessageDigestJDK$MD5#MD5()
+method:com.android.org.conscrypt.OpenSSLMessageDigestJDK$SHA1#SHA1()
+method:com.android.org.conscrypt.OpenSSLMessageDigestJDK$SHA224#SHA224()
+method:com.android.org.conscrypt.OpenSSLMessageDigestJDK$SHA256#SHA256()
+method:com.android.org.conscrypt.OpenSSLMessageDigestJDK$SHA384#SHA384()
+method:com.android.org.conscrypt.OpenSSLMessageDigestJDK$SHA512#SHA512()
+method:com.android.org.conscrypt.OpenSSLRSAKeyFactory#OpenSSLRSAKeyFactory()
+method:com.android.org.conscrypt.OpenSSLRSAKeyPairGenerator#OpenSSLRSAKeyPairGenerator()
+method:com.android.org.conscrypt.OpenSSLRandom#OpenSSLRandom()
+method:com.android.org.conscrypt.OpenSSLSignature$MD5RSA#MD5RSA()
+method:com.android.org.conscrypt.OpenSSLSignature$SHA1ECDSA#SHA1ECDSA()
+method:com.android.org.conscrypt.OpenSSLSignature$SHA1RSA#SHA1RSA()
+method:com.android.org.conscrypt.OpenSSLSignature$SHA1RSAPSS#SHA1RSAPSS()
+method:com.android.org.conscrypt.OpenSSLSignature$SHA224ECDSA#SHA224ECDSA()
+method:com.android.org.conscrypt.OpenSSLSignature$SHA224RSA#SHA224RSA()
+method:com.android.org.conscrypt.OpenSSLSignature$SHA224RSAPSS#SHA224RSAPSS()
+method:com.android.org.conscrypt.OpenSSLSignature$SHA256ECDSA#SHA256ECDSA()
+method:com.android.org.conscrypt.OpenSSLSignature$SHA256RSA#SHA256RSA()
+method:com.android.org.conscrypt.OpenSSLSignature$SHA256RSAPSS#SHA256RSAPSS()
+method:com.android.org.conscrypt.OpenSSLSignature$SHA384ECDSA#SHA384ECDSA()
+method:com.android.org.conscrypt.OpenSSLSignature$SHA384RSA#SHA384RSA()
+method:com.android.org.conscrypt.OpenSSLSignature$SHA384RSAPSS#SHA384RSAPSS()
+method:com.android.org.conscrypt.OpenSSLSignature$SHA512ECDSA#SHA512ECDSA()
+method:com.android.org.conscrypt.OpenSSLSignature$SHA512RSA#SHA512RSA()
+method:com.android.org.conscrypt.OpenSSLSignature$SHA512RSAPSS#SHA512RSAPSS()
+method:com.android.org.conscrypt.OpenSSLSignatureRawECDSA#OpenSSLSignatureRawECDSA()
+method:com.android.org.conscrypt.OpenSSLSignatureRawRSA#OpenSSLSignatureRawRSA()
+method:com.android.org.conscrypt.OpenSSLX509CertificateFactory#OpenSSLX509CertificateFactory()
+method:com.android.org.conscrypt.PSSParameters#PSSParameters()
+type:com.android.org.conscrypt.DESEDESecretKeyFactory
+type:com.android.org.conscrypt.DefaultSSLContextImpl
+type:com.android.org.conscrypt.ECParameters
+type:com.android.org.conscrypt.GCMParameters
+type:com.android.org.conscrypt.IvParameters
+type:com.android.org.conscrypt.IvParameters$AES
+type:com.android.org.conscrypt.IvParameters$ChaCha20
+type:com.android.org.conscrypt.IvParameters$DESEDE
+type:com.android.org.conscrypt.KeyGeneratorImpl
+type:com.android.org.conscrypt.KeyGeneratorImpl$AES
+type:com.android.org.conscrypt.KeyGeneratorImpl$ARC4
+type:com.android.org.conscrypt.KeyGeneratorImpl$ChaCha20
+type:com.android.org.conscrypt.KeyGeneratorImpl$DESEDE
+type:com.android.org.conscrypt.KeyGeneratorImpl$HmacMD5
+type:com.android.org.conscrypt.KeyGeneratorImpl$HmacSHA1
+type:com.android.org.conscrypt.KeyGeneratorImpl$HmacSHA224
+type:com.android.org.conscrypt.KeyGeneratorImpl$HmacSHA256
+type:com.android.org.conscrypt.KeyGeneratorImpl$HmacSHA384
+type:com.android.org.conscrypt.KeyGeneratorImpl$HmacSHA512
+type:com.android.org.conscrypt.OAEPParameters
+type:com.android.org.conscrypt.OpenSSLCipher
+type:com.android.org.conscrypt.OpenSSLAeadCipher
+type:com.android.org.conscrypt.OpenSSLAeadCipherAES
+type:com.android.org.conscrypt.OpenSSLAeadCipherAES$GCM
+type:com.android.org.conscrypt.OpenSSLAeadCipherAES$GCM$AES_128
+type:com.android.org.conscrypt.OpenSSLAeadCipherAES$GCM$AES_256
+type:com.android.org.conscrypt.OpenSSLAeadCipherAES$GCM_SIV
+type:com.android.org.conscrypt.OpenSSLAeadCipherAES$GCM_SIV$AES_128
+type:com.android.org.conscrypt.OpenSSLAeadCipherAES$GCM_SIV$AES_256
+type:com.android.org.conscrypt.OpenSSLAeadCipherChaCha20
+type:com.android.org.conscrypt.OpenSSLEvpCipher
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES$CBC
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES$CBC$NoPadding
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES$CBC$PKCS5Padding
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES$CTR
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES$ECB
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES$ECB$NoPadding
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES$ECB$PKCS5Padding
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_128
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_128$CBC
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_128$CBC$NoPadding
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_128$CBC$PKCS5Padding
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_128$ECB
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_128$ECB$NoPadding
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_128$ECB$PKCS5Padding
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_256
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_256$CBC
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_256$CBC$NoPadding
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_256$CBC$PKCS5Padding
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_256$ECB
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_256$ECB$NoPadding
+type:com.android.org.conscrypt.OpenSSLEvpCipherAES$AES_256$ECB$PKCS5Padding
+type:com.android.org.conscrypt.OpenSSLEvpCipherARC4
+type:com.android.org.conscrypt.OpenSSLEvpCipherDESEDE
+type:com.android.org.conscrypt.OpenSSLEvpCipherDESEDE$CBC
+type:com.android.org.conscrypt.OpenSSLEvpCipherDESEDE$CBC$NoPadding
+type:com.android.org.conscrypt.OpenSSLEvpCipherDESEDE$CBC$PKCS5Padding
+type:com.android.org.conscrypt.OpenSSLCipherChaCha20
+type:com.android.org.conscrypt.OpenSSLCipherRSA
+type:com.android.org.conscrypt.OpenSSLCipherRSA$OAEP
+type:com.android.org.conscrypt.OpenSSLCipherRSA$OAEP$SHA1
+type:com.android.org.conscrypt.OpenSSLCipherRSA$OAEP$SHA224
+type:com.android.org.conscrypt.OpenSSLCipherRSA$OAEP$SHA256
+type:com.android.org.conscrypt.OpenSSLCipherRSA$OAEP$SHA384
+type:com.android.org.conscrypt.OpenSSLCipherRSA$OAEP$SHA512
+type:com.android.org.conscrypt.OpenSSLCipherRSA$PKCS1
+type:com.android.org.conscrypt.OpenSSLCipherRSA$Raw
+type:com.android.org.conscrypt.OpenSSLContextImpl
+type:com.android.org.conscrypt.OpenSSLContextImpl$TLSv1
+type:com.android.org.conscrypt.OpenSSLContextImpl$TLSv11
+type:com.android.org.conscrypt.OpenSSLContextImpl$TLSv12
+type:com.android.org.conscrypt.OpenSSLContextImpl$TLSv13
+type:com.android.org.conscrypt.OpenSSLECDHKeyAgreement
+type:com.android.org.conscrypt.OpenSSLECKeyFactory
+type:com.android.org.conscrypt.OpenSSLECKeyPairGenerator
+type:com.android.org.conscrypt.OpenSSLMac
+type:com.android.org.conscrypt.OpenSSLMac$HmacMD5
+type:com.android.org.conscrypt.OpenSSLMac$HmacSHA1
+type:com.android.org.conscrypt.OpenSSLMac$HmacSHA224
+type:com.android.org.conscrypt.OpenSSLMac$HmacSHA256
+type:com.android.org.conscrypt.OpenSSLMac$HmacSHA384
+type:com.android.org.conscrypt.OpenSSLMac$HmacSHA512
+type:com.android.org.conscrypt.OpenSSLMessageDigestJDK
+type:com.android.org.conscrypt.OpenSSLMessageDigestJDK$MD5
+type:com.android.org.conscrypt.OpenSSLMessageDigestJDK$SHA1
+type:com.android.org.conscrypt.OpenSSLMessageDigestJDK$SHA224
+type:com.android.org.conscrypt.OpenSSLMessageDigestJDK$SHA256
+type:com.android.org.conscrypt.OpenSSLMessageDigestJDK$SHA384
+type:com.android.org.conscrypt.OpenSSLMessageDigestJDK$SHA512
+type:com.android.org.conscrypt.OpenSSLRSAKeyFactory
+type:com.android.org.conscrypt.OpenSSLRSAKeyPairGenerator
+type:com.android.org.conscrypt.OpenSSLRandom
+type:com.android.org.conscrypt.OpenSSLSignature
+type:com.android.org.conscrypt.OpenSSLSignature$MD5RSA
+type:com.android.org.conscrypt.OpenSSLSignature$SHA1ECDSA
+type:com.android.org.conscrypt.OpenSSLSignature$SHA1RSA
+type:com.android.org.conscrypt.OpenSSLSignature$SHA1RSAPSS
+type:com.android.org.conscrypt.OpenSSLSignature$SHA224ECDSA
+type:com.android.org.conscrypt.OpenSSLSignature$SHA224RSA
+type:com.android.org.conscrypt.OpenSSLSignature$SHA224RSAPSS
+type:com.android.org.conscrypt.OpenSSLSignature$SHA256ECDSA
+type:com.android.org.conscrypt.OpenSSLSignature$SHA256RSA
+type:com.android.org.conscrypt.OpenSSLSignature$SHA256RSAPSS
+type:com.android.org.conscrypt.OpenSSLSignature$SHA384ECDSA
+type:com.android.org.conscrypt.OpenSSLSignature$SHA384RSA
+type:com.android.org.conscrypt.OpenSSLSignature$SHA384RSAPSS
+type:com.android.org.conscrypt.OpenSSLSignature$SHA512ECDSA
+type:com.android.org.conscrypt.OpenSSLSignature$SHA512RSA
+type:com.android.org.conscrypt.OpenSSLSignature$SHA512RSAPSS
+type:com.android.org.conscrypt.OpenSSLSignatureRawECDSA
+type:com.android.org.conscrypt.OpenSSLSignatureRawRSA
+type:com.android.org.conscrypt.OpenSSLX509CertificateFactory
+type:com.android.org.conscrypt.PSSParameters
diff --git a/srcgen/unsupported-app-usage.json b/srcgen/unsupported-app-usage.json
new file mode 100644
index 0000000..20d30f0
--- /dev/null
+++ b/srcgen/unsupported-app-usage.json
@@ -0,0 +1,334 @@
+// See com.google.currysrc.aosp.Annotations.addUnsupportedAppUsage(Path) method for details on the
+// syntax.
+[
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getAlpnSelectedProtocol()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getApplicationProtocols()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getChannelId()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getHostname()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getHostnameOrIP()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getNpnSelectedProtocol()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#getSoWriteTimeout()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setAlpnProtocols(byte[])"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setAlpnProtocols(String[])"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setApplicationProtocols(String[])"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setChannelIdEnabled(boolean)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setChannelIdPrivateKey(PrivateKey)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setHandshakeTimeout(int)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setHostname(String)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setNpnProtocols(byte[])"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setSoWriteTimeout(int)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.AbstractConscryptSocket#setUseSessionTickets(boolean)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.ClientSessionContext#getSession(String,int)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.ClientSessionContext#setPersistentCache(SSLClientSessionCache)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.ConscryptFileDescriptorSocket#setHostname(String)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.ConscryptFileDescriptorSocket#setUseSessionTickets(boolean)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.FileClientSessionCache$Impl#getSessionData(String,int)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.FileClientSessionCache#usingDirectory(File)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#ASN1_seq_pack_X509(long[])"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#ASN1_seq_unpack_X509_bio(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#ASN1_TIME_to_Calendar(long,Calendar)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#BIO_free_all(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#create_BIO_InputStream(OpenSSLBIOInputStream,boolean)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#create_BIO_OutputStream(OutputStream)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#d2i_PKCS7_bio(long,int)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#d2i_SSL_SESSION(byte[])"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#d2i_X509(byte[])"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#d2i_X509_bio(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#d2i_X509_CRL_bio(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#EC_GROUP_clear_free(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#EC_GROUP_new_by_curve_name(String)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#EC_POINT_clear_free(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#EVP_CIPHER_CTX_new()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#EVP_CIPHER_iv_length(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#EVP_get_cipherbyname(String)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#EVP_get_digestbyname(String)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#EVP_MD_CTX_create()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#EVP_MD_CTX_destroy(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#EVP_MD_size(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#EVP_PKEY_free(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#EVP_PKEY_new_RSA(byte[],byte[],byte[],byte[],byte[],byte[],byte[],byte[])"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#get_X509_REVOKED_ext_oids(long,int)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#get_X509_REVOKED_revocationDate(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#i2d_PKCS7(long[])"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#i2d_SSL_SESSION(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#i2d_X509_REVOKED(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#PEM_read_bio_PKCS7(long,int)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#PEM_read_bio_X509(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#PEM_read_bio_X509_CRL(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#RAND_bytes(byte[])"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#RSA_generate_key_ex(int,byte[])"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#SSL_CTX_new()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#SSL_SESSION_cipher(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#SSL_SESSION_free(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#SSL_SESSION_get_time(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#SSL_SESSION_get_version(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#SSL_SESSION_session_id(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#X509_REVOKED_dup(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#X509_REVOKED_get_ext(long,String)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#X509_REVOKED_get_ext_oid(long,String)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#X509_REVOKED_get_serialNumber(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#X509_REVOKED_print(long,long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.NativeCrypto#X509_supported_extension(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLBIOInputStream#OpenSSLBIOInputStream(InputStream,boolean)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLBIOInputStream#getBioContext()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLBIOInputStream#release()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLContextImpl$TLSv12#TLSv12()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLContextImpl#OpenSSLContextImpl()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLContextImpl#getPreferred()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLKey#OpenSSLKey(long)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLKey#fromPrivateKey(PrivateKey)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLKey#getNativeRef()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLKey#getPublicKey()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLKeyHolder#getOpenSSLKey()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLProvider#OpenSSLProvider()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLRandom#OpenSSLRandom()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketFactoryImpl#OpenSSLSocketFactoryImpl()"
+  },
+  {
+    "@location": "field:com.android.org.conscrypt.OpenSSLSocketFactoryImpl#sslParameters"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getAlpnSelectedProtocol()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getChannelId()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getHostname()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getHostnameOrIP()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getNpnSelectedProtocol()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#getSoWriteTimeout()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setAlpnProtocols(byte[])"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setAlpnProtocols(String[])"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setChannelIdEnabled(boolean)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setChannelIdPrivateKey(PrivateKey)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setHandshakeTimeout(int)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setHostname(String)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setNpnProtocols(byte[])"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setSoWriteTimeout(int)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLSocketImpl#setUseSessionTickets(boolean)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.OpenSSLX509Certificate#fromX509PemInputStream(InputStream)"
+  },
+  {
+    "@location": "field:com.android.org.conscrypt.OpenSSLX509Certificate#mContext"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.SSLParametersImpl#getDefault()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.SSLParametersImpl#getDefaultX509TrustManager()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.SSLParametersImpl#getX509TrustManager()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.SSLParametersImpl#setEnabledProtocols(String[])"
+  },
+  {
+    "@location": "field:com.android.org.conscrypt.SSLParametersImpl#x509TrustManager"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.TrustedCertificateStore#TrustedCertificateStore()"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.TrustedCertificateStore#getCertificateChain(X509Certificate)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.TrustManagerImpl#TrustManagerImpl(KeyStore)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.TrustManagerImpl#checkServerTrusted(X509Certificate[],String,String)"
+  },
+  {
+    "@location": "method:com.android.org.conscrypt.X509PublicKey#X509PublicKey(String,byte[])"
+  }
+]