Merge "Clean up the zlib-related native code." into dalvik-dev
diff --git a/include/ScopedGlobalRef.h b/include/ScopedGlobalRef.h
new file mode 100644
index 0000000..15a9055
--- /dev/null
+++ b/include/ScopedGlobalRef.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef SCOPED_GLOBAL_REF_H_included
+#define SCOPED_GLOBAL_REF_H_included
+
+#include "JNIHelp.h"
+
+// A smart pointer that provides access to a JNI global reference
+class ScopedGlobalRef {
+public:
+    ScopedGlobalRef(JNIEnv* env, jobject localRef)
+    : mEnv(env), mGlobalRef(NULL)
+    {
+        mGlobalRef = env->NewGlobalRef(localRef);
+    }
+
+    ~ScopedGlobalRef() {
+        reset();
+    }
+
+    void reset() {
+        if (mGlobalRef != NULL) {
+            mEnv->DeleteGlobalRef(mGlobalRef);
+            mGlobalRef = NULL;
+        }
+    }
+
+    jobject get () const {
+        return mGlobalRef;
+    }
+
+private:
+    JNIEnv* mEnv;
+    jobject mGlobalRef;
+
+    // Disallow copy and assignment.
+    ScopedGlobalRef(const ScopedGlobalRef&);
+    void operator=(const ScopedGlobalRef&);
+};
+
+#endif  // SCOPED_GLOBAL_REF_H_included
diff --git a/luni/src/main/native/ScopedJavaUnicodeString.h b/include/ScopedJavaUnicodeString.h
similarity index 100%
rename from luni/src/main/native/ScopedJavaUnicodeString.h
rename to include/ScopedJavaUnicodeString.h
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertImpl.java
index d3b4563..e649991 100644
--- a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertImpl.java
+++ b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertImpl.java
@@ -54,7 +54,7 @@
 
 // BEGIN android-added
 import java.security.interfaces.RSAPublicKey;
-import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl;
+import org.apache.harmony.xnet.provider.jsse.NativeCrypto;
 // END android-added
 
 /**
@@ -597,7 +597,7 @@
         }
 
         byte[] sig = certificate.getSignatureValue();
-        if (!OpenSSLSocketImpl.verifySignature(tbsCertificate, sig, algorithm, rsaKey)) {
+        if (!NativeCrypto.verifySignature(tbsCertificate, sig, algorithm, rsaKey)) {
             throw new SignatureException(Messages.getString("security.15C")); //$NON-NLS-1$
         }
     }
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
index dbc8282..6e38cf6 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
@@ -23,14 +23,14 @@
 import java.security.PrivateKey;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPublicKey;
 import java.util.ArrayList;
-
 import org.bouncycastle.openssl.PEMWriter;
 
 /**
- * Provides the Java side of our JNI glue for OpenSSL. Currently only hashing
- * and verifying are covered. Is expected to grow over time. Also needs to move
- * into libcore/openssl at some point.
+ * Provides the Java side of our JNI glue for OpenSSL. Currently only
+ * hashing and verifying are covered. Is expected to grow over
+ * time. Also needs to move into libcore/openssl at some point.
  */
 public class NativeCrypto {
 
@@ -75,6 +75,30 @@
 
     public static native int EVP_VerifyFinal(int ctx, byte[] signature, int offset, int length, int key);
 
+    // --- Legacy Signature handling -------------------------------------------
+    // TODO rewrite/replace with EVP_Verify*
+    /**
+     * Verifies an RSA signature. Conceptually, this method doesn't really
+     * belong here, but due to its native code being closely tied to OpenSSL
+     * (just like the rest of this class), we put it here for the time being.
+     * This also solves potential problems with native library initialization.
+     *
+     * @param message The message to verify
+     * @param signature The signature to verify
+     * @param algorithm The hash/sign algorithm to use, i.e. "RSA-SHA1"
+     * @param key The RSA public key to use
+     * @return true if the verification succeeds, false otherwise
+     */
+    public static boolean verifySignature(byte[] message, byte[] signature, String algorithm, RSAPublicKey key) {
+        byte[] modulus = key.getModulus().toByteArray();
+        byte[] exponent = key.getPublicExponent().toByteArray();
+
+        return verifySignature(message, signature, algorithm, modulus, exponent) == 1;
+    }
+
+    private static native int verifySignature(byte[] message, byte[] signature,
+            String algorithm, byte[] modulus, byte[] exponent);
+
     // --- SSL handling --------------------------------------------------------
 
     private static final String SUPPORTED_PROTOCOL_SSLV3 = "SSLv3";
@@ -305,6 +329,22 @@
 
     public static native byte[][] SSL_get_certificate(int sslNativePointer);
 
+    /**
+     * Reads with the native SSL_read function from the encrypted data stream
+     * @return -1 if error or the end of the stream is reached.
+     */
+    public static native int SSL_read_byte(int sslNativePointer, int timeout) throws IOException;
+    public static native int SSL_read(int sslNativePointer, byte[] b, int off, int len, int timeout) throws IOException;
+
+    /**
+     * Writes with the native SSL_write function to the encrypted data stream.
+     */
+    public static native void SSL_write_byte(int sslNativePointer, int b) throws IOException;
+    public static native void SSL_write(int sslNativePointer, byte[] b, int off, int len) throws IOException;
+
+    public static native void SSL_interrupt(int sslNativePointer) throws IOException;
+    public static native void SSL_shutdown(int sslNativePointer) throws IOException;
+
     public static native void SSL_free(int sslNativePointer);
 
     public static native byte[] SSL_SESSION_session_id(int sslSessionNativePointer);
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
index a813bf0..f643249 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
@@ -19,34 +19,25 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.io.OutputStreamWriter;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.net.SocketException;
-import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPublicKey;
 import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Level;
 import java.util.logging.Logger;
-
 import javax.net.ssl.HandshakeCompletedEvent;
 import javax.net.ssl.HandshakeCompletedListener;
 import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLSession;
-
 import org.apache.harmony.security.provider.cert.X509CertImpl;
 
 /**
  * Implementation of the class OpenSSLSocketImpl
- * based on OpenSSL. The JNI native interface for some methods
- * of this this class are defined in the file:
- * org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
+ * based on OpenSSL.
  *
  * This class only supports SSLv3 and TLSv1. This should be documented elsewhere
  * later, for example in the package.html or a separate reference document.
@@ -531,13 +522,6 @@
     }
 
     /**
-     * Reads with the native SSL_read function from the encrypted data stream
-     * @return -1 if error or the end of the stream is reached.
-     */
-    private native int nativeread(int sslNativePointer, int timeout) throws IOException;
-    private native int nativeread(int sslNativePointer, byte[] b, int off, int len, int timeout) throws IOException;
-
-    /**
      * This inner class provides input data stream functionality
      * for the OpenSSL native implementation. It is used to
      * read data received via SSL protocol.
@@ -560,7 +544,7 @@
          */
         public int read() throws IOException {
             synchronized(readLock) {
-                return OpenSSLSocketImpl.this.nativeread(sslNativePointer, timeout);
+                return NativeCrypto.SSL_read_byte(sslNativePointer, timeout);
             }
         }
 
@@ -570,18 +554,12 @@
          */
         public int read(byte[] b, int off, int len) throws IOException {
             synchronized(readLock) {
-                return OpenSSLSocketImpl.this.nativeread(sslNativePointer, b, off, len, timeout);
+                return NativeCrypto.SSL_read(sslNativePointer, b, off, len, timeout);
             }
         }
     }
 
     /**
-     * Writes with the native SSL_write function to the encrypted data stream.
-     */
-    private native void nativewrite(int sslNativePointer, int b) throws IOException;
-    private native void nativewrite(int sslNativePointer, byte[] b, int off, int len) throws IOException;
-
-    /**
      * 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.
@@ -601,7 +579,7 @@
          */
         public void write(int b) throws IOException {
             synchronized(writeLock) {
-                OpenSSLSocketImpl.this.nativewrite(sslNativePointer, b);
+                NativeCrypto.SSL_write_byte(sslNativePointer, b);
             }
         }
 
@@ -611,7 +589,7 @@
          */
         public void write(byte[] b, int start, int len) throws IOException {
             synchronized(writeLock) {
-                OpenSSLSocketImpl.this.nativewrite(sslNativePointer, b, start, len);
+                NativeCrypto.SSL_write(sslNativePointer, b, start, len);
             }
         }
     }
@@ -870,9 +848,6 @@
     }
     // END android-added
 
-    private native void nativeinterrupt(int sslNativePointer) throws IOException;
-    private native void nativeclose(int sslNativePointer) throws IOException;
-
     /**
      * Closes the SSL socket. Once closed, a socket is not available for further
      * use anymore under any circumstance. A new socket must be created.
@@ -903,7 +878,7 @@
             }
         }
 
-        nativeinterrupt(sslNativePointer);
+        NativeCrypto.SSL_interrupt(sslNativePointer);
 
         synchronized (this) {
             synchronized (writeLock) {
@@ -914,7 +889,7 @@
                     // Shut down the SSL connection, per se.
                     try {
                         if (handshakeStarted) {
-                            nativeclose(sslNativePointer);
+                            NativeCrypto.SSL_shutdown(sslNativePointer);
                         }
                     } catch (IOException ex) {
                         /*
@@ -975,26 +950,4 @@
         updateInstanceCount(-1);
         free();
     }
-
-    /**
-     * Verifies an RSA signature. Conceptually, this method doesn't really
-     * belong here, but due to its native code being closely tied to OpenSSL
-     * (just like the rest of this class), we put it here for the time being.
-     * This also solves potential problems with native library initialization.
-     *
-     * @param message The message to verify
-     * @param signature The signature to verify
-     * @param algorithm The hash/sign algorithm to use, i.e. "RSA-SHA1"
-     * @param key The RSA public key to use
-     * @return true if the verification succeeds, false otherwise
-     */
-    public static boolean verifySignature(byte[] message, byte[] signature, String algorithm, RSAPublicKey key) {
-        byte[] modulus = key.getModulus().toByteArray();
-        byte[] exponent = key.getPublicExponent().toByteArray();
-
-        return nativeverifysignature(message, signature, algorithm, modulus, exponent) == 1;
-    }
-
-    private static native int nativeverifysignature(byte[] message, byte[] signature,
-            String algorithm, byte[] modulus, byte[] exponent);
 }
