summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--luni/src/main/java/java/net/MulticastSocket.java3
-rw-r--r--luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnection.java2
-rw-r--r--luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java6
-rw-r--r--luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp735
-rw-r--r--luni/src/test/java/java/net/URLConnectionTest.java6
-rw-r--r--luni/src/test/java/javax/net/ssl/SSLContextTest.java215
-rw-r--r--luni/src/test/java/javax/net/ssl/SSLSessionContextTest.java55
-rw-r--r--luni/src/test/java/javax/net/ssl/SSLSessionTest.java107
-rw-r--r--luni/src/test/java/javax/net/ssl/SSLSocketFactoryTest.java47
-rw-r--r--luni/src/test/java/javax/net/ssl/SSLSocketTest.java420
-rw-r--r--luni/src/test/java/tests/AllTests.java3
-rwxr-xr-xrun-core-tests4
-rw-r--r--support/src/test/java/javax/net/ssl/StandardNames.java112
-rw-r--r--support/src/test/java/javax/net/ssl/TestSSLContext.java226
-rw-r--r--support/src/test/java/javax/net/ssl/TestSSLSessions.java68
-rw-r--r--support/src/test/java/javax/net/ssl/TestSSLSocketPair.java82
-rw-r--r--x-net/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java37
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java10
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java7
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java151
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java65
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java33
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java428
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java11
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java4
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java15
-rw-r--r--x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp1728
-rw-r--r--x-net/src/test/java/tests/api/javax/net/ssl/SSLSessionTest.java15
28 files changed, 2393 insertions, 2202 deletions
diff --git a/luni/src/main/java/java/net/MulticastSocket.java b/luni/src/main/java/java/net/MulticastSocket.java
index 22cefb9..7815b01 100644
--- a/luni/src/main/java/java/net/MulticastSocket.java
+++ b/luni/src/main/java/java/net/MulticastSocket.java
@@ -31,9 +31,6 @@ import org.apache.harmony.luni.util.Msg;
* @see DatagramSocket
*/
public class MulticastSocket extends DatagramSocket {
-
- final static int SO_REUSEPORT = 512;
-
private InetAddress interfaceSet;
/**
diff --git a/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnection.java b/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnection.java
index 3491ccc..2ef1a9b 100644
--- a/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnection.java
+++ b/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnection.java
@@ -160,8 +160,6 @@ public class HttpConnection {
int port = config.getHostPort();
// create the wrapper over connected socket
sslSocket = (SSLSocket) sslSocketFactory.createSocket(socket, hostName, port, true);
- sslSocket.setUseClientMode(true);
- sslSocket.startHandshake();
if (!hostnameVerifier.verify(hostName, sslSocket.getSession())) {
throw new IOException("Hostname '" + hostName + "' was not verified");
}
diff --git a/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java b/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java
index 7721cbb..d4f8e6f 100644
--- a/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java
+++ b/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java
@@ -158,12 +158,12 @@ public class PlainDatagramSocketImpl extends DatagramSocketImpl {
@Override
public int getTimeToLive() throws IOException {
- return getTTL() & 0xff;
+ return ((Integer) getOption(IP_MULTICAST_TTL)).intValue();
}
@Override
public byte getTTL() throws IOException {
- return ((Byte) getOption(IP_MULTICAST_TTL)).byteValue();
+ return (byte) getTimeToLive();
}
@Override
@@ -296,7 +296,7 @@ public class PlainDatagramSocketImpl extends DatagramSocketImpl {
@Override
public void setTTL(byte ttl) throws IOException {
- setTimeToLive(ttl);
+ setTimeToLive((int) ttl & 0xff); // Avoid sign extension.
}
@Override
diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
index 11c056d..ffb271a 100644
--- a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
+++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
@@ -123,7 +123,7 @@
#define JAVASOCKOPT_SO_REUSEADDR 4
#define JAVASOCKOPT_SO_KEEPALIVE 8
#define JAVASOCKOPT_IP_MULTICAST_IF 16
-#define JAVASOCKOPT_MCAST_TTL 17
+#define JAVASOCKOPT_MULTICAST_TTL 17
#define JAVASOCKOPT_IP_MULTICAST_LOOP 18
#define JAVASOCKOPT_MCAST_ADD_MEMBERSHIP 19
#define JAVASOCKOPT_MCAST_DROP_MEMBERSHIP 20
@@ -141,10 +141,6 @@
#define SOCKET_STEP_CHECK 20
#define SOCKET_STEP_DONE 30
-#define BROKEN_MULTICAST_IF 1
-#define BROKEN_MULTICAST_TTL 2
-#define BROKEN_TCP_NODELAY 4
-
#define SOCKET_CONNECT_STEP_START 0
#define SOCKET_CONNECT_STEP_CHECK 1
@@ -154,11 +150,7 @@
#define SOCKET_NOFLAGS 0
-// Local constants for getOrSetSocketOption
-#define SOCKOPT_GET 1
-#define SOCKOPT_SET 2
-
-struct CachedFields {
+static struct CachedFields {
jfieldID fd_descriptor;
jclass iaddr_class;
jmethodID iaddr_getbyaddress;
@@ -173,7 +165,6 @@ struct CachedFields {
jmethodID boolean_class_init;
jfieldID boolean_class_value;
jclass byte_class;
- jmethodID byte_class_init;
jfieldID byte_class_value;
jclass socketimpl_class;
jfieldID socketimpl_address;
@@ -308,7 +299,7 @@ static int getSocketAddressFamily(int socket) {
}
}
-jobject byteArrayToInetAddress(JNIEnv* env, jbyteArray byteArray) {
+static jobject byteArrayToInetAddress(JNIEnv* env, jbyteArray byteArray) {
if (byteArray == NULL) {
return NULL;
}
@@ -596,23 +587,6 @@ static jobject newJavaLangBoolean(JNIEnv * env, jint anInt) {
}
/**
- * Answer a new java.lang.Byte object.
- *
- * @param env pointer to the JNI library
- * @param anInt the Byte constructor argument
- *
- * @return the new Byte
- */
-static jobject newJavaLangByte(JNIEnv * env, jbyte val) {
- jclass tempClass;
- jmethodID tempMethod;
-
- tempClass = gCachedFields.byte_class;
- tempMethod = gCachedFields.byte_class_init;
- return env->NewObject(tempClass, tempMethod, val);
-}
-
-/**
* Answer a new java.lang.Integer object.
*
* @param env pointer to the JNI library
@@ -1143,7 +1117,7 @@ static const char *sockoptLevelToString(int level) {
*
* @note on internal failure, the errno variable will be set appropriately
*/
-static int getOrSetSocketOption(int action, int socket, int ipv4Option,
+static int getSocketOption(int socket, int ipv4Option,
int ipv6Option, void *optionValue, socklen_t *optionLength) {
int option;
int protocol;
@@ -1164,28 +1138,12 @@ static int getOrSetSocketOption(int action, int socket, int ipv4Option,
return -1;
}
- int ret;
- if (action == SOCKOPT_GET) {
- ret = getsockopt(socket, protocol, option, optionValue, optionLength);
+ int ret = getsockopt(socket, protocol, option, optionValue, optionLength);
#if LOG_SOCKOPT
- LOGI("getsockopt(%d, %s, %d, %p, [%d]) = %d %s",
+ LOGI("getsockopt(%d, %s, %d, %p, [%d]) = %d %s",
socket, sockoptLevelToString(protocol), option, optionValue,
*optionLength, ret, (ret == -1) ? strerror(errno) : "");
#endif
- } else if (action == SOCKOPT_SET) {
- ret = setsockopt(socket, protocol, option, optionValue, *optionLength);
-#if LOG_SOCKOPT
- LOGI("setsockopt(%d, %s, %d, [%d], %d) = %d %s",
- socket, sockoptLevelToString(protocol), option,
- // Note: this only works for integer options.
- // TODO: Use dvmPrintHexDump() to log non-integer options.
- *(int *)optionValue, *optionLength, ret,
- (ret == -1) ? strerror(errno) : "");
-#endif
- } else {
- errno = EINVAL;
- ret = -1;
- }
return ret;
}
@@ -1202,29 +1160,22 @@ static int getOrSetSocketOption(int action, int socket, int ipv4Option,
*/
static int interfaceIndexFromMulticastSocket(int socket) {
int family = getSocketAddressFamily(socket);
- int interfaceIndex;
- int result;
if (family == AF_INET) {
// IP_MULTICAST_IF returns a pointer to a struct ip_mreqn.
struct ip_mreqn tempRequest;
socklen_t requestLength = sizeof(tempRequest);
- result = getsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, &tempRequest,
- &requestLength);
- interfaceIndex = tempRequest.imr_ifindex;
+ int rc = getsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, &tempRequest, &requestLength);
+ return (rc == -1) ? -1 : tempRequest.imr_ifindex;
} else if (family == AF_INET6) {
// IPV6_MULTICAST_IF returns a pointer to an integer.
+ int interfaceIndex;
socklen_t requestLength = sizeof(interfaceIndex);
- result = getsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_IF,
- &interfaceIndex, &requestLength);
+ int rc = getsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_IF, &interfaceIndex, &requestLength);
+ return (rc == -1) ? -1 : interfaceIndex;
} else {
errno = EAFNOSUPPORT;
return -1;
}
-
- if (result == 0)
- return interfaceIndex;
- else
- return -1;
}
/**
@@ -1248,8 +1199,7 @@ static int interfaceIndexFromMulticastSocket(int socket) {
*
* @exception SocketException if an error occurs during the call
*/
-static void mcastAddDropMembership(JNIEnv *env, int handle, jobject optVal,
- int ignoreIF, int setSockOptVal) {
+static void mcastAddDropMembership(JNIEnv *env, int handle, jobject optVal, int setSockOptVal) {
struct sockaddr_storage sockaddrP;
int result;
// By default, let the system decide which interface to use.
@@ -1261,7 +1211,7 @@ static void mcastAddDropMembership(JNIEnv *env, int handle, jobject optVal,
* is passed in, only support IPv4 as obtaining an interface from an
* InetAddress is complex and should be done by the Java caller.
*/
- if (env->IsInstanceOf (optVal, gCachedFields.iaddr_class)) {
+ if (env->IsInstanceOf(optVal, gCachedFields.iaddr_class)) {
/*
* optVal is an InetAddress. Construct a multicast request structure
* from this address. Support IPv4 only.
@@ -1270,14 +1220,11 @@ static void mcastAddDropMembership(JNIEnv *env, int handle, jobject optVal,
socklen_t length = sizeof(multicastRequest);
memset(&multicastRequest, 0, length);
- // If ignoreIF is false, determine the index of the interface to use.
- if (!ignoreIF) {
- interfaceIndex = interfaceIndexFromMulticastSocket(handle);
- multicastRequest.imr_ifindex = interfaceIndex;
- if (interfaceIndex == -1) {
- jniThrowSocketException(env, errno);
- return;
- }
+ interfaceIndex = interfaceIndexFromMulticastSocket(handle);
+ multicastRequest.imr_ifindex = interfaceIndex;
+ if (interfaceIndex == -1) {
+ jniThrowSocketException(env, errno);
+ return;
}
// Convert the inetAddress to an IPv4 address structure.
@@ -1303,21 +1250,15 @@ static void mcastAddDropMembership(JNIEnv *env, int handle, jobject optVal,
* it and construct a multicast request structure from these. Support
* both IPv4 and IPv6.
*/
- jclass cls;
- jfieldID multiaddrID;
- jfieldID interfaceIdxID;
- jobject multiaddr;
// Get the multicast address to join or leave.
- cls = env->GetObjectClass(optVal);
- multiaddrID = env->GetFieldID(cls, "multiaddr", "Ljava/net/InetAddress;");
- multiaddr = env->GetObjectField(optVal, multiaddrID);
+ jclass cls = env->GetObjectClass(optVal);
+ jfieldID multiaddrID = env->GetFieldID(cls, "multiaddr", "Ljava/net/InetAddress;");
+ jobject multiaddr = env->GetObjectField(optVal, multiaddrID);
// Get the interface index to use.
- if (! ignoreIF) {
- interfaceIdxID = env->GetFieldID(cls, "interfaceIdx", "I");
- interfaceIndex = env->GetIntField(optVal, interfaceIdxID);
- }
+ jfieldID interfaceIdxID = env->GetFieldID(cls, "interfaceIdx", "I");
+ interfaceIndex = env->GetIntField(optVal, interfaceIdxID);
LOGI("mcastAddDropMembership interfaceIndex=%i", interfaceIndex);
if (!inetAddressToSocketAddress(env, multiaddr, 0, &sockaddrP)) {
@@ -1412,7 +1353,6 @@ static bool initCachedFields(JNIEnv* env) {
{&c->i4addr_class_init, c->i4addr_class, "<init>", "([B)V", false},
{&c->integer_class_init, c->integer_class, "<init>", "(I)V", false},
{&c->boolean_class_init, c->boolean_class, "<init>", "(Z)V", false},
- {&c->byte_class_init, c->byte_class, "<init>", "(B)V", false},
{&c->iaddr_getbyaddress, c->iaddr_class, "getByAddress",
"([B)Ljava/net/InetAddress;", true}
};
@@ -1489,8 +1429,19 @@ static void osNetworkSystem_createStreamSocket(JNIEnv* env, jobject, jobject fil
createSocketFileDescriptor(env, fileDescriptor, SOCK_STREAM);
}
-static void osNetworkSystem_createDatagramSocket(JNIEnv* env, jobject, jobject fd, jboolean) {
- createSocketFileDescriptor(env, fd, SOCK_DGRAM);
+static void osNetworkSystem_createDatagramSocket(JNIEnv* env, jobject, jobject fileDescriptor, jboolean) {
+ int fd = createSocketFileDescriptor(env, fileDescriptor, SOCK_DGRAM);
+#ifdef __linux__
+ // The RFC (http://tools.ietf.org/rfc/rfc3493.txt) says that IPV6_MULTICAST_HOPS defaults to 1.
+ // The Linux kernel (at least up to 2.6.32) accidentally defaults to 64 (which would be correct
+ // for the *unicast* hop limit). See http://www.spinics.net/lists/netdev/msg129022.html.
+ // When that's fixed, we can remove this code. Until then, we manually set the hop limit on
+ // IPv6 datagram sockets. (IPv4 is already correct.)
+ if (fd != -1 && getSocketAddressFamily(fd) == AF_INET6) {
+ int ttl = 1;
+ setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(int));
+ }
+#endif
}
static jint osNetworkSystem_readDirect(JNIEnv* env, jobject,
@@ -2353,389 +2304,225 @@ static jint osNetworkSystem_getSocketLocalPort(JNIEnv* env, jobject,
return getSocketAddressPort(&addr);
}
-static jobject osNetworkSystem_getSocketOption(JNIEnv* env, jobject,
- jobject fileDescriptor, jint anOption) {
- int intValue = 0;
- socklen_t intSize = sizeof(int);
- int result;
- struct sockaddr_storage sockVal;
- socklen_t sockSize = sizeof(sockVal);
-
- int handle;
- if (!jniGetFd(env, fileDescriptor, handle)) {
- return 0;
+template <typename T>
+static bool getSocketOption(JNIEnv* env, int fd, int level, int option, T* value) {
+ socklen_t size = sizeof(*value);
+ int rc = getsockopt(fd, level, option, value, &size);
+ if (rc == -1) {
+ LOGE("getSocketOption(fd=%i, level=%i, option=%i) failed: %s (errno=%i)",
+ fd, level, option, strerror(errno), errno);
+ jniThrowSocketException(env, errno);
+ return false;
}
+ return true;
+}
- switch ((int) anOption & 0xffff) {
- case JAVASOCKOPT_SO_LINGER: {
- struct linger lingr;
- socklen_t size = sizeof(struct linger);
- result = getsockopt(handle, SOL_SOCKET, SO_LINGER, &lingr, &size);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- if (!lingr.l_onoff) {
- intValue = -1;
- } else {
- intValue = lingr.l_linger;
- }
- return newJavaLangInteger(env, intValue);
- }
-
- case JAVASOCKOPT_TCP_NODELAY: {
- if ((anOption >> 16) & BROKEN_TCP_NODELAY) {
- return NULL;
- }
- result = getsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangBoolean(env, intValue);
- }
-
- case JAVASOCKOPT_SO_SNDBUF: {
- result = getsockopt(handle, SOL_SOCKET, SO_SNDBUF, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangInteger(env, intValue);
- }
-
- case JAVASOCKOPT_SO_RCVBUF: {
- result = getsockopt(handle, SOL_SOCKET, SO_RCVBUF, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangInteger(env, intValue);
- }
+static jobject getSocketOption_Boolean(JNIEnv* env, int fd, int level, int option) {
+ int value;
+ return getSocketOption(env, fd, level, option, &value) ? newJavaLangBoolean(env, value) : NULL;
+}
- case JAVASOCKOPT_SO_BROADCAST: {
- result = getsockopt(handle, SOL_SOCKET, SO_BROADCAST, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangBoolean(env, intValue);
- }
+static jobject getSocketOption_Integer(JNIEnv* env, int fd, int level, int option) {
+ int value;
+ return getSocketOption(env, fd, level, option, &value) ? newJavaLangInteger(env, value) : NULL;
+}
- case JAVASOCKOPT_SO_REUSEADDR: {
- result = getsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangBoolean(env, intValue);
- }
+static jobject osNetworkSystem_getSocketOption(JNIEnv* env, jobject, jobject fileDescriptor, jint option) {
+ int fd;
+ if (!jniGetFd(env, fileDescriptor, fd)) {
+ return NULL;
+ }
- case JAVASOCKOPT_SO_KEEPALIVE: {
- result = getsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangBoolean(env, intValue);
- }
+ int family = getSocketAddressFamily(fd);
+ if (family != AF_INET && family != AF_INET6) {
+ jniThrowSocketException(env, EAFNOSUPPORT);
+ return NULL;
+ }
- case JAVASOCKOPT_SO_OOBINLINE: {
- result = getsockopt(handle, SOL_SOCKET, SO_OOBINLINE, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangBoolean(env, intValue);
+ switch (option) {
+ case JAVASOCKOPT_TCP_NODELAY:
+ return getSocketOption_Boolean(env, fd, IPPROTO_TCP, TCP_NODELAY);
+ case JAVASOCKOPT_SO_SNDBUF:
+ return getSocketOption_Integer(env, fd, SOL_SOCKET, SO_SNDBUF);
+ case JAVASOCKOPT_SO_RCVBUF:
+ return getSocketOption_Integer(env, fd, SOL_SOCKET, SO_RCVBUF);
+ case JAVASOCKOPT_SO_BROADCAST:
+ return getSocketOption_Boolean(env, fd, SOL_SOCKET, SO_BROADCAST);
+ case JAVASOCKOPT_SO_REUSEADDR:
+ return getSocketOption_Boolean(env, fd, SOL_SOCKET, SO_REUSEADDR);
+ case JAVASOCKOPT_SO_KEEPALIVE:
+ return getSocketOption_Boolean(env, fd, SOL_SOCKET, SO_KEEPALIVE);
+ case JAVASOCKOPT_SO_OOBINLINE:
+ return getSocketOption_Boolean(env, fd, SOL_SOCKET, SO_OOBINLINE);
+ case JAVASOCKOPT_IP_TOS:
+ if (family == AF_INET) {
+ return getSocketOption_Boolean(env, fd, IPPROTO_IP, IP_TOS);
+ } else {
+ return getSocketOption_Boolean(env, fd, IPPROTO_IPV6, IPV6_TCLASS);
}
-
- case JAVASOCKOPT_IP_TOS: {
- result = getOrSetSocketOption(SOCKOPT_GET, handle, IP_TOS,
- IPV6_TCLASS, &intValue, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangInteger(env, intValue);
+ case JAVASOCKOPT_SO_LINGER:
+ {
+ linger lingr;
+ bool ok = getSocketOption(env, fd, SOL_SOCKET, SO_LINGER, &lingr);
+ return ok ? newJavaLangInteger(env, !lingr.l_onoff ? -1 : lingr.l_linger) : NULL;
}
-
- case JAVASOCKOPT_SO_RCVTIMEOUT: {
- struct timeval timeout;
- socklen_t size = sizeof(timeout);
- result = getsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, &timeout, &size);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangInteger(env, toMs(timeout));
+ case JAVASOCKOPT_SO_RCVTIMEOUT:
+ {
+ timeval timeout;
+ bool ok = getSocketOption(env, fd, SOL_SOCKET, SO_RCVTIMEO, &timeout);
+ return ok ? newJavaLangInteger(env, toMs(timeout)) : NULL;
}
-
#ifdef ENABLE_MULTICAST
- case JAVASOCKOPT_MCAST_TTL: {
- if ((anOption >> 16) & BROKEN_MULTICAST_TTL) {
- return newJavaLangByte(env, 0);
- }
- // Java uses a byte to store the TTL, but the kernel uses an int.
- result = getOrSetSocketOption(SOCKOPT_GET, handle, IP_MULTICAST_TTL,
- IPV6_MULTICAST_HOPS, &intValue,
- &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangByte(env, (jbyte)(intValue & 0xFF));
- }
-
- case JAVASOCKOPT_IP_MULTICAST_IF: {
- if ((anOption >> 16) & BROKEN_MULTICAST_IF) {
- return NULL;
- }
- result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF,
- &sockVal, &sockSize);
- if (result == -1) {
- jniThrowSocketException(env, errno);
+ case JAVASOCKOPT_IP_MULTICAST_IF:
+ {
+ struct sockaddr_storage sockVal;
+ if (!getSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_IF, &sockVal)) {
return NULL;
}
if (sockVal.ss_family != AF_INET) {
+ LOGE("sockVal.ss_family != AF_INET (%i)", sockVal.ss_family);
// Java expects an AF_INET INADDR_ANY, but Linux just returns AF_UNSPEC.
jbyteArray inAddrAny = env->NewByteArray(4); // { 0, 0, 0, 0 }
return byteArrayToInetAddress(env, inAddrAny);
}
return socketAddressToInetAddress(env, &sockVal);
}
-
- case JAVASOCKOPT_IP_MULTICAST_IF2: {
- if ((anOption >> 16) & BROKEN_MULTICAST_IF) {
- return NULL;
- }
+ case JAVASOCKOPT_IP_MULTICAST_IF2:
+ if (family == AF_INET) {
struct ip_mreqn multicastRequest;
- int interfaceIndex = 0;
- socklen_t optionLength;
- int addressFamily = getSocketAddressFamily(handle);
- switch (addressFamily) {
- case AF_INET:
- optionLength = sizeof(multicastRequest);
- result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF,
- &multicastRequest, &optionLength);
- if (result == 0)
- interfaceIndex = multicastRequest.imr_ifindex;
- break;
- case AF_INET6:
- optionLength = sizeof(interfaceIndex);
- result = getsockopt(handle, IPPROTO_IPV6, IPV6_MULTICAST_IF,
- &interfaceIndex, &optionLength);
- break;
- default:
- jniThrowSocketException(env, EAFNOSUPPORT);
- return NULL;
- }
-
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangInteger(env, interfaceIndex);
- }
-
- case JAVASOCKOPT_IP_MULTICAST_LOOP: {
- result = getOrSetSocketOption(SOCKOPT_GET, handle,
- IP_MULTICAST_LOOP,
- IPV6_MULTICAST_LOOP, &intValue,
- &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return NULL;
- }
- return newJavaLangBoolean(env, intValue);
+ bool ok = getSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_IF, &multicastRequest);
+ return ok ? newJavaLangInteger(env, multicastRequest.imr_ifindex) : NULL;
+ } else {
+ return getSocketOption_Integer(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_IF);
+ }
+ case JAVASOCKOPT_IP_MULTICAST_LOOP:
+ if (family == AF_INET) {
+ // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte.
+ u_char loopback;
+ bool ok = getSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loopback);
+ return ok ? newJavaLangBoolean(env, loopback) : NULL;
+ } else {
+ return getSocketOption_Boolean(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP);
+ }
+ case JAVASOCKOPT_MULTICAST_TTL:
+ if (family == AF_INET) {
+ // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int,
+ // IPv4 multicast TTL uses a byte.
+ u_char ttl;
+ bool ok = getSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl);
+ return ok ? newJavaLangInteger(env, ttl) : NULL;
+ } else {
+ return getSocketOption_Integer(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
}
#else
- case JAVASOCKOPT_MCAST_TTL:
- case JAVASOCKOPT_IP_MULTICAST_IF:
- case JAVASOCKOPT_IP_MULTICAST_IF2:
- case JAVASOCKOPT_IP_MULTICAST_LOOP: {
- jniThrowException(env, "java/lang/UnsupportedOperationException", NULL);
- return NULL;
- }
+ case JAVASOCKOPT_MULTICAST_TTL:
+ case JAVASOCKOPT_IP_MULTICAST_IF:
+ case JAVASOCKOPT_IP_MULTICAST_IF2:
+ case JAVASOCKOPT_IP_MULTICAST_LOOP:
+ jniThrowException(env, "java/lang/UnsupportedOperationException", NULL);
+ return NULL;
#endif // def ENABLE_MULTICAST
-
- default: {
- jniThrowSocketException(env, ENOPROTOOPT);
- return NULL;
- }
+ default:
+ jniThrowSocketException(env, ENOPROTOOPT);
+ return NULL;
}
+}
+template <typename T>
+static void setSocketOption(JNIEnv* env, int fd, int level, int option, T* value) {
+ int rc = setsockopt(fd, level, option, value, sizeof(*value));
+ if (rc == -1) {
+ LOGE("setSocketOption(fd=%i, level=%i, option=%i) failed: %s (errno=%i)",
+ fd, level, option, strerror(errno), errno);
+ jniThrowSocketException(env, errno);
+ }
}
-static void osNetworkSystem_setSocketOption(JNIEnv* env, jobject,
- jobject fileDescriptor, jint anOption, jobject optVal) {
- int result;
- int intVal;
- socklen_t intSize = sizeof(int);
- struct sockaddr_storage sockVal;
- int sockSize = sizeof(sockVal);
+static void osNetworkSystem_setSocketOption(JNIEnv* env, jobject, jobject fileDescriptor, jint option, jobject optVal) {
+ int fd;
+ if (!jniGetFd(env, fileDescriptor, fd)) {
+ return;
+ }
+ int intVal;
if (env->IsInstanceOf(optVal, gCachedFields.integer_class)) {
intVal = (int) env->GetIntField(optVal, gCachedFields.integer_class_value);
} else if (env->IsInstanceOf(optVal, gCachedFields.boolean_class)) {
intVal = (int) env->GetBooleanField(optVal, gCachedFields.boolean_class_value);
} else if (env->IsInstanceOf(optVal, gCachedFields.byte_class)) {
- // TTL uses a byte in Java, but the kernel still wants an int.
intVal = (int) env->GetByteField(optVal, gCachedFields.byte_class_value);
- } else if (env->IsInstanceOf(optVal, gCachedFields.iaddr_class)) {
- if (!inetAddressToSocketAddress(env, optVal, 0, &sockVal)) {
- return;
- }
- } else if (env->IsInstanceOf(optVal, gCachedFields.genericipmreq_class)) {
+ } else if (env->IsInstanceOf(optVal, gCachedFields.genericipmreq_class) || env->IsInstanceOf(optVal, gCachedFields.iaddr_class)) {
// we'll use optVal directly
} else {
- jniThrowSocketException(env, ENOPROTOOPT);
+ jniThrowSocketException(env, EINVAL);
return;
}
- int handle;
- if (!jniGetFd(env, fileDescriptor, handle)) {
+ int family = getSocketAddressFamily(fd);
+ if (family != AF_INET && family != AF_INET6) {
+ jniThrowSocketException(env, EAFNOSUPPORT);
return;
}
- switch ((int) anOption & 0xffff) {
- case JAVASOCKOPT_SO_LINGER: {
- struct linger lingr;
+ switch (option) {
+ case JAVASOCKOPT_SO_LINGER:
+ {
+ linger lingr;
lingr.l_onoff = intVal > 0 ? 1 : 0;
lingr.l_linger = intVal;
- result = setsockopt(handle, SOL_SOCKET, SO_LINGER, &lingr,
- sizeof(struct linger));
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return;
- }
- break;
- }
-
- case JAVASOCKOPT_TCP_NODELAY: {
- if ((anOption >> 16) & BROKEN_TCP_NODELAY) {
- return;
- }
- result = setsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &intVal, intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return;
- }
- break;
- }
-
- case JAVASOCKOPT_SO_SNDBUF: {
- result = setsockopt(handle, SOL_SOCKET, SO_SNDBUF, &intVal, intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return;
- }
- break;
- }
-
- case JAVASOCKOPT_SO_RCVBUF: {
- result = setsockopt(handle, SOL_SOCKET, SO_RCVBUF, &intVal, intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return;
- }
- break;
- }
-
- case JAVASOCKOPT_SO_BROADCAST: {
- result = setsockopt(handle, SOL_SOCKET, SO_BROADCAST, &intVal, intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return;
- }
- break;
- }
-
- case JAVASOCKOPT_SO_REUSEADDR: {
- result = setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intVal, intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return;
- }
- break;
- }
- case JAVASOCKOPT_SO_KEEPALIVE: {
- result = setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &intVal, intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return;
- }
- break;
- }
-
- case JAVASOCKOPT_SO_OOBINLINE: {
- result = setsockopt(handle, SOL_SOCKET, SO_OOBINLINE, &intVal, intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return;
- }
- break;
- }
-
- case JAVASOCKOPT_IP_TOS: {
- result = getOrSetSocketOption(SOCKOPT_SET, handle, IP_TOS,
- IPV6_TCLASS, &intVal, &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return;
- }
- break;
- }
-
- case JAVASOCKOPT_REUSEADDR_AND_REUSEPORT: {
- // SO_REUSEPORT doesn't need to get set on this System
- result = setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intVal, intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return;
- }
- break;
+ setSocketOption(env, fd, SOL_SOCKET, SO_LINGER, &lingr);
+ return;
}
-
- case JAVASOCKOPT_SO_RCVTIMEOUT: {
+ case JAVASOCKOPT_SO_SNDBUF:
+ setSocketOption(env, fd, SOL_SOCKET, SO_SNDBUF, &intVal);
+ return;
+ case JAVASOCKOPT_SO_RCVBUF:
+ setSocketOption(env, fd, SOL_SOCKET, SO_RCVBUF, &intVal);
+ return;
+ case JAVASOCKOPT_SO_BROADCAST:
+ setSocketOption(env, fd, SOL_SOCKET, SO_BROADCAST, &intVal);
+ return;
+ case JAVASOCKOPT_SO_REUSEADDR:
+ setSocketOption(env, fd, SOL_SOCKET, SO_REUSEADDR, &intVal);
+ return;
+ case JAVASOCKOPT_SO_KEEPALIVE:
+ setSocketOption(env, fd, SOL_SOCKET, SO_KEEPALIVE, &intVal);
+ return;
+ case JAVASOCKOPT_SO_OOBINLINE:
+ setSocketOption(env, fd, SOL_SOCKET, SO_OOBINLINE, &intVal);
+ return;
+ case JAVASOCKOPT_REUSEADDR_AND_REUSEPORT:
+ // SO_REUSEPORT doesn't need to get set on this System
+ setSocketOption(env, fd, SOL_SOCKET, SO_REUSEADDR, &intVal);
+ return;
+ case JAVASOCKOPT_SO_RCVTIMEOUT:
+ {
timeval timeout(toTimeval(intVal));
- result = setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, &timeout,
- sizeof(struct timeval));
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return;
- }
- break;
- }
-
-#ifdef ENABLE_MULTICAST
- case JAVASOCKOPT_MCAST_TTL: {
- if ((anOption >> 16) & BROKEN_MULTICAST_TTL) {
- return;
- }
- result = getOrSetSocketOption(SOCKOPT_SET, handle, IP_MULTICAST_TTL,
- IPV6_MULTICAST_HOPS, &intVal,
- &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return;
- }
- break;
- }
-
- case JAVASOCKOPT_MCAST_ADD_MEMBERSHIP: {
- mcastAddDropMembership(env, handle, optVal,
- (anOption >> 16) & BROKEN_MULTICAST_IF, IP_ADD_MEMBERSHIP);
- break;
+ setSocketOption(env, fd, SOL_SOCKET, SO_RCVTIMEO, &timeout);
+ return;
}
-
- case JAVASOCKOPT_MCAST_DROP_MEMBERSHIP: {
- mcastAddDropMembership(env, handle, optVal,
- (anOption >> 16) & BROKEN_MULTICAST_IF, IP_DROP_MEMBERSHIP);
- break;
+ case JAVASOCKOPT_IP_TOS:
+ if (family == AF_INET) {
+ setSocketOption(env, fd, IPPROTO_IP, IP_TOS, &intVal);
+ } else {
+ setSocketOption(env, fd, IPPROTO_IPV6, IPV6_TCLASS, &intVal);
}
-
- case JAVASOCKOPT_IP_MULTICAST_IF: {
- if ((anOption >> 16) & BROKEN_MULTICAST_IF) {
+ return;
+ case JAVASOCKOPT_TCP_NODELAY:
+ setSocketOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &intVal);
+ return;
+#ifdef ENABLE_MULTICAST
+ case JAVASOCKOPT_MCAST_ADD_MEMBERSHIP:
+ mcastAddDropMembership(env, fd, optVal, IP_ADD_MEMBERSHIP);
+ return;
+ case JAVASOCKOPT_MCAST_DROP_MEMBERSHIP:
+ mcastAddDropMembership(env, fd, optVal, IP_DROP_MEMBERSHIP);
+ return;
+ case JAVASOCKOPT_IP_MULTICAST_IF:
+ {
+ struct sockaddr_storage sockVal;
+ if (!env->IsInstanceOf(optVal, gCachedFields.iaddr_class) ||
+ !inetAddressToSocketAddress(env, optVal, 0, &sockVal)) {
return;
}
// This call is IPv4 only. The socket may be IPv6, but the address
@@ -2746,79 +2533,53 @@ static void osNetworkSystem_setSocketOption(JNIEnv* env, jobject,
}
struct ip_mreqn mcast_req;
memset(&mcast_req, 0, sizeof(mcast_req));
- struct sockaddr_in *sin = (struct sockaddr_in *) &sockVal;
- mcast_req.imr_address = sin->sin_addr;
- result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF,
- &mcast_req, sizeof(mcast_req));
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return;
- }
- break;
+ mcast_req.imr_address = reinterpret_cast<sockaddr_in*>(&sockVal)->sin_addr;
+ setSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_IF, &mcast_req);
+ return;
}
-
- case JAVASOCKOPT_IP_MULTICAST_IF2: {
- if ((anOption >> 16) & BROKEN_MULTICAST_IF) {
- return;
- }
- int addressFamily = getSocketAddressFamily(handle);
- int interfaceIndex = intVal;
- void *optionValue;
- socklen_t optionLength;
+ case JAVASOCKOPT_IP_MULTICAST_IF2:
+ if (family == AF_INET) {
+ // IP_MULTICAST_IF expects a pointer to a struct ip_mreqn.
struct ip_mreqn multicastRequest;
- switch (addressFamily) {
- case AF_INET:
- // IP_MULTICAST_IF expects a pointer to a struct ip_mreqn.
- memset(&multicastRequest, 0, sizeof(multicastRequest));
- multicastRequest.imr_ifindex = interfaceIndex;
- optionValue = &multicastRequest;
- optionLength = sizeof(multicastRequest);
- break;
- case AF_INET6:
- // IPV6_MULTICAST_IF expects a pointer to an integer.
- optionValue = &interfaceIndex;
- optionLength = sizeof(interfaceIndex);
- break;
- default:
- jniThrowSocketException(env, EAFNOSUPPORT);
- return;
- }
- result = getOrSetSocketOption(SOCKOPT_SET, handle,
- IP_MULTICAST_IF, IPV6_MULTICAST_IF, optionValue,
- &optionLength);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return;
- }
- break;
+ memset(&multicastRequest, 0, sizeof(multicastRequest));
+ multicastRequest.imr_ifindex = intVal;
+ setSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_IF, &multicastRequest);
+ } else {
+ // IPV6_MULTICAST_IF expects a pointer to an integer.
+ setSocketOption(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &intVal);
}
-
- case JAVASOCKOPT_IP_MULTICAST_LOOP: {
- result = getOrSetSocketOption(SOCKOPT_SET, handle,
- IP_MULTICAST_LOOP,
- IPV6_MULTICAST_LOOP, &intVal,
- &intSize);
- if (0 != result) {
- jniThrowSocketException(env, errno);
- return;
- }
- break;
+ return;
+ case JAVASOCKOPT_MULTICAST_TTL:
+ if (family == AF_INET) {
+ // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int,
+ // IPv4 multicast TTL uses a byte.
+ u_char ttl = intVal;
+ setSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl);
+ } else {
+ setSocketOption(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &intVal);
}
-#else
- case JAVASOCKOPT_MCAST_TTL:
- case JAVASOCKOPT_MCAST_ADD_MEMBERSHIP:
- case JAVASOCKOPT_MCAST_DROP_MEMBERSHIP:
- case JAVASOCKOPT_IP_MULTICAST_IF:
- case JAVASOCKOPT_IP_MULTICAST_IF2:
- case JAVASOCKOPT_IP_MULTICAST_LOOP: {
- jniThrowException(env, "java/lang/UnsupportedOperationException", NULL);
- return;
+ return;
+ case JAVASOCKOPT_IP_MULTICAST_LOOP:
+ if (family == AF_INET) {
+ // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte.
+ u_char loopback = intVal;
+ setSocketOption(env, fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loopback);
+ } else {
+ setSocketOption(env, fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &intVal);
}
+ return;
+#else
+ case JAVASOCKOPT_MULTICAST_TTL:
+ case JAVASOCKOPT_MCAST_ADD_MEMBERSHIP:
+ case JAVASOCKOPT_MCAST_DROP_MEMBERSHIP:
+ case JAVASOCKOPT_IP_MULTICAST_IF:
+ case JAVASOCKOPT_IP_MULTICAST_IF2:
+ case JAVASOCKOPT_IP_MULTICAST_LOOP:
+ jniThrowException(env, "java/lang/UnsupportedOperationException", NULL);
+ return;
#endif // def ENABLE_MULTICAST
-
- default: {
- jniThrowSocketException(env, ENOPROTOOPT);
- }
+ default:
+ jniThrowSocketException(env, ENOPROTOOPT);
}
}
diff --git a/luni/src/test/java/java/net/URLConnectionTest.java b/luni/src/test/java/java/net/URLConnectionTest.java
index 6050c7d..9563256 100644
--- a/luni/src/test/java/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/java/net/URLConnectionTest.java
@@ -16,12 +16,12 @@
package java.net;
-import java.net.*;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Arrays;
+import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import tests.support.Support_TestWebServer;
@@ -99,7 +99,7 @@ public class URLConnectionTest extends junit.framework.TestCase {
int n = 512*1024;
AtomicInteger total = new AtomicInteger(0);
ServerSocket ss = startSinkServer(total);
- URL url = new URL("http://localhost:" + ss.getLocalPort() + "/test1");
+ URL url = new URL("http://localhost:" + ss.getLocalPort() + "/" + UUID.randomUUID());
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
@@ -121,7 +121,7 @@ public class URLConnectionTest extends junit.framework.TestCase {
}
}
out.close();
- assertTrue(conn.getResponseCode() > 0);
+ assertEquals(200, conn.getResponseCode());
assertEquals(uploadKind == UploadKind.CHUNKED ? -1 : n, total.get());
}
diff --git a/luni/src/test/java/javax/net/ssl/SSLContextTest.java b/luni/src/test/java/javax/net/ssl/SSLContextTest.java
index 508aaaf..53ffe9c 100644
--- a/luni/src/test/java/javax/net/ssl/SSLContextTest.java
+++ b/luni/src/test/java/javax/net/ssl/SSLContextTest.java
@@ -17,32 +17,13 @@
package javax.net.ssl;
import dalvik.annotation.KnownFailure;
-import java.math.BigInteger;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.KeyStore;
import java.security.Provider;
-import java.security.SecureRandom;
-import java.security.Security;
-import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.util.Date;
-import java.util.Hashtable;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import junit.framework.TestCase;
-import org.bouncycastle.jce.X509Principal;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.x509.X509V3CertificateGenerator;
public class SSLContextTest extends TestCase {
- public static final boolean IS_RI = !"Dalvik Core Library".equals(System.getProperty("java.specification.name"));
- public static final String PROVIDER_NAME = (IS_RI) ? "SunJSSE" : "HarmonyJSSE";
-
public void test_SSLContext_getInstance() throws Exception {
try {
SSLContext.getInstance(null);
@@ -73,7 +54,7 @@ public class SSLContextTest extends TestCase {
} catch (IllegalArgumentException e) {
}
try {
- SSLContext.getInstance(null, PROVIDER_NAME);
+ SSLContext.getInstance(null, TestSSLContext.PROVIDER_NAME);
fail();
} catch (NullPointerException e) {
}
@@ -93,7 +74,7 @@ public class SSLContextTest extends TestCase {
public void test_SSLContext_getProvider() throws Exception {
Provider provider = SSLContext.getInstance("TLS").getProvider();
assertNotNull(provider);
- assertEquals(PROVIDER_NAME, provider.getName());
+ assertEquals(TestSSLContext.PROVIDER_NAME, provider.getName());
}
public void test_SSLContext_init() throws Exception {
@@ -176,186 +157,16 @@ public class SSLContextTest extends TestCase {
sessionContext);
}
- /**
- * SSLContextTest.Helper 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.
- */
- public static final class Helper {
-
- static {
- if (SSLContextTest.IS_RI) {
- Security.addProvider(new BouncyCastleProvider());
- }
- }
-
- public final KeyStore keyStore;
- public final char[] keyStorePassword;
- public final String publicAlias;
- public final String privateAlias;
- public final SSLContext sslContext;
- public final SSLServerSocket serverSocket;
- public final InetAddress host;
- public final int port;
-
- private Helper(KeyStore keyStore,
- char[] keyStorePassword,
- String publicAlias,
- String privateAlias,
- SSLContext sslContext,
- SSLServerSocket serverSocket,
- InetAddress host,
- int port) {
- this.keyStore = keyStore;
- this.keyStorePassword = keyStorePassword;
- this.publicAlias = publicAlias;
- this.privateAlias = privateAlias;
- this.sslContext = sslContext;
- this.serverSocket = serverSocket;
- this.host = host;
- this.port = port;
- }
-
- public static Helper create() {
- try {
- char[] keyStorePassword = null;
- String publicAlias = "public";
- String privateAlias = "private";
- return create(createKeyStore(keyStorePassword, publicAlias, privateAlias),
- null,
- publicAlias,
- privateAlias);
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- public static Helper create(KeyStore keyStore,
- char[] keyStorePassword,
- String publicAlias,
- String privateAlias) {
- try {
- SSLContext sslContext = createSSLContext(keyStore, keyStorePassword);
-
- SSLServerSocket serverSocket = (SSLServerSocket)
- sslContext.getServerSocketFactory().createServerSocket(0);
- InetSocketAddress sa = (InetSocketAddress) serverSocket.getLocalSocketAddress();
- InetAddress host = sa.getAddress();
- int port = sa.getPort();
-
- return new Helper(keyStore, keyStorePassword, publicAlias, privateAlias,
- sslContext, serverSocket, host, port);
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Create a BKS KeyStore containing an RSAPrivateKey with alias
- * "private" and a X509Certificate based on the matching
- * RSAPublicKey stored under the alias name publicAlias.
- *
- * The private key will have a certificate chain including the
- * certificate stored under the alias name privateAlias. The
- * certificate will be signed by the private key. The certificate
- * Subject and Issuer Common-Name will be the local host's
- * canonical hostname. The certificate will be valid for one day
- * before and one day after the time of creation.
- *
- * The KeyStore is optionally password protected by the
- * keyStorePassword argument, which can be null if a password is
- * not desired.
- *
- * Based on:
- * org.bouncycastle.jce.provider.test.SigTest
- * org.bouncycastle.jce.provider.test.CertTest
- */
- public static KeyStore createKeyStore(char[] keyStorePassword,
- String publicAlias,
- String privateAlias)
- throws Exception {
-
- // 1.) we make the keys
- int keysize = 1024;
- KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
- kpg.initialize(keysize, new SecureRandom());
- KeyPair kp = kpg.generateKeyPair();
- RSAPrivateKey privateKey = (RSAPrivateKey)kp.getPrivate();
- RSAPublicKey publicKey = (RSAPublicKey)kp.getPublic();
-
- // 2.) use keys to make certficate
-
- // note that there doesn't seem to be a standard way to make a
- // certificate using java.* or javax.*. The CertificateFactory
- // interface assumes you want to read in a stream of bytes a
- // factory specific format. So here we use Bouncy Castle's
- // X509V3CertificateGenerator and related classes.
-
- Hashtable attributes = new Hashtable();
- attributes.put(X509Principal.CN, InetAddress.getLocalHost().getCanonicalHostName());
- X509Principal dn = new X509Principal(attributes);
-
- long millisPerDay = 24 * 60 * 60 * 1000;
- long now = System.currentTimeMillis();
- Date start = new Date(now - millisPerDay);
- Date end = new Date(now + millisPerDay);
- BigInteger serial = BigInteger.valueOf(1);
-
- X509V3CertificateGenerator x509cg = new X509V3CertificateGenerator();
- x509cg.setSubjectDN(dn);
- x509cg.setIssuerDN(dn);
- x509cg.setNotBefore(start);
- x509cg.setNotAfter(end);
- x509cg.setPublicKey(publicKey);
- x509cg.setSignatureAlgorithm("sha1WithRSAEncryption");
- x509cg.setSerialNumber(serial);
- X509Certificate x509c = x509cg.generateX509Certificate(privateKey);
- X509Certificate[] x509cc = new X509Certificate[] { x509c };
-
-
- // 3.) put certificate and private key to make a key store
- KeyStore ks = KeyStore.getInstance("BKS");
- ks.load(null, null);
- ks.setKeyEntry(privateAlias, privateKey, keyStorePassword, x509cc);
- ks.setCertificateEntry(publicAlias, x509c);
- return ks;
- }
-
- /**
- * 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 final SSLContext createSSLContext(final KeyStore keyStore, final char[] keyStorePassword)
- throws Exception {
- String kmfa = KeyManagerFactory.getDefaultAlgorithm();
- KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfa);
- kmf.init(keyStore, keyStorePassword);
-
- String tmfa = TrustManagerFactory.getDefaultAlgorithm();
- TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfa);
- tmf.init(keyStore);
-
- SSLContext context = SSLContext.getInstance("TLS");
- context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
- return context;
- }
- }
-
- public void test_SSLContextTest_Helper_create() {
- Helper helper = Helper.create();
- assertNotNull(helper);
- assertNotNull(helper.keyStore);
- assertNull(helper.keyStorePassword);
- assertNotNull(helper.publicAlias);
- assertNotNull(helper.privateAlias);
- assertNotNull(helper.sslContext);
- assertNotNull(helper.serverSocket);
- assertNotNull(helper.host);
- assertTrue(helper.port != 0);
+ public void test_SSLContextTest_TestSSLContext_create() {
+ TestSSLContext testContext = TestSSLContext.create();
+ assertNotNull(testContext);
+ assertNotNull(testContext.keyStore);
+ assertNull(testContext.keyStorePassword);
+ assertNotNull(testContext.publicAlias);
+ assertNotNull(testContext.privateAlias);
+ assertNotNull(testContext.sslContext);
+ assertNotNull(testContext.serverSocket);
+ assertNotNull(testContext.host);
+ assertTrue(testContext.port != 0);
}
}
diff --git a/luni/src/test/java/javax/net/ssl/SSLSessionContextTest.java b/luni/src/test/java/javax/net/ssl/SSLSessionContextTest.java
index 83ed9c9..cb0f852 100644
--- a/luni/src/test/java/javax/net/ssl/SSLSessionContextTest.java
+++ b/luni/src/test/java/javax/net/ssl/SSLSessionContextTest.java
@@ -31,25 +31,33 @@ public class SSLSessionContextTest extends TestCase {
}
public void test_SSLSessionContext_getIds() {
- SSLContextTest.Helper c = SSLContextTest.Helper.create();
+ TestSSLContext c = TestSSLContext.create();
assertSSLSessionContextSize(0, c.sslContext.getClientSessionContext());
assertSSLSessionContextSize(0, c.sslContext.getServerSessionContext());
- SSLSocketTest.Helper s = SSLSocketTest.Helper.create_workaround();
+ TestSSLSocketPair s = TestSSLSocketPair.create();
assertSSLSessionContextSize(1, s.c.sslContext.getClientSessionContext());
- assertSSLSessionContextSize(1, s.c.sslContext.getServerSessionContext());
+ if (TestSSLContext.sslServerSocketSupportsSessionTickets()) {
+ assertSSLSessionContextSize(0, s.c.sslContext.getServerSessionContext());
+ } else {
+ assertSSLSessionContextSize(1, s.c.sslContext.getServerSessionContext());
+ }
Enumeration clientIds = s.c.sslContext.getClientSessionContext().getIds();
Enumeration serverIds = s.c.sslContext.getServerSessionContext().getIds();
byte[] clientId = (byte[]) clientIds.nextElement();
- byte[] serverId = (byte[]) serverIds.nextElement();
assertEquals(32, clientId.length);
- assertEquals(32, serverId.length);
- assertTrue(Arrays.equals(clientId, serverId));
+ if (TestSSLContext.sslServerSocketSupportsSessionTickets()) {
+ assertFalse(serverIds.hasMoreElements());
+ } else {
+ byte[] serverId = (byte[]) serverIds.nextElement();
+ assertEquals(32, serverId.length);
+ assertTrue(Arrays.equals(clientId, serverId));
+ }
}
@KnownFailure("Should throw NullPointerException on getSession(null)")
public void test_SSLSessionContext_getSession() {
- SSLContextTest.Helper c = SSLContextTest.Helper.create();
+ TestSSLContext c = TestSSLContext.create();
try {
c.sslContext.getClientSessionContext().getSession(null);
fail();
@@ -58,7 +66,7 @@ public class SSLSessionContextTest extends TestCase {
assertNull(c.sslContext.getClientSessionContext().getSession(new byte[0]));
assertNull(c.sslContext.getClientSessionContext().getSession(new byte[1]));
- SSLSocketTest.Helper s = SSLSocketTest.Helper.create_workaround();
+ TestSSLSocketPair s = TestSSLSocketPair.create();
SSLSessionContext client = s.c.sslContext.getClientSessionContext();
SSLSessionContext server = s.c.sslContext.getServerSessionContext();
byte[] clientId = (byte[]) client.getIds().nextElement();
@@ -71,18 +79,18 @@ public class SSLSessionContextTest extends TestCase {
@KnownFailure("Should return 0 for unlimited, not 10 entries")
public void test_SSLSessionContext_getSessionCacheSize() {
- SSLContextTest.Helper c = SSLContextTest.Helper.create();
+ TestSSLContext c = TestSSLContext.create();
assertEquals(0, c.sslContext.getClientSessionContext().getSessionCacheSize());
assertEquals(0, c.sslContext.getServerSessionContext().getSessionCacheSize());
- SSLSocketTest.Helper s = SSLSocketTest.Helper.create_workaround();
+ TestSSLSocketPair s = TestSSLSocketPair.create();
assertEquals(0, s.c.sslContext.getClientSessionContext().getSessionCacheSize());
assertEquals(0, s.c.sslContext.getServerSessionContext().getSessionCacheSize());
}
@KnownFailure("Should return 0 for unlimited, not 10 entries")
public void test_SSLSessionContext_setSessionCacheSize_basic() {
- SSLContextTest.Helper c = SSLContextTest.Helper.create();
+ TestSSLContext c = TestSSLContext.create();
assertBasicSetSessionCacheSizeBehavior(c.sslContext.getClientSessionContext());
assertBasicSetSessionCacheSizeBehavior(c.sslContext.getServerSessionContext());
}
@@ -101,7 +109,7 @@ public class SSLSessionContextTest extends TestCase {
@KnownFailure("Should return 0 for unlimited, not 10 entries")
public void test_SSLSessionContext_setSessionCacheSize_dynamic() {
- SSLSocketTest.Helper s = SSLSocketTest.Helper.create_workaround();
+ TestSSLSocketPair s = TestSSLSocketPair.create();
SSLSessionContext client = s.c.sslContext.getClientSessionContext();
SSLSessionContext server = s.c.sslContext.getServerSessionContext();
assertEquals(0, client.getSessionCacheSize());
@@ -144,12 +152,10 @@ public class SSLSessionContextTest extends TestCase {
*/
assertTrue(uniqueCipherSuites.size() > 5);
- SSLSocketTest.Helper.connect_workaround(s.c,
- new String[] { uniqueCipherSuites.remove() }); // 1
+ TestSSLSocketPair.connect(s.c, new String[] { uniqueCipherSuites.remove() }); // 1
assertSSLSessionContextSize(2, client);
assertSSLSessionContextSize(2, server);
- SSLSocketTest.Helper.connect_workaround(s.c,
- new String[] { uniqueCipherSuites.remove() }); // 2
+ TestSSLSocketPair.connect(s.c, new String[] { uniqueCipherSuites.remove() }); // 2
assertSSLSessionContextSize(3, client);
assertSSLSessionContextSize(3, server);
@@ -159,37 +165,34 @@ public class SSLSessionContextTest extends TestCase {
assertEquals(1, server.getSessionCacheSize());
assertSSLSessionContextSize(1, client);
assertSSLSessionContextSize(1, server);
- SSLSocketTest.Helper.connect_workaround(s.c,
- new String[] { uniqueCipherSuites.remove() }); // 3
+ TestSSLSocketPair.connect(s.c, new String[] { uniqueCipherSuites.remove() }); // 3
assertSSLSessionContextSize(1, client);
assertSSLSessionContextSize(1, server);
client.setSessionCacheSize(2);
server.setSessionCacheSize(2);
- SSLSocketTest.Helper.connect_workaround(s.c,
- new String[] { uniqueCipherSuites.remove() }); // 4
+ TestSSLSocketPair.connect(s.c, new String[] { uniqueCipherSuites.remove() }); // 4
assertSSLSessionContextSize(2, client);
assertSSLSessionContextSize(2, server);
- SSLSocketTest.Helper.connect_workaround(s.c,
- new String[] { uniqueCipherSuites.remove() }); // 5
+ TestSSLSocketPair.connect(s.c, new String[] { uniqueCipherSuites.remove() }); // 5
assertSSLSessionContextSize(2, client);
assertSSLSessionContextSize(2, server);
}
@KnownFailure("Should return 86400 seconds (1 day), not 0 for unlimited")
public void test_SSLSessionContext_getSessionTimeout() {
- SSLContextTest.Helper c = SSLContextTest.Helper.create();
+ TestSSLContext c = TestSSLContext.create();
assertEquals(86400, c.sslContext.getClientSessionContext().getSessionTimeout());
assertEquals(86400, c.sslContext.getServerSessionContext().getSessionTimeout());
- SSLSocketTest.Helper s = SSLSocketTest.Helper.create_workaround();
+ TestSSLSocketPair s = TestSSLSocketPair.create();
assertEquals(86400, s.c.sslContext.getClientSessionContext().getSessionTimeout());
assertEquals(86400, s.c.sslContext.getServerSessionContext().getSessionTimeout());
}
@KnownFailure("Should return 86400 seconds (1 day), not 0 for unlimited")
public void test_SSLSessionContext_setSessionTimeout() throws Exception {
- SSLContextTest.Helper c = SSLContextTest.Helper.create();
+ TestSSLContext c = TestSSLContext.create();
assertEquals(86400, c.sslContext.getClientSessionContext().getSessionTimeout());
assertEquals(86400, c.sslContext.getServerSessionContext().getSessionTimeout());
c.sslContext.getClientSessionContext().setSessionTimeout(0);
@@ -208,7 +211,7 @@ public class SSLSessionContextTest extends TestCase {
} catch (IllegalArgumentException e) {
}
- SSLSocketTest.Helper s = SSLSocketTest.Helper.create_workaround();
+ TestSSLSocketPair s = TestSSLSocketPair.create();
assertSSLSessionContextSize(1, s.c.sslContext.getClientSessionContext());
assertSSLSessionContextSize(1, s.c.sslContext.getServerSessionContext());
Thread.sleep(1 * 1000);
diff --git a/luni/src/test/java/javax/net/ssl/SSLSessionTest.java b/luni/src/test/java/javax/net/ssl/SSLSessionTest.java
index 020cd41..36b565b 100644
--- a/luni/src/test/java/javax/net/ssl/SSLSessionTest.java
+++ b/luni/src/test/java/javax/net/ssl/SSLSessionTest.java
@@ -23,55 +23,8 @@ import junit.framework.TestCase;
public class SSLSessionTest extends TestCase {
- public static final class Helper {
-
- /**
- * 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 SSLSocketTest.Helper s;
-
- private Helper(SSLSession invalid,
- SSLSession server,
- SSLSession client,
- SSLSocketTest.Helper s) {
- this.invalid = invalid;
- this.server = server;
- this.client = client;
- this.s = s;
- }
-
- public static final Helper create() {
- try {
- SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
- SSLSocket ssl = (SSLSocket) sf.createSocket();
- SSLSession invalid = ssl.getSession();
- SSLSocketTest.Helper s = SSLSocketTest.Helper.create_workaround();
- return new Helper(invalid, s.server.getSession(), s.client.getSession(), s);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
-
-
- public void test_SSLSocket_Helper_create() {
- Helper s = Helper.create();
+ public void test_SSLSocket_TestSSLSessions_create() {
+ TestSSLSessions s = TestSSLSessions.create();
assertNotNull(s.invalid);
assertFalse(s.invalid.isValid());
assertTrue(s.server.isValid());
@@ -79,7 +32,7 @@ public class SSLSessionTest extends TestCase {
}
public void test_SSLSession_getApplicationBufferSize() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
assertTrue(s.invalid.getApplicationBufferSize() > 0);
assertTrue(s.server.getApplicationBufferSize() > 0);
assertTrue(s.client.getApplicationBufferSize() > 0);
@@ -87,7 +40,7 @@ public class SSLSessionTest extends TestCase {
@KnownFailure("Expected SSL_NULL_WITH_NULL_NULL but received TLS_NULL_WITH_NULL_NULL")
public void test_SSLSession_getCipherSuite() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
assertNotNull(s.invalid.getCipherSuite());
assertEquals("SSL_NULL_WITH_NULL_NULL", s.invalid.getCipherSuite());
assertNotNull(s.server.getCipherSuite());
@@ -97,7 +50,7 @@ public class SSLSessionTest extends TestCase {
}
public void test_SSLSession_getCreationTime() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
assertTrue(s.invalid.getCreationTime() > 0);
assertTrue(s.server.getCreationTime() > 0);
assertTrue(s.client.getCreationTime() > 0);
@@ -105,18 +58,22 @@ public class SSLSessionTest extends TestCase {
}
public void test_SSLSession_getId() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
assertNotNull(s.invalid.getId());
assertNotNull(s.server.getId());
assertNotNull(s.client.getId());
assertEquals(0, s.invalid.getId().length);
- assertEquals(32, s.server.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);
- assertTrue(Arrays.equals(s.server.getId(), s.client.getId()));
}
public void test_SSLSession_getLastAccessedTime() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
assertTrue(s.invalid.getLastAccessedTime() > 0);
assertTrue(s.server.getLastAccessedTime() > 0);
assertTrue(s.client.getLastAccessedTime() > 0);
@@ -128,9 +85,12 @@ public class SSLSessionTest extends TestCase {
s.client.getCreationTime());
}
+ @KnownFailure("client local certificates should be null as it should not have been requested by server")
public void test_SSLSession_getLocalCertificates() throws Exception {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
assertNull(s.invalid.getLocalCertificates());
+ // TODO Fix Known Failure
+ // Need to fix NativeCrypto.SSL_new to not use SSL_use_certificate
assertNull(s.client.getLocalCertificates());
assertNotNull(s.server.getLocalCertificates());
assertEquals(1, s.server.getLocalCertificates().length);
@@ -138,9 +98,12 @@ public class SSLSessionTest extends TestCase {
s.server.getLocalCertificates()[0]);
}
+ @KnownFailure("client local principal should be null as it should not have been requested by server")
public void test_SSLSession_getLocalPrincipal() throws Exception {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
assertNull(s.invalid.getLocalPrincipal());
+ // TODO Fix Known Failure
+ // Need to fix NativeCrypto.SSL_new to not use SSL_use_certificate
assertNull(s.client.getLocalPrincipal());
assertNotNull(s.server.getLocalPrincipal());
assertNotNull(s.server.getLocalPrincipal().getName());
@@ -151,14 +114,14 @@ public class SSLSessionTest extends TestCase {
}
public void test_SSLSession_getPacketBufferSize() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
assertTrue(s.invalid.getPacketBufferSize() > 0);
assertTrue(s.server.getPacketBufferSize() > 0);
assertTrue(s.client.getPacketBufferSize() > 0);
}
public void test_SSLSession_getPeerCertificateChain() throws Exception {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
try {
s.invalid.getPeerCertificateChain();
fail();
@@ -176,7 +139,7 @@ public class SSLSessionTest extends TestCase {
}
public void test_SSLSession_getPeerCertificates() throws Exception {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
try {
s.invalid.getPeerCertificates();
fail();
@@ -194,21 +157,21 @@ public class SSLSessionTest extends TestCase {
}
public void test_SSLSession_getPeerHost() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
assertNull(s.invalid.getPeerHost());
assertNotNull(s.server.getPeerHost());
assertNotNull(s.client.getPeerHost());
}
public void test_SSLSession_getPeerPort() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
assertEquals(-1, s.invalid.getPeerPort());
assertTrue(s.server.getPeerPort() > 0);
assertEquals(s.s.c.port, s.client.getPeerPort());
}
public void test_SSLSession_getPeerPrincipal() throws Exception {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
try {
s.invalid.getPeerPrincipal();
fail();
@@ -229,7 +192,7 @@ public class SSLSessionTest extends TestCase {
}
public void test_SSLSession_getProtocol() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
assertNotNull(s.invalid.getProtocol());
assertEquals("NONE", s.invalid.getProtocol());
assertNotNull(s.server.getProtocol());
@@ -239,7 +202,7 @@ public class SSLSessionTest extends TestCase {
}
public void test_SSLSession_getSessionContext() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
assertNull(s.invalid.getSessionContext());
assertNotNull(s.server.getSessionContext());
assertNotNull(s.client.getSessionContext());
@@ -252,7 +215,7 @@ public class SSLSessionTest extends TestCase {
}
public void test_SSLSession_getValue() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
try {
s.invalid.getValue(null);
} catch (IllegalArgumentException e) {
@@ -261,13 +224,13 @@ public class SSLSessionTest extends TestCase {
}
public void test_SSLSession_getValueNames() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
assertNotNull(s.invalid.getValueNames());
assertEquals(0, s.invalid.getValueNames().length);
}
public void test_SSLSession_invalidate() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
assertFalse(s.invalid.isValid());
s.invalid.invalidate();
assertFalse(s.invalid.isValid());
@@ -285,14 +248,14 @@ public class SSLSessionTest extends TestCase {
}
public void test_SSLSession_isValid() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
assertFalse(s.invalid.isValid());
assertTrue(s.server.isValid());
assertTrue(s.client.isValid());
}
public void test_SSLSession_putValue() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
String key = "KEY";
String value = "VALUE";
assertNull(s.invalid.getValue(key));
@@ -304,7 +267,7 @@ public class SSLSessionTest extends TestCase {
}
public void test_SSLSession_removeValue() {
- Helper s = Helper.create();
+ TestSSLSessions s = TestSSLSessions.create();
String key = "KEY";
String value = "VALUE";
s.invalid.putValue(key, value);
diff --git a/luni/src/test/java/javax/net/ssl/SSLSocketFactoryTest.java b/luni/src/test/java/javax/net/ssl/SSLSocketFactoryTest.java
index 5ccae7f..bb76390 100644
--- a/luni/src/test/java/javax/net/ssl/SSLSocketFactoryTest.java
+++ b/luni/src/test/java/javax/net/ssl/SSLSocketFactoryTest.java
@@ -22,6 +22,9 @@ import java.net.SocketException;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import junit.framework.TestCase;
@@ -33,18 +36,50 @@ public class SSLSocketFactoryTest extends TestCase {
assertTrue(SSLSocketFactory.class.isAssignableFrom(sf.getClass()));
}
+ @KnownFailure("Using OpenSSL cipher suite names")
public void test_SSLSocketFactory_getDefaultCipherSuites() {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
- String[] cs = sf.getDefaultCipherSuites();
- assertNotNull(cs);
- assertTrue(cs.length != 0);
+ String[] cipherSuites = sf.getDefaultCipherSuites();
+ assertNotNull(cipherSuites);
+ assertTrue(cipherSuites.length != 0);
+
+ // Make sure modifying the result is not observable
+ String savedCipherSuite = cipherSuites[0];
+ assertNotNull(savedCipherSuite);
+ cipherSuites[0] = null;
+ assertNotNull(sf.getSupportedCipherSuites()[0]);
+ cipherSuites[0] = savedCipherSuite;
+
+ // Make sure all cipherSuites names are expected
+ for (String cipherSuite : cipherSuites) {
+ // TODO Fix Known Failure
+ // Need to fix CipherSuites methods to use JSSE names
+ assertTrue(StandardNames.CIPHER_SUITES.contains(cipherSuite));
+ }
}
+ @KnownFailure("Using OpenSSL cipher suite names")
public void test_SSLSocketFactory_getSupportedCipherSuites() {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
- String[] cs = sf.getSupportedCipherSuites();
- assertNotNull(cs);
- assertTrue(cs.length != 0);
+ String[] cipherSuites = sf.getSupportedCipherSuites();
+ assertNotNull(cipherSuites);
+ assertTrue(cipherSuites.length != 0);
+
+ // Make sure modifying the result is not observable
+ String savedCipherSuite = cipherSuites[0];
+ assertNotNull(savedCipherSuite);
+ cipherSuites[0] = null;
+ assertNotNull(sf.getSupportedCipherSuites()[0]);
+ cipherSuites[0] = savedCipherSuite;
+
+ // Make sure all cipherSuites names are expected
+ Set remainingCipherSuites = new HashSet<String>(StandardNames.CIPHER_SUITES);
+ for (String cipherSuite : cipherSuites) {
+ assertNotNull(remainingCipherSuites.remove(cipherSuite));
+ }
+ // TODO Fix Known Failure
+ // Need to fix CipherSuites methods to use JSSE names
+ assertEquals(Collections.EMPTY_SET, remainingCipherSuites);
}
@KnownFailure("Should not parse bogus port number -1 during createSocket")
diff --git a/luni/src/test/java/javax/net/ssl/SSLSocketTest.java b/luni/src/test/java/javax/net/ssl/SSLSocketTest.java
index d02aeee..9e2cb63 100644
--- a/luni/src/test/java/javax/net/ssl/SSLSocketTest.java
+++ b/luni/src/test/java/javax/net/ssl/SSLSocketTest.java
@@ -17,51 +17,61 @@
package javax.net.ssl;
import dalvik.annotation.KnownFailure;
-import java.math.BigInteger;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.security.Key;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.KeyStore;
import java.security.Principal;
-import java.security.SecureRandom;
-import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.Date;
+import java.util.Collections;
import java.util.Enumeration;
-import java.util.Hashtable;
-import javax.net.ServerSocketFactory;
-import javax.net.SocketFactory;
+import java.util.HashSet;
+import java.util.Set;
import junit.framework.TestCase;
public class SSLSocketTest extends TestCase {
+ @KnownFailure("Using OpenSSL cipher suite names")
public void test_SSLSocket_getSupportedCipherSuites() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket ssl = (SSLSocket) sf.createSocket();
- String[] cs = ssl.getSupportedCipherSuites();
- assertNotNull(cs);
- assertTrue(cs.length != 0);
+ String[] cipherSuites = ssl.getSupportedCipherSuites();
+ assertNotNull(cipherSuites);
+ assertTrue(cipherSuites.length != 0);
+ Set remainingCipherSuites = new HashSet<String>(StandardNames.CIPHER_SUITES);
+ for (String cipherSuite : cipherSuites) {
+ assertNotNull(remainingCipherSuites.remove(cipherSuite));
+ }
+ // TODO Fix Known Failure
+ // Need to fix CipherSuites methods to use JSSE names
+ assertEquals(Collections.EMPTY_SET, remainingCipherSuites);
}
+ @KnownFailure("Using OpenSSL cipher suite names")
public void test_SSLSocket_getEnabledCipherSuites() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket ssl = (SSLSocket) sf.createSocket();
- String[] cs = ssl.getEnabledCipherSuites();
- assertNotNull(cs);
- assertTrue(cs.length != 0);
+ String[] cipherSuites = ssl.getEnabledCipherSuites();
+ assertNotNull(cipherSuites);
+ assertTrue(cipherSuites.length != 0);
+
+ // Make sure modifying the result is not observable
+ String savedCipherSuite = cipherSuites[0];
+ assertNotNull(savedCipherSuite);
+ cipherSuites[0] = null;
+ assertNotNull(ssl.getEnabledCipherSuites()[0]);
+ cipherSuites[0] = savedCipherSuite;
+
+ // Make sure all cipherSuites names are expected
+ for (String cipherSuite : cipherSuites) {
+ // TODO Fix Known Failure
+ // Need to fix CipherSuites methods to use JSSE names
+ assertTrue(StandardNames.CIPHER_SUITES.contains(cipherSuite));
+ }
}
- @KnownFailure("Should support disabling all cipher suites")
public void test_SSLSocket_setEnabledCipherSuites() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket ssl = (SSLSocket) sf.createSocket();
@@ -90,20 +100,43 @@ public class SSLSocketTest extends TestCase {
public void test_SSLSocket_getSupportedProtocols() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket ssl = (SSLSocket) sf.createSocket();
- String[] p = ssl.getSupportedProtocols();
- assertNotNull(p);
- assertTrue(p.length != 0);
+ String[] protocols = ssl.getSupportedProtocols();
+ assertNotNull(protocols);
+ assertTrue(protocols.length != 0);
+
+ // Make sure modifying the result is not observable
+ String savedProtocol = protocols[0];
+ assertNotNull(savedProtocol);
+ protocols[0] = null;
+ assertNotNull(ssl.getSupportedProtocols()[0]);
+ protocols[0] = savedProtocol;
+
+ // Make sure all protocol names are expected
+ for (String protocol : protocols) {
+ assertNotNull(StandardNames.SSL_SOCKET_PROTOCOLS.contains(protocol));
+ }
}
public void test_SSLSocket_getEnabledProtocols() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket ssl = (SSLSocket) sf.createSocket();
- String[] p = ssl.getEnabledProtocols();
- assertNotNull(p);
- assertTrue(p.length != 0);
+ String[] protocols = ssl.getEnabledProtocols();
+ assertNotNull(protocols);
+ assertTrue(protocols.length != 0);
+
+ // Make sure modifying the result is not observable
+ String savedProtocol = protocols[0];
+ assertNotNull(savedProtocol);
+ protocols[0] = null;
+ assertNotNull(ssl.getEnabledProtocols()[0]);
+ protocols[0] = savedProtocol;
+
+ // Make sure all protocol names are expected
+ for (String protocol : protocols) {
+ assertNotNull(StandardNames.SSL_SOCKET_PROTOCOLS.contains(protocol));
+ }
}
- @KnownFailure("Should thow IllegalArgumentException not NullPointerException on null enabled protocols argument")
public void test_SSLSocket_setEnabledProtocols() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket ssl = (SSLSocket) sf.createSocket();
@@ -128,7 +161,6 @@ public class SSLSocketTest extends TestCase {
ssl.setEnabledProtocols(ssl.getSupportedProtocols());
}
- @KnownFailure("session of unconnected socket should not be valid")
public void test_SSLSocket_getSession() throws Exception {
SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket ssl = (SSLSocket) sf.createSocket();
@@ -137,26 +169,10 @@ public class SSLSocketTest extends TestCase {
assertFalse(session.isValid());
}
- @KnownFailure("Implementation should not start handshake in ServerSocket.accept")
+ @KnownFailure("local certificates should be null as it should not have been requested by server")
public void test_SSLSocket_startHandshake() throws Exception {
- final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+ final TestSSLContext c = TestSSLContext.create();
SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
- if (!SSLContextTest.IS_RI) {
- /*
- * The following hangs in accept in the Dalvik
- * implementation because accept is also incorrectly
- * starting the handhake.
- */
- c.serverSocket.setSoTimeout(1 * 1000);
- /*
- * That workaround doesn't seem to work so...
- *
- * See test_SSLSocket_startHandshake_workaround for
- * redundant version of this test that works around this
- * issue.
- */
- fail();
- }
final SSLSocket server = (SSLSocket) c.serverSocket.accept();
Thread thread = new Thread(new Runnable () {
public void run() {
@@ -192,79 +208,27 @@ public class SSLSocketTest extends TestCase {
thread.join();
}
- @KnownFailure("local certificates should be null as it should not have been requested by server")
- public void test_SSLSocket_startHandshake_workaround() throws Exception {
- final SSLContextTest.Helper c = SSLContextTest.Helper.create();
- Thread thread = new Thread(new Runnable () {
- public void run() {
- try {
- SSLSocket server = (SSLSocket) c.serverSocket.accept();
- server.startHandshake();
- assertNotNull(server.getSession());
- try {
- server.getSession().getPeerCertificates();
- fail();
- } catch (SSLPeerUnverifiedException e) {
- }
- Certificate[] localCertificates = server.getSession().getLocalCertificates();
- assertNotNull(localCertificates);
- assertEquals(1, localCertificates.length);
- assertNotNull(localCertificates[0]);
- assertNotNull(localCertificates[0].equals(c.keyStore.getCertificate(c.privateAlias)));
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- });
- thread.start();
- SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
- client.startHandshake();
- assertNotNull(client.getSession());
- assertNull(client.getSession().getLocalCertificates());
- Certificate[] peerCertificates = client.getSession().getPeerCertificates();
- assertNotNull(peerCertificates);
- assertEquals(1, peerCertificates.length);
- assertNotNull(peerCertificates[0]);
- assertNotNull(peerCertificates[0].equals(c.keyStore.getCertificate(c.publicAlias)));
- thread.join();
- }
-
- @KnownFailure("Should throw SSLException on server, not IOException on client")
- public void test_SSLSocket_startHandshake_noKeyStore_workaround() throws Exception {
- final SSLContextTest.Helper c = SSLContextTest.Helper.create(null, null, null, null);
- Thread thread = new Thread(new Runnable () {
- public void run() {
- try {
- c.serverSocket.accept();
- fail();
- } catch (SSLException e) {
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- });
- thread.start();
+ @KnownFailure("Should throw SSLException from SSLServerSocket.accept with no private key configured")
+ public void test_SSLSocket_startHandshake_noKeyStore() throws Exception {
+ TestSSLContext c = TestSSLContext.create(null, null, null, null);
SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
- if (!SSLContextTest.IS_RI) {
- client.startHandshake();
+ try {
+ SSLSocket server = (SSLSocket) c.serverSocket.accept();
+ // TODO Fix Known Failure
+ // Need to make SSLServerSocket.accept check if necessary private keys for enabled cipher suites are available
+ fail();
+ } catch (SSLException e) {
}
- thread.join();
}
- /**
- * Marked workaround because it avoid accepting on main thread like test_SSLSocket_startHandshake_workaround
- */
@KnownFailure("local certificates should be null as it should not have been requested by server")
- public void test_SSLSocket_HandshakeCompletedListener_workaround() throws Exception {
- final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+ public void test_SSLSocket_HandshakeCompletedListener() throws Exception {
+ final TestSSLContext c = TestSSLContext.create();
+ final SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+ final SSLSocket server = (SSLSocket) c.serverSocket.accept();
Thread thread = new Thread(new Runnable () {
public void run() {
try {
- SSLSocket server = (SSLSocket) c.serverSocket.accept();
server.startHandshake();
} catch (RuntimeException e) {
throw e;
@@ -274,7 +238,6 @@ public class SSLSocketTest extends TestCase {
}
});
thread.start();
- final SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
final boolean[] handshakeCompletedListenerCalled = new boolean[1];
client.addHandshakeCompletedListener(new HandshakeCompletedListener() {
public void handshakeCompleted(HandshakeCompletedEvent event) {
@@ -304,7 +267,9 @@ public class SSLSocketTest extends TestCase {
assertNotNull(id);
assertEquals(32, id.length);
assertNotNull(c.sslContext.getClientSessionContext().getSession(id));
- assertNotNull(c.sslContext.getServerSessionContext().getSession(id));
+ if (!TestSSLContext.sslServerSocketSupportsSessionTickets()) {
+ assertNotNull(c.sslContext.getServerSessionContext().getSession(id));
+ }
assertNotNull(cipherSuite);
assertTrue(Arrays.asList(client.getEnabledCipherSuites()).contains(cipherSuite));
@@ -331,6 +296,8 @@ public class SSLSocketTest extends TestCase {
assertTrue(X509Certificate.class.isAssignableFrom(certificate.getClass()));
X509Certificate x509certificate = (X509Certificate) certificate;
+ // TODO Fix Known Failure
+ // Need to fix NativeCrypto.SSL_new to not use SSL_use_certificate
assertNull(localCertificates);
assertNotNull(peerCertificates);
@@ -374,74 +341,51 @@ public class SSLSocketTest extends TestCase {
}
}
- /**
- * Marked workaround because it avoid accepting on main thread like test_SSLSocket_startHandshake_workaround.
- * Technically this test shouldn't even need a second thread.
- */
- public void test_SSLSocket_getUseClientMode_workaround() throws Exception {
- final SSLContextTest.Helper c = SSLContextTest.Helper.create();
- Thread thread = new Thread(new Runnable () {
- public void run() {
- try {
- SSLSocket server = (SSLSocket) c.serverSocket.accept();
- assertFalse(server.getUseClientMode());
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- });
- thread.start();
+ public void test_SSLSocket_getUseClientMode() throws Exception {
+ TestSSLContext c = TestSSLContext.create();
SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
- if (!SSLContextTest.IS_RI) {
- client.startHandshake();
- }
+ SSLSocket server = (SSLSocket) c.serverSocket.accept();
assertTrue(client.getUseClientMode());
- thread.join();
+ assertFalse(server.getUseClientMode());
}
- /**
- * Marked workaround because it avoid accepting on main thread like test_SSLSocket_startHandshake_workaround.
- * Technically this test shouldn't even need a second thread.
- */
- @KnownFailure("This test relies on socket timeouts which are not working. It also fails because ServerSocket.accept is handshaking")
- public void test_SSLSocket_setUseClientMode_workaround() throws Exception {
+ @KnownFailure("This test relies on socket timeouts which are not working. It also fails because SSLException is thrown instead of SSLProtocolException")
+ public void test_SSLSocket_setUseClientMode() throws Exception {
// client is client, server is server
- test_SSLSocket_setUseClientMode_workaround(true, false);
+ test_SSLSocket_setUseClientMode(true, false);
// client is server, server is client
- test_SSLSocket_setUseClientMode_workaround(true, false);
+ test_SSLSocket_setUseClientMode(true, false);
// both are client
try {
- test_SSLSocket_setUseClientMode_workaround(true, true);
+ test_SSLSocket_setUseClientMode(true, true);
fail();
} catch (SSLProtocolException e) {
+ // TODO Fix [Un]KnownFailure
+ // The more generic SSLException is thrown instead of SSLProtocolException
}
// both are server
try {
- test_SSLSocket_setUseClientMode_workaround(false, false);
+ test_SSLSocket_setUseClientMode(false, false);
fail();
} catch (SocketTimeoutException e) {
}
}
- private void test_SSLSocket_setUseClientMode_workaround(final boolean clientClientMode,
- final boolean serverClientMode)
+ private void test_SSLSocket_setUseClientMode(final boolean clientClientMode,
+ final boolean serverClientMode)
throws Exception {
- final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+ TestSSLContext c = TestSSLContext.create();
+ SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+ final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+
final SSLProtocolException[] sslProtocolException = new SSLProtocolException[1];
final SocketTimeoutException[] socketTimeoutException = new SocketTimeoutException[1];
Thread thread = new Thread(new Runnable () {
public void run() {
try {
- SSLSocket server = (SSLSocket) c.serverSocket.accept();
if (!serverClientMode) {
server.setSoTimeout(1 * 1000);
- if (!SSLContextTest.IS_RI) {
- /* as above setSoTimeout isn't working in dalvikvm */
- fail();
- }
}
server.setUseClientMode(serverClientMode);
server.startHandshake();
@@ -457,13 +401,8 @@ public class SSLSocketTest extends TestCase {
}
});
thread.start();
- SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
if (!clientClientMode) {
client.setSoTimeout(1 * 1000);
- if (!SSLContextTest.IS_RI) {
- /* as above setSoTimeout isn't working in dalvikvm */
- fail();
- }
}
client.setUseClientMode(clientClientMode);
client.startHandshake();
@@ -476,15 +415,13 @@ public class SSLSocketTest extends TestCase {
}
}
- /**
- * Marked workaround because it avoid accepting on main thread like test_SSLSocket_startHandshake_workaround
- */
- public void test_SSLSocket_clientAuth_workaround() throws Exception {
- final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+ public void test_SSLSocket_clientAuth() throws Exception {
+ TestSSLContext c = TestSSLContext.create();
+ SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+ final SSLSocket server = (SSLSocket) c.serverSocket.accept();
Thread thread = new Thread(new Runnable () {
public void run() {
try {
- SSLSocket server = (SSLSocket) c.serverSocket.accept();
assertFalse(server.getWantClientAuth());
assertFalse(server.getNeedClientAuth());
@@ -513,50 +450,27 @@ public class SSLSocketTest extends TestCase {
}
});
thread.start();
- SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
client.startHandshake();
assertNotNull(client.getSession().getLocalCertificates());
assertEquals(1, client.getSession().getLocalCertificates().length);
thread.join();
}
- /**
- * Marked workaround because it avoid accepting on main thread like test_SSLSocket_startHandshake_workaround
- * Technically this test shouldn't even need a second thread.
- */
- public void test_SSLSocket_getEnableSessionCreation_workaround() throws Exception {
- final SSLContextTest.Helper c = SSLContextTest.Helper.create();
- Thread thread = new Thread(new Runnable () {
- public void run() {
- try {
- SSLSocket server = (SSLSocket) c.serverSocket.accept();
- assertTrue(server.getEnableSessionCreation());
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- });
- thread.start();
+ public void test_SSLSocket_getEnableSessionCreation() throws Exception {
+ TestSSLContext c = TestSSLContext.create();
SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+ SSLSocket server = (SSLSocket) c.serverSocket.accept();
assertTrue(client.getEnableSessionCreation());
- if (!SSLContextTest.IS_RI) {
- client.startHandshake();
- }
- thread.join();
+ assertTrue(server.getEnableSessionCreation());
}
- /**
- * Marked workaround because it avoid accepting on main thread like test_SSLSocket_startHandshake_workaround
- */
- @KnownFailure("Server side session creation disabling does not work, should throw SSLException, not fail")
- public void test_SSLSocket_setEnableSessionCreation_server_workaround() throws Exception {
- final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+ public void test_SSLSocket_setEnableSessionCreation_server() throws Exception {
+ TestSSLContext c = TestSSLContext.create();
+ SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+ final SSLSocket server = (SSLSocket) c.serverSocket.accept();
Thread thread = new Thread(new Runnable () {
public void run() {
try {
- SSLSocket server = (SSLSocket) c.serverSocket.accept();
server.setEnableSessionCreation(false);
try {
server.startHandshake();
@@ -571,7 +485,6 @@ public class SSLSocketTest extends TestCase {
}
});
thread.start();
- SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
try {
client.startHandshake();
fail();
@@ -580,16 +493,13 @@ public class SSLSocketTest extends TestCase {
thread.join();
}
- /**
- * Marked workaround because it avoid accepting on main thread like test_SSLSocket_startHandshake_workaround
- */
- @KnownFailure("Should throw SSLException on server, not IOException")
- public void test_SSLSocket_setEnableSessionCreation_client_workaround() throws Exception {
- final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+ public void test_SSLSocket_setEnableSessionCreation_client() throws Exception {
+ TestSSLContext c = TestSSLContext.create();
+ SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+ final SSLSocket server = (SSLSocket) c.serverSocket.accept();
Thread thread = new Thread(new Runnable () {
public void run() {
try {
- SSLSocket server = (SSLSocket) c.serverSocket.accept();
try {
server.startHandshake();
fail();
@@ -603,94 +513,44 @@ public class SSLSocketTest extends TestCase {
}
});
thread.start();
- SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
client.setEnableSessionCreation(false);
try {
client.startHandshake();
fail();
} catch (SSLException e) {
- if (!SSLContextTest.IS_RI) {
- fail();
- }
}
thread.join();
}
- /**
- * SSLSocketTest.Helper is a convenience class for other tests that
- * want a pair of connected and handshaked client and server
- * SSLSocketsfor testing so they don't have to duplicate the
- * logic.
- */
- public static final class Helper {
- public final SSLContextTest.Helper c;
- public final SSLSocket server;
- public final SSLSocket client;
-
- private Helper (SSLContextTest.Helper c,
- SSLSocket server,
- SSLSocket client) {
- this.c = c;
- this.server = server;
- this.client = client;
- }
-
- /**
- * based on test_SSLSocket_startHandshake_workaround, should
- * be written to non-workaround form when possible
- */
- public static Helper create_workaround () {
- SSLContextTest.Helper c = SSLContextTest.Helper.create();
- SSLSocket[] sockets = connect_workaround(c, null);
- return new Helper(c, sockets[0], sockets[1]);
- }
-
- /**
- * Create a new connected server/client socket pair within a
- * existing SSLContext. Optional clientCipherSuites allows
- * forcing new SSLSession to test SSLSessionContext caching
- */
- public static SSLSocket[] connect_workaround (final SSLContextTest.Helper c,
- String[] clientCipherSuites) {
- try {
- final SSLSocket[] server = new SSLSocket[1];
- Thread thread = new Thread(new Runnable () {
- public void run() {
- try {
- server[0] = (SSLSocket) c.serverSocket.accept();
- server[0].startHandshake();
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- });
- thread.start();
- SSLSocket client = (SSLSocket)
- c.sslContext.getSocketFactory().createSocket(c.host, c.port);
- if (clientCipherSuites != null) {
- client.setEnabledCipherSuites(clientCipherSuites);
- }
- client.startHandshake();
- thread.join();
- return new SSLSocket[] { server[0], client };
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
-
public void test_SSLSocketTest_Test_create() {
- Helper test = Helper.create_workaround();
+ TestSSLSocketPair test = TestSSLSocketPair.create();
assertNotNull(test.c);
assertNotNull(test.server);
assertNotNull(test.client);
- assertNotNull(test.server.isConnected());
- assertNotNull(test.client.isConnected());
+ assertTrue(test.server.isConnected());
+ assertTrue(test.client.isConnected());
assertNotNull(test.server.getSession());
assertNotNull(test.client.getSession());
}
+
+ /**
+ * Not run by default by JUnit, but can be run by Vogar by
+ * specifying it explictly (or with main method below)
+ */
+ public void stress_test_SSLSocketTest_Test_create() {
+ final boolean verbose = true;
+ while (true) {
+ TestSSLSocketPair test = TestSSLSocketPair.create();
+ if (verbose) {
+ System.out.println("client=" + test.client.getLocalPort()
+ + " server=" + test.server.getLocalPort());
+ } else {
+ System.out.print("X");
+ }
+ }
+ }
+
+ public static final void main (String[] args) {
+ new SSLSocketTest().stress_test_SSLSocketTest_Test_create();
+ }
}
diff --git a/luni/src/test/java/tests/AllTests.java b/luni/src/test/java/tests/AllTests.java
index fdea653..4b3a484 100644
--- a/luni/src/test/java/tests/AllTests.java
+++ b/luni/src/test/java/tests/AllTests.java
@@ -65,8 +65,7 @@ public class AllTests
suite.addTest(java.text.AllTests.suite());
suite.addTest(java.util.AllTests.suite());
suite.addTest(javax.xml.parsers.AllTests.suite());
- // disable until hangs are resolved in our JSSE implementation
- // suite.addTest(javax.net.ssl.AllTests.suite());
+ suite.addTest(javax.net.ssl.AllTests.suite());
suite.addTest(org.apache.harmony.luni.platform.AllTests.suite());
suite.addTest(org.json.AllTests.suite());
suite.addTest(tests.api.org.apache.harmony.kernel.dalvik.AllTests.suite());
diff --git a/run-core-tests b/run-core-tests
index 95e034e..b1df621 100755
--- a/run-core-tests
+++ b/run-core-tests
@@ -25,9 +25,7 @@ chmod 777 $tmp
# Build the classpath by putting together the jar file for each module.
classpath="/system/framework/sqlite-jdbc.jar" # Bonus item for jdbc testing.
-modules="annotation archive concurrent crypto dom json \
- logging luni-kernel luni math nio_char prefs regex security sql \
- suncompat support text x-net xml"
+modules="dom json luni-kernel luni prefs support x-net xml"
for module in $modules; do
classpath="$classpath:/system/framework/core-tests-$module.jar"
done
diff --git a/support/src/test/java/javax/net/ssl/StandardNames.java b/support/src/test/java/javax/net/ssl/StandardNames.java
new file mode 100644
index 0000000..ccd3ee1
--- /dev/null
+++ b/support/src/test/java/javax/net/ssl/StandardNames.java
@@ -0,0 +1,112 @@
+/*
+ * 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 javax.net.ssl;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This class defines expected string names for protocols, key types, client and server auth types, cipher suites.
+ * Based on documentation from http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA
+ */
+public final class StandardNames {
+
+ public static final Set<String> SSL_CONTEXT_PROTOCOLS = new HashSet<String>(Arrays.asList(
+ "SSL",
+ "SSLv2",
+ "SSLv3",
+ "TLS",
+ "TLSv1"));
+
+ public static final Set<String> KEY_TYPES = new HashSet<String>(Arrays.asList(
+ "RSA",
+ "DSA",
+ "DH_RSA",
+ "DH_DSA"));
+
+ public static final Set<String> SSL_SOCKET_PROTOCOLS = new HashSet<String>(Arrays.asList(
+ "SSLv2",
+ "SSLv3",
+ "TLSv1",
+ "SSLv2Hello"));
+
+ public static final Set<String> CLIENT_AUTH_TYPES = new HashSet<String>(KEY_TYPES);
+
+ 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",
+ "UNKNOWN"));
+
+ // removed cipher suites not actually found in RI
+ public static final Set<String> CIPHER_SUITES = new HashSet<String>(Arrays.asList(
+ "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
+ "SSL_DHE_DSS_WITH_DES_CBC_SHA",
+ "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_DHE_RSA_WITH_DES_CBC_SHA",
+ //"SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA",
+ //"SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
+ "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
+ "SSL_DH_anon_WITH_DES_CBC_SHA",
+ "SSL_DH_anon_WITH_RC4_128_MD5",
+ //"SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA",
+ //"SSL_RSA_EXPORT1024_WITH_RC4_56_SHA",
+ "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ //"SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
+ "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
+ "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_RSA_WITH_DES_CBC_SHA",
+ "SSL_RSA_WITH_NULL_MD5",
+ "SSL_RSA_WITH_NULL_SHA",
+ "SSL_RSA_WITH_RC4_128_MD5",
+ "SSL_RSA_WITH_RC4_128_SHA",
+ "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
+ //"TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
+ "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
+ //"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_DH_anon_WITH_AES_128_CBC_SHA",
+ //"TLS_DH_anon_WITH_AES_256_CBC_SHA",
+ "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5",
+ "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA",
+ //"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5",
+ //"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA",
+ "TLS_KRB5_EXPORT_WITH_RC4_40_MD5",
+ "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
+ "TLS_KRB5_WITH_3DES_EDE_CBC_MD5",
+ "TLS_KRB5_WITH_3DES_EDE_CBC_SHA",
+ "TLS_KRB5_WITH_DES_CBC_MD5",
+ "TLS_KRB5_WITH_DES_CBC_SHA",
+ "TLS_KRB5_WITH_RC4_128_MD5",
+ "TLS_KRB5_WITH_RC4_128_SHA",
+ "TLS_RSA_WITH_AES_128_CBC_SHA"));
+ //"TLS_RSA_WITH_AES_256_CBC_SHA"));
+}
diff --git a/support/src/test/java/javax/net/ssl/TestSSLContext.java b/support/src/test/java/javax/net/ssl/TestSSLContext.java
new file mode 100644
index 0000000..44b21c9
--- /dev/null
+++ b/support/src/test/java/javax/net/ssl/TestSSLContext.java
@@ -0,0 +1,226 @@
+/*
+ * 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 javax.net.ssl;
+
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Date;
+import java.util.Hashtable;
+import org.bouncycastle.jce.X509Principal;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.x509.X509V3CertificateGenerator;
+
+/**
+ * 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.
+ */
+public final class TestSSLContext {
+
+ public static final boolean IS_RI = !"Dalvik Core Library".equals(System.getProperty("java.specification.name"));
+ public static final String PROVIDER_NAME = (IS_RI) ? "SunJSSE" : "HarmonyJSSE";
+
+ static {
+ if (IS_RI) {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+ }
+
+ /**
+ * 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 () {
+ return !IS_RI;
+ }
+
+ public final KeyStore keyStore;
+ public final char[] keyStorePassword;
+ public final String publicAlias;
+ public final String privateAlias;
+ public final SSLContext sslContext;
+ public final SSLServerSocket serverSocket;
+ public final InetAddress host;
+ public final int port;
+
+ private TestSSLContext(KeyStore keyStore,
+ char[] keyStorePassword,
+ String publicAlias,
+ String privateAlias,
+ SSLContext sslContext,
+ SSLServerSocket serverSocket,
+ InetAddress host,
+ int port) {
+ this.keyStore = keyStore;
+ this.keyStorePassword = keyStorePassword;
+ this.publicAlias = publicAlias;
+ this.privateAlias = privateAlias;
+ this.sslContext = sslContext;
+ this.serverSocket = serverSocket;
+ this.host = host;
+ this.port = port;
+ }
+
+ public static TestSSLContext create() {
+ try {
+ char[] keyStorePassword = null;
+ String publicAlias = "public";
+ String privateAlias = "private";
+ return create(createKeyStore(keyStorePassword, publicAlias, privateAlias),
+ null,
+ publicAlias,
+ privateAlias);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static TestSSLContext create(KeyStore keyStore,
+ char[] keyStorePassword,
+ String publicAlias,
+ String privateAlias) {
+ try {
+ SSLContext sslContext = createSSLContext(keyStore, keyStorePassword);
+
+ SSLServerSocket serverSocket = (SSLServerSocket)
+ sslContext.getServerSocketFactory().createServerSocket(0);
+ InetSocketAddress sa = (InetSocketAddress) serverSocket.getLocalSocketAddress();
+ InetAddress host = sa.getAddress();
+ int port = sa.getPort();
+
+ return new TestSSLContext(keyStore, keyStorePassword, publicAlias, privateAlias,
+ sslContext, serverSocket, host, port);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Create a BKS KeyStore containing an RSAPrivateKey with alias
+ * "private" and a X509Certificate based on the matching
+ * RSAPublicKey stored under the alias name publicAlias.
+ *
+ * The private key will have a certificate chain including the
+ * certificate stored under the alias name privateAlias. The
+ * certificate will be signed by the private key. The certificate
+ * Subject and Issuer Common-Name will be the local host's
+ * canonical hostname. The certificate will be valid for one day
+ * before and one day after the time of creation.
+ *
+ * The KeyStore is optionally password protected by the
+ * keyStorePassword argument, which can be null if a password is
+ * not desired.
+ *
+ * Based on:
+ * org.bouncycastle.jce.provider.test.SigTest
+ * org.bouncycastle.jce.provider.test.CertTest
+ */
+ public static KeyStore createKeyStore(char[] keyStorePassword,
+ String publicAlias,
+ String privateAlias)
+ throws Exception {
+
+ // 1.) we make the keys
+ int keysize = 1024;
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+ kpg.initialize(keysize, new SecureRandom());
+ KeyPair kp = kpg.generateKeyPair();
+ RSAPrivateKey privateKey = (RSAPrivateKey)kp.getPrivate();
+ RSAPublicKey publicKey = (RSAPublicKey)kp.getPublic();
+
+ // 2.) use keys to make certficate
+
+ // note that there doesn't seem to be a standard way to make a
+ // certificate using java.* or javax.*. The CertificateFactory
+ // interface assumes you want to read in a stream of bytes a
+ // factory specific format. So here we use Bouncy Castle's
+ // X509V3CertificateGenerator and related classes.
+
+ Hashtable attributes = new Hashtable();
+ attributes.put(X509Principal.CN, InetAddress.getLocalHost().getCanonicalHostName());
+ X509Principal dn = new X509Principal(attributes);
+
+ long millisPerDay = 24 * 60 * 60 * 1000;
+ long now = System.currentTimeMillis();
+ Date start = new Date(now - millisPerDay);
+ Date end = new Date(now + millisPerDay);
+ BigInteger serial = BigInteger.valueOf(1);
+
+ X509V3CertificateGenerator x509cg = new X509V3CertificateGenerator();
+ x509cg.setSubjectDN(dn);
+ x509cg.setIssuerDN(dn);
+ x509cg.setNotBefore(start);
+ x509cg.setNotAfter(end);
+ x509cg.setPublicKey(publicKey);
+ x509cg.setSignatureAlgorithm("sha1WithRSAEncryption");
+ x509cg.setSerialNumber(serial);
+ X509Certificate x509c = x509cg.generateX509Certificate(privateKey);
+ X509Certificate[] x509cc = new X509Certificate[] { x509c };
+
+
+ // 3.) put certificate and private key to make a key store
+ KeyStore ks = KeyStore.getInstance("BKS");
+ ks.load(null, null);
+ ks.setKeyEntry(privateAlias, privateKey, keyStorePassword, x509cc);
+ ks.setCertificateEntry(publicAlias, x509c);
+ return ks;
+ }
+
+ /**
+ * 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 final SSLContext createSSLContext(final KeyStore keyStore, final char[] keyStorePassword)
+ throws Exception {
+ String kmfa = KeyManagerFactory.getDefaultAlgorithm();
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfa);
+ kmf.init(keyStore, keyStorePassword);
+
+ String tmfa = TrustManagerFactory.getDefaultAlgorithm();
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfa);
+ tmf.init(keyStore);
+
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
+ return context;
+ }
+}
diff --git a/support/src/test/java/javax/net/ssl/TestSSLSessions.java b/support/src/test/java/javax/net/ssl/TestSSLSessions.java
new file mode 100644
index 0000000..23b8ed5
--- /dev/null
+++ b/support/src/test/java/javax/net/ssl/TestSSLSessions.java
@@ -0,0 +1,68 @@
+/*
+ * 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 javax.net.ssl;
+
+/**
+ * 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.
+ */
+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 static final TestSSLSessions create() {
+ try {
+ SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+ SSLSocket ssl = (SSLSocket) sf.createSocket();
+ SSLSession invalid = ssl.getSession();
+ TestSSLSocketPair s = TestSSLSocketPair.create();
+ return new TestSSLSessions(invalid, s.server.getSession(), s.client.getSession(), s);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/support/src/test/java/javax/net/ssl/TestSSLSocketPair.java b/support/src/test/java/javax/net/ssl/TestSSLSocketPair.java
new file mode 100644
index 0000000..4409183
--- /dev/null
+++ b/support/src/test/java/javax/net/ssl/TestSSLSocketPair.java
@@ -0,0 +1,82 @@
+/*
+ * 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 javax.net.ssl;
+
+/**
+ * TestSSLSocketPair is a convenience class for other tests that want
+ * a pair of connected and handshaked client and server SSLSockets for
+ * testing.
+ */
+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;
+ }
+
+ /**
+ * based on test_SSLSocket_startHandshake
+ */
+ public static TestSSLSocketPair create () {
+ TestSSLContext c = TestSSLContext.create();
+ SSLSocket[] sockets = connect(c, null);
+ return new TestSSLSocketPair(c, sockets[0], sockets[1]);
+ }
+
+ /**
+ * Create a new connected server/client socket pair within a
+ * existing SSLContext. Optional clientCipherSuites allows
+ * forcing new SSLSession to test SSLSessionContext caching
+ */
+ public static SSLSocket[] connect (final TestSSLContext c,
+ String[] clientCipherSuites) {
+ try {
+ SSLSocket client = (SSLSocket)
+ c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+ final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+ Thread thread = new Thread(new Runnable () {
+ public void run() {
+ try {
+ server.startHandshake();
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ thread.start();
+ if (clientCipherSuites != null) {
+ client.setEnabledCipherSuites(clientCipherSuites);
+ }
+ client.startHandshake();
+ thread.join();
+ return new SSLSocket[] { server, client };
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
+
diff --git a/x-net/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java b/x-net/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
index 6adca42..779c46a 100644
--- a/x-net/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
+++ b/x-net/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
@@ -89,44 +89,7 @@ class DefaultHostnameVerifier implements HostnameVerifier {
throw new NullPointerException("host to verify is null");
}
- ssl.startHandshake();
SSLSession session = ssl.getSession();
- if(session == null) {
- // In our experience this only happens under IBM 1.4.x when
- // spurious (unrelated) certificates show up in the server'
- // chain. Hopefully this will unearth the real problem:
- InputStream in = ssl.getInputStream();
- in.available();
- /*
- If you're looking at the 2 lines of code above because
- you're running into a problem, you probably have two
- options:
-
- #1. Clean up the certificate chain that your server
- is presenting (e.g. edit "/etc/apache2/server.crt"
- or wherever it is your server's certificate chain
- is defined).
-
- OR
-
- #2. Upgrade to an IBM 1.5.x or greater JVM, or switch
- to a non-IBM JVM.
- */
-
- // If ssl.getInputStream().available() didn't cause an
- // exception, maybe at least now the session is available?
- session = ssl.getSession();
- if(session == null) {
- // If it's still null, probably a startHandshake() will
- // unearth the real problem.
- ssl.startHandshake();
-
- // Okay, if we still haven't managed to cause an exception,
- // might as well go for the NPE. Or maybe we're okay now?
- session = ssl.getSession();
- }
- }
-
Certificate[] certs = session.getPeerCertificates();
X509Certificate x509 = (X509Certificate) certs[0];
verify(host, x509);
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java
index b780943..7a0985e 100644
--- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java
@@ -34,7 +34,6 @@ abstract class AbstractSessionContext implements SSLSessionContext {
volatile int maximumSize;
volatile int timeout;
- final SSLParameters parameters;
final int sslCtxNativePointer;
/** Identifies OpenSSL sessions. */
@@ -43,13 +42,12 @@ abstract class AbstractSessionContext implements SSLSessionContext {
/**
* Constructs a new session context.
*
- * @param parameters
+ * @param sslCtxNativePointer Associated native SSL_CTX
* @param maximumSize of cache
* @param timeout for cache entries
*/
- AbstractSessionContext(SSLParameters parameters, int sslCtxNativePointer,
+ AbstractSessionContext(int sslCtxNativePointer,
int maximumSize, int timeout) {
- this.parameters = parameters;
this.sslCtxNativePointer = sslCtxNativePointer;
this.maximumSize = maximumSize;
this.timeout = timeout;
@@ -133,6 +131,7 @@ abstract class AbstractSessionContext implements SSLSessionContext {
daos.writeInt(data.length);
daos.write(data);
}
+ // TODO: local certificates?
return baos.toByteArray();
} catch (IOException e) {
@@ -172,8 +171,7 @@ abstract class AbstractSessionContext implements SSLSessionContext {
certs[i] = X509Certificate.getInstance(certData);
}
- return new OpenSSLSessionImpl(sessionData, parameters, host, port,
- certs, this);
+ return new OpenSSLSessionImpl(sessionData, host, port, certs, this);
} catch (IOException e) {
log(e);
return null;
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java
index 338fc39..66e8d03 100644
--- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java
@@ -63,10 +63,9 @@ public class ClientSessionContext extends AbstractSessionContext {
final SSLClientSessionCache persistentCache;
- public ClientSessionContext(SSLParameters parameters,
- int sslCtxNativePointer,
- SSLClientSessionCache persistentCache) {
- super(parameters, sslCtxNativePointer, 10, 0);
+ public ClientSessionContext(int sslCtxNativePointer,
+ SSLClientSessionCache persistentCache) {
+ super(sslCtxNativePointer, 10, 0);
this.persistentCache = persistentCache;
}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
index ad6ae15..2220d36 100644
--- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
@@ -17,11 +17,13 @@
package org.apache.harmony.xnet.provider.jsse;
import java.io.ByteArrayOutputStream;
-import java.io.OutputStreamWriter;
import java.io.IOException;
-import java.util.ArrayList;
+import java.io.OutputStreamWriter;
+import java.net.Socket;
import java.security.PrivateKey;
+import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import org.bouncycastle.openssl.PEMWriter;
@@ -78,6 +80,10 @@ public class NativeCrypto {
private static final String SUPPORTED_PROTOCOL_SSLV3 = "SSLv3";
private static final String SUPPORTED_PROTOCOL_TLSV1 = "TLSv1";
+ // SSL mode
+ public static long SSL_MODE_HANDSHAKE_CUTTHROUGH = 0x00000040L;
+
+ // SSL options
public static long SSL_OP_NO_SSLv3 = 0x02000000L;
public static long SSL_OP_NO_TLSv1 = 0x04000000L;
@@ -115,7 +121,7 @@ public class NativeCrypto {
// TODO support more than RSA certificates? non-openssl
// SSLEngine implementation did these callbacks during
// handshake after selecting cipher suite, not before
- // handshake.
+ // handshake. Should do the same via SSL_CTX_set_client_cert_cb
final String alias = (client) ?
sslParameters.getKeyManager().chooseClientAlias(new String[] { "RSA" }, null, null) :
sslParameters.getKeyManager().chooseServerAlias("RSA", null, null);
@@ -156,10 +162,18 @@ public class NativeCrypto {
}
+ public static native long SSL_get_mode(int ssl);
+
+ public static native long SSL_set_mode(int ssl, long options);
+
+ public static native long SSL_clear_mode(int ssl, long options);
+
public static native long SSL_get_options(int ssl);
public static native long SSL_set_options(int ssl, long options);
+ public static native long SSL_clear_options(int ssl, long options);
+
public static String[] getSupportedProtocols() {
return new String[] { SUPPORTED_PROTOCOL_SSLV3, SUPPORTED_PROTOCOL_TLSV1 };
}
@@ -178,72 +192,139 @@ public class NativeCrypto {
public static void setEnabledProtocols(int ssl, String[] protocols) {
if (protocols == null) {
- throw new IllegalArgumentException("Provided parameter is null");
+ throw new IllegalArgumentException("protocols == null");
}
+
// openssl uses negative logic letting you disable protocols.
- // so first, lets turn them all off, and in the loop selectively enable
- long options = SSL_get_options(ssl);
- options |= (SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1);
+ // so first, assume we need to set all (disable all ) and clear none (enable none).
+ // in the loop, selectively move bits from set to clear (from disable to enable)
+ long optionsToSet = (SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1);
+ long optionsToClear = 0;
for (int i = 0; i < protocols.length; i++) {
- if (protocols[i].equals(SUPPORTED_PROTOCOL_SSLV3)) {
- options ^= SSL_OP_NO_SSLv3;
- } else if (protocols[i].equals(SUPPORTED_PROTOCOL_TLSV1)) {
- options ^= SSL_OP_NO_TLSv1;
+ String protocol = protocols[i];
+ if (protocol == null) {
+ throw new IllegalArgumentException("protocols[" + i + "] == null");
+ }
+ if (protocol.equals(SUPPORTED_PROTOCOL_SSLV3)) {
+ optionsToSet &= ~SSL_OP_NO_SSLv3;
+ optionsToClear |= SSL_OP_NO_SSLv3;
+ } else if (protocol.equals(SUPPORTED_PROTOCOL_TLSV1)) {
+ optionsToSet &= ~SSL_OP_NO_TLSv1;
+ optionsToClear |= SSL_OP_NO_TLSv1;
} else {
- throw new IllegalArgumentException("Protocol " + protocols[i] +
+ throw new IllegalArgumentException("Protocol " + protocol +
" is not supported");
}
}
- SSL_set_options(ssl, options);
+
+ SSL_set_options(ssl, optionsToSet);
+ SSL_clear_options(ssl, optionsToClear);
+ }
+
+ public static String[] checkEnabledProtocols(String[] protocols) {
+ if (protocols == null) {
+ throw new IllegalArgumentException("protocols parameter is null");
+ }
+ for (int i = 0; i < protocols.length; i++) {
+ String protocol = protocols[i];
+ if (protocol == null) {
+ throw new IllegalArgumentException("protocols[" + i + "] == null");
+ }
+ if ((!protocol.equals(SUPPORTED_PROTOCOL_SSLV3))
+ && (!protocol.equals(SUPPORTED_PROTOCOL_TLSV1))) {
+ throw new IllegalArgumentException("Protocol " + protocol +
+ " is not supported");
+ }
+ }
+ return protocols;
}
public static native String[] SSL_get_ciphers(int ssl);
public static native void SSL_set_cipher_list(int ssl, String ciphers);
- public static void setEnabledCipherSuites(int ssl, String[] suites) {
- if (suites == null) {
- throw new IllegalArgumentException("Provided parameter is null");
- }
-
- // makes sure all suites are valid, throwing on error
- String[] supportedCipherSuites = getSupportedCipherSuites();
- for (String suite : suites) {
- findSuite(supportedCipherSuites, suite);
- }
-
+ public static void setEnabledCipherSuites(int ssl, String[] cipherSuites) {
+ checkEnabledCipherSuites(cipherSuites);
String controlString = "";
- for (int i = 0; i < suites.length; i++) {
+ for (int i = 0; i < cipherSuites.length; i++) {
+ String cipherSuite = cipherSuites[i];
if (i == 0) {
- controlString = suites[i];
+ controlString = cipherSuite;
} else {
- controlString += ":" + suites[i];
+ controlString += ":" + cipherSuite;
}
}
SSL_set_cipher_list(ssl, controlString);
}
+ public static String[] checkEnabledCipherSuites(String[] cipherSuites) {
+ if (cipherSuites == null) {
+ throw new IllegalArgumentException("cipherSuites == null");
+ }
+ // makes sure all suites are valid, throwing on error
+ String[] supportedCipherSuites = getSupportedCipherSuites();
+ for (int i = 0; i < cipherSuites.length; i++) {
+ String cipherSuite = cipherSuites[i];
+ if (cipherSuite == null) {
+ throw new IllegalArgumentException("cipherSuites[" + i + "] == null");
+ }
+ findSuite(supportedCipherSuites, cipherSuite);
+ }
+ return cipherSuites;
+ }
+
private static void findSuite(String[] supportedCipherSuites, String suite) {
- for(int i = 0; i < supportedCipherSuites.length; i++) {
- if (supportedCipherSuites[i].equals(suite)) {
+ for (String supportedCipherSuite : supportedCipherSuites) {
+ if (supportedCipherSuite.equals(suite)) {
return;
}
}
throw new IllegalArgumentException("Protocol " + suite + " is not supported.");
}
- public static native void SSL_free(int ssl);
+ /*
+ * See the OpenSSL ssl.h header file for more information.
+ */
+ public static final int SSL_VERIFY_NONE = 0x00;
+ public static final int SSL_VERIFY_PEER = 0x01;
+ public static final int SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 0x02;
+ public static final int SSL_VERIFY_CLIENT_ONCE = 0x04;
+
+ public static native void SSL_set_verify(int sslNativePointer, int mode) throws IOException;
+
+ public static native void SSL_set_session(int sslNativePointer, int sslSessionNativePointer) throws IOException;
+
+ public static native void SSL_set_session_creation_enabled(int sslNativePointer, boolean creationEnabled) throws IOException;
+
+ /**
+ * Returns the sslSessionNativePointer of the negotiated session
+ */
+ public static native int SSL_do_handshake(int sslNativePointer, Socket sock,
+ CertificateChainVerifier ccv, HandshakeCompletedCallback hcc,
+ int timeout, boolean client_mode) throws IOException, CertificateException;
+
+ public static native byte[][] SSL_get_certificate(int sslNativePointer);
+
+ public static native void SSL_free(int sslNativePointer);
public interface CertificateChainVerifier {
/**
* Verify that we trust the certificate chain is trusted.
*
- * @param bytes An array of certficates in byte form
+ * @param bytes An array of certficates in PEM encode bytes
+ * @param authMethod auth algorithm name
*
- * @throws AlertException if the certificate is untrusted
- * @return false if there are other problems verifying the certificate chain
+ * @throws CertificateException if the certificate is untrusted
+ */
+ public void verifyCertificateChain(byte[][] bytes, String authMethod) throws CertificateException;
+ }
+
+ public interface HandshakeCompletedCallback {
+ /**
+ * Called when SSL handshake is completed. Note that this can
+ * be after SSL_do_handshake returns when handshake cutthrough
+ * is enabled.
*/
- // TODO throw on error in all cases instead of returning false
- public boolean verifyCertificateChain(byte[][] bytes);
+ public void handshakeCompleted();
}
}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
index c79dccf..8d5a43e 100644
--- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
@@ -28,34 +28,31 @@ import java.net.Socket;
*/
public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
private final SSLParameters sslParameters;
- private int sslNativePointer;
+ private String[] enabledProtocols = NativeCrypto.getSupportedProtocols();
+ private String[] enabledCipherSuites = NativeCrypto.getDefaultCipherSuites();
protected OpenSSLServerSocketImpl(SSLParameters sslParameters)
throws IOException {
super();
this.sslParameters = sslParameters;
- this.sslNativePointer = NativeCrypto.SSL_new(sslParameters);
}
protected OpenSSLServerSocketImpl(int port, SSLParameters sslParameters)
throws IOException {
super(port);
this.sslParameters = sslParameters;
- this.sslNativePointer = NativeCrypto.SSL_new(sslParameters);
}
protected OpenSSLServerSocketImpl(int port, int backlog, SSLParameters sslParameters)
throws IOException {
super(port, backlog);
this.sslParameters = sslParameters;
- this.sslNativePointer = NativeCrypto.SSL_new(sslParameters);
}
protected OpenSSLServerSocketImpl(int port, int backlog, InetAddress iAddress, SSLParameters sslParameters)
throws IOException {
super(port, backlog, iAddress);
this.sslParameters = sslParameters;
- this.sslNativePointer = NativeCrypto.SSL_new(sslParameters);
}
@Override
@@ -85,7 +82,7 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
*/
@Override
public String[] getEnabledProtocols() {
- return NativeCrypto.getEnabledProtocols(sslNativePointer);
+ return enabledProtocols.clone();
}
/**
@@ -99,7 +96,7 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
*/
@Override
public void setEnabledProtocols(String[] protocols) {
- NativeCrypto.setEnabledProtocols(sslNativePointer, protocols);
+ enabledProtocols = NativeCrypto.checkEnabledProtocols(protocols);
}
@Override
@@ -109,7 +106,7 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
@Override
public String[] getEnabledCipherSuites() {
- return NativeCrypto.SSL_get_ciphers(sslNativePointer);
+ return enabledCipherSuites.clone();
}
/**
@@ -122,33 +119,7 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
*/
@Override
public void setEnabledCipherSuites(String[] suites) {
- NativeCrypto.setEnabledCipherSuites(sslNativePointer, suites);
- }
-
- /**
- * See the OpenSSL ssl.h header file for more information.
- */
- static private int SSL_VERIFY_NONE = 0x00;
- static private int SSL_VERIFY_PEER = 0x01;
- static private int SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 0x02;
- static private int SSL_VERIFY_CLIENT_ONCE = 0x04;
-
- /**
- * Calls the SSL_set_verify(...) OpenSSL function with the passed int
- * value.
- */
- private static native void nativesetclientauth(int sslNativePointer, int value);
-
- private void setClientAuth() {
- int value = SSL_VERIFY_NONE;
-
- if (sslParameters.getNeedClientAuth()) {
- value |= SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT|SSL_VERIFY_CLIENT_ONCE;
- } else if (sslParameters.getWantClientAuth()) {
- value |= SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE;
- }
-
- nativesetclientauth(sslNativePointer, value);
+ enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites);
}
@Override
@@ -159,7 +130,6 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
@Override
public void setWantClientAuth(boolean want) {
sslParameters.setWantClientAuth(want);
- setClientAuth();
}
@Override
@@ -170,7 +140,6 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
@Override
public void setNeedClientAuth(boolean need) {
sslParameters.setNeedClientAuth(need);
- setClientAuth();
}
@Override
@@ -185,26 +154,10 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
@Override
public Socket accept() throws IOException {
- OpenSSLSocketImpl socket = new OpenSSLSocketImpl(sslParameters, null);
+ OpenSSLSocketImpl socket = new OpenSSLSocketImpl(sslParameters,
+ enabledProtocols.clone(),
+ enabledCipherSuites.clone());
implAccept(socket);
- socket.accept(sslNativePointer);
return socket;
}
-
- /**
- * Unbinds the port if the socket is open.
- */
- @Override
- protected void finalize() throws Throwable {
- if (!isClosed()) close();
- }
-
- @Override
- public synchronized void close() throws IOException {
- if (sslNativePointer != 0) {
- NativeCrypto.SSL_free(sslNativePointer);
- sslNativePointer = 0;
- }
- super.close();
- }
}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
index 2a3908c..f42bcae 100644
--- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
@@ -53,10 +53,10 @@ public class OpenSSLSessionImpl implements SSLSession {
private TwoKeyHashMap values = new TwoKeyHashMap();
private javax.security.cert.X509Certificate[] peerCertificateChain;
protected int sslSessionNativePointer;
- private SSLParameters sslParameters;
private String peerHost;
private int peerPort;
- private final AbstractSessionContext sessionContext;
+ private AbstractSessionContext sessionContext;
+ private byte[] id;
/**
* Class constructor creates an SSL session context given the appropriate
@@ -65,10 +65,10 @@ public class OpenSSLSessionImpl implements SSLSession {
* @param session the Identifier for SSL session
* @param sslParameters the SSL parameters like ciphers' suites etc.
*/
- protected OpenSSLSessionImpl(int sslSessionNativePointer, SSLParameters sslParameters,
+ protected OpenSSLSessionImpl(int sslSessionNativePointer, X509Certificate[] localCertificates,
String peerHost, int peerPort, AbstractSessionContext sessionContext) {
this.sslSessionNativePointer = sslSessionNativePointer;
- this.sslParameters = sslParameters;
+ this.localCertificates = localCertificates;
this.peerHost = peerHost;
this.peerPort = peerPort;
this.sessionContext = sessionContext;
@@ -79,13 +79,13 @@ public class OpenSSLSessionImpl implements SSLSession {
* allows loading the saved session.
* @throws IOException
*/
- OpenSSLSessionImpl(byte[] derData, SSLParameters sslParameters,
+ OpenSSLSessionImpl(byte[] derData,
String peerHost, int peerPort,
javax.security.cert.X509Certificate[] peerCertificateChain,
AbstractSessionContext sessionContext)
throws IOException {
this(initializeNativeImpl(derData, derData.length),
- sslParameters,
+ null,
peerHost,
peerPort,
sessionContext);
@@ -103,10 +103,17 @@ public class OpenSSLSessionImpl implements SSLSession {
* @return array of sessions' identifiers.
*/
public byte[] getId() {
- return getId(sslSessionNativePointer);
+ if (id == null) {
+ resetId();
+ }
+ return id;
}
- private static native byte[] getId(int sslSessionNativePointer);
+ public static native byte[] getId(int sslSessionNativePointer);
+
+ void resetId() {
+ id = getId(sslSessionNativePointer);
+ }
/**
* Get the session object in DER format. This allows saving the session
@@ -179,12 +186,6 @@ public class OpenSSLSessionImpl implements SSLSession {
* were used during the handshaking phase.
*/
public Certificate[] getLocalCertificates() {
- X509Certificate[] localCertificates = null;
- // This implementation only supports RSA certificates.
- String alias = sslParameters.getKeyManager().chooseClientAlias(new String[] { "RSA" }, null, null);
- if (alias != null) {
- localCertificates = sslParameters.getKeyManager().getCertificateChain(alias);
- }
return localCertificates;
}
@@ -271,6 +272,7 @@ public class OpenSSLSessionImpl implements SSLSession {
*
*/
public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ getPeerCertificates();
if (peerCertificates == null) {
throw new SSLPeerUnverifiedException("No peer certificate");
}
@@ -371,6 +373,7 @@ public class OpenSSLSessionImpl implements SSLSession {
*/
public void invalidate() {
isValid = false;
+ sessionContext = null;
}
/**
@@ -476,5 +479,5 @@ public class OpenSSLSessionImpl implements SSLSession {
freeImpl(sslSessionNativePointer);
}
- private static native void freeImpl(int session);
+ public static native void freeImpl(int session);
}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
index 58d2110..edef590 100644
--- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
@@ -36,6 +36,7 @@ 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;
@@ -51,7 +52,7 @@ import org.apache.harmony.security.provider.cert.X509CertImpl;
*/
public class OpenSSLSocketImpl
extends javax.net.ssl.SSLSocket
- implements NativeCrypto.CertificateChainVerifier {
+ implements NativeCrypto.CertificateChainVerifier, NativeCrypto.HandshakeCompletedCallback {
private int sslNativePointer;
private InputStream is;
private OutputStream os;
@@ -59,10 +60,21 @@ public class OpenSSLSocketImpl
private final Object readLock = new Object();
private final Object writeLock = new Object();
private SSLParameters sslParameters;
+ private String[] enabledProtocols;
+ private String[] enabledCipherSuites;
private OpenSSLSessionImpl sslSession;
private Socket socket;
private boolean autoClose;
private boolean handshakeStarted = false;
+
+ /**
+ * Not set to true until the update from native that tells us the
+ * full handshake is complete, since SSL_do_handshake can return
+ * before the handshake is completely done due to
+ * handshake_cutthrough support.
+ */
+ private boolean handshakeCompleted = false;
+
private ArrayList<HandshakeCompletedListener> listeners;
private int timeout = 0;
// BEGIN android-added
@@ -93,6 +105,20 @@ public class OpenSSLSocketImpl
}
/**
+ * Create an OpenSSLSocketImpl from an OpenSSLServerSocketImpl
+ *
+ * @param sslParameters Parameters for the SSL
+ * context
+ * @throws IOException if network fails
+ */
+ protected OpenSSLSocketImpl(SSLParameters sslParameters,
+ String[] enabledProtocols,
+ String[] enabledCipherSuites) throws IOException {
+ super();
+ init(sslParameters, enabledProtocols, enabledCipherSuites);
+ }
+
+ /**
* Class constructor with 3 parameters
*
* @throws IOException if network fails
@@ -166,47 +192,35 @@ public class OpenSSLSocketImpl
* future handshaking.
*/
private void init(SSLParameters sslParameters) throws IOException {
- this.sslParameters = sslParameters;
- this.sslNativePointer = NativeCrypto.SSL_new(sslParameters);
- updateInstanceCount(1);
+ init(sslParameters,
+ NativeCrypto.getSupportedProtocols(),
+ NativeCrypto.getDefaultCipherSuites());
}
/**
- * Construct a OpenSSLSocketImpl from an SSLParameters and an
- * existing SSL native pointer. Used for transitioning accepting
- * the OpenSSLSocketImpl within OpenSSLServerSocketImpl.
+ * Initialize the SSL socket and set the certificates for the
+ * future handshaking.
*/
- protected OpenSSLSocketImpl(SSLParameters sslParameters,
- OpenSSLServerSocketImpl dummy) {
- super();
- this.sslParameters = (SSLParameters) sslParameters.clone();
+ private void init(SSLParameters sslParameters,
+ String[] enabledProtocols,
+ String[] enabledCipherSuites) throws IOException {
+ this.sslParameters = sslParameters;
+ this.enabledProtocols = enabledProtocols;
+ this.enabledCipherSuites = enabledCipherSuites;
updateInstanceCount(1);
}
/**
- * Adds OpenSSL functionality to the existing socket and starts the SSL
- * handshaking.
- */
- private native boolean nativeconnect(int sslNativePointer, Socket sock, int timeout, boolean client_mode, int sslSessionNativePointer) throws IOException;
- private native int nativegetsslsession(int sslNativePointer);
- private native String nativecipherauthenticationmethod(int sslNativePointer);
-
- /**
* Gets the suitable session reference from the session cache container.
*
* @return OpenSSLSessionImpl
*/
- private OpenSSLSessionImpl getCachedClientSession() {
- if (!sslParameters.getUseClientMode()) {
- return null;
- }
+ private OpenSSLSessionImpl getCachedClientSession(ClientSessionContext sessionContext) {
if (super.getInetAddress() == null ||
super.getInetAddress().getHostAddress() == null ||
super.getInetAddress().getHostName() == null) {
return null;
}
- ClientSessionContext sessionContext
- = sslParameters.getClientSessionContext();
return (OpenSSLSessionImpl) sessionContext.getSession(
super.getInetAddress().getHostName(),
super.getPort());
@@ -229,7 +243,15 @@ public class OpenSSLSocketImpl
*
* @throws <code>IOException</code> if network fails
*/
- public synchronized void startHandshake() throws IOException {
+ public void startHandshake() throws IOException {
+ startHandshake(true);
+ }
+
+ /**
+ * Perform the handshake
+ * @param full If true, disable handshake cutthrough for a fully synchronous handshake
+ */
+ public synchronized void startHandshake(boolean full) throws IOException {
synchronized (handshakeLock) {
if (!handshakeStarted) {
handshakeStarted = true;
@@ -238,11 +260,56 @@ public class OpenSSLSocketImpl
}
}
- OpenSSLSessionImpl session = getCachedClientSession();
+ this.sslNativePointer = NativeCrypto.SSL_new(sslParameters);
+ // TODO move more code out of NativeCrypto.SSL_new
+ NativeCrypto.setEnabledProtocols(sslNativePointer, enabledProtocols);
+ NativeCrypto.setEnabledCipherSuites(sslNativePointer, enabledCipherSuites);
+
+ boolean enableSessionCreation = sslParameters.getEnableSessionCreation();
+ if (!enableSessionCreation) {
+ NativeCrypto.SSL_set_session_creation_enabled(sslNativePointer,
+ enableSessionCreation);
+ }
+
+ boolean client = sslParameters.getUseClientMode();
- // Check if it's allowed to create a new session (default is true)
- if (session == null && !sslParameters.getEnableSessionCreation()) {
- throw new SSLHandshakeException("SSL Session may not be created");
+ AbstractSessionContext sessionContext;
+ OpenSSLSessionImpl session;
+ if (client) {
+ // look for client session to reuse
+ ClientSessionContext clientSessionContext = sslParameters.getClientSessionContext();
+ sessionContext = clientSessionContext;
+ session = getCachedClientSession(clientSessionContext);
+ if (session != null) {
+ NativeCrypto.SSL_set_session(sslNativePointer, session.sslSessionNativePointer);
+ }
+ } else {
+ sessionContext = sslParameters.getServerSessionContext();
+ session = null;
+ }
+
+ // setup peer certificate verification
+ if (client) {
+ // TODO support for anonymous cipher would require us to conditionally use SSL_VERIFY_NONE
+ } else {
+ // needing client auth takes priority...
+ if (sslParameters.getNeedClientAuth()) {
+ NativeCrypto.SSL_set_verify(sslNativePointer,
+ NativeCrypto.SSL_VERIFY_PEER|
+ NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT|
+ NativeCrypto.SSL_VERIFY_CLIENT_ONCE);
+ // ... over just wanting it...
+ } else if (sslParameters.getWantClientAuth()) {
+ NativeCrypto.SSL_set_verify(sslNativePointer,
+ NativeCrypto.SSL_VERIFY_PEER|
+ NativeCrypto.SSL_VERIFY_CLIENT_ONCE);
+ }
+ // ... and it defaults properly so we don't need call SSL_set_verify in the common case.
+ }
+
+ if (client && full) {
+ // we want to do a full synchronous handshake, so turn off cutthrough
+ NativeCrypto.SSL_clear_mode(sslNativePointer, NativeCrypto.SSL_MODE_HANDSHAKE_CUTTHROUGH);
}
// BEGIN android-added
@@ -253,58 +320,60 @@ public class OpenSSLSocketImpl
}
// END android-added
+
Socket socket = this.socket != null ? this.socket : this;
- int sslSessionNativePointer = session != null ? session.sslSessionNativePointer : 0;
- boolean reusedSession = nativeconnect(sslNativePointer, socket, timeout,
- sslParameters.getUseClientMode(), sslSessionNativePointer);
- if (reusedSession) {
- // nativeconnect shouldn't return true if the session is not
- // done
+ int sslSessionNativePointer;
+ try {
+ sslSessionNativePointer = NativeCrypto.SSL_do_handshake(sslNativePointer, socket, this, this, timeout, client);
+ } catch (CertificateException e) {
+ throw new SSLPeerUnverifiedException(e.getMessage());
+ }
+ byte[] sessionId = OpenSSLSessionImpl.getId(sslSessionNativePointer);
+ sslSession = (OpenSSLSessionImpl) sessionContext.getSession(sessionId);
+ if (sslSession != null) {
session.lastAccessedTime = System.currentTimeMillis();
- sslSession = session;
-
LoggerHolder.logger.fine("Reused cached session for "
- + getInetAddress().getHostName() + ".");
+ + getInetAddress() + ".");
+ OpenSSLSessionImpl.freeImpl(sslSessionNativePointer);
} else {
- if (session != null) {
- LoggerHolder.logger.fine("Reuse of cached session for "
- + getInetAddress().getHostName() + " failed.");
+ if (!enableSessionCreation) {
+ // Should have been prevented by NativeCrypto.SSL_set_session_creation_enabled
+ throw new IllegalStateException("SSL Session may not be created");
+ }
+ byte[][] localCertificatesBytes = NativeCrypto.SSL_get_certificate(sslNativePointer);
+ X509Certificate[] localCertificates;
+ if (localCertificatesBytes == null) {
+ localCertificates = null;
} else {
- LoggerHolder.logger.fine("Created new session for "
- + getInetAddress().getHostName() + ".");
+ localCertificates = new X509Certificate[localCertificatesBytes.length];
+ for (int i = 0; i < localCertificatesBytes.length; i++) {
+ try {
+ // TODO do not go through PEM decode, DER encode, DER decode
+ localCertificates[i]
+ = new X509CertImpl(
+ javax.security.cert.X509Certificate.getInstance(
+ localCertificatesBytes[i]).getEncoded());
+ } catch (javax.security.cert.CertificateException e) {
+ throw new IOException("Problem decoding local certificate", e);
+ }
+ }
}
- AbstractSessionContext sessionContext =
- (sslParameters.getUseClientMode()) ?
- sslParameters.getClientSessionContext() :
- sslParameters.getServerSessionContext();
- sslSessionNativePointer = nativegetsslsession(sslNativePointer);
if (address == null) {
- sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, sslParameters,
+ sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, localCertificates,
super.getInetAddress().getHostName(),
super.getPort(), sessionContext);
} else {
- sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, sslParameters,
+ sslSession = new OpenSSLSessionImpl(sslSessionNativePointer, localCertificates,
address.getHostName(), address.getPort(),
sessionContext);
}
-
- try {
- X509Certificate[] peerCertificates = (X509Certificate[])
- sslSession.getPeerCertificates();
-
- if (peerCertificates == null
- || peerCertificates.length == 0) {
- throw new SSLException("Server sends no certificate");
- }
-
- String authMethod = nativecipherauthenticationmethod(sslNativePointer);
- sslParameters.getTrustManager().checkServerTrusted(peerCertificates,
- authMethod);
+ // putSession will be done later in handshakeCompleted() callback
+ if (handshakeCompleted) {
sessionContext.putSession(sslSession);
- } catch (CertificateException e) {
- throw new SSLException("Not trusted server certificate", e);
}
+ LoggerHolder.logger.fine("Created new session for "
+ + getInetAddress().getHostName() + ".");
}
// BEGIN android-added
@@ -314,74 +383,91 @@ public class OpenSSLSocketImpl
}
// END android-added
- if (listeners != null) {
- // notify the listeners
- HandshakeCompletedEvent event =
- new HandshakeCompletedEvent(this, sslSession);
- int size = listeners.size();
- for (int i = 0; i < size; i++) {
- listeners.get(i).handshakeCompleted(event);
- }
+ // notifyHandshakeCompletedListeners will be done later in handshakeCompleted() callback
+ if (handshakeCompleted) {
+ notifyHandshakeCompletedListeners();
}
- }
- // To be synchronized because of the verify_callback
- native synchronized int nativeaccept(int sslNativePointer, Socket socketObject);
+ }
/**
- * Performs the first part of a SSL/TLS handshaking process with a given
- * 'host' connection and initializes the SSLSession.
+ * Implementation of NativeCrypto.HandshakeCompletedCallback
+ * invoked via JNI from info_callback
*/
- protected void accept(int serverSslNativePointer) throws IOException {
- // Must be set because no handshaking is necessary
- // in this situation
- handshakeStarted = true;
+ public void handshakeCompleted() {
+ handshakeCompleted = true;
- sslNativePointer = nativeaccept(serverSslNativePointer, this);
+ // If sslSession is null, the handshake was completed during
+ // the call to NativeCrypto.SSL_do_handshake and not during a
+ // later read operation. That means we do not need to fixup
+ // the SSLSession and session cache or notify
+ // HandshakeCompletedListeners, it will be done in
+ // startHandshake.
+ if (sslSession == null) {
+ return;
+ }
- ServerSessionContext sessionContext
- = sslParameters.getServerSessionContext();
- sslSession = new OpenSSLSessionImpl(nativegetsslsession(sslNativePointer),
- sslParameters, super.getInetAddress().getHostName(),
- super.getPort(), sessionContext);
- sslSession.lastAccessedTime = System.currentTimeMillis();
+ // reset session id from the native pointer and update the
+ // appropriate cache.
+ sslSession.resetId();
+ AbstractSessionContext sessionContext =
+ (sslParameters.getUseClientMode())
+ ? sslParameters.getClientSessionContext()
+ : sslParameters.getServerSessionContext();
sessionContext.putSession(sslSession);
+
+ // let listeners know we are finally done
+ notifyHandshakeCompletedListeners();
+ }
+
+ private void notifyHandshakeCompletedListeners() {
+ if (listeners != null && !listeners.isEmpty()) {
+ // notify the listeners
+ HandshakeCompletedEvent event =
+ new HandshakeCompletedEvent(this, sslSession);
+ for (HandshakeCompletedListener listener : listeners) {
+ try {
+ listener.handshakeCompleted(event);
+ } catch (RuntimeException e) {
+ // TODO log?
+ }
+ }
+ }
}
/**
* Implementation of NativeCrypto.CertificateChainVerifier.
*
- * Callback method for the OpenSSL native certificate verification process.
+ * @param bytes An array of certficates in PEM encode bytes
+ * @param authMethod auth algorithm name
*
- * @param bytes Byte array containing the cert's
- * information.
- * @return false if the certificate verification fails or true if OK
+ * @throws CertificateException if the certificate is untrusted
*/
@SuppressWarnings("unused")
- public boolean verifyCertificateChain(byte[][] bytes) {
+ public void verifyCertificateChain(byte[][] bytes, String authMethod) throws CertificateException {
try {
- X509Certificate[] peerCertificateChain
- = new X509Certificate[bytes.length];
- for(int i = 0; i < bytes.length; i++) {
+ X509Certificate[] peerCertificateChain = new X509Certificate[bytes.length];
+ for (int i = 0; i < bytes.length; i++) {
peerCertificateChain[i] =
new X509CertImpl(javax.security.cert.X509Certificate.getInstance(bytes[i]).getEncoded());
}
- try {
- // TODO "null" String
- sslParameters.getTrustManager().checkClientTrusted(peerCertificateChain, "null");
- } catch (CertificateException e) {
- throw new AlertException(AlertProtocol.BAD_CERTIFICATE,
- new SSLException("Not trusted server certificate", e));
+ boolean client = sslParameters.getUseClientMode();
+ if (client) {
+ if (peerCertificateChain == null
+ || peerCertificateChain.length == 0) {
+ throw new SSLException("Server sends no certificate");
+ }
+ sslParameters.getTrustManager().checkServerTrusted(peerCertificateChain, authMethod);
+ } else {
+ sslParameters.getTrustManager().checkClientTrusted(peerCertificateChain, authMethod);
}
- } catch (javax.security.cert.CertificateException e) {
- // TODO throw in all cases for consistency
- return false;
- } catch (IOException e) {
- // TODO throw in all cases for consistency
- return false;
+
+ } catch (CertificateException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
}
- return true;
}
/**
@@ -421,20 +507,22 @@ public class OpenSSLSocketImpl
}
}
+ /**
+ * This method is not supported for this SSLSocket implementation
+ * because reading from an SSLSocket may involve writing to the
+ * network.
+ */
public void shutdownInput() throws IOException {
- if (socket == null) {
- super.shutdownInput();
- return;
- }
- socket.shutdownInput();
+ throw new UnsupportedOperationException();
}
+ /**
+ * This method is not supported for this SSLSocket implementation
+ * because writing to an SSLSocket may involve reading from the
+ * network.
+ */
public void shutdownOutput() throws IOException {
- if (socket == null) {
- super.shutdownOutput();
- return;
- }
- socket.shutdownOutput();
+ throw new UnsupportedOperationException();
}
/**
@@ -455,7 +543,7 @@ public class OpenSSLSocketImpl
/* Note: When startHandshake() throws an exception, no
* SSLInputStream object will be created.
*/
- OpenSSLSocketImpl.this.startHandshake();
+ OpenSSLSocketImpl.this.startHandshake(false);
}
/**
@@ -499,7 +587,7 @@ public class OpenSSLSocketImpl
/* Note: When startHandshake() throws an exception, no
* SSLOutputStream object will be created.
*/
- OpenSSLSocketImpl.this.startHandshake();
+ OpenSSLSocketImpl.this.startHandshake(false);
}
/**
@@ -534,7 +622,7 @@ public class OpenSSLSocketImpl
*/
public SSLSession getSession() {
try {
- startHandshake();
+ startHandshake(true);
} catch (IOException e) {
// return an invalid session with
// invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
@@ -616,7 +704,7 @@ public class OpenSSLSocketImpl
* @return an array of cipher suite names
*/
public String[] getEnabledCipherSuites() {
- return NativeCrypto.SSL_get_ciphers(sslNativePointer);
+ return enabledCipherSuites.clone();
}
/**
@@ -630,7 +718,7 @@ public class OpenSSLSocketImpl
* is null.
*/
public void setEnabledCipherSuites(String[] suites) {
- NativeCrypto.setEnabledCipherSuites(sslNativePointer, suites);
+ enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites);
}
/**
@@ -650,7 +738,7 @@ public class OpenSSLSocketImpl
*/
@Override
public String[] getEnabledProtocols() {
- return NativeCrypto.getEnabledProtocols(sslNativePointer);
+ return enabledProtocols.clone();
}
/**
@@ -664,7 +752,7 @@ public class OpenSSLSocketImpl
*/
@Override
public synchronized void setEnabledProtocols(String[] protocols) {
- NativeCrypto.setEnabledProtocols(sslNativePointer, protocols);
+ enabledProtocols = NativeCrypto.checkEnabledProtocols(protocols);
}
/**
@@ -793,6 +881,7 @@ public class OpenSSLSocketImpl
synchronized (handshakeLock) {
if (!handshakeStarted) {
+ // prevent further attemps to start handshake
handshakeStarted = true;
synchronized (this) {
@@ -862,75 +951,24 @@ public class OpenSSLSocketImpl
}
protected void finalize() throws IOException {
- updateInstanceCount(-1);
-
- if (sslNativePointer == 0) {
- /*
- * It's already been closed, so there's no need to do anything
- * more at this point.
- */
- return;
- }
-
- // Note the underlying socket up-front, for possible later use.
- Socket underlyingSocket = socket;
-
- // Fire up a thread to (hopefully) do all the real work.
- Finalizer f = new Finalizer();
- f.setDaemon(true);
- f.start();
-
/*
- * Give the finalizer thread one second to run. If it fails to
- * terminate in that time, interrupt it (which may help if it
- * is blocked on an interruptible I/O operation), make a note
- * in the log, and go ahead and close the underlying socket if
- * possible.
+ * 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.
*/
- try {
- f.join(1000);
- } catch (InterruptedException ex) {
- // Reassert interrupted status.
- Thread.currentThread().interrupt();
- }
-
- if (f.isAlive()) {
- f.interrupt();
- Logger.global.log(Level.SEVERE,
- "Slow finalization of SSL socket (" + this + ", for " +
- underlyingSocket + ")");
- if ((underlyingSocket != null) && !underlyingSocket.isClosed()) {
- underlyingSocket.close();
- }
- }
- }
-
- /**
- * Helper class for a thread that knows how to call
- * {@link OpenSSLSocketImpl#close} on behalf of instances being finalized,
- * since that call can take arbitrarily long (e.g., due to a slow network),
- * and an overly long-running finalizer will cause the process to be
- * totally aborted.
- */
- private class Finalizer extends Thread {
- public void run() {
- Socket underlyingSocket = socket; // for error reporting
- try {
- close();
- } catch (IOException ex) {
- /*
- * Clear interrupted status, so that the Logger call
- * immediately below won't get spuriously interrupted.
- */
- Thread.interrupted();
-
- Logger.global.log(Level.SEVERE,
- "Trouble finalizing SSL socket (" +
- OpenSSLSocketImpl.this + ", for " + underlyingSocket +
- ")",
- ex);
- }
- }
+ updateInstanceCount(-1);
+ free();
}
/**
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java
index 525756d..9c6f0a0 100644
--- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java
@@ -17,22 +17,27 @@
package org.apache.harmony.xnet.provider.jsse;
+import java.security.InvalidAlgorithmParameterException;
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.security.InvalidAlgorithmParameterException;
import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
+import org.apache.harmony.security.provider.cert.X509CertImpl;
+
/**
* The instances of this class incapsulate all the info
* about enabled cipher suites and protocols,
@@ -109,9 +114,9 @@ public class SSLParameters implements Cloneable {
SSLServerSessionCache serverCache)
throws KeyManagementException {
this.serverSessionContext
- = new ServerSessionContext(this, NativeCrypto.SSL_CTX_new(), serverCache);
+ = new ServerSessionContext(NativeCrypto.SSL_CTX_new(), serverCache);
this.clientSessionContext
- = new ClientSessionContext(this, NativeCrypto.SSL_CTX_new(), clientCache);
+ = new ClientSessionContext(NativeCrypto.SSL_CTX_new(), clientCache);
// END android-changed
try {
// initialize key manager
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java
index cd74d57..922de2b 100644
--- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java
@@ -177,6 +177,7 @@ public class SSLSessionImpl implements SSLSession, Cloneable {
this.cipherSuite = CipherSuite.TLS_NULL_WITH_NULL_NULL;
id = new byte[0];
isServer = false;
+ isValid = false;
} else {
this.cipherSuite = cipher_suite;
id = new byte[32];
@@ -275,7 +276,7 @@ public class SSLSessionImpl implements SSLSession, Cloneable {
}
public String getProtocol() {
- return protocol.name;
+ return (protocol == null) ? "NONE" : protocol.name;
}
public SSLSessionContext getSessionContext() {
@@ -307,6 +308,7 @@ public class SSLSessionImpl implements SSLSession, Cloneable {
public void invalidate() {
isValid = false;
+ context = null;
}
public boolean isValid() {
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java
index c379fee..160188d 100644
--- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java
@@ -45,11 +45,18 @@ public class ServerSessionContext extends AbstractSessionContext {
private final SSLServerSessionCache persistentCache;
- public ServerSessionContext(SSLParameters parameters,
- int sslCtxNativePointer,
- SSLServerSessionCache persistentCache) {
- super(parameters, sslCtxNativePointer, 100, 0);
+ public ServerSessionContext(int sslCtxNativePointer,
+ SSLServerSessionCache persistentCache) {
+ super(sslCtxNativePointer, 100, 0);
this.persistentCache = persistentCache;
+
+ // 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 to use SSL_CTX_sessions to remove from native cache
}
Iterator<SSLSession> sessionIterator() {
diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
index 8af33aa..86eaadf 100644
--- a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
+++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
@@ -36,14 +36,16 @@
#include <openssl/rsa.h>
#include <openssl/ssl.h>
-/**
- * Structure to hold JNI state for openssl callback
- */
-struct jsse_ssl_app_data_t {
- JNIEnv* env;
- jobject object;
-};
-
+#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("\n"))
+#else
+#define JNI_TRACE(...) ((void)0)
+#endif
/**
* Frees the SSL error state.
*
@@ -90,16 +92,16 @@ static void throwSocketTimeoutException(JNIEnv* env, const char* message) {
}
/**
- * Throws a java.io.IOException with the given string as a message.
+ * Throws a javax.net.ssl.SSLException with the given string as a message.
*/
-static void throwIOExceptionStr(JNIEnv* env, const char* message) {
- if (jniThrowException(env, "java/io/IOException", message)) {
+static void throwSSLExceptionStr(JNIEnv* env, const char* message) {
+ if (jniThrowException(env, "javax/net/ssl/SSLException", message)) {
LOGE("Unable to throw");
}
}
/**
- * Throws an IOException with a message constructed from the current
+ * Throws an SSLException with a message constructed from the current
* SSL errors. This will also log the errors.
*
* @param env the JNI environment
@@ -107,7 +109,7 @@ static void throwIOExceptionStr(JNIEnv* env, const char* message) {
* @param sslErrorCode error code returned from SSL_get_error()
* @param message null-ok; general error message
*/
-static void throwIOExceptionWithSslErrors(JNIEnv* env, int sslReturnCode,
+static void throwSSLExceptionWithSslErrors(JNIEnv* env, int sslReturnCode,
int sslErrorCode, const char* message) {
const char* messageStr = NULL;
char* str;
@@ -148,8 +150,9 @@ static void throwIOExceptionWithSslErrors(JNIEnv* env, int sslReturnCode,
// Prepend either our explicit message or a default one.
if (asprintf(&str, "%s: %s",
- (message != NULL) ? message : "SSL error", messageStr) == 0) {
- throwIOExceptionStr(env, messageStr);
+ (message != NULL) ? message : "SSL error", messageStr) <= 0) {
+ // problem with asprintf
+ throwSSLExceptionStr(env, messageStr);
LOGV("%s", messageStr);
freeSslErrorState();
return;
@@ -203,7 +206,7 @@ static void throwIOExceptionWithSslErrors(JNIEnv* env, int sslReturnCode,
}
}
- throwIOExceptionStr(env, allocStr);
+ throwSSLExceptionStr(env, allocStr);
LOGV("%s", allocStr);
free(allocStr);
@@ -214,7 +217,7 @@ static void throwIOExceptionWithSslErrors(JNIEnv* env, int sslReturnCode,
* Helper function that grabs the casts an ssl pointer and then checks for nullness.
* If this function returns NULL and <code>throwIfNull</code> is
* passed as <code>true</code>, then this function will call
- * <code>throwIOExceptionStr</code> before returning, so in this case of
+ * <code>throwSSLExceptionStr</code> before returning, so in this case of
* NULL, a caller of this function should simply return and allow JNI
* to do its thing.
*
@@ -226,7 +229,7 @@ static void throwIOExceptionWithSslErrors(JNIEnv* env, int sslReturnCode,
static SSL* getSslPointer(JNIEnv* env, int ssl_address, bool throwIfNull) {
SSL* ssl = reinterpret_cast<SSL*>(static_cast<uintptr_t>(ssl_address));
if ((ssl == NULL) && throwIfNull) {
- throwIOExceptionStr(env, "null SSL pointer");
+ throwSSLExceptionStr(env, "null SSL pointer");
}
return ssl;
@@ -640,14 +643,479 @@ static const char* get_content_type(int content_type) {
*/
static void ssl_msg_callback_LOG(int write_p, int ssl_version, int content_type,
const void *buf, size_t len, SSL* ssl, void* arg) {
- LOGD("SSL %p %s %s %s %p %d %p",
- ssl,
- (write_p) ? "send" : "recv",
- get_ssl_version(ssl_version),
- get_content_type(content_type),
- buf,
- len,
- arg);
+ JNI_TRACE("ssl=%p SSL msg %s %s %s %p %d %p",
+ ssl,
+ (write_p) ? "send" : "recv",
+ get_ssl_version(ssl_version),
+ get_content_type(content_type),
+ buf,
+ len,
+ arg);
+}
+
+/**
+ * Based on example logging call back from SSL_CTX_set_info_callback man page
+ */
+static void info_callback_LOG(const SSL *s, int where, int ret)
+{
+ int w = where & ~SSL_ST_MASK;
+ const char* str;
+ if (w & SSL_ST_CONNECT) {
+ str = "SSL_connect";
+ } else if (w & SSL_ST_ACCEPT) {
+ str = "SSL_accept";
+ } else {
+ str = "undefined";
+ }
+
+ if (where & SSL_CB_LOOP) {
+ JNI_TRACE("ssl=%p %s:%s %s", s, str, SSL_state_string(s), SSL_state_string_long(s));
+ } else if (where & SSL_CB_ALERT) {
+ str = (where & SSL_CB_READ) ? "read" : "write";
+ JNI_TRACE("ssl=%p SSL3 alert %s:%s:%s %s %s",
+ s,
+ str,
+ SSL_alert_type_string(ret),
+ SSL_alert_desc_string(ret),
+ SSL_alert_type_string_long(ret),
+ SSL_alert_desc_string_long(ret));
+ } else if (where & SSL_CB_EXIT) {
+ if (ret == 0) {
+ JNI_TRACE("ssl=%p %s:failed exit in %s %s",
+ s, str, SSL_state_string(s), SSL_state_string_long(s));
+ } else if (ret < 0) {
+ JNI_TRACE("ssl=%p %s:error exit in %s %s",
+ s, str, SSL_state_string(s), SSL_state_string_long(s));
+ } else if (ret == 1) {
+ JNI_TRACE("ssl=%p %s:ok exit in %s %s",
+ s, str, SSL_state_string(s), SSL_state_string_long(s));
+ } else {
+ JNI_TRACE("ssl=%p %s:unknown exit %d in %s %s",
+ s, str, ret, SSL_state_string(s), SSL_state_string_long(s));
+ }
+ } else if (where & SSL_CB_HANDSHAKE_START) {
+ JNI_TRACE("ssl=%p handshake start in %s %s",
+ s, SSL_state_string(s), SSL_state_string_long(s));
+ } else if (where & SSL_CB_HANDSHAKE_DONE) {
+ JNI_TRACE("ssl=%p handshake done in %s %s",
+ s, SSL_state_string(s), SSL_state_string_long(s));
+ } else {
+ JNI_TRACE("ssl=%p %s:unknown where %d in %s %s",
+ s, str, where, SSL_state_string(s), SSL_state_string_long(s));
+ }
+}
+
+/**
+ * Returns an array containing all the X509 certificate's bytes.
+ */
+static jobjectArray getCertificateBytes(JNIEnv* env,
+ const STACK_OF(X509) *chain)
+{
+ if (chain == NULL) {
+ // Chain can be NULL if the associated cipher doesn't do certs.
+ return NULL;
+ }
+
+ int count = sk_X509_num(chain);
+ if (count <= 0) {
+ NULL;
+ }
+
+ jobjectArray joa = env->NewObjectArray(count, env->FindClass("[B"), NULL);
+ if (joa == NULL) {
+ return NULL;
+ }
+
+ BIO* bio = BIO_new(BIO_s_mem());
+
+ // LOGD("Start fetching the certificates");
+ for (int i = 0; i < count; i++) {
+ X509* cert = sk_X509_value(chain, i);
+
+ BIO_reset(bio);
+ PEM_write_bio_X509(bio, cert);
+
+ BUF_MEM* bptr;
+ BIO_get_mem_ptr(bio, &bptr);
+ jbyteArray bytes = env->NewByteArray(bptr->length);
+
+ if (bytes == NULL) {
+ /*
+ * Indicate an error by resetting joa to NULL. It will
+ * eventually get gc'ed.
+ */
+ joa = NULL;
+ break;
+ }
+ jbyte* src = reinterpret_cast<jbyte*>(bptr->data);
+ env->SetByteArrayRegion(bytes, 0, bptr->length, src);
+ env->SetObjectArrayElement(joa, i, bytes);
+ }
+
+ // LOGD("Certificate fetching complete");
+ BIO_free(bio);
+ return joa;
+}
+
+/**
+ * Our additional application data needed for getting synchronization right.
+ * This maybe warrants a bit of lengthy prose:
+ *
+ * (1) We use a flag to reflect whether we consider the SSL connection alive.
+ * Any read or write attempt loops will be cancelled once this flag becomes 0.
+ *
+ * (2) We use an int to count the number of threads that are blocked by the
+ * underlying socket. This may be at most two (one reader and one writer), since
+ * the Java layer ensures that no more threads will enter the native code at the
+ * same time.
+ *
+ * (3) The pipe is used primarily as a means of cancelling a blocking select()
+ * when we want to close the connection (aka "emergency button"). It is also
+ * necessary for dealing with a possible race condition situation: There might
+ * be cases where both threads see an SSL_ERROR_WANT_READ or
+ * SSL_ERROR_WANT_WRITE. Both will enter a select() with the proper argument.
+ * If one leaves the select() successfully before the other enters it, the
+ * "success" event is already consumed and the second thread will be blocked,
+ * possibly forever (depending on network conditions).
+ *
+ * The idea for solving the problem looks like this: Whenever a thread is
+ * successful in moving around data on the network, and it knows there is
+ * another thread stuck in a select(), it will write a byte to the pipe, waking
+ * up the other thread. A thread that returned from select(), on the other hand,
+ * knows whether it's been woken up by the pipe. If so, it will consume the
+ * byte, and the original state of affairs has been restored.
+ *
+ * The pipe may seem like a bit of overhead, but it fits in nicely with the
+ * other file descriptors of the select(), so there's only one condition to wait
+ * for.
+ *
+ * (4) Finally, a mutex is needed to make sure that at most one thread is in
+ * either SSL_read() or SSL_write() at any given time. This is an OpenSSL
+ * requirement. We use the same mutex to guard the field for counting the
+ * waiting threads.
+ *
+ * Note: The current implementation assumes that we don't have to deal with
+ * problems induced by multiple cores or processors and their respective
+ * memory caches. One possible problem is that of inconsistent views on the
+ * "aliveAndKicking" field. This could be worked around by also enclosing all
+ * accesses to that field inside a lock/unlock sequence of our mutex, but
+ * currently this seems a bit like overkill. Marking volatile at the very least.
+ *
+ * During handshaking, three additional fields are used to up-call into
+ * Java to perform certificate verification and handshake completion.
+ *
+ * (5) the JNIEnv so we can invoke the Java callback
+ *
+ * (6) a NativeCrypto.CertificateChainVerifier to call with the peer certificate chain
+ *
+ * (7) a NativeCrypto.HandshakeCompletedCallback to call back when handshake is done
+ *
+ * These fields are cleared by the info_callback the handshake has
+ * completed. SSL_VERIFY_CLIENT_ONCE is currently used to disable
+ * renegotiation but if that changes, care would need to be taken to
+ * maintain an appropriate JNIEnv on any downcall to openssl that
+ * could result in an upcall to Java. The current code does try to
+ * cover these cases by conditionally setting the JNIenv on calls that
+ * can read and write to the SSL such as SSL_do_handshake, SSL_read,
+ * SSL_write, and SSL_shutdown if handshaking is not complete.
+ *
+ */
+class AppData {
+ public:
+ volatile int aliveAndKicking;
+ int waitingThreads;
+ int fdsEmergency[2];
+ MUTEX_TYPE mutex;
+ JNIEnv* env;
+ jobject certificateChainVerifier;
+ jobject handshakeCompletedCallback;
+
+ /**
+ * Creates our application data and attaches it to a given SSL connection.
+ *
+ * @param ssl The SSL connection to attach the data to.
+ * @param env The JNIEnv
+ * @param ccv The CertificateChainVerifier
+ * @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;
+ if (pipe(appData->fdsEmergency) == -1) {
+ return NULL;
+ }
+ if (MUTEX_SETUP(appData->mutex) == -1) {
+ return NULL;
+ }
+ return appData;
+ }
+
+ private:
+ AppData(JNIEnv* e, jobject ccv, jobject hcc) :
+ aliveAndKicking(1),
+ waitingThreads(0),
+ env(e),
+ certificateChainVerifier(ccv),
+ handshakeCompletedCallback(hcc) {}
+
+ /**
+ * Destroys our application data, cleaning up everything in the process.
+ */
+ public:
+ ~AppData() {
+ aliveAndKicking = 0;
+ if (fdsEmergency[0] != -1) {
+ close(fdsEmergency[0]);
+ }
+ if (fdsEmergency[1] != -1) {
+ close(fdsEmergency[1]);
+ }
+ MUTEX_CLEANUP(mutex);
+ }
+
+ void setEnv(JNIEnv* e) {
+ if (handshakeCompletedCallback == NULL) {
+ return;
+ }
+ env = e;
+ }
+ void clearEnv() {
+ env = NULL;
+ }
+};
+
+/**
+ * Dark magic helper function that checks, for a given SSL session, whether it
+ * can SSL_read() or SSL_write() without blocking. Takes into account any
+ * concurrent attempts to close the SSL session from the Java side. This is
+ * needed to get rid of the hangs that occur when thread #1 closes the SSLSocket
+ * while thread #2 is sitting in a blocking read or write. The type argument
+ * specifies whether we are waiting for readability or writability. It expects
+ * to be passed either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, since we
+ * only need to wait in case one of these problems occurs.
+ *
+ * @param type Either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE
+ * @param fd The file descriptor to wait for (the underlying socket)
+ * @param data The application data structure with mutex info etc.
+ * @param timeout The timeout value for select call, with the special value
+ * 0 meaning no timeout at all (wait indefinitely). Note: This is
+ * the Java semantics of the timeout value, not the usual
+ * select() semantics.
+ * @return The result of the inner select() call, -1 on additional errors
+ */
+static int sslSelect(int type, int fd, AppData* appData, int timeout) {
+ fd_set rfds;
+ fd_set wfds;
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+
+ if (type == SSL_ERROR_WANT_READ) {
+ FD_SET(fd, &rfds);
+ } else {
+ FD_SET(fd, &wfds);
+ }
+
+ FD_SET(appData->fdsEmergency[0], &rfds);
+
+ int max = fd > appData->fdsEmergency[0] ? fd : appData->fdsEmergency[0];
+
+ // Build a struct for the timeout data if we actually want a timeout.
+ struct timeval tv;
+ struct timeval *ptv;
+ if (timeout > 0) {
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = 0;
+ ptv = &tv;
+ } else {
+ ptv = NULL;
+ }
+
+ // LOGD("Doing select() for SSL_ERROR_WANT_%s...", type == SSL_ERROR_WANT_READ ? "READ" : "WRITE");
+ int result = select(max + 1, &rfds, &wfds, NULL, ptv);
+ // LOGD("Returned from select(), result is %d", result);
+
+ // Lock
+ if (MUTEX_LOCK(appData->mutex) == -1) {
+ return -1;
+ }
+
+ // If we have been woken up by the emergency pipe, there must be a token in
+ // it. Thus we can safely read it (even in a blocking way).
+ if (FD_ISSET(appData->fdsEmergency[0], &rfds)) {
+ char token;
+ do {
+ read(appData->fdsEmergency[0], &token, 1);
+ } while (errno == EINTR);
+ }
+
+ // Tell the world that there is now one thread less waiting for the
+ // underlying network.
+ appData->waitingThreads--;
+
+ // Unlock
+ MUTEX_UNLOCK(appData->mutex);
+ // LOGD("leave sslSelect");
+ return result;
+}
+
+/**
+ * Helper function that wakes up a thread blocked in select(), in case there is
+ * one. Is being called by sslRead() and sslWrite() as well as by JNI glue
+ * before closing the connection.
+ *
+ * @param data The application data structure with mutex info etc.
+ */
+static void sslNotify(AppData* appData) {
+ // Write a byte to the emergency pipe, so a concurrent select() can return.
+ // Note we have to restore the errno of the original system call, since the
+ // caller relies on it for generating error messages.
+ int errnoBackup = errno;
+ char token = '*';
+ do {
+ errno = 0;
+ write(appData->fdsEmergency[1], &token, 1);
+ } while (errno == EINTR);
+ errno = errnoBackup;
+}
+
+// From private header file external/openssl/ssl_locl.h
+#define SSL_aRSA 0x00000001L
+#define SSL_aDSS 0x00000002L
+#define SSL_aNULL 0x00000004L
+#define SSL_aDH 0x00000008L
+#define SSL_aECDH 0x00000010L
+#define SSL_aKRB5 0x00000020L
+#define SSL_aECDSA 0x00000040L
+#define SSL_aPSK 0x00000080L
+
+/**
+ * Converts an SSL_CIPHER's algorithms field to a TrustManager auth argument
+ */
+static const char* SSL_CIPHER_authentication_method(const SSL_CIPHER* cipher)
+{
+ unsigned long alg_auth = cipher->algorithm_auth;
+
+ const char *au;
+ switch (alg_auth) {
+ case SSL_aRSA:
+ au="RSA";
+ break;
+ case SSL_aDSS:
+ au="DSS";
+ break;
+ case SSL_aDH:
+ au="DH";
+ break;
+ case SSL_aKRB5:
+ au="KRB5";
+ break;
+ case SSL_aECDH:
+ au = "ECDH";
+ break;
+ case SSL_aNULL:
+ au="None";
+ break;
+ case SSL_aECDSA:
+ au="ECDSA";
+ break;
+ case SSL_aPSK:
+ au="PSK";
+ break;
+ default:
+ au="unknown";
+ break;
+ }
+ return au;
+}
+
+/**
+ * Verify the X509 certificate via SSL_CTX_set_cert_verify_callback
+ */
+static int cert_verify_callback(X509_STORE_CTX* x509_store_ctx, void* arg)
+{
+ /* Get the correct index to the SSLobject stored into X509_STORE_CTX. */
+ SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(x509_store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+ JNI_TRACE("ssl=%p cert_verify_callback x509_store_ctx=%p arg=%p", ssl, x509_store_ctx, arg);
+
+ AppData* appData = (AppData*) SSL_get_app_data(ssl);
+ 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);
+ return 0;
+ }
+ jobject certificateChainVerifier = appData->certificateChainVerifier;
+
+ jclass cls = env->GetObjectClass(certificateChainVerifier);
+ jmethodID methodID = env->GetMethodID(cls, "verifyCertificateChain", "([[BLjava/lang/String;)V");
+
+ jobjectArray objectArray = getCertificateBytes(env, x509_store_ctx->untrusted);
+
+ const char* authMethod;
+ switch (ssl->version) {
+ case SSL2_VERSION:
+ authMethod = "RSA";
+ break;
+ case SSL3_VERSION:
+ case TLS1_VERSION:
+ case DTLS1_VERSION:
+ authMethod = SSL_CIPHER_authentication_method(ssl->s3->tmp.new_cipher);
+ break;
+ default:
+ authMethod = "unknown";
+ break;
+ }
+ jstring authMethodString = env->NewStringUTF(authMethod);
+
+ env->CallVoidMethod(certificateChainVerifier, methodID, objectArray, authMethodString);
+
+ int result = (env->ExceptionCheck()) ? 0 : 1;
+ JNI_TRACE("ssl=%p cert_verify_callback => %d", ssl, result);
+ return result;
+}
+
+/**
+ * Call back to watch for handshake to be completed. This is necessary
+ * for SSL_MODE_HANDSHAKE_CUTTHROUGH support, since SSL_do_handshake
+ * returns before the handshake is completed in this case.
+ */
+static void info_callback(const SSL *ssl, int where, int ret) {
+ JNI_TRACE("ssl=%p info_callback where=0x%x ret=%d", ssl, where, ret);
+#ifdef WITH_JNI_TRACE
+ info_callback_LOG(ssl, where, ret);
+#endif
+ if (!(where & SSL_CB_HANDSHAKE_DONE)) {
+ JNI_TRACE("ssl=%p info_callback ignored", ssl);
+ return;
+ }
+
+ AppData* appData = (AppData*) SSL_get_app_data(ssl);
+ JNIEnv* env = appData->env;
+ if (env == NULL) {
+ LOGE("AppData->env missing in info_callback");
+ JNI_TRACE("ssl=%p info_callback env error", ssl, result);
+ return;
+ }
+ jobject handshakeCompletedCallback = appData->handshakeCompletedCallback;
+
+ jclass cls = env->GetObjectClass(handshakeCompletedCallback);
+ jmethodID methodID = env->GetMethodID(cls, "handshakeCompleted", "()V");
+
+ JNI_TRACE("ssl=%p info_callback calling handshakeCompleted", ssl);
+ env->CallVoidMethod(handshakeCompletedCallback, methodID);
+
+ if (env->ExceptionCheck()) {
+ JNI_TRACE("ssl=%p info_callback exception", ssl);
+ }
+
+ // no longer needed after handshake is complete
+ appData->env = NULL;
+ appData->certificateChainVerifier = NULL;
+ appData->handshakeCompletedCallback = NULL;
+ JNI_TRACE("ssl=%p info_callback completed", ssl);
}
/*
@@ -679,7 +1147,13 @@ static int NativeCrypto_SSL_CTX_new(JNIEnv* env, jclass clazz) {
#endif
SSL_CTX_set_mode(sslCtx, mode);
- // SSL_CTX_set_msg_callback(sslCtx, ssl_msg_callback_LOG); /* enable for handshake debug */
+ SSL_CTX_set_cert_verify_callback(sslCtx, cert_verify_callback, NULL);
+ SSL_CTX_set_info_callback(sslCtx, info_callback);
+
+#ifdef WITH_JNI_TRACE
+ SSL_CTX_set_msg_callback(sslCtx, ssl_msg_callback_LOG); /* enable for message debug */
+#endif
+ JNI_TRACE("NativeCrypto_SSL_CTX_new => %p", sslCtx);
return (jint) sslCtx;
}
@@ -698,6 +1172,7 @@ static jobjectArray makeCipherList(JNIEnv* env, STACK_OF(SSL_CIPHER)* cipher_lis
// Fill in the cipher names.
for (int i = 0; i < cipherCount; ++i) {
const char* c = sk_SSL_CIPHER_value(cipher_list, i)->name;
+ JNI_TRACE("makeCipherList[i=%d]=%s", i, c);
env->SetObjectArrayElement(array, i, env->NewStringUTF(c));
}
return array;
@@ -711,6 +1186,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));
+ JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_get_ciphers", ssl_ctx);
if (ssl_ctx == NULL) {
jniThrowNullPointerException(env, "SSL_CTX is null");
return NULL;
@@ -725,10 +1201,12 @@ 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));
+ JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_CTX_free", ssl_ctx);
if (ssl_ctx == NULL) {
jniThrowNullPointerException(env, "SSL_CTX is null");
return;
}
+ env->DeleteGlobalRef((jobject) ssl_ctx->app_verify_arg);
SSL_CTX_free(ssl_ctx);
}
@@ -736,7 +1214,7 @@ static void NativeCrypto_SSL_CTX_free(JNIEnv* env,
* Gets the chars of a String object as a '\0'-terminated UTF-8 string,
* stored in a freshly-allocated BIO memory buffer.
*/
-static BIO *stringToMemBuf(JNIEnv* env, jstring string) {
+static BIO* stringToMemBuf(JNIEnv* env, jstring string) {
jsize byteCount = env->GetStringUTFLength(string);
LocalArray<1024> buf(byteCount + 1);
env->GetStringUTFRegion(string, 0, env->GetStringLength(string), &buf[0]);
@@ -747,15 +1225,19 @@ static BIO *stringToMemBuf(JNIEnv* env, jstring string) {
}
/**
- * public static native int SSL_new(int ssl_ctx, String privatekey, String certificate, byte[] seed) throws IOException;
+ * public static native int SSL_new(int ssl_ctx, String privatekey, String certificate, byte[] seed,
+ * CertificateChainVerifier ccv) throws SSLException;
*/
static jint NativeCrypto_SSL_new(JNIEnv* env, jclass,
- jint ssl_ctx_address, jstring privatekey, jstring certificates, jbyteArray seed)
+ 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, privatekey, certificates, seed, ccv);
if (ssl_ctx == NULL) {
jniThrowNullPointerException(env, "SSL_CTX is null");
- return 0;
+ JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new => NULL", ssl_ctx);
+ return NULL;
}
// 'seed == null' when no SecureRandom Object is set
@@ -770,8 +1252,9 @@ static jint NativeCrypto_SSL_new(JNIEnv* env, jclass,
SSL* ssl = SSL_new(ssl_ctx);
if (ssl == NULL) {
- throwIOExceptionWithSslErrors(env, 0, 0,
+ throwSSLExceptionWithSslErrors(env, 0, 0,
"Unable to create SSL structure");
+ JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new => NULL", ssl_ctx);
return NULL;
}
@@ -793,9 +1276,10 @@ static jint NativeCrypto_SSL_new(JNIEnv* env, jclass,
if (privatekeyevp == NULL) {
LOGE(ERR_error_string(ERR_get_error(), NULL));
- throwIOExceptionWithSslErrors(env, 0, 0,
+ throwSSLExceptionWithSslErrors(env, 0, 0,
"Error parsing the private key");
SSL_free(ssl);
+ JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new => NULL", ssl_ctx);
return NULL;
}
@@ -806,58 +1290,113 @@ static jint NativeCrypto_SSL_new(JNIEnv* env, jclass,
if (certificatesx509 == NULL) {
LOGE(ERR_error_string(ERR_get_error(), NULL));
- throwIOExceptionWithSslErrors(env, 0, 0,
+ throwSSLExceptionWithSslErrors(env, 0, 0,
"Error parsing the certificates");
EVP_PKEY_free(privatekeyevp);
SSL_free(ssl);
+ JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new => NULL", ssl_ctx);
return NULL;
}
int ret = SSL_use_certificate(ssl, certificatesx509);
if (ret != 1) {
LOGE(ERR_error_string(ERR_get_error(), NULL));
- throwIOExceptionWithSslErrors(env, ret, 0,
+ throwSSLExceptionWithSslErrors(env, ret, 0,
"Error setting the certificates");
X509_free(certificatesx509);
EVP_PKEY_free(privatekeyevp);
SSL_free(ssl);
+ JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new => NULL", ssl_ctx);
return NULL;
}
ret = SSL_use_PrivateKey(ssl, privatekeyevp);
if (ret != 1) {
LOGE(ERR_error_string(ERR_get_error(), NULL));
- throwIOExceptionWithSslErrors(env, ret, 0,
+ throwSSLExceptionWithSslErrors(env, ret, 0,
"Error setting the private key");
X509_free(certificatesx509);
EVP_PKEY_free(privatekeyevp);
SSL_free(ssl);
+ JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new => NULL", ssl_ctx);
return NULL;
}
ret = SSL_check_private_key(ssl);
if (ret != 1) {
- throwIOExceptionWithSslErrors(env, ret, 0,
+ throwSSLExceptionWithSslErrors(env, ret, 0,
"Error checking the private key");
X509_free(certificatesx509);
EVP_PKEY_free(privatekeyevp);
SSL_free(ssl);
+ JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new => NULL", ssl_ctx);
return NULL;
}
}
+ JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new => ssl=%p", ssl_ctx, ssl);
return (jint)ssl;
}
/**
+ * public static native long SSL_get_mode(int ssl);
+ */
+static jlong NativeCrypto_SSL_get_mode(JNIEnv* env, jclass,
+ jint ssl_address) {
+ SSL* ssl = getSslPointer(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);
+ return 0;
+ }
+ long mode = SSL_get_mode(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_mode => 0x%lx", ssl, mode);
+ return mode;
+}
+
+/**
+ * public static native long SSL_set_mode(int ssl, long mode);
+ */
+static jlong NativeCrypto_SSL_set_mode(JNIEnv* env, jclass,
+ jint ssl_address, jlong mode) {
+ SSL* ssl = getSslPointer(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set_mode mode=0x%llx", ssl, mode);
+ if (ssl == NULL) {
+ return 0;
+ }
+ long result = SSL_set_mode(ssl, mode);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set_mode => 0x%lx", ssl, result);
+ return result;
+}
+
+/**
+ * public static native long SSL_clear_mode(int ssl, long mode);
+ */
+static jlong NativeCrypto_SSL_clear_mode(JNIEnv* env, jclass,
+ jint ssl_address, jlong mode) {
+ SSL* ssl = getSslPointer(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_clear_mode mode=0x%llx", ssl, mode);
+ if (ssl == NULL) {
+ return 0;
+ }
+ long result = SSL_clear_mode(ssl, mode);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_clear_mode => 0x%lx", ssl, result);
+ return result;
+}
+
+/**
* public static native long SSL_get_options(int ssl);
*/
static jlong NativeCrypto_SSL_get_options(JNIEnv* env, jclass,
jint ssl_address) {
SSL* ssl = getSslPointer(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);
return 0;
}
- return SSL_get_options(ssl);
+ long options = SSL_get_options(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_options => 0x%lx", ssl, options);
+ return options;
}
/**
@@ -866,10 +1405,28 @@ static jlong NativeCrypto_SSL_get_options(JNIEnv* env, jclass,
static jlong NativeCrypto_SSL_set_options(JNIEnv* env, jclass,
jint ssl_address, jlong options) {
SSL* ssl = getSslPointer(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set_options options=0x%llx", ssl, options);
+ if (ssl == NULL) {
+ return 0;
+ }
+ long result = SSL_set_options(ssl, options);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set_options => 0x%lx", ssl, result);
+ return result;
+}
+
+/**
+ * public static native long SSL_clear_options(int ssl, long options);
+ */
+static jlong NativeCrypto_SSL_clear_options(JNIEnv* env, jclass,
+ jint ssl_address, jlong options) {
+ SSL* ssl = getSslPointer(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_clear_options options=0x%llx", ssl, options);
if (ssl == NULL) {
- return 0 ;
+ return 0;
}
- return SSL_set_options(ssl, options);
+ long result = SSL_clear_options(ssl, options);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_clear_options => 0x%lx", ssl, result);
+ return result;
}
/**
@@ -880,6 +1437,7 @@ static jobjectArray NativeCrypto_SSL_get_ciphers(JNIEnv* env,
jclass, jint ssl_address)
{
SSL* ssl = getSslPointer(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_ciphers", ssl);
if (ssl == NULL) {
return NULL;
}
@@ -893,10 +1451,12 @@ static void NativeCrypto_SSL_set_cipher_list(JNIEnv* env, jclass,
jint ssl_address, jstring controlString)
{
SSL* ssl = getSslPointer(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);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_controlString str=%s", ssl, str);
int rc = SSL_set_cipher_list(ssl, str);
env->ReleaseStringUTFChars(controlString, str);
if (rc == 0) {
@@ -907,124 +1467,290 @@ static void NativeCrypto_SSL_set_cipher_list(JNIEnv* env, jclass,
}
/**
- * Our additional application data needed for getting synchronization right.
- * This maybe warrants a bit of lengthy prose:
- *
- * (1) We use a flag to reflect whether we consider the SSL connection alive.
- * Any read or write attempt loops will be cancelled once this flag becomes 0.
- *
- * (2) We use an int to count the number of threads that are blocked by the
- * underlying socket. This may be at most two (one reader and one writer), since
- * the Java layer ensures that no more threads will enter the native code at the
- * same time.
- *
- * (3) The pipe is used primarily as a means of cancelling a blocking select()
- * when we want to close the connection (aka "emergency button"). It is also
- * necessary for dealing with a possible race condition situation: There might
- * be cases where both threads see an SSL_ERROR_WANT_READ or
- * SSL_ERROR_WANT_WRITE. Both will enter a select() with the proper argument.
- * If one leaves the select() successfully before the other enters it, the
- * "success" event is already consumed and the second thread will be blocked,
- * possibly forever (depending on network conditions).
- *
- * The idea for solving the problem looks like this: Whenever a thread is
- * successful in moving around data on the network, and it knows there is
- * another thread stuck in a select(), it will write a byte to the pipe, waking
- * up the other thread. A thread that returned from select(), on the other hand,
- * knows whether it's been woken up by the pipe. If so, it will consume the
- * byte, and the original state of affairs has been restored.
- *
- * The pipe may seem like a bit of overhead, but it fits in nicely with the
- * other file descriptors of the select(), so there's only one condition to wait
- * for.
- *
- * (4) Finally, a mutex is needed to make sure that at most one thread is in
- * either SSL_read() or SSL_write() at any given time. This is an OpenSSL
- * requirement. We use the same mutex to guard the field for counting the
- * waiting threads.
- *
- * Note: The current implementation assumes that we don't have to deal with
- * problems induced by multiple cores or processors and their respective
- * memory caches. One possible problem is that of inconsistent views on the
- * "aliveAndKicking" field. This could be worked around by also enclosing all
- * accesses to that field inside a lock/unlock sequence of our mutex, but
- * currently this seems a bit like overkill.
+ * Sets certificate expectations, especially for server to request client auth
*/
-typedef struct app_data {
- volatile int aliveAndKicking;
- int waitingThreads;
- int fdsEmergency[2];
- MUTEX_TYPE mutex;
-} APP_DATA;
+static void NativeCrypto_SSL_set_verify(JNIEnv* env,
+ jclass, jint ssl_address, jint mode)
+{
+ SSL* ssl = getSslPointer(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set_verify", ssl);
+ if (ssl == NULL) {
+ return;
+ }
+ SSL_set_verify(ssl, (int)mode, NULL);
+}
/**
- * Creates our application data and attaches it to a given SSL connection.
- *
- * @param ssl The SSL connection to attach the data to.
- * @return 0 on success, -1 on failure.
+ * Sets the ciphers suites that are enabled in the SSL
*/
-static int sslCreateAppData(SSL* ssl) {
- APP_DATA* data = (APP_DATA*) malloc(sizeof(APP_DATA));
+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));
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set_session ssl_session=%p", ssl, ssl_session);
+ if (ssl == NULL) {
+ return;
+ }
- memset(data, 0, sizeof(APP_DATA));
+ int ret = SSL_set_session(ssl, ssl_session);
+ if (ret != 1) {
+ /*
+ * Translate the error, and throw if it turns out to be a real
+ * problem.
+ */
+ int sslErrorCode = SSL_get_error(ssl, ret);
+ if (sslErrorCode != SSL_ERROR_ZERO_RETURN) {
+ throwSSLExceptionWithSslErrors(env, ret, sslErrorCode,
+ "SSL session set");
+ SSL_clear(ssl);
+ }
+ }
+}
- data->aliveAndKicking = 1;
- data->waitingThreads = 0;
- data->fdsEmergency[0] = -1;
- data->fdsEmergency[1] = -1;
+/**
+ * Sets the ciphers suites that are enabled in the SSL
+ */
+static void NativeCrypto_SSL_set_session_creation_enabled(JNIEnv* env, jclass,
+ jint ssl_address, jboolean creation_enabled)
+{
+ SSL* ssl = getSslPointer(env, ssl_address, true);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_set_session_creation_enabled creation_enabled=%d", ssl, creation_enabled);
+ if (ssl == NULL) {
+ return;
+ }
+ SSL_set_session_creation_enabled(ssl, creation_enabled);
+}
- if (pipe(data->fdsEmergency) == -1) {
- free(data);
- return -1;
+/**
+ * Module scope variables initialized during JNI registration.
+ */
+static jfieldID field_Socket_mImpl;
+static jfieldID field_Socket_mFD;
+
+/**
+ * Perform SSL handshake
+ */
+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);
+ 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) {
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => 0", ssl);
+ return 0;
}
- if (MUTEX_SETUP(data->mutex) == -1) {
- free(data);
- return -1;
+ if (socketObject == NULL) {
+ jniThrowNullPointerException(env, "Socket is null");
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => 0", ssl);
+ return 0;
+ }
+ if (ccv == NULL) {
+ jniThrowNullPointerException(env, "CertificateChainVerifier is null");
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => 0", ssl);
+ return 0;
}
- SSL_set_app_data(ssl, (char*) data);
+ jobject socketImplObject = env->GetObjectField(socketObject, field_Socket_mImpl);
+ if (socketImplObject == NULL) {
+ throwSSLExceptionStr(env,
+ "couldn't get the socket impl from the socket");
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => 0", ssl);
+ return 0;
+ }
- return 0;
-}
+ jobject fdObject = env->GetObjectField(socketImplObject, field_Socket_mFD);
+ if (fdObject == NULL) {
+ throwSSLExceptionStr(env,
+ "couldn't get the file descriptor from the socket impl");
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => 0", ssl);
+ return 0;
+ }
-/**
- * Destroys our application data, cleaning up everything in the process.
- *
- * @param ssl The SSL connection to take the data from.
- */
-static void sslDestroyAppData(SSL* ssl) {
- APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
+ int fd = jniGetFDFromFileDescriptor(env, fdObject);
+ if (fd == -1) {
+ throwSSLExceptionStr(env, "Invalid file descriptor");
+ SSL_clear(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => 0", ssl);
+ return 0;
+ }
+
+ int ret = SSL_set_fd(ssl, fd);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake s=%d", ssl, fd);
- if (data != NULL) {
- SSL_set_app_data(ssl, NULL);
+ if (ret != 1) {
+ throwSSLExceptionWithSslErrors(env, ret, 0,
+ "Error setting the file descriptor");
+ SSL_clear(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => 0", ssl);
+ return 0;
+ }
- data->aliveAndKicking = 0;
+ /*
+ * Make socket non-blocking, so SSL_connect SSL_read() and SSL_write() don't hang
+ * forever and we can use select() to find out if the socket is ready.
+ */
+ int mode = fcntl(fd, F_GETFL);
+ if (mode == -1 || fcntl(fd, F_SETFL, mode | O_NONBLOCK) == -1) {
+ throwSSLExceptionStr(env, "Unable to make socket non blocking");
+ SSL_clear(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => 0", ssl);
+ return 0;
+ }
- if (data->fdsEmergency[0] != -1) {
- close(data->fdsEmergency[0]);
+ /*
+ * Create our special application data.
+ */
+ AppData* appData = AppData::create(env, ccv, hcc);
+ if (appData == NULL) {
+ throwSSLExceptionStr(env, "Unable to create application data");
+ SSL_clear(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => 0", ssl);
+ return 0;
+ }
+ SSL_set_app_data(ssl, (char*) appData);
+
+ if (client_mode) {
+ SSL_set_connect_state(ssl);
+ } else {
+ SSL_set_accept_state(ssl);
+ }
+
+ while (appData->aliveAndKicking) {
+ errno = 0;
+ appData->setEnv(env);
+ ret = SSL_do_handshake(ssl);
+ appData->clearEnv();
+ // cert_verify_callback threw exception
+ if (env->ExceptionCheck()) {
+ SSL_clear(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => 0", ssl);
+ return 0;
}
+ if (ret == 1) {
+ break;
+ } else if (errno == EINTR) {
+ continue;
+ } else {
+ // LOGD("SSL_connect: result %d, errno %d, timeout %d", ret, errno, timeout);
+ int error = SSL_get_error(ssl, ret);
- if (data->fdsEmergency[1] != -1) {
- close(data->fdsEmergency[1]);
+ /*
+ * If SSL_connect doesn't succeed due to the socket being
+ * either unreadable or unwritable, we use sslSelect to
+ * wait for it to become ready. If that doesn't happen
+ * before the specified timeout or an error occurs, we
+ * cancel the handshake. Otherwise we try the SSL_connect
+ * again.
+ */
+ if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
+ appData->waitingThreads++;
+ int selectResult = sslSelect(error, fd, appData, timeout);
+
+ if (selectResult == -1) {
+ throwSSLExceptionWithSslErrors(env, -1, error,
+ "handshake error");
+ SSL_clear(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => 0", ssl);
+ return 0;
+ } else if (selectResult == 0) {
+ throwSocketTimeoutException(env, "SSL handshake timed out");
+ SSL_clear(ssl);
+ freeSslErrorState();
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => 0", ssl);
+ return 0;
+ }
+ } else {
+ LOGE("Unknown error %d during handshake", error);
+ break;
+ }
}
+ }
- MUTEX_CLEANUP(data->mutex);
+ if (ret == 0) {
+ /*
+ * The other side closed the socket before the handshake could be
+ * completed, but everything is within the bounds of the TLS protocol.
+ * We still might want to find out the real reason of the failure.
+ */
+ int sslErrorCode = SSL_get_error(ssl, ret);
+ if (sslErrorCode == SSL_ERROR_NONE ||
+ (sslErrorCode == SSL_ERROR_SYSCALL && errno == 0)) {
+ throwSSLExceptionStr(env, "Connection closed by peer");
+ } else {
+ throwSSLExceptionWithSslErrors(env, ret, sslErrorCode,
+ "Trouble accepting connection");
+ }
+ SSL_clear(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => 0", ssl);
+ return 0;
+ }
+ if (ret < 0) {
+ /*
+ * Translate the error and throw exception. We are sure it is an error
+ * at this point.
+ */
+ int sslErrorCode = SSL_get_error(ssl, ret);
+ throwSSLExceptionWithSslErrors(env, ret, sslErrorCode,
+ "Trouble accepting connection");
+ SSL_clear(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => 0", ssl);
+ return 0;
+ }
+ SSL_SESSION* ssl_session = SSL_get1_session(ssl);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_do_handshake => ssl_session=%p", ssl, ssl_session);
+ return (jint) ssl_session;
+}
- free(data);
+/**
+ * public static native byte[][] SSL_get_certificate(int ssl);
+ */
+static jobjectArray NativeCrypto_SSL_get_certificate(JNIEnv* env, jclass, jint ssl_address)
+{
+ SSL* ssl = getSslPointer(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);
+ return NULL;
}
+ X509* certificate = SSL_get_certificate(ssl);
+ if (certificate == NULL) {
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => NULL", ssl);
+ return NULL;
+ }
+ // TODO convert from single certificate to chain properly. One
+ // option would be to have the chain remembered where
+ // SSL_use_certificate is used. Another would be to save the
+ // intermediate CAs with SSL_CTX SSL_CTX_add_extra_chain_cert.
+ STACK_OF(X509)* chain = sk_X509_new_null();
+ if (chain == NULL) {
+ jniThrowRuntimeException(env, "Unable to allocate local certificate chain");
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => NULL", ssl);
+ return NULL;
+ }
+ sk_X509_push(chain, certificate);
+ jobjectArray objectArray = getCertificateBytes(env, chain);
+ sk_X509_free(chain);
+ JNI_TRACE("ssl=%p NativeCrypto_SSL_get_certificate => %p", ssl, objectArray);
+ return objectArray;
}
+
/**
* 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;
}
- sslDestroyAppData(ssl);
+ AppData* appData = (AppData*) SSL_get_app_data(ssl);
+ delete appData;
+ SSL_set_app_data(ssl, NULL);
SSL_free(ssl);
}
@@ -1052,159 +1778,26 @@ static JNINativeMethod sNativeCryptoMethods[] = {
{ "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_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_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},
};
-/**
- * Module scope variables initialized during JNI registration.
- */
-static jfieldID field_Socket_mImpl;
-static jfieldID field_Socket_mFD;
-
// ============================================================================
// === OpenSSL-related helper stuff begins here. ==============================
// ============================================================================
-int get_socket_timeout(int type, int sd) {
- struct timeval tv;
- socklen_t len = sizeof(tv);
- if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) {
- LOGE("getsockopt(%d, SOL_SOCKET): %s (%d)",
- sd,
- strerror(errno),
- errno);
- return 0;
- }
- // LOGI("Current socket timeout (%d(s), %d(us))!",
- // (int)tv.tv_sec, (int)tv.tv_usec);
- int timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
- return timeout;
-}
-
-#ifdef TIMEOUT_DEBUG_SSL
-
-void print_socket_timeout(const char* name, int type, int sd) {
- struct timeval tv;
- int len = sizeof(tv);
- if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) {
- LOGE("getsockopt(%d, SOL_SOCKET, %s): %s (%d)",
- sd,
- name,
- strerror(errno),
- errno);
- }
- LOGI("Current socket %s is (%d(s), %d(us))!",
- name, (int)tv.tv_sec, (int)tv.tv_usec);
-}
-
-void print_timeout(const char* method, SSL* ssl) {
- LOGI("SSL_get_default_timeout %d in %s", SSL_get_default_timeout(ssl), method);
- int fd = SSL_get_fd(ssl);
- print_socket_timeout("SO_RCVTIMEO", SO_RCVTIMEO, fd);
- print_socket_timeout("SO_SNDTIMEO", SO_SNDTIMEO, fd);
-}
-
-#endif
-
-/**
- * Dark magic helper function that checks, for a given SSL session, whether it
- * can SSL_read() or SSL_write() without blocking. Takes into account any
- * concurrent attempts to close the SSL session from the Java side. This is
- * needed to get rid of the hangs that occur when thread #1 closes the SSLSocket
- * while thread #2 is sitting in a blocking read or write. The type argument
- * specifies whether we are waiting for readability or writability. It expects
- * to be passed either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, since we
- * only need to wait in case one of these problems occurs.
- *
- * @param type Either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE
- * @param fd The file descriptor to wait for (the underlying socket)
- * @param data The application data structure with mutex info etc.
- * @param timeout The timeout value for select call, with the special value
- * 0 meaning no timeout at all (wait indefinitely). Note: This is
- * the Java semantics of the timeout value, not the usual
- * select() semantics.
- * @return The result of the inner select() call, -1 on additional errors
- */
-static int sslSelect(int type, int fd, APP_DATA *data, int timeout) {
- fd_set rfds;
- fd_set wfds;
-
- FD_ZERO(&rfds);
- FD_ZERO(&wfds);
-
- if (type == SSL_ERROR_WANT_READ) {
- FD_SET(fd, &rfds);
- } else {
- FD_SET(fd, &wfds);
- }
-
- FD_SET(data->fdsEmergency[0], &rfds);
-
- int max = fd > data->fdsEmergency[0] ? fd : data->fdsEmergency[0];
-
- // Build a struct for the timeout data if we actually want a timeout.
- struct timeval tv;
- struct timeval *ptv;
- if (timeout > 0) {
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = 0;
- ptv = &tv;
- } else {
- ptv = NULL;
- }
-
- // LOGD("Doing select() for SSL_ERROR_WANT_%s...", type == SSL_ERROR_WANT_READ ? "READ" : "WRITE");
- int result = select(max + 1, &rfds, &wfds, NULL, ptv);
- // LOGD("Returned from select(), result is %d", result);
-
- // Lock
- if (MUTEX_LOCK(data->mutex) == -1) {
- return -1;
- }
-
- // If we have been woken up by the emergency pipe, there must be a token in
- // it. Thus we can safely read it (even in a blocking way).
- if (FD_ISSET(data->fdsEmergency[0], &rfds)) {
- char token;
- do {
- read(data->fdsEmergency[0], &token, 1);
- } while (errno == EINTR);
- }
-
- // Tell the world that there is now one thread less waiting for the
- // underlying network.
- data->waitingThreads--;
-
- // Unlock
- MUTEX_UNLOCK(data->mutex);
- // LOGD("leave sslSelect");
- return result;
-}
-
-/**
- * Helper function that wakes up a thread blocked in select(), in case there is
- * one. Is being called by sslRead() and sslWrite() as well as by JNI glue
- * before closing the connection.
- *
- * @param data The application data structure with mutex info etc.
- */
-static void sslNotify(APP_DATA *data) {
- // Write a byte to the emergency pipe, so a concurrent select() can return.
- // Note we have to restore the errno of the original system call, since the
- // caller relies on it for generating error messages.
- int errnoBackup = errno;
- char token = '*';
- do {
- errno = 0;
- write(data->fdsEmergency[1], &token, 1);
- } while (errno == EINTR);
- errno = errnoBackup;
-}
-
/**
* Helper function which does the actual reading. The Java layer guarantees that
* at most one thread will enter this function at any given time.
@@ -1217,7 +1810,7 @@ static void sslNotify(APP_DATA *data) {
* @return number of bytes read on success, -1 if the connection was
* cleanly shut down, or THROW_EXCEPTION if an exception should be thrown.
*/
-static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode,
+static int sslRead(JNIEnv* env, SSL* ssl, char* buf, jint len, int* sslReturnCode,
int* sslErrorCode, int timeout) {
// LOGD("Entering sslRead, caller requests to read %d bytes...", len);
@@ -1228,22 +1821,25 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode,
}
int fd = SSL_get_fd(ssl);
- BIO *bio = SSL_get_rbio(ssl);
+ BIO* bio = SSL_get_rbio(ssl);
- APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
+ AppData* appData = (AppData*) SSL_get_app_data(ssl);
- while (data->aliveAndKicking) {
+ while (appData->aliveAndKicking) {
errno = 0;
// Lock
- if (MUTEX_LOCK(data->mutex) == -1) {
+ if (MUTEX_LOCK(appData->mutex) == -1) {
return -1;
}
unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
// LOGD("Doing SSL_Read()");
+ AppData* appData = (AppData*) SSL_get_app_data(ssl);
+ appData->setEnv(env);
int result = SSL_read(ssl, buf, len);
+ appData->clearEnv();
int error = SSL_ERROR_NONE;
if (result <= 0) {
error = SSL_get_error(ssl, result);
@@ -1254,18 +1850,18 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode,
// If we have been successful in moving data around, check whether it
// might make sense to wake up other blocked threads, so they can give
// it a try, too.
- if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved && data->waitingThreads > 0) {
- sslNotify(data);
+ if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved && appData->waitingThreads > 0) {
+ sslNotify(appData);
}
// If we are blocked by the underlying socket, tell the world that
// there will be one more waiting thread now.
if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
- data->waitingThreads++;
+ appData->waitingThreads++;
}
// Unlock
- MUTEX_UNLOCK(data->mutex);
+ MUTEX_UNLOCK(appData->mutex);
switch (error) {
// Sucessfully read at least one byte.
@@ -1281,7 +1877,7 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode,
// Need to wait for availability of underlying layer, then retry.
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE: {
- int selectResult = sslSelect(error, fd, data, timeout);
+ int selectResult = sslSelect(error, fd, appData, timeout);
if (selectResult == -1) {
*sslReturnCode = -1;
*sslErrorCode = error;
@@ -1335,7 +1931,7 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode,
* @return number of bytes read on success, -1 if the connection was
* cleanly shut down, or THROW_EXCEPTION if an exception should be thrown.
*/
-static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode,
+static int sslWrite(JNIEnv* env, SSL* ssl, const char* buf, jint len, int* sslReturnCode,
int* sslErrorCode) {
// LOGD("Entering sslWrite(), caller requests to write %d bytes...", len);
@@ -1346,22 +1942,24 @@ static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode,
}
int fd = SSL_get_fd(ssl);
- BIO *bio = SSL_get_wbio(ssl);
+ BIO* bio = SSL_get_wbio(ssl);
- APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
+ AppData* appData = (AppData*) SSL_get_app_data(ssl);
int count = len;
- while (data->aliveAndKicking && len > 0) {
+ while (appData->aliveAndKicking && len > 0) {
errno = 0;
- if (MUTEX_LOCK(data->mutex) == -1) {
+ if (MUTEX_LOCK(appData->mutex) == -1) {
return -1;
}
unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
// LOGD("Doing SSL_write() with %d bytes to go", len);
+ appData->setEnv(env);
int result = SSL_write(ssl, buf, len);
+ appData->clearEnv();
int error = SSL_ERROR_NONE;
if (result <= 0) {
error = SSL_get_error(ssl, result);
@@ -1372,17 +1970,17 @@ static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode,
// If we have been successful in moving data around, check whether it
// might make sense to wake up other blocked threads, so they can give
// it a try, too.
- if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved && data->waitingThreads > 0) {
- sslNotify(data);
+ if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved && appData->waitingThreads > 0) {
+ sslNotify(appData);
}
// If we are blocked by the underlying socket, tell the world that
// there will be one more waiting thread now.
if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
- data->waitingThreads++;
+ appData->waitingThreads++;
}
- MUTEX_UNLOCK(data->mutex);
+ MUTEX_UNLOCK(appData->mutex);
switch (error) {
// Sucessfully write at least one byte.
@@ -1402,7 +2000,7 @@ static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode,
// it's also not standard Java behavior, so we wait forever here.
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE: {
- int selectResult = sslSelect(error, fd, data, 0);
+ int selectResult = sslSelect(error, fd, appData, 0);
if (selectResult == -1) {
*sslReturnCode = -1;
*sslErrorCode = error;
@@ -1538,329 +2136,18 @@ static int rsaVerify(unsigned char* msg, unsigned int msgLen, unsigned char* sig
// === OpenSSL-related helper stuff ends here. JNI glue follows. ==============
// ============================================================================
-/**
- * A connection within an OpenSSL context is established. (1) A new socket is
- * constructed, (2) the TLS/SSL handshake with a server is initiated.
- */
-static jboolean org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect(JNIEnv* env, jclass,
- jint ssl_address, jobject socketObject, jint timeout, jboolean client_mode, jint ssl_session_address)
-{
- // LOGD("ENTER connect");
- SSL* ssl = getSslPointer(env, ssl_address, true);
- if (ssl == NULL) {
- return (jboolean) false;
- }
- SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
-
- jobject socketImplObject = env->GetObjectField(socketObject, field_Socket_mImpl);
- if (socketImplObject == NULL) {
- throwIOExceptionStr(env,
- "couldn't get the socket impl from the socket");
- return (jboolean) false;
- }
-
- jobject fdObject = env->GetObjectField(socketImplObject, field_Socket_mFD);
- if (fdObject == NULL) {
- throwIOExceptionStr(env,
- "couldn't get the file descriptor from the socket impl");
- return (jboolean) false;
- }
-
- int fd = jniGetFDFromFileDescriptor(env, fdObject);
-
- int ret = SSL_set_fd(ssl, fd);
-
- if (ret != 1) {
- throwIOExceptionWithSslErrors(env, ret, 0,
- "Error setting the file descriptor");
- SSL_clear(ssl);
- return (jboolean) false;
- }
-
- if (ssl_session != NULL) {
- // LOGD("Trying to reuse session %p", ssl_session);
- ret = SSL_set_session(ssl, ssl_session);
- if (ret != 1) {
- /*
- * Translate the error, and throw if it turns out to be a real
- * problem.
- */
- int sslErrorCode = SSL_get_error(ssl, ret);
- if (sslErrorCode != SSL_ERROR_ZERO_RETURN) {
- throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
- "SSL session set");
- SSL_clear(ssl);
- return (jboolean) false;
- }
- }
- }
-
- /*
- * Make socket non-blocking, so SSL_connect SSL_read() and SSL_write() don't hang
- * forever and we can use select() to find out if the socket is ready.
- */
- int mode = fcntl(fd, F_GETFL);
- if (mode == -1 || fcntl(fd, F_SETFL, mode | O_NONBLOCK) == -1) {
- throwIOExceptionStr(env, "Unable to make socket non blocking");
- SSL_clear(ssl);
- return (jboolean) false;
- }
-
- /*
- * Create our special application data.
- */
- if (sslCreateAppData(ssl) == -1) {
- throwIOExceptionStr(env, "Unable to create application data");
- SSL_clear(ssl);
- // TODO
- return (jboolean) false;
- }
-
- APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
-
- while (data->aliveAndKicking) {
- errno = 0;
- ret = SSL_connect(ssl);
- if (ret == 1) {
- break;
- } else if (errno == EINTR) {
- continue;
- } else {
- // LOGD("SSL_connect: result %d, errno %d, timeout %d", ret, errno, timeout);
- int error = SSL_get_error(ssl, ret);
-
- /*
- * If SSL_connect doesn't succeed due to the socket being
- * either unreadable or unwritable, we use sslSelect to
- * wait for it to become ready. If that doesn't happen
- * before the specified timeout or an error occurs, we
- * cancel the handshake. Otherwise we try the SSL_connect
- * again.
- */
- if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
- data->waitingThreads++;
- int selectResult = sslSelect(error, fd, data, timeout);
-
- if (selectResult == -1) {
- throwIOExceptionWithSslErrors(env, -1, error,
- "Connect error");
- SSL_clear(ssl);
- return (jboolean) false;
- } else if (selectResult == 0) {
- throwSocketTimeoutException(env, "SSL handshake timed out");
- SSL_clear(ssl);
- freeSslErrorState();
- return (jboolean) false;
- }
- } else {
- LOGE("Unknown error %d during connect", error);
- break;
- }
- }
- }
-
- if (ret != 1) {
- /*
- * Translate the error, and throw if it turns out to be a real
- * problem.
- */
- int sslErrorCode = SSL_get_error(ssl, ret);
- if (sslErrorCode != SSL_ERROR_ZERO_RETURN) {
- throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
- "SSL handshake failure");
- SSL_clear(ssl);
- return (jboolean) false;
- }
- }
-
- if (ssl_session != NULL) {
- ret = SSL_session_reused(ssl);
- // if (ret == 1) LOGD("Session %p was reused", ssl_session);
- // else LOGD("Session %p was not reused, using new session %p", ssl_session, SSL_get_session(ssl));
- return (jboolean) ret;
- } else {
- // LOGD("New session %p was negotiated", SSL_get_session(ssl));
- return (jboolean) 0;
- }
- // LOGD("LEAVE connect");
-}
-
static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession(JNIEnv* env, jclass,
- jint jssl)
-{
- return (jint) SSL_get1_session((SSL*) jssl);
-}
-
-static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept(JNIEnv* env, jclass,
- jint ssl_address, jobject socketObject)
-{
- SSL* serverSocketSsl = reinterpret_cast<SSL*>(static_cast<uintptr_t>(ssl_address));
- if (serverSocketSsl == NULL) {
- throwIOExceptionWithSslErrors(env, 0, 0,
- "Unusable SSL structure");
- return NULL;
- }
-
- SSL* ssl = SSL_dup(serverSocketSsl);
- if (ssl == NULL) {
- throwIOExceptionWithSslErrors(env, 0, 0,
- "Unable to create SSL structure");
- return NULL;
- }
-
- jobject socketImplObject = env->GetObjectField(socketObject, field_Socket_mImpl);
- if (socketImplObject == NULL) {
- throwIOExceptionStr(env, "couldn't get the socket impl from the socket");
- return NULL;
- }
-
- jobject fdObject = env->GetObjectField(socketImplObject, field_Socket_mFD);
- if (fdObject == NULL) {
- throwIOExceptionStr(env, "couldn't get the file descriptor from the socket impl");
- return NULL;
- }
-
-
- int sd = jniGetFDFromFileDescriptor(env, fdObject);
-
- BIO* bio = BIO_new_socket(sd, BIO_NOCLOSE);
- SSL_set_bio(ssl, bio, bio);
-
- /*
- * Fill in the stack allocated appdata structure needed for the
- * certificate callback and store this in the SSL application data
- * slot.
- */
- jsse_ssl_app_data_t appdata;
- appdata.env = env;
- appdata.object = socketObject;
- SSL_set_app_data(ssl, &appdata);
-
- /*
- * Do the actual SSL_accept(). It is possible this code is insufficient.
- * Maybe we need to deal with all the special SSL error cases (WANT_*),
- * just like we do for SSL_connect(). But currently it is looking ok.
- */
- int ret = SSL_accept(ssl);
-
- /*
- * Clear the SSL application data slot again, so we can safely use it for
- * our ordinary synchronization structure afterwards. Also, we don't want
- * sslDestroyAppData() to think that there is something that needs to be
- * freed right now (in case of an error).
- */
- SSL_set_app_data(ssl, NULL);
-
- if (ret == 0) {
- /*
- * The other side closed the socket before the handshake could be
- * completed, but everything is within the bounds of the TLS protocol.
- * We still might want to find out the real reason of the failure.
- */
- int sslErrorCode = SSL_get_error(ssl, ret);
- if (sslErrorCode == SSL_ERROR_NONE ||
- (sslErrorCode == SSL_ERROR_SYSCALL && errno == 0)) {
- throwIOExceptionStr(env, "Connection closed by peer");
- } else {
- throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
- "Trouble accepting connection");
- }
- SSL_clear(ssl);
- return NULL;
- } else if (ret < 0) {
- /*
- * Translate the error and throw exception. We are sure it is an error
- * at this point.
- */
- int sslErrorCode = SSL_get_error(ssl, ret);
- throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
- "Trouble accepting connection");
- SSL_clear(ssl);
- return NULL;
- }
-
- /*
- * Make socket non-blocking, so SSL_read() and SSL_write() don't hang
- * forever and we can use select() to find out if the socket is ready.
- */
- int fd = SSL_get_fd(ssl);
- int mode = fcntl(fd, F_GETFL);
- if (mode == -1 || fcntl(fd, F_SETFL, mode | O_NONBLOCK) == -1) {
- throwIOExceptionStr(env, "Unable to make socket non blocking");
- SSL_clear(ssl);
- return NULL;
- }
-
- /*
- * Create our special application data.
- */
- if (sslCreateAppData(ssl) == -1) {
- throwIOExceptionStr(env, "Unable to create application data");
- SSL_clear(ssl);
- return NULL;
- }
-
- return (jint) ssl;
-}
-
-#define SSL_aRSA 0x00000001L
-#define SSL_aDSS 0x00000002L
-#define SSL_aNULL 0x00000004L
-#define SSL_aDH 0x00000008L
-#define SSL_aECDH 0x00000010L
-#define SSL_aKRB5 0x00000020L
-#define SSL_aECDSA 0x00000040L
-#define SSL_aPSK 0x00000080L
-
-/**
- * Sets the client's crypto algorithms and authentication methods.
- */
-static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod(JNIEnv* env,
- jclass, jint ssl_address)
+ jint ssl_address)
{
SSL* ssl = getSslPointer(env, ssl_address, true);
+ JNI_TRACE("ssl=%p OpenSSLSocketImpl_getsslsession", ssl);
if (ssl == NULL) {
- return NULL;
- }
-
- const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl);
-
- unsigned long alg_auth = cipher->algorithm_auth;
-
- const char *au;
- switch (alg_auth) {
- case SSL_aRSA:
- au="RSA";
- break;
- case SSL_aDSS:
- au="DSS";
- break;
- case SSL_aDH:
- au="DH";
- break;
- case SSL_aKRB5:
- au="KRB5";
- break;
- case SSL_aECDH:
- au = "ECDH";
- break;
- case SSL_aNULL:
- au="None";
- break;
- case SSL_aECDSA:
- au="ECDSA";
- break;
- case SSL_aPSK:
- au="PSK";
- break;
- default:
- au="unknown";
- break;
+ JNI_TRACE("ssl=%p OpenSSLSocketImpl_getsslsession => NULL", ssl);
+ return NULL;
}
-
- jstring ret = env->NewStringUTF(au);
-
- return ret;
+ SSL_SESSION* ssl_session = SSL_get1_session(ssl);
+ JNI_TRACE("ssl=%p OpenSSLSocketImpl_getsslsession => ssl_session=%p", ssl, ssl_session);
+ return (jint) ssl_session;
}
/**
@@ -1869,6 +2156,7 @@ static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipheraut
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;
}
@@ -1877,24 +2165,31 @@ static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read(JNIEnv*
int returnCode = 0;
int errorCode = 0;
- int ret = sslRead(ssl, (char *) &byteRead, 1, &returnCode, &errorCode, timeout);
+ 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.
- throwIOExceptionWithSslErrors(env, returnCode, errorCode,
+ throwSSLExceptionWithSslErrors(env, returnCode, errorCode,
"Read error");
- return -1;
+ result = -1;
+ break;
case THROW_SOCKETTIMEOUTEXCEPTION:
throwSocketTimeoutException(env, "Read timed out");
- return -1;
+ result = -1;
+ break;
case -1:
// Propagate EOF upwards.
- return -1;
+ result = -1;
+ break;
default:
// Return the actual char read, make sure it stays 8 bits wide.
- return ((jint) byteRead) & 0xFF;
+ result = ((jint) byteRead) & 0xFF;
+ break;
}
+ JNI_TRACE("ssl=%p OpenSSLSocketImpl_read => %d", ssl, result);
+ return result;
}
/**
@@ -1904,6 +2199,7 @@ static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read(JNIEnv*
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;
}
@@ -1912,22 +2208,25 @@ static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba(JNIEn
int returnCode = 0;
int errorCode = 0;
- int ret =
- sslRead(ssl, (char*) (bytes + offset), len, &returnCode, &errorCode, timeout);
+ 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.
- throwIOExceptionWithSslErrors(env, returnCode, errorCode,
+ throwSSLExceptionWithSslErrors(env, returnCode, errorCode,
"Read error");
- return -1;
+ result = -1;
} else if(ret == THROW_SOCKETTIMEOUTEXCEPTION) {
throwSocketTimeoutException(env, "Read timed out");
- return -1;
+ result = -1;
+ } else {
+ result = ret;
}
- return ret;
+ JNI_TRACE("ssl=%p OpenSSLSocketImpl_readba => %d", ssl, result);
+ return result;
}
/**
@@ -1936,6 +2235,7 @@ static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba(JNIEn
static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write(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);
if (ssl == NULL) {
return;
}
@@ -1943,11 +2243,11 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write(JNIEnv
int returnCode = 0;
int errorCode = 0;
char buf[1] = { (char) b };
- int ret = sslWrite(ssl, buf, 1, &returnCode, &errorCode);
+ int ret = sslWrite(env, ssl, buf, 1, &returnCode, &errorCode);
if (ret == THROW_EXCEPTION) {
// See sslWrite() regarding improper failure to handle normal cases.
- throwIOExceptionWithSslErrors(env, returnCode, errorCode,
+ throwSSLExceptionWithSslErrors(env, returnCode, errorCode,
"Write error");
} else if(ret == THROW_SOCKETTIMEOUTEXCEPTION) {
throwSocketTimeoutException(env, "Write timed out");
@@ -1961,6 +2261,7 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba(JNIE
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);
if (ssl == NULL) {
return;
}
@@ -1968,14 +2269,13 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba(JNIE
jbyte* bytes = env->GetByteArrayElements(dest, NULL);
int returnCode = 0;
int errorCode = 0;
- int ret = sslWrite(ssl, (const char *) (bytes + offset), len,
- &returnCode, &errorCode);
+ int ret = sslWrite(env, ssl, (const char *) (bytes + offset), len, &returnCode, &errorCode);
env->ReleaseByteArrayElements(dest, bytes, 0);
if (ret == THROW_EXCEPTION) {
// See sslWrite() regarding improper failure to handle normal cases.
- throwIOExceptionWithSslErrors(env, returnCode, errorCode,
+ throwSSLExceptionWithSslErrors(env, returnCode, errorCode,
"Write error");
} else if(ret == THROW_SOCKETTIMEOUTEXCEPTION) {
throwSocketTimeoutException(env, "Write timed out");
@@ -1988,6 +2288,7 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba(JNIE
static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt(
JNIEnv* env, jclass, jint ssl_address) {
SSL* ssl = getSslPointer(env, ssl_address, false);
+ JNI_TRACE("ssl=%p OpenSSLSocketImpl_interrupt", ssl);
if (ssl == NULL) {
return;
}
@@ -1996,13 +2297,13 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt(
* Mark the connection as quasi-dead, then send something to the emergency
* file descriptor, so any blocking select() calls are woken up.
*/
- APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
- if (data != NULL) {
- data->aliveAndKicking = 0;
+ AppData* appData = (AppData*) SSL_get_app_data(ssl);
+ if (appData != NULL) {
+ appData->aliveAndKicking = 0;
// At most two threads can be waiting.
- sslNotify(data);
- sslNotify(data);
+ sslNotify(appData);
+ sslNotify(appData);
}
}
@@ -2012,23 +2313,27 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt(
static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close(
JNIEnv* env, jclass, jint ssl_address) {
SSL* ssl = getSslPointer(env, ssl_address, false);
+ JNI_TRACE("ssl=%p OpenSSLSocketImpl_close", ssl);
if (ssl == NULL) {
return;
}
-
/*
* 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);
if (fd != -1) {
int mode = fcntl(fd, F_GETFL);
if (mode == -1 || fcntl(fd, F_SETFL, mode & ~O_NONBLOCK) == -1) {
-// throwIOExceptionStr(env, "Unable to make socket blocking again");
+// throwSSLExceptionStr(env, "Unable to make socket blocking again");
// LOGW("Unable to make socket blocking again");
}
}
+ AppData* appData = (AppData*) SSL_get_app_data(ssl);
+ appData->setEnv(env);
int ret = SSL_shutdown(ssl);
+ appData->clearEnv();
switch (ret) {
case 0:
/*
@@ -2053,7 +2358,7 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close(
* exception.
*/
int sslErrorCode = SSL_get_error(ssl, ret);
- throwIOExceptionWithSslErrors(env, ret, sslErrorCode, "SSL shutdown failed");
+ throwSSLExceptionWithSslErrors(env, ret, sslErrorCode, "SSL shutdown failed");
break;
}
@@ -2067,10 +2372,12 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close(
static int org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_verifysignature(JNIEnv* env, jclass clazz,
jbyteArray msg, jbyteArray sig, jstring algorithm, jbyteArray mod, jbyteArray exp) {
- // LOGD("Entering verifysignature()");
+ 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;
}
@@ -2089,6 +2396,7 @@ static int org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_verifysignatu
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) {
@@ -2116,123 +2424,22 @@ static int org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_verifysignatu
freeSslErrorState();
}
+ JNI_TRACE("OpenSSLSocketImpl_verifysignature => %d", result);
return result;
}
static JNINativeMethod sSocketImplMethods[] =
{
- {"nativeconnect", "(ILjava/net/Socket;IZI)Z", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect},
- {"nativegetsslsession", "(I)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession},
{"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},
- {"nativeaccept", "(ILjava/net/Socket;)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept},
- {"nativecipherauthenticationmethod", "(I)Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod},
{"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},
};
/**
- * Gives an array back containing all the X509 certificate's bytes.
- */
-static jobjectArray getcertificatebytes(JNIEnv* env,
- const STACK_OF(X509) *chain)
-{
- BUF_MEM *bptr;
- int count, i;
- jbyteArray bytes;
- jobjectArray joa;
-
- if (chain == NULL) {
- // Chain can be NULL if the associated cipher doesn't do certs.
- return NULL;
- }
-
- count = sk_X509_num(chain);
-
- if (count > 0) {
- joa = env->NewObjectArray(count, env->FindClass("[B"), NULL);
-
- if (joa == NULL) {
- return NULL;
- }
-
- BIO *bio = BIO_new(BIO_s_mem());
-
- // LOGD("Start fetching the certificates");
- for (i = 0; i < count; i++) {
- X509 *cert = sk_X509_value(chain, i);
-
- BIO_reset(bio);
- PEM_write_bio_X509(bio, cert);
-
- BIO_get_mem_ptr(bio, &bptr);
- bytes = env->NewByteArray(bptr->length);
-
- if (bytes == NULL) {
- /*
- * Indicate an error by resetting joa to NULL. It will
- * eventually get gc'ed.
- */
- joa = NULL;
- break;
- } else {
- jbyte* src = reinterpret_cast<jbyte*>(bptr->data);
- env->SetByteArrayRegion(bytes, 0, bptr->length, src);
- env->SetObjectArrayElement(joa, i, bytes);
- }
- }
-
- // LOGD("Certificate fetching complete");
- BIO_free(bio);
- return joa;
- } else {
- return NULL;
- }
-}
-
-/**
- * Verify the X509 certificate.
- */
-static int verify_callback(int preverify_ok, X509_STORE_CTX *x509_store_ctx)
-{
- /* Get the correct index to the SSLobject stored into X509_STORE_CTX. */
- SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(x509_store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
-
- jsse_ssl_app_data_t* appdata = (jsse_ssl_app_data_t*)SSL_get_app_data(ssl);
-
- jclass cls = appdata->env->GetObjectClass(appdata->object);
-
- jmethodID methodID = appdata->env->GetMethodID(cls, "verifyCertificateChain", "([[B)Z");
-
- jobjectArray objectArray = getcertificatebytes(appdata->env, x509_store_ctx->untrusted);
-
- jboolean verified = appdata->env->CallBooleanMethod(appdata->object, methodID, objectArray);
-
- return (verified) ? 1 : 0;
-}
-
-/**
- * Sets the client's credentials and the depth of theirs verification.
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth(JNIEnv* env,
- jclass, jint ssl_address, jint value)
-{
- SSL* ssl = getSslPointer(env, ssl_address, true);
- if (ssl == NULL) {
- return;
- }
- SSL_set_verify(ssl, (int)value, verify_callback);
-}
-
-static JNINativeMethod sServerSocketImplMethods[] =
-{
- {"nativesetclientauth", "(II)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth},
-};
-
-/**
* Our implementation of what might be considered
* SSL_SESSION_get_peer_cert_chain
*/
@@ -2249,13 +2456,16 @@ static jobjectArray OpenSSLSessionImpl_getPeerCertificatesImpl(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 OpenSSLSessionImpl_getPeerCertificatesImpl ssl_ctx=%p", ssl_session, ssl_ctx);
if (ssl_ctx == NULL) {
jniThrowNullPointerException(env, "SSL_CTX is null");
+ JNI_TRACE("ssl_session=%p OpenSSLSessionImpl_getPeerCertificatesImpl => NULL", ssl_session);
return NULL;
}
- SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
STACK_OF(X509)* chain = SSL_SESSION_get_peer_cert_chain(ssl_ctx, ssl_session);
- jobjectArray objectArray = getcertificatebytes(env, chain);
+ jobjectArray objectArray = getCertificateBytes(env, chain);
+ JNI_TRACE("ssl_session=%p OpenSSLSessionImpl_getPeerCertificatesImpl => %p", ssl_session, objectArray);
return objectArray;
}
@@ -2266,13 +2476,16 @@ static jobjectArray OpenSSLSessionImpl_getPeerCertificatesImpl(JNIEnv* env,
*/
static jbyteArray OpenSSLSessionImpl_getEncoded(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 OpenSSLSessionImpl_getEncoded", ssl_session);
if (ssl_session == NULL) {
+ JNI_TRACE("ssl_session=%p OpenSSLSessionImpl_getEncoded => 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 OpenSSLSessionImpl_getEncoded => NULL", ssl_session);
return NULL;
}
@@ -2284,6 +2497,7 @@ static jbyteArray OpenSSLSessionImpl_getEncoded(JNIEnv* env, jclass, jint ssl_se
env->ReleaseByteArrayElements(bytes, tmp, 0);
}
+ JNI_TRACE("ssl_session=%p OpenSSLSessionImpl_getEncoded => size=%d", ssl_session, size);
return bytes;
}
@@ -2291,7 +2505,9 @@ static jbyteArray OpenSSLSessionImpl_getEncoded(JNIEnv* env, jclass, jint ssl_se
* Deserialize the session.
*/
static jint OpenSSLSessionImpl_initializeNativeImpl(JNIEnv* env, jclass, jbyteArray bytes, jint size) {
+ JNI_TRACE("OpenSSLSessionImpl_initializeNativeImpl bytes=%p size=%d", bytes, size);
if (bytes == NULL) {
+ JNI_TRACE("OpenSSLSessionImpl_initializeNativeImpl => 0");
return 0;
}
@@ -2300,6 +2516,7 @@ static jint OpenSSLSessionImpl_initializeNativeImpl(JNIEnv* env, jclass, jbyteAr
SSL_SESSION* ssl_session = d2i_SSL_SESSION(NULL, &ucp, size);
env->ReleaseByteArrayElements(bytes, tmp, 0);
+ JNI_TRACE("OpenSSLSessionImpl_initializeNativeImpl => %p", ssl_session);
return static_cast<jint>(reinterpret_cast<uintptr_t>(ssl_session));
}
@@ -2308,13 +2525,14 @@ static jint OpenSSLSessionImpl_initializeNativeImpl(JNIEnv* env, jclass, jbyteAr
*/
static jbyteArray OpenSSLSessionImpl_getId(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 OpenSSLSessionImpl_getId", 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 OpenSSLSessionImpl_getId => %p session_id_length=%d",
+ ssl_session, result, ssl_session->session_id_length);
return result;
}
@@ -2324,8 +2542,10 @@ static jbyteArray OpenSSLSessionImpl_getId(JNIEnv* env, jclass, jint ssl_session
*/
static jlong OpenSSLSessionImpl_getCreationTime(JNIEnv* env, jclass, jint ssl_session_address) {
SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(static_cast<uintptr_t>(ssl_session_address));
- jlong result = SSL_SESSION_get_time(ssl_session);
+ JNI_TRACE("ssl_session=%p OpenSSLSessionImpl_getCreationTime", 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 OpenSSLSessionImpl_getCreationTime => %lld", ssl_session, result);
return result;
}
@@ -2344,7 +2564,9 @@ static const char* SSL_SESSION_get_version(SSL_SESSION* ssl_session) {
*/
static jstring OpenSSLSessionImpl_getProtocol(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 OpenSSLSessionImpl_getProtocol", ssl_session);
const char* protocol = SSL_SESSION_get_version(ssl_session);
+ JNI_TRACE("ssl_session=%p OpenSSLSessionImpl_getProtocol => %s", ssl_session, protocol);
jstring result = env->NewStringUTF(protocol);
return result;
}
@@ -2354,9 +2576,11 @@ static jstring OpenSSLSessionImpl_getProtocol(JNIEnv* env, jclass, jint ssl_sess
*/
static jstring OpenSSLSessionImpl_getCipherSuite(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 OpenSSLSessionImpl_getCipherSuite", ssl_session);
const SSL_CIPHER* cipher = ssl_session->cipher;
- jstring result = env->NewStringUTF(SSL_CIPHER_get_name(cipher));
- return result;
+ const char* name = SSL_CIPHER_get_name(cipher);
+ JNI_TRACE("ssl_session=%p OpenSSLSessionImpl_getCipherSuite => %s", ssl_session, name);
+ return env->NewStringUTF(name);
}
/**
@@ -2364,19 +2588,19 @@ static jstring OpenSSLSessionImpl_getCipherSuite(JNIEnv* env, jclass, jint ssl_s
*/
static void OpenSSLSessionImpl_freeImpl(JNIEnv* env, jclass, jint session) {
SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(session);
- // LOGD("Freeing OpenSSL session %p", session);
+ JNI_TRACE("ssl_session=%p OpenSSLSessionImpl_freeImpl", ssl_session);
SSL_SESSION_free(ssl_session);
}
static JNINativeMethod sSessionImplMethods[] = {
- { "freeImpl", "(I)V", (void*) OpenSSLSessionImpl_freeImpl },
- { "getCipherSuite", "(I)Ljava/lang/String;", (void*) OpenSSLSessionImpl_getCipherSuite },
- { "getCreationTime", "(I)J", (void*) OpenSSLSessionImpl_getCreationTime },
- { "getEncoded", "(I)[B", (void*) OpenSSLSessionImpl_getEncoded },
- { "getId", "(I)[B", (void*) OpenSSLSessionImpl_getId },
- { "getPeerCertificatesImpl", "(II)[[B", (void*) OpenSSLSessionImpl_getPeerCertificatesImpl },
- { "getProtocol", "(I)Ljava/lang/String;", (void*) OpenSSLSessionImpl_getProtocol },
- { "initializeNativeImpl", "([BI)I", (void*) OpenSSLSessionImpl_initializeNativeImpl },
+ { "freeImpl", "(I)V", (void*) OpenSSLSessionImpl_freeImpl },
+ { "getCipherSuite", "(I)Ljava/lang/String;", (void*) OpenSSLSessionImpl_getCipherSuite },
+ { "getCreationTime", "(I)J", (void*) OpenSSLSessionImpl_getCreationTime },
+ { "getEncoded", "(I)[B", (void*) OpenSSLSessionImpl_getEncoded },
+ { "getId", "(I)[B", (void*) OpenSSLSessionImpl_getId },
+ { "getPeerCertificatesImpl", "(II)[[B", (void*) OpenSSLSessionImpl_getPeerCertificatesImpl },
+ { "getProtocol", "(I)Ljava/lang/String;", (void*) OpenSSLSessionImpl_getProtocol },
+ { "initializeNativeImpl", "([BI)I", (void*) OpenSSLSessionImpl_initializeNativeImpl },
};
typedef struct {
@@ -2388,10 +2612,10 @@ typedef struct {
static JNINativeClass sClasses[] = {
{ "org/apache/harmony/xnet/provider/jsse/NativeCrypto", sNativeCryptoMethods, NELEM(sNativeCryptoMethods) },
{ "org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl", sSocketImplMethods, NELEM(sSocketImplMethods) },
- { "org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl", sServerSocketImplMethods, NELEM(sServerSocketImplMethods) },
{ "org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl", sSessionImplMethods, NELEM(sSessionImplMethods) },
};
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,
diff --git a/x-net/src/test/java/tests/api/javax/net/ssl/SSLSessionTest.java b/x-net/src/test/java/tests/api/javax/net/ssl/SSLSessionTest.java
index 384084f..6f3b61d 100644
--- a/x-net/src/test/java/tests/api/javax/net/ssl/SSLSessionTest.java
+++ b/x-net/src/test/java/tests/api/javax/net/ssl/SSLSessionTest.java
@@ -197,8 +197,10 @@ public class SSLSessionTest extends TestCase {
public void test_getCreationTime() {
try {
// check if creation time was in the last 10 seconds
- long diff = new Date().getTime() - clientSession.getCreationTime();
- assertTrue (diff < 10000);
+ long currentTime = System.currentTimeMillis();
+ long sessionTime = clientSession.getCreationTime();
+ long diff = currentTime - sessionTime;
+ assertTrue("diff between " + currentTime + " and " + sessionTime + " should be < 10000", diff < 10000);
} catch (Exception ex) {
fail("Unexpected exception " + ex);
}
@@ -219,6 +221,7 @@ public class SSLSessionTest extends TestCase {
try {
SSLSession sess =
clientSslContext.getClientSessionContext().getSession(id);
+ assertNotNull("Could not find session for id " + id, sess);
assertEquals(clientSession, sess);
} catch (Exception ex) {
fail("Unexpected exception " + ex);
@@ -238,8 +241,11 @@ public class SSLSessionTest extends TestCase {
public void test_getLastAccessedTime() {
try {
// check if last access time was in the last 10 seconds
- long diff = new Date().getTime() - clientSession.getLastAccessedTime();
- assertTrue (diff < 10000);
+ long currentTime = System.currentTimeMillis();
+ long sessionTime = clientSession.getLastAccessedTime();
+ long diff = currentTime - sessionTime;
+ assertTrue("diff between " + currentTime + " and " + sessionTime + " should be < 10000", diff < 10000);
+ assertTrue ("diff should be < 10000 but is " + diff, diff < 10000);
} catch (Exception ex) {
fail("Unexpected exception " + ex);
}
@@ -275,7 +281,6 @@ public class SSLSessionTest extends TestCase {
method = "getLocalPrincipal",
args = {}
)
- @KnownFailure("getLocalPrincipal returns null")
@AndroidOnly("Uses bks key store. Change useBKS to false to run on the RI")
public void test_getLocalPrincipal() {
try {