diff --git a/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp b/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
index 5e2aaf6..16ff6df 100644
--- a/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
+++ b/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
@@ -36,13 +36,20 @@
 #include <openssl/rsa.h>
 #include <openssl/ssl.h>
 
+#include "ScopedByteArray.h"
+#include "ScopedGlobalRef.h"
+#include "ScopedUtfChars.h"
+#include "UniquePtr.h"
+
 #undef WITH_JNI_TRACE
 #ifdef WITH_JNI_TRACE
 #define JNI_TRACE(...) \
         ((void)LOG(LOG_INFO, LOG_TAG "-jni", __VA_ARGS__));     \
-        ((void)printf("I/" LOG_TAG "-jni:"));                   \
-        ((void)printf(__VA_ARGS__));                            \
+/*
+        ((void)printf("I/" LOG_TAG "-jni:"));         \
+        ((void)printf(__VA_ARGS__));          \
         ((void)printf("\n"))
+*/
 #else
 #define JNI_TRACE(...) ((void)0)
 #endif
@@ -226,7 +233,7 @@
  * @param throwIfNull whether to throw if the SSL pointer is NULL
  * @returns the pointer, which may be NULL
  */
-static SSL* getSslPointer(JNIEnv* env, int ssl_address, bool throwIfNull) {
+static SSL* to_SSL(JNIEnv* env, int ssl_address, bool throwIfNull) {
     SSL* ssl = reinterpret_cast<SSL*>(static_cast<uintptr_t>(ssl_address));
     if ((ssl == NULL) && throwIfNull) {
         throwSSLExceptionStr(env, "null SSL pointer");
@@ -235,6 +242,14 @@
     return ssl;
 }
 
+static SSL_CTX* to_SSL_CTX(int ssl_ctx_address) {
+    return reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
+}
+
+static SSL_SESSION* to_SSL_SESSION(int ssl_session_address) {
+    return reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
+}
+
 /**
  * Converts a Java byte[] to an OpenSSL BIGNUM, allocating the BIGNUM on the
  * fly.
@@ -242,10 +257,9 @@
 static BIGNUM* arrayToBignum(JNIEnv* env, jbyteArray source) {
     // LOGD("Entering arrayToBignum()");
 
-    jbyte* sourceBytes = env->GetByteArrayElements(source, NULL);
+    ScopedByteArray sourceBytes(env, source);
     int sourceLength = env->GetArrayLength(source);
-    BIGNUM* bignum = BN_bin2bn((unsigned char*) sourceBytes, sourceLength, NULL);
-    env->ReleaseByteArrayElements(source, sourceBytes, JNI_ABORT);
+    BIGNUM* bignum = BN_bin2bn((unsigned char*) sourceBytes.bytes(), sourceLength, NULL);
     return bignum;
 }
 
@@ -336,6 +350,10 @@
     // LOGD("Entering EVP_PKEY_new_DSA()");
 
     DSA* dsa = DSA_new();
+    if (dsa == NULL) {
+        jniThrowRuntimeException(env, "DSA_new failed");
+        return NULL;
+    }
 
     dsa->p = arrayToBignum(env, p);
     dsa->q = arrayToBignum(env, q);
@@ -353,6 +371,10 @@
     }
 
     EVP_PKEY* pkey = EVP_PKEY_new();
+    if (pkey == NULL) {
+        jniThrowRuntimeException(env, "EVP_PKEY_new failed");
+        return NULL;
+    }
     EVP_PKEY_assign_DSA(pkey, dsa);
 
     return pkey;
@@ -365,6 +387,10 @@
     // LOGD("Entering EVP_PKEY_new_RSA()");
 
     RSA* rsa = RSA_new();
+    if (rsa == NULL) {
+        jniThrowRuntimeException(env, "RSA_new failed");
+        return NULL;
+    }
 
     rsa->n = arrayToBignum(env, n);
     rsa->e = arrayToBignum(env, e);
@@ -391,6 +417,11 @@
     }
 
     EVP_PKEY* pkey = EVP_PKEY_new();
+    if (pkey == NULL) {
+        RSA_free(rsa);
+        jniThrowRuntimeException(env, "EVP_PKEY_new failed");
+        return NULL;
+    }
     EVP_PKEY_assign_RSA(pkey, rsa);
 
     return pkey;
@@ -460,10 +491,9 @@
         return;
     }
 
-    const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL);
+    ScopedUtfChars algorithmChars(env, algorithm);
 
-    const EVP_MD *digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars));
-    env->ReleaseStringUTFChars(algorithm, algorithmChars);
+    const EVP_MD *digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars.c_str()));
 
     if (digest == NULL) {
         jniThrowRuntimeException(env, "Hash algorithm not found");
@@ -522,9 +552,8 @@
         return;
     }
 
-    jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL);
-    EVP_DigestUpdate(ctx, (unsigned char*) (bufferBytes + offset), length);
-    env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT);
+    ScopedByteArray bufferBytes(env, buffer);
+    EVP_DigestUpdate(ctx, (unsigned char*) (bufferBytes.bytes() + offset), length);
 
     throwExceptionIfNecessary(env, "NativeCrypto_EVP_DigestUpdate");
 }
@@ -540,10 +569,9 @@
         return;
     }
 
-    const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL);
+    ScopedUtfChars algorithmChars(env, algorithm);
 
-    const EVP_MD *digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars));
-    env->ReleaseStringUTFChars(algorithm, algorithmChars);
+    const EVP_MD *digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars.c_str()));
 
     if (digest == NULL) {
         jniThrowRuntimeException(env, "Hash algorithm not found");
@@ -566,9 +594,8 @@
         return;
     }
 
-    jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL);
-    EVP_VerifyUpdate(ctx, (unsigned char*) (bufferBytes + offset), length);
-    env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT);
+    ScopedByteArray bufferBytes(env, buffer);
+    EVP_VerifyUpdate(ctx, (unsigned char*) (bufferBytes.bytes() + offset), length);
 
     throwExceptionIfNecessary(env, "NativeCrypto_EVP_VerifyUpdate");
 }
@@ -584,9 +611,8 @@
         return -1;
     }
 
-    jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL);
-    int result = EVP_VerifyFinal(ctx, (unsigned char*) (bufferBytes + offset), length, pkey);
-    env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT);
+    ScopedByteArray bufferBytes(env, buffer);
+    int result = EVP_VerifyFinal(ctx, (unsigned char*) (bufferBytes.bytes() + offset), length, pkey);
 
     throwExceptionIfNecessary(env, "NativeCrypto_EVP_VerifyFinal");
 
@@ -594,6 +620,129 @@
 }
 
 /**
+ * Helper function that creates an RSA public key from two buffers containing
+ * the big-endian bit representation of the modulus and the public exponent.
+ *
+ * @param mod The data of the modulus
+ * @param modLen The length of the modulus data
+ * @param exp The data of the exponent
+ * @param expLen The length of the exponent data
+ *
+ * @return A pointer to the new RSA structure, or NULL on error
+ */
+static RSA* rsaCreateKey(unsigned char* mod, int modLen, unsigned char* exp, int expLen) {
+    // LOGD("Entering rsaCreateKey()");
+
+    RSA* rsa = RSA_new();
+    if (rsa == NULL) {
+        return NULL;
+    }
+
+    rsa->n = BN_bin2bn(mod, modLen, NULL);
+    rsa->e = BN_bin2bn(exp, expLen, NULL);
+
+    if (rsa->n == NULL || rsa->e == NULL) {
+        RSA_free(rsa);
+        return NULL;
+    }
+
+    return rsa;
+}
+
+/**
+ * Helper function that verifies a given RSA signature for a given message.
+ *
+ * @param msg The message to verify
+ * @param msgLen The length of the message
+ * @param sig The signature to verify
+ * @param sigLen The length of the signature
+ * @param algorithm The name of the hash/sign algorithm to use, e.g. "RSA-SHA1"
+ * @param rsa The RSA public key to use
+ *
+ * @return 1 on success, 0 on failure, -1 on error (check SSL errors then)
+ *
+ */
+static int rsaVerify(unsigned char* msg, unsigned int msgLen, unsigned char* sig,
+                     unsigned int sigLen, char* algorithm, RSA* rsa) {
+
+    // LOGD("Entering rsaVerify(%x, %d, %x, %d, %s, %x)", msg, msgLen, sig, sigLen, algorithm, rsa);
+
+    EVP_PKEY* pkey = EVP_PKEY_new();
+    if (pkey == NULL) {
+        return -1;
+    }
+    EVP_PKEY_set1_RSA(pkey, rsa);
+
+    const EVP_MD *type = EVP_get_digestbyname(algorithm);
+    if (type == NULL) {
+        EVP_PKEY_free(pkey);
+        return -1;
+    }
+
+    EVP_MD_CTX ctx;
+    EVP_MD_CTX_init(&ctx);
+    if (EVP_VerifyInit_ex(&ctx, type, NULL) == 0) {
+        EVP_PKEY_free(pkey);
+        return -1;
+    }
+
+    EVP_VerifyUpdate(&ctx, msg, msgLen);
+    int result = EVP_VerifyFinal(&ctx, sig, sigLen, pkey);
+    EVP_MD_CTX_cleanup(&ctx);
+    EVP_PKEY_free(pkey);
+    return result;
+}
+
+/**
+ * Verifies an RSA signature.
+ */
+static int NativeCrypto_verifysignature(JNIEnv* env, jclass clazz,
+        jbyteArray msg, jbyteArray sig, jstring algorithm, jbyteArray mod, jbyteArray exp) {
+
+    JNI_TRACE("NativeCrypto_verifysignature msg=%p sig=%p algorithm=%p mod=%p exp%p",
+             msg, sig, algorithm, mod, exp);
+
+    if (msg == NULL || sig == NULL || algorithm == NULL || mod == NULL || exp == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        JNI_TRACE("NativeCrypto_verifysignature => -1");
+        return -1;
+    }
+
+    int result = -1;
+
+    ScopedByteArray msgBytes(env, msg);
+    jint msgLength = env->GetArrayLength(msg);
+
+    ScopedByteArray sigBytes(env, sig);
+    jint sigLength = env->GetArrayLength(sig);
+
+    ScopedByteArray modBytes(env, mod);
+    jint modLength = env->GetArrayLength(mod);
+
+    ScopedByteArray expBytes(env, exp);
+    jint expLength = env->GetArrayLength(exp);
+
+    ScopedUtfChars algorithmChars(env, algorithm);
+    JNI_TRACE("NativeCrypto_verifysignature algorithmChars=%s", algorithmChars);
+
+    RSA* rsa = rsaCreateKey((unsigned char*) modBytes.bytes(), modLength, (unsigned char*) expBytes.bytes(), expLength);
+    if (rsa != NULL) {
+        result = rsaVerify((unsigned char*) msgBytes.bytes(), msgLength, (unsigned char*) sigBytes.bytes(), sigLength,
+                (char*) algorithmChars.c_str(), rsa);
+        RSA_free(rsa);
+    }
+
+    if (result == -1) {
+        if (!throwExceptionIfNecessary(env, "NativeCrypto_verifysignature")) {
+            jniThrowRuntimeException(env, "Internal error during verification");
+        }
+    }
+
+    JNI_TRACE("NativeCrypto_verifysignature => %d", result);
+    return result;
+}
+
+/**
  * Convert ssl version constant to string. Based on SSL_get_version
  */
 // TODO move to jsse.patch
@@ -729,6 +878,10 @@
     }
 
     BIO* bio = BIO_new(BIO_s_mem());
+    if (bio == NULL) {
+        jniThrowRuntimeException(env, "BIO_new failed");
+        return NULL;
+    }
 
     // LOGD("Start fetching the certificates");
     for (int i = 0; i < count; i++) {
@@ -829,8 +982,8 @@
     int fdsEmergency[2];
     MUTEX_TYPE mutex;
     JNIEnv* env;
-    jobject certificateChainVerifier;
-    jobject handshakeCompletedCallback;
+    ScopedGlobalRef certificateChainVerifier;
+    ScopedGlobalRef handshakeCompletedCallback;
 
     /**
      * Creates our application data and attaches it to a given SSL connection.
@@ -841,17 +994,29 @@
      * @param hcc The HandshakeCompletedCallback
      */
   public:
-    static AppData* create(JNIEnv* e, jobject ccv, jobject hcc) {
-        AppData* appData = new AppData(e, ccv, hcc);
-        appData->fdsEmergency[0] = -1;
-        appData->fdsEmergency[1] = -1;
+    static AppData* create(JNIEnv* e,
+                           jobject certificateChainVerifier,
+                           jobject handshakeCompletedCallback) {
+        if (certificateChainVerifier == NULL) {
+            return NULL;
+        }
+        if (handshakeCompletedCallback == NULL) {
+            return NULL;
+        }
+        UniquePtr<AppData> appData(new AppData(e, certificateChainVerifier, handshakeCompletedCallback));
+        if (appData->certificateChainVerifier.get() == NULL) {
+            return NULL;
+        }
+        if (appData->handshakeCompletedCallback.get() == NULL) {
+            return NULL;
+        }
         if (pipe(appData->fdsEmergency) == -1) {
             return NULL;
         }
         if (MUTEX_SETUP(appData->mutex) == -1) {
-          return NULL;
+            return NULL;
         }
-        return appData;
+        return appData.release();
     }
 
   private:
@@ -859,8 +1024,11 @@
             aliveAndKicking(1),
             waitingThreads(0),
             env(e),
-            certificateChainVerifier(ccv),
-            handshakeCompletedCallback(hcc) {}
+            certificateChainVerifier(e, ccv),
+            handshakeCompletedCallback(e, hcc) {
+        fdsEmergency[0] = -1;
+        fdsEmergency[1] = -1;
+    }
 
     /**
      * Destroys our application data, cleaning up everything in the process.
@@ -878,7 +1046,7 @@
     }
 
     void setEnv(JNIEnv* e) {
-        if (handshakeCompletedCallback == NULL) {
+        if (handshakeCompletedCallback.get() == NULL) {
             return;
         }
         env = e;
@@ -886,6 +1054,12 @@
     void clearEnv() {
         env = NULL;
     }
+
+    void handshakeCompleted() {
+        certificateChainVerifier.reset();
+        handshakeCompletedCallback.reset();
+        clearEnv();
+    }
 };
 
 /**
@@ -1048,10 +1222,10 @@
     JNIEnv* env = appData->env;
     if (env == NULL) {
         LOGE("AppData->env missing in cert_verify_callback");
-        JNI_TRACE("ssl=%p cert_verify_callback => 0", ssl, result);
+        JNI_TRACE("ssl=%p cert_verify_callback => 0", ssl);
         return 0;
     }
-    jobject certificateChainVerifier = appData->certificateChainVerifier;
+    jobject certificateChainVerifier = appData->certificateChainVerifier.get();
 
     jclass cls = env->GetObjectClass(certificateChainVerifier);
     jmethodID methodID = env->GetMethodID(cls, "verifyCertificateChain", "([[BLjava/lang/String;)V");
@@ -1100,10 +1274,10 @@
     JNIEnv* env = appData->env;
     if (env == NULL) {
         LOGE("AppData->env missing in info_callback");
-        JNI_TRACE("ssl=%p info_callback env error", ssl, result);
+        JNI_TRACE("ssl=%p info_callback env error", ssl);
         return;
     }
-    jobject handshakeCompletedCallback = appData->handshakeCompletedCallback;
+    jobject handshakeCompletedCallback = appData->handshakeCompletedCallback.get();
 
     jclass cls = env->GetObjectClass(handshakeCompletedCallback);
     jmethodID methodID = env->GetMethodID(cls, "handshakeCompleted", "()V");
@@ -1115,10 +1289,7 @@
       JNI_TRACE("ssl=%p info_callback exception", ssl);
     }
 
-    // no longer needed after handshake is complete
-    appData->clearEnv();
-    appData->certificateChainVerifier = NULL;
-    appData->handshakeCompletedCallback = NULL;
+    appData->handshakeCompleted();
     JNI_TRACE("ssl=%p info_callback completed", ssl);
 }
 
@@ -1127,6 +1298,10 @@
  */
 static int NativeCrypto_SSL_CTX_new(JNIEnv* env, jclass clazz) {
     SSL_CTX* sslCtx = SSL_CTX_new(SSLv23_method());
+    if (sslCtx == NULL) {
+        jniThrowRuntimeException(env, "SSL_CTX_new");
+        return NULL;
+    }
     // Note: We explicitly do not allow SSLv2 to be used.
     SSL_CTX_set_options(sslCtx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
 
@@ -1189,7 +1364,7 @@
 static jobjectArray NativeCrypto_SSL_CTX_get_ciphers(JNIEnv* env,
         jclass, jint ssl_ctx_address)
 {
-    SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
+    SSL_CTX* ssl_ctx = to_SSL_CTX(ssl_ctx_address);
     JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_get_ciphers", ssl_ctx);
     if (ssl_ctx == NULL) {
         jniThrowNullPointerException(env, "SSL_CTX is null");
@@ -1204,7 +1379,7 @@
 static void NativeCrypto_SSL_CTX_free(JNIEnv* env,
         jclass, jint ssl_ctx_address)
 {
-    SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
+    SSL_CTX* ssl_ctx = to_SSL_CTX(ssl_ctx_address);
     JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_free", ssl_ctx);
     if (ssl_ctx == NULL) {
         jniThrowNullPointerException(env, "SSL_CTX is null");
@@ -1223,9 +1398,13 @@
     LocalArray<1024> buf(byteCount + 1);
     env->GetStringUTFRegion(string, 0, env->GetStringLength(string), &buf[0]);
 
-    BIO* result = BIO_new(BIO_s_mem());
-    BIO_puts(result, &buf[0]);
-    return result;
+    BIO* bio = BIO_new(BIO_s_mem());
+    if (bio == NULL) {
+        jniThrowRuntimeException(env, "BIO_new failed");
+        return NULL;
+    }
+    BIO_puts(bio, &buf[0]);
+    return bio;
 }
 
 /**
@@ -1235,8 +1414,8 @@
 static jint NativeCrypto_SSL_new(JNIEnv* env, jclass,
                                  jint ssl_ctx_address, jstring privatekey, jstring certificates, jbyteArray seed, jobject ccv)
 {
-    SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
-    JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new privatekey=%p certificates=%p seed=%p ccv=%p", 
+    SSL_CTX* ssl_ctx = to_SSL_CTX(ssl_ctx_address);
+    JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new privatekey=%p certificates=%p seed=%p ccv=%p",
              ssl_ctx, privatekey, certificates, seed, ccv);
     if (ssl_ctx == NULL) {
         jniThrowNullPointerException(env, "SSL_CTX is null");
@@ -1346,7 +1525,7 @@
  */
 static jlong NativeCrypto_SSL_get_mode(JNIEnv* env, jclass,
         jint ssl_address) {
-    SSL* ssl = getSslPointer(env, ssl_address, true);
+    SSL* ssl = to_SSL(env, ssl_address, true);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_get_mode", ssl);
     if (ssl == NULL) {
       JNI_TRACE("ssl=%p NativeCrypto_SSL_get_mode => 0", ssl);
@@ -1362,7 +1541,7 @@
  */
 static jlong NativeCrypto_SSL_set_mode(JNIEnv* env, jclass,
         jint ssl_address, jlong mode) {
-    SSL* ssl = getSslPointer(env, ssl_address, true);
+    SSL* ssl = to_SSL(env, ssl_address, true);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_set_mode mode=0x%llx", ssl, mode);
     if (ssl == NULL) {
       return 0;
@@ -1377,7 +1556,7 @@
  */
 static jlong NativeCrypto_SSL_clear_mode(JNIEnv* env, jclass,
         jint ssl_address, jlong mode) {
-    SSL* ssl = getSslPointer(env, ssl_address, true);
+    SSL* ssl = to_SSL(env, ssl_address, true);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_clear_mode mode=0x%llx", ssl, mode);
     if (ssl == NULL) {
       return 0;
@@ -1392,7 +1571,7 @@
  */
 static jlong NativeCrypto_SSL_get_options(JNIEnv* env, jclass,
         jint ssl_address) {
-    SSL* ssl = getSslPointer(env, ssl_address, true);
+    SSL* ssl = to_SSL(env, ssl_address, true);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_get_options", ssl);
     if (ssl == NULL) {
       JNI_TRACE("ssl=%p NativeCrypto_SSL_get_options => 0", ssl);
@@ -1408,7 +1587,7 @@
  */
 static jlong NativeCrypto_SSL_set_options(JNIEnv* env, jclass,
         jint ssl_address, jlong options) {
-    SSL* ssl = getSslPointer(env, ssl_address, true);
+    SSL* ssl = to_SSL(env, ssl_address, true);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_set_options options=0x%llx", ssl, options);
     if (ssl == NULL) {
       return 0;
@@ -1423,7 +1602,7 @@
  */
 static jlong NativeCrypto_SSL_clear_options(JNIEnv* env, jclass,
         jint ssl_address, jlong options) {
-    SSL* ssl = getSslPointer(env, ssl_address, true);
+    SSL* ssl = to_SSL(env, ssl_address, true);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_clear_options options=0x%llx", ssl, options);
     if (ssl == NULL) {
       return 0;
@@ -1440,7 +1619,7 @@
 static jobjectArray NativeCrypto_SSL_get_ciphers(JNIEnv* env,
         jclass, jint ssl_address)
 {
-    SSL* ssl = getSslPointer(env, ssl_address, true);
+    SSL* ssl = to_SSL(env, ssl_address, true);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_get_ciphers", ssl);
     if (ssl == NULL) {
       return NULL;
@@ -1454,15 +1633,14 @@
 static void NativeCrypto_SSL_set_cipher_list(JNIEnv* env, jclass,
         jint ssl_address, jstring controlString)
 {
-    SSL* ssl = getSslPointer(env, ssl_address, true);
+    SSL* ssl = to_SSL(env, ssl_address, true);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_set_cipher_list controlString=%p", ssl, controlString);
     if (ssl == NULL) {
       return;
     }
-    const char* str = env->GetStringUTFChars(controlString, NULL);
+    ScopedUtfChars str(env, controlString);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_controlString str=%s", ssl, str);
-    int rc = SSL_set_cipher_list(ssl, str);
-    env->ReleaseStringUTFChars(controlString, str);
+    int rc = SSL_set_cipher_list(ssl, str.c_str());
     if (rc == 0) {
         freeSslErrorState();
         jniThrowException(env, "java/lang/IllegalArgumentException",
@@ -1476,7 +1654,7 @@
 static void NativeCrypto_SSL_set_verify(JNIEnv* env,
         jclass, jint ssl_address, jint mode)
 {
-    SSL* ssl = getSslPointer(env, ssl_address, true);
+    SSL* ssl = to_SSL(env, ssl_address, true);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_set_verify", ssl);
     if (ssl == NULL) {
       return;
@@ -1490,8 +1668,8 @@
 static void NativeCrypto_SSL_set_session(JNIEnv* env, jclass,
         jint ssl_address, jint ssl_session_address)
 {
-    SSL* ssl = getSslPointer(env, ssl_address, true);
-    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
+    SSL* ssl = to_SSL(env, ssl_address, true);
+    SSL_SESSION* ssl_session = to_SSL_SESSION(ssl_session_address);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_set_session ssl_session=%p", ssl, ssl_session);
     if (ssl == NULL) {
         return;
@@ -1518,7 +1696,7 @@
 static void NativeCrypto_SSL_set_session_creation_enabled(JNIEnv* env, jclass,
         jint ssl_address, jboolean creation_enabled)
 {
-    SSL* ssl = getSslPointer(env, ssl_address, true);
+    SSL* ssl = to_SSL(env, ssl_address, true);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_set_session_creation_enabled creation_enabled=%d", ssl, creation_enabled);
     if (ssl == NULL) {
         return;
@@ -1538,7 +1716,7 @@
 static jint NativeCrypto_SSL_do_handshake(JNIEnv* env, jclass,
     jint ssl_address, jobject socketObject, jobject ccv, jobject hcc, jint timeout, jboolean client_mode)
 {
-    SSL* ssl = getSslPointer(env, ssl_address, true);
+    SSL* ssl = to_SSL(env, ssl_address, true);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake socketObject=%p ccv=%p timeout=%d client_mode=%d",
              ssl, socketObject, ccv, timeout, client_mode);
     if (ssl == NULL) {
@@ -1667,7 +1845,7 @@
                     return 0;
                 }
             } else {
-                LOGE("Unknown error %d during handshake", error);
+                // LOGE("Unknown error %d during handshake", error);
                 break;
             }
         }
@@ -1713,7 +1891,7 @@
  */
 static jobjectArray NativeCrypto_SSL_get_certificate(JNIEnv* env, jclass, jint ssl_address)
 {
-    SSL* ssl = getSslPointer(env, ssl_address, true);
+    SSL* ssl = to_SSL(env, ssl_address, true);
     JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate", ssl);
     if (ssl == NULL) {
       JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => NULL", ssl);
@@ -1743,231 +1921,6 @@
 
 
 /**
- * public static native void SSL_free(int ssl);
- */
-static void NativeCrypto_SSL_free(JNIEnv* env, jclass, jint ssl_address)
-{
-    SSL* ssl = getSslPointer(env, ssl_address, true);
-    JNI_TRACE("ssl=%p NativeCrypto_SSL_free", ssl);
-    if (ssl == NULL) {
-      return;
-    }
-    AppData* appData = (AppData*) SSL_get_app_data(ssl);
-    SSL_set_app_data(ssl, NULL);
-    delete appData;
-    SSL_free(ssl);
-}
-
-/**
- * Gets and returns in a byte array the ID of the actual SSL session.
- */
-static jbyteArray NativeCrypto_SSL_SESSION_session_id(JNIEnv* env, jclass, jint ssl_session_address) {
-    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
-    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_session_id", ssl_session);
-    jbyteArray result = env->NewByteArray(ssl_session->session_id_length);
-    if (result != NULL) {
-        jbyte* src = reinterpret_cast<jbyte*>(ssl_session->session_id);
-        env->SetByteArrayRegion(result, 0, ssl_session->session_id_length, src);
-    }
-    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_session_id => %p session_id_length=%d", 
-             ssl_session, result, ssl_session->session_id_length);
-    return result;
-}
-
-/**
- * Our implementation of what might be considered
- * SSL_SESSION_get_peer_cert_chain
- *
- */
-// TODO move to jsse.patch
-static STACK_OF(X509)* SSL_SESSION_get_peer_cert_chain(SSL_CTX* ssl_ctx, SSL_SESSION* ssl_session) {
-    SSL* ssl = SSL_new(ssl_ctx);
-    SSL_set_session(ssl, ssl_session);
-    STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl);
-    SSL_free(ssl);
-    return chain;
-}
-
-// Fills a byte[][] with the peer certificates in the chain.
-static jobjectArray NativeCrypto_SSL_SESSION_get_peer_cert_chain(JNIEnv* env,
-        jclass, jint ssl_ctx_address, jint ssl_session_address)
-{
-    SSL_CTX* ssl_ctx = reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
-    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
-    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_peer_cert_chain ssl_ctx=%p", ssl_session, ssl_ctx);
-    if (ssl_ctx == NULL) {
-        jniThrowNullPointerException(env, "SSL_CTX is null");
-        JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_peer_cert_chain => NULL", ssl_session);
-        return NULL;
-    }
-    STACK_OF(X509)* chain = SSL_SESSION_get_peer_cert_chain(ssl_ctx, ssl_session);
-    jobjectArray objectArray = getCertificateBytes(env, chain);
-    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_peer_cert_chain => %p", ssl_session, objectArray);
-    return objectArray;
-}
-
-/**
- * Gets and returns in a long integer the creation's time of the
- * actual SSL session.
- */
-static jlong NativeCrypto_SSL_SESSION_get_time(JNIEnv* env, jclass, jint ssl_session_address) {
-    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
-    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_time", ssl_session);
-    jlong result = SSL_SESSION_get_time(ssl_session); // must be jlong, not long or *1000 will overflow
-    result *= 1000; // OpenSSL uses seconds, Java uses milliseconds.
-    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_time => %lld", ssl_session, result);
-    return result;
-}
-
-/**
- * Our implementation of what might be considered
- * SSL_SESSION_get_version, based on SSL_get_version.
- * See get_ssl_version above.
- */
-// TODO move to jsse.patch
-static const char* SSL_SESSION_get_version(SSL_SESSION* ssl_session) {
-  return get_ssl_version(ssl_session->ssl_version);
-}
-
-/**
- * Gets and returns in a string the version of the SSL protocol. If it
- * returns the string "unknown" it means that no connection is established.
- */
-static jstring NativeCrypto_SSL_SESSION_get_version(JNIEnv* env, jclass, jint ssl_session_address) {
-    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
-    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_version", ssl_session);
-    const char* protocol = SSL_SESSION_get_version(ssl_session);
-    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_version => %s", ssl_session, protocol);
-    jstring result = env->NewStringUTF(protocol);
-    return result;
-}
-
-/**
- * Gets and returns in a string the set of ciphers the actual SSL session uses.
- */
-static jstring NativeCrypto_SSL_SESSION_cipher(JNIEnv* env, jclass, jint ssl_session_address) {
-    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
-    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_cipher", ssl_session);
-    const SSL_CIPHER* cipher = ssl_session->cipher;
-    const char* name = SSL_CIPHER_get_name(cipher);
-    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_cipher => %s", ssl_session, name);
-    return env->NewStringUTF(name);
-}
-
-/**
- * Frees the SSL session.
- */
-static void NativeCrypto_SSL_SESSION_free(JNIEnv* env, jclass, jint session) {
-    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(session);
-    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_free", ssl_session);
-    SSL_SESSION_free(ssl_session);
-}
-
-
-/**
- * Serializes the native state of the session (ID, cipher, and keys but
- * not certificates). Returns a byte[] containing the DER-encoded state.
- * See apache mod_ssl.
- */
-static jbyteArray NativeCrypto_i2d_SSL_SESSION(JNIEnv* env, jclass, jint ssl_session_address) {
-    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
-    JNI_TRACE("ssl_session=%p NativeCrypto_i2d_SSL_SESSION", ssl_session);
-    if (ssl_session == NULL) {
-        JNI_TRACE("ssl_session=%p NativeCrypto_i2d_SSL_SESSION => NULL", ssl_session);
-        return NULL;
-    }
-
-    // Compute the size of the DER data
-    int size = i2d_SSL_SESSION(ssl_session, NULL);
-    if (size == 0) {
-        JNI_TRACE("ssl_session=%p NativeCrypto_i2d_SSL_SESSION => NULL", ssl_session);
-        return NULL;
-    }
-
-    jbyteArray bytes = env->NewByteArray(size);
-    if (bytes != NULL) {
-        jbyte* tmp = env->GetByteArrayElements(bytes, NULL);
-        unsigned char* ucp = reinterpret_cast<unsigned char*>(tmp);
-        i2d_SSL_SESSION(ssl_session, &ucp);
-        env->ReleaseByteArrayElements(bytes, tmp, 0);
-    }
-
-    JNI_TRACE("ssl_session=%p NativeCrypto_i2d_SSL_SESSION => size=%d", ssl_session, size);
-    return bytes;
-}
-
-/**
- * Deserialize the session.
- */
-static jint NativeCrypto_d2i_SSL_SESSION(JNIEnv* env, jclass, jbyteArray bytes, jint size) {
-    JNI_TRACE("NativeCrypto_d2i_SSL_SESSION bytes=%p size=%d", bytes, size);
-    if (bytes == NULL) {
-        JNI_TRACE("NativeCrypto_d2i_SSL_SESSION => 0");
-        return 0;
-    }
-
-    jbyte* tmp = env->GetByteArrayElements(bytes, NULL);
-    const unsigned char* ucp = reinterpret_cast<const unsigned char*>(tmp);
-    SSL_SESSION* ssl_session = d2i_SSL_SESSION(NULL, &ucp, size);
-    env->ReleaseByteArrayElements(bytes, tmp, 0);
-
-    JNI_TRACE("NativeCrypto_d2i_SSL_SESSION => %p", ssl_session);
-    return static_cast<jint>(reinterpret_cast<uintptr_t>(ssl_session));
-}
-
-/*
- * Defines the mapping from Java methods and their signatures
- * to native functions. Order is (1) Java name, (2) signature,
- * (3) pointer to C function.
- */
-static JNINativeMethod sNativeCryptoMethods[] = {
-    { "clinit",              "()V",           (void*)NativeCrypto_clinit},
-    { "EVP_PKEY_new_DSA",    "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_DSA },
-    { "EVP_PKEY_new_RSA",    "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_RSA },
-    { "EVP_PKEY_free",       "(I)V",          (void*)NativeCrypto_EVP_PKEY_free },
-    { "EVP_new",             "()I",           (void*)NativeCrypto_EVP_new },
-    { "EVP_free",            "(I)V",          (void*)NativeCrypto_EVP_free },
-    { "EVP_DigestFinal",     "(I[BI)I",       (void*)NativeCrypto_EVP_DigestFinal },
-    { "EVP_DigestInit",      "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_DigestInit },
-    { "EVP_DigestBlockSize", "(I)I",          (void*)NativeCrypto_EVP_DigestBlockSize },
-    { "EVP_DigestSize",      "(I)I",          (void*)NativeCrypto_EVP_DigestSize },
-    { "EVP_DigestUpdate",    "(I[BII)V",      (void*)NativeCrypto_EVP_DigestUpdate },
-    { "EVP_VerifyInit",      "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_VerifyInit },
-    { "EVP_VerifyUpdate",    "(I[BII)V",      (void*)NativeCrypto_EVP_VerifyUpdate },
-    { "EVP_VerifyFinal",     "(I[BIII)I",     (void*)NativeCrypto_EVP_VerifyFinal },
-    { "SSL_CTX_new",         "()I",           (void*)NativeCrypto_SSL_CTX_new },
-    { "SSL_CTX_get_ciphers", "(I)[Ljava/lang/String;", (void*)NativeCrypto_SSL_CTX_get_ciphers},
-    { "SSL_CTX_free",        "(I)V",          (void*)NativeCrypto_SSL_CTX_free },
-    { "SSL_new",             "(ILjava/lang/String;Ljava/lang/String;[B)I", (void*)NativeCrypto_SSL_new},
-    { "SSL_get_mode",        "(I)J",          (void*)NativeCrypto_SSL_get_mode },
-    { "SSL_set_mode",        "(IJ)J",         (void*)NativeCrypto_SSL_set_mode },
-    { "SSL_clear_mode",      "(IJ)J",         (void*)NativeCrypto_SSL_clear_mode },
-    { "SSL_get_options",     "(I)J",          (void*)NativeCrypto_SSL_get_options },
-    { "SSL_set_options",     "(IJ)J",         (void*)NativeCrypto_SSL_set_options },
-    { "SSL_clear_options",   "(IJ)J",         (void*)NativeCrypto_SSL_clear_options },
-    { "SSL_get_ciphers",     "(I)[Ljava/lang/String;", (void*)NativeCrypto_SSL_get_ciphers },
-    { "SSL_set_cipher_list", "(ILjava/lang/String;)V", (void*)NativeCrypto_SSL_set_cipher_list },
-    { "SSL_set_verify",      "(II)V",         (void*)NativeCrypto_SSL_set_verify},
-    { "SSL_set_session",     "(II)V",         (void*)NativeCrypto_SSL_set_session },
-    { "SSL_set_session_creation_enabled", "(IZ)V", (void*)NativeCrypto_SSL_set_session_creation_enabled },
-    { "SSL_do_handshake",    "(ILjava/net/Socket;Lorg/apache/harmony/xnet/provider/jsse/NativeCrypto$CertificateChainVerifier;Lorg/apache/harmony/xnet/provider/jsse/NativeCrypto$HandshakeCompletedCallback;IZ)I",(void*)NativeCrypto_SSL_do_handshake},
-    { "SSL_get_certificate", "(I)[[B",        (void*)NativeCrypto_SSL_get_certificate},
-    { "SSL_free",            "(I)V",          (void*)NativeCrypto_SSL_free},
-    { "SSL_SESSION_session_id", "(I)[B",      (void*)NativeCrypto_SSL_SESSION_session_id },
-    { "SSL_SESSION_get_peer_cert_chain", "(II)[[B", (void*)NativeCrypto_SSL_SESSION_get_peer_cert_chain },
-    { "SSL_SESSION_get_time", "(I)J",         (void*)NativeCrypto_SSL_SESSION_get_time },
-    { "SSL_SESSION_get_version", "(I)Ljava/lang/String;", (void*)NativeCrypto_SSL_SESSION_get_version },
-    { "SSL_SESSION_cipher",  "(I)Ljava/lang/String;", (void*)NativeCrypto_SSL_SESSION_cipher },
-    { "SSL_SESSION_free",    "(I)V",          (void*)NativeCrypto_SSL_SESSION_free },
-    { "i2d_SSL_SESSION",     "(I)[B",         (void*)NativeCrypto_i2d_SSL_SESSION },
-    { "d2i_SSL_SESSION",     "([BI)I",        (void*)NativeCrypto_d2i_SSL_SESSION },
-};
-
-// ============================================================================
-// === OpenSSL-related helper stuff begins here. ==============================
-// ============================================================================
-
-/**
  * Helper function which does the actual reading. The Java layer guarantees that
  * at most one thread will enter this function at any given time.
  *
@@ -2089,6 +2042,84 @@
 }
 
 /**
+ * OpenSSL read function (1): only one chunk is read (returned as jint).
+ */
+static jint NativeCrypto_SSL_read_byte(JNIEnv* env, jclass, jint ssl_address, jint timeout)
+{
+    SSL* ssl = to_SSL(env, ssl_address, true);
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_read_byte timeout=%d", ssl, timeout);
+    if (ssl == NULL) {
+        return 0;
+    }
+
+    unsigned char byteRead;
+    int returnCode = 0;
+    int errorCode = 0;
+
+    int ret = sslRead(env, ssl, (char *) &byteRead, 1, &returnCode, &errorCode, timeout);
+
+    int result;
+    switch (ret) {
+        case THROW_EXCEPTION:
+            // See sslRead() regarding improper failure to handle normal cases.
+            throwSSLExceptionWithSslErrors(env, returnCode, errorCode, "Read error");
+            result = -1;
+            break;
+        case THROW_SOCKETTIMEOUTEXCEPTION:
+            throwSocketTimeoutException(env, "Read timed out");
+            result = -1;
+            break;
+        case -1:
+            // Propagate EOF upwards.
+            result = -1;
+            break;
+        default:
+            // Return the actual char read, make sure it stays 8 bits wide.
+            result = ((jint) byteRead) & 0xFF;
+            break;
+    }
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_read_byte => %d", ssl, result);
+    return result;
+}
+
+/**
+ * OpenSSL read function (2): read into buffer at offset n chunks.
+ * Returns 1 (success) or value <= 0 (failure).
+ */
+static jint NativeCrypto_SSL_read(JNIEnv* env, jclass, jint ssl_address, jbyteArray dest, jint offset, jint len, jint timeout)
+{
+    SSL* ssl = to_SSL(env, ssl_address, true);
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_read dest=%p offset=%d len=%d timeout=%d", ssl, dest, offset, len, timeout);
+    if (ssl == NULL) {
+        return 0;
+    }
+
+    jbyte* bytes = env->GetByteArrayElements(dest, NULL);
+    int returnCode = 0;
+    int errorCode = 0;
+
+    int ret = sslRead(env, ssl, (char*) (bytes + offset), len, &returnCode, &errorCode, timeout);
+
+    env->ReleaseByteArrayElements(dest, bytes, 0);
+
+    int result;
+    if (ret == THROW_EXCEPTION) {
+        // See sslRead() regarding improper failure to handle normal cases.
+        throwSSLExceptionWithSslErrors(env, returnCode, errorCode,
+                "Read error");
+        result = -1;
+    } else if(ret == THROW_SOCKETTIMEOUTEXCEPTION) {
+        throwSocketTimeoutException(env, "Read timed out");
+        result = -1;
+    } else {
+        result = ret;
+    }
+
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_read => %d", ssl, result);
+    return result;
+}
+
+/**
  * Helper function which does the actual writing. The Java layer guarantees that
  * at most one thread will enter this function at any given time.
  *
@@ -2213,198 +2244,12 @@
 }
 
 /**
- * Helper function that creates an RSA public key from two buffers containing
- * the big-endian bit representation of the modulus and the public exponent.
- *
- * @param mod The data of the modulus
- * @param modLen The length of the modulus data
- * @param exp The data of the exponent
- * @param expLen The length of the exponent data
- *
- * @return A pointer to the new RSA structure, or NULL on error
- */
-static RSA* rsaCreateKey(unsigned char* mod, int modLen, unsigned char* exp, int expLen) {
-    // LOGD("Entering rsaCreateKey()");
-
-    RSA* rsa = RSA_new();
-
-    rsa->n = BN_bin2bn((unsigned char*) mod, modLen, NULL);
-    rsa->e = BN_bin2bn((unsigned char*) exp, expLen, NULL);
-
-    if (rsa->n == NULL || rsa->e == NULL) {
-        RSA_free(rsa);
-        return NULL;
-    }
-
-    return rsa;
-}
-
-/**
- * Helper function that frees an RSA key. Just calls the corresponding OpenSSL
- * function.
- *
- * @param rsa The pointer to the new RSA structure to free.
- */
-static void rsaFreeKey(RSA* rsa) {
-    // LOGD("Entering rsaFreeKey()");
-
-    if (rsa != NULL) {
-        RSA_free(rsa);
-    }
-}
-
-/**
- * Helper function that verifies a given RSA signature for a given message.
- *
- * @param msg The message to verify
- * @param msgLen The length of the message
- * @param sig The signature to verify
- * @param sigLen The length of the signature
- * @param algorithm The name of the hash/sign algorithm to use, e.g. "RSA-SHA1"
- * @param rsa The RSA public key to use
- *
- * @return 1 on success, 0 on failure, -1 on error (check SSL errors then)
- *
- */
-static int rsaVerify(unsigned char* msg, unsigned int msgLen, unsigned char* sig,
-                     unsigned int sigLen, char* algorithm, RSA* rsa) {
-
-    // LOGD("Entering rsaVerify(%x, %d, %x, %d, %s, %x)", msg, msgLen, sig, sigLen, algorithm, rsa);
-
-    int result = -1;
-
-    EVP_PKEY* key = EVP_PKEY_new();
-    EVP_PKEY_set1_RSA(key, rsa);
-
-    const EVP_MD *type = EVP_get_digestbyname(algorithm);
-    if (type == NULL) {
-        goto cleanup;
-    }
-
-    EVP_MD_CTX ctx;
-
-    EVP_MD_CTX_init(&ctx);
-    if (EVP_VerifyInit_ex(&ctx, type, NULL) == 0) {
-        goto cleanup;
-    }
-
-    EVP_VerifyUpdate(&ctx, msg, msgLen);
-    result = EVP_VerifyFinal(&ctx, sig, sigLen, key);
-    EVP_MD_CTX_cleanup(&ctx);
-
-    cleanup:
-
-    if (key != NULL) {
-        EVP_PKEY_free(key);
-    }
-
-    return result;
-}
-
-// ============================================================================
-// === OpenSSL-related helper stuff ends here. JNI glue follows. ==============
-// ============================================================================
-
-static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession(JNIEnv* env, jclass,
-        jint ssl_address)
-{
-    SSL* ssl = getSslPointer(env, ssl_address, true);
-    JNI_TRACE("ssl=%p OpenSSLSocketImpl_getsslsession", ssl);
-    if (ssl == NULL) {
-      JNI_TRACE("ssl=%p OpenSSLSocketImpl_getsslsession => NULL", ssl);
-      return NULL;
-    }
-    SSL_SESSION* ssl_session = SSL_get1_session(ssl);
-    JNI_TRACE("ssl=%p OpenSSLSocketImpl_getsslsession => ssl_session=%p", ssl, ssl_session);
-    return (jint) ssl_session;
-}
-
-/**
- * OpenSSL read function (1): only one chunk is read (returned as jint).
- */
-static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read(JNIEnv* env, jclass, jint ssl_address, jint timeout)
-{
-    SSL* ssl = getSslPointer(env, ssl_address, true);
-    JNI_TRACE("ssl=%p OpenSSLSocketImpl_readba timeout=%d", ssl, timeout);
-    if (ssl == NULL) {
-        return 0;
-    }
-
-    unsigned char byteRead;
-    int returnCode = 0;
-    int errorCode = 0;
-
-    int ret = sslRead(env, ssl, (char *) &byteRead, 1, &returnCode, &errorCode, timeout);
-
-    int result;
-    switch (ret) {
-        case THROW_EXCEPTION:
-            // See sslRead() regarding improper failure to handle normal cases.
-            throwSSLExceptionWithSslErrors(env, returnCode, errorCode,
-                    "Read error");
-            result = -1;
-            break;
-        case THROW_SOCKETTIMEOUTEXCEPTION:
-            throwSocketTimeoutException(env, "Read timed out");
-            result = -1;
-            break;
-        case -1:
-            // Propagate EOF upwards.
-            result = -1;
-            break;
-        default:
-            // Return the actual char read, make sure it stays 8 bits wide.
-            result = ((jint) byteRead) & 0xFF;
-            break;
-    }
-    JNI_TRACE("ssl=%p OpenSSLSocketImpl_read => %d", ssl, result);
-    return result;
-}
-
-/**
- * OpenSSL read function (2): read into buffer at offset n chunks.
- * Returns 1 (success) or value <= 0 (failure).
- */
-static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba(JNIEnv* env, jclass, jint ssl_address, jbyteArray dest, jint offset, jint len, jint timeout)
-{
-    SSL* ssl = getSslPointer(env, ssl_address, true);
-    JNI_TRACE("ssl=%p OpenSSLSocketImpl_readba dest=%p offset=%d len=%d timeout=%d", ssl, dest, offset, len, timeout);
-    if (ssl == NULL) {
-        return 0;
-    }
-
-    jbyte* bytes = env->GetByteArrayElements(dest, NULL);
-    int returnCode = 0;
-    int errorCode = 0;
-
-    int ret = sslRead(env, ssl, (char*) (bytes + offset), len, &returnCode, &errorCode, timeout);
-
-    env->ReleaseByteArrayElements(dest, bytes, 0);
-
-    int result;
-    if (ret == THROW_EXCEPTION) {
-        // See sslRead() regarding improper failure to handle normal cases.
-        throwSSLExceptionWithSslErrors(env, returnCode, errorCode,
-                "Read error");
-        result = -1;
-    } else if(ret == THROW_SOCKETTIMEOUTEXCEPTION) {
-        throwSocketTimeoutException(env, "Read timed out");
-        result = -1;
-    } else {
-        result = ret;
-    }
-
-    JNI_TRACE("ssl=%p OpenSSLSocketImpl_readba => %d", ssl, result);
-    return result;
-}
-
-/**
  * OpenSSL write function (1): only one chunk is written.
  */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write(JNIEnv* env, jclass, jint ssl_address, jint b)
+static void NativeCrypto_SSL_write_byte(JNIEnv* env, jclass, jint ssl_address, jint b)
 {
-    SSL* ssl = getSslPointer(env, ssl_address, true);
-    JNI_TRACE("ssl=%p OpenSSLSocketImpl_write b=%d", ssl, b);
+    SSL* ssl = to_SSL(env, ssl_address, true);
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_write_byte b=%d", ssl, b);
     if (ssl == NULL) {
         return;
     }
@@ -2426,21 +2271,19 @@
 /**
  * OpenSSL write function (2): write into buffer at offset n chunks.
  */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba(JNIEnv* env, jclass,
+static void NativeCrypto_SSL_write(JNIEnv* env, jclass,
         jint ssl_address, jbyteArray dest, jint offset, jint len)
 {
-    SSL* ssl = getSslPointer(env, ssl_address, true);
-    JNI_TRACE("ssl=%p OpenSSLSocketImpl_writeba dest=%p offset=%d len=%d", ssl, dest, offset, len);
+    SSL* ssl = to_SSL(env, ssl_address, true);
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_write dest=%p offset=%d len=%d", ssl, dest, offset, len);
     if (ssl == NULL) {
         return;
     }
 
-    jbyte* bytes = env->GetByteArrayElements(dest, NULL);
+    ScopedByteArray bytes(env, dest);
     int returnCode = 0;
     int errorCode = 0;
-    int ret = sslWrite(env, ssl, (const char *) (bytes + offset), len, &returnCode, &errorCode);
-
-    env->ReleaseByteArrayElements(dest, bytes, 0);
+    int ret = sslWrite(env, ssl, (const char *) (bytes.bytes() + offset), len, &returnCode, &errorCode);
 
     if (ret == THROW_EXCEPTION) {
         // See sslWrite() regarding improper failure to handle normal cases.
@@ -2454,10 +2297,10 @@
 /**
  * Interrupt any pending IO before closing the socket.
  */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt(
+static void NativeCrypto_SSL_interrupt(
         JNIEnv* env, jclass, jint ssl_address) {
-    SSL* ssl = getSslPointer(env, ssl_address, false);
-    JNI_TRACE("ssl=%p OpenSSLSocketImpl_interrupt", ssl);
+    SSL* ssl = to_SSL(env, ssl_address, false);
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_interrupt", ssl);
     if (ssl == NULL) {
         return;
     }
@@ -2479,10 +2322,10 @@
 /**
  * OpenSSL close SSL socket function.
  */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close(
+static void NativeCrypto_SSL_shutdown(
         JNIEnv* env, jclass, jint ssl_address) {
-    SSL* ssl = getSslPointer(env, ssl_address, false);
-    JNI_TRACE("ssl=%p OpenSSLSocketImpl_close", ssl);
+    SSL* ssl = to_SSL(env, ssl_address, false);
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_shutdown", ssl);
     if (ssl == NULL) {
         return;
     }
@@ -2490,7 +2333,7 @@
      * Try to make socket blocking again. OpenSSL literature recommends this.
      */
     int fd = SSL_get_fd(ssl);
-    JNI_TRACE("ssl=%p OpenSSLSocketImpl_close s=%d", ssl, fd);
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_shutdown s=%d", ssl, fd);
     if (fd != -1) {
         int mode = fcntl(fd, F_GETFL);
         if (mode == -1 || fcntl(fd, F_SETFL, mode & ~O_NONBLOCK) == -1) {
@@ -2536,99 +2379,243 @@
 }
 
 /**
- * Verifies an RSA signature.
+ * public static native void SSL_free(int ssl);
  */
-static int org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_verifysignature(JNIEnv* env, jclass clazz,
-        jbyteArray msg, jbyteArray sig, jstring algorithm, jbyteArray mod, jbyteArray exp) {
-
-    JNI_TRACE("OpenSSLSocketImpl_verifysignature msg=%p sig=%p algorithm=%p mod=%p exp%p",
-             msg, sig, algorithm, mod, exp);
-
-    if (msg == NULL || sig == NULL || algorithm == NULL || mod == NULL || exp == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        JNI_TRACE("OpenSSLSocketImpl_verifysignature => -1");
-        return -1;
+static void NativeCrypto_SSL_free(JNIEnv* env, jclass, jint ssl_address)
+{
+    SSL* ssl = to_SSL(env, ssl_address, true);
+    JNI_TRACE("ssl=%p NativeCrypto_SSL_free", ssl);
+    if (ssl == NULL) {
+      return;
     }
+    AppData* appData = (AppData*) SSL_get_app_data(ssl);
+    SSL_set_app_data(ssl, NULL);
+    delete appData;
+    SSL_free(ssl);
+}
 
-    int result = -1;
-
-    jbyte* msgBytes = env->GetByteArrayElements(msg, NULL);
-    jint msgLength = env->GetArrayLength(msg);
-
-    jbyte* sigBytes = env->GetByteArrayElements(sig, NULL);
-    jint sigLength = env->GetArrayLength(sig);
-
-    jbyte* modBytes = env->GetByteArrayElements(mod, NULL);
-    jint modLength = env->GetArrayLength(mod);
-
-    jbyte* expBytes = env->GetByteArrayElements(exp, NULL);
-    jint expLength = env->GetArrayLength(exp);
-
-    const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL);
-    JNI_TRACE("OpenSSLSocketImpl_verifysignature algorithmChars=%s", algorithmChars);
-
-    RSA* rsa = rsaCreateKey((unsigned char*) modBytes, modLength, (unsigned char*) expBytes, expLength);
-    if (rsa != NULL) {
-        result = rsaVerify((unsigned char*) msgBytes, msgLength, (unsigned char*) sigBytes, sigLength,
-                (char*) algorithmChars, rsa);
-        rsaFreeKey(rsa);
+/**
+ * Gets and returns in a byte array the ID of the actual SSL session.
+ */
+static jbyteArray NativeCrypto_SSL_SESSION_session_id(JNIEnv* env, jclass, jint ssl_session_address) {
+    SSL_SESSION* ssl_session = to_SSL_SESSION(ssl_session_address);
+    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_session_id", ssl_session);
+    jbyteArray result = env->NewByteArray(ssl_session->session_id_length);
+    if (result != NULL) {
+        jbyte* src = reinterpret_cast<jbyte*>(ssl_session->session_id);
+        env->SetByteArrayRegion(result, 0, ssl_session->session_id_length, src);
     }
-
-    env->ReleaseStringUTFChars(algorithm, algorithmChars);
-
-    env->ReleaseByteArrayElements(exp, expBytes, JNI_ABORT);
-    env->ReleaseByteArrayElements(mod, modBytes, JNI_ABORT);
-    env->ReleaseByteArrayElements(sig, sigBytes, JNI_ABORT);
-    env->ReleaseByteArrayElements(msg, msgBytes, JNI_ABORT);
-
-    if (result == -1) {
-        int error = ERR_get_error();
-        if (error != 0) {
-            char message[50];
-            ERR_error_string_n(error, message, sizeof(message));
-            jniThrowRuntimeException(env, message);
-        } else {
-            jniThrowRuntimeException(env, "Internal error during verification");
-        }
-        freeSslErrorState();
-    }
-
-    JNI_TRACE("OpenSSLSocketImpl_verifysignature => %d", result);
+    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_session_id => %p session_id_length=%d",
+             ssl_session, result, ssl_session->session_id_length);
     return result;
 }
 
-static JNINativeMethod sSocketImplMethods[] =
+/**
+ * Our implementation of what might be considered
+ * SSL_SESSION_get_peer_cert_chain
+ *
+ */
+// TODO move to jsse.patch
+static STACK_OF(X509)* SSL_SESSION_get_peer_cert_chain(SSL_CTX* ssl_ctx, SSL_SESSION* ssl_session) {
+    SSL* ssl = SSL_new(ssl_ctx);
+    if (ssl == NULL) {
+        return NULL;
+    }
+    SSL_set_session(ssl, ssl_session);
+    STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl);
+    SSL_free(ssl);
+    return chain;
+}
+
+// Fills a byte[][] with the peer certificates in the chain.
+static jobjectArray NativeCrypto_SSL_SESSION_get_peer_cert_chain(JNIEnv* env,
+        jclass, jint ssl_ctx_address, jint ssl_session_address)
 {
-    {"nativeread", "(II)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read},
-    {"nativeread", "(I[BIII)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba},
-    {"nativewrite", "(II)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write},
-    {"nativewrite", "(I[BII)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba},
-    {"nativeinterrupt", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt},
-    {"nativeclose", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close},
-    {"nativeverifysignature", "([B[BLjava/lang/String;[B[B)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_verifysignature},
+    SSL_CTX* ssl_ctx = to_SSL_CTX(ssl_ctx_address);
+    SSL_SESSION* ssl_session = to_SSL_SESSION(ssl_session_address);
+    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_peer_cert_chain ssl_ctx=%p", ssl_session, ssl_ctx);
+    if (ssl_ctx == NULL) {
+        jniThrowNullPointerException(env, "SSL_CTX is null");
+        JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_peer_cert_chain => NULL", ssl_session);
+        return NULL;
+    }
+    STACK_OF(X509)* chain = SSL_SESSION_get_peer_cert_chain(ssl_ctx, ssl_session);
+    jobjectArray objectArray = getCertificateBytes(env, chain);
+    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_peer_cert_chain => %p", ssl_session, objectArray);
+    return objectArray;
+}
+
+/**
+ * Gets and returns in a long integer the creation's time of the
+ * actual SSL session.
+ */
+static jlong NativeCrypto_SSL_SESSION_get_time(JNIEnv* env, jclass, jint ssl_session_address) {
+    SSL_SESSION* ssl_session = to_SSL_SESSION(ssl_session_address);
+    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_time", ssl_session);
+    jlong result = SSL_SESSION_get_time(ssl_session); // must be jlong, not long or *1000 will overflow
+    result *= 1000; // OpenSSL uses seconds, Java uses milliseconds.
+    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_time => %lld", ssl_session, result);
+    return result;
+}
+
+/**
+ * Our implementation of what might be considered
+ * SSL_SESSION_get_version, based on SSL_get_version.
+ * See get_ssl_version above.
+ */
+// TODO move to jsse.patch
+static const char* SSL_SESSION_get_version(SSL_SESSION* ssl_session) {
+  return get_ssl_version(ssl_session->ssl_version);
+}
+
+/**
+ * Gets and returns in a string the version of the SSL protocol. If it
+ * returns the string "unknown" it means that no connection is established.
+ */
+static jstring NativeCrypto_SSL_SESSION_get_version(JNIEnv* env, jclass, jint ssl_session_address) {
+    SSL_SESSION* ssl_session = to_SSL_SESSION(ssl_session_address);
+    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_version", ssl_session);
+    const char* protocol = SSL_SESSION_get_version(ssl_session);
+    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_get_version => %s", ssl_session, protocol);
+    return env->NewStringUTF(protocol);
+}
+
+/**
+ * Gets and returns in a string the set of ciphers the actual SSL session uses.
+ */
+static jstring NativeCrypto_SSL_SESSION_cipher(JNIEnv* env, jclass, jint ssl_session_address) {
+    SSL_SESSION* ssl_session = to_SSL_SESSION(ssl_session_address);
+    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_cipher", ssl_session);
+    const SSL_CIPHER* cipher = ssl_session->cipher;
+    const char* name = SSL_CIPHER_get_name(cipher);
+    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_cipher => %s", ssl_session, name);
+    return env->NewStringUTF(name);
+}
+
+/**
+ * Frees the SSL session.
+ */
+static void NativeCrypto_SSL_SESSION_free(JNIEnv* env, jclass, jint ssl_session_address) {
+    SSL_SESSION* ssl_session = to_SSL_SESSION(ssl_session_address);
+    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_free", ssl_session);
+    SSL_SESSION_free(ssl_session);
+}
+
+
+/**
+ * Serializes the native state of the session (ID, cipher, and keys but
+ * not certificates). Returns a byte[] containing the DER-encoded state.
+ * See apache mod_ssl.
+ */
+static jbyteArray NativeCrypto_i2d_SSL_SESSION(JNIEnv* env, jclass, jint ssl_session_address) {
+    SSL_SESSION* ssl_session = to_SSL_SESSION(ssl_session_address);
+    JNI_TRACE("ssl_session=%p NativeCrypto_i2d_SSL_SESSION", ssl_session);
+    if (ssl_session == NULL) {
+        JNI_TRACE("ssl_session=%p NativeCrypto_i2d_SSL_SESSION => NULL", ssl_session);
+        return NULL;
+    }
+
+    // Compute the size of the DER data
+    int size = i2d_SSL_SESSION(ssl_session, NULL);
+    if (size == 0) {
+        JNI_TRACE("ssl_session=%p NativeCrypto_i2d_SSL_SESSION => NULL", ssl_session);
+        return NULL;
+    }
+
+    jbyteArray bytes = env->NewByteArray(size);
+    if (bytes != NULL) {
+        jbyte* tmp = env->GetByteArrayElements(bytes, NULL);
+        unsigned char* ucp = reinterpret_cast<unsigned char*>(tmp);
+        i2d_SSL_SESSION(ssl_session, &ucp);
+        env->ReleaseByteArrayElements(bytes, tmp, 0);
+    }
+
+    JNI_TRACE("ssl_session=%p NativeCrypto_i2d_SSL_SESSION => size=%d", ssl_session, size);
+    return bytes;
+}
+
+/**
+ * Deserialize the session.
+ */
+static jint NativeCrypto_d2i_SSL_SESSION(JNIEnv* env, jclass, jbyteArray bytes, jint size) {
+    JNI_TRACE("NativeCrypto_d2i_SSL_SESSION bytes=%p size=%d", bytes, size);
+    if (bytes == NULL) {
+        JNI_TRACE("NativeCrypto_d2i_SSL_SESSION => 0");
+        return 0;
+    }
+
+    ScopedByteArray tmp(env, bytes);
+    const unsigned char* ucp = reinterpret_cast<const unsigned char*>(tmp.bytes());
+    SSL_SESSION* ssl_session = d2i_SSL_SESSION(NULL, &ucp, size);
+
+    JNI_TRACE("NativeCrypto_d2i_SSL_SESSION => %p", ssl_session);
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(ssl_session));
+}
+
+/*
+ * Defines the mapping from Java methods and their signatures
+ * to native functions. Order is (1) Java name, (2) signature,
+ * (3) pointer to C function.
+ */
+static JNINativeMethod sNativeCryptoMethods[] = {
+    { "clinit",              "()V",           (void*)NativeCrypto_clinit},
+    { "EVP_PKEY_new_DSA",    "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_DSA },
+    { "EVP_PKEY_new_RSA",    "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_RSA },
+    { "EVP_PKEY_free",       "(I)V",          (void*)NativeCrypto_EVP_PKEY_free },
+    { "EVP_new",             "()I",           (void*)NativeCrypto_EVP_new },
+    { "EVP_free",            "(I)V",          (void*)NativeCrypto_EVP_free },
+    { "EVP_DigestFinal",     "(I[BI)I",       (void*)NativeCrypto_EVP_DigestFinal },
+    { "EVP_DigestInit",      "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_DigestInit },
+    { "EVP_DigestBlockSize", "(I)I",          (void*)NativeCrypto_EVP_DigestBlockSize },
+    { "EVP_DigestSize",      "(I)I",          (void*)NativeCrypto_EVP_DigestSize },
+    { "EVP_DigestUpdate",    "(I[BII)V",      (void*)NativeCrypto_EVP_DigestUpdate },
+    { "EVP_VerifyInit",      "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_VerifyInit },
+    { "EVP_VerifyUpdate",    "(I[BII)V",      (void*)NativeCrypto_EVP_VerifyUpdate },
+    { "EVP_VerifyFinal",     "(I[BIII)I",     (void*)NativeCrypto_EVP_VerifyFinal },
+    { "verifySignature",     "([B[BLjava/lang/String;[B[B)I", (void*)NativeCrypto_verifysignature},
+    { "SSL_CTX_new",         "()I",           (void*)NativeCrypto_SSL_CTX_new },
+    { "SSL_CTX_get_ciphers", "(I)[Ljava/lang/String;", (void*)NativeCrypto_SSL_CTX_get_ciphers},
+    { "SSL_CTX_free",        "(I)V",          (void*)NativeCrypto_SSL_CTX_free },
+    { "SSL_new",             "(ILjava/lang/String;Ljava/lang/String;[B)I", (void*)NativeCrypto_SSL_new},
+    { "SSL_get_mode",        "(I)J",          (void*)NativeCrypto_SSL_get_mode },
+    { "SSL_set_mode",        "(IJ)J",         (void*)NativeCrypto_SSL_set_mode },
+    { "SSL_clear_mode",      "(IJ)J",         (void*)NativeCrypto_SSL_clear_mode },
+    { "SSL_get_options",     "(I)J",          (void*)NativeCrypto_SSL_get_options },
+    { "SSL_set_options",     "(IJ)J",         (void*)NativeCrypto_SSL_set_options },
+    { "SSL_clear_options",   "(IJ)J",         (void*)NativeCrypto_SSL_clear_options },
+    { "SSL_get_ciphers",     "(I)[Ljava/lang/String;", (void*)NativeCrypto_SSL_get_ciphers },
+    { "SSL_set_cipher_list", "(ILjava/lang/String;)V", (void*)NativeCrypto_SSL_set_cipher_list },
+    { "SSL_set_verify",      "(II)V",         (void*)NativeCrypto_SSL_set_verify},
+    { "SSL_set_session",     "(II)V",         (void*)NativeCrypto_SSL_set_session },
+    { "SSL_set_session_creation_enabled", "(IZ)V", (void*)NativeCrypto_SSL_set_session_creation_enabled },
+    { "SSL_do_handshake",    "(ILjava/net/Socket;Lorg/apache/harmony/xnet/provider/jsse/NativeCrypto$CertificateChainVerifier;Lorg/apache/harmony/xnet/provider/jsse/NativeCrypto$HandshakeCompletedCallback;IZ)I",(void*)NativeCrypto_SSL_do_handshake},
+    { "SSL_get_certificate", "(I)[[B",        (void*)NativeCrypto_SSL_get_certificate},
+    { "SSL_read_byte",       "(II)I",         (void*)NativeCrypto_SSL_read_byte},
+    { "SSL_read",            "(I[BIII)I",     (void*)NativeCrypto_SSL_read},
+    { "SSL_write_byte",      "(II)V",         (void*)NativeCrypto_SSL_write_byte},
+    { "SSL_write",           "(I[BII)V",      (void*)NativeCrypto_SSL_write},
+    { "SSL_interrupt",       "(I)V",          (void*)NativeCrypto_SSL_interrupt},
+    { "SSL_shutdown",        "(I)V",          (void*)NativeCrypto_SSL_shutdown},
+    { "SSL_free",            "(I)V",          (void*)NativeCrypto_SSL_free},
+    { "SSL_SESSION_session_id", "(I)[B",      (void*)NativeCrypto_SSL_SESSION_session_id },
+    { "SSL_SESSION_get_peer_cert_chain", "(II)[[B", (void*)NativeCrypto_SSL_SESSION_get_peer_cert_chain },
+    { "SSL_SESSION_get_time", "(I)J",         (void*)NativeCrypto_SSL_SESSION_get_time },
+    { "SSL_SESSION_get_version", "(I)Ljava/lang/String;", (void*)NativeCrypto_SSL_SESSION_get_version },
+    { "SSL_SESSION_cipher",  "(I)Ljava/lang/String;", (void*)NativeCrypto_SSL_SESSION_cipher },
+    { "SSL_SESSION_free",    "(I)V",          (void*)NativeCrypto_SSL_SESSION_free },
+    { "i2d_SSL_SESSION",     "(I)[B",         (void*)NativeCrypto_i2d_SSL_SESSION },
+    { "d2i_SSL_SESSION",     "([BI)I",        (void*)NativeCrypto_d2i_SSL_SESSION },
 };
 
-typedef struct {
-    const char*            name;
-    const JNINativeMethod* methods;
-    jint                   nMethods;
-} JNINativeClass;
-
-static JNINativeClass sClasses[] = {
-    { "org/apache/harmony/xnet/provider/jsse/NativeCrypto", sNativeCryptoMethods, NELEM(sNativeCryptoMethods) },
-    { "org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl", sSocketImplMethods, NELEM(sSocketImplMethods) },
-};
 int register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(JNIEnv* env) {
     JNI_TRACE("register_org_apache_harmony_xnet_provider_jsse_NativeCrypto");
-    // Register org.apache.harmony.xnet.provider.jsse.* methods
-    for (int i = 0; i < NELEM(sClasses); i++) {
-        int result = jniRegisterNativeMethods(env,
-                                              sClasses[i].name,
-                                              sClasses[i].methods,
-                                              sClasses[i].nMethods);
-        if (result == -1) {
-          return -1;
-        }
+    // Register org.apache.harmony.xnet.provider.jsse.NativeCrypto methods
+    int result = jniRegisterNativeMethods(env,
+                                          "org/apache/harmony/xnet/provider/jsse/NativeCrypto",
+                                          sNativeCryptoMethods,
+                                          NELEM(sNativeCryptoMethods));
+    if (result == -1) {
+        return -1;
     }
 
     // java.net.Socket