summaryrefslogtreecommitdiffstats
path: root/x-net/src/main
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commitfdb2704414a9ed92394ada0d1395e4db86889465 (patch)
tree9b591a4a50054274a197f02b3ccb51313681879f /x-net/src/main
downloadlibcore-fdb2704414a9ed92394ada0d1395e4db86889465.zip
libcore-fdb2704414a9ed92394ada0d1395e4db86889465.tar.gz
libcore-fdb2704414a9ed92394ada0d1395e4db86889465.tar.bz2
Initial Contribution
Diffstat (limited to 'x-net/src/main')
-rw-r--r--x-net/src/main/java/javax/net/DefaultServerSocketFactory.java49
-rw-r--r--x-net/src/main/java/javax/net/DefaultSocketFactory.java58
-rw-r--r--x-net/src/main/java/javax/net/ServerSocketFactory.java61
-rw-r--r--x-net/src/main/java/javax/net/SocketFactory.java66
-rw-r--r--x-net/src/main/java/javax/net/ssl/CertPathTrustManagerParameters.java43
-rw-r--r--x-net/src/main/java/javax/net/ssl/ContextImpl.java37
-rw-r--r--x-net/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java40
-rw-r--r--x-net/src/main/java/javax/net/ssl/DefaultSSLContext.java136
-rw-r--r--x-net/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java65
-rw-r--r--x-net/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java90
-rw-r--r--x-net/src/main/java/javax/net/ssl/HandshakeCompletedEvent.java86
-rw-r--r--x-net/src/main/java/javax/net/ssl/HandshakeCompletedListener.java36
-rw-r--r--x-net/src/main/java/javax/net/ssl/HostnameVerifier.java35
-rw-r--r--x-net/src/main/java/javax/net/ssl/HttpsURLConnection.java122
-rw-r--r--x-net/src/main/java/javax/net/ssl/KeyManager.java30
-rw-r--r--x-net/src/main/java/javax/net/ssl/KeyManagerFactory.java186
-rw-r--r--x-net/src/main/java/javax/net/ssl/KeyManagerFactorySpi.java64
-rw-r--r--x-net/src/main/java/javax/net/ssl/KeyStoreBuilderParameters.java53
-rw-r--r--x-net/src/main/java/javax/net/ssl/ManagerFactoryParameters.java30
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLContext.java199
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLContextSpi.java84
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLEngine.java312
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLEngineResult.java130
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLException.java63
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLHandshakeException.java40
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLKeyException.java40
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLPeerUnverifiedException.java40
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLPermission.java42
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLProtocolException.java40
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLServerSocket.java65
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLServerSocketFactory.java88
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLSession.java162
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLSessionBindingEvent.java56
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLSessionBindingListener.java45
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLSessionContext.java68
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLSocket.java78
-rw-r--r--x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java108
-rw-r--r--x-net/src/main/java/javax/net/ssl/TrustManager.java30
-rw-r--r--x-net/src/main/java/javax/net/ssl/TrustManagerFactory.java183
-rw-r--r--x-net/src/main/java/javax/net/ssl/TrustManagerFactorySpi.java60
-rw-r--r--x-net/src/main/java/javax/net/ssl/X509ExtendedKeyManager.java46
-rw-r--r--x-net/src/main/java/javax/net/ssl/X509KeyManager.java73
-rw-r--r--x-net/src/main/java/javax/net/ssl/X509TrustManager.java53
-rw-r--r--x-net/src/main/java/javax/net/ssl/package.html24
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/internal/nls/Messages.java124
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/internal/nls/messages.properties17
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertException.java70
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertProtocol.java293
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Appendable.java38
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateMessage.java179
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java192
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateVerify.java101
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CipherSuite.java612
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java649
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java210
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java156
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionState.java174
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateSSLv3.java356
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateTLS.java355
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ContentType.java54
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DHParameters.java113
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DataStream.java47
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DelegatedTask.java70
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java243
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfBufferException.java42
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfSourceException.java42
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Finished.java85
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Handshake.java94
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeIODataStream.java462
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeProtocol.java544
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HelloRequest.java78
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java127
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java135
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerImpl.java242
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java125
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Message.java78
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java91
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigest.java108
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigestJDK.java82
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketFactoryImpl.java71
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java330
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java466
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignature.java223
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketFactoryImpl.java85
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java1037
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImplWrapper.java170
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/PRF.java206
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ProtocolVersion.java164
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLBufferedInput.java84
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java108
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineAppData.java101
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineDataStream.java96
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineImpl.java751
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java135
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java447
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLRecordProtocol.java488
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionContextImpl.java146
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java410
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLStreamedInput.java60
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLv3Constants.java89
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java738
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHello.java140
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHelloDone.java80
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerKeyExchange.java196
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java136
-rw-r--r--x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java208
-rw-r--r--x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp465
-rw-r--r--x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp324
-rw-r--r--x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp289
-rw-r--r--x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp1929
-rw-r--r--x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h123
-rw-r--r--x-net/src/main/native/sub.mk24
112 files changed, 20123 insertions, 0 deletions
diff --git a/x-net/src/main/java/javax/net/DefaultServerSocketFactory.java b/x-net/src/main/java/javax/net/DefaultServerSocketFactory.java
new file mode 100644
index 0000000..2bf3a22
--- /dev/null
+++ b/x-net/src/main/java/javax/net/DefaultServerSocketFactory.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+
+/**
+ * Default implementation of javax.net.ServerSocketFactory
+ *
+ */
+class DefaultServerSocketFactory extends ServerSocketFactory {
+
+ public ServerSocket createServerSocket(int port) throws IOException {
+ return new ServerSocket(port);
+ }
+
+ public ServerSocket createServerSocket(int port, int backlog)
+ throws IOException {
+ return new ServerSocket(port, backlog);
+ }
+
+ public ServerSocket createServerSocket(int port, int backlog,
+ InetAddress iAddress) throws IOException {
+ return new ServerSocket(port, backlog, iAddress);
+ }
+
+}
diff --git a/x-net/src/main/java/javax/net/DefaultSocketFactory.java b/x-net/src/main/java/javax/net/DefaultSocketFactory.java
new file mode 100644
index 0000000..ead9651
--- /dev/null
+++ b/x-net/src/main/java/javax/net/DefaultSocketFactory.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+/**
+ * Default implementation of javax.net.SocketFactory
+ *
+ */
+class DefaultSocketFactory extends SocketFactory {
+
+ public Socket createSocket() throws IOException {
+ return new Socket();
+ }
+
+ public Socket createSocket(String host, int port) throws IOException,
+ UnknownHostException {
+ return new Socket(host, port);
+ }
+
+ public Socket createSocket(String host, int port, InetAddress localHost,
+ int localPort) throws IOException, UnknownHostException {
+ return new Socket(host, port, localHost, localPort);
+ }
+
+ public Socket createSocket(InetAddress host, int port) throws IOException {
+ return new Socket(host, port);
+ }
+
+ public Socket createSocket(InetAddress address, int port,
+ InetAddress localAddress, int localPort) throws IOException {
+ return new Socket(address, port, localAddress, localPort);
+ }
+}
diff --git a/x-net/src/main/java/javax/net/ServerSocketFactory.java b/x-net/src/main/java/javax/net/ServerSocketFactory.java
new file mode 100644
index 0000000..c2b8bfd
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ServerSocketFactory.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.SocketException;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+
+public abstract class ServerSocketFactory {
+ static ServerSocketFactory defaultFactory;
+ protected ServerSocketFactory() {
+ }
+
+ public static synchronized ServerSocketFactory getDefault() {
+ if (defaultFactory == null) {
+ defaultFactory = new DefaultServerSocketFactory();
+ }
+ return defaultFactory;
+ }
+
+ public ServerSocket createServerSocket() throws IOException {
+ // follow RI's behavior
+ throw new SocketException("Unbound server sockets not implemented");
+ }
+
+ public abstract ServerSocket createServerSocket(int port)
+ throws IOException;
+
+ public abstract ServerSocket createServerSocket(int port, int backlog)
+ throws IOException;
+
+ public abstract ServerSocket createServerSocket(int port, int backlog,
+ InetAddress iAddress) throws IOException;
+
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/SocketFactory.java b/x-net/src/main/java/javax/net/SocketFactory.java
new file mode 100644
index 0000000..1f8fa87
--- /dev/null
+++ b/x-net/src/main/java/javax/net/SocketFactory.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public abstract class SocketFactory {
+
+ static SocketFactory defaultFactory;
+
+ protected SocketFactory() {
+ }
+
+ public static synchronized SocketFactory getDefault() {
+ if (defaultFactory == null) {
+ defaultFactory = new DefaultSocketFactory();
+ }
+ return defaultFactory;
+ }
+
+ public Socket createSocket() throws IOException {
+ // follow RI's behavior
+ throw new SocketException("Unconnected sockets not implemented");
+ }
+
+ public abstract Socket createSocket(String host, int port)
+ throws IOException, UnknownHostException;
+
+ public abstract Socket createSocket(String host, int port,
+ InetAddress localHost, int localPort) throws IOException,
+ UnknownHostException;
+
+ public abstract Socket createSocket(InetAddress host, int port)
+ throws IOException;
+
+ public abstract Socket createSocket(InetAddress address, int port,
+ InetAddress localAddress, int localPort) throws IOException;
+}
diff --git a/x-net/src/main/java/javax/net/ssl/CertPathTrustManagerParameters.java b/x-net/src/main/java/javax/net/ssl/CertPathTrustManagerParameters.java
new file mode 100644
index 0000000..9a37270
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/CertPathTrustManagerParameters.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.security.cert.CertPathParameters;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public class CertPathTrustManagerParameters implements ManagerFactoryParameters {
+
+ private CertPathParameters param;
+
+ public CertPathTrustManagerParameters(CertPathParameters parameters) {
+ param = (CertPathParameters) parameters.clone();
+ }
+
+ public CertPathParameters getParameters() {
+ return (CertPathParameters) param.clone();
+ }
+
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/ContextImpl.java b/x-net/src/main/java/javax/net/ssl/ContextImpl.java
new file mode 100644
index 0000000..5787266
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/ContextImpl.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris V. Kuznetsov
+ * @version $Revision$
+ */
+
+package javax.net.ssl;
+
+import java.security.Provider;
+
+/**
+ * Support class for this package.
+ *
+ */
+
+class ContextImpl extends SSLContext {
+ public ContextImpl(SSLContextSpi contextSpi, Provider provider,
+ String protocol) {
+ super(contextSpi, provider, protocol);
+ }
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java b/x-net/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
new file mode 100644
index 0000000..373e792
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/DefaultHostnameVerifier.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+/**
+ * Default implementation of javax.net.ssl.HostnameVerifier
+ *
+ */
+class DefaultHostnameVerifier implements HostnameVerifier {
+
+ /**
+ * DefaultHostnameVerifier assumes the connection should not be permitted
+ *
+ * @param hostname
+ * @param session
+ */
+ public boolean verify(String hostname, SSLSession session) {
+ return false;
+ }
+}
diff --git a/x-net/src/main/java/javax/net/ssl/DefaultSSLContext.java b/x-net/src/main/java/javax/net/ssl/DefaultSSLContext.java
new file mode 100644
index 0000000..cc029f4
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/DefaultSSLContext.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris V. Kuznetsov
+ * @version $Revision$
+ */
+
+package javax.net.ssl;
+
+import java.io.FileInputStream;
+import java.security.AccessController;
+import java.security.Provider;
+import java.security.Security;
+import java.security.KeyStore;
+import java.util.Iterator;
+
+import org.apache.harmony.security.fortress.Engine;
+import org.apache.harmony.security.fortress.Services;
+
+
+/**
+ * Support class for this package.
+ *
+ */
+
+class DefaultSSLContext {
+ private static SSLContext defaultSSLContext;
+
+ public static SSLContext getContext() {
+ if (defaultSSLContext == null) {
+ defaultSSLContext = AccessController
+ .doPrivileged(new java.security.PrivilegedAction<SSLContext>() {
+ public SSLContext run() {
+ return findDefault();
+ }
+ });
+ }
+ return defaultSSLContext;
+ }
+
+ private static SSLContext findDefault() {
+ // FIXME EXPORT CONTROL
+ Provider.Service service;
+ for (Iterator it1 = Services.getProvidersList().iterator(); it1
+ .hasNext();) {
+ service = Engine.door.getService((Provider) it1.next(),
+ "SSLContext");
+ if (service != null) {
+ try {
+ SSLContext con = new ContextImpl(
+ (SSLContextSpi) service.newInstance(null),
+ service.getProvider(),
+ service.getAlgorithm());
+
+ //TODO javax.net.ssl.keyStoreProvider, javax.net.ssl.trustStoreProvider system property
+ // find KeyStore, KeyManagers
+ KeyManager[] keyManagers = null;
+ KeyStore ks = KeyStore.getInstance(KeyStore
+ .getDefaultType());
+ String keystore = System
+ .getProperty("javax.net.ssl.keyStore");
+ String keystorepwd = System
+ .getProperty("javax.net.ssl.keyStorePassword");
+ char[] pwd = null;
+ if (keystorepwd != null) {
+ pwd = keystorepwd.toCharArray();
+ }
+ if (keystore != null) {
+ FileInputStream fis = new java.io.FileInputStream(
+ keystore);
+ ks.load(fis, pwd);
+ fis.close();
+
+ KeyManagerFactory kmf;
+ String kmfAlg = Security
+ .getProperty("ssl.KeyManagerFactory.algorithm");
+ if (kmfAlg == null) {
+ kmfAlg = "SunX509";
+ }
+ kmf = KeyManagerFactory.getInstance(kmfAlg);
+ kmf.init(ks, pwd);
+ keyManagers = kmf.getKeyManagers();
+ }
+
+ // find TrustStore, TrustManagers
+ TrustManager[] trustManagers = null;
+ keystore = System.getProperty("javax.net.ssl.trustStore");
+ keystorepwd = System
+ .getProperty("javax.net.ssl.trustStorePassword");
+ pwd = null;
+ if (keystorepwd != null) {
+ pwd = keystorepwd.toCharArray();
+ }
+ //TODO Defaults: jssecacerts; cacerts
+ if (keystore != null) {
+ FileInputStream fis = new java.io.FileInputStream(
+ keystore);
+ ks.load(fis, pwd);
+ fis.close();
+ TrustManagerFactory tmf;
+ String tmfAlg = Security
+ .getProperty("ssl.TrustManagerFactory.algorithm");
+ if (tmfAlg == null) {
+ tmfAlg = "PKIX";
+ }
+ tmf = TrustManagerFactory.getInstance(tmfAlg);
+ tmf.init(ks);
+ trustManagers = tmf.getTrustManagers();
+ }
+
+ con.init(keyManagers, trustManagers, null);
+ return con;
+ } catch (Exception e) {
+ // e.printStackTrace();
+ // ignore and try another
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/x-net/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java b/x-net/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java
new file mode 100644
index 0000000..f382072
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.SocketException;
+
+/**
+ * Default inoperative implementation of javax.net.ssl.SSLServerSocketFactory
+ *
+ */
+class DefaultSSLServerSocketFactory extends SSLServerSocketFactory {
+
+ private String errMessage;
+
+ public String[] getDefaultCipherSuites() {
+ return new String[0];
+ }
+
+ public String[] getSupportedCipherSuites() {
+ return new String[0];
+ }
+
+ public ServerSocket createServerSocket(int port) throws IOException {
+ throw new SocketException(errMessage);
+ }
+
+
+ public ServerSocket createServerSocket(int port, int backlog)
+ throws IOException {
+ throw new SocketException(errMessage);
+ }
+
+ public ServerSocket createServerSocket(int port, int backlog,
+ InetAddress iAddress) throws IOException {
+ throw new SocketException(errMessage);
+ }
+
+ DefaultSSLServerSocketFactory(String mes) {
+ errMessage = mes;
+ }
+
+}
diff --git a/x-net/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java b/x-net/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java
new file mode 100644
index 0000000..bda575e
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+/**
+ * Default inoperative implementation of javax.net.ssl.SSLSocketFactory
+ *
+ */
+class DefaultSSLSocketFactory extends SSLSocketFactory {
+
+ private String errMessage;
+
+ public String[] getDefaultCipherSuites() {
+ return new String[0];
+ }
+
+ public String[] getSupportedCipherSuites() {
+ return new String[0];
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSocketFactory#createSocket(java.net.Socket, java.lang.String, int, boolean)
+ */
+ public Socket createSocket(Socket s, String host, int port,
+ boolean autoClose) throws IOException {
+ throw new SocketException(errMessage);
+ }
+
+ /**
+ * @see javax.net.SocketFactory#createSocket(java.lang.String, int)
+ */
+ public Socket createSocket(String host, int port) throws IOException,
+ UnknownHostException {
+ throw new SocketException(errMessage);
+ }
+
+ /**
+ * @see javax.net.SocketFactory#createSocket(java.lang.String, int, java.net.InetAddress, int)
+ */
+ public Socket createSocket(String host, int port, InetAddress localHost,
+ int localPort) throws IOException, UnknownHostException {
+ throw new SocketException(errMessage);
+ }
+
+ /**
+ * @see javax.net.SocketFactory#createSocket(java.net.InetAddress, int)
+ */
+ public Socket createSocket(InetAddress host, int port) throws IOException {
+ throw new SocketException(errMessage);
+ }
+
+ /**
+ * @see javax.net.SocketFactory#createSocket(java.net.InetAddress, int, java.net.InetAddress, int)
+ */
+ public Socket createSocket(InetAddress address, int port,
+ InetAddress localAddress, int localPort) throws IOException {
+ throw new SocketException(errMessage);
+ }
+
+ DefaultSSLSocketFactory(String mes) {
+ errMessage = mes;
+ }
+
+}
diff --git a/x-net/src/main/java/javax/net/ssl/HandshakeCompletedEvent.java b/x-net/src/main/java/javax/net/ssl/HandshakeCompletedEvent.java
new file mode 100644
index 0000000..a783a30
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/HandshakeCompletedEvent.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.io.Serializable;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import javax.security.cert.X509Certificate;
+import java.util.EventObject;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public class HandshakeCompletedEvent extends EventObject implements
+ Serializable {
+
+ /**
+ * @serial
+ * The 5.0 spec. doesn't declare this serialVersionUID field
+ * In order to be compatible it is explicitly declared here
+ */
+ private static final long serialVersionUID = 7914963744257769778L;
+
+ private transient SSLSession session;
+
+ public HandshakeCompletedEvent(SSLSocket sock, SSLSession s) {
+ super(sock);
+ session = s;
+ }
+
+ public SSLSession getSession() {
+ return session;
+ }
+
+ public String getCipherSuite() {
+ return session.getCipherSuite();
+ }
+
+ public Certificate[] getLocalCertificates() {
+ return session.getLocalCertificates();
+ }
+
+ public Certificate[] getPeerCertificates()
+ throws SSLPeerUnverifiedException {
+ return session.getPeerCertificates();
+ }
+
+ public X509Certificate[] getPeerCertificateChain()
+ throws SSLPeerUnverifiedException {
+ return session.getPeerCertificateChain();
+ }
+
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ return session.getPeerPrincipal();
+ }
+
+ public Principal getLocalPrincipal() {
+ return session.getLocalPrincipal();
+ }
+
+ public SSLSocket getSocket() {
+ return (SSLSocket)this.source;
+ }
+
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/HandshakeCompletedListener.java b/x-net/src/main/java/javax/net/ssl/HandshakeCompletedListener.java
new file mode 100644
index 0000000..55b8894
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/HandshakeCompletedListener.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.util.EventListener;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public interface HandshakeCompletedListener extends EventListener {
+ /**
+ * @com.intel.drl.spec_ref
+ */
+ public void handshakeCompleted(HandshakeCompletedEvent event);
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/HostnameVerifier.java b/x-net/src/main/java/javax/net/ssl/HostnameVerifier.java
new file mode 100644
index 0000000..609c861
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/HostnameVerifier.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public interface HostnameVerifier {
+
+ /**
+ * @com.intel.drl.spec_ref
+ */
+ public boolean verify(String hostname, SSLSession session);
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/HttpsURLConnection.java b/x-net/src/main/java/javax/net/ssl/HttpsURLConnection.java
new file mode 100644
index 0000000..5d983b6
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/HttpsURLConnection.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public abstract class HttpsURLConnection extends HttpURLConnection {
+
+ private static HostnameVerifier defaultHostnameVerifier = new DefaultHostnameVerifier();
+
+ private static SSLSocketFactory defaultSSLSocketFactory = (SSLSocketFactory) SSLSocketFactory
+ .getDefault();
+
+ protected HostnameVerifier hostnameVerifier;
+
+ private static SSLSocketFactory socketFactory;
+
+ protected HttpsURLConnection(URL url) {
+ super(url);
+ hostnameVerifier = defaultHostnameVerifier;
+ socketFactory = defaultSSLSocketFactory;
+ }
+
+ public abstract String getCipherSuite();
+
+ public abstract Certificate[] getLocalCertificates();
+
+ public abstract Certificate[] getServerCertificates()
+ throws SSLPeerUnverifiedException;
+
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ Certificate[] certs = getServerCertificates();
+ if (certs == null || certs.length == 0 ||
+ (!(certs[0] instanceof X509Certificate))) {
+ throw new SSLPeerUnverifiedException(
+ "No server's end-entity certificate");
+ }
+ return ((X509Certificate) certs[0]).getSubjectX500Principal();
+ }
+
+ public Principal getLocalPrincipal() {
+ Certificate[] certs = getLocalCertificates();
+ if (certs == null || certs.length == 0
+ || (!(certs[0] instanceof X509Certificate))) {
+ return null;
+ }
+ return ((X509Certificate) certs[0]).getSubjectX500Principal();
+ }
+
+ public static void setDefaultHostnameVerifier(HostnameVerifier v) {
+ if (v == null) {
+ throw new IllegalArgumentException("HostnameVerifier is null");
+ }
+ defaultHostnameVerifier = v;
+ }
+
+ public static HostnameVerifier getDefaultHostnameVerifier() {
+ return defaultHostnameVerifier;
+ }
+
+ public void setHostnameVerifier(HostnameVerifier v) {
+ if (v == null) {
+ throw new IllegalArgumentException("HostnameVerifier is null");
+ }
+ hostnameVerifier = v;
+ }
+
+ public HostnameVerifier getHostnameVerifier() {
+ return hostnameVerifier;
+ }
+
+ public static void setDefaultSSLSocketFactory(SSLSocketFactory sf) {
+ if (sf == null) {
+ throw new IllegalArgumentException("SSLSocketFactory is null");
+ }
+ defaultSSLSocketFactory = sf;
+ }
+
+ public static SSLSocketFactory getDefaultSSLSocketFactory() {
+ return defaultSSLSocketFactory;
+ }
+
+ public void setSSLSocketFactory(SSLSocketFactory sf) {
+ if (sf == null) {
+ throw new IllegalArgumentException("SSLSocketFactory is null");
+ }
+ socketFactory = sf;
+ }
+
+ public SSLSocketFactory getSSLSocketFactory() {
+ return socketFactory;
+ }
+
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/KeyManager.java b/x-net/src/main/java/javax/net/ssl/KeyManager.java
new file mode 100644
index 0000000..6a8bcab
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/KeyManager.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public interface KeyManager {
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/KeyManagerFactory.java b/x-net/src/main/java/javax/net/ssl/KeyManagerFactory.java
new file mode 100644
index 0000000..9c0fddd
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/KeyManagerFactory.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.security.AccessController;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.Security;
+import java.security.UnrecoverableKeyException;
+
+import org.apache.harmony.security.fortress.Engine;
+
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+
+public class KeyManagerFactory {
+ // Store KeyManagerFactory service name
+ private static final String SERVICE = "KeyManagerFactory";
+
+ // Used to access common engine functionality
+ private static Engine engine = new Engine(SERVICE);
+
+ // Store default property name
+ private static final String PROPERTY_NAME = "ssl.KeyManagerFactory.algorithm";
+
+ // Store used provider
+ private final Provider provider;
+
+ // Store used KeyManagerFactorySpi implementation
+ private final KeyManagerFactorySpi spiImpl;
+
+ // Store used algorithm
+ private final String algorithm;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected KeyManagerFactory(KeyManagerFactorySpi factorySpi,
+ Provider provider, String algorithm) {
+ this.provider = provider;
+ this.algorithm = algorithm;
+ this.spiImpl = factorySpi;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final String getAlgorithm() {
+ return algorithm;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ * throws NullPointerException if algorithm is null (instead of
+ * NoSuchAlgorithmException as in 1.4 release)
+ */
+ public static final KeyManagerFactory getInstance(String algorithm)
+ throws NoSuchAlgorithmException {
+ if (algorithm == null) {
+ throw new NullPointerException("algorith is null");
+ }
+ synchronized (engine) {
+ engine.getInstance(algorithm, null);
+ return new KeyManagerFactory((KeyManagerFactorySpi) engine.spi,
+ engine.provider, algorithm);
+ }
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ * throws NullPointerException if algorithm is null (instead of
+ * NoSuchAlgorithmException as in 1.4 release)
+ */
+ public static final KeyManagerFactory getInstance(String algorithm,
+ String provider) throws NoSuchAlgorithmException,
+ NoSuchProviderException {
+ if ((provider == null) || (provider.length() == 0)) {
+ throw new IllegalArgumentException("Provider is null or empty");
+ }
+ Provider impProvider = Security.getProvider(provider);
+ if (impProvider == null) {
+ throw new NoSuchProviderException(provider);
+ }
+ return getInstance(algorithm, impProvider);
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ * throws NullPointerException if algorithm is null (instead of
+ * NoSuchAlgorithmException as in 1.4 release)
+ */
+ public static final KeyManagerFactory getInstance(String algorithm,
+ Provider provider) throws NoSuchAlgorithmException {
+ if (provider == null) {
+ throw new IllegalArgumentException("Provider is null");
+ }
+ if (algorithm == null) {
+ throw new NullPointerException("algorith is null");
+ }
+ synchronized (engine) {
+ engine.getInstance(algorithm, provider, null);
+ return new KeyManagerFactory((KeyManagerFactorySpi) engine.spi,
+ provider, algorithm);
+ }
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final Provider getProvider() {
+ return provider;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final void init(KeyStore ks, char[] password)
+ throws KeyStoreException, NoSuchAlgorithmException,
+ UnrecoverableKeyException {
+ spiImpl.engineInit(ks, password);
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final void init(ManagerFactoryParameters spec)
+ throws InvalidAlgorithmParameterException {
+ spiImpl.engineInit(spec);
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final KeyManager[] getKeyManagers() {
+ return spiImpl.engineGetKeyManagers();
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public static final String getDefaultAlgorithm() {
+ return AccessController
+ .doPrivileged(new java.security.PrivilegedAction<String>() {
+ public String run() {
+ return Security.getProperty(PROPERTY_NAME);
+ }
+ });
+ }
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/KeyManagerFactorySpi.java b/x-net/src/main/java/javax/net/ssl/KeyManagerFactorySpi.java
new file mode 100644
index 0000000..ae6a10c
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/KeyManagerFactorySpi.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+
+public abstract class KeyManagerFactorySpi {
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public KeyManagerFactorySpi() {
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected abstract void engineInit(KeyStore ks, char[] password)
+ throws KeyStoreException, NoSuchAlgorithmException,
+ UnrecoverableKeyException;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected abstract void engineInit(ManagerFactoryParameters spec)
+ throws InvalidAlgorithmParameterException;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected abstract KeyManager[] engineGetKeyManagers();
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/KeyStoreBuilderParameters.java b/x-net/src/main/java/javax/net/ssl/KeyStoreBuilderParameters.java
new file mode 100644
index 0000000..dc6d05a
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/KeyStoreBuilderParameters.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.net.ssl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.security.KeyStore;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public class KeyStoreBuilderParameters implements ManagerFactoryParameters {
+
+ private List ksbuilders;
+
+ public KeyStoreBuilderParameters(KeyStore.Builder builder) {
+ ksbuilders = new ArrayList();
+ if (builder != null) {
+ ksbuilders.add(builder);
+ }
+ }
+
+ public KeyStoreBuilderParameters(List parameters) {
+ if (parameters == null) {
+ throw new NullPointerException("Builders list is null");
+ }
+ if (parameters.isEmpty()) {
+ throw new IllegalArgumentException("Builders list is empty");
+ }
+ ksbuilders = new ArrayList(parameters);
+ }
+
+ public List getParameters() {
+ return Collections.unmodifiableList(ksbuilders);
+ }
+}
diff --git a/x-net/src/main/java/javax/net/ssl/ManagerFactoryParameters.java b/x-net/src/main/java/javax/net/ssl/ManagerFactoryParameters.java
new file mode 100644
index 0000000..6c6121a
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/ManagerFactoryParameters.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public interface ManagerFactoryParameters {
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLContext.java b/x-net/src/main/java/javax/net/ssl/SSLContext.java
new file mode 100644
index 0000000..2edcfb0
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLContext.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+
+import org.apache.harmony.security.fortress.Engine;
+
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+
+public class SSLContext {
+ // StoreSSLContext service name
+ private static final String SERVICE = "SSLContext";
+
+ // Used to access common engine functionality
+ private static Engine engine = new Engine(SERVICE);
+
+ // Storeused provider
+ private final Provider provider;
+
+ // Storeused SSLContextSpi implementation
+ private final SSLContextSpi spiImpl;
+
+ // Storeused protocol
+ private final String protocol;
+
+ /*
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected SSLContext(SSLContextSpi contextSpi, Provider provider,
+ String protocol) {
+ this.provider = provider;
+ this.protocol = protocol;
+ this.spiImpl = contextSpi;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ * throws NullPointerException if protocol is null (instead of
+ * NoSuchAlgorithmException as in 1.4 release)
+ */
+ public static SSLContext getInstance(String protocol)
+ throws NoSuchAlgorithmException {
+ if (protocol == null) {
+ throw new NullPointerException("protocol is null");
+ }
+ synchronized (engine) {
+ engine.getInstance(protocol, null);
+ return new SSLContext((SSLContextSpi) engine.spi, engine.provider,
+ protocol);
+ }
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ * throws NullPointerException if protocol is null (instead of
+ * NoSuchAlgorithmException as in 1.4 release)
+ */
+ public static SSLContext getInstance(String protocol, String provider)
+ throws NoSuchAlgorithmException, NoSuchProviderException {
+ if (provider == null) {
+ throw new IllegalArgumentException("Provider is null");
+ }
+ if (provider.length() == 0) {
+ throw new IllegalArgumentException("Provider is empty");
+ }
+ Provider impProvider = Security.getProvider(provider);
+ if (impProvider == null) {
+ throw new NoSuchProviderException(provider);
+ }
+ return getInstance(protocol, impProvider);
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ * throws NullPointerException if protocol is null (instead of
+ * NoSuchAlgorithmException as in 1.4 release)
+ */
+ public static SSLContext getInstance(String protocol, Provider provider)
+ throws NoSuchAlgorithmException {
+ if (provider == null) {
+ throw new IllegalArgumentException("provider is null");
+ }
+ if (protocol == null) {
+ throw new NullPointerException("protocol is null");
+ }
+ synchronized (engine) {
+ engine.getInstance(protocol, provider, null);
+ return new SSLContext((SSLContextSpi) engine.spi, provider, protocol);
+ }
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final String getProtocol() {
+ return protocol;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final Provider getProvider() {
+ return provider;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ * FIXME: check what exception will be thrown when parameters are null
+ */
+ public final void init(KeyManager[] km, TrustManager[] tm, SecureRandom sr)
+ throws KeyManagementException {
+ spiImpl.engineInit(km, tm, sr);
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final SSLSocketFactory getSocketFactory() {
+ return spiImpl.engineGetSocketFactory();
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final SSLServerSocketFactory getServerSocketFactory() {
+ return spiImpl.engineGetServerSocketFactory();
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final SSLEngine createSSLEngine() {
+ return spiImpl.engineCreateSSLEngine();
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final SSLEngine createSSLEngine(String peerHost, int peerPort) {
+ return spiImpl.engineCreateSSLEngine(peerHost, peerPort);
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final SSLSessionContext getServerSessionContext() {
+ return spiImpl.engineGetServerSessionContext();
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final SSLSessionContext getClientSessionContext() {
+ return spiImpl.engineGetClientSessionContext();
+ }
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLContextSpi.java b/x-net/src/main/java/javax/net/ssl/SSLContextSpi.java
new file mode 100644
index 0000000..fdbd336
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLContextSpi.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.security.KeyManagementException;
+import java.security.SecureRandom;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+
+public abstract class SSLContextSpi {
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public SSLContextSpi() {
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected abstract void engineInit(KeyManager[] km, TrustManager[] tm,
+ SecureRandom sr) throws KeyManagementException;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected abstract SSLSocketFactory engineGetSocketFactory();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected abstract SSLServerSocketFactory engineGetServerSocketFactory();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected abstract SSLEngine engineCreateSSLEngine(String host, int port);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected abstract SSLEngine engineCreateSSLEngine();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected abstract SSLSessionContext engineGetServerSessionContext();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected abstract SSLSessionContext engineGetClientSessionContext();
+
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLEngine.java b/x-net/src/main/java/javax/net/ssl/SSLEngine.java
new file mode 100644
index 0000000..af635c6
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLEngine.java
@@ -0,0 +1,312 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.nio.ByteBuffer;
+import java.nio.ReadOnlyBufferException;
+
+/**
+ *
+ * @com.intel.drl.spec_ref
+ *
+ *
+ */
+public abstract class SSLEngine {
+ // Store host value
+ private final String host;
+
+ // Store port value
+ private final int port;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected SSLEngine() {
+ host = null;
+ port = -1;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected SSLEngine(String host, int port) {
+ this.host = host;
+ this.port = port;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract void beginHandshake() throws SSLException;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract void closeInbound() throws SSLException;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract void closeOutbound();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract Runnable getDelegatedTask();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract String[] getEnabledCipherSuites();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract String[] getEnabledProtocols();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract boolean getEnableSessionCreation();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract SSLEngineResult.HandshakeStatus getHandshakeStatus();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract boolean getNeedClientAuth();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public String getPeerHost() {
+ return host;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public int getPeerPort() {
+ return port;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract SSLSession getSession();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract String[] getSupportedCipherSuites();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract String[] getSupportedProtocols();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract boolean getUseClientMode();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract boolean getWantClientAuth();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract boolean isInboundDone();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract boolean isOutboundDone();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract void setEnabledCipherSuites(String[] suites);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract void setEnabledProtocols(String[] protocols);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract void setEnableSessionCreation(boolean flag);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract void setNeedClientAuth(boolean need);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract void setUseClientMode(boolean mode);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract void setWantClientAuth(boolean want);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts,
+ int offset, int length) throws SSLException;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public abstract SSLEngineResult wrap(ByteBuffer[] srcs, int offset,
+ int length, ByteBuffer dst) throws SSLException;
+
+ /**
+ * implementation behavior follows RI:
+ * jdk 1.5 does not throw IllegalArgumentException when parameters are null
+ * and does not throw ReadOnlyBufferException if dst is read only byte buffer
+ *
+ */
+ public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer dst)
+ throws SSLException {
+// if (src == null) {
+// throw new IllegalArgumentException("Byte buffer src is null");
+// }
+// if (dst == null) {
+// throw new IllegalArgumentException("Byte buffer dst is null");
+// }
+// if (dst.isReadOnly()) {
+// throw new ReadOnlyBufferException();
+// }
+ return unwrap(src, new ByteBuffer[] { dst }, 0, 1);
+ }
+
+ /**
+ * implementation behavior follows RI:
+ * jdk 1.5 does not throw IllegalArgumentException when src is null or if
+ * dsts contains null elements
+ * It does not throw ReadOnlyBufferException when dsts contains read only elements
+ */
+ public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts)
+ throws SSLException {
+// if (src == null) {
+// throw new IllegalArgumentException("Byte buffer src is null");
+// }
+ if (dsts == null) {
+ throw new IllegalArgumentException("Byte buffer array dsts is null");
+ }
+// for (int i = 0; i < dsts.length; i++) {
+// if (dsts[i] == null) {
+// throw new IllegalArgumentException("Byte buffer dsts[" + i
+// + "] is null");
+// }
+// if (dsts[i].isReadOnly()) {
+// throw new ReadOnlyBufferException();
+// }
+// }
+ return unwrap(src, dsts, 0, dsts.length);
+ }
+
+ /**
+ * implementation behavior follows RI: jdk 1.5 does not throw
+ * IllegalArgumentException when dst is null or if srcs contains null
+ * elements It does not throw ReadOnlyBufferException for read only dst
+ *
+ */
+ public SSLEngineResult wrap(ByteBuffer[] srcs, ByteBuffer dst)
+ throws SSLException {
+ if (srcs == null) {
+ throw new IllegalArgumentException("Byte buffer array srcs is null");
+ }
+// for (int i = 0; i < srcs.length; i++) {
+// if (srcs[i] == null) {
+// throw new IllegalArgumentException("Byte buffer srcs[" + i
+// + "] is null");
+// }
+// }
+// if (dst == null) {
+// throw new IllegalArgumentException("Byte buffer array dst is null");
+// }
+// if (dst.isReadOnly()) {
+// throw new ReadOnlyBufferException();
+// }
+ return wrap(srcs, 0, srcs.length, dst);
+ }
+
+ /**
+ * implementation behavior follows RI:
+ * jdk 1.5 does not throw IllegalArgumentException when parameters are null
+ * and does not throw ReadOnlyBufferException if dst is read only byte buffer
+ *
+ */
+ public SSLEngineResult wrap(ByteBuffer src, ByteBuffer dst)
+ throws SSLException {
+// if (src == null) {
+// throw new IllegalArgumentException("Byte buffer src is null");
+// }
+// if (dst == null) {
+// throw new IllegalArgumentException("Byte buffer dst is null");
+// }
+// if (dst.isReadOnly()) {
+// throw new ReadOnlyBufferException();
+// }
+ return wrap(new ByteBuffer[] { src }, 0, 1, dst);
+ }
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLEngineResult.java b/x-net/src/main/java/javax/net/ssl/SSLEngineResult.java
new file mode 100644
index 0000000..5cfba1e
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLEngineResult.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public class SSLEngineResult {
+
+ // Store Status object
+ private final SSLEngineResult.Status status;
+
+ // Store HandshakeStatus object
+ private final SSLEngineResult.HandshakeStatus handshakeStatus;
+
+ // Store bytesConsumed
+ private final int bytesConsumed;
+
+ // Store bytesProduced
+ private final int bytesProduced;
+
+ /**
+ * @com.intel.drl.spec_ref
+ */
+ public SSLEngineResult(SSLEngineResult.Status status,
+ SSLEngineResult.HandshakeStatus handshakeStatus, int bytesConsumed,
+ int bytesProduced) {
+ if (status == null) {
+ throw new IllegalArgumentException("status is null");
+ }
+ if (handshakeStatus == null) {
+ throw new IllegalArgumentException("handshakeStatus is null");
+ }
+ if (bytesConsumed < 0) {
+ throw new IllegalArgumentException("bytesConsumed is negative");
+ }
+ if (bytesProduced < 0) {
+ throw new IllegalArgumentException("bytesProduced is negative");
+ }
+ this.status = status;
+ this.handshakeStatus = handshakeStatus;
+ this.bytesConsumed = bytesConsumed;
+ this.bytesProduced = bytesProduced;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ */
+ public final Status getStatus() {
+ return status;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ */
+ public final HandshakeStatus getHandshakeStatus() {
+ return handshakeStatus;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ */
+ public final int bytesConsumed() {
+ return bytesConsumed;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ */
+ public final int bytesProduced() {
+ return bytesProduced;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer("SSLEngineReport: Status = ");
+ sb.append(status.toString());
+ sb.append(" HandshakeStatus = ");
+ sb.append(handshakeStatus.toString());
+ sb.append("\n bytesConsumed = ");
+ sb.append(Integer.toString(bytesConsumed));
+ sb.append(" bytesProduced = ");
+ sb.append(Integer.toString(bytesProduced));
+ return sb.toString();
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ */
+ public enum HandshakeStatus {
+ NOT_HANDSHAKING,
+ FINISHED,
+ NEED_TASK,
+ NEED_WRAP,
+ NEED_UNWRAP
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ */
+ public static enum Status {
+ BUFFER_OVERFLOW,
+ BUFFER_UNDERFLOW,
+ CLOSED,
+ OK
+ }
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLException.java b/x-net/src/main/java/javax/net/ssl/SSLException.java
new file mode 100644
index 0000000..87f5caa
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLException.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.io.IOException;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public class SSLException extends IOException {
+ /**
+ * @com.intel.drl.spec_ref
+ * @serial
+ */
+ private static final long serialVersionUID = 4511006460650708967L;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public SSLException(String reason) {
+ super(reason);
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public SSLException(String message, Throwable cause) {
+ super(message);
+ super.initCause(cause);
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public SSLException(Throwable cause) {
+ super(cause == null ? null : cause.toString());
+ super.initCause(cause);
+ }
+}
diff --git a/x-net/src/main/java/javax/net/ssl/SSLHandshakeException.java b/x-net/src/main/java/javax/net/ssl/SSLHandshakeException.java
new file mode 100644
index 0000000..a137e47
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLHandshakeException.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public class SSLHandshakeException extends SSLException {
+
+ private static final long serialVersionUID = -5045881315018326890L;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public SSLHandshakeException(String reason) {
+ super(reason);
+ }
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLKeyException.java b/x-net/src/main/java/javax/net/ssl/SSLKeyException.java
new file mode 100644
index 0000000..e00ae0a
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLKeyException.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public class SSLKeyException extends SSLException {
+
+ private static final long serialVersionUID = -8071664081941937874L;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public SSLKeyException(String reason) {
+ super(reason);
+ }
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLPeerUnverifiedException.java b/x-net/src/main/java/javax/net/ssl/SSLPeerUnverifiedException.java
new file mode 100644
index 0000000..c112454
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLPeerUnverifiedException.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public class SSLPeerUnverifiedException extends SSLException {
+
+ private static final long serialVersionUID = -8919512675000600547L;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public SSLPeerUnverifiedException(String reason) {
+ super(reason);
+ }
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLPermission.java b/x-net/src/main/java/javax/net/ssl/SSLPermission.java
new file mode 100644
index 0000000..eeea71e
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLPermission.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.security.BasicPermission;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public final class SSLPermission extends BasicPermission {
+
+ private static final long serialVersionUID = -3456898025505876775L;
+
+ public SSLPermission(String name) {
+ super(name);
+ }
+
+ public SSLPermission(String name, String actions) {
+ super(name, actions);
+ }
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLProtocolException.java b/x-net/src/main/java/javax/net/ssl/SSLProtocolException.java
new file mode 100644
index 0000000..1ec9827
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLProtocolException.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public class SSLProtocolException extends SSLException {
+
+ private static final long serialVersionUID = 5445067063799134928L;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public SSLProtocolException(String reason) {
+ super(reason);
+ }
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLServerSocket.java b/x-net/src/main/java/javax/net/ssl/SSLServerSocket.java
new file mode 100644
index 0000000..b1b6f7a
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLServerSocket.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public abstract class SSLServerSocket extends ServerSocket {
+ protected SSLServerSocket() throws IOException {
+ super();
+ }
+
+ protected SSLServerSocket(int port) throws IOException {
+ super(port);
+ }
+
+ protected SSLServerSocket(int port, int backlog) throws IOException {
+ super(port, backlog);
+ }
+
+ protected SSLServerSocket(int port, int backlog, InetAddress address)
+ throws IOException {
+ super(port, backlog, address);
+ }
+
+ public abstract String[] getEnabledCipherSuites();
+ public abstract void setEnabledCipherSuites(String[] suites);
+ public abstract String[] getSupportedCipherSuites();
+ public abstract String[] getSupportedProtocols();
+ public abstract String[] getEnabledProtocols();
+ public abstract void setEnabledProtocols(String[] protocols);
+ public abstract void setNeedClientAuth(boolean need);
+ public abstract boolean getNeedClientAuth();
+ public abstract void setWantClientAuth(boolean want);
+ public abstract boolean getWantClientAuth();
+ public abstract void setUseClientMode(boolean mode);
+ public abstract boolean getUseClientMode();
+ public abstract void setEnableSessionCreation(boolean flag);
+ public abstract boolean getEnableSessionCreation();
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLServerSocketFactory.java b/x-net/src/main/java/javax/net/ssl/SSLServerSocketFactory.java
new file mode 100644
index 0000000..1445794
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLServerSocketFactory.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.security.AccessController;
+import java.security.Security;
+
+import javax.net.ServerSocketFactory;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public abstract class SSLServerSocketFactory extends ServerSocketFactory {
+// TODO EXPORT CONTROL
+
+ // The default SSL socket factory
+ private static ServerSocketFactory defaultServerSocketFactory;
+
+ private static String defaultName;
+
+ protected SSLServerSocketFactory() {
+ super();
+ }
+
+ public static ServerSocketFactory getDefault() {
+ if (defaultServerSocketFactory != null) {
+ return defaultServerSocketFactory;
+ }
+ if (defaultName == null) {
+ AccessController.doPrivileged(new java.security.PrivilegedAction(){
+ public Object run() {
+ defaultName = Security.getProperty("ssl.ServerSocketFactory.provider");
+ if (defaultName != null) {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ if (cl == null) {
+ cl = ClassLoader.getSystemClassLoader();
+ }
+ try {
+ defaultServerSocketFactory = (ServerSocketFactory) Class
+ .forName(defaultName, true, cl)
+ .newInstance();
+ } catch (Exception e) {
+ return e;
+ }
+ }
+ return null;
+ }
+ });
+ }
+ if (defaultServerSocketFactory == null) {
+ // Try to find in providers
+ SSLContext context = DefaultSSLContext.getContext();
+ if (context != null) {
+ defaultServerSocketFactory = context.getServerSocketFactory();
+ }
+ }
+ if (defaultServerSocketFactory == null) {
+ // Use internal dummy implementation
+ defaultServerSocketFactory = new DefaultSSLServerSocketFactory("No ServerSocketFactory installed");
+ }
+ return defaultServerSocketFactory;
+ }
+
+ public abstract String[] getDefaultCipherSuites();
+ public abstract String[] getSupportedCipherSuites();
+
+}
diff --git a/x-net/src/main/java/javax/net/ssl/SSLSession.java b/x-net/src/main/java/javax/net/ssl/SSLSession.java
new file mode 100644
index 0000000..36a2528
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLSession.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.security.Principal;
+import java.security.cert.Certificate;
+import javax.security.cert.X509Certificate;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public interface SSLSession {
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public int getApplicationBufferSize();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public String getCipherSuite();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public long getCreationTime();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public byte[] getId();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public long getLastAccessedTime();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public Certificate[] getLocalCertificates();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public Principal getLocalPrincipal();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public int getPacketBufferSize();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public X509Certificate[] getPeerCertificateChain()
+ throws SSLPeerUnverifiedException;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public Certificate[] getPeerCertificates()
+ throws SSLPeerUnverifiedException;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public String getPeerHost();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public int getPeerPort();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public String getProtocol();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public SSLSessionContext getSessionContext();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public Object getValue(String name);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public String[] getValueNames();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public void invalidate();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public boolean isValid();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public void putValue(String name, Object value);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public void removeValue(String name);
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLSessionBindingEvent.java b/x-net/src/main/java/javax/net/ssl/SSLSessionBindingEvent.java
new file mode 100644
index 0000000..6d30534
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLSessionBindingEvent.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.io.Serializable;
+import java.util.EventObject;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public class SSLSessionBindingEvent extends EventObject implements Serializable {
+
+ /**
+ * @serial
+ * The 5.0 spec. doesn't declare this serialVersionUID field
+ * In order to be compatible it is explicitly declared here
+ */
+ private static final long serialVersionUID = 3989172637106345L;
+
+ private String name;
+
+ public SSLSessionBindingEvent(SSLSession session, String name) {
+ super(session);
+ this.name = name;
+ }
+ public String getName() {
+ return name;
+ }
+ public SSLSession getSession() {
+ return (SSLSession)this.source;
+ }
+
+
+
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLSessionBindingListener.java b/x-net/src/main/java/javax/net/ssl/SSLSessionBindingListener.java
new file mode 100644
index 0000000..4e3723d
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLSessionBindingListener.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.util.EventListener;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public interface SSLSessionBindingListener extends EventListener {
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public void valueBound(SSLSessionBindingEvent event);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public void valueUnbound(SSLSessionBindingEvent event);
+
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLSessionContext.java b/x-net/src/main/java/javax/net/ssl/SSLSessionContext.java
new file mode 100644
index 0000000..dd32486
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLSessionContext.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.util.Enumeration;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public interface SSLSessionContext {
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public Enumeration getIds();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public SSLSession getSession(byte[] sessionId);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public int getSessionCacheSize();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public int getSessionTimeout();
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public void setSessionCacheSize(int size) throws IllegalArgumentException;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public void setSessionTimeout(int seconds) throws IllegalArgumentException;
+
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLSocket.java b/x-net/src/main/java/javax/net/ssl/SSLSocket.java
new file mode 100644
index 0000000..f11410c
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLSocket.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public abstract class SSLSocket extends Socket {
+ protected SSLSocket() {
+ super();
+ }
+
+ protected SSLSocket(String host, int port) throws IOException,
+ UnknownHostException {
+ super(host, port);
+ }
+
+ protected SSLSocket(InetAddress address, int port) throws IOException {
+ super(address, port);
+ }
+
+ protected SSLSocket(String host, int port, InetAddress clientAddress,
+ int clientPort) throws IOException, UnknownHostException {
+ super(host, port, clientAddress, clientPort);
+ }
+
+ protected SSLSocket(InetAddress address, int port,
+ InetAddress clientAddress, int clientPort) throws IOException {
+ super(address, port, clientAddress, clientPort);
+ }
+
+ public abstract String[] getSupportedCipherSuites();
+ public abstract String[] getEnabledCipherSuites();
+ public abstract void setEnabledCipherSuites(String[] suites);
+ public abstract String[] getSupportedProtocols();
+ public abstract String[] getEnabledProtocols();
+ public abstract void setEnabledProtocols(String[] protocols);
+ public abstract SSLSession getSession();
+ public abstract void addHandshakeCompletedListener(HandshakeCompletedListener listener);
+ public abstract void removeHandshakeCompletedListener(HandshakeCompletedListener listener);
+ public abstract void startHandshake() throws IOException;
+ public abstract void setUseClientMode(boolean mode);
+ public abstract boolean getUseClientMode();
+ public abstract void setNeedClientAuth(boolean need);
+ public abstract boolean getNeedClientAuth();
+ public abstract void setWantClientAuth(boolean want);
+ public abstract boolean getWantClientAuth();
+ public abstract void setEnableSessionCreation(boolean flag);
+ public abstract boolean getEnableSessionCreation();
+
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java b/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java
new file mode 100644
index 0000000..34a221e
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.Socket;
+import java.security.AccessController;
+import java.security.Security;
+
+import javax.net.SocketFactory;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public abstract class SSLSocketFactory extends SocketFactory {
+ // FIXME EXPORT CONTROL
+
+ // The default SSL socket factory
+ private static SocketFactory defaultSocketFactory;
+
+ private static String defaultName;
+
+ public SSLSocketFactory() {
+ super();
+ }
+
+ public static SocketFactory getDefault() {
+ if (defaultSocketFactory != null) {
+ log("SSLSocketFactory", "Using factory " + defaultSocketFactory);
+ return defaultSocketFactory;
+ }
+ if (defaultName == null) {
+ AccessController.doPrivileged(new java.security.PrivilegedAction(){
+ public Object run() {
+ defaultName = Security.getProperty("ssl.SocketFactory.provider");
+ if (defaultName != null) {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ if (cl == null) {
+ cl = ClassLoader.getSystemClassLoader();
+ }
+ try {
+ defaultSocketFactory = (SocketFactory) Class.forName(
+ defaultName, true, cl).newInstance();
+ } catch (Exception e) {
+ return e;
+ }
+ }
+ return null;
+ }
+ });
+ }
+
+ if (defaultSocketFactory == null) {
+ // Try to find in providers
+ SSLContext context = DefaultSSLContext.getContext();
+ if (context != null) {
+ defaultSocketFactory = context.getSocketFactory();
+ }
+ }
+ if (defaultSocketFactory == null) {
+ defaultSocketFactory = new DefaultSSLSocketFactory("No SSLSocketFactory installed");
+ }
+
+ log("SSLSocketFactory", "Using factory " + defaultSocketFactory);
+ return defaultSocketFactory;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void log(String tag, String msg) {
+ try {
+ Class clazz = Class.forName("android.util.Log");
+ Method method = clazz.getMethod("d", new Class[] { String.class, String.class });
+ method.invoke(null, new Object[] { tag, msg });
+ } catch (Exception ex) {
+ // Silently ignore.
+ }
+ }
+
+ public abstract String[] getDefaultCipherSuites();
+
+ public abstract String[] getSupportedCipherSuites();
+
+ public abstract Socket createSocket(Socket s, String host, int port,
+ boolean autoClose) throws IOException;
+
+}
diff --git a/x-net/src/main/java/javax/net/ssl/TrustManager.java b/x-net/src/main/java/javax/net/ssl/TrustManager.java
new file mode 100644
index 0000000..14b75e5
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/TrustManager.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public interface TrustManager {
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/TrustManagerFactory.java b/x-net/src/main/java/javax/net/ssl/TrustManagerFactory.java
new file mode 100644
index 0000000..43919eb
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/TrustManagerFactory.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.security.AccessController;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.Security;
+
+import org.apache.harmony.security.fortress.Engine;
+
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+
+public class TrustManagerFactory {
+ // Store TrustManager service name
+ private static final String SERVICE = "TrustManagerFactory";
+
+ // Used to access common engine functionality
+ private static Engine engine = new Engine(SERVICE);
+
+ // Store default property name
+ private static final String PROPERTYNAME = "ssl.TrustManagerFactory.algorithm";
+
+ // Store used provider
+ private final Provider provider;
+
+ // Storeused TrustManagerFactorySpi implementation
+ private final TrustManagerFactorySpi spiImpl;
+
+ // Store used algorithm
+ private final String algorithm;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected TrustManagerFactory(TrustManagerFactorySpi factorySpi,
+ Provider provider, String algorithm) {
+ this.provider = provider;
+ this.algorithm = algorithm;
+ this.spiImpl = factorySpi;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final String getAlgorithm() {
+ return algorithm;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ * throws NullPointerException if algorithm is null (instead of
+ * NoSuchAlgorithmException as in 1.4 release)
+ */
+ public static final TrustManagerFactory getInstance(String algorithm)
+ throws NoSuchAlgorithmException {
+ if (algorithm == null) {
+ throw new NullPointerException("algorithm is null");
+ }
+ synchronized (engine) {
+ engine.getInstance(algorithm, null);
+ return new TrustManagerFactory((TrustManagerFactorySpi) engine.spi,
+ engine.provider, algorithm);
+ }
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ * throws NullPointerException if algorithm is null (instead of
+ * NoSuchAlgorithmException as in 1.4 release)
+ */
+ public static final TrustManagerFactory getInstance(String algorithm,
+ String provider) throws NoSuchAlgorithmException,
+ NoSuchProviderException {
+ if ((provider == null) || (provider.length() == 0)) {
+ throw new IllegalArgumentException("Provider is null oe empty");
+ }
+ Provider impProvider = Security.getProvider(provider);
+ if (impProvider == null) {
+ throw new NoSuchProviderException(provider);
+ }
+ return getInstance(algorithm, impProvider);
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ * throws NullPointerException if algorithm is null (instead of
+ * NoSuchAlgorithmException as in 1.4 release)
+ */
+ public static final TrustManagerFactory getInstance(String algorithm,
+ Provider provider) throws NoSuchAlgorithmException {
+ if (provider == null) {
+ throw new IllegalArgumentException("Provider is null");
+ }
+ if (algorithm == null) {
+ throw new NullPointerException("algorithm is null");
+ }
+ synchronized (engine) {
+ engine.getInstance(algorithm, provider, null);
+ return new TrustManagerFactory((TrustManagerFactorySpi) engine.spi,
+ provider, algorithm);
+ }
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final Provider getProvider() {
+ return provider;
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final void init(KeyStore ks) throws KeyStoreException {
+ spiImpl.engineInit(ks);
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final void init(ManagerFactoryParameters spec)
+ throws InvalidAlgorithmParameterException {
+ spiImpl.engineInit(spec);
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public final TrustManager[] getTrustManagers() {
+ return spiImpl.engineGetTrustManagers();
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public static final String getDefaultAlgorithm() {
+ return AccessController
+ .doPrivileged(new java.security.PrivilegedAction<String>() {
+ public String run() {
+ return Security.getProperty(PROPERTYNAME);
+ }
+ });
+ }
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/TrustManagerFactorySpi.java b/x-net/src/main/java/javax/net/ssl/TrustManagerFactorySpi.java
new file mode 100644
index 0000000..84b986d
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/TrustManagerFactorySpi.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+
+public abstract class TrustManagerFactorySpi {
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public TrustManagerFactorySpi() {
+ }
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected abstract void engineInit(KeyStore ks) throws KeyStoreException;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected abstract void engineInit(ManagerFactoryParameters spec)
+ throws InvalidAlgorithmParameterException;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ protected abstract TrustManager[] engineGetTrustManagers();
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/X509ExtendedKeyManager.java b/x-net/src/main/java/javax/net/ssl/X509ExtendedKeyManager.java
new file mode 100644
index 0000000..6c47fa9
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/X509ExtendedKeyManager.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris V. Kuznetsov
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.security.Principal;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public abstract class X509ExtendedKeyManager implements X509KeyManager {
+ protected X509ExtendedKeyManager() {
+ super();
+ }
+
+ public String chooseEngineClientAlias(String[] keyType,
+ Principal[] issuers, SSLEngine engine) {
+ return null;
+ }
+
+ public String chooseEngineServerAlias(String keyType, Principal[] issuers,
+ SSLEngine engine) {
+ return null;
+ }
+
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/X509KeyManager.java b/x-net/src/main/java/javax/net/ssl/X509KeyManager.java
new file mode 100644
index 0000000..075e2fc
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/X509KeyManager.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.net.Socket;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public interface X509KeyManager extends KeyManager {
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public String chooseClientAlias(String[] keyType, Principal[] issuers,
+ Socket socket);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public String chooseServerAlias(String keyType, Principal[] issuers,
+ Socket socket);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public X509Certificate[] getCertificateChain(String alias);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public String[] getClientAliases(String keyType, Principal[] issuers);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public String[] getServerAliases(String keyType, Principal[] issuers);
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public PrivateKey getPrivateKey(String alias);
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/X509TrustManager.java b/x-net/src/main/java/javax/net/ssl/X509TrustManager.java
new file mode 100644
index 0000000..ed5a221
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/X509TrustManager.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Vera Y. Petrashkova
+* @version $Revision$
+*/
+
+package javax.net.ssl;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+/**
+ * @com.intel.drl.spec_ref
+ *
+ */
+public interface X509TrustManager extends TrustManager {
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException;
+
+ /**
+ * @com.intel.drl.spec_ref
+ *
+ */
+ public X509Certificate[] getAcceptedIssuers();
+} \ No newline at end of file
diff --git a/x-net/src/main/java/javax/net/ssl/package.html b/x-net/src/main/java/javax/net/ssl/package.html
new file mode 100644
index 0000000..732b05f
--- /dev/null
+++ b/x-net/src/main/java/javax/net/ssl/package.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
+</head>
+<html>
+<body>
+<p>
+This package provides all the classes and interfaces needed to implemenet and program the Secure Socket
+abstraction based on the SSL protocol SSSLv3.0 or TLSv1.2
+All the details of the SSL handshake protocol ar card for, and the cipher set with which
+a client or a server work can be specified.
+
+X.509 certificates are verified and if desired either the client or the server can walk through
+the whole certificates' chain until the root CA is reached.
+
+Notice that the Android javax.net.ssl package uses the OpenSSL Library to implement the low level
+SSL functionality. All the relevant OpenSSl write(...) and read(...) functions are hidden within two
+JNI files. The signatures of all the Java SSL methods are compliant with that of the Java 5.0
+specification.
+
+The provider for all SSL cryptological tools is The Legion of Bouncy Castle (http://www.bouncycastle.org).
+</p>
+</body>
+</html> \ No newline at end of file
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/internal/nls/Messages.java b/x-net/src/main/java/org/apache/harmony/xnet/internal/nls/Messages.java
new file mode 100644
index 0000000..efad845
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/internal/nls/Messages.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * THE FILE HAS BEEN AUTOGENERATED BY MSGTOOL TOOL.
+ * All changes made to this file manually will be overwritten
+ * if this tool runs again. Better make changes in the template file.
+ */
+
+package org.apache.harmony.xnet.internal.nls;
+
+import org.apache.harmony.luni.util.MsgHelp;
+
+/**
+ * This class retrieves strings from a resource bundle and returns them,
+ * formatting them with MessageFormat when required.
+ * <p>
+ * It is used by the system classes to provide national language support, by
+ * looking up messages in the <code>
+ * org.apache.harmony.xnet.internal.nls.messages
+ * </code>
+ * resource bundle. Note that if this file is not available, or an invalid key
+ * is looked up, or resource bundle support is not available, the key itself
+ * will be returned as the associated message. This means that the <em>KEY</em>
+ * should a reasonable human-readable (english) string.
+ *
+ */
+public class Messages {
+
+ private static final String sResource =
+ "org.apache.harmony.xnet.internal.nls.messages"; //$NON-NLS-1$
+
+ /**
+ * Retrieves a message which has no arguments.
+ *
+ * @param msg
+ * String the key to look up.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg) {
+ return MsgHelp.getString(sResource, msg);
+ }
+
+ /**
+ * Retrieves a message which takes 1 argument.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg
+ * Object the object to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, Object arg) {
+ return getString(msg, new Object[] { arg });
+ }
+
+ /**
+ * Retrieves a message which takes 1 integer argument.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg
+ * int the integer to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, int arg) {
+ return getString(msg, new Object[] { Integer.toString(arg) });
+ }
+
+ /**
+ * Retrieves a message which takes 1 character argument.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg
+ * char the character to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, char arg) {
+ return getString(msg, new Object[] { String.valueOf(arg) });
+ }
+
+ /**
+ * Retrieves a message which takes 2 arguments.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg1
+ * Object an object to insert in the formatted output.
+ * @param arg2
+ * Object another object to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, Object arg1, Object arg2) {
+ return getString(msg, new Object[] { arg1, arg2 });
+ }
+
+ /**
+ * Retrieves a message which takes several arguments.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param args
+ * Object[] the objects to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, Object[] args) {
+ return MsgHelp.getString(sResource, msg, args);
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/internal/nls/messages.properties b/x-net/src/main/java/org/apache/harmony/xnet/internal/nls/messages.properties
new file mode 100644
index 0000000..229ca61
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/internal/nls/messages.properties
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# messages for EN locale \ No newline at end of file
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertException.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertException.java
new file mode 100644
index 0000000..edf7638
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertException.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import javax.net.ssl.SSLException;
+
+/**
+ * This exception is used to signalize the fatal alert
+ * occured during the work of protocol.
+ */
+public class AlertException extends RuntimeException {
+
+ // SSLException to be thrown to application side
+ private final SSLException reason;
+ // alert description code
+ private final byte description;
+
+ /**
+ * Constructs the instance.
+ * @param description: The alert description code.
+ * @see org.apache.harmony.xnet.provider.jsse.AlertProtocol
+ * @param reason: The SSLException to be thrown to application
+ * side after alert processing (sending the record with alert,
+ * shoutdown work, etc).
+ */
+ protected AlertException(byte description, SSLException reason) {
+ super(reason);
+ this.reason = reason;
+ this.description = description;
+ }
+
+ /**
+ * Returns the reason of alert. This reason should be rethrown
+ * after alert protcessin.
+ * @return the reason of alert.
+ */
+ protected SSLException getReason() {
+ return reason;
+ }
+
+ /**
+ * Returns alert's description code.
+ * @return byte value describing the occured alert.
+ * @see org.apache.harmony.xnet.provider.jsse.AlertProtocol for more information about possible
+ * reason codes.
+ */
+ protected byte getDescriptionCode() {
+ return description;
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertProtocol.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertProtocol.java
new file mode 100644
index 0000000..8f10875
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertProtocol.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.SSLRecordProtocol;
+import org.apache.harmony.xnet.provider.jsse.Logger;
+import org.apache.harmony.xnet.provider.jsse.ContentType;
+
+/**
+ * This class encapsulates the functionality of Alert Protocol.
+ * Constant values are taken according to the TLS v1 specification
+ * (http://www.ietf.org/rfc/rfc2246.txt), p 7.2.
+ */
+public class AlertProtocol {
+
+ // ------------------------ AlertLevel codes --------------------------
+ /**
+ * Defines the severity of alert as warning
+ */
+ protected static final byte WARNING = 1;
+ /**
+ * Defines the severity of alert as fatal
+ */
+ protected static final byte FATAL = 2;
+
+ // --------------------- AlertDescription codes -----------------------
+ /**
+ * Defines the description code of the close_notify alert
+ */
+ protected static final byte CLOSE_NOTIFY = 0;
+ /**
+ * Defines the description code of the unexpected_message alert
+ */
+ protected static final byte UNEXPECTED_MESSAGE = 10;
+ /**
+ * Defines the description code of the bad_record_mac alert
+ */
+ protected static final byte BAD_RECORD_MAC = 20;
+ /**
+ * Defines the description code of the decryption_failed alert
+ */
+ protected static final byte DECRYPTION_FAILED = 21;
+ /**
+ * Defines the description code of the record_overflow alert
+ */
+ protected static final byte RECORD_OVERFLOW = 22;
+ /**
+ * Defines the description code of the decompression_failure alert
+ */
+ protected static final byte DECOMPRESSION_FAILURE = 30;
+ /**
+ * Defines the description code of the handshake_failure alert
+ */
+ protected static final byte HANDSHAKE_FAILURE = 40;
+ /**
+ * Defines the description code of the bad_certificate alert
+ */
+ protected static final byte BAD_CERTIFICATE = 42;
+ /**
+ * Defines the description code of the unsupported_certificate alert
+ */
+ protected static final byte UNSUPPORTED_CERTIFICATE = 43;
+ /**
+ * Defines the description code of the certificate_revoked alert
+ */
+ protected static final byte CERTIFICATE_REVOKED = 44;
+ /**
+ * Defines the description code of the certificate_expired alert
+ */
+ protected static final byte CERTIFICATE_EXPIRED = 45;
+ /**
+ * Defines the description code of the certificate_unknown alert
+ */
+ protected static final byte CERTIFICATE_UNKNOWN = 46;
+ /**
+ * Defines the description code of the illegal_parameter alert
+ */
+ protected static final byte ILLEGAL_PARAMETER = 47;
+ /**
+ * Defines the description code of the unknown_ca alert
+ */
+ protected static final byte UNKNOWN_CA = 48;
+ /**
+ * Defines the description code of the access_denied alert
+ */
+ protected static final byte ACCESS_DENIED = 49;
+ /**
+ * Defines the description code of the decode_error alert
+ */
+ protected static final byte DECODE_ERROR = 50;
+ /**
+ * Defines the description code of the decrypt_error alert
+ */
+ protected static final byte DECRYPT_ERROR = 51;
+ /**
+ * Defines the description code of the export_restriction alert
+ */
+ protected static final byte EXPORT_RESTRICTION = 60;
+ /**
+ * Defines the description code of the protocol_version alert
+ */
+ protected static final byte PROTOCOL_VERSION = 70;
+ /**
+ * Defines the description code of the insufficient_security alert
+ */
+ protected static final byte INSUFFICIENT_SECURITY = 71;
+ /**
+ * Defines the description code of the internal_error alert
+ */
+ protected static final byte INTERNAL_ERROR = 80;
+ /**
+ * Defines the description code of the user_canceled alert
+ */
+ protected static final byte USER_CANCELED = 90;
+ /**
+ * Defines the description code of the no_renegotiation alert
+ */
+ protected static final byte NO_RENEGOTIATION = 100;
+
+
+ // holds level and description codes
+ private final byte[] alert = new byte[2];
+ // record protocol to be used to wrap the alerts
+ private SSLRecordProtocol recordProtocol;
+
+ private Logger.Stream logger = Logger.getStream("alert");
+
+ /**
+ * Creates the instance of AlertProtocol.
+ * Note that class is not ready to work without providing of
+ * record protocol
+ * @see #setRecordProtocol
+ */
+ protected AlertProtocol() {}
+
+ /**
+ * Sets up the record protocol to be used by this allert protocol.
+ */
+ protected void setRecordProtocol(SSLRecordProtocol recordProtocol) {
+ this.recordProtocol = recordProtocol;
+ }
+
+ /**
+ * Reports an alert to be sent/received by transport.
+ * This method is usually called during processing
+ * of the income TSL record: if it contains alert message from another
+ * peer, or if warning alert occured during the processing of the
+ * message and this warning should be sent to another peer.
+ * @param level: alert level code
+ * @param description: alert description code
+ * @return
+ */
+ protected void alert(byte level, byte description) {
+ if (logger != null) {
+ logger.println("Alert.alert: "+level+" "+description);
+ }
+ this.alert[0] = level;
+ this.alert[1] = description;
+ }
+
+ /**
+ * Returns the description code of alert or -100 if there
+ * is no alert.
+ */
+ protected byte getDescriptionCode() {
+ return (alert[0] != 0) ? alert[1] : -100;
+ }
+
+ /**
+ * Resets the protocol to be in "no alert" state.
+ * This method shoud be called after processing of the reported alert.
+ */
+ protected void setProcessed() {
+ // free the info about alert
+ if (logger != null) {
+ logger.println("Alert.setProcessed");
+ }
+ this.alert[0] = 0;
+ }
+
+ /**
+ * Checks if any alert has occured.
+ */
+ protected boolean hasAlert() {
+ return (alert[0] != 0);
+ }
+
+ /**
+ * Checks if occured alert is fatal alert.
+ */
+ protected boolean isFatalAlert() {
+ return (alert[0] == 2);
+ }
+
+ /**
+ * Returns the string representation of occured alert.
+ * If no alert has occured null is returned.
+ */
+ protected String getAlertDescription() {
+ switch (alert[1]) {
+ case CLOSE_NOTIFY:
+ return "close_notify";
+ case UNEXPECTED_MESSAGE:
+ return "unexpected_message";
+ case BAD_RECORD_MAC:
+ return "bad_record_mac";
+ case DECRYPTION_FAILED:
+ return "decryption_failed";
+ case RECORD_OVERFLOW:
+ return "record_overflow";
+ case DECOMPRESSION_FAILURE:
+ return "decompression_failure";
+ case HANDSHAKE_FAILURE:
+ return "handshake_failure";
+ case BAD_CERTIFICATE:
+ return "bad_certificate";
+ case UNSUPPORTED_CERTIFICATE:
+ return "unsupported_certificate";
+ case CERTIFICATE_REVOKED:
+ return "certificate_revoked";
+ case CERTIFICATE_EXPIRED:
+ return "certificate_expired";
+ case CERTIFICATE_UNKNOWN:
+ return "certificate_unknown";
+ case ILLEGAL_PARAMETER:
+ return "illegal_parameter";
+ case UNKNOWN_CA:
+ return "unknown_ca";
+ case ACCESS_DENIED:
+ return "access_denied";
+ case DECODE_ERROR:
+ return "decode_error";
+ case DECRYPT_ERROR:
+ return "decrypt_error";
+ case EXPORT_RESTRICTION:
+ return "export_restriction";
+ case PROTOCOL_VERSION:
+ return "protocol_version";
+ case INSUFFICIENT_SECURITY:
+ return "insufficient_security";
+ case INTERNAL_ERROR:
+ return "internal_error";
+ case USER_CANCELED:
+ return "user_canceled";
+ case NO_RENEGOTIATION:
+ return "no_renegotiation";
+ }
+ return null;
+ }
+
+ /**
+ * Returns the record with reported alert message.
+ * The returned array of bytes is ready to be sent to another peer.
+ * Note, that this method does not automatically set the state of allert
+ * protocol in "no alert" state, so after wrapping the method setProcessed
+ * should be called.
+ */
+ protected byte[] wrap() {
+ byte[] res = recordProtocol.wrap(ContentType.ALERT, alert, 0, 2);
+ return res;
+ }
+
+ /**
+ * Shutdownes the protocol. It will be impossiblke to use the instance
+ * after the calling of this method.
+ */
+ protected void shutdown() {
+ alert[0] = 0;
+ alert[1] = 0;
+ recordProtocol = null;
+ }
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Appendable.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Appendable.java
new file mode 100644
index 0000000..1485bae
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Appendable.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+/**
+ * This interface represents the ability of the input stream related
+ * classes to provide additianal data to be read.
+ */
+public interface Appendable {
+
+ /**
+ * Provides the additional data to be read.
+ * @param src: the source data to be appended.
+ */
+ public void append(byte[] src);
+
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateMessage.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateMessage.java
new file mode 100644
index 0000000..6aac128
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateMessage.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.Message;
+import org.apache.harmony.xnet.provider.jsse.Handshake;
+import org.apache.harmony.xnet.provider.jsse.HandshakeIODataStream;
+import org.apache.harmony.xnet.provider.jsse.AlertProtocol;
+
+import java.io.IOException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Vector;
+
+/**
+ *
+ * Represents server/client certificate message
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS
+ * 1.0 spec., 7.4.2. Server certificate; 7.4.6. Client certificate</a>
+ *
+ */
+public class CertificateMessage extends Message {
+
+ /**
+ * Certificates
+ */
+ X509Certificate[] certs;
+
+ /**
+ * Certificates in encoded form
+ */
+ byte[][] encoded_certs;
+
+ /**
+ * Creates inbound message
+ *
+ * @param in
+ * @param length
+ * @throws IOException
+ */
+ public CertificateMessage(HandshakeIODataStream in, int length)
+ throws IOException {
+ int l = in.readUint24(); // total_length
+ if (l == 0) { // message contais no certificates
+ if (length != 3) { // no more bytes after total_length
+ fatalAlert(AlertProtocol.DECODE_ERROR,
+ "DECODE ERROR: incorrect CertificateMessage");
+ }
+ certs = new X509Certificate[0];
+ encoded_certs = new byte[0][0];
+ this.length = 3;
+ return;
+ }
+ CertificateFactory cf;
+ try {
+ cf = CertificateFactory.getInstance("X509");
+ } catch (CertificateException e) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e);
+ return;
+ }
+ Vector certs_vector = new Vector();
+ int size = 0;
+ int enc_size = 0;
+ while (l > 0) {
+ size = in.readUint24();
+ l -= 3;
+ try {
+ certs_vector.add(cf.generateCertificate(in));
+ } catch (CertificateException e) {
+ fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR", e);
+ }
+ l -= size;
+ enc_size += size;
+ }
+ certs = new X509Certificate[certs_vector.size()];
+ for (int i = 0; i < certs.length; i++) {
+ certs[i] = (X509Certificate) certs_vector.elementAt(i);
+ }
+ this.length = 3 + 3 * certs.length + enc_size;
+ if (this.length != length) {
+ fatalAlert(AlertProtocol.DECODE_ERROR,
+ "DECODE ERROR: incorrect CertificateMessage");
+ }
+
+ }
+
+ /**
+ * Creates outbound message
+ *
+ * @param certs
+ */
+ public CertificateMessage(X509Certificate[] certs) {
+ if (certs == null) {
+ this.certs = new X509Certificate[0];
+ encoded_certs = new byte[0][0];
+ length = 3;
+ return;
+ }
+ this.certs = certs;
+ if (encoded_certs == null) {
+ encoded_certs = new byte[certs.length][];
+ for (int i = 0; i < certs.length; i++) {
+ try {
+ encoded_certs[i] = certs[i].getEncoded();
+ } catch (CertificateEncodingException e) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR",
+ e);
+ }
+ }
+ }
+ length = 3 + 3 * encoded_certs.length;
+ for (int i = 0; i < encoded_certs.length; i++) {
+ length += encoded_certs[i].length;
+ }
+ }
+
+ /**
+ * Sends message
+ *
+ * @param out
+ */
+ public void send(HandshakeIODataStream out) {
+
+ int total_length = 0;
+ if (encoded_certs == null) {
+ encoded_certs = new byte[certs.length][];
+ for (int i = 0; i < certs.length; i++) {
+ try {
+ encoded_certs[i] = certs[i].getEncoded();
+ } catch (CertificateEncodingException e) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR",
+ e);
+ }
+ }
+ }
+ total_length = 3 * encoded_certs.length;
+ for (int i = 0; i < encoded_certs.length; i++) {
+ total_length += encoded_certs[i].length;
+ }
+ out.writeUint24(total_length);
+ for (int i = 0; i < encoded_certs.length; i++) {
+ out.writeUint24(encoded_certs[i].length);
+ out.write(encoded_certs[i]);
+ }
+
+ }
+
+ /**
+ * Returns message type
+ *
+ * @return
+ */
+ public int getType() {
+ return Handshake.CERTIFICATE;
+ }
+
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java
new file mode 100644
index 0000000..8bedccd
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.Message;
+import org.apache.harmony.xnet.provider.jsse.Handshake;
+import org.apache.harmony.xnet.provider.jsse.HandshakeIODataStream;
+import org.apache.harmony.xnet.provider.jsse.AlertProtocol;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.Vector;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ *
+ * Represents certificate request message
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.4.
+ * Certificate request</a>
+ */
+public class CertificateRequest extends Message {
+
+ /**
+ * Client certificate types as defined in
+ * TLS 1.0 spec., 7.4.4. Certificate request
+ */
+ public static final byte RSA_SIGN = 1;
+ public static final byte DSS_SIGN = 2;
+ public static final byte RSA_FIXED_DH = 3;
+ public static final byte DSS_FIXED_DH = 4;
+
+ /**
+ * Requested certificate types
+ */
+ final byte[] certificate_types;
+
+ /**
+ * Certificate authorities
+ */
+ X500Principal[] certificate_authorities;
+
+ //Requested certificate types as Strings
+ // ("RSA", "DSA", "DH_RSA" or "DH_DSA")
+ private String[] types;
+
+ // Encoded form of certificate authorities
+ private byte[][] encoded_principals;
+
+ /**
+ * Creates outbound message
+ *
+ * @param certificate_types
+ * @param accepted - array of certificate authority certificates
+ */
+ public CertificateRequest(byte[] certificate_types,
+ X509Certificate[] accepted) {
+
+ if (accepted == null) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR,
+ "CertificateRequest: array of certificate authority certificates is null");
+ }
+ this.certificate_types = certificate_types;
+
+ int totalPrincipalsLength = 0;
+ certificate_authorities = new X500Principal[accepted.length];
+ encoded_principals = new byte[accepted.length][];
+ for (int i = 0; i < accepted.length; i++) {
+ certificate_authorities[i] = accepted[i].getIssuerX500Principal();
+ encoded_principals[i] = certificate_authorities[i].getEncoded();
+ totalPrincipalsLength += encoded_principals[i].length + 2;
+ }
+
+ length = 3 + certificate_types.length + totalPrincipalsLength;
+ }
+
+ /**
+ * Creates inbound message
+ *
+ * @param in
+ * @param length
+ * @throws IOException
+ */
+ public CertificateRequest(HandshakeIODataStream in, int length)
+ throws IOException {
+ int size = in.readUint8();
+ certificate_types = new byte[size];
+ in.read(certificate_types, 0, size);
+ size = in.readUint16();
+ certificate_authorities = new X500Principal[size];
+ int totalPrincipalsLength = 0;
+ int principalLength = 0;
+ Vector principals = new Vector();
+ while (totalPrincipalsLength < size) {
+ principalLength = in.readUint16(); // encoded X500Principal size
+ principals.add(new X500Principal(in));
+ totalPrincipalsLength += 2;
+ totalPrincipalsLength += principalLength;
+ }
+ certificate_authorities = new X500Principal[principals.size()];
+ for (int i = 0; i < certificate_authorities.length; i++) {
+ certificate_authorities[i] = (X500Principal) principals.elementAt(i);
+ }
+ this.length = 3 + certificate_types.length + totalPrincipalsLength;
+ if (this.length != length) {
+ fatalAlert(AlertProtocol.DECODE_ERROR,
+ "DECODE ERROR: incorrect CertificateRequest");
+ }
+
+ }
+
+ /**
+ * Sends message
+ *
+ * @param out
+ */
+ public void send(HandshakeIODataStream out) {
+
+ out.writeUint8(certificate_types.length);
+ for (int i = 0; i < certificate_types.length; i++) {
+ out.write(certificate_types[i]);
+ }
+ int authoritiesLength = 0;
+ for (int i = 0; i < certificate_authorities.length; i++) {
+ authoritiesLength += encoded_principals[i].length +2;
+ }
+ out.writeUint16(authoritiesLength);
+ for (int i = 0; i < certificate_authorities.length; i++) {
+ out.writeUint16(encoded_principals[i].length);
+ out.write(encoded_principals[i]);
+ }
+ }
+
+ /**
+ * Returns message type
+ *
+ * @return
+ */
+ public int getType() {
+ return Handshake.CERTIFICATE_REQUEST;
+ }
+
+ /**
+ * Returns requested certificate types as array of strings
+ */
+ public String[] getTypesAsString() {
+ if (types == null) {
+ types = new String[certificate_types.length];
+ for (int i = 0; i < types.length; i++) {
+ switch (certificate_types[i]) {
+ case 1:
+ types[i] = "RSA";
+ break;
+ case 2:
+ types[i] = "DSA";
+ break;
+ case 3:
+ types[i] = "DH_RSA";
+ break;
+ case 4:
+ types[i] = "DH_DSA";
+ break;
+ default:
+ fatalAlert(AlertProtocol.DECODE_ERROR,
+ "DECODE ERROR: incorrect CertificateRequest");
+ }
+ }
+ }
+ return types;
+ }
+
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateVerify.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateVerify.java
new file mode 100644
index 0000000..183b8aa
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateVerify.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.Message;
+import org.apache.harmony.xnet.provider.jsse.Handshake;
+import org.apache.harmony.xnet.provider.jsse.HandshakeIODataStream;
+import org.apache.harmony.xnet.provider.jsse.AlertProtocol;
+
+import java.io.IOException;
+
+/**
+ *
+ * Represents certificate verify message
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.8.
+ * Certificate verify</a>
+ */
+public class CertificateVerify extends Message {
+
+ /**
+ * Signature
+ */
+ byte[] signedHash;
+
+ /**
+ * Creates outbound message
+ *
+ * @param hash
+ */
+ public CertificateVerify(byte[] hash) {
+ if (hash == null || hash.length == 0) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR,
+ "INTERNAL ERROR: incorrect certificate verify hash");
+ }
+ this.signedHash = hash;
+ length = hash.length + 2;
+ }
+
+ /**
+ * Creates inbound message
+ *
+ * @param in
+ * @param length
+ * @throws IOException
+ */
+ public CertificateVerify(HandshakeIODataStream in, int length)
+ throws IOException {
+ if (length == 0) {
+ fatalAlert(AlertProtocol.DECODE_ERROR,
+ "DECODE ERROR: incorrect CertificateVerify");
+ } else {
+ if (in.readUint16() != length - 2) {
+ fatalAlert(AlertProtocol.DECODE_ERROR,
+ "DECODE ERROR: incorrect CertificateVerify");
+ }
+ signedHash = in.read(length -2);
+ }
+ this.length = length;
+ }
+
+ /**
+ * Sends message
+ *
+ * @param out
+ */
+ public void send(HandshakeIODataStream out) {
+ if (signedHash.length != 0) {
+ out.writeUint16(signedHash.length);
+ out.write(signedHash);
+ }
+ }
+
+ /**
+ * Returns message type
+ *
+ * @return
+ */
+ public int getType() {
+ return Handshake.CERTIFICATE_VERIFY;
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CipherSuite.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CipherSuite.java
new file mode 100644
index 0000000..8352386
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CipherSuite.java
@@ -0,0 +1,612 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.security.GeneralSecurityException;
+import java.util.Hashtable;
+
+import javax.crypto.Cipher;
+
+/**
+ * Represents Cipher Suite as defined in TLS 1.0 spec.,
+ * A.5. The CipherSuite;
+ * C. CipherSuite definitions.
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec.</a>
+ *
+ */
+public class CipherSuite {
+
+ /**
+ * true if this cipher suite is supported
+ */
+ boolean supported = true;
+
+ /**
+ * cipher suite key exchange
+ */
+ final int keyExchange;
+
+ /**
+ * cipher
+ */
+ final String cipherName;
+
+ /**
+ * Cipher information
+ */
+ final int keyMaterial;
+ final int expandedKeyMaterial;
+ final int effectiveKeyBytes;
+ final int IVSize;
+ final private int blockSize;
+
+ // cipher suite code
+ private final byte[] cipherSuiteCode;
+
+ // cipher suite name
+ private final String name;
+
+ // true if cipher suite is exportable
+ private final boolean isExportable;
+
+ // Hash algorithm
+ final private String hashName;
+
+ // MAC algorithm
+ final private String hmacName;
+
+ // Hash size
+ final private int hashSize;
+
+ /**
+ * key exchange values
+ */
+ static int KeyExchange_RSA = 1;
+ static int KeyExchange_RSA_EXPORT = 2;
+ static int KeyExchange_DHE_DSS = 3;
+ static int KeyExchange_DHE_DSS_EXPORT = 4;
+ static int KeyExchange_DHE_RSA = 5;
+ static int KeyExchange_DHE_RSA_EXPORT = 6;
+ static int KeyExchange_DH_DSS = 7;
+ static int KeyExchange_DH_RSA = 8;
+ static int KeyExchange_DH_anon = 9;
+ static int KeyExchange_DH_anon_EXPORT = 10;
+ static int KeyExchange_DH_DSS_EXPORT = 11;
+ static int KeyExchange_DH_RSA_EXPORT = 12;
+
+ /**
+ * TLS cipher suite codes
+ */
+ static byte[] code_TLS_NULL_WITH_NULL_NULL = { 0x00, 0x00 };
+ static byte[] code_TLS_RSA_WITH_NULL_MD5 = { 0x00, 0x01 };
+ static byte[] code_TLS_RSA_WITH_NULL_SHA = { 0x00, 0x02 };
+ static byte[] code_TLS_RSA_EXPORT_WITH_RC4_40_MD5 = { 0x00, 0x03 };
+ static byte[] code_TLS_RSA_WITH_RC4_128_MD5 = { 0x00, 0x04 };
+ static byte[] code_TLS_RSA_WITH_RC4_128_SHA = { 0x00, 0x05 };
+ static byte[] code_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = { 0x00, 0x06 };
+ static byte[] code_TLS_RSA_WITH_IDEA_CBC_SHA = { 0x00, 0x07 };
+ static byte[] code_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x08 };
+ static byte[] code_TLS_RSA_WITH_DES_CBC_SHA = { 0x00, 0x09 };
+ static byte[] code_TLS_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x0A };
+ static byte[] code_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x0B };
+ static byte[] code_TLS_DH_DSS_WITH_DES_CBC_SHA = { 0x00, 0x0C };
+ static byte[] code_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x0D };
+ static byte[] code_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x0E };
+ static byte[] code_TLS_DH_RSA_WITH_DES_CBC_SHA = { 0x00, 0x0F };
+ static byte[] code_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x10 };
+ static byte[] code_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x11 };
+ static byte[] code_TLS_DHE_DSS_WITH_DES_CBC_SHA = { 0x00, 0x12 };
+ static byte[] code_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x13 };
+ static byte[] code_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x14 };
+ static byte[] code_TLS_DHE_RSA_WITH_DES_CBC_SHA = { 0x00, 0x15 };
+ static byte[] code_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x16 };
+ static byte[] code_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = { 0x00, 0x17 };
+ static byte[] code_TLS_DH_anon_WITH_RC4_128_MD5 = { 0x00, 0x18 };
+ static byte[] code_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x19 };
+ static byte[] code_TLS_DH_anon_WITH_DES_CBC_SHA = { 0x00, 0x1A };
+ static byte[] code_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x1B };
+
+ static CipherSuite TLS_NULL_WITH_NULL_NULL = new CipherSuite(
+ "TLS_NULL_WITH_NULL_NULL", true, 0, null, null,
+ code_TLS_NULL_WITH_NULL_NULL);
+
+ static CipherSuite TLS_RSA_WITH_NULL_MD5 = new CipherSuite(
+ "TLS_RSA_WITH_NULL_MD5", true, KeyExchange_RSA, null, "MD5",
+ code_TLS_RSA_WITH_NULL_MD5);
+
+ static CipherSuite TLS_RSA_WITH_NULL_SHA = new CipherSuite(
+ "TLS_RSA_WITH_NULL_SHA", true, KeyExchange_RSA, null, "SHA",
+ code_TLS_RSA_WITH_NULL_SHA);
+
+ static CipherSuite TLS_RSA_EXPORT_WITH_RC4_40_MD5 = new CipherSuite(
+ "TLS_RSA_EXPORT_WITH_RC4_40_MD5", true, KeyExchange_RSA_EXPORT,
+ "RC4_40", "MD5", code_TLS_RSA_EXPORT_WITH_RC4_40_MD5);
+
+ static CipherSuite TLS_RSA_WITH_RC4_128_MD5 = new CipherSuite(
+ "TLS_RSA_WITH_RC4_128_MD5", false, KeyExchange_RSA, "RC4_128",
+ "MD5", code_TLS_RSA_WITH_RC4_128_MD5);
+
+ static CipherSuite TLS_RSA_WITH_RC4_128_SHA = new CipherSuite(
+ "TLS_RSA_WITH_RC4_128_SHA", false, KeyExchange_RSA, "RC4_128",
+ "SHA", code_TLS_RSA_WITH_RC4_128_SHA);
+
+ static CipherSuite TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = new CipherSuite(
+ "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5", true, KeyExchange_RSA_EXPORT,
+ "RC2_CBC_40", "MD5", code_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5);
+
+ static CipherSuite TLS_RSA_WITH_IDEA_CBC_SHA = new CipherSuite(
+ "TLS_RSA_WITH_IDEA_CBC_SHA", false, KeyExchange_RSA, "IDEA_CBC",
+ "SHA", code_TLS_RSA_WITH_IDEA_CBC_SHA);
+
+ static CipherSuite TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
+ "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA", true, KeyExchange_RSA_EXPORT,
+ "DES40_CBC", "SHA", code_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA);
+
+ static CipherSuite TLS_RSA_WITH_DES_CBC_SHA = new CipherSuite(
+ "TLS_RSA_WITH_DES_CBC_SHA", false, KeyExchange_RSA, "DES_CBC",
+ "SHA", code_TLS_RSA_WITH_DES_CBC_SHA);
+
+ static CipherSuite TLS_RSA_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
+ "TLS_RSA_WITH_3DES_EDE_CBC_SHA", false, KeyExchange_RSA,
+ "3DES_EDE_CBC", "SHA", code_TLS_RSA_WITH_3DES_EDE_CBC_SHA);
+
+ static CipherSuite TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
+ "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", true,
+ KeyExchange_DH_DSS_EXPORT, "DES40_CBC", "SHA",
+ code_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA);
+
+ static CipherSuite TLS_DH_DSS_WITH_DES_CBC_SHA = new CipherSuite(
+ "TLS_DH_DSS_WITH_DES_CBC_SHA", false, KeyExchange_DH_DSS,
+ "DES_CBC", "SHA", code_TLS_DH_DSS_WITH_DES_CBC_SHA);
+
+ static CipherSuite TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
+ "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA", false, KeyExchange_DH_DSS,
+ "3DES_EDE_CBC", "SHA", code_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA);
+
+ static CipherSuite TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
+ "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", true,
+ KeyExchange_DH_RSA_EXPORT, "DES40_CBC", "SHA",
+ code_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA);
+
+ static CipherSuite TLS_DH_RSA_WITH_DES_CBC_SHA = new CipherSuite(
+ "TLS_DH_RSA_WITH_DES_CBC_SHA", false, KeyExchange_DH_RSA,
+ "DES_CBC", "SHA", code_TLS_DH_RSA_WITH_DES_CBC_SHA);
+
+ static CipherSuite TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
+ "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA", false, KeyExchange_DH_RSA,
+ "3DES_EDE_CBC", "SHA", code_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA);
+
+ static CipherSuite TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
+ "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", true,
+ KeyExchange_DHE_DSS_EXPORT, "DES40_CBC", "SHA",
+ code_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA);
+
+ static CipherSuite TLS_DHE_DSS_WITH_DES_CBC_SHA = new CipherSuite(
+ "TLS_DHE_DSS_WITH_DES_CBC_SHA", false, KeyExchange_DHE_DSS,
+ "DES_CBC", "SHA", code_TLS_DHE_DSS_WITH_DES_CBC_SHA);
+
+ static CipherSuite TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
+ "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", false, KeyExchange_DHE_DSS,
+ "3DES_EDE_CBC", "SHA", code_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA);
+
+ static CipherSuite TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
+ "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", true,
+ KeyExchange_DHE_RSA_EXPORT, "DES40_CBC", "SHA",
+ code_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA);
+
+ static CipherSuite TLS_DHE_RSA_WITH_DES_CBC_SHA = new CipherSuite(
+ "TLS_DHE_RSA_WITH_DES_CBC_SHA", false, KeyExchange_DHE_RSA,
+ "DES_CBC", "SHA", code_TLS_DHE_RSA_WITH_DES_CBC_SHA);
+
+ static CipherSuite TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
+ "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", false, KeyExchange_DHE_RSA,
+ "3DES_EDE_CBC", "SHA", code_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA);
+
+ static CipherSuite TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = new CipherSuite(
+ "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5", true,
+ KeyExchange_DH_anon_EXPORT, "RC4_40", "MD5",
+ code_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5);
+
+ static CipherSuite TLS_DH_anon_WITH_RC4_128_MD5 = new CipherSuite(
+ "TLS_DH_anon_WITH_RC4_128_MD5", false, KeyExchange_DH_anon,
+ "RC4_128", "MD5", code_TLS_DH_anon_WITH_RC4_128_MD5);
+
+ static CipherSuite TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
+ "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA", true,
+ KeyExchange_DH_anon_EXPORT, "DES40_CBC", "SHA",
+ code_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA);
+
+ static CipherSuite TLS_DH_anon_WITH_DES_CBC_SHA = new CipherSuite(
+ "TLS_DH_anon_WITH_DES_CBC_SHA", false, KeyExchange_DH_anon,
+ "DES_CBC", "SHA", code_TLS_DH_anon_WITH_DES_CBC_SHA);
+
+ static CipherSuite TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
+ "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA", false, KeyExchange_DH_anon,
+ "3DES_EDE_CBC", "SHA", code_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA);
+
+ // array for quick access to cipher suite by code
+ private static CipherSuite[] cuitesByCode = {
+ TLS_NULL_WITH_NULL_NULL,
+ TLS_RSA_WITH_NULL_MD5,
+ TLS_RSA_WITH_NULL_SHA,
+ TLS_RSA_EXPORT_WITH_RC4_40_MD5,
+ TLS_RSA_WITH_RC4_128_MD5,
+ TLS_RSA_WITH_RC4_128_SHA,
+ TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
+ TLS_RSA_WITH_IDEA_CBC_SHA,
+ TLS_RSA_EXPORT_WITH_DES40_CBC_SHA,
+ TLS_RSA_WITH_DES_CBC_SHA,
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA,
+ TLS_DH_DSS_WITH_DES_CBC_SHA,
+ TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA,
+ TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA,
+ TLS_DH_RSA_WITH_DES_CBC_SHA,
+ TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
+ TLS_DHE_DSS_WITH_DES_CBC_SHA,
+ TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+ TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
+ TLS_DHE_RSA_WITH_DES_CBC_SHA,
+ TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_DH_anon_EXPORT_WITH_RC4_40_MD5,
+ TLS_DH_anon_WITH_RC4_128_MD5,
+ TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA,
+ TLS_DH_anon_WITH_DES_CBC_SHA,
+ TLS_DH_anon_WITH_3DES_EDE_CBC_SHA
+ };
+
+ // hash for quick access to cipher suite by name
+ private static Hashtable cuitesByName;
+
+ /**
+ * array of supported sipher suites.
+ * Set of supported suites is defined at the moment provider's start
+ */
+// TODO Dinamical supported suites: new providers may be dynamically
+// added/removed and the set of supportes suites may be changed
+ static CipherSuite[] supportedCipherSuites;
+
+ /**
+ * array of supported sipher suites names
+ */
+ static String[] supportedCipherSuiteNames;
+
+ /**
+ * default sipher suites
+ */
+ static CipherSuite[] defaultCipherSuites;
+
+ static {
+ int count = 0;
+ cuitesByName = new Hashtable();
+ for (int i = 0; i < cuitesByCode.length; i++) {
+ cuitesByName.put(cuitesByCode[i].getName(), cuitesByCode[i]);
+ if (cuitesByCode[i].supported) {
+ count++;
+ }
+ }
+ supportedCipherSuites = new CipherSuite[count];
+ supportedCipherSuiteNames = new String[count];
+ count = 0;
+ for (int i = 0; i < cuitesByCode.length; i++) {
+ if (cuitesByCode[i].supported) {
+ supportedCipherSuites[count] = cuitesByCode[i];
+ supportedCipherSuiteNames[count] = supportedCipherSuites[count].getName();
+ count++;
+ }
+ }
+
+ CipherSuite[] defaultPretendent = {
+ TLS_RSA_WITH_RC4_128_MD5,
+ TLS_RSA_WITH_RC4_128_SHA,
+ // TLS_RSA_WITH_AES_128_CBC_SHA,
+ // TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+ // LS_DHE_DSS_WITH_AES_128_CBC_SHA,
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_RSA_WITH_DES_CBC_SHA,
+ TLS_DHE_RSA_WITH_DES_CBC_SHA, TLS_DHE_DSS_WITH_DES_CBC_SHA,
+ TLS_RSA_EXPORT_WITH_RC4_40_MD5,
+ TLS_RSA_EXPORT_WITH_DES40_CBC_SHA,
+ TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
+ TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
+ };
+ count = 0;
+ for (int i = 0; i < defaultPretendent.length; i++) {
+ if (defaultPretendent[i].supported) {
+ count++;
+ }
+ }
+ defaultCipherSuites = new CipherSuite[count];
+ count = 0;
+ for (int i = 0; i < defaultPretendent.length; i++) {
+ if (defaultPretendent[i].supported) {
+ defaultCipherSuites[count++] = defaultPretendent[i];
+ }
+ }
+ }
+
+ /**
+ * Returns CipherSuite by name
+ * @param name
+ * @return
+ */
+ public static CipherSuite getByName(String name) {
+ return (CipherSuite) cuitesByName.get(name);
+ }
+
+ /**
+ * Returns CipherSuite based on TLS CipherSuite code
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., A.5. The CipherSuite</a>
+ * @param b1
+ * @param b2
+ * @return
+ */
+ public static CipherSuite getByCode(byte b1, byte b2) {
+ if (b1 != 0 || b2 > cuitesByCode.length) {
+ // Unknoun
+ return new CipherSuite("UNKNOUN_" + b1 + "_" + b2, false, 0, "",
+ "", new byte[] { b1, b2 });
+ }
+ return cuitesByCode[b2];
+ }
+
+ /**
+ * Returns CipherSuite based on V2CipherSpec code
+ * as described in TLS 1.0 spec., E. Backward Compatibility With SSL
+ *
+ * @param b1
+ * @param b2
+ * @param b3
+ * @return CipherSuite
+ */
+ public static CipherSuite getByCode(byte b1, byte b2, byte b3) {
+ if (b1 == 0 && b2 == 0) {
+ if (b3 <= cuitesByCode.length) {
+ return cuitesByCode[b3];
+ }
+ }
+ // as TLSv1 equivalent of V2CipherSpec should be included in
+ // V2ClientHello, ignore V2CipherSpec
+ return new CipherSuite("UNKNOUN_" + b1 + "_" + b2 + "_" + b3, false, 0,
+ "", "", new byte[] { b1, b2, b3 });
+ }
+
+ /**
+ * Creates CipherSuite
+ * @param name
+ * @param isExportable
+ * @param keyExchange
+ * @param cipherName
+ * @param hash
+ * @param code
+ */
+ public CipherSuite(String name, boolean isExportable, int keyExchange,
+ String cipherName, String hash, byte[] code) {
+ this.name = name;
+ this.keyExchange = keyExchange;
+ this.isExportable = isExportable;
+ if (cipherName == null) {
+ this.cipherName = null;
+ keyMaterial = 0;
+ expandedKeyMaterial = 0;
+ effectiveKeyBytes = 0;
+ IVSize = 0;
+ blockSize = 0;
+ } else if ("IDEA_CBC".equals(cipherName)) {
+ this.cipherName = "IDEA/CBC/NoPadding";
+ keyMaterial = 16;
+ expandedKeyMaterial = 16;
+ effectiveKeyBytes = 16;
+ IVSize = 8;
+ blockSize = 8;
+ } else if ("RC2_CBC_40".equals(cipherName)) {
+ this.cipherName = "RC2/CBC/NoPadding";
+ keyMaterial = 5;
+ expandedKeyMaterial = 16;
+ effectiveKeyBytes = 5;
+ IVSize = 8;
+ blockSize = 8;
+ } else if ("RC4_40".equals(cipherName)) {
+ this.cipherName = "RC4";
+ keyMaterial = 5;
+ expandedKeyMaterial = 16;
+ effectiveKeyBytes = 5;
+ IVSize = 0;
+ blockSize = 0;
+ } else if ("RC4_128".equals(cipherName)) {
+ this.cipherName = "RC4";
+ keyMaterial = 16;
+ expandedKeyMaterial = 16;
+ effectiveKeyBytes = 16;
+ IVSize = 0;
+ blockSize = 0;
+ } else if ("DES40_CBC".equals(cipherName)) {
+ this.cipherName = "DES/CBC/NoPadding";
+ keyMaterial = 5;
+ expandedKeyMaterial = 8;
+ effectiveKeyBytes = 5;
+ IVSize = 8;
+ blockSize = 8;
+ } else if ("DES_CBC".equals(cipherName)) {
+ this.cipherName = "DES/CBC/NoPadding";
+ keyMaterial = 8;
+ expandedKeyMaterial = 8;
+ effectiveKeyBytes = 7;
+ IVSize = 8;
+ blockSize = 8;
+ } else if ("3DES_EDE_CBC".equals(cipherName)) {
+ this.cipherName = "DESede/CBC/NoPadding";
+ keyMaterial = 24;
+ expandedKeyMaterial = 24;
+ effectiveKeyBytes = 24;
+ IVSize = 8;
+ blockSize = 8;
+ } else {
+ this.cipherName = cipherName;
+ keyMaterial = 0;
+ expandedKeyMaterial = 0;
+ effectiveKeyBytes = 0;
+ IVSize = 0;
+ blockSize = 0;
+ }
+
+ if ("MD5".equals(hash)) {
+ this.hmacName = "HmacMD5";
+ this.hashName = "MD5";
+ hashSize = 16;
+ } else if ("SHA".equals(hash)) {
+ this.hmacName = "HmacSHA1";
+ this.hashName = "SHA-1";
+ hashSize = 20;
+ } else {
+ this.hmacName = null;
+ this.hashName = null;
+ hashSize = 0;
+ }
+
+ cipherSuiteCode = code;
+
+ if (this.cipherName != null) {
+ try {
+ Cipher.getInstance(this.cipherName);
+ } catch (GeneralSecurityException e) {
+ supported = false;
+ }
+ }
+
+ }
+
+ /**
+ * Returns true if cipher suite is anonymous
+ * @return
+ */
+ public boolean isAnonymous() {
+ if (keyExchange == KeyExchange_DH_anon
+ || keyExchange == KeyExchange_DH_anon_EXPORT) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns array of supported CipherSuites
+ * @return
+ */
+ public static CipherSuite[] getSupported() {
+ return supportedCipherSuites;
+ }
+
+ /**
+ * Returns array of supported cipher suites names
+ * @return
+ */
+ public static String[] getSupportedCipherSuiteNames() {
+ return (String[]) supportedCipherSuiteNames.clone();
+ }
+
+ /**
+ * Returns cipher suite name
+ * @return
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns cipher suite code as byte array
+ * @return
+ */
+ public byte[] toBytes() {
+ return cipherSuiteCode;
+ }
+
+ /**
+ * Returns cipher suite description
+ */
+ public String toString() {
+ return name + ": " + cipherSuiteCode[0] + " " + cipherSuiteCode[1];
+ }
+
+ /**
+ * Compares this cipher suite to the specified object.
+ */
+ public boolean equals(Object obj) {
+ if (obj instanceof CipherSuite
+ && this.cipherSuiteCode[0] == ((CipherSuite) obj).cipherSuiteCode[0]
+ && this.cipherSuiteCode[1] == ((CipherSuite) obj).cipherSuiteCode[1]) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns cipher algorithm name
+ * @return
+ */
+ public String getBulkEncryptionAlgorithm() {
+ return cipherName;
+ }
+
+ /**
+ * Returns cipher block size
+ * @return
+ */
+ public int getBlockSize() {
+ return blockSize;
+ }
+
+ /**
+ * Returns MAC algorithm name
+ * @return
+ */
+ public String getHmacName() {
+ return hmacName;
+ }
+
+ /**
+ * Returns hash algorithm name
+ * @return
+ */
+ public String getHashName() {
+ return hashName;
+ }
+
+ /**
+ * Returns hash size
+ * @return
+ */
+ public int getMACLength() {
+ return hashSize;
+ }
+
+ /**
+ * Indicates whether this cipher suite is exportable
+ * @return
+ */
+ public boolean isExportable() {
+ return isExportable;
+ }
+
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java
new file mode 100644
index 0000000..90ba0a9
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java
@@ -0,0 +1,649 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PrivilegedExceptionAction;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Enumeration;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.interfaces.DHKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPublicKeySpec;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.X509ExtendedKeyManager;
+
+/**
+ * Client side handshake protocol implementation.
+ * Handshake protocol operates on top of the Record Protocol.
+ * It is responsible for session negotiating.
+ *
+ * The implementation proceses inbound server handshake messages,
+ * creates and sends respond messages. Outbound messages are supplied
+ * to Record Protocol. Detected errors are reported to the Alert protocol.
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7. The
+ * TLS Handshake Protocol</a>
+ *
+ */
+public class ClientHandshakeImpl extends HandshakeProtocol {
+
+ /**
+ * Creates Client Handshake Implementation
+ *
+ * @param owner
+ */
+ ClientHandshakeImpl(Object owner) {
+ super(owner);
+ }
+
+ /**
+ * Starts handshake
+ *
+ */
+ public void start() {
+ if (session == null) { // initial handshake
+ session = findSessionToResume();
+ } else { // start session renegotiation
+ if (clientHello != null && this.status != FINISHED) {
+ // current negotiation has not completed
+ return; // ignore
+ }
+ if (!session.isValid()) {
+ session = null;
+ }
+ }
+ if (session != null) {
+ isResuming = true;
+ } else if (parameters.getEnableSessionCreation()){
+ isResuming = false;
+ session = new SSLSessionImpl(parameters.getSecureRandom());
+ session.protocol = ProtocolVersion.getLatestVersion(parameters
+ .getEnabledProtocols());
+ recordProtocol.setVersion(session.protocol.version);
+ } else {
+ fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "SSL Session may not be created ");
+ }
+ startSession();
+ }
+
+ /**
+ * Starts renegotiation on a new session
+ *
+ */
+ private void renegotiateNewSession() {
+ if (parameters.getEnableSessionCreation()){
+ isResuming = false;
+ session = new SSLSessionImpl(parameters.getSecureRandom());
+ session.protocol = ProtocolVersion.getLatestVersion(parameters
+ .getEnabledProtocols());
+ recordProtocol.setVersion(session.protocol.version);
+ startSession();
+ } else {
+ status = NOT_HANDSHAKING;
+ sendWarningAlert(AlertProtocol.NO_RENEGOTIATION);
+ }
+ }
+
+ /*
+ * Starts/resumes session
+ */
+ private void startSession() {
+ CipherSuite[] cipher_suites;
+ if (isResuming) {
+ cipher_suites = new CipherSuite[] { session.cipherSuite };
+ } else {
+ // BEGIN android-removed
+ // cipher_suites = parameters.enabledCipherSuites;
+ // END android-removed
+ // BEGIN android-added
+ cipher_suites = parameters.getEnabledCipherSuitesMember();
+ // END android-added
+ }
+ clientHello = new ClientHello(parameters.getSecureRandom(),
+ session.protocol.version, session.id, cipher_suites);
+ session.clientRandom = clientHello.random;
+ send(clientHello);
+ status = NEED_UNWRAP;
+ }
+
+ /**
+ * Proceses inbound handshake messages
+ * @param bytes
+ */
+ public void unwrap(byte[] bytes) {
+ if (this.delegatedTaskErr != null) {
+ Exception e = this.delegatedTaskErr;
+ this.delegatedTaskErr = null;
+ this.fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "Error in delegated task", e);
+ }
+ int handshakeType;
+ io_stream.append(bytes);
+ while (io_stream.available() > 0) {
+ io_stream.mark();
+ int length;
+ try {
+ handshakeType = io_stream.read();
+ length = io_stream.readUint24();
+ if (io_stream.available() < length) {
+ io_stream.reset();
+ return;
+ }
+ switch (handshakeType) {
+ case 0: // HELLO_REQUEST
+ // we don't need to take this message into account
+ // during FINISH message verification, so remove it
+ io_stream.removeFromMarkedPosition();
+ if (clientHello != null
+ && (clientFinished == null || serverFinished == null)) {
+ //currently negotiating - ignore
+ break;
+ }
+ // renegotiate
+ if (session.isValid()) {
+ session = (SSLSessionImpl) session.clone();
+ isResuming = true;
+ startSession();
+ } else {
+ // if SSLSession is invalidated (e.g. timeout limit is
+ // exceeded) connection can't resume the session.
+ renegotiateNewSession();
+ }
+ break;
+ case 2: // SERVER_HELLO
+ if (clientHello == null || serverHello != null) {
+ unexpectedMessage();
+ return;
+ }
+ serverHello = new ServerHello(io_stream, length);
+
+ //check protocol version
+ ProtocolVersion servProt = ProtocolVersion
+ .getByVersion(serverHello.server_version);
+ String[] enabled = parameters.getEnabledProtocols();
+ find: {
+ for (int i = 0; i < enabled.length; i++) {
+ if (servProt.equals(ProtocolVersion
+ .getByName(enabled[i]))) {
+ break find;
+ }
+ }
+ fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+ "Bad server hello protocol version");
+ }
+
+ // check compression method
+ if (serverHello.compression_method != 0) {
+ fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+ "Bad server hello compression method");
+ }
+
+ //check cipher_suite
+ // BEGIN android-removed
+ // CipherSuite[] enabledSuites = parameters.enabledCipherSuites;
+ // END android-removed
+ // BEGIN android-added
+ CipherSuite[] enabledSuites = parameters.getEnabledCipherSuitesMember();
+ // END android-added
+ find: {
+ for (int i = 0; i < enabledSuites.length; i++) {
+ if (serverHello.cipher_suite
+ .equals(enabledSuites[i])) {
+ break find;
+ }
+ }
+ fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+ "Bad server hello cipher suite");
+ }
+
+ if (isResuming) {
+ if (serverHello.session_id.length == 0) {
+ // server is not willing to establish the new connection
+ // using specified session
+ isResuming = false;
+ } else if (!Arrays.equals(serverHello.session_id, clientHello.session_id)) {
+ isResuming = false;
+ } else if (!session.protocol.equals(servProt)) {
+ fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+ "Bad server hello protocol version");
+ } else if (!session.cipherSuite
+ .equals(serverHello.cipher_suite)) {
+ fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+ "Bad server hello cipher suite");
+ }
+ if (serverHello.server_version[1] == 1) {
+ computerReferenceVerifyDataTLS("server finished");
+ } else {
+ computerReferenceVerifyDataSSLv3(SSLv3Constants.server);
+ }
+ }
+ session.protocol = servProt;
+ recordProtocol.setVersion(session.protocol.version);
+ session.cipherSuite = serverHello.cipher_suite;
+ session.id = (byte[]) serverHello.session_id.clone();
+ session.serverRandom = serverHello.random;
+ break;
+ case 11: // CERTIFICATE
+ if (serverHello == null || serverKeyExchange != null
+ || serverCert != null || isResuming) {
+ unexpectedMessage();
+ return;
+ }
+ serverCert = new CertificateMessage(io_stream, length);
+ break;
+ case 12: // SERVER_KEY_EXCHANGE
+ if (serverHello == null || serverKeyExchange != null
+ || isResuming) {
+ unexpectedMessage();
+ return;
+ }
+ serverKeyExchange = new ServerKeyExchange(io_stream,
+ length, session.cipherSuite.keyExchange);
+ break;
+ case 13: // CERTIFICATE_REQUEST
+ if (serverCert == null || certificateRequest != null
+ || session.cipherSuite.isAnonymous() || isResuming) {
+ unexpectedMessage();
+ return;
+ }
+ certificateRequest = new CertificateRequest(io_stream,
+ length);
+ break;
+ case 14: // SERVER_HELLO_DONE
+ if (serverHello == null || serverHelloDone != null
+ || isResuming) {
+ unexpectedMessage();
+ return;
+ }
+ serverHelloDone = new ServerHelloDone(io_stream, length);
+ if (this.nonBlocking) {
+ delegatedTasks.add(new DelegatedTask(
+ new PrivilegedExceptionAction(){
+ public Object run() throws Exception {
+ processServerHelloDone();
+ return null;
+ }
+ },
+ this,
+ AccessController.getContext()));
+ return;
+ }
+ processServerHelloDone();
+ break;
+ case 20: // FINISHED
+ if (!changeCipherSpecReceived) {
+ unexpectedMessage();
+ return;
+ }
+ serverFinished = new Finished(io_stream, length);
+ verifyFinished(serverFinished.getData());
+ session.lastAccessedTime = System.currentTimeMillis();
+ // BEGIN android-added
+ session.context = parameters.getClientSessionContext();
+ // END android-added
+ parameters.getClientSessionContext().putSession(session);
+ if (isResuming) {
+ sendChangeCipherSpec();
+ } else {
+ session.lastAccessedTime = System.currentTimeMillis();
+ status = FINISHED;
+ }
+ // XXX there is no cleanup work
+ break;
+ default:
+ unexpectedMessage();
+ return;
+ }
+ } catch (IOException e) {
+ // io stream dosn't contain complete handshake message
+ io_stream.reset();
+ return;
+ }
+ }
+
+ }
+
+ /**
+ * Processes SSLv2 Hello message.
+ * SSLv2 client hello message message is an unexpected message
+ * for client side of handshake protocol.
+ * @ see TLS 1.0 spec., E.1. Version 2 client hello
+ * @param bytes
+ */
+ public void unwrapSSLv2(byte[] bytes) {
+ unexpectedMessage();
+ }
+
+ /**
+ * Creates and sends Finished message
+ */
+ protected void makeFinished() {
+ byte[] verify_data;
+ if (serverHello.server_version[1] == 1) {
+ verify_data = new byte[12];
+ computerVerifyDataTLS("client finished", verify_data);
+ } else {
+ verify_data = new byte[36];
+ computerVerifyDataSSLv3(SSLv3Constants.client, verify_data);
+ }
+ clientFinished = new Finished(verify_data);
+ send(clientFinished);
+ if (isResuming) {
+ session.lastAccessedTime = System.currentTimeMillis();
+ status = FINISHED;
+ } else {
+ if (serverHello.server_version[1] == 1) {
+ computerReferenceVerifyDataTLS("server finished");
+ } else {
+ computerReferenceVerifyDataSSLv3(SSLv3Constants.server);
+ }
+ status = NEED_UNWRAP;
+ }
+ }
+
+ /**
+ * Processes ServerHelloDone: makes verification of the server messages; sends
+ * client messages, computers masterSecret, sends ChangeCipherSpec
+ */
+ void processServerHelloDone() {
+ PrivateKey clientKey = null;
+
+ if (serverCert != null) {
+ if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DH_anon
+ || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DH_anon_EXPORT) {
+ unexpectedMessage();
+ return;
+ }
+ verifyServerCert();
+ } else {
+ if (session.cipherSuite.keyExchange != CipherSuite.KeyExchange_DH_anon
+ && session.cipherSuite.keyExchange != CipherSuite.KeyExchange_DH_anon_EXPORT) {
+ unexpectedMessage();
+ return;
+ }
+ }
+
+ // Client certificate
+ if (certificateRequest != null) {
+ X509Certificate[] certs = null;
+ String clientAlias = ((X509ExtendedKeyManager) parameters
+ .getKeyManager()).chooseClientAlias(certificateRequest
+ .getTypesAsString(),
+ certificateRequest.certificate_authorities, null);
+ if (clientAlias != null) {
+ X509ExtendedKeyManager km = (X509ExtendedKeyManager) parameters
+ .getKeyManager();
+ certs = km.getCertificateChain((clientAlias));
+ clientKey = km.getPrivateKey(clientAlias);
+ }
+ session.localCertificates = certs;
+ clientCert = new CertificateMessage(certs);
+ send(clientCert);
+ }
+ // Client key exchange
+ if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_RSA
+ || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT) {
+ // RSA encrypted premaster secret message
+ Cipher c;
+ try {
+ c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ if (serverKeyExchange != null) {
+ c.init(Cipher.ENCRYPT_MODE, serverKeyExchange
+ .getRSAPublicKey());
+ } else {
+ c.init(Cipher.ENCRYPT_MODE, serverCert.certs[0]);
+ }
+ } catch (Exception e) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR,
+ "Unexpected exception", e);
+ return;
+ }
+ preMasterSecret = new byte[48];
+ parameters.getSecureRandom().nextBytes(preMasterSecret);
+ System.arraycopy(clientHello.client_version, 0, preMasterSecret, 0,
+ 2);
+ try {
+ clientKeyExchange = new ClientKeyExchange(c
+ .doFinal(preMasterSecret),
+ serverHello.server_version[1] == 1);
+ } catch (Exception e) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR,
+ "Unexpected exception", e);
+ return;
+ }
+ } else {
+ PublicKey serverPublic;
+ KeyAgreement agreement = null;
+ DHParameterSpec spec;
+ try {
+ KeyFactory kf = null;
+ try {
+ kf = KeyFactory.getInstance("DH");
+ } catch (NoSuchAlgorithmException e) {
+ kf = KeyFactory.getInstance("DiffieHellman");
+ }
+
+ try {
+ agreement = KeyAgreement.getInstance("DH");
+ } catch (NoSuchAlgorithmException ee) {
+ agreement = KeyAgreement.getInstance("DiffieHellman");
+ }
+
+ KeyPairGenerator kpg = null;
+ try {
+ kpg = KeyPairGenerator.getInstance("DH");
+ } catch (NoSuchAlgorithmException e) {
+ kpg = KeyPairGenerator.getInstance("DiffieHellman");
+ }
+ if (serverKeyExchange != null) {
+ serverPublic = kf.generatePublic(new DHPublicKeySpec(
+ serverKeyExchange.par3, serverKeyExchange.par1,
+ serverKeyExchange.par2));
+ spec = new DHParameterSpec(serverKeyExchange.par1,
+ serverKeyExchange.par2);
+ } else {
+ serverPublic = serverCert.certs[0].getPublicKey();
+ spec = ((DHPublicKey) serverPublic).getParams();
+ }
+ kpg.initialize(spec);
+
+ KeyPair kp = kpg.generateKeyPair();
+ Key key = kp.getPublic();
+ if (clientCert != null
+ && serverCert != null
+ && (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_RSA
+ || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_DSS)) {
+ PublicKey client_pk = clientCert.certs[0].getPublicKey();
+ PublicKey server_pk = serverCert.certs[0].getPublicKey();
+ if (client_pk instanceof DHKey
+ && server_pk instanceof DHKey) {
+ if (((DHKey) client_pk).getParams().getG().equals(
+ ((DHKey) server_pk).getParams().getG())
+ && ((DHKey) client_pk).getParams().getP()
+ .equals(((DHKey) server_pk).getParams().getG())) {
+ // client cert message DH public key parameters
+ // matched those specified by the
+ // server in its certificate,
+ clientKeyExchange = new ClientKeyExchange(); // empty
+ }
+ }
+ } else {
+ clientKeyExchange = new ClientKeyExchange(
+ ((DHPublicKey) key).getY());
+ }
+ key = kp.getPrivate();
+ agreement.init(key);
+ agreement.doPhase(serverPublic, true);
+ preMasterSecret = agreement.generateSecret();
+ } catch (Exception e) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR,
+ "Unexpected exception", e);
+ return;
+ }
+ }
+ if (clientKeyExchange != null) {
+ send(clientKeyExchange);
+ }
+
+ computerMasterSecret();
+
+ // send certificate verify for all certificates except those containing
+ // fixed DH parameters
+ if (clientCert != null && !clientKeyExchange.isEmpty()) {
+ // Certificate verify
+ DigitalSignature ds = new DigitalSignature(
+ session.cipherSuite.keyExchange);
+ ds.init(clientKey);
+
+ if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT
+ || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_RSA
+ || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_RSA
+ || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_RSA_EXPORT) {
+ ds.setMD5(io_stream.getDigestMD5());
+ ds.setSHA(io_stream.getDigestSHA());
+ } else if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_DSS
+ || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_DSS_EXPORT) {
+ ds.setSHA(io_stream.getDigestSHA());
+ // The Signature should be empty in case of anonimous signature algorithm:
+ // } else if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DH_anon ||
+ // session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DH_anon_EXPORT) {
+ }
+ certificateVerify = new CertificateVerify(ds.sign());
+ send(certificateVerify);
+ }
+
+ sendChangeCipherSpec();
+ }
+
+ /*
+ * Verifies certificate path
+ */
+ private void verifyServerCert() {
+ String authType = null;
+ switch (session.cipherSuite.keyExchange) {
+ case 1: // KeyExchange_RSA
+ authType = "RSA";
+ break;
+ case 2: // KeyExchange_RSA_EXPORT
+ if (serverKeyExchange != null ) {
+ // ephemeral RSA key is used
+ authType = "RSA_EXPORT";
+ } else {
+ authType = "RSA";
+ }
+ break;
+ case 3: // KeyExchange_DHE_DSS
+ case 4: // KeyExchange_DHE_DSS_EXPORT
+ authType = "DHE_DSS";
+ break;
+ case 5: // KeyExchange_DHE_RSA
+ case 6: // KeyExchange_DHE_RSA_EXPORT
+ authType = "DHE_RSA";
+ break;
+ case 7: // KeyExchange_DH_DSS
+ case 11: // KeyExchange_DH_DSS_EXPORT
+ authType = "DH_DSS";
+ break;
+ case 8: // KeyExchange_DH_RSA
+ case 12: // KeyExchange_DH_RSA_EXPORT
+ authType = "DH_RSA";
+ break;
+ case 9: // KeyExchange_DH_anon
+ case 10: // KeyExchange_DH_anon_EXPORT
+ return;
+ }
+ try {
+ parameters.getTrustManager().checkServerTrusted(serverCert.certs,
+ authType);
+ } catch (CertificateException e) {
+ fatalAlert(AlertProtocol.BAD_CERTIFICATE, "Not trusted server certificate", e);
+ return;
+ }
+ session.peerCertificates = serverCert.certs;
+ }
+
+ /**
+ * Proceses ChangeCipherSpec message
+ */
+ public void receiveChangeCipherSpec() {
+ if (isResuming) {
+ if (serverHello == null) {
+ unexpectedMessage();
+ }
+ } else if (clientFinished == null) {
+ unexpectedMessage();
+ }
+ changeCipherSpecReceived = true;
+ }
+
+ // Find session to resume in client session context
+ private SSLSessionImpl findSessionToResume() {
+ // BEGIN android-removed
+ // String host;
+ // int port;
+ // END android-removed
+ // BEGIN android-added
+ String host = null;
+ int port = -1;
+ // END android-added
+ if (engineOwner != null) {
+ host = engineOwner.getPeerHost();
+ port = engineOwner.getPeerPort();
+ // BEGIN android-removed
+ // } else {
+ // host = socketOwner.getInetAddress().getHostName();
+ // port = socketOwner.getPort();
+ // END android-removed
+ }
+ if (host == null || port == -1) {
+ return null; // starts new session
+ }
+
+ byte[] id;
+ SSLSession ses;
+ SSLSessionContext context = parameters.getClientSessionContext();
+ for (Enumeration en = context.getIds(); en.hasMoreElements();) {
+ id = (byte[])en.nextElement();
+ ses = context.getSession(id);
+ if (host.equals(ses.getPeerHost()) && port == ses.getPeerPort()) {
+ return (SSLSessionImpl)((SSLSessionImpl)ses).clone(); // resume
+ }
+ }
+ return null; // starts new session
+ }
+
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java
new file mode 100644
index 0000000..aa811fb
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris Kuznetsov
+* @version $Revision$
+*/
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+/**
+ *
+ * Represents Client Hello message
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.1.2.
+ * Client hello</a>
+ *
+ */
+public class ClientHello extends Message {
+
+ /**
+ * Client version
+ */
+ final byte[] client_version;
+
+ /**
+ * Random bytes
+ */
+ final byte[] random = new byte[32];
+
+ /**
+ * Session id
+ */
+ final byte[] session_id;
+
+ /**
+ * Cipher suites supported by the client
+ */
+ final CipherSuite[] cipher_suites;
+
+ /**
+ * Compression methods supported by the client
+ */
+ final byte[] compression_methods;
+
+ /**
+ * Creates outbound message
+ * @param sr
+ * @param version
+ * @param ses_id
+ * @param cipher_suite
+ */
+ public ClientHello(SecureRandom sr, byte[] version, byte[] ses_id,
+ CipherSuite[] cipher_suite) {
+ client_version = version;
+ long gmt_unix_time = System.currentTimeMillis()/1000;
+ sr.nextBytes(random);
+ random[0] = (byte) (gmt_unix_time & 0xFF000000 >>> 24);
+ random[1] = (byte) (gmt_unix_time & 0xFF0000 >>> 16);
+ random[2] = (byte) (gmt_unix_time & 0xFF00 >>> 8);
+ random[3] = (byte) (gmt_unix_time & 0xFF);
+ session_id = ses_id;
+ this.cipher_suites = cipher_suite;
+ compression_methods = new byte[] { 0 }; // CompressionMethod.null
+ length = 38 + session_id.length + (this.cipher_suites.length << 1)
+ + compression_methods.length;
+ }
+
+ /**
+ * Creates inbound message
+ * @param in
+ * @param length
+ * @throws IOException
+ */
+ public ClientHello(HandshakeIODataStream in, int length) throws IOException {
+ client_version = new byte[2];
+ client_version[0] = (byte) in.readUint8();
+ client_version[1] = (byte) in.readUint8();
+ in.read(random, 0, 32);
+ int size = in.read();
+ session_id = new byte[size];
+ in.read(session_id, 0, size);
+ int l = in.readUint16();
+ if ((l & 0x01) == 0x01) { // cipher suites length must be an even number
+ fatalAlert(AlertProtocol.DECODE_ERROR,
+ "DECODE ERROR: incorrect ClientHello");
+ }
+ size = l >> 1;
+ cipher_suites = new CipherSuite[size];
+ for (int i = 0; i < size; i++) {
+ byte b0 = (byte) in.read();
+ byte b1 = (byte) in.read();
+ cipher_suites[i] = CipherSuite.getByCode(b0, b1);
+ }
+ size = in.read();
+ compression_methods = new byte[size];
+ in.read(compression_methods, 0, size);
+ this.length = 38 + session_id.length + (cipher_suites.length << 1)
+ + compression_methods.length;
+ if (this.length > length) {
+ fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect ClientHello");
+ }
+ // for forward compatibility, extra data is permitted;
+ // must be ignored
+ if (this.length < length) {
+ in.skip(length - this.length);
+ this.length = length;
+ }
+ }
+ /**
+ * Parse V2ClientHello
+ * @param in
+ * @throws IOException
+ */
+ public ClientHello(HandshakeIODataStream in) throws IOException {
+ if (in.readUint8() != 1) {
+ fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect V2ClientHello");
+ }
+ client_version = new byte[2];
+ client_version[0] = (byte) in.readUint8();
+ client_version[1] = (byte) in.readUint8();
+ int cipher_spec_length = in.readUint16();
+ if (in.readUint16() != 0) { // session_id_length
+ // as client already knows the protocol known to a server it should
+ // initiate the connection in that native protocol
+ fatalAlert(AlertProtocol.DECODE_ERROR,
+ "DECODE ERROR: incorrect V2ClientHello, cannot be used for resuming");
+ }
+ int challenge_length = in.readUint16();
+ if (challenge_length < 16) {
+ fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect V2ClientHello, short challenge data");
+ }
+ session_id = new byte[0];
+ cipher_suites = new CipherSuite[cipher_spec_length/3];
+ for (int i = 0; i < cipher_suites.length; i++) {
+ byte b0 = (byte) in.read();
+ byte b1 = (byte) in.read();
+ byte b2 = (byte) in.read();
+ cipher_suites[i] = CipherSuite.getByCode(b0, b1, b2);
+ }
+ compression_methods = new byte[] { 0 }; // CompressionMethod.null
+
+ if (challenge_length < 32) {
+ Arrays.fill(random, 0, 32 - challenge_length, (byte)0);
+ System.arraycopy(in.read(challenge_length), 0, random, 32 - challenge_length, challenge_length);
+ } else if (challenge_length == 32) {
+ System.arraycopy(in.read(32), 0, random, 0, 32);
+ } else {
+ System.arraycopy(in.read(challenge_length), challenge_length - 32, random, 0, 32);
+ }
+ if (in.available() > 0) {
+ fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect V2ClientHello, extra data");
+ }
+ this.length = 38 + session_id.length + (cipher_suites.length << 1)
+ + compression_methods.length;
+ }
+
+ /**
+ * Sends message
+ * @param out
+ */
+ public void send(HandshakeIODataStream out) {
+ out.write(client_version);
+ out.write(random);
+ out.writeUint8(session_id.length);
+ out.write(session_id);
+ int size = cipher_suites.length << 1;
+ out.writeUint16(size);
+ for (int i = 0; i < cipher_suites.length; i++) {
+ out.write(cipher_suites[i].toBytes());
+ }
+ out.writeUint8(compression_methods.length);
+ for (int i = 0; i < compression_methods.length; i++) {
+ out.write(compression_methods[i]);
+ }
+ }
+
+ /**
+ * Returns client random
+ * @return client random
+ */
+ public byte[] getRandom() {
+ return random;
+ }
+
+ /**
+ * Returns message type
+ * @return
+ */
+ public int getType() {
+ return Handshake.CLIENT_HELLO;
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java
new file mode 100644
index 0000000..a208456
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris Kuznetsov
+* @version $Revision$
+*/
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.Message;
+import org.apache.harmony.xnet.provider.jsse.Handshake;
+import org.apache.harmony.xnet.provider.jsse.HandshakeIODataStream;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+/**
+ *
+ * Represents client key exchange message
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.7.
+ * Client key exchange message</a>
+ *
+ */
+public class ClientKeyExchange extends Message {
+
+ /**
+ * Exchange keys
+ */
+ final byte[] exchange_keys;
+
+ /**
+ * Equals true if TLS1.0 protocol is used
+ */
+ boolean isTLS;
+
+ /**
+ * Equals true if key exchange algorithm is RSA
+ */
+ final boolean isRSA;
+
+ /**
+ * Creates outbound message
+ * @param encrypted_pre_master_secret
+ * @param isTLS
+ */
+ public ClientKeyExchange(byte[] encrypted_pre_master_secret, boolean isTLS) {
+ this.exchange_keys = encrypted_pre_master_secret;
+ length = this.exchange_keys.length;
+ if (isTLS) {
+ length += 2;
+ }
+ this.isTLS = isTLS;
+ isRSA = true;
+ }
+
+ /**
+ * Creates outbound message
+ * @param dh_Yc
+ */
+ public ClientKeyExchange(BigInteger dh_Yc) {
+ byte[] bb = dh_Yc.toByteArray();
+ if (bb[0] == 0) {
+ exchange_keys = new byte[bb.length-1];
+ System.arraycopy(bb, 1, exchange_keys, 0, exchange_keys.length);
+ } else {
+ exchange_keys = bb;
+ }
+ length = exchange_keys.length +2;
+ isRSA = false;
+ }
+
+ /**
+ * Creates empty message
+ *
+ */
+ public ClientKeyExchange() {
+ exchange_keys = new byte[0];
+ length = 0;
+ isRSA = false;
+ }
+
+ /**
+ * Creates inbound message
+ * @param length
+ * @param isTLS
+ * @param isRSA
+ * @throws IOException
+ */
+ public ClientKeyExchange(HandshakeIODataStream in, int length, boolean isTLS, boolean isRSA)
+ throws IOException {
+ this.isTLS = isTLS;
+ this.isRSA = isRSA;
+ if (length == 0) {
+ this.length = 0;
+ exchange_keys = new byte[0];
+ } else {
+ int size;
+ if (isRSA && !isTLS) {// SSL3.0 RSA
+ size = length;
+ this.length = size;
+ } else { // DH or TLSv1 RSA
+ size = in.readUint16();
+ this.length = 2 + size;
+ }
+ exchange_keys = new byte[size];
+ in.read(exchange_keys, 0, size);
+ if (this.length != length) {
+ fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect ClientKeyExchange");
+ }
+ }
+ }
+
+ /**
+ * Sends message
+ * @param out
+ */
+ public void send(HandshakeIODataStream out) {
+ if (exchange_keys.length != 0) {
+ if (!isRSA || isTLS) {// DH or TLSv1 RSA
+ out.writeUint16(exchange_keys.length);
+ }
+ out.write(exchange_keys);
+ }
+ }
+
+ /**
+ * Returns message type
+ * @return
+ */
+ public int getType() {
+ return Handshake.CLIENT_KEY_EXCHANGE;
+ }
+
+ /**
+ * Returns true if the message is empty (in case of implicit DH Yc)
+ * @return
+ */
+ public boolean isEmpty() {
+ return (exchange_keys.length == 0);
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionState.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionState.java
new file mode 100644
index 0000000..63bad5d
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionState.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.Logger;
+
+import javax.crypto.Cipher;
+
+/**
+ * This abstract class is a base for Record Protocol operating environmet
+ * of different SSL protocol versions.
+ */
+public abstract class ConnectionState {
+
+ /**
+ * The cipher used for encode operations
+ */
+ protected Cipher encCipher;
+
+ /**
+ * The cipher used for decode operations
+ */
+ protected Cipher decCipher;
+
+ /**
+ * The cipher type
+ */
+ protected boolean is_block_cipher;
+
+ /**
+ * The size of MAC used under this connection state
+ */
+ protected int hash_size;
+
+ /**
+ * Write sequence number which is incremented after each
+ * encrypt call
+ */
+ protected final byte[] write_seq_num = {0, 0, 0, 0, 0, 0, 0, 0};
+
+ /**
+ * Read sequence number which is incremented after each
+ * decrypt call
+ */
+ protected final byte[] read_seq_num = {0, 0, 0, 0, 0, 0, 0, 0};
+
+ protected Logger.Stream logger = Logger.getStream("conn_state");
+
+ /**
+ * Returns the minimal possible size of the
+ * Generic[Stream|Generic]Cipher structure under this
+ * connection state.
+ */
+ protected int getMinFragmentSize() {
+ // block ciphers return value with padding included
+ return encCipher.getOutputSize(1+hash_size); // 1 byte for data
+ }
+
+ /**
+ * Returns the size of the Generic[Stream|Generic]Cipher structure
+ * corresponding to the content data of specified size.
+ */
+ protected int getFragmentSize(int content_size) {
+ return encCipher.getOutputSize(content_size+hash_size);
+ }
+
+ /**
+ * Returns the minimal upper bound of the content size enclosed
+ * into the Generic[Stream|Generic]Cipher structure of specified size.
+ * For stream ciphers the returned value will be exact value.
+ */
+ protected int getContentSize(int generic_cipher_size) {
+ //it does not take the padding of block ciphered structures
+ //into account (so returned value can be greater than actual)
+ return decCipher.getOutputSize(generic_cipher_size)-hash_size;
+ }
+
+ /**
+ * Creates the GenericStreamCipher or GenericBlockCipher
+ * data structure for specified data of specified type.
+ * @param type - the ContentType of the provided data
+ * @param fragment - the byte array containing the
+ * data to be encrypted under the current connection state.
+ */
+ protected byte[] encrypt(byte type, byte[] fragment) {
+ return encrypt(type, fragment, 0, fragment.length);
+ }
+
+ /**
+ * Creates the GenericStreamCipher or GenericBlockCipher
+ * data structure for specified data of specified type.
+ * @param type - the ContentType of the provided data
+ * @param fragment - the byte array containing the
+ * data to be encrypted under the current connection state.
+ * @param offset - the offset from which the data begins with.
+ * @param len - the length of the data.
+ */
+ protected abstract byte[] encrypt
+ (byte type, byte[] fragment, int offset, int len);
+
+ /**
+ * Retrieves the fragment of the Plaintext structure of
+ * the specified type from the provided data.
+ * @param type - the ContentType of the data to be decrypted.
+ * @param fragment - the byte array containing the
+ * data to be encrypted under the current connection state.
+ */
+ protected byte[] decrypt(byte type, byte[] fragment) {
+ return decrypt(type, fragment, 0, fragment.length);
+ }
+
+ /**
+ * Retrieves the fragment of the Plaintext structure of
+ * the specified type from the provided data.
+ * @param type - the ContentType of the data to be decrypted.
+ * @param fragment - the byte array containing the
+ * data to be encrypted under the current connection state.
+ * @param offset - the offset from which the data begins with.
+ * @param len - the length of the data.
+ */
+ protected abstract byte[] decrypt
+ (byte type, byte[] fragment, int offset, int len);
+
+ /**
+ * Increments the sequence number.
+ */
+ protected static void incSequenceNumber(byte[] seq_num) {
+ int octet = 7;
+ while (octet >= 0) {
+ seq_num[octet] ++;
+ if (seq_num[octet] == 0) {
+ // characteristic overflow, so
+ // carrying a number in adding
+ octet --;
+ } else {
+ return;
+ }
+ }
+ }
+
+ /**
+ * Shutdownes the protocol. It will be impossiblke to use the instance
+ * after the calling of this method.
+ */
+ protected void shutdown() {
+ encCipher = null;
+ decCipher = null;
+ for (int i=0; i<write_seq_num.length; i++) {
+ write_seq_num[i] = 0;
+ read_seq_num[i] = 0;
+ }
+ }
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateSSLv3.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateSSLv3.java
new file mode 100644
index 0000000..078bf58
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateSSLv3.java
@@ -0,0 +1,356 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.SSLProtocolException;
+
+/**
+ * This class incapsulates the operating environment of the SSL v3
+ * (http://wp.netscape.com/eng/ssl3) Record Protocol and provides
+ * relating encryption/decryption functionality.
+ * The work functionality is based on the security
+ * parameters negotiated during the handshake.
+ */
+public class ConnectionStateSSLv3 extends ConnectionState {
+
+ // digest to create and check the message integrity info
+ private final MessageDigest messageDigest;
+ private final byte[] mac_write_secret;
+ private final byte[] mac_read_secret;
+
+ // paddings
+ private final byte[] pad_1;
+ private final byte[] pad_2;
+ // array will hold the part of the MAC material:
+ // length of 3 == 1(SSLCompressed.type) + 2(SSLCompressed.length)
+ // (more on SSLv3 MAC computation and payload protection see
+ // SSL v3 specification, p. 5.2.3)
+ private final byte[] mac_material_part = new byte[3];
+
+ /**
+ * Creates the instance of SSL v3 Connection State. All of the
+ * security parameters are provided by session object.
+ * @param session: the sessin object which incapsulates
+ * all of the security parameters established by handshake protocol.
+ * The key calculation for the state is done according
+ * to the SSL v3 Protocol specification.
+ * (http://www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt)
+ */
+ protected ConnectionStateSSLv3(SSLSessionImpl session) {
+ try {
+ CipherSuite cipherSuite = session.cipherSuite;
+
+ boolean is_exportabe = cipherSuite.isExportable();
+ hash_size = cipherSuite.getMACLength();
+ int key_size = (is_exportabe)
+ ? cipherSuite.keyMaterial
+ : cipherSuite.expandedKeyMaterial;
+ int iv_size = cipherSuite.getBlockSize();
+
+ String algName = cipherSuite.getBulkEncryptionAlgorithm();
+ String hashName = cipherSuite.getHashName();
+ if (logger != null) {
+ logger.println("ConnectionStateSSLv3.create:");
+ logger.println(" cipher suite name: "
+ + session.getCipherSuite());
+ logger.println(" encryption alg name: " + algName);
+ logger.println(" hash alg name: " + hashName);
+ logger.println(" hash size: " + hash_size);
+ logger.println(" block size: " + iv_size);
+ logger.println(" IV size (== block size):" + iv_size);
+ logger.println(" key size: " + key_size);
+ }
+
+ byte[] clientRandom = session.clientRandom;
+ byte[] serverRandom = session.serverRandom;
+ // so we need PRF value of size of
+ // 2*hash_size + 2*key_size + 2*iv_size
+ byte[] key_block = new byte[2*hash_size + 2*key_size + 2*iv_size];
+ byte[] seed = new byte[clientRandom.length + serverRandom.length];
+ System.arraycopy(serverRandom, 0, seed, 0, serverRandom.length);
+ System.arraycopy(clientRandom, 0, seed, serverRandom.length,
+ clientRandom.length);
+
+ PRF.computePRF_SSLv3(key_block, session.master_secret, seed);
+
+ byte[] client_mac_secret = new byte[hash_size];
+ byte[] server_mac_secret = new byte[hash_size];
+ byte[] client_key = new byte[key_size];
+ byte[] server_key = new byte[key_size];
+
+ boolean is_client = !session.isServer;
+
+ is_block_cipher = (iv_size > 0);
+
+ System.arraycopy(key_block, 0, client_mac_secret, 0, hash_size);
+ System.arraycopy(key_block, hash_size,
+ server_mac_secret, 0, hash_size);
+ System.arraycopy(key_block, 2*hash_size, client_key, 0, key_size);
+ System.arraycopy(key_block, 2*hash_size+key_size,
+ server_key, 0, key_size);
+
+ IvParameterSpec clientIV = null;
+ IvParameterSpec serverIV = null;
+
+ if (is_exportabe) {
+ if (logger != null) {
+ logger.println("ConnectionStateSSLv3: is_exportable");
+ }
+
+ MessageDigest md5 = MessageDigest.getInstance("MD5");
+ md5.update(client_key);
+ md5.update(clientRandom);
+ md5.update(serverRandom);
+ client_key = md5.digest();
+
+ md5.update(server_key);
+ md5.update(serverRandom);
+ md5.update(clientRandom);
+ server_key = md5.digest();
+
+ key_size = cipherSuite.expandedKeyMaterial;
+
+ if (is_block_cipher) {
+ md5.update(clientRandom);
+ md5.update(serverRandom);
+ clientIV = new IvParameterSpec(md5.digest(), 0, iv_size);
+ md5.update(serverRandom);
+ md5.update(clientRandom);
+ serverIV = new IvParameterSpec(md5.digest(), 0, iv_size);
+ }
+ } else if (is_block_cipher) {
+ clientIV = new IvParameterSpec(key_block,
+ 2*hash_size+2*key_size, iv_size);
+ serverIV = new IvParameterSpec(key_block,
+ 2*hash_size+2*key_size+iv_size, iv_size);
+ }
+
+ if (logger != null) {
+ logger.println("is exportable: "+is_exportabe);
+ logger.println("master_secret");
+ logger.print(session.master_secret);
+ logger.println("client_random");
+ logger.print(clientRandom);
+ logger.println("server_random");
+ logger.print(serverRandom);
+ //logger.println("key_block");
+ //logger.print(key_block);
+ logger.println("client_mac_secret");
+ logger.print(client_mac_secret);
+ logger.println("server_mac_secret");
+ logger.print(server_mac_secret);
+ logger.println("client_key");
+ logger.print(client_key, 0, key_size);
+ logger.println("server_key");
+ logger.print(server_key, 0, key_size);
+ if (clientIV != null) {
+ logger.println("client_iv");
+ logger.print(clientIV.getIV());
+ logger.println("server_iv");
+ logger.print(serverIV.getIV());
+ } else {
+ logger.println("no IV.");
+ }
+ }
+ encCipher = Cipher.getInstance(algName);
+ decCipher = Cipher.getInstance(algName);
+ messageDigest = MessageDigest.getInstance(hashName);
+ if (is_client) { // client side
+ encCipher.init(Cipher.ENCRYPT_MODE,
+ new SecretKeySpec(client_key, 0, key_size, algName),
+ clientIV);
+ decCipher.init(Cipher.DECRYPT_MODE,
+ new SecretKeySpec(server_key, 0, key_size, algName),
+ serverIV);
+ mac_write_secret = client_mac_secret;
+ mac_read_secret = server_mac_secret;
+ } else { // server side
+ encCipher.init(Cipher.ENCRYPT_MODE,
+ new SecretKeySpec(server_key, 0, key_size, algName),
+ serverIV);
+ decCipher.init(Cipher.DECRYPT_MODE,
+ new SecretKeySpec(client_key, 0, key_size, algName),
+ clientIV);
+ mac_write_secret = server_mac_secret;
+ mac_read_secret = client_mac_secret;
+ }
+ if (hashName.equals("MD5")) {
+ pad_1 = SSLv3Constants.MD5pad1;
+ pad_2 = SSLv3Constants.MD5pad2;
+ } else {
+ pad_1 = SSLv3Constants.SHApad1;
+ pad_2 = SSLv3Constants.SHApad2;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new AlertException(AlertProtocol.INTERNAL_ERROR,
+ new SSLProtocolException(
+ "Error during computation of security parameters"));
+ }
+ }
+
+ /**
+ * Creates the GenericStreamCipher or GenericBlockCipher
+ * data structure for specified data of specified type.
+ * @throws org.apache.harmony.xnet.provider.jsse.AlertException if alert was occured.
+ */
+ protected byte[] encrypt(byte type, byte[] fragment, int offset, int len) {
+ try {
+ int content_mac_length = len + hash_size;
+ int padding_length = is_block_cipher
+ ? padding_length =
+ ((8 - (++content_mac_length & 0x07)) & 0x07)
+ : 0;
+ byte[] res = new byte[content_mac_length + padding_length];
+ System.arraycopy(fragment, offset, res, 0, len);
+
+ mac_material_part[0] = type;
+ mac_material_part[1] = (byte) ((0x00FF00 & len) >> 8);
+ mac_material_part[2] = (byte) (0x0000FF & len);
+
+ messageDigest.update(mac_write_secret);
+ messageDigest.update(pad_1);
+ messageDigest.update(write_seq_num);
+ messageDigest.update(mac_material_part);
+ messageDigest.update(fragment, offset, len);
+ byte[] digest = messageDigest.digest();
+ messageDigest.update(mac_write_secret);
+ messageDigest.update(pad_2);
+ messageDigest.update(digest);
+ digest = messageDigest.digest();
+ System.arraycopy(digest, 0, res, len, hash_size);
+
+ //if (logger != null) {
+ // logger.println("MAC Material:");
+ // logger.print(write_seq_num);
+ // logger.print(mac_material_header);
+ // logger.print(fragment, offset, len);
+ //}
+
+ if (is_block_cipher) {
+ // do padding:
+ Arrays.fill(res, content_mac_length-1,
+ res.length, (byte) (padding_length));
+ }
+ if (logger != null) {
+ logger.println("SSLRecordProtocol.encrypt: "
+ + (is_block_cipher
+ ? "GenericBlockCipher with padding["
+ +padding_length+"]:"
+ : "GenericStreamCipher:"));
+ logger.print(res);
+ }
+ byte[] rez = new byte[encCipher.getOutputSize(res.length)];
+ encCipher.update(res, 0, res.length, rez);
+ incSequenceNumber(write_seq_num);
+ return rez;
+ } catch (GeneralSecurityException e) {
+ e.printStackTrace();
+ throw new AlertException(AlertProtocol.INTERNAL_ERROR,
+ new SSLProtocolException("Error during the encryption"));
+ }
+ }
+
+ /**
+ * Retrieves the fragment of the Plaintext structure of
+ * the specified type from the provided data.
+ * @throws AlertException if alert was occured.
+ */
+ protected byte[] decrypt(byte type, byte[] fragment,
+ int offset, int len) {
+ // plain data of the Generic[Stream|Block]Cipher structure
+ byte[] data = decCipher.update(fragment, offset, len);
+ // the 'content' part of the structure
+ byte[] content;
+ if (is_block_cipher) {
+ // check padding
+ int padding_length = data[data.length-1];
+ for (int i=0; i<padding_length; i++) {
+ if (data[data.length-2-i] != padding_length) {
+ throw new AlertException(
+ AlertProtocol.DECRYPTION_FAILED,
+ new SSLProtocolException(
+ "Received message has bad padding"));
+ }
+ }
+ content = new byte[data.length - hash_size - padding_length - 1];
+ } else {
+ content = new byte[data.length - hash_size];
+ }
+
+ byte[] mac_value;
+
+ mac_material_part[0] = type;
+ mac_material_part[1] = (byte) ((0x00FF00 & content.length) >> 8);
+ mac_material_part[2] = (byte) (0x0000FF & content.length);
+
+ messageDigest.update(mac_read_secret);
+ messageDigest.update(pad_1);
+ messageDigest.update(read_seq_num);
+ messageDigest.update(mac_material_part);
+ messageDigest.update(data, 0, content.length);
+ mac_value = messageDigest.digest();
+ messageDigest.update(mac_read_secret);
+ messageDigest.update(pad_2);
+ messageDigest.update(mac_value);
+ mac_value = messageDigest.digest();
+
+ if (logger != null) {
+ logger.println("Decrypted:");
+ logger.print(data);
+ //logger.println("MAC Material:");
+ //logger.print(read_seq_num);
+ //logger.print(mac_material_header);
+ //logger.print(data, 0, content.length);
+ logger.println("Expected mac value:");
+ logger.print(mac_value);
+ }
+ // checking the mac value
+ for (int i=0; i<hash_size; i++) {
+ if (mac_value[i] != data[i+content.length]) {
+ throw new AlertException(AlertProtocol.BAD_RECORD_MAC,
+ new SSLProtocolException("Bad record MAC"));
+ }
+ }
+ System.arraycopy(data, 0, content, 0, content.length);
+ incSequenceNumber(read_seq_num);
+ return content;
+ }
+
+ /**
+ * Shutdownes the protocol. It will be impossiblke to use the instance
+ * after the calling of this method.
+ */
+ protected void shutdown() {
+ Arrays.fill(mac_write_secret, (byte) 0);
+ Arrays.fill(mac_read_secret, (byte) 0);
+ super.shutdown();
+ }
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateTLS.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateTLS.java
new file mode 100644
index 0000000..1b21b17
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateTLS.java
@@ -0,0 +1,355 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.AlertException;
+import org.apache.harmony.xnet.provider.jsse.SSLSessionImpl;
+import org.apache.harmony.xnet.provider.jsse.PRF;
+import org.apache.harmony.xnet.provider.jsse.ConnectionState;
+
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.SSLProtocolException;
+
+/**
+ * This class incapsulates the operating environment of the TLS v1
+ * (http://www.ietf.org/rfc/rfc2246.txt) Record Protocol and provides
+ * relating encryption/decryption functionality.
+ * The work functionality is based on the security
+ * parameters negotiated during the handshake.
+ */
+public class ConnectionStateTLS extends ConnectionState {
+
+ // Precomputed prf label values:
+ // "key expansion".getBytes()
+ private static byte[] KEY_EXPANSION_LABEL = {
+ (byte) 0x6B, (byte) 0x65, (byte) 0x79, (byte) 0x20, (byte) 0x65,
+ (byte) 0x78, (byte) 0x70, (byte) 0x61, (byte) 0x6E, (byte) 0x73,
+ (byte) 0x69, (byte) 0x6F, (byte) 0x6E };
+
+ // "client write key".getBytes()
+ private static byte[] CLIENT_WRITE_KEY_LABEL = {
+ (byte) 0x63, (byte) 0x6C, (byte) 0x69, (byte) 0x65, (byte) 0x6E,
+ (byte) 0x74, (byte) 0x20, (byte) 0x77, (byte) 0x72, (byte) 0x69,
+ (byte) 0x74, (byte) 0x65, (byte) 0x20, (byte) 0x6B, (byte) 0x65,
+ (byte) 0x79 };
+
+ // "server write key".getBytes()
+ private static byte[] SERVER_WRITE_KEY_LABEL = {
+ (byte) 0x73, (byte) 0x65, (byte) 0x72, (byte) 0x76, (byte) 0x65,
+ (byte) 0x72, (byte) 0x20, (byte) 0x77, (byte) 0x72, (byte) 0x69,
+ (byte) 0x74, (byte) 0x65, (byte) 0x20, (byte) 0x6B, (byte) 0x65,
+ (byte) 0x79 };
+
+ // "IV block".getBytes()
+ private static byte[] IV_BLOCK_LABEL = {
+ (byte) 0x49, (byte) 0x56, (byte) 0x20, (byte) 0x62, (byte) 0x6C,
+ (byte) 0x6F, (byte) 0x63, (byte) 0x6B };
+
+ // MACs to create and check the message integrity info
+ private final Mac encMac;
+ private final Mac decMac;
+
+ // Once created permanently used array:
+ // is used to create the header of the MAC material value:
+ // 5 == 1(TLSCompressed.type) + 2(TLSCompressed.version) +
+ // 2(TLSCompressed.length)
+ private final byte[] mac_material_header = new byte[] {0, 3, 1, 0, 0};
+
+ /**
+ * Creates the instance of TLS v1 Connection State. All of the
+ * security parameters are provided by session object.
+ * @param session: the sessin object which incapsulates
+ * all of the security parameters established by handshake protocol.
+ * The key calculation for the state is done according
+ * to the TLS v 1.0 Protocol specification.
+ * (http://www.ietf.org/rfc/rfc2246.txt)
+ */
+ protected ConnectionStateTLS(SSLSessionImpl session) {
+ try {
+ CipherSuite cipherSuite = session.cipherSuite;
+
+ hash_size = cipherSuite.getMACLength();
+ boolean is_exportabe = cipherSuite.isExportable();
+ int key_size = (is_exportabe)
+ ? cipherSuite.keyMaterial
+ : cipherSuite.expandedKeyMaterial;
+ int iv_size = cipherSuite.getBlockSize();
+
+ String algName = cipherSuite.getBulkEncryptionAlgorithm();
+ String macName = cipherSuite.getHmacName();
+ if (logger != null) {
+ logger.println("ConnectionStateTLS.create:");
+ logger.println(" cipher suite name: "
+ + cipherSuite.getName());
+ logger.println(" encryption alg name: " + algName);
+ logger.println(" mac alg name: " + macName);
+ logger.println(" hash size: " + hash_size);
+ logger.println(" block size: " + iv_size);
+ logger.println(" IV size (== block size):" + iv_size);
+ logger.println(" key size: " + key_size);
+ }
+
+ byte[] clientRandom = session.clientRandom;
+ byte[] serverRandom = session.serverRandom;
+ // so we need PRF value of size of
+ // 2*hash_size + 2*key_size + 2*iv_size
+ byte[] key_block = new byte[2*hash_size + 2*key_size + 2*iv_size];
+ byte[] seed = new byte[clientRandom.length + serverRandom.length];
+ System.arraycopy(serverRandom, 0, seed, 0, serverRandom.length);
+ System.arraycopy(clientRandom, 0, seed, serverRandom.length,
+ clientRandom.length);
+
+ PRF.computePRF(key_block, session.master_secret,
+ KEY_EXPANSION_LABEL, seed);
+
+ byte[] client_mac_secret = new byte[hash_size];
+ byte[] server_mac_secret = new byte[hash_size];
+ byte[] client_key = new byte[key_size];
+ byte[] server_key = new byte[key_size];
+
+ boolean is_client = !session.isServer;
+
+ is_block_cipher = (iv_size > 0);
+ // do not count, as block_size is always 8
+ // block_size = iv_size;
+
+ System.arraycopy(key_block, 0, client_mac_secret, 0, hash_size);
+ System.arraycopy(key_block, hash_size,
+ server_mac_secret, 0, hash_size);
+ System.arraycopy(key_block, 2*hash_size, client_key, 0, key_size);
+ System.arraycopy(key_block, 2*hash_size+key_size,
+ server_key, 0, key_size);
+
+ IvParameterSpec clientIV = null;
+ IvParameterSpec serverIV = null;
+
+ if (is_exportabe) {
+ System.arraycopy(clientRandom, 0,
+ seed, 0, clientRandom.length);
+ System.arraycopy(serverRandom, 0,
+ seed, clientRandom.length, serverRandom.length);
+ byte[] final_client_key =
+ new byte[cipherSuite.expandedKeyMaterial];
+ byte[] final_server_key =
+ new byte[cipherSuite.expandedKeyMaterial];
+ PRF.computePRF(final_client_key, client_key,
+ CLIENT_WRITE_KEY_LABEL, seed);
+ PRF.computePRF(final_server_key, server_key,
+ SERVER_WRITE_KEY_LABEL, seed);
+ client_key = final_client_key;
+ server_key = final_server_key;
+ if (is_block_cipher) {
+ byte[] iv_block = new byte[2*iv_size];
+ PRF.computePRF(iv_block, null, IV_BLOCK_LABEL, seed);
+ clientIV = new IvParameterSpec(iv_block, 0, iv_size);
+ serverIV = new IvParameterSpec(iv_block, iv_size, iv_size);
+ }
+ } else if (is_block_cipher) {
+ clientIV = new IvParameterSpec(key_block,
+ 2*(hash_size+key_size), iv_size);
+ serverIV = new IvParameterSpec(key_block,
+ 2*(hash_size+key_size)+iv_size, iv_size);
+ }
+
+ if (logger != null) {
+ logger.println("is exportable: "+is_exportabe);
+ logger.println("master_secret");
+ logger.print(session.master_secret);
+ logger.println("client_random");
+ logger.print(clientRandom);
+ logger.println("server_random");
+ logger.print(serverRandom);
+ //logger.println("key_block");
+ //logger.print(key_block);
+ logger.println("client_mac_secret");
+ logger.print(client_mac_secret);
+ logger.println("server_mac_secret");
+ logger.print(server_mac_secret);
+ logger.println("client_key");
+ logger.print(client_key);
+ logger.println("server_key");
+ logger.print(server_key);
+ if (clientIV == null) {
+ logger.println("no IV.");
+ } else {
+ logger.println("client_iv");
+ logger.print(clientIV.getIV());
+ logger.println("server_iv");
+ logger.print(serverIV.getIV());
+ }
+ }
+
+ encCipher = Cipher.getInstance(algName);
+ decCipher = Cipher.getInstance(algName);
+ encMac = Mac.getInstance(macName);
+ decMac = Mac.getInstance(macName);
+
+ if (is_client) { // client side
+ encCipher.init(Cipher.ENCRYPT_MODE,
+ new SecretKeySpec(client_key, algName), clientIV);
+ decCipher.init(Cipher.DECRYPT_MODE,
+ new SecretKeySpec(server_key, algName), serverIV);
+ encMac.init(new SecretKeySpec(client_mac_secret, macName));
+ decMac.init(new SecretKeySpec(server_mac_secret, macName));
+ } else { // server side
+ encCipher.init(Cipher.ENCRYPT_MODE,
+ new SecretKeySpec(server_key, algName), serverIV);
+ decCipher.init(Cipher.DECRYPT_MODE,
+ new SecretKeySpec(client_key, algName), clientIV);
+ encMac.init(new SecretKeySpec(server_mac_secret, macName));
+ decMac.init(new SecretKeySpec(client_mac_secret, macName));
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new AlertException(AlertProtocol.INTERNAL_ERROR,
+ new SSLProtocolException(
+ "Error during computation of security parameters"));
+ }
+ }
+
+ /**
+ * Creates the GenericStreamCipher or GenericBlockCipher
+ * data structure for specified data of specified type.
+ * @throws org.apache.harmony.xnet.provider.jsse.AlertException if alert was occured.
+ */
+ protected byte[] encrypt(byte type, byte[] fragment, int offset, int len) {
+ try {
+ int content_mac_length = len + hash_size;
+ int padding_length = is_block_cipher
+ ? ((8 - (++content_mac_length & 0x07)) & 0x07)
+ : 0;
+ byte[] res = new byte[content_mac_length + padding_length];
+ System.arraycopy(fragment, offset, res, 0, len);
+
+ mac_material_header[0] = type;
+ mac_material_header[3] = (byte) ((0x00FF00 & len) >> 8);
+ mac_material_header[4] = (byte) (0x0000FF & len);
+
+ encMac.update(write_seq_num);
+ encMac.update(mac_material_header);
+ encMac.update(fragment, offset, len);
+ encMac.doFinal(res, len);
+
+ //if (logger != null) {
+ // logger.println("MAC Material:");
+ // logger.print(write_seq_num);
+ // logger.print(mac_material_header);
+ // logger.print(fragment, offset, len);
+ //}
+
+ if (is_block_cipher) {
+ // do padding:
+ Arrays.fill(res, content_mac_length-1,
+ res.length, (byte) (padding_length));
+ }
+ if (logger != null) {
+ logger.println("SSLRecordProtocol.do_encryption: Generic"
+ + (is_block_cipher
+ ? "BlockCipher with padding["+padding_length+"]:"
+ : "StreamCipher:"));
+ logger.print(res);
+ }
+ byte[] rez = new byte[encCipher.getOutputSize(res.length)];
+ // We should not call just doFinal because it reinitialize
+ // the cipher, but as says rfc 2246:
+ // "For stream ciphers that do not use a synchronization
+ // vector (such as RC4), the stream cipher state from the end
+ // of one record is simply used on the subsequent packet."
+ // and for block ciphers:
+ // "The IV for subsequent records is the last ciphertext block from
+ // the previous record."
+ // i.e. we should keep the cipher state.
+ encCipher.update(res, 0, res.length, rez);
+ incSequenceNumber(write_seq_num);
+ return rez;
+ } catch (GeneralSecurityException e) {
+ e.printStackTrace();
+ throw new AlertException(AlertProtocol.INTERNAL_ERROR,
+ new SSLProtocolException("Error during the encryption"));
+ }
+ }
+
+ /**
+ * Retrieves the fragment of the Plaintext structure of
+ * the specified type from the provided data representing
+ * the Generic[Stream|Block]Cipher structure.
+ * @throws org.apache.harmony.xnet.provider.jsse.AlertException if alert was occured.
+ */
+ protected byte[] decrypt(byte type, byte[] fragment,
+ int offset, int len) {
+ // plain data of the Generic[Stream|Block]Cipher structure
+ byte[] data = decCipher.update(fragment, offset, len);
+ // the 'content' part of the structure
+ byte[] content;
+ if (is_block_cipher) {
+ // check padding
+ int padding_length = data[data.length-1];
+ for (int i=0; i<padding_length; i++) {
+ if (data[data.length-2-i] != padding_length) {
+ throw new AlertException(
+ AlertProtocol.DECRYPTION_FAILED,
+ new SSLProtocolException(
+ "Received message has bad padding"));
+ }
+ }
+ content = new byte[data.length - hash_size - padding_length - 1];
+ } else {
+ content = new byte[data.length - hash_size];
+ }
+
+ mac_material_header[0] = type;
+ mac_material_header[3] = (byte) ((0x00FF00 & content.length) >> 8);
+ mac_material_header[4] = (byte) (0x0000FF & content.length);
+
+ decMac.update(read_seq_num);
+ decMac.update(mac_material_header);
+ decMac.update(data, 0, content.length); // mac.update(fragment);
+ byte[] mac_value = decMac.doFinal();
+ if (logger != null) {
+ logger.println("Decrypted:");
+ logger.print(data);
+ //logger.println("MAC Material:");
+ //logger.print(read_seq_num);
+ //logger.print(mac_material_header);
+ //logger.print(data, 0, content.length);
+ logger.println("Expected mac value:");
+ logger.print(mac_value);
+ }
+ // checking the mac value
+ for (int i=0; i<hash_size; i++) {
+ if (mac_value[i] != data[i+content.length]) {
+ throw new AlertException(AlertProtocol.BAD_RECORD_MAC,
+ new SSLProtocolException("Bad record MAC"));
+ }
+ }
+ System.arraycopy(data, 0, content, 0, content.length);
+ incSequenceNumber(read_seq_num);
+ return content;
+ }
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ContentType.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ContentType.java
new file mode 100644
index 0000000..dedfe64
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ContentType.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+/**
+ * This class incapsulates the constants determining the
+ * types of SSL/TLS record's content data.
+ * Constant values are taken according to the TLS v1 specification
+ * (http://www.ietf.org/rfc/rfc2246.txt).
+ */
+public class ContentType {
+
+ /**
+ * Identifies change cipher spec message
+ */
+ protected static final byte CHANGE_CIPHER_SPEC = 20;
+
+ /**
+ * Identifies alert message
+ */
+ protected static final byte ALERT = 21;
+
+ /**
+ * Identifies handshake message
+ */
+ protected static final byte HANDSHAKE = 22;
+
+ /**
+ * Identifies application data message
+ */
+ protected static final byte APPLICATION_DATA = 23;
+
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DHParameters.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DHParameters.java
new file mode 100644
index 0000000..1a441a5
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DHParameters.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+package org.apache.harmony.xnet.provider.jsse;
+
+/**
+ * This class contains well-known primes
+ */
+public class DHParameters {
+
+ // Well-known 512 bit prime
+ // http://news.hping.org/sci.crypt.archive/2370.html
+ private static byte[] prime512 = new byte[] { (byte) 0xF5, (byte) 0x2A, (byte) 0xFF,
+ (byte) 0x3C, (byte) 0xE1, (byte) 0xB1, (byte) 0x29, (byte) 0x40,
+ (byte) 0x18, (byte) 0x11, (byte) 0x8D, (byte) 0x7C, (byte) 0x84,
+ (byte) 0xA7, (byte) 0x0A, (byte) 0x72, (byte) 0xD6, (byte) 0x86,
+ (byte) 0xC4, (byte) 0x03, (byte) 0x19, (byte) 0xC8, (byte) 0x07,
+ (byte) 0x29, (byte) 0x7A, (byte) 0xCA, (byte) 0x95, (byte) 0x0C,
+ (byte) 0xD9, (byte) 0x96, (byte) 0x9F, (byte) 0xAB, (byte) 0xD0,
+ (byte) 0x0A, (byte) 0x50, (byte) 0x9B, (byte) 0x02, (byte) 0x46,
+ (byte) 0xD3, (byte) 0x08, (byte) 0x3D, (byte) 0x66, (byte) 0xA4,
+ (byte) 0x5D, (byte) 0x41, (byte) 0x9F, (byte) 0x9C, (byte) 0x7C,
+ (byte) 0xBD, (byte) 0x89, (byte) 0x4B, (byte) 0x22, (byte) 0x19,
+ (byte) 0x26, (byte) 0xBA, (byte) 0xAB, (byte) 0xA2, (byte) 0x5E,
+ (byte) 0xC3, (byte) 0x55, (byte) 0xE9, (byte) 0x2A, (byte) 0x05,
+ (byte) 0x5F };
+
+ // Well-Known Group 1: A 768 bit prime rfc 2539
+ // (http://www.ietf.org/rfc/rfc2539.txt?number=2539)
+ private static byte[] primeGroup1 = { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xC9,
+ (byte) 0x0F, (byte) 0xDA, (byte) 0xA2, (byte) 0x21, (byte) 0x68,
+ (byte) 0xC2, (byte) 0x34, (byte) 0xC4, (byte) 0xC6, (byte) 0x62,
+ (byte) 0x8B, (byte) 0x80, (byte) 0xDC, (byte) 0x1C, (byte) 0xD1,
+ (byte) 0x29, (byte) 0x02, (byte) 0x4E, (byte) 0x08, (byte) 0x8A,
+ (byte) 0x67, (byte) 0xCC, (byte) 0x74, (byte) 0x02, (byte) 0x0B,
+ (byte) 0xBE, (byte) 0xA6, (byte) 0x3B, (byte) 0x13, (byte) 0x9B,
+ (byte) 0x22, (byte) 0x51, (byte) 0x4A, (byte) 0x08, (byte) 0x79,
+ (byte) 0x8E, (byte) 0x34, (byte) 0x04, (byte) 0xDD, (byte) 0xEF,
+ (byte) 0x95, (byte) 0x19, (byte) 0xB3, (byte) 0xCD, (byte) 0x3A,
+ (byte) 0x43, (byte) 0x1B, (byte) 0x30, (byte) 0x2B, (byte) 0x0A,
+ (byte) 0x6D, (byte) 0xF2, (byte) 0x5F, (byte) 0x14, (byte) 0x37,
+ (byte) 0x4F, (byte) 0xE1, (byte) 0x35, (byte) 0x6D, (byte) 0x6D,
+ (byte) 0x51, (byte) 0xC2, (byte) 0x45, (byte) 0xE4, (byte) 0x85,
+ (byte) 0xB5, (byte) 0x76, (byte) 0x62, (byte) 0x5E, (byte) 0x7E,
+ (byte) 0xC6, (byte) 0xF4, (byte) 0x4C, (byte) 0x42, (byte) 0xE9,
+ (byte) 0xA6, (byte) 0x3A, (byte) 0x36, (byte) 0x20, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF };
+
+ // Well-Known Group 2: A 1024 bit prime rfc 2539
+ // (http://www.ietf.org/rfc/rfc2539.txt?number=2539)
+ private static byte[] primeGroup2 = { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xC9,
+ (byte) 0x0F, (byte) 0xDA, (byte) 0xA2, (byte) 0x21, (byte) 0x68,
+ (byte) 0xC2, (byte) 0x34, (byte) 0xC4, (byte) 0xC6, (byte) 0x62,
+ (byte) 0x8B, (byte) 0x80, (byte) 0xDC, (byte) 0x1C, (byte) 0xD1,
+ (byte) 0x29, (byte) 0x02, (byte) 0x4E, (byte) 0x08, (byte) 0x8A,
+ (byte) 0x67, (byte) 0xCC, (byte) 0x74, (byte) 0x02, (byte) 0x0B,
+ (byte) 0xBE, (byte) 0xA6, (byte) 0x3B, (byte) 0x13, (byte) 0x9B,
+ (byte) 0x22, (byte) 0x51, (byte) 0x4A, (byte) 0x08, (byte) 0x79,
+ (byte) 0x8E, (byte) 0x34, (byte) 0x04, (byte) 0xDD, (byte) 0xEF,
+ (byte) 0x95, (byte) 0x19, (byte) 0xB3, (byte) 0xCD, (byte) 0x3A,
+ (byte) 0x43, (byte) 0x1B, (byte) 0x30, (byte) 0x2B, (byte) 0x0A,
+ (byte) 0x6D, (byte) 0xF2, (byte) 0x5F, (byte) 0x14, (byte) 0x37,
+ (byte) 0x4F, (byte) 0xE1, (byte) 0x35, (byte) 0x6D, (byte) 0x6D,
+ (byte) 0x51, (byte) 0xC2, (byte) 0x45, (byte) 0xE4, (byte) 0x85,
+ (byte) 0xB5, (byte) 0x76, (byte) 0x62, (byte) 0x5E, (byte) 0x7E,
+ (byte) 0xC6, (byte) 0xF4, (byte) 0x4C, (byte) 0x42, (byte) 0xE9,
+ (byte) 0xA6, (byte) 0x37, (byte) 0xED, (byte) 0x6B, (byte) 0x0B,
+ (byte) 0xFF, (byte) 0x5C, (byte) 0xB6, (byte) 0xF4, (byte) 0x06,
+ (byte) 0xB7, (byte) 0xED, (byte) 0xEE, (byte) 0x38, (byte) 0x6B,
+ (byte) 0xFB, (byte) 0x5A, (byte) 0x89, (byte) 0x9F, (byte) 0xA5,
+ (byte) 0xAE, (byte) 0x9F, (byte) 0x24, (byte) 0x11, (byte) 0x7C,
+ (byte) 0x4B, (byte) 0x1F, (byte) 0xE6, (byte) 0x49, (byte) 0x28,
+ (byte) 0x66, (byte) 0x51, (byte) 0xEC, (byte) 0xE6, (byte) 0x53,
+ (byte) 0x81, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
+ };
+
+ private static byte[] prime;
+
+ static {
+//TODO set prime depand on some system or security property
+ prime = prime512;
+ }
+
+ /**
+ * Returns prime bytes
+ * @return
+ */
+ public static byte[] getPrime() {
+ return prime;
+ }
+} \ No newline at end of file
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DataStream.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DataStream.java
new file mode 100644
index 0000000..b52b838
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DataStream.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+/**
+ * This interface represents the ability of the
+ * classes to provide the chunks of data.
+ */
+public interface DataStream {
+
+ /**
+ * Checks if there is data to be read.
+ * @return true if there is the input data in the stream,
+ * false otherwise
+ */
+ public boolean hasData();
+
+ /**
+ * Retrieves the data of specified length from the stream.
+ * If the data size in the stream is less than specified length,
+ * method returns all the data contained in the stream.
+ * @return byte array containing the demanded data.
+ */
+ public byte[] getData(int length);
+
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DelegatedTask.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DelegatedTask.java
new file mode 100644
index 0000000..ea6ba78
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DelegatedTask.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.HandshakeProtocol;
+
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+/**
+ * Delegated Runnable task for SSLEngine
+ */
+public class DelegatedTask implements Runnable {
+
+ private final HandshakeProtocol handshaker;
+ private final PrivilegedExceptionAction action;
+ private final AccessControlContext context;
+
+ /**
+ * Creates DelegatedTask
+ * @param action
+ * @param handshaker
+ * @param context
+ */
+ public DelegatedTask(PrivilegedExceptionAction action, HandshakeProtocol handshaker, AccessControlContext context) {
+ this.action = action;
+ this.handshaker = handshaker;
+ this.context = context;
+ }
+
+ /**
+ * Executes DelegatedTask
+ */
+ public void run() {
+ synchronized (handshaker) {
+ try {
+ AccessController.doPrivileged(action, context);
+ } catch (PrivilegedActionException e) {
+ // pass exception to HandshakeProtocol
+ handshaker.delegatedTaskErr = e.getException();
+ } catch (RuntimeException e) {
+ // pass exception to HandshakeProtocol
+ handshaker.delegatedTaskErr = e;
+ }
+ }
+
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java
new file mode 100644
index 0000000..a8794df
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.AlertException;
+
+import java.security.MessageDigest;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.cert.Certificate;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.net.ssl.SSLException;
+
+/**
+ * This class represents Signature type, as descrybed in TLS v 1.0 Protocol
+ * specification, 7.4.3. It allow to init, update and sign hash. Hash algorithm
+ * depends on SignatureAlgorithm.
+ *
+ * select (SignatureAlgorithm)
+ * { case anonymous: struct { };
+ * case rsa:
+ * digitally-signed struct {
+ * opaque md5_hash[16];
+ * opaque sha_hash[20];
+ * };
+ * case dsa:
+ * digitally-signed struct {
+ * opaque sha_hash[20];
+ * };
+ * } Signature;
+ *
+ * Digital signing description see in TLS spec., 4.7.
+ * (http://www.ietf.org/rfc/rfc2246.txt)
+ *
+ */
+public class DigitalSignature {
+
+ private MessageDigest md5 = null;
+ private MessageDigest sha = null;
+ private Signature signature = null;
+ private Cipher cipher = null;
+
+ private byte[] md5_hash;
+ private byte[] sha_hash;
+
+ /**
+ * Create Signature type
+ * @param keyExchange
+ */
+ public DigitalSignature(int keyExchange) {
+ try {
+ if (keyExchange == CipherSuite.KeyExchange_RSA_EXPORT ||
+ keyExchange == CipherSuite.KeyExchange_RSA ||
+ keyExchange == CipherSuite.KeyExchange_DHE_RSA ||
+ keyExchange == CipherSuite.KeyExchange_DHE_RSA_EXPORT) {
+ // SignatureAlgorithm is rsa
+ md5 = MessageDigest.getInstance("MD5");
+ sha = MessageDigest.getInstance("SHA-1");
+ cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ } else if (keyExchange == CipherSuite.KeyExchange_DHE_DSS ||
+ keyExchange == CipherSuite.KeyExchange_DHE_DSS_EXPORT ) {
+ // SignatureAlgorithm is dsa
+ sha = MessageDigest.getInstance("SHA-1");
+ signature = Signature.getInstance("NONEwithDSA");
+// The Signature should be empty in case of anonimous signature algorithm:
+// } else if (keyExchange == CipherSuite.KeyExchange_DH_anon ||
+// keyExchange == CipherSuite.KeyExchange_DH_anon_EXPORT) {
+//
+ }
+ } catch (Exception e) {
+ throw new AlertException(
+ AlertProtocol.INTERNAL_ERROR,
+ new SSLException(
+ "INTERNAL ERROR: Unexpected exception on digital signature",
+ e));
+ }
+
+ }
+
+ /**
+ * Initiate Signature type by private key
+ * @param key
+ */
+ public void init(PrivateKey key) {
+ try {
+ if (signature != null) {
+ signature.initSign(key);
+ } else if (cipher != null) {
+ cipher.init(Cipher.ENCRYPT_MODE, key);
+ }
+ } catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Initiate Signature type by certificate
+ * @param cert
+ */
+ public void init(Certificate cert) {
+ try {
+ if (signature != null) {
+ signature.initVerify(cert);
+ } else if (cipher != null) {
+ cipher.init(Cipher.DECRYPT_MODE, cert);
+ }
+ } catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Update Signature hash
+ * @param data
+ */
+ public void update(byte[] data) {
+ try {
+ if (sha != null) {
+ sha.update(data);
+ }
+ if (md5 != null) {
+ md5.update(data);
+ }
+ } catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Sets MD5 hash
+ * @param data
+ */
+ public void setMD5(byte[] data) {
+ md5_hash = data;
+ }
+
+ /**
+ * Sets SHA hash
+ * @param data
+ */
+ public void setSHA(byte[] data) {
+ sha_hash = data;
+ }
+
+ /**
+ * Sign hash
+ * @return Signature bytes
+ */
+ public byte[] sign() {
+ try {
+ if (md5 != null && md5_hash == null) {
+ md5_hash = new byte[16];
+ md5.digest(md5_hash, 0, md5_hash.length);
+ }
+ if (md5_hash != null) {
+ if (signature != null) {
+ signature.update(md5_hash);
+ } else if (cipher != null) {
+ cipher.update(md5_hash);
+ }
+ }
+ if (sha != null && sha_hash == null) {
+ sha_hash = new byte[20];
+ sha.digest(sha_hash, 0, sha_hash.length);
+ }
+ if (sha_hash != null) {
+ if (signature != null) {
+ signature.update(sha_hash);
+ } else if (cipher != null) {
+ cipher.update(sha_hash);
+ }
+ }
+ if (signature != null) {
+ return signature.sign();
+ } else if (cipher != null) {
+ return cipher.doFinal();
+ }
+ return new byte[0];
+ } catch (Exception e){
+ e.printStackTrace();
+ return new byte[0];
+ }
+ }
+
+ /**
+ * Verifies the signature data.
+ * @param data - the signature bytes
+ * @return true if verified
+ */
+ public boolean verifySignature(byte[] data) {
+ try {
+ if (signature != null) {
+ return signature.verify(data);
+ } else if (cipher != null) {
+ byte[] decrypt = cipher.doFinal(data);
+ byte[] md5_sha;
+ if (md5_hash != null && sha_hash != null) {
+ md5_sha = new byte[md5_hash.length + sha_hash.length];
+ System.arraycopy(md5_hash, 0, md5_sha, 0, md5_hash.length);
+ System.arraycopy(sha_hash, 0, md5_sha, md5_hash.length, sha_hash.length);
+ } else if (md5_hash != null) {
+ md5_sha = md5_hash;
+ } else {
+ md5_sha = sha_hash;
+ }
+ if (Arrays.equals(decrypt, md5_sha)) {
+ return true;
+ } else {
+ return false;
+ }
+ } else if (data == null || data.length == 0) {
+ return true;
+ } else {
+ return false;
+ }
+ } catch (Exception e){
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfBufferException.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfBufferException.java
new file mode 100644
index 0000000..b2bcafe
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfBufferException.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.IOException;
+
+/**
+ * This class represents the exception signalizing that
+ * data could not be read from the stream because
+ * underlying input stream reached its end.
+ */
+public class EndOfBufferException extends IOException {
+
+ /**
+ * Constructor
+ */
+ public EndOfBufferException() {
+ super();
+ }
+
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfSourceException.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfSourceException.java
new file mode 100644
index 0000000..fbc1eaf
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfSourceException.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.IOException;
+
+/**
+ * This class represents the exception signalizing that
+ * data could not be read from the buffered stream because
+ * underlying data buffer was exhausted.
+ */
+public class EndOfSourceException extends IOException {
+
+ /**
+ * Constructor
+ */
+ public EndOfSourceException() {
+ super();
+ }
+
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Finished.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Finished.java
new file mode 100644
index 0000000..d0f1fe1
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Finished.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris Kuznetsov
+* @version $Revision$
+*/
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.Message;
+
+import java.io.IOException;
+
+/**
+ *
+ * Represents Finished message
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.9.
+ * Finished</a>
+ *
+ */
+public class Finished extends Message {
+
+ // verify data
+ private byte[] data;
+
+ /**
+ * Creates outbound message
+ * @param bytes
+ */
+ public Finished(byte[] bytes) {
+ data = bytes;
+ length = data.length;
+ }
+
+ /**
+ * Creates inbound message
+ * @param in
+ * @param length
+ * @throws IOException
+ */
+ public Finished(HandshakeIODataStream in, int length)
+ throws IOException {
+ if (length == 12 || length == 36) {
+ data = in.read(length);
+ length = data.length;
+ } else {
+ fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect Finished");
+ }
+ }
+
+ public void send(HandshakeIODataStream out) {
+ out.write(data);
+ }
+
+ /**
+ * Returns message type
+ * @return
+ */
+ public int getType() {
+ return Handshake.FINISHED;
+ }
+
+ /**
+ * Returns verify data
+ * @return
+ */
+ public byte[] getData() {
+ return data;
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Handshake.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Handshake.java
new file mode 100644
index 0000000..4668b8c
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Handshake.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+/**
+ *
+ * This class incapsulates the constants determining the types of handshake
+ * messages as defined in TLS 1.0 spec., 7.4. Handshake protocol.
+ * (http://www.ietf.org/rfc/rfc2246.txt)
+ *
+ */
+public class Handshake {
+
+ /**
+ *
+ * hello_request handshake type
+ */
+ public static final byte HELLO_REQUEST = 0;
+
+ /**
+ *
+ * client_hello handshake type
+ */
+ public static final byte CLIENT_HELLO = 1;
+
+ /**
+ *
+ * server_hello handshake type
+ */
+ public static final byte SERVER_HELLO = 2;
+
+ /**
+ *
+ * certificate handshake type
+ */
+ public static final byte CERTIFICATE = 11;
+
+ /**
+ *
+ * server_key_exchange handshake type
+ */
+ public static final byte SERVER_KEY_EXCHANGE = 12;
+
+ /**
+ *
+ * certificate_request handshake type
+ */
+ public static final byte CERTIFICATE_REQUEST = 13;
+
+ /**
+ *
+ * server_hello_done handshake type
+ */
+ public static final byte SERVER_HELLO_DONE = 14;
+
+ /**
+ *
+ * certificate_verify handshake type
+ */
+ public static final byte CERTIFICATE_VERIFY = 15;
+
+ /**
+ *
+ * client_key_exchange handshake type
+ */
+ public static final byte CLIENT_KEY_EXCHANGE = 16;
+
+ /**
+ *
+ * finished handshake type
+ */
+ public static final byte FINISHED = 20;
+
+} \ No newline at end of file
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeIODataStream.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeIODataStream.java
new file mode 100644
index 0000000..b5c4553
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeIODataStream.java
@@ -0,0 +1,462 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.AlertException;
+import org.apache.harmony.xnet.provider.jsse.SSLInputStream;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import javax.net.ssl.SSLHandshakeException;
+
+/**
+ * This class provides Input/Output data functionality
+ * for handshake layer. It provides read and write operations
+ * and accumulates all sent/received handshake's data.
+ * This class can be presented as a combination of 2 data pipes.
+ * The first data pipe is a pipe of income data: append method
+ * places the data at the beginning of the pipe, and read methods
+ * consume the data from the pipe. The second pipe is an outcoming
+ * data pipe: write operations plases the data into the pipe,
+ * and getData methods consume the data.
+ * It is important to note that work with pipe cound not be
+ * started if there is unconsumed data in another pipe. It is
+ * reasoned by the following: handshake protocol performs read
+ * and write operations consecuently. I.e. it first reads all
+ * income data and only than produces the responce and places it
+ * into the stream.
+ * The read operations of the stream presented by the methods
+ * of SSLInputStream which in its turn is an extension of InputStream.
+ * So this stream can be used as an InputStream parameter for
+ * certificate generation.
+ * Also input stream functionality supports marks. The marks
+ * help to reset the position of the stream in case of incompleate
+ * handshake records. Note that in case of exhausting
+ * of income data the EndOfBufferException is thown which implies
+ * the following:
+ * 1. the stream contains scrappy handshake record,
+ * 2. the read position should be reseted to marked,
+ * 3. and more income data is expected.
+ * The throwing of the exception (instead of returning of -1 value
+ * or incompleate filling of destination buffer)
+ * helps to speed up the process of scrappy data recognition and
+ * processing.
+ * For more information about TLS handshake process see
+ * TLS v 1 specification at http://www.ietf.org/rfc/rfc2246.txt.
+ */
+public class HandshakeIODataStream
+ extends SSLInputStream implements org.apache.harmony.xnet.provider.jsse.Appendable, DataStream {
+
+ // Objects are used to compute digests of data passed
+ // during the handshake phase
+ private static final MessageDigest md5;
+ private static final MessageDigest sha;
+
+ static {
+ try {
+ md5 = MessageDigest.getInstance("MD5");
+ sha = MessageDigest.getInstance("SHA-1");
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException(
+ "Could not initialize the Digest Algorithms.");
+ }
+ }
+
+ public HandshakeIODataStream() {}
+
+ // buffer is used to keep the handshaking data;
+ private int buff_size = 1024;
+ private int inc_buff_size = 1024;
+ private byte[] buffer = new byte[buff_size];
+
+
+ // ---------------- Input related functionality -----------------
+
+ // position of the next byte to read
+ private int read_pos;
+ private int marked_pos;
+ // position of the last byte to read + 1
+ private int read_pos_end;
+
+ public int available() {
+ return read_pos_end - read_pos;
+ }
+
+ public boolean markSupported() {
+ return true;
+ }
+
+ public void mark(int limit) {
+ marked_pos = read_pos;
+ }
+
+ public void mark() {
+ marked_pos = read_pos;
+ }
+
+ public void reset() {
+ read_pos = marked_pos;
+ }
+
+ /**
+ * Removes the data from the marked position to
+ * the current read position. The method is usefull when it is needed
+ * to delete one message from the internal buffer.
+ */
+ protected void removeFromMarkedPosition() {
+ System.arraycopy(buffer, read_pos,
+ buffer, marked_pos, read_pos_end - read_pos);
+ read_pos_end -= (read_pos - marked_pos);
+ read_pos = marked_pos;
+ }
+
+ /**
+ * read an opaque value;
+ * @param byte: byte
+ * @return
+ */
+ public int read() throws IOException {
+ if (read_pos == read_pos_end) {
+ //return -1;
+ throw new EndOfBufferException();
+ }
+ return buffer[read_pos++] & 0xFF;
+ }
+
+ /**
+ * reads vector of opaque values
+ * @param new: long
+ * @return
+ */
+ public byte[] read(int length) throws IOException {
+ if (length > available()) {
+ throw new EndOfBufferException();
+ }
+ byte[] res = new byte[length];
+ System.arraycopy(buffer, read_pos, res, 0, length);
+ read_pos = read_pos + length;
+ return res;
+ }
+
+ public int read(byte[] dest, int offset, int length) throws IOException {
+ if (length > available()) {
+ throw new EndOfBufferException();
+ }
+ System.arraycopy(buffer, read_pos, dest, offset, length);
+ read_pos = read_pos + length;
+ return length;
+ }
+
+ // ------------------- Extending of the input data ---------------------
+
+ /**
+ * Appends the income data to be read by handshake protocol.
+ * The attempts to overflow the buffer by meens of this methos
+ * seem to be futile because of:
+ * 1. The SSL protocol specifies the maximum size of the record
+ * and record protocol does not pass huge messages.
+ * (see TLS v1 specification http://www.ietf.org/rfc/rfc2246.txt ,
+ * p 6.2)
+ * 2. After each call of this method, handshake protocol should
+ * start (and starts) the operations on received data and recognize
+ * the fake data if such was provided (to check the size of certificate
+ * for example).
+ */
+ public void append(byte[] src) {
+ append(src, 0, src.length);
+ }
+
+ private void append(byte[] src, int from, int length) {
+ if (read_pos == read_pos_end) {
+ // start reading state after writing
+ if (write_pos_beg != write_pos) {
+ // error: outboud handshake data was not sent,
+ // but inbound handshake data has been received.
+ throw new AlertException(
+ AlertProtocol.UNEXPECTED_MESSAGE,
+ new SSLHandshakeException(
+ "Handshake message has been received before "
+ + "the last oubound message had been sent."));
+ }
+ if (read_pos < write_pos) {
+ read_pos = write_pos;
+ read_pos_end = read_pos;
+ }
+ }
+ if (read_pos_end + length > buff_size) {
+ enlargeBuffer(read_pos_end+length-buff_size);
+ }
+ System.arraycopy(src, from, buffer, read_pos_end, length);
+ read_pos_end += length;
+ }
+
+ private void enlargeBuffer(int size) {
+ buff_size = (size < inc_buff_size)
+ ? buff_size + inc_buff_size
+ : buff_size + size;
+ byte[] new_buff = new byte[buff_size];
+ System.arraycopy(buffer, 0, new_buff, 0, buffer.length);
+ buffer = new_buff;
+ }
+
+ protected void clearBuffer() {
+ read_pos = 0;
+ marked_pos = 0;
+ read_pos_end = 0;
+ write_pos = 0;
+ write_pos_beg = 0;
+ Arrays.fill(buffer, (byte) 0);
+ }
+
+ // ------------------- Output related functionality --------------------
+
+ // position in the buffer available for write
+ private int write_pos;
+ // position in the buffer where the last write session has begun
+ private int write_pos_beg;
+
+ // checks if the data can be written in the buffer
+ private void check(int length) {
+ // (write_pos == write_pos_beg) iff:
+ // 1. there were not write operations yet
+ // 2. all written data was demanded by getData methods
+ if (write_pos == write_pos_beg) {
+ // just started to write after the reading
+ if (read_pos != read_pos_end) {
+ // error: attempt to write outbound data into the stream before
+ // all the inbound handshake data had been read
+ throw new AlertException(
+ AlertProtocol.INTERNAL_ERROR,
+ new SSLHandshakeException("Data was not fully read: "
+ + read_pos + " " + read_pos_end));
+ }
+ // set up the write positions
+ if (write_pos_beg < read_pos_end) {
+ write_pos_beg = read_pos_end;
+ write_pos = write_pos_beg;
+ }
+ }
+ // if there is not enought free space in the buffer - enlarge it:
+ if (write_pos + length >= buff_size) {
+ enlargeBuffer(length);
+ }
+ }
+
+ /**
+ * Writes an opaque value
+ * @param byte: byte
+ */
+ public void write(byte b) {
+ check(1);
+ buffer[write_pos++] = b;
+ }
+
+ /**
+ * Writes Uint8 value
+ * @param long: the value to be written (last byte)
+ */
+ public void writeUint8(long n) {
+ check(1);
+ buffer[write_pos++] = (byte) (n & 0x00ff);
+ }
+
+ /**
+ * Writes Uint16 value
+ * @param long: the value to be written (last 2 bytes)
+ */
+ public void writeUint16(long n) {
+ check(2);
+ buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8);
+ buffer[write_pos++] = (byte) (n & 0x00ff);
+ }
+
+ /**
+ * Writes Uint24 value
+ * @param long: the value to be written (last 3 bytes)
+ */
+ public void writeUint24(long n) {
+ check(3);
+ buffer[write_pos++] = (byte) ((n & 0x00ff0000) >> 16);
+ buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8);
+ buffer[write_pos++] = (byte) (n & 0x00ff);
+ }
+
+ /**
+ * Writes Uint32 value
+ * @param long: the value to be written (last 4 bytes)
+ */
+ public void writeUint32(long n) {
+ check(4);
+ buffer[write_pos++] = (byte) ((n & 0x00ff000000) >> 24);
+ buffer[write_pos++] = (byte) ((n & 0x00ff0000) >> 16);
+ buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8);
+ buffer[write_pos++] = (byte) (n & 0x00ff);
+ }
+
+ /**
+ * Writes Uint64 value
+ * @param long: the value to be written
+ */
+ public void writeUint64(long n) {
+ check(8);
+ buffer[write_pos++] = (byte) ((n & 0x00ff00000000000000L) >> 56);
+ buffer[write_pos++] = (byte) ((n & 0x00ff000000000000L) >> 48);
+ buffer[write_pos++] = (byte) ((n & 0x00ff0000000000L) >> 40);
+ buffer[write_pos++] = (byte) ((n & 0x00ff00000000L) >> 32);
+ buffer[write_pos++] = (byte) ((n & 0x00ff000000) >> 24);
+ buffer[write_pos++] = (byte) ((n & 0x00ff0000) >> 16);
+ buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8);
+ buffer[write_pos++] = (byte) (n & 0x00ff);
+ }
+
+ /**
+ * writes vector of opaque values
+ * @param vector the vector to be written
+ */
+ public void write(byte[] vector) {
+ check(vector.length);
+ System.arraycopy(vector, 0, buffer, write_pos, vector.length);
+ write_pos += vector.length;
+ }
+
+ // ------------------- Retrieve the written bytes ----------------------
+
+ public boolean hasData() {
+ return (write_pos > write_pos_beg);
+ }
+
+ /**
+ * returns the chunk of stored data with the length no more than specified.
+ * @param length: int
+ * @return
+ */
+ public byte[] getData(int length) {
+ byte[] res;
+ if (write_pos - write_pos_beg < length) {
+ res = new byte[write_pos - write_pos_beg];
+ System.arraycopy(buffer, write_pos_beg,
+ res, 0, write_pos-write_pos_beg);
+ write_pos_beg = write_pos;
+ } else {
+ res = new byte[length];
+ System.arraycopy(buffer, write_pos_beg, res, 0, length);
+ write_pos_beg += length;
+ }
+ return res;
+ }
+
+ // ---------------------- Debud functionality -------------------------
+
+ protected void printContent(PrintStream outstream) {
+ int perLine = 20;
+ String prefix = " ";
+ String delimiter = "";
+
+ for (int i=write_pos_beg; i<write_pos; i++) {
+ String tail = Integer.toHexString(
+ 0x00ff & buffer[i]).toUpperCase();
+ if (tail.length() == 1) {
+ tail = "0" + tail;
+ }
+ outstream.print(prefix + tail + delimiter);
+
+ if (((i-write_pos_beg+1)%10) == 0) {
+ outstream.print(" ");
+ }
+
+ if (((i-write_pos_beg+1)%perLine) == 0) {
+ outstream.println();
+ }
+ }
+ outstream.println();
+ }
+
+ // ---------------------- Message Digest Functionality ----------------
+
+ /**
+ * Returns the MD5 digest of the data passed throught the stream
+ * @return MD5 digest
+ */
+ protected byte[] getDigestMD5() {
+ synchronized (md5) {
+ int len = (read_pos_end > write_pos)
+ ? read_pos_end
+ : write_pos;
+ md5.update(buffer, 0, len);
+ return md5.digest();
+ }
+ }
+
+ /**
+ * Returns the SHA-1 digest of the data passed throught the stream
+ * @return SHA-1 digest
+ */
+ protected byte[] getDigestSHA() {
+ synchronized (sha) {
+ int len = (read_pos_end > write_pos)
+ ? read_pos_end
+ : write_pos;
+ sha.update(buffer, 0, len);
+ return sha.digest();
+ }
+ }
+
+ /**
+ * Returns the MD5 digest of the data passed throught the stream
+ * except last message
+ * @return MD5 digest
+ */
+ protected byte[] getDigestMD5withoutLast() {
+ synchronized (md5) {
+ md5.update(buffer, 0, marked_pos);
+ return md5.digest();
+ }
+ }
+
+ /**
+ * Returns the SHA-1 digest of the data passed throught the stream
+ * except last message
+ * @return SHA-1 digest
+ */
+ protected byte[] getDigestSHAwithoutLast() {
+ synchronized (sha) {
+ sha.update(buffer, 0, marked_pos);
+ return sha.digest();
+ }
+ }
+
+ /**
+ * Returns all the data passed throught the stream
+ * @return all the data passed throught the stream at the moment
+ */
+ protected byte[] getMessages() {
+ int len = (read_pos_end > write_pos) ? read_pos_end : write_pos;
+ byte[] res = new byte[len];
+ System.arraycopy(buffer, 0, res, 0, len);
+ return res;
+ }
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeProtocol.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeProtocol.java
new file mode 100644
index 0000000..14c8ae5
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeProtocol.java
@@ -0,0 +1,544 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.interfaces.RSAKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Arrays;
+import java.util.Vector;
+
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+
+/**
+ * Base class for ClientHandshakeImpl and ServerHandshakeImpl classes.
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.
+ * Handshake protocol</a>
+ *
+ */
+public abstract class HandshakeProtocol {
+
+ /**
+ * Handshake status NEED_UNWRAP - HandshakeProtocol needs to receive data
+ */
+ public final static int NEED_UNWRAP = 1;
+
+ /**
+ * Handshake status NOT_HANDSHAKING - is not currently handshaking
+ */
+ public final static int NOT_HANDSHAKING = 2;
+
+ /**
+ * Handshake status FINISHED - HandshakeProtocol has just finished
+ */
+ public final static int FINISHED = 3;
+
+ /**
+ * Handshake status NEED_TASK - HandshakeProtocol needs the results of delegated task
+ */
+ public final static int NEED_TASK = 4;
+
+ /**
+ * Current handshake status
+ */
+ protected int status = NOT_HANDSHAKING;
+
+ /**
+ * IO stream for income/outcome handshake data
+ */
+ protected HandshakeIODataStream io_stream = new HandshakeIODataStream();
+
+ /**
+ * SSL Record Protocol implementation.
+ */
+ protected SSLRecordProtocol recordProtocol;
+
+ /**
+ * SSLParameters suplied by SSLSocket or SSLEngine
+ */
+ protected SSLParameters parameters;
+
+ /**
+ * Delegated tasks for this handshake implementation
+ */
+ protected Vector delegatedTasks = new Vector();
+
+ /**
+ * Indicates non-blocking handshake
+ */
+ protected boolean nonBlocking;
+
+ /**
+ * Pending session
+ */
+ protected SSLSessionImpl session;
+
+ /**
+ * Sended and received handshake messages
+ */
+ protected ClientHello clientHello;
+ protected ServerHello serverHello;
+ protected CertificateMessage serverCert;
+ protected ServerKeyExchange serverKeyExchange;
+ protected CertificateRequest certificateRequest;
+ protected ServerHelloDone serverHelloDone;
+ protected CertificateMessage clientCert;
+ protected ClientKeyExchange clientKeyExchange;
+ protected CertificateVerify certificateVerify;
+ protected Finished clientFinished;
+ protected Finished serverFinished;
+
+ /**
+ * Indicates that change cipher spec message has been received
+ */
+ protected boolean changeCipherSpecReceived = false;
+
+ /**
+ * Indicates previous session resuming
+ */
+ protected boolean isResuming = false;
+
+ /**
+ * Premaster secret
+ */
+ protected byte[] preMasterSecret;
+
+ /**
+ * Exception occured in delegated task
+ */
+ protected Exception delegatedTaskErr;
+
+ // reference verify_data used to verify finished message
+ private byte[] verify_data = new byte[12];
+
+ // Encoding of "master secret" string: "master secret".getBytes()
+ private byte[] master_secret_bytes =
+ {109, 97, 115, 116, 101, 114, 32, 115, 101, 99, 114, 101, 116 };
+
+ // indicates whether protocol needs to send change cipher spec message
+ private boolean needSendCCSpec = false;
+
+ // indicates whether protocol needs to send change cipher spec message
+ protected boolean needSendHelloRequest = false;
+
+ /**
+ * SSLEngine owning this HandshakeProtocol
+ */
+ public SSLEngineImpl engineOwner;
+
+ /**
+ * SSLSocket owning this HandshakeProtocol
+ */
+ // BEGIN android-removed
+ // public SSLSocketImpl socketOwner;
+ // END android-removed
+
+ /**
+ * Creates HandshakeProtocol instance
+ * @param owner
+ */
+ protected HandshakeProtocol(Object owner) {
+ if (owner instanceof SSLEngineImpl) {
+ engineOwner = (SSLEngineImpl) owner;
+ nonBlocking = true;
+ this.parameters = (SSLParameters) engineOwner.sslParameters;
+ // BEGIN android-removed
+ // } else if (owner instanceof SSLSocketImpl) {
+ // socketOwner = (SSLSocketImpl) owner;
+ // nonBlocking = false;
+ // this.parameters = (SSLParameters) socketOwner.sslParameters;
+ // }
+ // END android-removed
+ // BEGIN android-added
+ }
+ // END android-added
+ }
+
+ /**
+ * Sets SSL Record Protocol
+ * @param recordProtocol
+ */
+ public void setRecordProtocol(SSLRecordProtocol recordProtocol) {
+ this.recordProtocol = recordProtocol;
+ }
+
+ /**
+ * Start session negotiation
+ * @param session
+ */
+ public abstract void start();
+
+ /**
+ * Stops the current session renegotiation process.
+ * Such functionality is needed when it is session renegotiation
+ * process and no_renegotiation alert message is received
+ * from another peer.
+ * @param session
+ */
+ protected void stop() {
+ clearMessages();
+ status = NOT_HANDSHAKING;
+ }
+
+ /**
+ * Returns handshake status
+ * @return
+ */
+ public SSLEngineResult.HandshakeStatus getStatus() {
+ if (io_stream.hasData() || needSendCCSpec ||
+ needSendHelloRequest || delegatedTaskErr != null) {
+ return SSLEngineResult.HandshakeStatus.NEED_WRAP;
+ }
+ if (!delegatedTasks.isEmpty()) {
+ return SSLEngineResult.HandshakeStatus.NEED_TASK;
+ }
+
+ switch (status) {
+ case HandshakeProtocol.NEED_UNWRAP:
+ return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
+ case HandshakeProtocol.FINISHED:
+ status = NOT_HANDSHAKING;
+ clearMessages();
+ return SSLEngineResult.HandshakeStatus.FINISHED;
+ default: // HandshakeProtocol.NOT_HANDSHAKING:
+ return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
+ }
+ }
+
+ /**
+ * Returns pending session
+ * @return session
+ */
+ public SSLSessionImpl getSession() {
+ return session;
+ }
+
+ protected void sendChangeCipherSpec() {
+ needSendCCSpec = true;
+ }
+
+ protected void sendHelloRequest() {
+ needSendHelloRequest = true;
+ }
+
+ /**
+ * Proceses inbound ChangeCipherSpec message
+ */
+ abstract void receiveChangeCipherSpec();
+
+ /**
+ * Creates and sends finished message
+ */
+ abstract void makeFinished();
+
+ /**
+ * Proceses inbound handshake messages
+ * @param bytes
+ */
+ public abstract void unwrap(byte[] bytes);
+
+ /**
+ * Processes SSLv2 Hello message
+ * @param bytes
+ */
+ public abstract void unwrapSSLv2(byte[] bytes);
+
+ /**
+ * Proceses outbound handshake messages
+ * @return
+ */
+ public byte[] wrap() {
+ if (delegatedTaskErr != null) {
+ // process error occured in delegated task
+ Exception e = delegatedTaskErr;
+ delegatedTaskErr = null;
+ fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+ "Error occured in delegated task:" + e.getMessage(), e);
+ }
+ if (io_stream.hasData()) {
+ return recordProtocol.wrap(ContentType.HANDSHAKE, io_stream);
+ } else if (needSendCCSpec) {
+ makeFinished();
+ needSendCCSpec = false;
+ return recordProtocol.getChangeCipherSpecMesage(getSession());
+ } else if (needSendHelloRequest) {
+ needSendHelloRequest = false;
+ return recordProtocol.wrap(ContentType.HANDSHAKE,
+ // hello request message
+ // (see TLS v 1 specification:
+ // http://www.ietf.org/rfc/rfc2246.txt)
+ new byte[] {0, 0, 0, 0}, 0, 4);
+ } else {
+ return null; // nothing to send;
+ }
+ }
+
+ /**
+ * Sends fatal alert, breaks execution
+ *
+ * @param description
+ */
+ protected void sendWarningAlert(byte description) {
+ recordProtocol.alert(AlertProtocol.WARNING, description);
+ }
+
+ /**
+ * Sends fatal alert, breaks execution
+ *
+ * @param description
+ * @param reason
+ */
+ protected void fatalAlert(byte description, String reason) {
+ throw new AlertException(description, new SSLHandshakeException(reason));
+ }
+
+ /**
+ * Sends fatal alert, breaks execution
+ *
+ * @param description
+ * @param reason
+ * @param cause
+ */
+ protected void fatalAlert(byte description, String reason, Exception cause) {
+ throw new AlertException(description, new SSLException(reason, cause));
+ }
+
+ /**
+ * Sends fatal alert, breaks execution
+ *
+ * @param description
+ * @param cause
+ */
+ protected void fatalAlert(byte description, SSLException cause) {
+ throw new AlertException(description, cause);
+ }
+
+ /**
+ * Computers reference TLS verify_data that is used to verify finished message
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS spec. 7.4.9. Finished</a>
+ * @param label
+ */
+ protected void computerReferenceVerifyDataTLS(String label) {
+ computerVerifyDataTLS(label, verify_data);
+ }
+
+ /**
+ * Computer TLS verify_data
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS spec. 7.4.9. Finished</a>
+ * @param label
+ * @param buf
+ */
+ protected void computerVerifyDataTLS(String label, byte[] buf) {
+ byte[] md5_digest = io_stream.getDigestMD5();
+ byte[] sha_digest = io_stream.getDigestSHA();
+
+ byte[] digest = new byte[md5_digest.length + sha_digest.length];
+ System.arraycopy(md5_digest, 0, digest, 0, md5_digest.length);
+ System.arraycopy(sha_digest, 0, digest, md5_digest.length,
+ sha_digest.length);
+ try {
+ PRF.computePRF(buf, session.master_secret,
+ label.getBytes(), digest);
+ } catch (GeneralSecurityException e) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR, "PRF error", e);
+ }
+ }
+
+ /**
+ * Computer reference SSLv3 verify_data that is used to verify finished message
+ * @see "SSLv3 spec. 7.6.9. Finished"
+ * @param label
+ */
+ protected void computerReferenceVerifyDataSSLv3(byte[] sender) {
+ verify_data = new byte[36];
+ computerVerifyDataSSLv3(sender, verify_data);
+ }
+
+ /**
+ * Computer SSLv3 verify_data
+ * @see "SSLv3 spec. 7.6.9. Finished"
+ * @param label
+ * @param buf
+ */
+ protected void computerVerifyDataSSLv3(byte[] sender, byte[] buf) {
+ MessageDigest md5;
+ MessageDigest sha;
+ try {
+ md5 = MessageDigest.getInstance("MD5");
+ sha = MessageDigest.getInstance("SHA-1");
+ } catch (Exception e) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR, "Could not initialize the Digest Algorithms.", e);
+ return;
+ }
+ try {
+ byte[] hanshake_messages = io_stream.getMessages();
+ md5.update(hanshake_messages);
+ md5.update(sender);
+ md5.update(session.master_secret);
+ byte[] b = md5.digest(SSLv3Constants.MD5pad1);
+ md5.update(session.master_secret);
+ md5.update(SSLv3Constants.MD5pad2);
+ System.arraycopy(md5.digest(b), 0, buf, 0, 16);
+
+ sha.update(hanshake_messages);
+ sha.update(sender);
+ sha.update(session.master_secret);
+ b = sha.digest(SSLv3Constants.SHApad1);
+ sha.update(session.master_secret);
+ sha.update(SSLv3Constants.SHApad2);
+ System.arraycopy(sha.digest(b), 0, buf, 16, 20);
+ } catch (Exception e) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e);
+
+ }
+ }
+
+ /**
+ * Verifies finished data
+ *
+ * @param data
+ * @param isServer
+ */
+ protected void verifyFinished(byte[] data) {
+ if (!Arrays.equals(verify_data, data)) {
+ fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "Incorrect FINISED");
+ }
+ }
+
+ /**
+ * Sends fatal alert "UNEXPECTED MESSAGE"
+ *
+ */
+ protected void unexpectedMessage() {
+ fatalAlert(AlertProtocol.UNEXPECTED_MESSAGE, "UNEXPECTED MESSAGE");
+ }
+
+ /**
+ * Writes message to HandshakeIODataStream
+ *
+ * @param message
+ */
+ public void send(Message message) {
+ io_stream.writeUint8(message.getType());
+ io_stream.writeUint24(message.length());
+ message.send(io_stream);
+ }
+
+ /**
+ * Computers master secret
+ *
+ */
+ public void computerMasterSecret() {
+ byte[] seed = new byte[64];
+ System.arraycopy(clientHello.getRandom(), 0, seed, 0, 32);
+ System.arraycopy(serverHello.getRandom(), 0, seed, 32, 32);
+ session.master_secret = new byte[48];
+ if (serverHello.server_version[1] == 1) { // TLSv1
+ try {
+ PRF.computePRF(session.master_secret, preMasterSecret,
+ master_secret_bytes, seed);
+ } catch (GeneralSecurityException e) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR, "PRF error", e);
+ }
+ } else { // SSL3.0
+ PRF.computePRF_SSLv3(session.master_secret, preMasterSecret, seed);
+ }
+
+ //delete preMasterSecret from memory
+ Arrays.fill(preMasterSecret, (byte)0);
+ preMasterSecret = null;
+ }
+
+ /**
+ * Returns a delegated task.
+ * @return Delegated task or null
+ */
+ public Runnable getTask() {
+ if (delegatedTasks.isEmpty()) {
+ return null;
+ } else {
+ Runnable task = (Runnable)delegatedTasks.firstElement();
+ delegatedTasks.remove(0);
+ return task;
+ }
+ }
+
+ /**
+ *
+ * Clears previously sended and received handshake messages
+ */
+ protected void clearMessages() {
+ io_stream.clearBuffer();
+ clientHello = null;
+ serverHello = null;
+ serverCert = null;
+ serverKeyExchange = null;
+ certificateRequest = null;
+ serverHelloDone = null;
+ clientCert = null;
+ clientKeyExchange = null;
+ certificateVerify = null;
+ clientFinished = null;
+ serverFinished = null;
+ }
+
+ /**
+ * Returns RSA key length
+ * @param pk
+ * @return
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeySpecException
+ */
+ protected static int getRSAKeyLength(PublicKey pk)
+ throws NoSuchAlgorithmException, InvalidKeySpecException {
+
+ BigInteger mod;
+ if (pk instanceof RSAKey) {
+ mod = ((RSAKey) pk).getModulus();
+ } else {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ mod = ((RSAPublicKeySpec) kf.getKeySpec(pk, RSAPublicKeySpec.class))
+ .getModulus();
+ }
+ return mod.bitLength();
+ }
+
+ /**
+ * Shutdownes the protocol. It will be impossiblke to use the instance
+ * after the calling of this method.
+ */
+ protected void shutdown() {
+ clearMessages();
+ session = null;
+ preMasterSecret = null;
+ delegatedTasks.clear();
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HelloRequest.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HelloRequest.java
new file mode 100644
index 0000000..2ce4061
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HelloRequest.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris Kuznetsov
+* @version $Revision$
+*/
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.Message;
+import org.apache.harmony.xnet.provider.jsse.Handshake;
+import org.apache.harmony.xnet.provider.jsse.HandshakeIODataStream;
+
+import java.io.IOException;
+
+/**
+ *
+ * Represents Hello Request message
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.1.1.
+ * Hello request</a>
+ *
+ */
+public class HelloRequest extends Message {
+
+ /**
+ * Creates outbound message
+ *
+ */
+ public HelloRequest() {
+ }
+
+ /**
+ * Creates inbound message
+ * @param in
+ * @param length
+ * @throws IOException
+ */
+ public HelloRequest(HandshakeIODataStream in, int length)
+ throws IOException {
+ if (length != 0) {
+ fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect HelloRequest");
+ }
+ }
+
+ /**
+ * Sends message
+ * @param out
+ */
+ public void send(HandshakeIODataStream out) {
+ }
+
+ public int length() {
+ return 0;
+ }
+
+ /**
+ * Returns message type
+ * @return
+ */
+ public int getType() {
+ return Handshake.HELLO_REQUEST;
+ }
+
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java
new file mode 100644
index 0000000..25b027e
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.security.AccessController;
+import java.security.Provider;
+
+/**
+ * JSSE Provider implementation.
+ *
+ * This implementation is based on TLS v 1.0 and SSL v3 protocol specifications.
+ *
+ * <ul>
+ * <li><a href="http://www.ietf.org/rfc/rfc2246.txt">TLS v 1.0 Protocol
+ * specification</a></li>
+ * <li><a href="http://wp.netscape.com/eng/ssl3">SSL v3 Protocol
+ * specification</a></li>
+ * </ul>
+ *
+ * Provider implementation supports the following cipher suites:
+ * TLS_NULL_WITH_NULL_NULL
+ * TLS_RSA_WITH_NULL_MD5
+ * TLS_RSA_WITH_NULL_SHA
+ * TLS_RSA_EXPORT_WITH_RC4_40_MD5
+ * TLS_RSA_WITH_RC4_128_MD5
+ * TLS_RSA_WITH_RC4_128_SHA
+ * TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5
+ * TLS_RSA_WITH_IDEA_CBC_SHA
+ * TLS_RSA_EXPORT_WITH_DES40_CBC_SHA
+ * TLS_RSA_WITH_DES_CBC_SHA
+ * TLS_RSA_WITH_3DES_EDE_CBC_SHA
+ * TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA
+ * TLS_DH_DSS_WITH_DES_CBC_SHA
+ * TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA
+ * TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA
+ * TLS_DH_RSA_WITH_DES_CBC_SHA
+ * TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA
+ * TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
+ * TLS_DHE_DSS_WITH_DES_CBC_SHA
+ * TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
+ * TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
+ * TLS_DHE_RSA_WITH_DES_CBC_SHA
+ * TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
+ * TLS_DH_anon_EXPORT_WITH_RC4_40_MD5
+ * TLS_DH_anon_WITH_RC4_128_MD5
+ * TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA
+ * TLS_DH_anon_WITH_DES_CBC_SHA
+ * TLS_DH_anon_WITH_3DES_EDE_CBC_SHA
+ *
+ * The real set of availible cipher suites depends on set of availible
+ * crypto algorithms. These algorithms must be provided by some crypto
+ * provider.
+ *
+ * The following cipher algorithms are used by different cipher suites:
+ * IDEA/CBC/NoPadding
+ * RC2/CBC/NoPadding
+ * RC4
+ * DES/CBC/NoPadding
+ * DES/CBC/NoPadding
+ * DESede/CBC/NoPadding
+ *
+ * Also the current JSSE provider implementation uses the following
+ * crypto algorithms:
+ *
+ * Algorithms that MUST be provided by crypto provider:
+ * Mac HmacMD5
+ * Mac HmacSHA1
+ * MessageDigest MD5
+ * MessageDigest SHA-1
+ * CertificateFactory X509
+ *
+ * The cipher suites with RSA key exchange may also require:
+ * Cipher RSA
+ * KeyPairGenerator RSA
+ * KeyFactory RSA
+ *
+ * The cipher suites with DH key exchange may also require:
+ * Signature NONEwithDSA
+ * KeyPairGenerator DiffieHellman or DH
+ * KeyFactory DiffieHellman or DH
+ * KeyAgreement DiffieHellman or DH
+ * KeyPairGenerator DiffieHellman or DH
+ *
+ * Trust manager implementation requires:
+ * CertPathValidator PKIX
+ * CertificateFactory X509
+ *
+ */
+public final class JSSEProvider extends Provider {
+
+ public JSSEProvider() {
+ super("HarmonyJSSE", 1.0, "Harmony JSSE Provider");
+ AccessController.doPrivileged(new java.security.PrivilegedAction<Void>() {
+ public Void run() {
+ put("SSLContext.TLS",
+ "org.apache.harmony.xnet.provider.jsse.SSLContextImpl");
+ put("Alg.Alias.SSLContext.TLSv1", "TLS");
+ put("KeyManagerFactory.X509",
+ "org.apache.harmony.xnet.provider.jsse.KeyManagerFactoryImpl");
+ put("TrustManagerFactory.X509",
+ "org.apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl");
+ return null;
+ }
+ });
+ }
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java
new file mode 100644
index 0000000..1daf80c
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactorySpi;
+import javax.net.ssl.ManagerFactoryParameters;
+
+/**
+ * KeyManagerFactory implementation.
+ * @see javax.net.ssl.KeyManagerFactorySpi
+ */
+public class KeyManagerFactoryImpl extends KeyManagerFactorySpi {
+
+ // source of key material
+ private KeyStore keyStore;
+
+ //password
+ private char[] pwd;
+
+ /**
+ * @see javax.net.ssl.KeyManagerFactorySpi#engineInit(KeyStore ks, char[]
+ * password)
+ */
+ public void engineInit(KeyStore ks, char[] password)
+ throws KeyStoreException, NoSuchAlgorithmException,
+ UnrecoverableKeyException {
+ if (ks != null) {
+ keyStore = ks;
+ if (password != null) {
+ pwd = (char[]) password.clone();
+ } else {
+ pwd = new char[0];
+ }
+ } else {
+ keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ String keyStoreName = AccessController
+ .doPrivileged(new java.security.PrivilegedAction<String>() {
+ public String run() {
+ return System.getProperty("javax.net.ssl.keyStore");
+ }
+ });
+ String keyStorePwd = null;
+ if (keyStoreName == null || keyStoreName.equalsIgnoreCase("NONE")
+ || keyStoreName.length() == 0) {
+ try {
+ keyStore.load(null, null);
+ } catch (IOException e) {
+ throw new KeyStoreException(e);
+ } catch (CertificateException e) {
+ throw new KeyStoreException(e);
+ }
+ } else {
+ keyStorePwd = AccessController
+ .doPrivileged(new java.security.PrivilegedAction<String>() {
+ public String run() {
+ return System
+ .getProperty("javax.net.ssl.keyStorePassword");
+ }
+ });
+ if (keyStorePwd == null) {
+ pwd = new char[0];
+ } else {
+ pwd = keyStorePwd.toCharArray();
+ }
+ try {
+ keyStore.load(new FileInputStream(new File(keyStoreName)),
+ pwd);
+
+ } catch (FileNotFoundException e) {
+ throw new KeyStoreException(e);
+ } catch (IOException e) {
+ throw new KeyStoreException(e);
+ } catch (CertificateException e) {
+ throw new KeyStoreException(e);
+ }
+ }
+
+ }
+
+ }
+
+ /**
+ * @see javax.net.ssl.KeyManagerFactorySpi#engineInit(ManagerFactoryParameters
+ * spec)
+ */
+ public void engineInit(ManagerFactoryParameters spec)
+ throws InvalidAlgorithmParameterException {
+ throw new InvalidAlgorithmParameterException(
+ "ManagerFactoryParameters not supported");
+
+ }
+
+ /**
+ * @see javax.net.ssl.KeyManagerFactorySpi#engineGetKeyManagers()
+ */
+ public KeyManager[] engineGetKeyManagers() {
+ if (keyStore == null) {
+ throw new IllegalStateException("KeyManagerFactory is not initialized");
+ }
+ return new KeyManager[] { new KeyManagerImpl(keyStore, pwd) };
+ }
+
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerImpl.java
new file mode 100644
index 0000000..092d92d
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerImpl.java
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.net.Socket;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.UnrecoverableEntryException;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * KeyManager implementation.
+ * This implementation uses hashed key store information.
+ * It works faster than retrieving all of the data from the key store.
+ * Any key store changes, that happen after key manager was created, have no effect.
+ * The implementation does not use peer information (host, port)
+ * that may be obtained from socket or engine.
+ *
+ * @see javax.net.ssl.KeyManager
+ *
+ */
+public class KeyManagerImpl extends X509ExtendedKeyManager {
+
+ // hashed key store information
+ private final Hashtable hash = new Hashtable();
+
+ /**
+ * Creates Key manager
+ *
+ * @param keyStore
+ * @param pwd
+ */
+ public KeyManagerImpl(KeyStore keyStore, char[] pwd) {
+ String alias;
+ KeyStore.PrivateKeyEntry entry;
+ Enumeration aliases;
+ try {
+ aliases = keyStore.aliases();
+ } catch (KeyStoreException e) {
+ return;
+ }
+ for (; aliases.hasMoreElements();) {
+ alias = (String) aliases.nextElement();
+ try {
+ if (keyStore.entryInstanceOf(alias,
+ KeyStore.PrivateKeyEntry.class)) {
+ entry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias,
+ new KeyStore.PasswordProtection(pwd));
+ hash.put(alias, entry);
+ }
+ } catch (KeyStoreException e) {
+ continue;
+ } catch (UnrecoverableEntryException e) {
+ continue;
+ } catch (NoSuchAlgorithmException e) {
+ continue;
+ }
+ }
+
+ }
+
+ /**
+ * @see javax.net.ssl.X509ExtendedKeyManager#chooseClientAlias(String[]
+ * keyType, Principal[] issuers, Socket socket)
+ */
+ public String chooseClientAlias(String[] keyType, Principal[] issuers,
+ Socket socket) {
+ String[] al = chooseAlias(keyType, issuers);
+ if (al != null) {
+ return al[0];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @see javax.net.ssl.X509ExtendedKeyManager#chooseServerAlias(String
+ * keyType, Principal[] issuers, Socket socket)
+ */
+ public String chooseServerAlias(String keyType, Principal[] issuers,
+ Socket socket) {
+ String[] al = chooseAlias(new String[] { keyType }, issuers);
+ if (al != null) {
+ return al[0];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @see javax.net.ssl.X509ExtendedKeyManager#getCertificateChain(String
+ * alias)
+ */
+ public X509Certificate[] getCertificateChain(String alias) {
+ if (hash.containsKey(alias)) {
+ Certificate[] certs = ((KeyStore.PrivateKeyEntry) hash.get(alias))
+ .getCertificateChain();
+ if (certs[0] instanceof X509Certificate) {
+ X509Certificate[] xcerts = new X509Certificate[certs.length];
+ for (int i = 0; i < certs.length; i++) {
+ xcerts[i] = (X509Certificate) certs[i];
+ }
+ return xcerts;
+ }
+ }
+ return null;
+
+ }
+
+ /**
+ * @see javax.net.ssl.X509ExtendedKeyManager#getClientAliases(String
+ * keyType, Principal[] issuers)
+ */
+ public String[] getClientAliases(String keyType, Principal[] issuers) {
+ return chooseAlias(new String[] { keyType }, issuers);
+ }
+
+ /**
+ * @see javax.net.ssl.X509ExtendedKeyManager#getServerAliases(String
+ * keyType, Principal[] issuers)
+ */
+ public String[] getServerAliases(String keyType, Principal[] issuers) {
+ return chooseAlias(new String[] { keyType }, issuers);
+ }
+
+ /**
+ * @see javax.net.ssl.X509ExtendedKeyManager#getPrivateKey(String alias)
+ */
+ public PrivateKey getPrivateKey(String alias) {
+ if (hash.containsKey(alias)) {
+ return ((KeyStore.PrivateKeyEntry) hash.get(alias)).getPrivateKey();
+ }
+ return null;
+ }
+
+ /**
+ * @see javax.net.ssl.X509ExtendedKeyManager#chooseEngineClientAlias(String[]
+ * keyType, Principal[] issuers, SSLEngine engine)
+ */
+ public String chooseEngineClientAlias(String[] keyType,
+ Principal[] issuers, SSLEngine engine) {
+ String[] al = chooseAlias(keyType, issuers);
+ if (al != null) {
+ return al[0];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @see javax.net.ssl.X509ExtendedKeyManager#chooseEngineServerAlias(String
+ * keyType, Principal[] issuers, SSLEngine engine)
+ */
+ public String chooseEngineServerAlias(String keyType, Principal[] issuers,
+ SSLEngine engine) {
+ String[] al = chooseAlias(new String[] { keyType }, issuers);
+ if (al != null) {
+ return al[0];
+ } else {
+ return null;
+ }
+ }
+
+ private String[] chooseAlias(String[] keyType, Principal[] issuers) {
+ String alias;
+ KeyStore.PrivateKeyEntry entry;
+
+ if (keyType == null || keyType.length == 0) {
+ return null;
+ }
+ Vector found = new Vector();
+ int count = 0;
+ for (Enumeration aliases = hash.keys(); aliases.hasMoreElements();) {
+ alias = (String) aliases.nextElement();
+ entry = (KeyStore.PrivateKeyEntry) hash.get(alias);
+ Certificate[] certs = entry.getCertificateChain();
+ String alg = certs[0].getPublicKey().getAlgorithm();
+ for (int i = 0; i < keyType.length; i++) {
+ if (alg.equals(keyType[i])) {
+ if (issuers != null && issuers.length != 0) {
+ // check that certificate was issued by specified issuer
+ loop: for (int ii = 0; ii < certs.length; ii++) {
+ if (certs[ii] instanceof X509Certificate) {
+ X500Principal issuer = ((X509Certificate) certs[ii])
+ .getIssuerX500Principal();
+ for (int iii = 0; iii < issuers.length; iii++) {
+ if (issuer.equals(issuers[iii])) {
+ found.add(alias);
+ count++;
+ break loop;
+ }
+ }
+ }
+
+ }
+ } else {
+ found.add(alias);
+ count++;
+ }
+ }
+ }
+ }
+ if (count > 0) {
+ String[] result = new String[count];
+ found.toArray(result);
+ return result;
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
new file mode 100644
index 0000000..5b7ba2c
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.PrintStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * This class provides debug logging for JSSE provider implementation
+ * TODO: Use java.util.logging
+ */
+public class Logger {
+
+ public static class Stream extends PrintStream {
+ private final String prefix;
+ private static int indent = 0;
+
+ public Stream(String name) {
+ super(System.err);
+ prefix = name + "["+Thread.currentThread().getName()+"] ";
+ }
+
+ public void print(String msg) {
+ for (int i=0; i<indent; i++) {
+ super.print(" ");
+ }
+ super.print(msg);
+ }
+
+ public void newIndent() {
+ indent ++;
+ }
+
+ public void endIndent() {
+ indent --;
+ }
+
+ public void println(String msg) {
+ print(prefix);
+ super.println(msg);
+ }
+
+ public void print(byte[] data) {
+ printAsHex(16, " ", "", data, 0, data.length);
+ }
+
+ public void print(byte[] data, int offset, int len) {
+ printAsHex(16, " ", "", data, offset, len);
+ }
+
+ public void printAsHex(int perLine,
+ String prefix,
+ String delimiter,
+ byte[] data) {
+ printAsHex(perLine, prefix, delimiter, data, 0, data.length);
+ }
+
+ public void printAsHex(int perLine,
+ String prefix,
+ String delimiter,
+ byte[] data, int offset, int len) {
+ String line = "";
+ for (int i=0; i<len; i++) {
+ String tail =
+ Integer.toHexString(0x00ff & data[i+offset]).toUpperCase();
+ if (tail.length() == 1) {
+ tail = "0" + tail;
+ }
+ line += prefix + tail + delimiter;
+
+ if (((i+1)%perLine) == 0) {
+ super.println(line);
+ line = "";
+ }
+ }
+ super.println(line);
+ }
+ }
+
+ private static String[] names;
+
+ static {
+ try {
+ names = AccessController
+ .doPrivileged(new PrivilegedAction<String[]>() {
+ public String[] run() {
+ return System.getProperty("jsse", "").split(",");
+ }
+ });
+ } catch (Exception e) {
+ names = new String[0];
+ }
+ }
+
+ public static Stream getStream(String name) {
+ for (int i=0; i<names.length; i++) {
+ if (names[i].equals(name)) {
+ return new Stream(name);
+ }
+ }
+ return null;
+ }
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Message.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Message.java
new file mode 100644
index 0000000..cf99d6e
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Message.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris Kuznetsov
+* @version $Revision$
+*/
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.AlertException;
+
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+
+/**
+ *
+ * Base class for handshake messages
+ */
+public abstract class Message {
+
+ /*
+ * Message length
+ */
+ protected int length;
+
+ /**
+ * Returns message type
+ * @return
+ */
+ abstract int getType();
+
+ /**
+ * Returns message length
+ * @return
+ */
+ public int length() {
+ return length;
+ }
+
+ /**
+ * Sends message
+ * @param out
+ */
+ abstract void send(HandshakeIODataStream out);
+
+ /**
+ * Sends fatal alert
+ * @param description
+ * @param reason
+ */
+ protected void fatalAlert(byte description, String reason) {
+ throw new AlertException(description, new SSLHandshakeException(reason));
+ }
+
+ /**
+ * Sends fatal alert
+ * @param description
+ * @param reason
+ * @param cause
+ */
+ protected void fatalAlert(byte description, String reason, Throwable cause) {
+ throw new AlertException(description, new SSLException(reason, cause));
+ }
+}
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
new file mode 100644
index 0000000..508df3a
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+/**
+ * Provides the Java side of our JNI glue for OpenSSL. Currently only hashing
+ * and verifying are covered. Is expected to grow over time. Also needs to move
+ * into libcore/openssl at some point.
+ */
+public class NativeCrypto {
+
+ static {
+ // Need to ensure that OpenSSL initialization is done exactly once.
+ // This can be cleaned up later, when all OpenSSL glue moves into its
+ // own libcore module. Make it run, make it nice.
+ OpenSSLSocketImpl.class.getClass();
+ }
+
+ // --- DSA/RSA public/private key handling functions -----------------------
+
+ public static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g, byte[] priv_key, byte[] pub_key);
+
+ public static native int EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q);
+
+ public static native void EVP_PKEY_free(int pkey);
+
+ // --- RSA public/private key handling functions ---------------------------
+
+// public static native int rsaCreatePublicKey(byte[] n, byte[] e);
+//
+// public static native int rsaCreatePrivateKey(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q);
+//
+// public static native void rsaDestroyKey(int rsa);
+
+ // --- DSA public/private key handling functions ---------------------------
+
+// public static native int dsaCreatePublicKey(byte[] p, byte[] q, byte[] g, byte[] pub_key);
+//
+// public static native int dsaCreatePrivateKey(byte[] p, byte[] q, byte[] g, byte[] priv_key, byte[] pub_key);
+//
+// public static native void dsaDestroyKey(int dsa);
+
+ // --- RSA public/private key handling functions ---------------------------
+
+// public static native int rsaCreatePublicKey(byte[] n, byte[] e);
+//
+// public static native int rsaCreatePrivateKey(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q);
+//
+// public static native void rsaDestroyKey(int rsa);
+
+ // --- General context handling functions (despite the names) --------------
+
+ public static native int EVP_new();
+
+ public static native void EVP_free(int ctx);
+
+ // --- Digest handling functions -------------------------------------------
+
+ public static native void EVP_DigestInit(int ctx, String algorithm);
+
+ public static native void EVP_DigestUpdate(int ctx, byte[] buffer, int offset, int length);
+
+ public static native int EVP_DigestFinal(int ctx, byte[] hash, int offset);
+
+ public static native int EVP_DigestSize(int ctx);
+
+ public static native int EVP_DigestBlockSize(int ctx);
+
+ // --- Signature handling functions ----------------------------------------
+
+ public static native void EVP_VerifyInit(int ctx, String algorithm);
+
+ public static native void EVP_VerifyUpdate(int ctx, byte[] buffer, int offset, int length);
+
+ public static native int EVP_VerifyFinal(int ctx, byte[] signature, int offset, int length, int key);
+
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigest.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigest.java
new file mode 100644
index 0000000..f8067df
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.bouncycastle.crypto.ExtendedDigest;
+
+/**
+ * Implements the BouncyCastle Digest interface using OpenSSL's EVP API.
+ */
+public class OpenSSLMessageDigest implements ExtendedDigest {
+
+ /**
+ * Holds the name of the hashing algorithm, e.g. "SHA-1";
+ */
+ private String algorithm;
+
+ /**
+ * Holds a pointer to the native message digest context.
+ */
+ private int ctx;
+
+ /**
+ * Holds a dummy buffer for writing single bytes to the digest.
+ */
+ private byte[] singleByte = new byte[1];
+
+ /**
+ * Creates a new OpenSSLMessageDigest instance for the given algorithm
+ * name.
+ *
+ * @param algorithm The name of the algorithm, e.g. "SHA1".
+ *
+ * @return The new OpenSSLMessageDigest instance.
+ *
+ * @throws RuntimeException In case of problems.
+ */
+ public static OpenSSLMessageDigest getInstance(String algorithm) {
+ return new OpenSSLMessageDigest(algorithm);
+ }
+
+ /**
+ * Creates a new OpenSSLMessageDigest instance for the given algorithm
+ * name.
+ *
+ * @param algorithm The name of the algorithm, e.g. "SHA1".
+ */
+ private OpenSSLMessageDigest(String algorithm) {
+ this.algorithm = algorithm;
+ ctx = NativeCrypto.EVP_new();
+ try {
+ NativeCrypto.EVP_DigestInit(ctx, algorithm.replace("-", "").toLowerCase());
+ } catch (Exception ex) {
+ throw new RuntimeException(ex.getMessage() + " (" + algorithm + ")");
+ }
+ }
+
+ public int doFinal(byte[] out, int outOff) {
+ int i = NativeCrypto.EVP_DigestFinal(ctx, out, outOff);
+ reset();
+ return i;
+ }
+
+ public String getAlgorithmName() {
+ return algorithm;
+ }
+
+ public int getDigestSize() {
+ return NativeCrypto.EVP_DigestSize(ctx);
+ }
+
+ public int getByteLength() {
+ return NativeCrypto.EVP_DigestBlockSize(ctx);
+ }
+
+ public void reset() {
+ NativeCrypto.EVP_DigestInit(ctx, algorithm.replace("-", "").toLowerCase());
+ }
+
+ public void update(byte in) {
+ singleByte[0] = in;
+ NativeCrypto.EVP_DigestUpdate(ctx, singleByte, 0, 1);
+ }
+
+ public void update(byte[] in, int inOff, int len) {
+ NativeCrypto.EVP_DigestUpdate(ctx, in, inOff, len);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ NativeCrypto.EVP_free(ctx);
+ }
+
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigestJDK.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigestJDK.java
new file mode 100644
index 0000000..b00c5dd
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigestJDK.java
@@ -0,0 +1,82 @@
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Implements the JDK MessageDigest interface using OpenSSL's EVP API.
+ */
+public class OpenSSLMessageDigestJDK extends MessageDigest {
+
+ /**
+ * Holds a pointer to the native message digest context.
+ */
+ private int ctx;
+
+ /**
+ * Holds a dummy buffer for writing single bytes to the digest.
+ */
+ private byte[] singleByte = new byte[1];
+
+ /**
+ * Creates a new OpenSSLMessageDigestJDK instance for the given algorithm
+ * name.
+ *
+ * @param algorithm The name of the algorithm, e.g. "SHA1".
+ *
+ * @return The new OpenSSLMessageDigestJDK instance.
+ *
+ * @throws RuntimeException In case of problems.
+ */
+ public static OpenSSLMessageDigestJDK getInstance(String algorithm) throws NoSuchAlgorithmException{
+ return new OpenSSLMessageDigestJDK(algorithm);
+ }
+
+ /**
+ * Creates a new OpenSSLMessageDigest instance for the given algorithm
+ * name.
+ *
+ * @param algorithm The name of the algorithm, e.g. "SHA1".
+ */
+ private OpenSSLMessageDigestJDK(String algorithm) throws NoSuchAlgorithmException {
+ super(algorithm);
+
+ ctx = NativeCrypto.EVP_new();
+ try {
+ NativeCrypto.EVP_DigestInit(ctx, getAlgorithm().replace("-", "").toLowerCase());
+ } catch (Exception ex) {
+ throw new NoSuchAlgorithmException(ex.getMessage() + " (" + algorithm + ")");
+ }
+ }
+
+ @Override
+ protected byte[] engineDigest() {
+ byte[] result = new byte[NativeCrypto.EVP_DigestSize(ctx)];
+ NativeCrypto.EVP_DigestFinal(ctx, result, 0);
+ engineReset();
+ return result;
+ }
+
+ @Override
+ protected void engineReset() {
+ NativeCrypto.EVP_DigestInit(ctx, getAlgorithm().replace("-", "").toLowerCase());
+ }
+
+ @Override
+ protected void engineUpdate(byte input) {
+ singleByte[0] = input;
+ engineUpdate(singleByte, 0, 1);
+ }
+
+ @Override
+ protected void engineUpdate(byte[] input, int offset, int len) {
+ NativeCrypto.EVP_DigestUpdate(ctx, input, offset, len);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ NativeCrypto.EVP_free(ctx);
+ }
+
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketFactoryImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketFactoryImpl.java
new file mode 100644
index 0000000..970f5dc
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketFactoryImpl.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.security.KeyManagementException;
+
+public class OpenSSLServerSocketFactoryImpl extends javax.net.ssl.SSLServerSocketFactory {
+
+ private SSLParameters sslParameters;
+ private IOException instantiationException;
+
+ public OpenSSLServerSocketFactoryImpl() {
+ super();
+ try {
+ this.sslParameters = SSLParameters.getDefault();
+ this.sslParameters.setUseClientMode(false);
+ } catch (KeyManagementException e) {
+ instantiationException =
+ new IOException("Delayed instantiation exception:");
+ instantiationException.initCause(e);
+ }
+ }
+
+ public OpenSSLServerSocketFactoryImpl(SSLParameters sslParameters) {
+ this.sslParameters = sslParameters;
+ }
+
+ public String[] getDefaultCipherSuites() {
+ // TODO There might be a better way to implement this...
+ return OpenSSLServerSocketImpl.nativegetsupportedciphersuites();
+ }
+
+ public String[] getSupportedCipherSuites() {
+ return OpenSSLServerSocketImpl.nativegetsupportedciphersuites();
+ }
+
+ public ServerSocket createServerSocket() throws IOException {
+ return new OpenSSLServerSocketImpl((SSLParameters) sslParameters.clone());
+ }
+
+ public ServerSocket createServerSocket(int port) throws IOException {
+ return new OpenSSLServerSocketImpl(port, (SSLParameters) sslParameters.clone());
+ }
+
+ public ServerSocket createServerSocket(int port, int backlog)
+ throws IOException {
+ return new OpenSSLServerSocketImpl(port, backlog, (SSLParameters) sslParameters.clone());
+ }
+
+ public ServerSocket createServerSocket(int port, int backlog,
+ InetAddress iAddress) throws IOException {
+ return new OpenSSLServerSocketImpl(port, backlog, iAddress, (SSLParameters) sslParameters.clone());
+ }
+}
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
new file mode 100644
index 0000000..4fc6e99
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+
+import org.bouncycastle.openssl.PEMWriter;
+
+/**
+ * OpenSSL-based implementation of server sockets.
+ *
+ * This class only supports SSLv3 and TLSv1. This should be documented elsewhere
+ * later, for example in the package.html or a separate reference document.
+ */
+public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
+ private int ssl_ctx;
+ private boolean client_mode = true;
+ private long ssl_op_no = 0x00000000L;
+ private SSLParameters sslParameters;
+ private static final String[] supportedProtocols = new String[] {
+ "SSLv3",
+ "TLSv1"
+ };
+
+ private native static void nativeinitstatic();
+
+ static {
+ nativeinitstatic();
+ }
+
+ private native void nativeinit(String privatekey, String certificate, byte[] seed);
+
+ /**
+ * Initialize the SSL server socket and set the certificates for the
+ * future handshaking.
+ */
+ private void init() throws IOException {
+ String alias = sslParameters.getKeyManager().chooseServerAlias("RSA", null, null);
+ if (alias == null) {
+ throw new IOException("No suitable certificates found");
+ }
+
+ PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias);
+ X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias);
+
+ ByteArrayOutputStream privateKeyOS = new ByteArrayOutputStream();
+ PEMWriter privateKeyPEMWriter = new PEMWriter(new OutputStreamWriter(privateKeyOS));
+ privateKeyPEMWriter.writeObject(privateKey);
+ privateKeyPEMWriter.close();
+
+ ByteArrayOutputStream certificateOS = new ByteArrayOutputStream();
+ PEMWriter certificateWriter = new PEMWriter(new OutputStreamWriter(certificateOS));
+
+ for (int i = 0; i < certificates.length; i++) {
+ certificateWriter.writeObject(certificates[i]);
+ }
+ certificateWriter.close();
+
+ nativeinit(privateKeyOS.toString(), certificateOS.toString(),
+ sslParameters.getSecureRandomMember() != null ?
+ sslParameters.getSecureRandomMember().generateSeed(1024) : null);
+ }
+
+ protected OpenSSLServerSocketImpl(SSLParameters sslParameters)
+ throws IOException {
+ super();
+ this.sslParameters = sslParameters;
+ init();
+ }
+
+ protected OpenSSLServerSocketImpl(int port, SSLParameters sslParameters)
+ throws IOException {
+ super(port);
+ this.sslParameters = sslParameters;
+ init();
+ }
+
+ protected OpenSSLServerSocketImpl(int port, int backlog, SSLParameters sslParameters)
+ throws IOException {
+ super(port, backlog);
+ this.sslParameters = sslParameters;
+ init();
+ }
+
+ protected OpenSSLServerSocketImpl(int port, int backlog, InetAddress iAddress, SSLParameters sslParameters)
+ throws IOException {
+ super(port, backlog, iAddress);
+ this.sslParameters = sslParameters;
+ init();
+ }
+
+ @Override
+ public boolean getEnableSessionCreation() {
+ return sslParameters.getEnableSessionCreation();
+ }
+
+ @Override
+ public void setEnableSessionCreation(boolean flag) {
+ sslParameters.setEnableSessionCreation(flag);
+ }
+
+ /**
+ * The names of the protocols' versions that may be used on this SSL
+ * connection.
+ * @return an array of protocols names
+ */
+ @Override
+ public String[] getSupportedProtocols() {
+ return supportedProtocols.clone();
+ }
+
+ /**
+ * See the OpenSSL ssl.h header file for more information.
+ */
+ static private long SSL_OP_NO_SSLv3 = 0x02000000L;
+ static private long SSL_OP_NO_TLSv1 = 0x04000000L;
+
+ /**
+ * The names of the protocols' versions that in use on this SSL connection.
+ *
+ * @return an array of protocols names
+ */
+ @Override
+ public String[] getEnabledProtocols() {
+ ArrayList<String> array = new ArrayList<String>();
+
+ if ((ssl_op_no & SSL_OP_NO_SSLv3) == 0x00000000L) {
+ array.add(supportedProtocols[1]);
+ }
+ if ((ssl_op_no & SSL_OP_NO_TLSv1) == 0x00000000L) {
+ array.add(supportedProtocols[2]);
+ }
+ return array.toArray(new String[array.size()]);
+ }
+
+ private native void nativesetenabledprotocols(long l);
+
+ /**
+ * This method enables the protocols' versions listed by
+ * getSupportedProtocols().
+ *
+ * @param protocols names of all the protocols to enable.
+ *
+ * @throws IllegalArgumentException when one or more of the names in the
+ * array are not supported, or when the array is null.
+ */
+ @Override
+ public void setEnabledProtocols(String[] protocols) {
+ if (protocols == null) {
+ throw new IllegalArgumentException("Provided parameter is null");
+ }
+
+ ssl_op_no = SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
+
+ for (int i = 0; i < protocols.length; i++) {
+ if (protocols[i].equals("SSLv3"))
+ ssl_op_no ^= SSL_OP_NO_SSLv3;
+ else if (protocols[i].equals("TLSv1"))
+ ssl_op_no ^= SSL_OP_NO_TLSv1;
+ else throw new IllegalArgumentException("Protocol " + protocols[i] +
+ " is not supported.");
+ }
+
+ nativesetenabledprotocols(ssl_op_no);
+ }
+
+ /**
+ * Gets all available ciphers from the current OpenSSL library.
+ * Needed by OpenSSLServerSocketFactory too.
+ */
+ static native String[] nativegetsupportedciphersuites();
+
+ @Override
+ public String[] getSupportedCipherSuites() {
+ return nativegetsupportedciphersuites();
+ }
+
+ /**
+ * Calls native OpenSSL functions to get the enabled ciphers.
+ */
+ private native String[] nativegetenabledciphersuites();
+
+ @Override
+ public String[] getEnabledCipherSuites() {
+ return nativegetenabledciphersuites();
+ }
+
+ /**
+ * Calls the SSL_CTX_set_cipher_list(...) OpenSSL function with the passed
+ * char array.
+ */
+ private native void nativesetenabledciphersuites(String controlString);
+
+ private boolean findSuite(String suite) {
+ String[] supportedCipherSuites = nativegetsupportedciphersuites();
+ for(int i = 0; i < supportedCipherSuites.length; i++)
+ if (supportedCipherSuites[i].equals(suite)) return true;
+ throw new IllegalArgumentException("Protocol " + suite +
+ " is not supported.");
+ }
+
+ /**
+ * This method enables the cipher suites listed by
+ * getSupportedCipherSuites().
+ *
+ * @param suites the names of all the cipher suites to enable
+ * @throws IllegalArgumentException when one or more of the ciphers in array
+ * suites are not supported, or when the array is null.
+ */
+ @Override
+ public void setEnabledCipherSuites(String[] suites) {
+ if (suites == null) {
+ throw new IllegalArgumentException("Provided parameter is null");
+ }
+ String controlString = "";
+ for (int i = 0; i < suites.length; i++) {
+ findSuite(suites[i]);
+ if (i == 0) controlString = suites[i];
+ else controlString += ":" + suites[i];
+ }
+ nativesetenabledciphersuites(controlString);
+ }
+
+ /**
+ * 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_CTX_set_verify(...) OpenSSL function with the passed int
+ * value.
+ */
+ private native void nativesetclientauth(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(value);
+ }
+
+ @Override
+ public boolean getWantClientAuth() {
+ return sslParameters.getWantClientAuth();
+ }
+
+ @Override
+ public void setWantClientAuth(boolean want) {
+ sslParameters.setWantClientAuth(want);
+ setClientAuth();
+ }
+
+ @Override
+ public boolean getNeedClientAuth() {
+ return sslParameters.getNeedClientAuth();
+ }
+
+ @Override
+ public void setNeedClientAuth(boolean need) {
+ sslParameters.setNeedClientAuth(need);
+ setClientAuth();
+ }
+
+ @Override
+ public void setUseClientMode(boolean mode) {
+ sslParameters.setUseClientMode(mode);
+ }
+
+ @Override
+ public boolean getUseClientMode() {
+ return sslParameters.getUseClientMode();
+ }
+
+ @Override
+ public Socket accept() throws IOException {
+ OpenSSLSocketImpl socket = new OpenSSLSocketImpl(sslParameters, ssl_op_no);
+ implAccept(socket);
+ socket.accept(ssl_ctx, client_mode);
+
+ return socket;
+ }
+
+ /**
+ * Removes OpenSSL objects from memory.
+ */
+ private native void nativefree();
+
+ /**
+ * Unbinds the port if the socket is open.
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ if (!isClosed()) close();
+ }
+
+ @Override
+ public synchronized void close() throws IOException {
+ nativefree();
+ 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
new file mode 100644
index 0000000..475d388
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.IOException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLPermission;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionBindingEvent;
+import javax.net.ssl.SSLSessionBindingListener;
+import javax.net.ssl.SSLSessionContext;
+import javax.security.cert.CertificateEncodingException;
+
+import org.apache.harmony.luni.util.TwoKeyHashMap;
+import org.apache.harmony.security.provider.cert.X509CertImpl;
+
+/**
+ * Implementation of the class OpenSSLSessionImpl
+ * based on OpenSSL. The JNI native interface for some methods
+ * of this this class are defined in the file:
+ * org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp
+ */
+public class OpenSSLSessionImpl implements SSLSession {
+
+ long lastAccessedTime = 0;
+ X509Certificate[] localCertificates;
+ X509Certificate[] peerCertificates;
+
+ private boolean isValid = true;
+ private TwoKeyHashMap values = new TwoKeyHashMap();
+ private javax.security.cert.X509Certificate[] peerCertificateChain;
+ protected int session;
+ private SSLParameters sslParameters;
+ private String peerHost;
+ private int peerPort;
+
+ /**
+ * Class constructor creates an SSL session context given the appropriate
+ * SSL parameters.
+ * @param sslParameters the SSL parameters like ciphers' suites etc.
+ * @param ssl the Identifier for SSL session
+ */
+ protected OpenSSLSessionImpl(int session, SSLParameters sslParameters, String peerHost, int peerPort) {
+ this.session = session;
+ this.sslParameters = sslParameters;
+ this.peerHost = peerHost;
+ this.peerPort = peerPort;
+ }
+
+ /**
+ * Returns the identifier of the actual OpenSSL session.
+ */
+ private native byte[] nativegetid();
+
+ /**
+ * Gets the identifier of the actual SSL session
+ * @return array of sessions' identifiers.
+ */
+ public byte[] getId() {
+ return nativegetid();
+ }
+
+ /**
+ * Gets the creation time of the OpenSSL session.
+ * @return the session's creation time in milli seconds since January
+ * 1st, 1970
+ */
+ private native long nativegetcreationtime();
+
+ /**
+ * Gets the creation time of the SSL session.
+ * @return the session's creation time in milli seconds since 12.00 PM,
+ * January 1st, 1970
+ */
+ public long getCreationTime(){
+ return nativegetcreationtime();
+ }
+
+ /**
+ * Gives the last time this concrete SSL session was accessed. Accessing
+ * here is to mean that a new connection with the same SSL context data was
+ * established.
+ *
+ * @return the session's accessing time in milli seconds since 12.00 PM,
+ * January 1st, 1970
+ */
+ public long getLastAccessedTime() {
+ if (lastAccessedTime == 0)
+ return nativegetcreationtime();
+ else
+ return lastAccessedTime;
+ }
+
+ /**
+ * Gives the largest buffer size for the application's data bound to this
+ * concrete SSL session.
+ * @return the largest buffer size
+ */
+ public int getApplicationBufferSize() {
+ return SSLRecordProtocol.MAX_DATA_LENGTH;
+ }
+
+ /**
+ * Gives the largest SSL/TLS packet size one can expect for this concrete
+ * SSL session.
+ * @return the largest packet size
+ */
+ public int getPacketBufferSize() {
+ return SSLRecordProtocol.MAX_SSL_PACKET_SIZE;
+ }
+
+ /**
+ * Gives the principal (subject) of this concrete SSL session used in the
+ * handshaking phase of the connection.
+ * @return a X509 certificate or null if no principal was defined
+ */
+ public Principal getLocalPrincipal() {
+ if (localCertificates != null && localCertificates.length > 0) {
+ return localCertificates[0].getSubjectX500Principal();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Gives the certificate(s) of the principal (subject) of this concrete SSL
+ * session used in the handshaking phase of the connection. The OpenSSL
+ * native method supports only RSA certificates.
+ * @return an array of certificates (the local one first and then eventually
+ * that of the certification authority) or null if no certificate
+ * 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;
+ }
+
+ /**
+ * Returns the X509 certificates of the peer in the PEM format.
+ */
+ private native byte[][] nativegetpeercertificates();
+
+ /**
+ * Gives the certificate(s) of the peer in this SSL session
+ * used in the handshaking phase of the connection.
+ * Please notice hat this method is superseded by
+ * <code>getPeerCertificates()</code>.
+ * @return an array of X509 certificates (the peer's one first and then
+ * eventually that of the certification authority) or null if no
+ * certificate were used during the SSL connection.
+ * @throws <code>SSLPeerUnverifiedcertificateException</code> if either a
+ * not X509 certificate was used (i.e. Kerberos certificates) or the
+ * peer could not be verified.
+ */
+ public javax.security.cert.X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
+ if (peerCertificateChain == null) {
+ try {
+ byte[][] bytes = nativegetpeercertificates();
+ if (bytes == null) throw new SSLPeerUnverifiedException("No certificate available");
+
+ peerCertificateChain = new javax.security.cert.X509Certificate[bytes.length];
+
+ for(int i = 0; i < bytes.length; i++) {
+ peerCertificateChain[i] = javax.security.cert.X509Certificate.getInstance(bytes[i]);
+ }
+
+ return peerCertificateChain;
+ } catch (javax.security.cert.CertificateException e) {
+ throw new SSLPeerUnverifiedException(e.getMessage());
+ }
+ } else {
+ return peerCertificateChain;
+ }
+ }
+
+ /**
+ * Gives the identitity of the peer in this SSL session
+ * determined via certificate(s).
+ * @return an array of X509 certificates (the peer's one first and then
+ * eventually that of the certification authority) or null if no
+ * certificate were used during the SSL connection.
+ * @throws <code>SSLPeerUnverifiedException</code> if either a not X509
+ * certificate was used (i.e. Kerberos certificates) or the peer
+ * could not be verified.
+ */
+ public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+ if (peerCertificates == null) {
+ if (peerCertificateChain == null) getPeerCertificateChain();
+ try {
+ if (peerCertificateChain.length == 0) return new X509Certificate[]{};
+
+ peerCertificates = new X509CertImpl[peerCertificateChain.length];
+ for(int i = 0; i < peerCertificates.length; i++) {
+ peerCertificates[i] = new X509CertImpl(peerCertificateChain[i].getEncoded());
+ }
+ return peerCertificates;
+ } catch (SSLPeerUnverifiedException e) {
+ return new X509Certificate[]{};
+ } catch (IOException e) {
+ return new X509Certificate[]{};
+ } catch (CertificateEncodingException e) {
+ return new X509Certificate[]{};
+ }
+ } else {
+ return peerCertificates;
+ }
+ }
+
+ /**
+ * The identity of the principal that was used by the peer during the SSL
+ * handshake phase is returned by this method.
+ * @return a X500Principal of the last certificate for X509-based
+ * cipher suites. If no principal was sent, then null is returned.
+ * @throws <code>SSLPeerUnverifiedException</code> if either a not X509
+ * certificate was used (i.e. Kerberos certificates) or the
+ * peer does not exist.
+ *
+ */
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ if (peerCertificates == null) {
+ throw new SSLPeerUnverifiedException("No peer certificate");
+ }
+ return peerCertificates[0].getSubjectX500Principal();
+ }
+
+ /**
+ * Returns via OpenSSL call the actual peer host name.
+ */
+ private native String nativegetpeerhost();
+
+ /**
+ * The peer's host name used in this SSL session is returned. It is the host
+ * name of the client for the server; and that of the server for the client.
+ * It is not a reliable way to get a fully qualified host name: it is mainly
+ * used internally to implement links for a temporary cache of SSL sessions.
+ *
+ * @return the host name of the peer, or null if no information is
+ * available.
+ *
+ */
+ public String getPeerHost() {
+ return peerHost;
+ //return nativegetpeerhost();
+ }
+
+ /**
+ * Returns via OpenSSL call the actual peer port number.
+ */
+ private native String nativegetpeerport();
+
+ /**
+ * Gives the peer's port number for the actual SSL session. It is the port
+ * number of the client for the server; and that of the server for the
+ * client. It is not a reliable way to get a peer's port number: it is
+ * mainly used internally to implement links for a temporary cache of SSL
+ * sessions.
+ * @return the peer's port number, or -1 if no one is available.
+ *
+ */
+ public int getPeerPort() {
+ return peerPort;
+ //return Integer.parseInt(nativegetpeerport());
+ }
+
+ /**
+ * Returns via OpenSSL call the actual cipher suite in use.
+ */
+ private native String nativegetciphersuite();
+
+ /**
+ * Gives back a string identifier of the crypto tools used in the actual SSL
+ * session. For example AES_256_WITH_MD5.
+ *
+ * @return an identifier for all the cryptographic algorithms used in the
+ * actual SSL session.
+ */
+ public String getCipherSuite() {
+ return nativegetciphersuite();
+ }
+
+ /**
+ * Returns via OpenSSL call the actual version of the SSL protocol.
+ */
+ private native String nativegetprotocol();
+
+ /**
+ * Gives back the standard version name of the SSL protocol used in all
+ * connections pertaining to this SSL session.
+ *
+ * @return the standard version name of the SSL protocol used in all
+ * connections pertaining to this SSL session.
+ *
+ */
+ public String getProtocol() {
+ return nativegetprotocol();
+ }
+
+ /**
+ * Gives back the context to which the actual SSL session is bound. A SSL
+ * context consists of (1) a possible delegate, (2) a provider and (3) a
+ * protocol. If the security manager is activated and one tries to access
+ * the SSL context an exception may be thrown if a
+ * <code>SSLPermission("getSSLSessionContext")</code>
+ * permission is not set.
+ * @return the SSL context used for this session, or null if it is
+ * unavailable.
+ */
+ public SSLSessionContext getSessionContext() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new SSLPermission("getSSLSessionContext"));
+ }
+ return sslParameters.getClientSessionContext();
+ }
+
+ /**
+ * Gives back a boolean flag signaling whether a SSL session is valid and
+ * available
+ * for resuming or joining or not.
+ * @return true if this session may be resumed.
+ */
+ public boolean isValid() {
+ SSLSessionContextImpl context = sslParameters.getClientSessionContext();
+ if (isValid
+ && context != null
+ && context.getSessionTimeout() != 0
+ && lastAccessedTime + context.getSessionTimeout() > System
+ .currentTimeMillis()) {
+ isValid = false;
+ }
+ return isValid;
+ }
+
+ /**
+ * It invalidates a SSL session forbidding any resumption.
+ */
+ public void invalidate() {
+ isValid = false;
+ }
+
+ /**
+ * Gives back the object which is bound to the the input parameter name.
+ * This name is a sort of link to the data of the SSL session's application
+ * layer, if any exists. The search for this link is monitored, as a matter
+ * of security, by the full machinery of the <code>AccessController</code>
+ * class.
+ *
+ * @param <code>String name</code> the name of the binding to find.
+ * @return the value bound to that name, or null if the binding does not
+ * exist.
+ * @throws <code>IllegalArgumentException</code> if the argument is null.
+ */
+ public Object getValue(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("Parameter is null");
+ }
+ return values.get(name, AccessController.getContext());
+ }
+
+ /**
+ * Gives back an array with the names (sort of links) of all the data
+ * objects of the application layer bound into the SSL session. The search
+ * for this link is monitored, as a matter of security, by the full
+ * machinery of the <code>AccessController</code> class.
+ *
+ * @return a non-null (possibly empty) array of names of the data objects
+ * bound to this SSL session.
+ */
+ public String[] getValueNames() {
+ Vector v = new Vector();
+ AccessControlContext current = AccessController.getContext();
+ AccessControlContext cont;
+ for (Iterator it = values.entrySet().iterator(); it.hasNext();) {
+ TwoKeyHashMap.Entry entry = (TwoKeyHashMap.Entry) it.next();
+ cont = (AccessControlContext) entry.getKey2();
+ if ((current == null && cont == null)
+ || (current != null && current.equals(cont))) {
+ v.add(entry.getKey1());
+ }
+ }
+ return (String[]) v.toArray(new String[0]);
+ }
+
+ /**
+ * A link (name) with the specified value object of the SSL session's
+ * application layer data is created or replaced. If the new (or existing)
+ * value object implements the <code>SSLSessionBindingListener</code>
+ * interface, that object will be notified in due course. These links-to
+ * -data bounds are monitored, as a matter of security, by the full
+ * machinery of the <code>AccessController</code> class.
+ *
+ * @param <code>String name</code> the name of the link (no null are
+ * accepted!)
+ * @param <code>Object value</code> data object that shall be bound to
+ * name.
+ * @throws <code>IllegalArgumentException</code> if one or both
+ * argument(s) is null.
+ */
+ public void putValue(String name, Object value) {
+ if (name == null || value == null) {
+ throw new IllegalArgumentException("Parameter is null");
+ }
+ Object old = values.put(name, AccessController.getContext(), value);
+ if (value instanceof SSLSessionBindingListener) {
+ ((SSLSessionBindingListener) value)
+ .valueBound(new SSLSessionBindingEvent(this, name));
+ }
+ if (old != null && old instanceof SSLSessionBindingListener) {
+ ((SSLSessionBindingListener) old)
+ .valueUnbound(new SSLSessionBindingEvent(this, name));
+ }
+ }
+
+ /**
+ * Removes a link (name) with the specified value object of the SSL
+ * session's application layer data. These links-to -data bounds are
+ * monitored, as a matter of security, by the full machinery of the
+ * <code>AccessController</code> class.
+ *
+ * @param <code>String name</code> the name of the link (no null are
+ * accepted!)
+ * @throws <code>IllegalArgumentException</code> if the argument is null.
+ */
+ public void removeValue(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("Parameter is null");
+ }
+ values.remove(name, AccessController.getContext());
+ }
+
+ private native void nativefree(int session);
+
+ /**
+ * Frees the OpenSSL session in the memory.
+ */
+ protected void finalize() {
+ nativefree(session);
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignature.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignature.java
new file mode 100644
index 0000000..472c9df
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignature.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.lang.reflect.Method;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.RSAPublicKey;
+
+/**
+ * Implements the JDK MessageDigest interface using OpenSSL's EVP API.
+ */
+public class OpenSSLSignature extends Signature {
+
+ /**
+ * Holds a pointer to the native message digest context.
+ */
+ private int ctx;
+
+ /**
+ * Holds a pointer to the native DSA key.
+ */
+ private int dsa;
+
+ /**
+ * Holds a pointer to the native RSA key.
+ */
+ private int rsa;
+
+ /**
+ * Holds the OpenSSL name of the algorithm (lower case, no dashes).
+ */
+ private String evpAlgorithm;
+
+ /**
+ * Holds a dummy buffer for writing single bytes to the digest.
+ */
+ private byte[] singleByte = new byte[1];
+
+ /**
+ * Creates a new OpenSSLSignature instance for the given algorithm name.
+ *
+ * @param algorithm The name of the algorithm, e.g. "SHA1".
+ *
+ * @return The new OpenSSLSignature instance.
+ *
+ * @throws RuntimeException In case of problems.
+ */
+ public static OpenSSLSignature getInstance(String algorithm) throws NoSuchAlgorithmException {
+ //log("OpenSSLSignature", "getInstance() invoked with " + algorithm);
+ return new OpenSSLSignature(algorithm);
+ }
+
+ /**
+ * Creates a new OpenSSLSignature instance for the given algorithm name.
+ *
+ * @param algorithm The name of the algorithm, e.g. "SHA1".
+ */
+ private OpenSSLSignature(String algorithm) throws NoSuchAlgorithmException {
+ super(algorithm);
+
+ int i = algorithm.indexOf("with");
+ if (i == -1) {
+ throw new NoSuchAlgorithmException(algorithm);
+ }
+
+ // For the special combination of DSA and SHA1, we need to pass the
+ // algorithm name as a pair consisting of crypto algorithm and hash
+ // algorithm. For all other (RSA) cases, passing the hash algorithm
+ // alone is not only sufficient, but actually necessary. OpenSSL
+ // doesn't accept something like RSA-SHA1.
+ if ("1.3.14.3.2.26with1.2.840.10040.4.1".equals(algorithm)
+ || "SHA1withDSA".equals(algorithm)
+ || "SHAwithDSA".equals(algorithm)) {
+ evpAlgorithm = "DSA-SHA";
+ } else {
+ evpAlgorithm = algorithm.substring(0, i).replace("-", "").toUpperCase();
+ }
+
+ ctx = NativeCrypto.EVP_new();
+ }
+
+ @Override
+ protected void engineUpdate(byte input) {
+ singleByte[0] = input;
+ engineUpdate(singleByte, 0, 1);
+ }
+
+ @Override
+ protected void engineUpdate(byte[] input, int offset, int len) {
+ if (state == SIGN) {
+ throw new UnsupportedOperationException();
+ } else {
+ NativeCrypto.EVP_VerifyUpdate(ctx, input, offset, len);
+ }
+ }
+
+ @Override
+ protected Object engineGetParameter(String param) throws InvalidParameterException {
+ return null;
+ }
+
+ @Override
+ protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
+ //log("OpenSSLSignature", "engineInitVerify() invoked with " + publicKey.getClass().getCanonicalName());
+
+ if (publicKey instanceof DSAPublicKey) {
+ try {
+ DSAPublicKey dsaPublicKey = (DSAPublicKey)publicKey;
+ DSAParams dsaParams = dsaPublicKey.getParams();
+ dsa = NativeCrypto.EVP_PKEY_new_DSA(dsaParams.getP().toByteArray(),
+ dsaParams.getQ().toByteArray(), dsaParams.getG().toByteArray(),
+ dsaPublicKey.getY().toByteArray(), null);
+
+ } catch (Exception ex) {
+ throw new InvalidKeyException(ex.toString());
+ }
+ } else if (publicKey instanceof RSAPublicKey) {
+ try {
+ RSAPublicKey rsaPublicKey = (RSAPublicKey)publicKey;
+ rsa = NativeCrypto.EVP_PKEY_new_RSA(rsaPublicKey.getModulus().toByteArray(),
+ rsaPublicKey.getPublicExponent().toByteArray(), null, null, null);
+
+ } catch (Exception ex) {
+ throw new InvalidKeyException(ex.toString());
+ }
+ } else {
+ throw new InvalidKeyException("Need DSA or RSA public key");
+ }
+
+ try {
+ NativeCrypto.EVP_VerifyInit(ctx, evpAlgorithm);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @Override
+ protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
+ }
+
+ @Override
+ protected byte[] engineSign() throws SignatureException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+ int handle = (rsa != 0) ? rsa : dsa;
+
+ if (handle == 0) {
+ // This can't actually happen, but you never know...
+ throw new SignatureException("Need DSA or RSA public key");
+ }
+
+ try {
+ int result = NativeCrypto.EVP_VerifyFinal(ctx, sigBytes, 0, sigBytes.length, handle);
+ return result == 1;
+ } catch (Exception ex) {
+ throw new SignatureException(ex);
+ }
+
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+
+ if (dsa != 0) {
+ NativeCrypto.EVP_PKEY_free(dsa);
+ }
+
+ if (rsa != 0) {
+ NativeCrypto.EVP_PKEY_free(rsa);
+ }
+
+ if (ctx != 0) {
+ NativeCrypto.EVP_free(ctx);
+ }
+ }
+
+ // TODO Just for debugging purposes, remove later.
+ private static void log(String tag, String msg) {
+ try {
+ Class clazz = Class.forName("android.util.Log");
+ Method method = clazz.getMethod("d", new Class[] {
+ String.class, String.class
+ });
+ method.invoke(null, new Object[] {
+ tag, msg
+ });
+ } catch (Exception ex) {
+ // Silently ignore.
+ }
+ }
+
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketFactoryImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketFactoryImpl.java
new file mode 100644
index 0000000..aeb23b6
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketFactoryImpl.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+
+import org.apache.harmony.xnet.provider.jsse.SSLParameters;
+
+public class OpenSSLSocketFactoryImpl extends javax.net.ssl.SSLSocketFactory {
+
+ private SSLParameters sslParameters;
+ private IOException instantiationException;
+
+ public OpenSSLSocketFactoryImpl() {
+ super();
+ try {
+ sslParameters = SSLParameters.getDefault();
+ } catch (KeyManagementException e) {
+ instantiationException =
+ new IOException("Delayed instantiation exception:");
+ instantiationException.initCause(e);
+ }
+ }
+
+ public OpenSSLSocketFactoryImpl(SSLParameters sslParameters) {
+ super();
+ this.sslParameters = sslParameters;
+ }
+
+ public String[] getDefaultCipherSuites() {
+ // TODO There might be a better implementation for this...
+ return OpenSSLSocketImpl.nativegetsupportedciphersuites();
+ }
+
+ public String[] getSupportedCipherSuites() {
+ return OpenSSLSocketImpl.nativegetsupportedciphersuites();
+ }
+
+ public Socket createSocket() throws IOException {
+ if (instantiationException != null) {
+ throw instantiationException;
+ }
+ return new OpenSSLSocketImpl((SSLParameters) sslParameters.clone());
+ }
+
+ public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
+ return new OpenSSLSocketImpl(host, port, (SSLParameters) sslParameters.clone());
+ }
+
+ public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
+ throws IOException, UnknownHostException {
+ return new OpenSSLSocketImpl(host, port, localHost, localPort, (SSLParameters) sslParameters.clone());
+ }
+
+ public Socket createSocket(InetAddress host, int port) throws IOException {
+ return new OpenSSLSocketImpl(host, port, (SSLParameters) sslParameters.clone());
+ }
+
+ public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
+ throws IOException {
+ return new OpenSSLSocketImpl(address, port, localAddress, localPort, (SSLParameters) sslParameters.clone());
+ }
+
+ public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
+ return new OpenSSLSocketImplWrapper(s, host, port, autoClose, (SSLParameters) sslParameters.clone());
+ }
+}
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
new file mode 100644
index 0000000..1d38ca9
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
@@ -0,0 +1,1037 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLSession;
+
+import org.apache.harmony.security.provider.cert.X509CertImpl;
+import org.bouncycastle.openssl.PEMWriter;
+
+/**
+ * Implementation of the class OpenSSLSocketImpl
+ * based on OpenSSL. The JNI native interface for some methods
+ * of this this class are defined in the file:
+ * org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp
+ *
+ * This class only supports SSLv3 and TLSv1. This should be documented elsewhere
+ * later, for example in the package.html or a separate reference document.
+ */
+public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket {
+ private int ssl_ctx;
+ private int ssl;
+ private InputStream is;
+ private OutputStream os;
+ private Object handshakeLock = new Object();
+ private Object readLock = new Object();
+ private Object writeLock = new Object();
+ private SSLParameters sslParameters;
+ private OpenSSLSessionImpl sslSession;
+ private Socket socket;
+ private boolean autoClose;
+ private boolean handshakeStarted = false;
+ private ArrayList listeners;
+ private long ssl_op_no = 0x00000000L;
+ private int timeout = 0;
+ private InetSocketAddress address;
+
+ private static final String[] supportedProtocols = new String[] {
+ "SSLv3",
+ "TLSv1"
+ };
+
+ private static int instanceCount = 0;
+
+ public static int getInstanceCount() {
+ synchronized (OpenSSLSocketImpl.class) {
+ return instanceCount;
+ }
+ }
+
+ private static void updateInstanceCount(int amount) {
+ synchronized (OpenSSLSocketImpl.class) {
+ instanceCount += amount;
+ }
+ }
+
+ /**
+ * Initialize OpenSSL library.
+ */
+ private native static void nativeinitstatic();
+
+ static {
+ nativeinitstatic();
+ }
+
+ private native void nativeinit(String privatekey, String certificate, byte[] seed);
+
+ /**
+ * Initialize the SSL socket and set the certificates for the
+ * future handshaking.
+ */
+ private void init() throws IOException {
+ String alias = sslParameters.getKeyManager().chooseClientAlias(new String[] { "RSA" }, null, null);
+ if (alias != null) {
+ PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias);
+ X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias);
+
+ ByteArrayOutputStream privateKeyOS = new ByteArrayOutputStream();
+ PEMWriter privateKeyPEMWriter = new PEMWriter(new OutputStreamWriter(privateKeyOS));
+ privateKeyPEMWriter.writeObject(privateKey);
+ privateKeyPEMWriter.close();
+
+ ByteArrayOutputStream certificateOS = new ByteArrayOutputStream();
+ PEMWriter certificateWriter = new PEMWriter(new OutputStreamWriter(certificateOS));
+
+ for (int i = 0; i < certificates.length; i++) {
+ certificateWriter.writeObject(certificates[i]);
+ }
+ certificateWriter.close();
+
+ nativeinit(privateKeyOS.toString(), certificateOS.toString(),
+ sslParameters.getSecureRandomMember() != null ?
+ sslParameters.getSecureRandomMember().generateSeed(1024) : null);
+ } else {
+ nativeinit(null, null,
+ sslParameters.getSecureRandomMember() != null ?
+ sslParameters.getSecureRandomMember().generateSeed(1024) : null);
+ }
+ }
+
+ /**
+ * Class constructor with 2 parameters
+ *
+ * @param <code>SSLParameters sslParameters</code> Parameters for the SSL
+ * context
+ * @param <code>long ssl_op_no</code> Parameter to set the enabled
+ * protocols
+ * @throws <code>IOException</code> if network fails
+ */
+ protected OpenSSLSocketImpl(SSLParameters sslParameters, long ssl_op_no) throws IOException {
+ super();
+ this.sslParameters = sslParameters;
+ this.ssl_op_no = ssl_op_no;
+ updateInstanceCount(1);
+ }
+
+ /**
+ * Class constructor with 1 parameter
+ *
+ * @param <code>SSLParameters sslParameters</code> Parameters for the SSL
+ * context
+ * @throws <code>IOException</code> if network fails
+ */
+ protected OpenSSLSocketImpl(SSLParameters sslParameters) throws IOException {
+ super();
+ this.sslParameters = sslParameters;
+ init();
+ updateInstanceCount(1);
+ }
+
+ /**
+ * Class constructor with 3 parameters
+ *
+ * @param <code> String host</code>
+ * @param <code>int port</code>
+ * @param <code>SSLParameters sslParameters</code>
+ * @throws <code>IOException</code> if network fails
+ * @throws <code>UnknownHostException</code> host not defined
+ */
+ protected OpenSSLSocketImpl(String host, int port,
+ SSLParameters sslParameters)
+ throws IOException {
+ super(host, port);
+ this.sslParameters = sslParameters;
+ init();
+ updateInstanceCount(1);
+ }
+
+
+ /**
+ * Class constructor with 3 parameters: 1st is InetAddress
+ *
+ * @param <code>InetAddress address</code>
+ * @param <code>int port</code>
+ * @param <code>SSLParameters sslParameters</code>
+ * @throws <code>IOException</code> if network fails
+ * @throws <code>UnknownHostException</code> host not defined
+ */
+ protected OpenSSLSocketImpl(InetAddress address, int port,
+ SSLParameters sslParameters)
+ throws IOException {
+ super(address, port);
+ this.sslParameters = sslParameters;
+ init();
+ updateInstanceCount(1);
+ }
+
+
+ /**
+ * Class constructor with 5 parameters: 1st is host
+ *
+ * @param <code>String host</code>
+ * @param <code>int port</code>
+ * @param <code>InetAddress localHost</code>
+ * @param <code>int localPort</code>
+ * @param <code>SSLParameters sslParameters</code>
+ * @throws <code>IOException</code> if network fails
+ * @throws <code>UnknownHostException</code> host not defined
+ */
+ protected OpenSSLSocketImpl(String host, int port, InetAddress clientAddress,
+ int clientPort, SSLParameters sslParameters)
+ throws IOException {
+ super(host, port, clientAddress, clientPort);
+ this.sslParameters = sslParameters;
+ init();
+ updateInstanceCount(1);
+ }
+
+ /**
+ * Class constructor with 5 parameters: 1st is InetAddress
+ *
+ * @param <code>InetAddress address</code>
+ * @param <code>int port</code>
+ * @param <code>InetAddress localAddress</code>
+ * @param <code>int localPort</code>
+ * @param <code>SSLParameters sslParameters</code>
+ * @throws <code>IOException</code> if network fails
+ * @throws <code>UnknownHostException</code> host not defined
+ */
+ protected OpenSSLSocketImpl(InetAddress address, int port,
+ InetAddress clientAddress, int clientPort, SSLParameters sslParameters)
+ throws IOException {
+ super(address, port, clientAddress, clientPort);
+ this.sslParameters = sslParameters;
+ init();
+ updateInstanceCount(1);
+ }
+
+ /**
+ * Constructor with 5 parameters: 1st is socket. Enhances an existing socket
+ * with SSL functionality.
+ *
+ * @param <code>Socket socket</code>
+ * @param <code>String host</code>
+ * @param <code>int port</code>
+ * @param <code>boolean autoClose</code>
+ * @param <code>SSLParameters sslParameters</code>
+ * @throws <code>IOException</code> if network fails
+ */
+ protected OpenSSLSocketImpl(Socket socket, String host, int port,
+ boolean autoClose, SSLParameters sslParameters) throws IOException {
+ super();
+ this.socket = socket;
+ this.timeout = socket.getSoTimeout();
+ this.address = new InetSocketAddress(host, port);
+ this.autoClose = autoClose;
+ this.sslParameters = sslParameters;
+ init();
+ updateInstanceCount(1);
+ }
+
+ /**
+ * Adds OpenSSL functionality to the existing socket and starts the SSL
+ * handshaking.
+ */
+ private native boolean nativeconnect(int ctx, Socket sock, boolean client_mode, int sslsession) throws IOException;
+ private native int nativegetsslsession(int ssl);
+ private native String nativecipherauthenticationmethod();
+
+ /**
+ * Gets the suitable session reference from the session cache container.
+ *
+ * @return OpenSSLSessionImpl
+ */
+ private OpenSSLSessionImpl getOpenSSLSessionImpl() {
+ try {
+ byte[] id;
+ SSLSession ses;
+ for (Enumeration<byte[]> en = sslParameters.getClientSessionContext().getIds(); en.hasMoreElements();) {
+ id = en.nextElement();
+ ses = sslParameters.getClientSessionContext().getSession(id);
+ if (ses instanceof OpenSSLSessionImpl && ses.isValid() &&
+ super.getInetAddress() != null &&
+ super.getInetAddress().getHostAddress() != null &&
+ super.getInetAddress().getHostName().equals(ses.getPeerHost()) &&
+ super.getPort() == ses.getPeerPort()) {
+ return (OpenSSLSessionImpl) ses;
+ }
+ }
+ } catch (Exception ex) {
+ // It's not clear to me under what circumstances the above code
+ // might fail. I also can't reproduce it.
+ }
+ return null;
+ }
+
+ /**
+ * Starts a TLS/SSL handshake on this connection using some native methods
+ * from the OpenSSL library. It can negotiate new encryption keys, change
+ * cipher suites, or initiate a new session. The certificate chain is
+ * verified if the correspondent property in java.Security is set. All
+ * listensers are notified at the end of the TLS/SSL handshake.
+ *
+ * @throws <code>IOException</code> if network fails
+ */
+ public synchronized void startHandshake() throws IOException {
+ synchronized (handshakeLock) {
+ if (!handshakeStarted) {
+ handshakeStarted = true;
+ } else {
+ return;
+ }
+ }
+
+ {
+ // Debug
+ int size = 0;
+ for (Enumeration<byte[]> en = sslParameters.getClientSessionContext().getIds();
+ en.hasMoreElements(); en.nextElement()) { size++; };
+ }
+ OpenSSLSessionImpl session = getOpenSSLSessionImpl();
+
+ // Check if it's allowed to create a new session (default is true)
+ if (!sslParameters.getEnableSessionCreation() && session == null) {
+ throw new SSLHandshakeException("SSL Session may not be created");
+ } else {
+ if (nativeconnect(ssl_ctx, this.socket != null ?
+ this.socket : this, sslParameters.getUseClientMode(), session != null ? session.session : 0)) {
+ session.lastAccessedTime = System.currentTimeMillis();
+ sslSession = session;
+ } else {
+ if (address == null) sslSession = new OpenSSLSessionImpl(nativegetsslsession(ssl),
+ sslParameters, super.getInetAddress().getHostName(), super.getPort());
+ else sslSession = new OpenSSLSessionImpl(nativegetsslsession(ssl),
+ sslParameters, address.getHostName(), address.getPort());
+ try {
+ X509Certificate[] peerCertificates = (X509Certificate[]) sslSession.getPeerCertificates();
+
+ if (peerCertificates == null || peerCertificates.length == 0) {
+ throw new SSLException("Server sends no certificate");
+ }
+
+ sslParameters.getTrustManager().checkServerTrusted(peerCertificates,
+ nativecipherauthenticationmethod());
+ sslParameters.getClientSessionContext().putSession(sslSession);
+ } catch (CertificateException e) {
+ throw new SSLException("Not trusted server certificate", e);
+ }
+ }
+ }
+
+ if (listeners != null) {
+ // notify the listeners
+ HandshakeCompletedEvent event =
+ new HandshakeCompletedEvent(this, sslSession);
+ int size = listeners.size();
+ for (int i=0; i<size; i++) {
+ ((HandshakeCompletedListener)listeners.get(i))
+ .handshakeCompleted(event);
+ }
+ }
+ }
+
+ // To be synchronized because of the verify_callback
+ native synchronized void nativeaccept(Socket socketObject, int m_ctx, boolean client_mode);
+
+ /**
+ * Performs the first part of a SSL/TLS handshaking process with a given
+ * 'host' connection and initializes the SSLSession.
+ */
+ protected void accept(int m_ctx, boolean client_mode) throws IOException {
+ // Must be set because no handshaking is necessary
+ // in this situation
+ handshakeStarted = true;
+
+ nativeaccept(this, m_ctx, client_mode);
+
+ sslSession = new OpenSSLSessionImpl(nativegetsslsession(ssl),
+ sslParameters, super.getInetAddress().getHostName(), super.getPort());
+ sslSession.lastAccessedTime = System.currentTimeMillis();
+ }
+
+ /**
+ * Callback methode for the OpenSSL native certificate verification process.
+ *
+ * @param <code>byte[][] bytes</code> Byte array containing the cert's
+ * information.
+ * @return 0 if the certificate verification fails or 1 if OK
+ */
+ @SuppressWarnings("unused")
+ private int verify_callback(byte[][] bytes) {
+ try {
+ 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));
+ }
+ } catch (javax.security.cert.CertificateException e) {
+ return 0;
+ } catch (IOException e) {
+ return 0;
+ }
+ return 1;
+ }
+
+ /**
+ * Returns an input stream for this SSL socket using native calls to the
+ * OpenSSL library.
+ *
+ * @return: an input stream for reading bytes from this socket.
+ * @throws: <code>IOException</code> if an I/O error occurs when creating
+ * the input stream, the socket is closed, the socket is not
+ * connected, or the socket input has been shutdown.
+ */
+ public InputStream getInputStream() throws IOException {
+ synchronized(this) {
+ if (is == null) {
+ is = new SSLInputStream();
+ }
+
+ return is;
+ }
+ }
+
+ /**
+ * Returns an output stream for this SSL socket using native calls to the
+ * OpenSSL library.
+ *
+ * @return an output stream for writing bytes to this socket.
+ * @throws <code>IOException</code> if an I/O error occurs when creating
+ * the output stream, or no connection to the socket exists.
+ */
+ public OutputStream getOutputStream() throws IOException {
+ synchronized(this) {
+ if (os == null) {
+ os = new SSLOutputStream();
+ }
+
+ return os;
+ }
+ }
+
+ /**
+ * This method is not supported for this SSLSocket implementation.
+ */
+ public void shutdownInput() throws IOException {
+ throw new UnsupportedOperationException(
+ "Method shutdownInput() is not supported.");
+ }
+
+ /**
+ * This method is not supported for this SSLSocket implementation.
+ */
+ public void shutdownOutput() throws IOException {
+ throw new UnsupportedOperationException(
+ "Method shutdownOutput() is not supported.");
+ }
+
+ /**
+ * Reads with the native SSL_read function from the encrypted data stream
+ * @return -1 if error or the end of the stream is reached.
+ */
+ private native int nativeread(int timeout) throws IOException;
+ private native int nativeread(byte[] b, int off, int len, int timeout) throws IOException;
+
+ /**
+ * This inner class provides input data stream functionality
+ * for the OpenSSL native implementation. It is used to
+ * read data received via SSL protocol.
+ */
+ private class SSLInputStream extends InputStream {
+ SSLInputStream() throws IOException {
+ /**
+ /* Note: When startHandshake() throws an exception, no
+ * SSLInputStream object will be created.
+ */
+ OpenSSLSocketImpl.this.startHandshake();
+ }
+
+ /**
+ * Reads one byte. If there is no data in the underlying buffer,
+ * this operation can block until the data will be
+ * available.
+ * @return read value.
+ * @throws <code>IOException</code>
+ */
+ public int read() throws IOException {
+ synchronized(readLock) {
+ return OpenSSLSocketImpl.this.nativeread(timeout);
+ }
+ }
+
+ /**
+ * Method acts as described in spec for superclass.
+ * @see java.io.InputStream#read(byte[],int,int)
+ */
+ public int read(byte[] b, int off, int len) throws IOException {
+ synchronized(readLock) {
+ return OpenSSLSocketImpl.this.nativeread(b, off, len, timeout);
+ }
+ }
+ }
+
+ /**
+ * Writes with the native SSL_write function to the encrypted data stream.
+ */
+ private native void nativewrite(int b) throws IOException;
+ private native void nativewrite(byte[] b, int off, int len) throws IOException;
+
+ /**
+ * This inner class provides output data stream functionality
+ * for the OpenSSL native implementation. It is used to
+ * write data according to the encryption parameters given in SSL context.
+ */
+ private class SSLOutputStream extends OutputStream {
+ SSLOutputStream() throws IOException {
+ /**
+ /* Note: When startHandshake() throws an exception, no
+ * SSLInputStream object will be created.
+ */
+ OpenSSLSocketImpl.this.startHandshake();
+ }
+
+ /**
+ * Method acts as described in spec for superclass.
+ * @see java.io.OutputStream#write(int)
+ */
+ public void write(int b) throws IOException {
+ synchronized(writeLock) {
+ OpenSSLSocketImpl.this.nativewrite(b);
+ }
+ }
+
+ /**
+ * Method acts as described in spec for superclass.
+ * @see java.io.OutputStream#write(byte[],int,int)
+ */
+ public void write(byte[] b, int start, int len) throws IOException {
+ synchronized(writeLock) {
+ OpenSSLSocketImpl.this.nativewrite(b, start, len);
+ }
+ }
+ }
+
+
+ /**
+ * The SSL session used by this connection is returned. The SSL session
+ * determines which cipher suite should be used by all connections within
+ * that session and which identities have the session's client and server.
+ * This method starts the SSL handshake.
+ * @return the SSLSession.
+ * @throws <code>IOException</code> if the handshake fails
+ */
+ public SSLSession getSession() {
+ try {
+ startHandshake();
+ } catch (IOException e) {
+ Logger.getLogger(getClass().getName()).log(Level.WARNING,
+ "Error negotiating SSL connection.", e);
+
+ // return an invalid session with
+ // invalid cipher suite of "SSL_NULL_WITH_NULL_NULL"
+ return SSLSessionImpl.NULL_SESSION;
+ }
+ return sslSession;
+ }
+
+ /**
+ * Registers a listener to be notified that a SSL handshake
+ * was successfully completed on this connection.
+ * @param <code>HandShakeCompletedListener listener</code>
+ * @throws <code>IllegalArgumentException</code> if listener is null.
+ */
+ public void addHandshakeCompletedListener(
+ HandshakeCompletedListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("Provided listener is null");
+ }
+ if (listeners == null) {
+ listeners = new ArrayList();
+ }
+ listeners.add(listener);
+ }
+
+ /**
+ * The method removes a registered listener.
+ * @param <code>HandShakeCompletedListener listener</code>
+ * @throws IllegalArgumentException if listener is null or not registered
+ */
+ public void removeHandshakeCompletedListener(
+ HandshakeCompletedListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("Provided listener is null");
+ }
+ if (listeners == null) {
+ throw new IllegalArgumentException(
+ "Provided listener is not registered");
+ }
+ if (!listeners.remove(listener)) {
+ throw new IllegalArgumentException(
+ "Provided listener is not registered");
+ }
+ }
+
+ /**
+ * Returns true if new SSL sessions may be established by this socket.
+ *
+ * @return true if the session may be created; false if a session already
+ * exists and must be resumed.
+ */
+ public boolean getEnableSessionCreation() {
+ return sslParameters.getEnableSessionCreation();
+ }
+
+ /**
+ * Set a flag for the socket to inhibit or to allow the creation of a new
+ * SSL sessions. If the flag is set to false, and there are no actual
+ * sessions to resume, then there will be no successful handshaking.
+ *
+ * @param <code>boolean flag</code> true if session may be created; false
+ * if a session already exists and must be resumed.
+ */
+ public void setEnableSessionCreation(boolean flag) {
+ sslParameters.setEnableSessionCreation(flag);
+ }
+
+ /**
+ * Gets all available ciphers from the current OpenSSL library.
+ * Needed by OpenSSLSocketFactory too.
+ */
+ static native String[] nativegetsupportedciphersuites();
+
+ /**
+ * The names of the cipher suites which could be used by the SSL connection
+ * are returned.
+ * @return an array of cipher suite names
+ */
+ public String[] getSupportedCipherSuites() {
+ return nativegetsupportedciphersuites();
+ }
+
+ private native String[] nativegetenabledciphersuites();
+
+ /**
+ * The names of the cipher suites that are in use in the actual the SSL
+ * connection are returned.
+ *
+ * @return an array of cipher suite names
+ */
+ public String[] getEnabledCipherSuites() {
+ return nativegetenabledciphersuites();
+ }
+
+ /**
+ * Calls the SSL_CTX_set_cipher_list(...) OpenSSL function with the passed
+ * char array.
+ */
+ private native void nativesetenabledciphersuites(String controlString);
+
+ private boolean findSuite(String suite) {
+ String[] supportedCipherSuites = nativegetsupportedciphersuites();
+ for(int i = 0; i < supportedCipherSuites.length; i++)
+ if (supportedCipherSuites[i].equals(suite)) return true;
+ throw new IllegalArgumentException("Protocol " + suite +
+ " is not supported.");
+ }
+
+ /**
+ * This method enables the cipher suites listed by
+ * getSupportedCipherSuites().
+ *
+ * @param <code> String[] suites</code> names of all the cipher suites to
+ * put on use
+ * @throws <code>IllegalArgumentException</code> when one or more of the
+ * ciphers in array suites are not supported, or when the array
+ * is null.
+ */
+ public void setEnabledCipherSuites(String[] suites) {
+ if (suites == null) {
+ throw new IllegalArgumentException("Provided parameter is null");
+ }
+ String controlString = "";
+ for(int i = 0; i < suites.length; i++) {
+ findSuite(suites[i]);
+ if (i == 0) controlString = suites[i];
+ else controlString += ":" + suites[i];
+ }
+ nativesetenabledciphersuites(controlString);
+ }
+
+ /**
+ * The names of the protocols' versions that may be used on this SSL
+ * connection.
+ * @return an array of protocols names
+ */
+ public String[] getSupportedProtocols() {
+ return supportedProtocols.clone();
+ }
+
+ /**
+ * SSL mode of operation with or without back compatibility. See the OpenSSL
+ * ssl.h header file for more information.
+ */
+ static private long SSL_OP_NO_SSLv3 = 0x02000000L;
+ static private long SSL_OP_NO_TLSv1 = 0x04000000L;
+
+ /**
+ * The names of the protocols' versions that are in use on this SSL
+ * connection.
+ *
+ * @return an array of protocols names
+ */
+ @Override
+ public String[] getEnabledProtocols() {
+ ArrayList<String> array = new ArrayList<String>();
+
+ if ((ssl_op_no & SSL_OP_NO_SSLv3) == 0x00000000L) {
+ array.add(supportedProtocols[1]);
+ }
+ if ((ssl_op_no & SSL_OP_NO_TLSv1) == 0x00000000L) {
+ array.add(supportedProtocols[2]);
+ }
+ return array.toArray(new String[array.size()]);
+ }
+
+ private native void nativesetenabledprotocols(long l);
+
+ /**
+ * This method enables the protocols' versions listed by
+ * getSupportedProtocols().
+ *
+ * @param protocols The names of all the protocols to put on use
+ *
+ * @throws IllegalArgumentException when one or more of the names in the
+ * array are not supported, or when the array is null.
+ */
+ @Override
+ public synchronized void setEnabledProtocols(String[] protocols) {
+
+ if (protocols == null) {
+ throw new IllegalArgumentException("Provided parameter is null");
+ }
+
+ ssl_op_no = SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
+
+ for(int i = 0; i < protocols.length; i++) {
+ if (protocols[i].equals("SSLv3"))
+ ssl_op_no ^= SSL_OP_NO_SSLv3;
+ else if (protocols[i].equals("TLSv1"))
+ ssl_op_no ^= SSL_OP_NO_TLSv1;
+ else throw new IllegalArgumentException("Protocol " + protocols[i] +
+ " is not supported.");
+ }
+
+ nativesetenabledprotocols(ssl_op_no);
+ }
+
+ /**
+ * This method gives true back if the SSL socket is set to client mode.
+ *
+ * @return true if the socket should do the handshaking as client.
+ */
+ public boolean getUseClientMode() {
+ return sslParameters.getUseClientMode();
+ }
+
+ /**
+ * This method set the actual SSL socket to client mode.
+ *
+ * @param <code>boolean mode</code> true if the socket starts in client
+ * mode
+ * @throws <code>IllegalArgumentException</code> if mode changes during
+ * handshake.
+ */
+ public synchronized void setUseClientMode(boolean mode) {
+ if (handshakeStarted) {
+ throw new IllegalArgumentException(
+ "Could not change the mode after the initial handshake has begun.");
+ }
+ sslParameters.setUseClientMode(mode);
+ }
+
+ /**
+ * Returns true if the SSL socket requests client's authentication. Relevant
+ * only for server sockets!
+ *
+ * @return true if client authentication is desired, false if not.
+ */
+ public boolean getWantClientAuth() {
+ return sslParameters.getWantClientAuth();
+ }
+
+ /**
+ * Returns true if the SSL socket needs client's authentication. Relevant
+ * only for server sockets!
+ *
+ * @return true if client authentication is desired, false if not.
+ */
+ public boolean getNeedClientAuth() {
+ return sslParameters.getNeedClientAuth();
+ }
+
+ /**
+ * Sets the SSL socket to use client's authentication. Relevant only for
+ * server sockets!
+ *
+ * @param <code>boolean need</code> true if client authentication is
+ * desired, false if not.
+ */
+ public void setNeedClientAuth(boolean need) {
+ sslParameters.setNeedClientAuth(need);
+ }
+
+ /**
+ * Sets the SSL socket to use client's authentication. Relevant only for
+ * server sockets! Notice that in contrast to setNeedClientAuth(..) this
+ * method will continue the negotiation if the client decide not to send
+ * authentication credentials.
+ *
+ * @param <code>boolean want</code> true if client authentication is
+ * desired, false if not.
+ */
+ public void setWantClientAuth(boolean want) {
+ sslParameters.setWantClientAuth(want);
+ }
+
+ /**
+ * This method is not supported for SSLSocket implementation.
+ */
+ public void sendUrgentData(int data) throws IOException {
+ throw new SocketException(
+ "Method sendUrgentData() is not supported.");
+ }
+
+ /**
+ * This method is not supported for SSLSocket implementation.
+ */
+ public void setOOBInline(boolean on) throws SocketException {
+ throw new SocketException(
+ "Methods sendUrgentData, setOOBInline are not supported.");
+ }
+
+ /**
+ * Set the read timeout on this socket. The SO_TIMEOUT option, is specified
+ * in milliseconds. The read operation will block indefinitely for a zero
+ * value.
+ *
+ * @param timeout the read timeout value
+ * @throws SocketException if an error occurs setting the option
+ */
+ public synchronized void setSoTimeout(int timeout) throws SocketException {
+ super.setSoTimeout(timeout);
+ this.timeout = timeout;
+ }
+
+ private native void nativeinterrupt() throws IOException;
+ private native void nativeclose() throws IOException;
+
+ /**
+ * Closes the SSL socket. Once closed, a socket is not available for further
+ * use anymore under any circumstance. A new socket must be created.
+ *
+ * @throws <code>IOException</code> if an I/O error happens during the
+ * socket's closure.
+ */
+ public void close() throws IOException {
+ synchronized (handshakeLock) {
+ if (!handshakeStarted) {
+ handshakeStarted = true;
+
+ synchronized (this) {
+ nativefree();
+
+ if (socket != null) {
+ if (autoClose && !socket.isClosed()) socket.close();
+ } else {
+ if (!super.isClosed()) super.close();
+ }
+ }
+
+ return;
+ }
+ }
+
+ nativeinterrupt();
+
+ synchronized (this) {
+ synchronized (writeLock) {
+ synchronized (readLock) {
+
+ IOException pendingException = null;
+
+ // Shut down the SSL connection, per se.
+ try {
+ if (handshakeStarted) {
+ nativeclose();
+ }
+ } catch (IOException ex) {
+ /*
+ * Note the exception at this point, but try to continue
+ * to clean the rest of this all up before rethrowing.
+ */
+ pendingException = ex;
+ }
+
+ /*
+ * Even if the above call failed, it is still safe to free
+ * the native structs, and we need to do so lest we leak
+ * memory.
+ */
+ nativefree();
+
+ if (socket != null) {
+ if (autoClose && !socket.isClosed())
+ socket.close();
+ } else {
+ if (!super.isClosed())
+ super.close();
+ }
+
+ if (pendingException != null) {
+ throw pendingException;
+ }
+ }
+ }
+ }
+ }
+
+ private native void nativefree();
+
+ protected void finalize() throws IOException {
+ updateInstanceCount(-1);
+
+ if (ssl == 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.
+ */
+ 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 #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);
+ }
+ }
+ }
+
+ /**
+ * Verifies an RSA signature. Conceptually, this method doesn't really
+ * belong here, but due to its native code being closely tied to OpenSSL
+ * (just like the rest of this class), we put it here for the time being.
+ * This also solves potential problems with native library initialization.
+ *
+ * @param message The message to verify
+ * @param signature The signature to verify
+ * @param algorithm The hash/sign algorithm to use, i.e. "RSA-SHA1"
+ * @param key The RSA public key to use
+ * @return true if the verification succeeds, false otherwise
+ */
+ public static boolean verifySignature(byte[] message, byte[] signature, String algorithm, RSAPublicKey key) {
+ byte[] modulus = key.getModulus().toByteArray();
+ byte[] exponent = key.getPublicExponent().toByteArray();
+
+ return nativeverifysignature(message, signature, algorithm, modulus, exponent) == 1;
+ }
+
+ private static native int nativeverifysignature(byte[] message, byte[] signature,
+ String algorithm, byte[] modulus, byte[] exponent);
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImplWrapper.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImplWrapper.java
new file mode 100644
index 0000000..e3451aa
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImplWrapper.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+
+/**
+ * This class wraps the SSL functionality over an existing conneted socket.
+ */
+public class OpenSSLSocketImplWrapper extends OpenSSLSocketImpl {
+
+ private Socket socket;
+
+ protected OpenSSLSocketImplWrapper(Socket socket, String host, int port,
+ boolean autoClose, SSLParameters sslParameters) throws IOException {
+ super(socket, host, port, autoClose, sslParameters);
+ if (!socket.isConnected()) {
+ throw new SocketException("Socket is not connected.");
+ }
+ this.socket = socket;
+ }
+
+ public void connect(SocketAddress sockaddr, int timeout)
+ throws IOException {
+ throw new IOException("Underlying socket is already connected.");
+ }
+
+ public void connect(SocketAddress sockaddr) throws IOException {
+ throw new IOException("Underlying socket is already connected.");
+ }
+
+ public void bind(SocketAddress sockaddr) throws IOException {
+ throw new IOException("Underlying socket is already connected.");
+ }
+
+ public SocketAddress getRemoteSocketAddress() {
+ return socket.getRemoteSocketAddress();
+ }
+
+ public SocketAddress getLocalSocketAddress() {
+ return socket.getLocalSocketAddress();
+ }
+
+ public InetAddress getLocalAddress() {
+ return socket.getLocalAddress();
+ }
+
+ public InetAddress getInetAddress() {
+ return socket.getInetAddress();
+ }
+
+ public String toString() {
+ return "SSL socket over " + socket.toString();
+ }
+
+ public void setSoLinger(boolean on, int linger) throws SocketException {
+ socket.setSoLinger(on, linger);
+ }
+
+ public void setTcpNoDelay(boolean on) throws SocketException {
+ socket.setTcpNoDelay(on);
+ }
+
+ public void setReuseAddress(boolean on) throws SocketException {
+ socket.setReuseAddress(on);
+ }
+
+ public void setKeepAlive(boolean on) throws SocketException {
+ socket.setKeepAlive(on);
+ }
+
+ public void setTrafficClass(int tos) throws SocketException {
+ socket.setTrafficClass(tos);
+ }
+
+ public void setSoTimeout(int to) throws SocketException {
+ socket.setSoTimeout(to);
+ super.setSoTimeout(to);
+ }
+
+ public void setSendBufferSize(int size) throws SocketException {
+ socket.setSendBufferSize(size);
+ }
+
+ public void setReceiveBufferSize(int size) throws SocketException {
+ socket.setReceiveBufferSize(size);
+ }
+
+ public boolean getTcpNoDelay() throws SocketException {
+ return socket.getTcpNoDelay();
+ }
+
+ public boolean getReuseAddress() throws SocketException {
+ return socket.getReuseAddress();
+ }
+
+ public boolean getOOBInline() throws SocketException {
+ return socket.getOOBInline();
+ }
+
+ public boolean getKeepAlive() throws SocketException {
+ return socket.getKeepAlive();
+ }
+
+ public int getTrafficClass() throws SocketException {
+ return socket.getTrafficClass();
+ }
+
+ public int getSoTimeout() throws SocketException {
+ return socket.getSoTimeout();
+ }
+
+ public int getSoLinger() throws SocketException {
+ return socket.getSoLinger();
+ }
+
+ public int getSendBufferSize() throws SocketException {
+ return socket.getSendBufferSize();
+ }
+
+ public int getReceiveBufferSize() throws SocketException {
+ return socket.getReceiveBufferSize();
+ }
+
+ public boolean isConnected() {
+ return socket.isConnected();
+ }
+
+ public boolean isClosed() {
+ return socket.isClosed();
+ }
+
+ public boolean isBound() {
+ return socket.isBound();
+ }
+
+ public boolean isOutputShutdown() {
+ return socket.isOutputShutdown();
+ }
+
+ public boolean isInputShutdown() {
+ return socket.isInputShutdown();
+ }
+
+ public int getPort() {
+ return socket.getPort();
+ }
+
+ public int getLocalPort() {
+ return socket.getLocalPort();
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/PRF.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/PRF.java
new file mode 100644
index 0000000..3ed9b2a
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/PRF.java
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.AlertException;
+import org.apache.harmony.xnet.provider.jsse.Logger;
+
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import javax.net.ssl.SSLException;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * This class provides functionality for computation
+ * of PRF values for TLS (http://www.ietf.org/rfc/rfc2246.txt)
+ * and SSL v3 (http://wp.netscape.com/eng/ssl3) protocols.
+ */
+public class PRF {
+ private static Logger.Stream logger = Logger.getStream("prf");
+
+ private static Mac md5_mac;
+ private static Mac sha_mac;
+ protected static MessageDigest md5;
+ protected static MessageDigest sha;
+ private static int md5_mac_length;
+ private static int sha_mac_length;
+
+ static private void init() {
+ try {
+ md5_mac = Mac.getInstance("HmacMD5");
+ sha_mac = Mac.getInstance("HmacSHA1");
+ } catch (NoSuchAlgorithmException e) {
+ throw new AlertException(AlertProtocol.INTERNAL_ERROR,
+ new SSLException(
+ "There is no provider of HmacSHA1 or HmacMD5 "
+ + "algorithms installed in the system"));
+ }
+ md5_mac_length = md5_mac.getMacLength();
+ sha_mac_length = sha_mac.getMacLength();
+ try {
+ md5 = MessageDigest.getInstance("MD5");
+ sha = MessageDigest.getInstance("SHA-1");
+ } catch (Exception e) {
+ throw new AlertException(AlertProtocol.INTERNAL_ERROR,
+ new SSLException(
+ "Could not initialize the Digest Algorithms."));
+ }
+ }
+
+ /**
+ * Computes the value of SSLv3 pseudo random function.
+ * @param out: the buffer to fill up with the value of the function.
+ * @param secret: the buffer containing the secret value to generate prf.
+ * @param seed: the seed to be used.
+ */
+ static synchronized void computePRF_SSLv3(byte[] out, byte[] secret, byte[] seed) {
+ if (sha == null) {
+ init();
+ }
+ int pos = 0;
+ int iteration = 1;
+ byte[] digest;
+ while (pos < out.length) {
+ byte[] pref = new byte[iteration];
+ Arrays.fill(pref, (byte) (64 + iteration++));
+ sha.update(pref);
+ sha.update(secret);
+ sha.update(seed);
+ md5.update(secret);
+ md5.update(sha.digest());
+ digest = md5.digest(); // length == 16
+ if (pos + 16 > out.length) {
+ System.arraycopy(digest, 0, out, pos, out.length - pos);
+ pos = out.length;
+ } else {
+ System.arraycopy(digest, 0, out, pos, 16);
+ pos += 16;
+ }
+ }
+ }
+
+ /**
+ * Computes the value of TLS pseudo random function.
+ * @param out: the buffer to fill up with the value of the function.
+ * @param secret: the buffer containing the secret value to generate prf.
+ * @param str_bytes: the label bytes to be used.
+ * @param seed: the seed to be used.
+ */
+ synchronized static void computePRF(byte[] out, byte[] secret,
+ byte[] str_byts, byte[] seed) throws GeneralSecurityException {
+ if (sha_mac == null) {
+ init();
+ }
+ // Do concatenation of the label with the seed:
+ // (metterings show that is is faster to concatenate the arrays
+ // and to call HMAC.update on cancatenation, than twice call for
+ // each of the part, i.e.:
+ // time(HMAC.update(label+seed))
+ // < time(HMAC.update(label)) + time(HMAC.update(seed))
+ // but it takes more memmory (approximaty on 4%)
+ /*
+ byte[] tmp_seed = new byte[seed.length + str_byts.length];
+ System.arraycopy(str_byts, 0, tmp_seed, 0, str_byts.length);
+ System.arraycopy(seed, 0, tmp_seed, str_byts.length, seed.length);
+ seed = tmp_seed;
+ */
+ SecretKeySpec keyMd5;
+ SecretKeySpec keySha1;
+ if ((secret == null) || (secret.length == 0)) {
+ secret = new byte[8];
+ keyMd5 = new SecretKeySpec(secret, "HmacMD5");
+ keySha1 = new SecretKeySpec(secret, "HmacSHA1");
+ } else {
+ int length = secret.length >> 1; // division by 2
+ int offset = secret.length & 1; // remainder
+ keyMd5 = new SecretKeySpec(secret, 0, length + offset,
+ "HmacMD5");
+ keySha1 = new SecretKeySpec(secret, length, length
+ + offset, "HmacSHA1");
+ }
+
+ //byte[] str_byts = label.getBytes();
+
+ if (logger != null) {
+ logger.println("secret["+secret.length+"]: ");
+ logger.printAsHex(16, "", " ", secret);
+ logger.println("label["+str_byts.length+"]: ");
+ logger.printAsHex(16, "", " ", str_byts);
+ logger.println("seed["+seed.length+"]: ");
+ logger.printAsHex(16, "", " ", seed);
+ logger.println("MD5 key:");
+ logger.printAsHex(16, "", " ", keyMd5.getEncoded());
+ logger.println("SHA1 key:");
+ logger.printAsHex(16, "", " ", keySha1.getEncoded());
+ }
+
+ md5_mac.init(keyMd5);
+ sha_mac.init(keySha1);
+
+ int pos = 0;
+ md5_mac.update(str_byts);
+ byte[] hash = md5_mac.doFinal(seed); // A(1)
+ while (pos < out.length) {
+ md5_mac.update(hash);
+ md5_mac.update(str_byts);
+ md5_mac.update(seed);
+ if (pos + md5_mac_length < out.length) {
+ md5_mac.doFinal(out, pos);
+ pos += md5_mac_length;
+ } else {
+ System.arraycopy(md5_mac.doFinal(), 0, out,
+ pos, out.length - pos);
+ break;
+ }
+ // make A(i)
+ hash = md5_mac.doFinal(hash);
+ }
+ if (logger != null) {
+ logger.println("P_MD5:");
+ logger.printAsHex(md5_mac_length, "", " ", out);
+ }
+
+ pos = 0;
+ sha_mac.update(str_byts);
+ hash = sha_mac.doFinal(seed); // A(1)
+ byte[] sha1hash;
+ while (pos < out.length) {
+ sha_mac.update(hash);
+ sha_mac.update(str_byts);
+ sha1hash = sha_mac.doFinal(seed);
+ for (int i = 0; (i < sha_mac_length) & (pos < out.length); i++) {
+ out[pos++] ^= sha1hash[i];
+ }
+ // make A(i)
+ hash = sha_mac.doFinal(hash);
+ }
+
+ if (logger != null) {
+ logger.println("PRF:");
+ logger.printAsHex(sha_mac_length, "", " ", out);
+ }
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ProtocolVersion.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ProtocolVersion.java
new file mode 100644
index 0000000..1343c3b
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ProtocolVersion.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.util.Hashtable;
+
+/**
+ *
+ * Represents Protocol Version
+ */
+public class ProtocolVersion {
+
+ /**
+ * Protocol name
+ */
+ public final String name;
+
+ /**
+ * Protocol version as byte array
+ */
+ public final byte[] version;
+
+ /**
+ * Protocols supported by this provider implementaton
+ */
+ public static final String[] supportedProtocols = new String[] { "TLSv1",
+ "SSLv3" };
+
+ private static Hashtable protocolsByName = new Hashtable(4);
+
+ private ProtocolVersion(String name, byte[] version) {
+ this.name = name;
+ this.version = version;
+ }
+
+ /**
+ * Compares this ProtocolVersion to the specified object.
+ */
+ public boolean equals(Object o) {
+ if (o instanceof ProtocolVersion
+ && this.version[0] == ((ProtocolVersion) o).version[0]
+ && this.version[1] == ((ProtocolVersion) o).version[1]) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ *
+ * Returns true if protocol version is supported
+ *
+ * @param version
+ */
+ public static boolean isSupported(byte[] version) {
+ if (version[0] != 3 || (version[1] != 0 && version[1] != 1)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns ProtocolVersion
+ *
+ * @param version
+ * @return
+ */
+ public static ProtocolVersion getByVersion(byte[] version) {
+ if (version[0] == 3) {
+ if (version[1] == 1) {
+ return TLSv1;
+ }
+ if (version[1] == 0) {
+ return SSLv3;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if provider supports protocol version
+ *
+ * @param name
+ * @return
+ */
+ public static boolean isSupported(String name) {
+ return protocolsByName.containsKey(name);
+ }
+
+ /**
+ * Returns ProtocolVersion
+ *
+ * @param name
+ * @return
+ */
+ public static ProtocolVersion getByName(String name) {
+ return (ProtocolVersion) protocolsByName.get(name);
+ }
+
+ /**
+ * Highest protocol version supported by provider implementation
+ *
+ * @param protocols
+ * @return
+ */
+ public static ProtocolVersion getLatestVersion(String[] protocols) {
+ if (protocols == null || protocols.length == 0) {
+ return null;
+ }
+ ProtocolVersion latest = getByName(protocols[0]);
+ ProtocolVersion current;
+ for (int i = 1; i < protocols.length; i++) {
+ current = getByName(protocols[i]);
+ if (current == null) {
+ continue;
+ }
+ if ((latest == null)
+ || (latest.version[0] < current.version[0])
+ || (latest.version[0] == current.version[0] && latest.version[1] < current.version[1])) {
+ latest = current;
+ }
+ }
+ return latest;
+
+ }
+
+ /**
+ * SSL 3.0 protocol version
+ */
+ public static ProtocolVersion SSLv3 = new ProtocolVersion("SSLv3",
+ new byte[] { 3, 0 });
+
+ /**
+ * TLS 1.0 protocol version
+ */
+ public static ProtocolVersion TLSv1 = new ProtocolVersion("TLSv1",
+ new byte[] { 3, 1 });
+
+ static {
+ protocolsByName.put(SSLv3.name, SSLv3);
+ protocolsByName.put(TLSv1.name, TLSv1);
+ protocolsByName.put("SSL", SSLv3);
+ protocolsByName.put("TLS", TLSv1);
+ }
+
+} \ No newline at end of file
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLBufferedInput.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLBufferedInput.java
new file mode 100644
index 0000000..44009b9
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLBufferedInput.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.SSLInputStream;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * This is a wrapper input stream for ByteBuffer data source.
+ * Among with the read functionality it provides info
+ * about number of cunsumed bytes from the source ByteBuffer.
+ * The source ByteBuffer object can be reseted.
+ * So one instance of this wrapper can be reused for several
+ * ByteBuffer data sources.
+ */
+public class SSLBufferedInput extends SSLInputStream {
+
+ private ByteBuffer in;
+ private int bytik;
+ private int consumed = 0;
+
+ /**
+ * Constructor
+ */
+ protected SSLBufferedInput() {}
+
+ /**
+ * Sets the buffer as a data source
+ */
+ protected void setSourceBuffer(ByteBuffer in) {
+ consumed = 0;
+ this.in = in;
+ }
+
+ /**
+ * Returns the number of bytes available for reading.
+ */
+ public int available() throws IOException {
+ // in assumption that the buffer has been set
+ return in.remaining();
+ }
+
+ /**
+ * Returns the number of consumed bytes.
+ */
+ protected int consumed() {
+ return consumed;
+ }
+
+ /**
+ * Reads the following byte value. If there are no bytes in the source
+ * buffer, method throws java.nio.BufferUnderflowException.
+ */
+ public int read() throws IOException {
+ // TODO: implement optimized read(int)
+ // and read(byte[], int, int) methods
+ bytik = in.get() & 0x00FF;
+ consumed ++;
+ return bytik;
+ }
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java
new file mode 100644
index 0000000..0d3a07c
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+// BEGIN android-removed
+// import org.apache.harmony.xnet.provider.jsse.SSLSocketFactoryImpl;
+// END android-removed
+import org.apache.harmony.xnet.provider.jsse.SSLEngineImpl;
+import org.apache.harmony.xnet.provider.jsse.SSLParameters;
+// BEGIN android-removed
+// import org.apache.harmony.xnet.provider.jsse.SSLServerSocketFactoryImpl;
+// END android-removed
+
+import java.security.KeyManagementException;
+import java.security.SecureRandom;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContextSpi;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+
+/**
+ * Implementation of SSLContext service provider interface.
+ */
+public class SSLContextImpl extends SSLContextSpi {
+
+ // client session context contains the set of reusable
+ // client-side SSL sessions
+ private SSLSessionContextImpl clientSessionContext =
+ new SSLSessionContextImpl();
+ // server session context contains the set of reusable
+ // server-side SSL sessions
+ private SSLSessionContextImpl serverSessionContext =
+ new SSLSessionContextImpl();
+
+ protected SSLParameters sslParameters;
+
+ public SSLContextImpl() {
+ super();
+ }
+
+ public void engineInit(KeyManager[] kms, TrustManager[] tms,
+ SecureRandom sr) throws KeyManagementException {
+ sslParameters = new SSLParameters(kms, tms, sr, clientSessionContext,
+ serverSessionContext);
+ }
+
+ public SSLSocketFactory engineGetSocketFactory() {
+ if (sslParameters == null) {
+ throw new IllegalStateException("SSLContext is not initiallized.");
+ }
+ return new OpenSSLSocketFactoryImpl(sslParameters);
+ }
+
+ public SSLServerSocketFactory engineGetServerSocketFactory() {
+ if (sslParameters == null) {
+ throw new IllegalStateException("SSLContext is not initiallized.");
+ }
+ return new OpenSSLServerSocketFactoryImpl(sslParameters);
+ }
+
+ public SSLEngine engineCreateSSLEngine(String host, int port) {
+ if (sslParameters == null) {
+ throw new IllegalStateException("SSLContext is not initiallized.");
+ }
+ return new SSLEngineImpl(host, port,
+ (SSLParameters) sslParameters.clone());
+ }
+
+ public SSLEngine engineCreateSSLEngine() {
+ if (sslParameters == null) {
+ throw new IllegalStateException("SSLContext is not initiallized.");
+ }
+ return new SSLEngineImpl((SSLParameters) sslParameters.clone());
+ }
+
+ public SSLSessionContext engineGetServerSessionContext() {
+ return serverSessionContext;
+ }
+
+ public SSLSessionContext engineGetClientSessionContext() {
+ return clientSessionContext;
+ }
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineAppData.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineAppData.java
new file mode 100644
index 0000000..698723b
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineAppData.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.AlertException;
+
+import java.nio.ByteBuffer;
+import javax.net.ssl.SSLException;
+
+/**
+ * This class is used to retrieve the application data
+ * arrived for the SSLEngine.
+ */
+public class SSLEngineAppData implements org.apache.harmony.xnet.provider.jsse.Appendable {
+
+ /**
+ * Buffer containing received application data.
+ */
+ byte[] buffer;
+
+ /**
+ * Constructor
+ */
+ protected SSLEngineAppData() {}
+
+ /**
+ * Stores received data. The source data is not cloned,
+ * just the array reference is remembered into the buffer field.
+ */
+ public void append(byte[] src) {
+ if (buffer != null) {
+ throw new AlertException(
+ AlertProtocol.INTERNAL_ERROR,
+ new SSLException("Attempt to override the data"));
+ }
+ buffer = src;
+ }
+
+ /**
+ * Places the data from the buffer into the array of destination
+ * ByteBuffer objects.
+ */
+ protected int placeTo(ByteBuffer[] dsts, int offset, int length) {
+ if (buffer == null) {
+ return 0;
+ }
+ int pos = 0;
+ int len = buffer.length;
+ int rem;
+ // write data to the buffers
+ for (int i=offset; i<offset+length; i++) {
+ rem = dsts[i].remaining();
+ // TODO: optimization work - use hasArray, array(), arraycopy
+ if (len - pos < rem) {
+ // can fully write remaining data into buffer
+ dsts[i].put(buffer, pos, len - pos);
+ pos = len;
+ // data was written, exit
+ break;
+ } else {
+ // write chunk of data
+ dsts[i].put(buffer, pos, rem);
+ pos += rem;
+ }
+ }
+ if (pos != len) {
+ // The data did not feet into the buffers,
+ // it should not happen, because the destination buffers
+ // had been checked for the space before record unwrapping.
+ // But if it so, we should allert about internal error.
+ throw new AlertException(
+ AlertProtocol.INTERNAL_ERROR,
+ new SSLException(
+ "The received application data could not be fully written"
+ + "into the destination buffers"));
+ }
+ buffer = null;
+ return len;
+ }
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineDataStream.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineDataStream.java
new file mode 100644
index 0000000..bc13577
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineDataStream.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.nio.ByteBuffer;
+
+/**
+ * This class provides the DataStream functionality
+ * implemented over the array of ByteBuffer instances.
+ * Among with the data chunks read functionality
+ * it provides the info about amount of consumed data.
+ * The source ByteBuffer objects can be replaced by other.
+ * So one instance of this wrapper can be reused for several
+ * data sources.
+ */
+public class SSLEngineDataStream implements DataStream {
+
+ private ByteBuffer[] srcs;
+ private int offset;
+ private int limit;
+
+ private int available;
+ private int consumed;
+
+ protected SSLEngineDataStream() {}
+
+ protected void setSourceBuffers(ByteBuffer[] srcs, int offset, int length) {
+ this.srcs = srcs;
+ this.offset = offset;
+ this.limit = offset+length;
+ this.consumed = 0;
+ this.available = 0;
+ for (int i=offset; i<limit; i++) {
+ if (srcs[i] == null) {
+ throw new IllegalStateException(
+ "Some of the input parameters are null");
+ }
+ available += srcs[i].remaining();
+ }
+ }
+
+ public int available() {
+ return available;
+ }
+
+ public boolean hasData() {
+ return available > 0;
+ }
+
+ public byte[] getData(int length) {
+ // TODO: optimization work:
+ // use ByteBuffer.get(byte[],int,int)
+ // and ByteBuffer.hasArray() methods
+ int len = (length < available) ? length : available;
+ available -= len;
+ consumed += len;
+ byte[] res = new byte[len];
+ int pos = 0;
+ loop:
+ for (; offset<limit; offset++) {
+ while (srcs[offset].hasRemaining()) {
+ res[pos++] = srcs[offset].get();
+ len --;
+ if (len == 0) {
+ break loop;
+ }
+ }
+ }
+ return res;
+ }
+
+ protected int consumed() {
+ return consumed;
+ }
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineImpl.java
new file mode 100644
index 0000000..383e146
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineImpl.java
@@ -0,0 +1,751 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.AlertException;
+import org.apache.harmony.xnet.provider.jsse.SSLSessionImpl;
+import org.apache.harmony.xnet.provider.jsse.SSLEngineDataStream;
+
+import java.io.IOException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ReadOnlyBufferException;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+
+/**
+ * Implementation of SSLEngine.
+ * @see javax.net.ssl.SSLEngine class documentation for more information.
+ */
+public class SSLEngineImpl extends SSLEngine {
+
+ // indicates if peer mode was set
+ private boolean peer_mode_was_set = false;
+ // indicates if handshake has been started
+ private boolean handshake_started = false;
+ // indicates if inbound operations finished
+ private boolean isInboundDone = false;
+ // indicates if outbound operations finished
+ private boolean isOutboundDone = false;
+ // indicates if close_notify alert had been sent to another peer
+ private boolean close_notify_was_sent = false;
+ // indicates if close_notify alert had been received from another peer
+ private boolean close_notify_was_received = false;
+ // indicates if engine was closed (it means that
+ // all the works on it are done, except (probably) some finalizing work)
+ private boolean engine_was_closed = false;
+ // indicates if engine was shutted down (it means that
+ // all cleaning work had been done and the engine is not operable)
+ private boolean engine_was_shutteddown = false;
+
+ // record protocol to be used
+ protected SSLRecordProtocol recordProtocol;
+ // input stream for record protocol
+ private SSLBufferedInput recProtIS;
+ // handshake protocol to be used
+ private HandshakeProtocol handshakeProtocol;
+ // alert protocol to be used
+ private AlertProtocol alertProtocol;
+ // place where application data will be stored
+ private SSLEngineAppData appData;
+ // outcoming application data stream
+ private SSLEngineDataStream dataStream = new SSLEngineDataStream();
+ // active session object
+ private SSLSessionImpl session;
+
+ // peer configuration parameters
+ protected SSLParameters sslParameters;
+
+ // in case of emergency situations when data could not be
+ // placed in destination buffers it will be stored in this
+ // fields
+ private byte[] remaining_wrapped_data = null;
+ private byte[] remaining_hsh_data = null;
+
+ // logger
+ private Logger.Stream logger = Logger.getStream("engine");
+
+ /**
+ * Ctor
+ * @param sslParameters: SSLParameters
+ */
+ protected SSLEngineImpl(SSLParameters sslParameters) {
+ super();
+ this.sslParameters = sslParameters;
+ }
+
+ /**
+ * Ctor
+ * @param host: String
+ * @param port: int
+ * @param sslParameters: SSLParameters
+ */
+ protected SSLEngineImpl(String host, int port, SSLParameters sslParameters) {
+ super(host, port);
+ this.sslParameters = sslParameters;
+ }
+
+ /**
+ * Starts the handshake.
+ * @throws SSLException
+ * @see javax.net.ssl.SSLEngine#beginHandshake() method documentation
+ * for more information
+ */
+ public void beginHandshake() throws SSLException {
+ if (engine_was_closed) {
+ throw new SSLException("Engine has already been closed.");
+ }
+ if (!peer_mode_was_set) {
+ throw new IllegalStateException("Client/Server mode was not set");
+ }
+ if (!handshake_started) {
+ handshake_started = true;
+ if (getUseClientMode()) {
+ handshakeProtocol = new ClientHandshakeImpl(this);
+ } else {
+ handshakeProtocol = new ServerHandshakeImpl(this);
+ }
+ appData = new SSLEngineAppData();
+ alertProtocol = new AlertProtocol();
+ recProtIS = new SSLBufferedInput();
+ recordProtocol = new SSLRecordProtocol(handshakeProtocol,
+ alertProtocol, recProtIS, appData);
+ }
+ handshakeProtocol.start();
+ }
+
+ /**
+ * Closes inbound operations of this engine
+ * @throws SSLException
+ * @see javax.net.ssl.SSLEngine#closeInbound() method documentation
+ * for more information
+ */
+ public void closeInbound() throws SSLException {
+ if (logger != null) {
+ logger.println("closeInbound() "+isInboundDone);
+ }
+ if (isInboundDone) {
+ return;
+ }
+ isInboundDone = true;
+ engine_was_closed = true;
+ if (handshake_started) {
+ if (!close_notify_was_received) {
+ if (session != null) {
+ session.invalidate();
+ }
+ alertProtocol.alert(AlertProtocol.FATAL,
+ AlertProtocol.INTERNAL_ERROR);
+ throw new SSLException("Inbound is closed before close_notify "
+ + "alert has been received.");
+ }
+ } else {
+ // engine is closing before initial handshake has been made
+ shutdown();
+ }
+ }
+
+ /**
+ * Closes outbound operations of this engine
+ * @see javax.net.ssl.SSLEngine#closeOutbound() method documentation
+ * for more information
+ */
+ public void closeOutbound() {
+ if (logger != null) {
+ logger.println("closeOutbound() "+isOutboundDone);
+ }
+ if (isOutboundDone) {
+ return;
+ }
+ isOutboundDone = true;
+ if (handshake_started) {
+ // initial handshake had been started
+ alertProtocol.alert(AlertProtocol.WARNING,
+ AlertProtocol.CLOSE_NOTIFY);
+ close_notify_was_sent = true;
+ } else {
+ // engine is closing before initial handshake has been made
+ shutdown();
+ }
+ engine_was_closed = true;
+ }
+
+ /**
+ * Returns handshake's delegated tasks to be run
+ * @return the delegated task to be executed.
+ * @see javax.net.ssl.SSLEngine#getDelegatedTask() method documentation
+ * for more information
+ */
+ public Runnable getDelegatedTask() {
+ return handshakeProtocol.getTask();
+ }
+
+ /**
+ * Returns names of supported cipher suites.
+ * @return array of strings containing the names of supported cipher suites
+ * @see javax.net.ssl.SSLEngine#getSupportedCipherSuites() method
+ * documentation for more information
+ */
+ public String[] getSupportedCipherSuites() {
+ return CipherSuite.getSupportedCipherSuiteNames();
+ }
+
+ // --------------- SSLParameters based methods ---------------------
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#getEnabledCipherSuites() method
+ * documentation for more information
+ */
+ public String[] getEnabledCipherSuites() {
+ return sslParameters.getEnabledCipherSuites();
+ }
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#setEnabledCipherSuites(String[]) method
+ * documentation for more information
+ */
+ public void setEnabledCipherSuites(String[] suites) {
+ sslParameters.setEnabledCipherSuites(suites);
+ }
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#getSupportedProtocols() method
+ * documentation for more information
+ */
+ public String[] getSupportedProtocols() {
+ return (String[]) ProtocolVersion.supportedProtocols.clone();
+ }
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#getEnabledProtocols() method
+ * documentation for more information
+ */
+ public String[] getEnabledProtocols() {
+ return sslParameters.getEnabledProtocols();
+ }
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#setEnabledProtocols(String[]) method
+ * documentation for more information
+ */
+ public void setEnabledProtocols(String[] protocols) {
+ sslParameters.setEnabledProtocols(protocols);
+ }
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#setUseClientMode(boolean) method
+ * documentation for more information
+ */
+ public void setUseClientMode(boolean mode) {
+ if (handshake_started) {
+ throw new IllegalArgumentException(
+ "Could not change the mode after the initial handshake has begun.");
+ }
+ sslParameters.setUseClientMode(mode);
+ peer_mode_was_set = true;
+ }
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#getUseClientMode() method
+ * documentation for more information
+ */
+ public boolean getUseClientMode() {
+ return sslParameters.getUseClientMode();
+ }
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#setNeedClientAuth(boolean) method
+ * documentation for more information
+ */
+ public void setNeedClientAuth(boolean need) {
+ sslParameters.setNeedClientAuth(need);
+ }
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#getNeedClientAuth() method
+ * documentation for more information
+ */
+ public boolean getNeedClientAuth() {
+ return sslParameters.getNeedClientAuth();
+ }
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#setWantClientAuth(boolean) method
+ * documentation for more information
+ */
+ public void setWantClientAuth(boolean want) {
+ sslParameters.setWantClientAuth(want);
+ }
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#getWantClientAuth() method
+ * documentation for more information
+ */
+ public boolean getWantClientAuth() {
+ return sslParameters.getWantClientAuth();
+ }
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#setEnableSessionCreation(boolean) method
+ * documentation for more information
+ */
+ public void setEnableSessionCreation(boolean flag) {
+ sslParameters.setEnableSessionCreation(flag);
+ }
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#getEnableSessionCreation() method
+ * documentation for more information
+ */
+ public boolean getEnableSessionCreation() {
+ return sslParameters.getEnableSessionCreation();
+ }
+
+ // -----------------------------------------------------------------
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#getHandshakeStatus() method
+ * documentation for more information
+ */
+ public SSLEngineResult.HandshakeStatus getHandshakeStatus() {
+ if (!handshake_started || engine_was_shutteddown) {
+ // initial handshake has not been started yet
+ return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
+ }
+ if (alertProtocol.hasAlert()) {
+ // need to send an alert
+ return SSLEngineResult.HandshakeStatus.NEED_WRAP;
+ }
+ if (close_notify_was_sent && !close_notify_was_received) {
+ // waiting for "close_notify" response
+ return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
+ }
+ return handshakeProtocol.getStatus();
+ }
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#getSession() method
+ * documentation for more information
+ */
+ public SSLSession getSession() {
+ if (session != null) {
+ return session;
+ } else {
+ return SSLSessionImpl.NULL_SESSION;
+ }
+ }
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#isInboundDone() method
+ * documentation for more information
+ */
+ public boolean isInboundDone() {
+ return isInboundDone || engine_was_closed;
+ }
+
+ /**
+ * This method works according to the specification of implemented class.
+ * @see javax.net.ssl.SSLEngine#isOutboundDone() method
+ * documentation for more information
+ */
+ public boolean isOutboundDone() {
+ return isOutboundDone;
+ }
+
+ /**
+ * Decodes one complete SSL/TLS record provided in the source buffer.
+ * If decoded record contained application data, this data will
+ * be placed in the destination buffers.
+ * For more information about TLS record fragmentation see
+ * TLS v 1 specification (http://www.ietf.org/rfc/rfc2246.txt) p 6.2.
+ * @param src source buffer containing SSL/TLS record.
+ * @param dsts destination buffers to place received application data.
+ * @see javax.net.ssl.SSLEngine#unwrap(ByteBuffer,ByteBuffer[],int,int)
+ * method documentation for more information
+ */
+ public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts,
+ int offset, int length) throws SSLException {
+ if (engine_was_shutteddown) {
+ return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
+ SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0);
+ }
+ if ((src == null) || (dsts == null)) {
+ throw new IllegalStateException(
+ "Some of the input parameters are null");
+ }
+
+ if (!handshake_started) {
+ beginHandshake();
+ }
+
+ SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
+ // If is is initial handshake or connection closure stage,
+ // check if this call was made in spite of handshake status
+ if ((session == null || engine_was_closed) && (
+ handshakeStatus.equals(
+ SSLEngineResult.HandshakeStatus.NEED_WRAP) ||
+ handshakeStatus.equals(
+ SSLEngineResult.HandshakeStatus.NEED_TASK))) {
+ return new SSLEngineResult(
+ getEngineStatus(), handshakeStatus, 0, 0);
+ }
+
+ if (src.remaining() < recordProtocol.getMinRecordSize()) {
+ return new SSLEngineResult(
+ SSLEngineResult.Status.BUFFER_UNDERFLOW,
+ getHandshakeStatus(), 0, 0);
+ }
+
+ try {
+ src.mark();
+ // check the destination buffers and count their capacity
+ int capacity = 0;
+ for (int i=offset; i<offset+length; i++) {
+ if (dsts[i] == null) {
+ throw new IllegalStateException(
+ "Some of the input parameters are null");
+ }
+ if (dsts[i].isReadOnly()) {
+ throw new ReadOnlyBufferException();
+ }
+ capacity += dsts[i].remaining();
+ }
+ if (capacity < recordProtocol.getDataSize(src.remaining())) {
+ return new SSLEngineResult(
+ SSLEngineResult.Status.BUFFER_OVERFLOW,
+ getHandshakeStatus(), 0, 0);
+ }
+ recProtIS.setSourceBuffer(src);
+ // unwrap the record contained in source buffer, pass it
+ // to appropriate client protocol (alert, handshake, or app)
+ // and retrieve the type of unwrapped data
+ int type = recordProtocol.unwrap();
+ // process the data and return the result
+ switch (type) {
+ case ContentType.HANDSHAKE:
+ case ContentType.CHANGE_CIPHER_SPEC:
+ if (handshakeProtocol.getStatus().equals(
+ SSLEngineResult.HandshakeStatus.FINISHED)) {
+ session = recordProtocol.getSession();
+ }
+ break;
+ case ContentType.APPLICATION_DATA:
+ break;
+ case ContentType.ALERT:
+ if (alertProtocol.isFatalAlert()) {
+ alertProtocol.setProcessed();
+ if (session != null) {
+ session.invalidate();
+ }
+ String description = "Fatal alert received "
+ + alertProtocol.getAlertDescription();
+ shutdown();
+ throw new SSLException(description);
+ } else {
+ if (logger != null) {
+ logger.println("Warning allert has been received: "
+ + alertProtocol.getAlertDescription());
+ }
+ switch(alertProtocol.getDescriptionCode()) {
+ case AlertProtocol.CLOSE_NOTIFY:
+ alertProtocol.setProcessed();
+ close_notify_was_received = true;
+ if (!close_notify_was_sent) {
+ closeOutbound();
+ closeInbound();
+ } else {
+ closeInbound();
+ shutdown();
+ }
+ break;
+ case AlertProtocol.NO_RENEGOTIATION:
+ alertProtocol.setProcessed();
+ if (session == null) {
+ // message received during the initial
+ // handshake
+ throw new AlertException(
+ AlertProtocol.HANDSHAKE_FAILURE,
+ new SSLHandshakeException(
+ "Received no_renegotiation "
+ + "during the initial handshake"));
+ } else {
+ // just stop the handshake
+ handshakeProtocol.stop();
+ }
+ break;
+ default:
+ alertProtocol.setProcessed();
+ }
+ }
+ break;
+ }
+ return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(),
+ recProtIS.consumed(),
+ // place the app. data (if any) into the dest. buffers
+ // and get the number of produced bytes:
+ appData.placeTo(dsts, offset, length));
+ } catch (BufferUnderflowException e) {
+ // there was not enought data ource buffer to make complete packet
+ src.reset();
+ return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW,
+ getHandshakeStatus(), 0, 0);
+ } catch (AlertException e) {
+ // fatal alert occured
+ alertProtocol.alert(AlertProtocol.FATAL, e.getDescriptionCode());
+ engine_was_closed = true;
+ src.reset();
+ if (session != null) {
+ session.invalidate();
+ }
+ // shutdown work will be made after the alert will be sent
+ // to another peer (by wrap method)
+ throw e.getReason();
+ } catch (SSLException e) {
+ throw e;
+ } catch (IOException e) {
+ alertProtocol.alert(AlertProtocol.FATAL,
+ AlertProtocol.INTERNAL_ERROR);
+ engine_was_closed = true;
+ // shutdown work will be made after the alert will be sent
+ // to another peer (by wrap method)
+ throw new SSLException(e.getMessage());
+ }
+ }
+
+ /**
+ * Encodes the application data into SSL/TLS record. If handshake status
+ * of the engine differs from NOT_HANDSHAKING the operation can work
+ * without consuming of the source data.
+ * For more information about TLS record fragmentation see
+ * TLS v 1 specification (http://www.ietf.org/rfc/rfc2246.txt) p 6.2.
+ * @param srcs the source buffers with application data to be encoded
+ * into SSL/TLS record.
+ * @param offset the offset in the destination buffers array pointing to
+ * the first buffer with the source data.
+ * @param len specifies the maximum number of buffers to be procesed.
+ * @param dst the destination buffer where encoded data will be placed.
+ * @see javax.net.ssl.SSLEngine#wrap(ByteBuffer[],int,int,ByteBuffer) method
+ * documentation for more information
+ */
+ public SSLEngineResult wrap(ByteBuffer[] srcs, int offset,
+ int len, ByteBuffer dst) throws SSLException {
+ if (engine_was_shutteddown) {
+ return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
+ SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0);
+ }
+ if ((srcs == null) || (dst == null)) {
+ throw new IllegalStateException(
+ "Some of the input parameters are null");
+ }
+ if (dst.isReadOnly()) {
+ throw new ReadOnlyBufferException();
+ }
+
+ if (!handshake_started) {
+ beginHandshake();
+ }
+
+ SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
+ // If it is an initial handshake or connection closure stage,
+ // check if this call was made in spite of handshake status
+ if ((session == null || engine_was_closed) && (
+ handshakeStatus.equals(
+ SSLEngineResult.HandshakeStatus.NEED_UNWRAP) ||
+ handshakeStatus.equals(
+ SSLEngineResult.HandshakeStatus.NEED_TASK))) {
+ return new SSLEngineResult(
+ getEngineStatus(), handshakeStatus, 0, 0);
+ }
+
+ int capacity = dst.remaining();
+ int produced = 0;
+
+ if (alertProtocol.hasAlert()) {
+ // we have an alert to be sent
+ if (capacity < recordProtocol.getRecordSize(2)) {
+ return new SSLEngineResult(
+ SSLEngineResult.Status.BUFFER_OVERFLOW,
+ handshakeStatus, 0, 0);
+ }
+ byte[] alert_data = alertProtocol.wrap();
+ // place the alert record into destination
+ dst.put(alert_data);
+ if (alertProtocol.isFatalAlert()) {
+ alertProtocol.setProcessed();
+ if (session != null) {
+ session.invalidate();
+ }
+ // fatal alert has been sent, so shut down the engine
+ shutdown();
+ return new SSLEngineResult(
+ SSLEngineResult.Status.CLOSED,
+ SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING,
+ 0, alert_data.length);
+ } else {
+ alertProtocol.setProcessed();
+ // check if the works on this engine have been done
+ if (close_notify_was_sent && close_notify_was_received) {
+ shutdown();
+ return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
+ SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING,
+ 0, alert_data.length);
+ }
+ return new SSLEngineResult(
+ getEngineStatus(),
+ getHandshakeStatus(),
+ 0, alert_data.length);
+ }
+ }
+
+ if (capacity < recordProtocol.getMinRecordSize()) {
+ if (logger != null) {
+ logger.println("Capacity of the destination("
+ +capacity+") < MIN_PACKET_SIZE("
+ +recordProtocol.getMinRecordSize()+")");
+ }
+ return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW,
+ handshakeStatus, 0, 0);
+ }
+
+ try {
+ if (!handshakeStatus.equals(
+ SSLEngineResult.HandshakeStatus.NEED_WRAP)) {
+ // so we wraps application data
+ dataStream.setSourceBuffers(srcs, offset, len);
+ if ((capacity < SSLRecordProtocol.MAX_SSL_PACKET_SIZE) &&
+ (capacity < recordProtocol.getRecordSize(
+ dataStream.available()))) {
+ if (logger != null) {
+ logger.println("The destination buffer("
+ +capacity+") can not take the resulting packet("
+ + recordProtocol.getRecordSize(
+ dataStream.available())+")");
+ }
+ return new SSLEngineResult(
+ SSLEngineResult.Status.BUFFER_OVERFLOW,
+ handshakeStatus, 0, 0);
+ }
+ if (remaining_wrapped_data == null) {
+ remaining_wrapped_data =
+ recordProtocol.wrap(ContentType.APPLICATION_DATA,
+ dataStream);
+ }
+ if (capacity < remaining_wrapped_data.length) {
+ // It should newer happen because we checked the destination
+ // buffer size, but there is a possibility
+ // (if dest buffer was filled outside)
+ // so we just remember the data into remaining_wrapped_data
+ // and will enclose it during the the next call
+ return new SSLEngineResult(
+ SSLEngineResult.Status.BUFFER_OVERFLOW,
+ handshakeStatus, dataStream.consumed(), 0);
+ } else {
+ dst.put(remaining_wrapped_data);
+ produced = remaining_wrapped_data.length;
+ remaining_wrapped_data = null;
+ return new SSLEngineResult(getEngineStatus(),
+ handshakeStatus, dataStream.consumed(), produced);
+ }
+ } else {
+ if (remaining_hsh_data == null) {
+ remaining_hsh_data = handshakeProtocol.wrap();
+ }
+ if (capacity < remaining_hsh_data.length) {
+ // It should newer happen because we checked the destination
+ // buffer size, but there is a possibility
+ // (if dest buffer was filled outside)
+ // so we just remember the data into remaining_hsh_data
+ // and will enclose it during the the next call
+ return new SSLEngineResult(
+ SSLEngineResult.Status.BUFFER_OVERFLOW,
+ handshakeStatus, 0, 0);
+ } else {
+ dst.put(remaining_hsh_data);
+ produced = remaining_hsh_data.length;
+ remaining_hsh_data = null;
+
+ handshakeStatus = handshakeProtocol.getStatus();
+ if (handshakeStatus.equals(
+ SSLEngineResult.HandshakeStatus.FINISHED)) {
+ session = recordProtocol.getSession();
+ }
+ }
+ return new SSLEngineResult(
+ getEngineStatus(), getHandshakeStatus(), 0, produced);
+ }
+ } catch (AlertException e) {
+ // fatal alert occured
+ alertProtocol.alert(AlertProtocol.FATAL, e.getDescriptionCode());
+ engine_was_closed = true;
+ if (session != null) {
+ session.invalidate();
+ }
+ // shutdown work will be made after the alert will be sent
+ // to another peer (by wrap method)
+ throw e.getReason();
+ }
+ }
+
+ // Shutdownes the engine and makes all cleanup work.
+ private void shutdown() {
+ engine_was_closed = true;
+ engine_was_shutteddown = true;
+ isOutboundDone = true;
+ isInboundDone = true;
+ if (handshake_started) {
+ alertProtocol.shutdown();
+ alertProtocol = null;
+ handshakeProtocol.shutdown();
+ handshakeProtocol = null;
+ recordProtocol.shutdown();
+ recordProtocol = null;
+ }
+ }
+
+
+ private SSLEngineResult.Status getEngineStatus() {
+ return (engine_was_closed)
+ ? SSLEngineResult.Status.CLOSED
+ : SSLEngineResult.Status.OK;
+ }
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java
new file mode 100644
index 0000000..bd9b6cf
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This class is a base for all input stream classes used
+ * in protocol implementation. It extends an InputStream with
+ * some additional read methods allowing to read TLS specific
+ * data types such as uint8, uint32 etc (see TLS v 1 specification
+ * at http://www.ietf.org/rfc/rfc2246.txt).
+ */
+public abstract class SSLInputStream extends InputStream {
+
+ /**
+ * @see java.io.InputStream#available()
+ */
+ public abstract int available() throws IOException;
+
+ /**
+ * Reads the following byte value. Note that in the case of
+ * reaching of the end of the data this methods throws the
+ * exception, not return -1. The type of exception depends
+ * on implementation. It was done for simplifying and speeding
+ * up of processing of such cases.
+ * @see org.apache.harmony.xnet.provider.jsse.SSLStreamedInput#read()
+ * @see org.apache.harmony.xnet.provider.jsse.SSLBufferedInput#read()
+ * @see org.apache.harmony.xnet.provider.jsse.HandshakeIODataStream#read()
+ */
+ public abstract int read() throws IOException;
+
+ /**
+ * @see java.io.InputStream#skip(long)
+ */
+ public long skip(long n) throws IOException {
+ long skept = n;
+ while (n > 0) {
+ read();
+ n--;
+ }
+ return skept;
+ }
+
+ /**
+ * Reads and returns uint8 value.
+ */
+ public int readUint8() throws IOException {
+ return read() & 0x00FF;
+ }
+
+ /**
+ * Reads and returns uint16 value.
+ */
+ public int readUint16() throws IOException {
+ return (read() << 8) | (read() & 0x00FF);
+ }
+
+ /**
+ * Reads and returns uint24 value.
+ */
+ public int readUint24() throws IOException {
+ return (read() << 16) | (read() << 8) | (read() & 0x00FF);
+ }
+
+ /**
+ * Reads and returns uint32 value.
+ */
+ public long readUint32() throws IOException {
+ return (read() << 24) | (read() << 16)
+ | (read() << 8) | (read() & 0x00FF);
+ }
+
+ /**
+ * Reads and returns uint64 value.
+ */
+ public long readUint64() throws IOException {
+ return (read() << 56) | (read() << 48)
+ | (read() << 40) | (read() << 32)
+ | (read() << 24) | (read() << 16)
+ | (read() << 8) | (read() & 0x00FF);
+ }
+
+ /**
+ * Returns the vector of opaque values of specified length;
+ * @param length - the length of the vector to be read.
+ * @return the read data
+ * @throws IOException if read operation could not be finished.
+ */
+ public byte[] read(int length) throws IOException {
+ byte[] res = new byte[length];
+ for (int i=0; i<length; i++) {
+ res[i] = (byte) read();
+ }
+ return res;
+ }
+
+ /**
+ * @see java.io.InputStream#read(byte[],int,int)
+ */
+ public int read(byte[] b, int off, int len) throws IOException {
+ int read_b;
+ int i = 0;
+ do {
+ if ((read_b = read()) == -1) {
+ return (i == 0) ? -1 : i;
+ }
+ b[off+i] = (byte) read_b;
+ i++;
+ } while ((available() != 0) && (i<len));
+ return i;
+ }
+}
+
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
new file mode 100644
index 0000000..0607e17
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java
@@ -0,0 +1,447 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.SSLSessionContextImpl;
+
+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 javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * The instances of this class incapsulate all the info
+ * about enabled cipher suites and protocols,
+ * as well as the information about client/server mode of
+ * ssl socket, whether it require/want client authentication or not,
+ * and controls whether new SSL sessions may be established by this
+ * socket or not.
+ */
+public class SSLParameters {
+
+ // default source of authentication keys
+ private static X509KeyManager defaultKeyManager;
+ // default source of authentication trust decisions
+ private static X509TrustManager defaultTrustManager;
+ // default source of random numbers
+ private static SecureRandom defaultSecureRandom;
+ // default SSL parameters
+ private static SSLParameters defaultParameters;
+
+ // client session context contains the set of reusable
+ // client-side SSL sessions
+ private SSLSessionContextImpl clientSessionContext;
+ // server session context contains the set of reusable
+ // server-side SSL sessions
+ private SSLSessionContextImpl serverSessionContext;
+ // source of authentication keys
+ private X509KeyManager keyManager;
+ // source of authentication trust decisions
+ private X509TrustManager trustManager;
+ // source of random numbers
+ private SecureRandom secureRandom;
+
+ // cipher suites available for SSL connection
+ // BEGIN android-removed
+ // protected CipherSuite[] enabledCipherSuites;
+ // END android-removed
+ // BEGIN android-added
+ private CipherSuite[] enabledCipherSuites;
+ // END android-added
+ // string representations of available cipher suites
+ private String[] enabledCipherSuiteNames = null;
+
+ // protocols available for SSL connection
+ private String[] enabledProtocols = ProtocolVersion.supportedProtocols;
+
+ // if the peer with this parameters tuned to work in client mode
+ private boolean client_mode = true;
+ // if the peer with this parameters tuned to require client authentication
+ private boolean need_client_auth = false;
+ // if the peer with this parameters tuned to request client authentication
+ private boolean want_client_auth = false;
+ // if the peer with this parameters allowed to cteate new SSL session
+ private boolean enable_session_creation = true;
+
+ /**
+ * Creates an instance of SSLParameters.
+ */
+ private SSLParameters() {
+ // BEGIN android-removed
+ // this.enabledCipherSuites = CipherSuite.defaultCipherSuites;
+ // END android-removed
+ }
+
+ // BEGIN android-added
+ protected CipherSuite[] getEnabledCipherSuitesMember() {
+ if (enabledCipherSuites == null) this.enabledCipherSuites = CipherSuite.defaultCipherSuites;
+ return enabledCipherSuites;
+ }
+
+ /**
+ * Holds a pointer to our native SSL context.
+ */
+ private int ssl_ctx = 0;
+
+ /**
+ * Initializes our native SSL context.
+ */
+ private native int nativeinitsslctx();
+
+ /**
+ * Returns the native SSL context, creating it on-the-fly, if necessary.
+ */
+ protected synchronized int getSSLCTX() {
+ if (ssl_ctx == 0) ssl_ctx = nativeinitsslctx();
+ return ssl_ctx;
+ }
+ // END android-added
+
+ /**
+ * Initializes the parameters. Naturally this constructor is used
+ * in SSLContextImpl.engineInit method which dirrectly passes its
+ * parameters. In other words this constructor holds all
+ * the functionality provided by SSLContext.init method.
+ * See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[],SecureRandom)}
+ * for more information
+ */
+ protected SSLParameters(KeyManager[] kms, TrustManager[] tms,
+ SecureRandom sr, SSLSessionContextImpl clientSessionContext,
+ SSLSessionContextImpl serverSessionContext)
+ throws KeyManagementException {
+ this();
+ this.serverSessionContext = serverSessionContext;
+ this.clientSessionContext = clientSessionContext;
+ try {
+ // initialize key manager
+ boolean initialize_default = false;
+ // It's not described by the spec of SSLContext what should happen
+ // if the arrays of length 0 are specified. This implementation
+ // behave as for null arrays (i.e. use installed security providers)
+ if ((kms == null) || (kms.length == 0)) {
+ if (defaultKeyManager == null) {
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(
+ KeyManagerFactory.getDefaultAlgorithm());
+ kmf.init(null, null);
+ kms = kmf.getKeyManagers();
+ // tell that we are trying to initialize defaultKeyManager
+ initialize_default = true;
+ } else {
+ keyManager = defaultKeyManager;
+ }
+ }
+ if (keyManager == null) { // was not initialized by default
+ for (int i = 0; i < kms.length; i++) {
+ if (kms[i] instanceof X509KeyManager) {
+ keyManager = (X509KeyManager)kms[i];
+ break;
+ }
+ }
+ if (keyManager == null) {
+ throw new KeyManagementException("No X509KeyManager found");
+ }
+ if (initialize_default) {
+ // found keyManager is default key manager
+ defaultKeyManager = keyManager;
+ }
+ }
+
+ // initialize trust manager
+ initialize_default = false;
+ if ((tms == null) || (tms.length == 0)) {
+ if (defaultTrustManager == null) {
+ TrustManagerFactory tmf = TrustManagerFactory
+ .getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init((KeyStore)null);
+ tms = tmf.getTrustManagers();
+ initialize_default = true;
+ } else {
+ trustManager = defaultTrustManager;
+ }
+ }
+ if (trustManager == null) { // was not initialized by default
+ for (int i = 0; i < tms.length; i++) {
+ if (tms[i] instanceof X509TrustManager) {
+ trustManager = (X509TrustManager)tms[i];
+ break;
+ }
+ }
+ if (trustManager == null) {
+ throw new KeyManagementException("No X509TrustManager found");
+ }
+ if (initialize_default) {
+ // found trustManager is default trust manager
+ defaultTrustManager = trustManager;
+ }
+ }
+ } catch (NoSuchAlgorithmException e) {
+ throw new KeyManagementException(e);
+ } catch (KeyStoreException e) {
+ throw new KeyManagementException(e);
+ } catch (UnrecoverableKeyException e) {
+ throw new KeyManagementException(e);
+ }
+ // initialize secure random
+ // BEGIN android-removed
+ // if (sr == null) {
+ // if (defaultSecureRandom == null) {
+ // defaultSecureRandom = new SecureRandom();
+ // }
+ // secureRandom = defaultSecureRandom;
+ // } else {
+ // secureRandom = sr;
+ // }
+ // END android-removed
+ // BEGIN android-added
+ // We simply use the SecureRandom passed in by the caller. If it's
+ // null, we don't replace it by a new instance. The native code below
+ // then directly accesses /dev/urandom. Not the most elegant solution,
+ // but faster that going through the SecureRandom object.
+ secureRandom = sr;
+ // END android-added
+ }
+
+ protected static SSLParameters getDefault() throws KeyManagementException {
+ if (defaultParameters == null) {
+ defaultParameters = new SSLParameters(null, null, null,
+ new SSLSessionContextImpl(), new SSLSessionContextImpl());
+ }
+ return (SSLParameters) defaultParameters.clone();
+ }
+
+ /**
+ * @return server session context
+ */
+ protected SSLSessionContextImpl getServerSessionContext() {
+ return serverSessionContext;
+ }
+
+ /**
+ * @return client session context
+ */
+ protected SSLSessionContextImpl getClientSessionContext() {
+ return clientSessionContext;
+ }
+
+ /**
+ * @return key manager
+ */
+ protected X509KeyManager getKeyManager() {
+ return keyManager;
+ }
+
+ /**
+ * @return trust manager
+ */
+ protected X509TrustManager getTrustManager() {
+ return trustManager;
+ }
+
+ /**
+ * @return secure random
+ */
+ protected SecureRandom getSecureRandom() {
+ // BEGIN android-removed
+ // return secureRandom;
+ // END android-removed
+ // BEGIN android-added
+ if (secureRandom != null) return secureRandom;
+ if (defaultSecureRandom == null)
+ {
+ defaultSecureRandom = new SecureRandom();
+ }
+ secureRandom = defaultSecureRandom;
+ return secureRandom;
+ // END android-added
+ }
+
+ // BEGIN android-added
+ /**
+ * @return the secure random member reference, even it is null
+ */
+ protected SecureRandom getSecureRandomMember() {
+ return secureRandom;
+ }
+ // END android-added
+
+ /**
+ * @return the names of enabled cipher suites
+ */
+ protected String[] getEnabledCipherSuites() {
+ if (enabledCipherSuiteNames == null) {
+ // BEGIN android-added
+ CipherSuite[] enabledCipherSuites = getEnabledCipherSuitesMember();
+ // END android-added
+ enabledCipherSuiteNames = new String[enabledCipherSuites.length];
+ for (int i = 0; i< enabledCipherSuites.length; i++) {
+ enabledCipherSuiteNames[i] = enabledCipherSuites[i].getName();
+ }
+ }
+ return (String[]) enabledCipherSuiteNames.clone();
+ }
+
+ /**
+ * Sets the set of available cipher suites for use in SSL connection.
+ * @param suites: String[]
+ * @return
+ */
+ protected void setEnabledCipherSuites(String[] suites) {
+ if (suites == null) {
+ throw new IllegalArgumentException("Provided parameter is null");
+ }
+ CipherSuite[] cipherSuites = new CipherSuite[suites.length];
+ for (int i=0; i<suites.length; i++) {
+ cipherSuites[i] = CipherSuite.getByName(suites[i]);
+ if (cipherSuites[i] == null || !cipherSuites[i].supported) {
+ throw new IllegalArgumentException(suites[i] +
+ " is not supported.");
+ }
+ }
+ enabledCipherSuites = cipherSuites;
+ enabledCipherSuiteNames = suites;
+ }
+
+ /**
+ * @return the set of enabled protocols
+ */
+ protected String[] getEnabledProtocols() {
+ return (String[]) enabledProtocols.clone();
+ }
+
+ /**
+ * Sets the set of available protocols for use in SSL connection.
+ * @param suites: String[]
+ */
+ protected void setEnabledProtocols(String[] protocols) {
+ if (protocols == null) {
+ throw new IllegalArgumentException("Provided parameter is null");
+ }
+ for (int i=0; i<protocols.length; i++) {
+ if (!ProtocolVersion.isSupported(protocols[i])) {
+ throw new IllegalArgumentException("Protocol " + protocols[i] +
+ " is not supported.");
+ }
+ }
+ enabledProtocols = protocols;
+ }
+
+ /**
+ * Tunes the peer holding this parameters to work in client mode.
+ * @param mode if the peer is configured to work in client mode
+ */
+ protected void setUseClientMode(boolean mode) {
+ client_mode = mode;
+ }
+
+ /**
+ * Returns the value indicating if the parameters configured to work
+ * in client mode.
+ */
+ protected boolean getUseClientMode() {
+ return client_mode;
+ }
+
+ /**
+ * Tunes the peer holding this parameters to require client authentication
+ */
+ protected void setNeedClientAuth(boolean need) {
+ need_client_auth = need;
+ // reset the want_client_auth setting
+ want_client_auth = false;
+ }
+
+ /**
+ * Returns the value indicating if the peer with this parameters tuned
+ * to require client authentication
+ */
+ protected boolean getNeedClientAuth() {
+ return need_client_auth;
+ }
+
+ /**
+ * Tunes the peer holding this parameters to request client authentication
+ */
+ protected void setWantClientAuth(boolean want) {
+ want_client_auth = want;
+ // reset the need_client_auth setting
+ need_client_auth = false;
+ }
+
+ /**
+ * Returns the value indicating if the peer with this parameters
+ * tuned to request client authentication
+ * @return
+ */
+ protected boolean getWantClientAuth() {
+ return want_client_auth;
+ }
+
+ /**
+ * Allows/disallows the peer holding this parameters to
+ * create new SSL session
+ */
+ protected void setEnableSessionCreation(boolean flag) {
+ enable_session_creation = flag;
+ }
+
+ /**
+ * Returns the value indicating if the peer with this parameters
+ * allowed to cteate new SSL session
+ */
+ protected boolean getEnableSessionCreation() {
+ return enable_session_creation;
+ }
+
+ /**
+ * Returns the clone of this object.
+ * @return the clone.
+ */
+ protected Object clone() {
+ SSLParameters parameters = new SSLParameters();
+
+ parameters.clientSessionContext = clientSessionContext;
+ parameters.serverSessionContext = serverSessionContext;
+ parameters.keyManager = keyManager;
+ parameters.trustManager = trustManager;
+ parameters.secureRandom = secureRandom;
+
+ parameters.enabledCipherSuites = enabledCipherSuites;
+ parameters.enabledProtocols = enabledProtocols;
+
+ parameters.client_mode = client_mode;
+ parameters.need_client_auth = need_client_auth;
+ parameters.want_client_auth = want_client_auth;
+ parameters.enable_session_creation = enable_session_creation;
+
+ return parameters;
+ }
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLRecordProtocol.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLRecordProtocol.java
new file mode 100644
index 0000000..4428820
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLRecordProtocol.java
@@ -0,0 +1,488 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.AlertException;
+import org.apache.harmony.xnet.provider.jsse.SSLSessionImpl;
+import org.apache.harmony.xnet.provider.jsse.SSLInputStream;
+
+import java.io.IOException;
+import javax.net.ssl.SSLProtocolException;
+
+/**
+ * This class performs functionality dedicated to SSL record layer.
+ * It unpacks and routes income data to the appropriate
+ * client protocol (handshake, alert, application data protocols)
+ * and paketizes outcome data into SSL/TLS records.
+ * Initially created object has null connection state and does not
+ * perform any cryptography computations over the income/outcome data.
+ * After handshake protocol agreed upon security parameters they are placed
+ * into SSLSessionImpl object and available for record protocol as
+ * pending session. The order of setting up of the pending session
+ * as an active session differs for client and server modes.
+ * So for client mode the parameters are provided by handshake protocol
+ * during retrieving of change_cipher_spec message to be sent (by calling of
+ * getChangeCipherSpecMesage method).
+ * For server side mode record protocol retrieves the parameters from
+ * handshake protocol after receiving of client's change_cipher_spec message.
+ * After the pending session has been setted up as a curent session,
+ * new connectin state object is created and used for encryption/decryption
+ * of the messages.
+ * Among with base functionality this class provides the information about
+ * constrains on the data length, and information about correspondance
+ * of plain and encrypted data lengths.
+ * For more information on TLS v1 see http://www.ietf.org/rfc/rfc2246.txt,
+ * on SSL v3 see http://wp.netscape.com/eng/ssl3,
+ * on SSL v2 see http://wp.netscape.com/eng/security/SSL_2.html.
+ */
+public class SSLRecordProtocol {
+
+ /**
+ * Maximum length of allowed plain data fragment
+ * as specified by TLS specification.
+ */
+ protected static int MAX_DATA_LENGTH = 16384; // 2^14
+ /**
+ * Maximum length of allowed compressed data fragment
+ * as specified by TLS specification.
+ */
+ protected static int MAX_COMPRESSED_DATA_LENGTH
+ = MAX_DATA_LENGTH + 1024;
+ /**
+ * Maximum length of allowed ciphered data fragment
+ * as specified by TLS specification.
+ */
+ protected static int MAX_CIPHERED_DATA_LENGTH
+ = MAX_COMPRESSED_DATA_LENGTH + 1024;
+ /**
+ * Maximum length of ssl record. It is counted as:
+ * type(1) + version(2) + length(2) + MAX_CIPHERED_DATA_LENGTH
+ */
+ protected static int MAX_SSL_PACKET_SIZE
+ = MAX_CIPHERED_DATA_LENGTH + 5;
+ // the SSL session used for connection
+ private SSLSessionImpl session;
+ // protocol version of the connection
+ private byte[] version;
+ // input stream of record protocol
+ private SSLInputStream in;
+ // handshake protocol object to which handshaking data will be transmitted
+ private HandshakeProtocol handshakeProtocol;
+ // alert protocol to indicate alerts occured/received
+ private AlertProtocol alertProtocol;
+ // application data object to which application data will be transmitted
+ private org.apache.harmony.xnet.provider.jsse.Appendable appData;
+ // connection state holding object
+ private ConnectionState
+ activeReadState, activeWriteState, pendingConnectionState;
+
+ // logger
+ private Logger.Stream logger = Logger.getStream("record");
+
+ // flag indicating if session object has been changed after
+ // handshake phase (to distinguish session pending state)
+ private boolean sessionWasChanged = false;
+
+ // change cipher spec message content
+ private static final byte[] change_cipher_spec_byte = new byte[] {1};
+
+ /**
+ * Creates an instance of record protocol and tunes
+ * up the client protocols to use ut.
+ * @param handshakeProtocol: HandshakeProtocol
+ * @param alertProtocol: AlertProtocol
+ * @param in: SSLInputStream
+ * @param appData: Appendable
+ */
+ protected SSLRecordProtocol(HandshakeProtocol handshakeProtocol,
+ AlertProtocol alertProtocol,
+ SSLInputStream in,
+ Appendable appData) {
+ this.handshakeProtocol = handshakeProtocol;
+ this.handshakeProtocol.setRecordProtocol(this);
+ this.alertProtocol = alertProtocol;
+ this.alertProtocol.setRecordProtocol(this);
+ this.in = in;
+ this.appData = appData;
+ }
+
+ /**
+ * Returns the session obtained during the handshake negotiation.
+ * If the handshake process was not compleated, method returns null.
+ * @return the session in effect.
+ */
+ protected SSLSessionImpl getSession() {
+ return session;
+ }
+
+ /**
+ * Returns the minimum possible length of the SSL record.
+ * @return
+ */
+ protected int getMinRecordSize() {
+ return (activeReadState == null)
+ ? 6 // type + version + length + 1 byte of data
+ : 5 + activeReadState.getMinFragmentSize();
+ }
+
+ /**
+ * Returns the record length for the specified incoming data length.
+ * If actual resulting record length is greater than
+ * MAX_CIPHERED_DATA_LENGTH, MAX_CIPHERED_DATA_LENGTH is returned.
+ */
+ protected int getRecordSize(int data_size) {
+ if (activeWriteState == null) {
+ return 5+data_size; // type + version + length + data_size
+ } else {
+ int res = 5 + activeWriteState.getFragmentSize(data_size);
+ return (res > MAX_CIPHERED_DATA_LENGTH)
+ ? MAX_CIPHERED_DATA_LENGTH // so the source data should be
+ // splitted into several packets
+ : res;
+ }
+ }
+
+ /**
+ * Returns the upper bound of length of data containing in the record with
+ * specified length.
+ * If the provided record_size is greater or equal to
+ * MAX_CIPHERED_DATA_LENGTH the returned value will be
+ * MAX_DATA_LENGTH
+ * counted as for data with
+ * MAX_CIPHERED_DATA_LENGTH length.
+ */
+ protected int getDataSize(int record_size) {
+ record_size -= 5; // - (type + version + length + data_size)
+ if (record_size > MAX_CIPHERED_DATA_LENGTH) {
+ // the data of such size consists of the several packets
+ return MAX_DATA_LENGTH;
+ }
+ if (activeReadState == null) {
+ return record_size;
+ } else {
+ return activeReadState.getContentSize(record_size);
+ }
+ }
+
+ /**
+ * Depending on the Connection State (Session) encrypts and compress
+ * the provided data, and packs it into TLSCiphertext structute.
+ * @param content_type: int
+ * @param fragment: byte[]
+ * @return ssl packet created over the current connection state
+ */
+ protected byte[] wrap(byte content_type, DataStream dataStream) {
+ byte[] fragment = dataStream.getData(MAX_DATA_LENGTH);
+ return wrap(content_type, fragment, 0, fragment.length);
+ }
+
+ /**
+ * Depending on the Connection State (Session) encrypts and compress
+ * the provided data, and packs it into TLSCiphertext structute.
+ * @param content_type: int
+ * @param fragment: byte[]
+ * @return ssl packet created over the current connection state
+ */
+ protected byte[] wrap(byte content_type,
+ byte[] fragment, int offset, int len) {
+ if (logger != null) {
+ logger.println("SSLRecordProtocol.wrap: TLSPlaintext.fragment["
+ +len+"]:");
+ logger.print(fragment, offset, len);
+ }
+ if (len > MAX_DATA_LENGTH) {
+ throw new AlertException(
+ AlertProtocol.INTERNAL_ERROR,
+ new SSLProtocolException(
+ "The provided chunk of data is too big: " + len
+ + " > MAX_DATA_LENGTH == "+MAX_DATA_LENGTH));
+ }
+ byte[] ciphered_fragment = fragment;
+ if (activeWriteState != null) {
+ ciphered_fragment =
+ activeWriteState.encrypt(content_type, fragment, offset, len);
+ if (ciphered_fragment.length > MAX_CIPHERED_DATA_LENGTH) {
+ throw new AlertException(
+ AlertProtocol.INTERNAL_ERROR,
+ new SSLProtocolException(
+ "The ciphered data increased more than on 1024 bytes"));
+ }
+ if (logger != null) {
+ logger.println("SSLRecordProtocol.wrap: TLSCiphertext.fragment["
+ +ciphered_fragment.length+"]:");
+ logger.print(ciphered_fragment);
+ }
+ }
+ return packetize(content_type, version, ciphered_fragment);
+ }
+
+ private byte[] packetize(byte type, byte[] version, byte[] fragment) {
+ byte[] buff = new byte[5+fragment.length];
+ buff[0] = type;
+ if (version != null) {
+ buff[1] = version[0];
+ buff[2] = version[1];
+ } else {
+ buff[1] = 3;
+ buff[2] = 1;
+ }
+ buff[3] = (byte) ((0x00FF00 & fragment.length) >> 8);
+ buff[4] = (byte) (0x0000FF & fragment.length);
+ System.arraycopy(fragment, 0, buff, 5, fragment.length);
+ return buff;
+ }
+
+ /**
+ * Set the ssl session to be used after sending the changeCipherSpec message
+ * @param session: SSLSessionImpl
+ */
+ private void setSession(SSLSessionImpl session) {
+ if (!sessionWasChanged) {
+ // session was not changed for current handshake process
+ if (logger != null) {
+ logger.println("SSLRecordProtocol.setSession: Set pending session");
+ logger.println(" cipher name: " + session.getCipherSuite());
+ }
+ this.session = session;
+ // create new connection state
+ pendingConnectionState = ((version == null) || (version[1] == 1))
+ ? (ConnectionState) new ConnectionStateTLS(getSession())
+ : (ConnectionState) new ConnectionStateSSLv3(getSession());
+ sessionWasChanged = true;
+ } else {
+ // wait for rehandshaking's session
+ sessionWasChanged = false;
+ }
+ }
+
+ /**
+ * Returns the change cipher spec message to be sent to another peer.
+ * The pending connection state will be built on the base of provided
+ * session object
+ * The calling of this method triggers pending write connection state to
+ * be active.
+ * @return ssl record containing the "change cipher spec" message.
+ */
+ protected byte[] getChangeCipherSpecMesage(SSLSessionImpl session) {
+ // make change_cipher_spec_message:
+ byte[] change_cipher_spec_message;
+ if (activeWriteState == null) {
+ change_cipher_spec_message = new byte[] {
+ ContentType.CHANGE_CIPHER_SPEC, version[0],
+ version[1], 0, 1, 1
+ };
+ } else {
+ change_cipher_spec_message =
+ packetize(ContentType.CHANGE_CIPHER_SPEC, version,
+ activeWriteState.encrypt(ContentType.CHANGE_CIPHER_SPEC,
+ change_cipher_spec_byte, 0, 1));
+ }
+ setSession(session);
+ activeWriteState = pendingConnectionState;
+ if (logger != null) {
+ logger.println("SSLRecordProtocol.getChangeCipherSpecMesage");
+ logger.println("activeWriteState = pendingConnectionState");
+ logger.print(change_cipher_spec_message);
+ }
+ return change_cipher_spec_message;
+ }
+
+ /**
+ * Retrieves the fragment field of TLSCiphertext, and than
+ * depending on the established Connection State
+ * decrypts and decompresses it. The following structure is expected
+ * on the input at the moment of the call:
+ *
+ * struct {
+ * ContentType type;
+ * ProtocolVersion version;
+ * uint16 length;
+ * select (CipherSpec.cipher_type) {
+ * case stream: GenericStreamCipher;
+ * case block: GenericBlockCipher;
+ * } fragment;
+ * } TLSCiphertext;
+ *
+ * (as specified by RFC 2246, TLS v1 Protocol specification)
+ *
+ * In addition this method can recognize SSLv2 hello message which
+ * are often used to establish the SSL/TLS session.
+ *
+ * @throws IOException if some io errors have been occured
+ * @throws EndOfSourceException if underlying input stream
+ * has ran out of data.
+ * @throws EndOfBufferException if there was not enought data
+ * to build complete ssl packet.
+ * @return the type of unwrapped message.
+ */
+ protected int unwrap() throws IOException {
+ if (logger != null) {
+ logger.println("SSLRecordProtocol.unwrap: BEGIN [");
+ }
+ int type = in.readUint8();
+ if ((type < ContentType.CHANGE_CIPHER_SPEC)
+ || (type > ContentType.APPLICATION_DATA)) {
+ if (logger != null) {
+ logger.println("Non v3.1 message type:" + type);
+ }
+ if (type >= 0x80) {
+ // it is probably SSL v2 client_hello message
+ // (see SSL v2 spec at:
+ // http://wp.netscape.com/eng/security/SSL_2.html)
+ int length = (type & 0x7f) << 8 | in.read();
+ byte[] fragment = in.read(length);
+ handshakeProtocol.unwrapSSLv2(fragment);
+ if (logger != null) {
+ logger.println(
+ "SSLRecordProtocol:unwrap ] END, SSLv2 type");
+ }
+ return ContentType.HANDSHAKE;
+ }
+ throw new AlertException(AlertProtocol.UNEXPECTED_MESSAGE,
+ new SSLProtocolException(
+ "Unexpected message type has been received: "+type));
+ }
+ if (logger != null) {
+ logger.println("Got the message of type: " + type);
+ }
+ if (version != null) {
+ if ((in.read() != version[0])
+ || (in.read() != version[1])) {
+ throw new AlertException(AlertProtocol.UNEXPECTED_MESSAGE,
+ new SSLProtocolException(
+ "Unexpected message type has been received: " +
+ type));
+ }
+ } else {
+ in.skip((long) 2); // just skip the version number
+ }
+ int length = in.readUint16();
+ if (logger != null) {
+ logger.println("TLSCiphertext.fragment["+length+"]: ...");
+ }
+ if (length > MAX_CIPHERED_DATA_LENGTH) {
+ throw new AlertException(AlertProtocol.RECORD_OVERFLOW,
+ new SSLProtocolException(
+ "Received message is too big."));
+ }
+ byte[] fragment = in.read(length);
+ if (logger != null) {
+ logger.print(fragment);
+ }
+ if (activeReadState != null) {
+ fragment = activeReadState.decrypt((byte) type, fragment);
+ if (logger != null) {
+ logger.println("TLSPlaintext.fragment:");
+ logger.print(fragment);
+ }
+ }
+ if (fragment.length > MAX_DATA_LENGTH) {
+ throw new AlertException(AlertProtocol.DECOMPRESSION_FAILURE,
+ new SSLProtocolException(
+ "Decompressed plain data is too big."));
+ }
+ switch (type) {
+ case ContentType.CHANGE_CIPHER_SPEC:
+ // notify handshake protocol:
+ handshakeProtocol.receiveChangeCipherSpec();
+ setSession(handshakeProtocol.getSession());
+ // change cipher spec message has been received, so:
+ if (logger != null) {
+ logger.println("activeReadState = pendingConnectionState");
+ }
+ activeReadState = pendingConnectionState;
+ break;
+ case ContentType.ALERT:
+ alert(fragment[0], fragment[1]);
+ break;
+ case ContentType.HANDSHAKE:
+ handshakeProtocol.unwrap(fragment);
+ break;
+ case ContentType.APPLICATION_DATA:
+ if (logger != null) {
+ logger.println(
+ "TLSCiphertext.unwrap: APP DATA["+length+"]:");
+ logger.println(new String(fragment));
+ }
+ appData.append(fragment);
+ break;
+ default:
+ throw new AlertException(AlertProtocol.UNEXPECTED_MESSAGE,
+ new SSLProtocolException(
+ "Unexpected message type has been received: " +
+ type));
+ }
+ if (logger != null) {
+ logger.println("SSLRecordProtocol:unwrap ] END, type: " + type);
+ }
+ return type;
+ }
+
+ /**
+ * Passes the alert information to the alert protocol.
+ * @param level: byte
+ * @param description: byte
+ */
+ protected void alert(byte level, byte description) {
+ if (logger != null) {
+ logger.println("SSLRecordProtocol.allert: "+level+" "+description);
+ }
+ alertProtocol.alert(level, description);
+ }
+
+ /**
+ * Sets up the SSL version used in this connection.
+ * This method is calling from the hanshake protocol after
+ * it becomes known witch protocol version will be used.
+ * @param ver: byte[]
+ * @return
+ */
+ protected void setVersion(byte[] ver) {
+ this.version = ver;
+ }
+
+ /**
+ * Shutdownes the protocol. It will be impossiblke to use the instance
+ * after the calling of this method.
+ */
+ protected void shutdown() {
+ session = null;
+ version = null;
+ in = null;
+ handshakeProtocol = null;
+ alertProtocol = null;
+ appData = null;
+ if (pendingConnectionState != null) {
+ pendingConnectionState.shutdown();
+ }
+ pendingConnectionState = null;
+ if (activeReadState != null) {
+ activeReadState.shutdown();
+ }
+ activeReadState = null;
+ if (activeReadState != null) {
+ activeReadState.shutdown();
+ }
+ activeWriteState = null;
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionContextImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionContextImpl.java
new file mode 100644
index 0000000..7ae4c73
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionContextImpl.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+package org.apache.harmony.xnet.provider.jsse;
+
+//BEGIN android-changed
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+
+/**
+ * SSLSessionContext implementation
+ *
+ * @see javax.net.ssl.SSLSessionContext
+ */
+public class SSLSessionContextImpl implements SSLSessionContext {
+
+ private int cacheSize = 20;
+ private long timeout = 0;
+ private final LinkedHashMap<byte[], SSLSession> sessions =
+ new LinkedHashMap<byte[],SSLSession>(cacheSize, 0.75f, true) {
+ public boolean removeEldestEntry(Map.Entry eldest) {
+ return cacheSize > 0 && this.size() > cacheSize;
+ }
+ };
+ private volatile LinkedHashMap<byte[], SSLSession> clone = new LinkedHashMap<byte[], SSLSession>();
+
+ /**
+ * @see javax.net.ssl.SSLSessionContext#getIds()
+ */
+ public Enumeration<byte[]> getIds() {
+ return new Enumeration<byte[]>() {
+ Iterator<byte[]> iterator = clone.keySet().iterator();
+ public boolean hasMoreElements() {
+ return iterator.hasNext();
+ }
+ public byte[] nextElement() {
+ return iterator.next();
+ }
+ };
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSessionContext#getSession(byte[] sessionId)
+ */
+ public SSLSession getSession(byte[] sessionId) {
+ synchronized (sessions) {
+ return (SSLSession) sessions.get(sessionId);
+ }
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSessionContext#getSessionCacheSize()
+ */
+ public int getSessionCacheSize() {
+ return cacheSize;
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSessionContext#getSessionTimeout()
+ */
+ public int getSessionTimeout() {
+ return (int) (timeout/1000);
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSessionContext#setSessionCacheSize(int size)
+ */
+ public void setSessionCacheSize(int size) throws IllegalArgumentException {
+ if (size < 0) {
+ throw new IllegalArgumentException("size < 0");
+ }
+ synchronized (sessions) {
+ cacheSize = size;
+ Set<byte[]> set = sessions.keySet();
+ if (cacheSize > 0 && cacheSize < set.size()) {
+ // Resize the cache to the maximum
+ Iterator<byte[]> iterator = set.iterator();
+ for (int i = 0; iterator.hasNext(); i++) {
+ iterator.next();
+ if (i >= cacheSize) {
+ iterator.remove();
+ }
+ }
+ }
+ clone = (LinkedHashMap<byte[], SSLSession>) sessions.clone();
+ }
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSessionContext#setSessionTimeout(int seconds)
+ */
+ public void setSessionTimeout(int seconds) throws IllegalArgumentException {
+ if (seconds < 0) {
+ throw new IllegalArgumentException("seconds < 0");
+ }
+
+ synchronized (sessions) {
+ timeout = seconds*1000;
+ // Check timeouts and remove expired sessions
+ SSLSession ses;
+ for (Iterator<byte[]> iterator = sessions.keySet().iterator(); iterator.hasNext();) {
+ ses = (SSLSession)(sessions.get(iterator.next()));
+ if (!ses.isValid()) {
+ iterator.remove();
+ }
+ }
+ clone = (LinkedHashMap<byte[], SSLSession>) sessions.clone();
+ }
+ }
+
+ /**
+ * Adds session to the session cache
+ * @param ses
+ */
+ void putSession(SSLSession ses) {
+ synchronized (sessions) {
+ sessions.put(ses.getId(), ses);
+ clone = (LinkedHashMap<byte[], SSLSession>) sessions.clone();
+ }
+ }
+}
+// END android-changed \ No newline at end of file
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
new file mode 100644
index 0000000..df46094
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java
@@ -0,0 +1,410 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLPermission;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionBindingEvent;
+import javax.net.ssl.SSLSessionBindingListener;
+import javax.net.ssl.SSLSessionContext;
+
+import org.apache.harmony.luni.util.TwoKeyHashMap;
+import org.apache.harmony.xnet.provider.jsse.SSLSessionContextImpl;
+
+/**
+ *
+ * SSLSession implementation
+ *
+ * @see javax.net.ssl.SSLSession
+ */
+public class SSLSessionImpl implements SSLSession {
+
+ /**
+ * Session object reporting an invalid cipher suite of
+ * "SSL_NULL_WITH_NULL_NULL"
+ */
+ public static final SSLSessionImpl NULL_SESSION = new SSLSessionImpl(null);
+
+ private long creationTime;
+ private boolean isValid = true;
+ private TwoKeyHashMap values = new TwoKeyHashMap();
+
+ /**
+ * ID of the session
+ */
+ byte[] id;
+
+ /**
+ * Last time the session was accessed
+ */
+ long lastAccessedTime;
+
+ /**
+ * Protocol used in the session
+ */
+ ProtocolVersion protocol;
+
+ /**
+ * CipherSuite used in the session
+ */
+ CipherSuite cipherSuite;
+
+ /**
+ * Context of the session
+ */
+ SSLSessionContextImpl context;
+
+
+ /**
+ * certificates were sent to the peer
+ */
+ X509Certificate[] localCertificates;
+
+ /**
+ * Peer certificates
+ */
+ X509Certificate[] peerCertificates;
+
+ /**
+ * Peer host name
+ */
+ String peerHost;
+
+ /**
+ * Peer port number
+ */
+ int peerPort = -1;
+
+ /**
+ * Master secret
+ */
+ byte[] master_secret;
+
+
+ /**
+ * clientRandom
+ */
+ byte[] clientRandom;
+
+ /**
+ * serverRandom
+ */
+ byte[] serverRandom;
+
+ /**
+ * True if this entity is considered the server
+ */
+ boolean isServer = false;
+
+ /**
+ * Creates SSLSession implementation
+ * @param cipher_suite
+ * @param sr
+ */
+ public SSLSessionImpl(CipherSuite cipher_suite, SecureRandom sr) {
+ creationTime = System.currentTimeMillis();
+ lastAccessedTime = creationTime;
+ if (cipher_suite == null) {
+ this.cipherSuite = CipherSuite.TLS_NULL_WITH_NULL_NULL;
+ id = new byte[0];
+ isServer = false;
+ } else {
+ this.cipherSuite = cipher_suite;
+ id = new byte[32];
+ sr.nextBytes(id);
+ long time = new java.util.Date().getTime() / 1000;
+ id[28] = (byte) ((time & 0xFF000000) >>> 24);
+ id[29] = (byte) ((time & 0xFF0000) >>> 16);
+ id[30] = (byte) ((time & 0xFF00) >>> 8);
+ id[31] = (byte) (time & 0xFF);
+ isServer = true;
+ }
+
+ }
+
+ /**
+ * Creates SSLSession implementation
+ * @param sr
+ */
+ public SSLSessionImpl(SecureRandom sr) {
+ this(null, sr);
+ }
+
+ private SSLSessionImpl() {
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getApplicationBufferSize()
+ */
+ public int getApplicationBufferSize() {
+ return SSLRecordProtocol.MAX_DATA_LENGTH;
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getCipherSuite()
+ */
+ public String getCipherSuite() {
+ return cipherSuite.getName();
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getCreationTime()
+ */
+ public long getCreationTime() {
+ return creationTime;
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getId()
+ */
+ public byte[] getId() {
+ return id;
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getLastAccessedTime()
+ */
+ public long getLastAccessedTime() {
+ return lastAccessedTime;
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getLocalCertificates()
+ */
+ public Certificate[] getLocalCertificates() {
+ return localCertificates;
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getLocalPrincipal()
+ */
+ public Principal getLocalPrincipal() {
+ if (localCertificates != null && localCertificates.length > 0) {
+ return localCertificates[0].getSubjectX500Principal();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getPacketBufferSize()
+ */
+ public int getPacketBufferSize() {
+ return SSLRecordProtocol.MAX_SSL_PACKET_SIZE;
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getPeerCertificateChain()
+ */
+ public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+ throws SSLPeerUnverifiedException {
+ if (peerCertificates == null) {
+ throw new SSLPeerUnverifiedException("No peer certificate");
+ }
+ javax.security.cert.X509Certificate[] certs = new javax.security.cert.X509Certificate[peerCertificates.length];
+ for (int i = 0; i < certs.length; i++) {
+ try {
+ certs[i] = javax.security.cert.X509Certificate
+ .getInstance(peerCertificates[i].getEncoded());
+ } catch (javax.security.cert.CertificateException e) {
+ } catch (CertificateEncodingException e) {
+ }
+ }
+ return certs;
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getPeerCertificates()
+ */
+ public Certificate[] getPeerCertificates()
+ throws SSLPeerUnverifiedException {
+ if (peerCertificates == null) {
+ throw new SSLPeerUnverifiedException("No peer certificate");
+ }
+ return peerCertificates;
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getPeerHost()
+ */
+ public String getPeerHost() {
+ return peerHost;
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getPeerPort()
+ */
+ public int getPeerPort() {
+ return peerPort;
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getPeerPrincipal()
+ */
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ if (peerCertificates == null) {
+ throw new SSLPeerUnverifiedException("No peer certificate");
+ }
+ return peerCertificates[0].getSubjectX500Principal();
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getProtocol()
+ */
+ public String getProtocol() {
+ return protocol.name;
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getSessionContext()
+ */
+ public SSLSessionContext getSessionContext() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new SSLPermission("getSSLSessionContext"));
+ }
+ return context;
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getValue(String name)
+ */
+ public Object getValue(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("Parameter is null");
+ }
+ return values.get(name, AccessController.getContext());
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#getValueNames()
+ */
+ public String[] getValueNames() {
+ Vector v = new Vector();
+ AccessControlContext current = AccessController.getContext();
+ AccessControlContext cont;
+ for (Iterator it = values.entrySet().iterator(); it.hasNext();) {
+ TwoKeyHashMap.Entry entry = (TwoKeyHashMap.Entry) it.next();
+ cont = (AccessControlContext) entry.getKey2();
+ if ((current == null && cont == null)
+ || (current != null && current.equals(cont))) {
+ v.add(entry.getKey1());
+ }
+ }
+ return (String[]) v.toArray(new String[0]);
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#invalidate()
+ */
+ public void invalidate() {
+ isValid = false;
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#isValid()
+ */
+ public boolean isValid() {
+ if (isValid
+ && context != null
+ && context.getSessionTimeout() != 0
+ && lastAccessedTime + context.getSessionTimeout() > System
+ .currentTimeMillis()) {
+ isValid = false;
+ }
+ return isValid;
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#putValue(String name, Object value)
+ */
+ public void putValue(String name, Object value) {
+ if (name == null || value == null) {
+ throw new IllegalArgumentException("Parameter is null");
+ }
+ Object old = values.put(name, AccessController.getContext(), value);
+ if (value instanceof SSLSessionBindingListener) {
+ ((SSLSessionBindingListener) value)
+ .valueBound(new SSLSessionBindingEvent(this, name));
+ }
+ if (old != null && old instanceof SSLSessionBindingListener) {
+ ((SSLSessionBindingListener) old)
+ .valueUnbound(new SSLSessionBindingEvent(this, name));
+ }
+
+ }
+
+ /**
+ * @see javax.net.ssl.SSLSession#removeValue(String name)
+ */
+ public void removeValue(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("Parameter is null");
+ }
+ values.remove(name, AccessController.getContext());
+
+ }
+
+ public Object clone() {
+ SSLSessionImpl ses = new SSLSessionImpl();
+ ses.id = this.id;
+ ses.creationTime = this.creationTime;
+ ses.lastAccessedTime = this.lastAccessedTime;
+ ses.isValid = this.isValid;
+ ses.cipherSuite = this.cipherSuite;
+ ses.localCertificates = this.localCertificates;
+ ses.peerCertificates = this.peerCertificates;
+ ses.master_secret = this.master_secret;
+ ses.clientRandom = this.clientRandom;
+ ses.serverRandom = this.serverRandom;
+ ses.peerHost = this.peerHost;
+ ses.peerPort = this.peerPort;
+ ses.isServer = this.isServer;
+ ses.context = this.context;
+ ses.protocol = this.protocol;
+ ses.values = this.values;
+ return ses;
+ }
+
+
+ /**
+ * Sets the address of the peer
+ * @param peerHost
+ * @param peerPort
+ */
+ void setPeer(String peerHost, int peerPort) {
+ this.peerHost = peerHost;
+ this.peerPort = peerPort;
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLStreamedInput.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLStreamedInput.java
new file mode 100644
index 0000000..efabef8
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLStreamedInput.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This class acts like a filtered input stream: it takes
+ * the bytes from another InputStream.
+ */
+public class SSLStreamedInput extends SSLInputStream {
+
+ private InputStream in;
+
+ public SSLStreamedInput(InputStream in) {
+ this.in = in;
+ }
+
+ public int available() throws IOException {
+ return in.available();
+ }
+
+ /**
+ * Read an opaque value from the stream.
+ * @return the value read from the underlying stream.
+ * @throws IOException if the data could not be read from
+ * the underlying stream
+ * @throws org.apache.harmony.xnet.provider.jsse.EndOfSourceException if the end of the underlying
+ * stream has been reached.
+ */
+ public int read() throws IOException {
+ int res = in.read();
+ if (res < 0) {
+ throw new EndOfSourceException();
+ }
+ return res;
+ }
+}
+
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLv3Constants.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLv3Constants.java
new file mode 100644
index 0000000..1a03f7f
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLv3Constants.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+/**
+ *
+ * Contains SSL 3.0 constants
+ * @see <a href="http://wp.netscape.com/eng/ssl3">SSL 3.0 Spec.</a>
+ */
+public class SSLv3Constants {
+
+ /**
+ * Client is a sender. Used in hash calculating for finished message.
+ * @see <a href="http://wp.netscape.com/eng/ssl3">SSL 3.0 Spec., 5.6.9
+ * Finished</a>
+ */
+ static final byte[] client = new byte[] { 0x43, 0x4C, 0x4E, 0x54 };
+
+ /**
+ * Server is a sender. Used in hash calculating for finished message.
+ * @see <a href="http://wp.netscape.com/eng/ssl3">SSL 3.0 Spec., 5.6.9
+ * Finished</a>
+ */
+ static final byte[] server = new byte[] { 0x53, 0x52, 0x56, 0x52 };
+
+ /**
+ * pad_1 for MD5
+ * @see <a href="http://wp.netscape.com/eng/ssl3">SSL 3.0 Spec., 5.2.3.1
+ * Null or standard stream cipher</a>
+ */
+ static final byte[] MD5pad1 = new byte[] { 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 };
+
+ /**
+ * pad_1 for SHA
+ * @see <a href="http://wp.netscape.com/eng/ssl3">SSL 3.0 Spec., 5.2.3.1
+ * Null or standard stream cipher</a>
+ */
+ static final byte[] SHApad1 = new byte[] { 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36 };
+
+ /**
+ * pad_2 for MD5
+ * @see <a href="http://wp.netscape.com/eng/ssl3">SSL 3.0 Spec., 5.2.3.1
+ * Null or standard stream cipher</a>
+ */
+ static final byte[] MD5pad2 = new byte[] { 0x5C, 0x5C, 0x5C, 0x5C,
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C };
+
+ /**
+ * pad_2 for SHA
+ * @see <a href="http://wp.netscape.com/eng/ssl3">SSL 3.0 Spec., 5.2.3.1
+ * Null or standard stream cipher</a>
+ */
+ static final byte[] SHApad2 = new byte[] { 0x5C, 0x5C, 0x5C, 0x5C,
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+ 0x5C, 0x5C, 0x5C };
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java
new file mode 100644
index 0000000..84a663c
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java
@@ -0,0 +1,738 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris Kuznetsov
+* @version $Revision$
+*/
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.SSLv3Constants;
+import org.apache.harmony.xnet.provider.jsse.SSLSessionImpl;
+import org.apache.harmony.xnet.provider.jsse.ProtocolVersion;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.AccessController;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PrivilegedExceptionAction;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPublicKey;
+
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPublicKeySpec;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * Server side handshake protocol implementation.
+ * Handshake protocol operates on top of the Record Protocol.
+ * It responsible for negotiating a session.
+ *
+ * The implementation proceses inbound client handshake messages,
+ * creates and sends respond messages. Outbound messages are supplied
+ * to Record Protocol. Detected errors are reported to the Alert protocol.
+ *
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.
+ * Handshake protocol.</a>
+ *
+ */
+public class ServerHandshakeImpl extends HandshakeProtocol {
+
+ // private key used in key exchange
+ private PrivateKey privKey;
+
+ /**
+ * Creates Server Handshake Implementation
+ *
+ * @param owner
+ */
+ public ServerHandshakeImpl(Object owner) {
+ super(owner);
+ status = NEED_UNWRAP;
+ }
+
+ /**
+ * Start session negotiation
+ * @param session
+ */
+ public void start() {
+ if (session == null) { // initial handshake
+ status = NEED_UNWRAP;
+ return; // wait client hello
+ }
+ if (clientHello != null && this.status != FINISHED) {
+ // current negotiation has not completed
+ return; // ignore
+ }
+
+ // renegotiation
+ sendHelloRequest();
+ status = NEED_UNWRAP;
+ }
+
+ /**
+ * Proceses inbound handshake messages
+ * @param bytes
+ */
+ public void unwrap(byte[] bytes) {
+
+ io_stream.append(bytes);
+ while (io_stream.available() > 0) {
+ int handshakeType;
+ int length;
+ io_stream.mark();
+ try {
+ handshakeType = io_stream.read();
+ length = io_stream.readUint24();
+ if (io_stream.available() < length) {
+ io_stream.reset();
+ return;
+ }
+
+ switch (handshakeType) {
+ case 1: // CLIENT_HELLO
+ if (clientHello != null && this.status != FINISHED) {
+ // Client hello has been received during handshake
+ unexpectedMessage();
+ return;
+ }
+ // if protocol planed to send Hello Request message
+ // - cancel this demand.
+ needSendHelloRequest = false;
+ clientHello = new ClientHello(io_stream, length);
+ if (nonBlocking) {
+ delegatedTasks.add(new DelegatedTask(
+ new PrivilegedExceptionAction(){
+ public Object run() throws Exception {
+ processClientHello();
+ return null;
+ }
+ },
+ this,
+ AccessController.getContext()));
+ return;
+ }
+ processClientHello();
+ break;
+
+ case 11: // CLIENT CERTIFICATE
+ if (isResuming || certificateRequest == null
+ || serverHelloDone == null || clientCert != null) {
+ unexpectedMessage();
+ return;
+ }
+ clientCert = new CertificateMessage(io_stream, length);
+ if (clientCert.certs.length == 0) {
+ if (parameters.getNeedClientAuth()) {
+ fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+ "HANDSHAKE FAILURE: no client certificate recived");
+ }
+ } else {
+ String authType = clientCert.certs[0].getPublicKey()
+ .getAlgorithm();
+ try {
+ parameters.getTrustManager().checkClientTrusted(
+ clientCert.certs, authType);
+ } catch (CertificateException e) {
+ fatalAlert(AlertProtocol.BAD_CERTIFICATE,
+ "Untrusted Client Certificate ", e);
+ }
+ session.peerCertificates = clientCert.certs;
+ }
+ break;
+
+ case 15: // CERTIFICATE_VERIFY
+ if (isResuming
+ || clientKeyExchange == null
+ || clientCert == null
+ || clientKeyExchange.isEmpty() //client certificate
+ // contains fixed DH
+ // parameters
+ || certificateVerify != null
+ || changeCipherSpecReceived) {
+ unexpectedMessage();
+ return;
+ }
+ certificateVerify = new CertificateVerify(io_stream, length);
+
+ DigitalSignature ds = new DigitalSignature(session.cipherSuite.keyExchange);
+ ds.init(serverCert.certs[0]);
+ byte[] md5_hash = null;
+ byte[] sha_hash = null;
+
+ if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT
+ || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_RSA
+ || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_RSA
+ || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_RSA_EXPORT) {
+ md5_hash = io_stream.getDigestMD5withoutLast();
+ sha_hash = io_stream.getDigestSHAwithoutLast();
+ } else if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_DSS
+ || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DHE_DSS_EXPORT) {
+ sha_hash = io_stream.getDigestSHAwithoutLast();
+ } else if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DH_anon
+ || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_DH_anon_EXPORT) {
+ }
+ ds.setMD5(md5_hash);
+ ds.setSHA(sha_hash);
+ if (!ds.verifySignature(certificateVerify.signedHash)) {
+ fatalAlert(AlertProtocol.DECRYPT_ERROR,
+ "DECRYPT ERROR: CERTIFICATE_VERIFY incorrect signature");
+ }
+ break;
+ case 16: // CLIENT_KEY_EXCHANGE
+ if (isResuming
+ || serverHelloDone == null
+ || clientKeyExchange != null
+ || (clientCert == null && parameters
+ .getNeedClientAuth())) {
+ unexpectedMessage();
+ return;
+ }
+ if (session.cipherSuite.keyExchange == CipherSuite.KeyExchange_RSA
+ || session.cipherSuite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT) {
+ clientKeyExchange = new ClientKeyExchange(io_stream,
+ length, serverHello.server_version[1] == 1,
+ true);
+ Cipher c = null;
+ try {
+ c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ c.init(Cipher.DECRYPT_MODE, privKey);
+ preMasterSecret = c
+ .doFinal(clientKeyExchange.exchange_keys);
+ // check preMasterSecret:
+ if (preMasterSecret.length != 48
+ || preMasterSecret[0] != clientHello.client_version[0]
+ || preMasterSecret[1] != clientHello.client_version[1]) {
+ // incorrect preMasterSecret
+ // prevent an attack (see TLS 1.0 spec., 7.4.7.1.)
+ preMasterSecret = new byte[48];
+ parameters.getSecureRandom().nextBytes(
+ preMasterSecret);
+ }
+ } catch (Exception e) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR,
+ "INTERNAL ERROR", e);
+ }
+ } else { // diffie hellman key exchange
+ clientKeyExchange = new ClientKeyExchange(io_stream,
+ length, serverHello.server_version[1] == 1,
+ false);
+ if (clientKeyExchange.isEmpty()) {
+ // TODO check that client cert. DH params
+ // matched server cert. DH params
+
+ // client cert. contains fixed DH parameters
+ preMasterSecret = ((DHPublicKey) clientCert.certs[0]
+ .getPublicKey()).getY().toByteArray();
+ } else {
+ PublicKey clientPublic;
+ KeyAgreement agreement;
+ try {
+ KeyFactory kf = null;
+ try {
+ kf = KeyFactory.getInstance("DH");
+ } catch (NoSuchAlgorithmException ee) {
+ kf = KeyFactory
+ .getInstance("DiffieHellman");
+ }
+ try {
+ agreement = KeyAgreement.getInstance("DH");
+ } catch (NoSuchAlgorithmException ee) {
+ agreement = KeyAgreement
+ .getInstance("DiffieHellman");
+ }
+ clientPublic = kf
+ .generatePublic(new DHPublicKeySpec(
+ new BigInteger(
+ 1,
+ clientKeyExchange.exchange_keys),
+ serverKeyExchange.par1,
+ serverKeyExchange.par2));
+ agreement.init(privKey);
+ agreement.doPhase(clientPublic, true);
+ preMasterSecret = agreement.generateSecret();
+ } catch (Exception e) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR,
+ "INTERNAL ERROR", e);
+ return;
+ }
+ }
+ }
+
+ computerMasterSecret();
+ break;
+
+ case 20: // FINISHED
+ if (!isResuming && !changeCipherSpecReceived) {
+ unexpectedMessage();
+ return;
+ }
+
+ clientFinished = new Finished(io_stream, length);
+ verifyFinished(clientFinished.getData());
+ // BEGIN android-added
+ session.context = parameters.getClientSessionContext();
+ // END android-added
+ parameters.getServerSessionContext().putSession(session);
+ if (!isResuming) {
+ sendChangeCipherSpec();
+ } else {
+ session.lastAccessedTime = System.currentTimeMillis();
+ status = FINISHED;
+ }
+ break;
+ default:
+ unexpectedMessage();
+ return;
+ }
+ } catch (IOException e) {
+ // io stream dosn't contain complete handshake message
+ io_stream.reset();
+ return;
+ }
+ }
+ }
+ /**
+ * Processes SSLv2 Hello message
+ * @ see TLS 1.0 spec., E.1. Version 2 client hello
+ * @param bytes
+ */
+ public void unwrapSSLv2(byte[] bytes) {
+ try {
+ io_stream.append(bytes);
+ io_stream.mark();
+ try {
+ clientHello = new ClientHello(io_stream);
+ } catch (IOException e) {
+ io_stream.reset();
+ return;
+ }
+ if (nonBlocking) {
+ delegatedTasks.add(new DelegatedTask(
+ new PrivilegedExceptionAction(){
+ public Object run() throws Exception {
+ processClientHello();
+ return null;
+ }
+ },
+ this,
+ AccessController.getContext()));
+ return;
+ }
+ processClientHello();
+ } catch (Exception e) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e);
+ }
+ }
+
+ /**
+ *
+ * Processes Client Hello message.
+ * Server responds to client hello message with server hello
+ * and (if necessary) server certificate, server key exchange,
+ * certificate request, and server hello done messages.
+ */
+ void processClientHello() {
+ CipherSuite cipher_suite;
+
+ // check that clientHello contains CompressionMethod.null
+ checkCompression: {
+ for (int i = 0; i < clientHello.compression_methods.length; i++) {
+ if (clientHello.compression_methods[i] == 0) {
+ break checkCompression;
+ }
+ }
+ fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+ "HANDSHAKE FAILURE. Incorrect client hello message");
+ }
+
+ if (!ProtocolVersion.isSupported(clientHello.client_version)) {
+ fatalAlert(AlertProtocol.PROTOCOL_VERSION,
+ "PROTOCOL VERSION. Unsupported client version "
+ + clientHello.client_version[0]
+ + clientHello.client_version[1]);
+ }
+
+ isResuming = false;
+ FIND: if (clientHello.session_id.length != 0) {
+ // client wishes to reuse session
+
+ SSLSessionImpl sessionToResume;
+ boolean reuseCurrent = false;
+
+ // reuse current session
+ if (session != null
+ && Arrays.equals(session.id, clientHello.session_id)) {
+ if (session.isValid()) {
+ isResuming = true;
+ break FIND;
+ }
+ reuseCurrent = true;
+ }
+
+ // find session in cash
+ sessionToResume = findSessionToResume(clientHello.session_id);
+ if (sessionToResume == null || !sessionToResume.isValid()) {
+ if (!parameters.getEnableSessionCreation()) {
+ if (reuseCurrent) {
+ // we can continue current session
+ sendWarningAlert(AlertProtocol.NO_RENEGOTIATION);
+ status = NOT_HANDSHAKING;
+ clearMessages();
+ return;
+ } else {
+ fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+ "SSL Session may not be created");
+ }
+ }
+ session = null;
+ } else {
+ session = (SSLSessionImpl)sessionToResume.clone();
+ isResuming = true;
+ }
+ }
+
+ if (isResuming) {
+ cipher_suite = session.cipherSuite;
+ // clientHello.cipher_suites must include at least cipher_suite from the session
+ checkCipherSuite: {
+ for (int i = 0; i < clientHello.cipher_suites.length; i++) {
+ if (cipher_suite.equals(clientHello.cipher_suites[i])) {
+ break checkCipherSuite;
+ }
+ }
+ fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+ "HANDSHAKE FAILURE. Incorrect client hello message");
+ }
+ } else {
+ cipher_suite = selectSuite(clientHello.cipher_suites);
+ if (cipher_suite == null) {
+ fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "HANDSHAKE FAILURE. NO COMMON SUITE");
+ }
+ if (!parameters.getEnableSessionCreation()) {
+ fatalAlert(AlertProtocol.HANDSHAKE_FAILURE,
+ "SSL Session may not be created");
+ }
+ session = new SSLSessionImpl(cipher_suite, parameters
+ .getSecureRandom());
+ }
+
+ recordProtocol.setVersion(clientHello.client_version);
+ session.protocol = ProtocolVersion.getByVersion(clientHello.client_version);
+ session.clientRandom = clientHello.random;
+
+ // create server hello message
+ serverHello = new ServerHello(parameters.getSecureRandom(),
+ clientHello.client_version,
+ session.getId(), cipher_suite, (byte) 0); //CompressionMethod.null
+ session.serverRandom = serverHello.random;
+ send(serverHello);
+ if (isResuming) {
+ sendChangeCipherSpec();
+ return;
+ }
+
+ // create and send server certificate message if needed
+ if (!cipher_suite.isAnonymous()) { // need to send server certificate
+ X509Certificate[] certs = null;
+ String certType = null;
+ if (cipher_suite.keyExchange == CipherSuite.KeyExchange_RSA
+ || cipher_suite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT
+ || cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_RSA
+ || cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_RSA_EXPORT) {
+ certType = "RSA";
+ } else if (cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_DSS
+ || cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_DSS_EXPORT) {
+ certType = "DSA";
+ } else if (cipher_suite.keyExchange == CipherSuite.KeyExchange_DH_DSS) {
+ certType = "DH_DSA";
+ } else if (cipher_suite.keyExchange == CipherSuite.KeyExchange_DH_RSA) {
+ certType = "DH_RSA";
+ }
+ // obtain certificates from key manager
+ String alias = null;
+ X509KeyManager km = parameters.getKeyManager();
+ if (km instanceof X509ExtendedKeyManager) {
+ X509ExtendedKeyManager ekm = (X509ExtendedKeyManager)km;
+ // BEGIN android-removed
+ // if (this.socketOwner != null) {
+ // alias = ekm.chooseServerAlias(certType, null,
+ // this.socketOwner);
+ // } else {
+ // END android-removed
+ alias = ekm.chooseEngineServerAlias(certType, null,
+ this.engineOwner);
+ // BEGIN android-removed
+ // }
+ // END android-removed
+ if (alias != null) {
+ certs = ekm.getCertificateChain(alias);
+ }
+ } else {
+ // BEGIN android-removed
+ // alias = km.chooseServerAlias(certType, null, this.socketOwner);
+ // if (alias != null) {
+ // END android-removed
+ certs = km.getCertificateChain(alias);
+ // BEGIN android-removed
+ // }
+ // END android-removed
+ }
+
+ if (certs == null) {
+ fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "NO SERVER CERTIFICATE FOUND");
+ return;
+ }
+ session.localCertificates = certs;
+ serverCert = new CertificateMessage(certs);
+ privKey = parameters.getKeyManager().getPrivateKey(alias);
+ send(serverCert);
+ }
+
+ // create and send server key exchange message if needed
+ RSAPublicKey rsakey = null;
+ DHPublicKeySpec dhkeySpec = null;
+ byte[] hash = null;
+ BigInteger p = null;
+ BigInteger g = null;
+
+ KeyPairGenerator kpg = null;
+
+ try {
+ if (cipher_suite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT) {
+ PublicKey pk = serverCert.certs[0].getPublicKey();
+ if (getRSAKeyLength(pk) > 512) {
+ // key is longer than 512 bits
+ kpg = KeyPairGenerator.getInstance("RSA");
+ kpg.initialize(512);
+ }
+ } else if (cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_DSS
+ || cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_DSS_EXPORT
+ || cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_RSA
+ || cipher_suite.keyExchange == CipherSuite.KeyExchange_DHE_RSA_EXPORT
+ || cipher_suite.keyExchange == CipherSuite.KeyExchange_DH_anon
+ || cipher_suite.keyExchange == CipherSuite.KeyExchange_DH_anon_EXPORT) {
+ try {
+ kpg = KeyPairGenerator.getInstance("DH");
+ } catch (NoSuchAlgorithmException ee) {
+ kpg = KeyPairGenerator.getInstance("DiffieHellman");
+ }
+ p = new BigInteger(1, DHParameters.getPrime());
+ g = new BigInteger("2");
+ DHParameterSpec spec = new DHParameterSpec(p, g);
+ kpg.initialize(spec);
+ }
+ } catch (Exception e) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e);
+ }
+
+ if (kpg != null) {
+ // need to send server key exchange message
+ DigitalSignature ds = new DigitalSignature(cipher_suite.keyExchange);
+ KeyPair kp = null;
+ try {
+ kp = kpg.genKeyPair();
+ if (cipher_suite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT) {
+ rsakey = (RSAPublicKey) kp.getPublic();
+ } else {
+ DHPublicKey dhkey = (DHPublicKey) kp.getPublic();
+ KeyFactory kf = null;
+ try {
+ kf = KeyFactory.getInstance("DH");
+ } catch (NoSuchAlgorithmException e) {
+ kf = KeyFactory.getInstance("DiffieHellman");
+ }
+ dhkeySpec = (DHPublicKeySpec) kf.getKeySpec(dhkey,
+ DHPublicKeySpec.class);
+ }
+ if (!cipher_suite.isAnonymous()) { // calculate signed_params
+
+ // init by private key which correspond to
+ // server certificate
+ ds.init(privKey);
+
+ // use emphemeral key for key exchange
+ privKey = kp.getPrivate();
+ ds.update(clientHello.getRandom());
+ ds.update(serverHello.getRandom());
+
+ byte[] tmp;
+ byte[] tmpLength = new byte[2];
+//FIXME 1_byte==0x00
+ if (cipher_suite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT) {
+ tmp = rsakey.getModulus().toByteArray();
+ tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
+ tmpLength[1] = (byte) (tmp.length & 0xFF);
+ ds.update(tmpLength);
+ ds.update(tmp);
+ tmp = rsakey.getPublicExponent().toByteArray();
+ tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
+ tmpLength[1] = (byte) (tmp.length & 0xFF);
+ ds.update(tmp);
+ } else {
+ tmp = dhkeySpec.getP().toByteArray();
+ tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
+ tmpLength[1] = (byte) (tmp.length & 0xFF);
+ ds.update(tmp);
+ tmp = dhkeySpec.getG().toByteArray();
+ tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
+ tmpLength[1] = (byte) (tmp.length & 0xFF);
+ ds.update(tmp);
+ tmp = dhkeySpec.getY().toByteArray();
+ tmpLength[0] = (byte) ((tmp.length & 0xFF00) >>> 8);
+ tmpLength[1] = (byte) (tmp.length & 0xFF);
+ ds.update(tmp);
+ }
+ hash = ds.sign();
+ } else {
+ privKey = kp.getPrivate(); // use emphemeral key for key exchange
+ }
+ } catch (Exception e) {
+ fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e);
+ }
+
+ if (cipher_suite.keyExchange == CipherSuite.KeyExchange_RSA_EXPORT) {
+ serverKeyExchange = new ServerKeyExchange(rsakey.getModulus(),
+ rsakey.getPublicExponent(), null, hash);
+ } else {
+ serverKeyExchange = new ServerKeyExchange(p,
+ g, dhkeySpec.getY(), hash);
+ }
+ send(serverKeyExchange);
+ }
+
+ // CERTIFICATE_REQUEST
+ certRequest: if (parameters.getWantClientAuth()
+ || parameters.getNeedClientAuth()) {
+ X509Certificate[] accepted;
+ try {
+ X509TrustManager tm = parameters.getTrustManager();
+ accepted = tm.getAcceptedIssuers();
+ } catch (ClassCastException e) {
+ // don't send certificateRequest
+ break certRequest;
+ }
+ byte[] requestedClientCertTypes = {1, 2}; // rsa sign, dsa sign
+ certificateRequest = new CertificateRequest(
+ requestedClientCertTypes, accepted);
+ send(certificateRequest);
+ }
+
+ // SERVER_HELLO_DONE
+ serverHelloDone = new ServerHelloDone();
+ send(serverHelloDone);
+ status = NEED_UNWRAP;
+ }
+
+ /**
+ * Creates and sends finished message
+ */
+ protected void makeFinished() {
+ byte[] verify_data;
+ boolean isTLS = (serverHello.server_version[1] == 1); // TLS 1.0 protocol
+ if (isTLS) {
+ verify_data = new byte[12];
+ computerVerifyDataTLS("server finished", verify_data);
+ } else { // SSL 3.0 protocol (http://wp.netscape.com/eng/ssl3)
+ verify_data = new byte[36];
+ computerVerifyDataSSLv3(SSLv3Constants.server, verify_data);
+ }
+ serverFinished = new Finished(verify_data);
+ send(serverFinished);
+ if (isResuming) {
+ if (isTLS) {
+ computerReferenceVerifyDataTLS("client finished");
+ } else {
+ computerReferenceVerifyDataSSLv3(SSLv3Constants.client);
+ }
+ status = NEED_UNWRAP;
+ } else {
+ session.lastAccessedTime = System.currentTimeMillis();
+ status = FINISHED;
+ }
+ }
+
+ // find sesssion in the session hash
+ private SSLSessionImpl findSessionToResume(byte[] session_id) {
+ return (SSLSessionImpl)parameters.getServerSessionContext().getSession(session_id);
+ }
+
+ // find appropriate cipher_suite in the client suites
+ private CipherSuite selectSuite(CipherSuite[] client_suites) {
+ for (int i = 0; i < client_suites.length; i++) {
+ if (!client_suites[i].supported) {
+ continue;
+ }
+ // BEGIN android-removed
+ // for (int j = 0; j < parameters.enabledCipherSuites.length; j++) {
+ // if (client_suites[i].equals(parameters.enabledCipherSuites[j])) {
+ // return client_suites[i];
+ // }
+ // }
+ // END android-removed
+ // BEGIN android-added
+ for (int j = 0; j < parameters.getEnabledCipherSuitesMember().length; j++) {
+ if (client_suites[i].equals(parameters.getEnabledCipherSuitesMember()[j])) {
+ return client_suites[i];
+ }
+ }
+ // END android-added
+ }
+ return null;
+ }
+
+ /**
+ * Proceses inbound ChangeCipherSpec message
+ */
+ public void receiveChangeCipherSpec() {
+ if (isResuming) {
+ if (serverFinished == null) {
+ unexpectedMessage();
+ } else {
+ changeCipherSpecReceived = true;
+ }
+ } else {
+ if ((parameters.getNeedClientAuth() && clientCert == null)
+ || clientKeyExchange == null
+ || (clientCert != null && !clientKeyExchange.isEmpty() && certificateVerify == null)) {
+ unexpectedMessage();
+ } else {
+ changeCipherSpecReceived = true;
+ }
+ if (serverHello.server_version[1] == 1) {
+ computerReferenceVerifyDataTLS("client finished");
+ } else {
+ computerReferenceVerifyDataSSLv3(SSLv3Constants.client);
+ }
+ }
+ }
+
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHello.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHello.java
new file mode 100644
index 0000000..0365288
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHello.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris Kuznetsov
+* @version $Revision$
+*/
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.Message;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+/**
+ *
+ * Represents server hello message.
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.1.3.
+ * Server hello.</a>
+ */
+public class ServerHello extends Message {
+
+ /**
+ * Server version
+ */
+ byte[] server_version = new byte[2];
+
+ /**
+ * Random bytes
+ */
+ byte[] random = new byte[32];
+
+ /**
+ * Session id
+ */
+ byte[] session_id;
+
+ /**
+ * Selected cipher suite
+ */
+ CipherSuite cipher_suite;
+
+ /**
+ * Selected compression method
+ */
+ byte compression_method;
+
+ /**
+ * Creates outbound message
+ * @param sr
+ * @param server_version
+ * @param session_id
+ * @param cipher_suite
+ * @param compression_method
+ */
+ public ServerHello(SecureRandom sr, byte[] server_version,
+ byte[] session_id, CipherSuite cipher_suite, byte compression_method) {
+ long gmt_unix_time = new java.util.Date().getTime() / 1000;
+ sr.nextBytes(random);
+ random[0] = (byte) ((gmt_unix_time & 0xFF000000) >>> 24);
+ random[1] = (byte) ((gmt_unix_time & 0xFF0000) >>> 16);
+ random[2] = (byte) ((gmt_unix_time & 0xFF00) >>> 8);
+ random[3] = (byte) (gmt_unix_time & 0xFF);
+ this.session_id = session_id;
+ this.cipher_suite = cipher_suite;
+ this.compression_method = compression_method;
+ this.server_version = server_version;
+ length = 38 + session_id.length;
+ }
+
+ /**
+ * Creates inbound message
+ * @param in
+ * @param length
+ * @throws IOException
+ */
+ public ServerHello(HandshakeIODataStream in, int length) throws IOException {
+
+ server_version[0] = (byte) in.read();
+ server_version[1] = (byte) in.read();
+ in.read(random, 0, 32);
+ int size = in.readUint8();
+ session_id = new byte[size];
+ in.read(session_id, 0, size);
+ byte b0 = (byte) in.read();
+ byte b1 = (byte) in.read();
+ cipher_suite = CipherSuite.getByCode(b0, b1);
+ compression_method = (byte) in.read();
+ this.length = 38 + session_id.length;
+ if (this.length != length) {
+ fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect ServerHello");
+ }
+
+ }
+
+ /**
+ * Sends message
+ * @param out
+ */
+ public void send(HandshakeIODataStream out) {
+ out.write(server_version);
+ out.write(random);
+ out.writeUint8(session_id.length);
+ out.write(session_id);
+ out.write(cipher_suite.toBytes());
+ out.write(compression_method);
+ length = 38 + session_id.length;
+ }
+
+ /**
+ * Returns server random
+ * @return
+ */
+ public byte[] getRandom() {
+ return random;
+ }
+
+ /**
+ * Returns message type
+ * @return
+ */
+ public int getType() {
+ return Handshake.SERVER_HELLO;
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHelloDone.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHelloDone.java
new file mode 100644
index 0000000..e794ed9
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHelloDone.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris Kuznetsov
+* @version $Revision$
+*/
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.Message;
+
+import java.io.IOException;
+
+/**
+ *
+ * Represents server hello done message
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.5.
+ * Server hello done</a>
+ *
+ */
+public class ServerHelloDone extends Message {
+
+ /**
+ * Creates outbound message
+ *
+ */
+ public ServerHelloDone() {
+ }
+
+ /**
+ * Creates inbound message
+ * @param in
+ * @param length
+ * @throws IOException
+ */
+ public ServerHelloDone(HandshakeIODataStream in, int length)
+ throws IOException {
+ if (length != 0) {
+ fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect ServerHelloDone");
+ }
+ }
+
+ /**
+ * Sends message
+ * @param out
+ */
+ public void send(HandshakeIODataStream out) {
+ }
+
+ /**
+ * Returns message length
+ * @return
+ */
+ public int length() {
+ return 0;
+ }
+
+ /**
+ * Returns message type
+ * @return
+ */
+ public int getType() {
+ return Handshake.SERVER_HELLO_DONE;
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerKeyExchange.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerKeyExchange.java
new file mode 100644
index 0000000..1d93ece
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerKeyExchange.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+* @author Boris Kuznetsov
+* @version $Revision$
+*/
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import org.apache.harmony.xnet.provider.jsse.Message;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.RSAPublicKeySpec;
+
+/**
+ *
+ * Represents server key exchange message.
+ * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.3.
+ * Server key exchange message.</a>
+ *
+ */
+public class ServerKeyExchange extends Message {
+
+ // ServerRSAParams ServerDHParams
+ final BigInteger par1; // rsa_modulus dh_p
+ final byte[] bytes1;
+
+ final BigInteger par2; // rsa_exponent dh_g
+ final byte[] bytes2;
+
+ final BigInteger par3; // dh_Ys
+ final byte[] bytes3;
+
+ /**
+ * Signature
+ */
+ final byte[] hash;
+
+ private RSAPublicKey key;
+
+ /**
+ * Creates outbound message
+ * @param par1 rsa_modulus or dh_p
+ * @param par2 rsa_exponent or dh_g
+ * @param par3 dh_Ys for ServerDHParams; should be null for ServerRSAParams
+ * @param hash should be null for anonymous SignatureAlgorithm
+ */
+ public ServerKeyExchange(BigInteger par1, BigInteger par2, BigInteger par3,
+ byte[] hash) {
+ this.par1 = par1;
+ this.par2 = par2;
+ this.par3 = par3;
+ this.hash = hash;
+
+ byte[] bb = this.par1.toByteArray();
+ if (bb[0] == 0) {
+// XXX check for par1 == 0 or bb.length > 1
+ bytes1 = new byte[bb.length - 1];
+ System.arraycopy(bb, 1, bytes1, 0, bytes1.length);
+ } else {
+ bytes1 = bb;
+ }
+
+ bb = this.par2.toByteArray();
+ if (bb[0] == 0) {
+ bytes2 = new byte[bb.length - 1];
+ System.arraycopy(bb, 1, bytes2, 0, bytes2.length);
+ } else {
+ bytes2 = bb;
+ }
+
+ length = 4 + bytes1.length + bytes2.length;
+ if (hash != null) {
+ length += 2 + hash.length;
+ }
+ if (par3 == null) {
+ bytes3 = null;
+ return;
+ }
+ bb = this.par3.toByteArray();
+ if (bb[0] == 0) {
+ bytes3 = new byte[bb.length - 1];
+ System.arraycopy(bb, 1, bytes3, 0, bytes3.length);
+ } else {
+ bytes3 = bb;
+ }
+ length += 2 + bytes3.length;
+ }
+
+ /**
+ * Creates inbound message
+ * @param in
+ * @param length
+ * @param keyExchange
+ * @throws IOException
+ */
+ public ServerKeyExchange(HandshakeIODataStream in, int length,
+ int keyExchange) throws IOException {
+
+ int size = in.readUint16();
+ bytes1 = in.read(size);
+ par1 = new BigInteger(1, bytes1);
+ this.length = 2 + bytes1.length;
+ size = in.readUint16();
+ bytes2 = in.read(size);
+ par2 = new BigInteger(1, bytes2);
+ this.length += 2 + bytes2.length;
+ if (keyExchange != CipherSuite.KeyExchange_RSA_EXPORT) {
+ size = in.readUint16();
+ bytes3 = in.read(size);
+ par3 = new BigInteger(1, bytes3);
+ this.length += 2 + bytes3.length;
+ } else {
+ par3 = null;
+ bytes3 = null;
+ }
+ if (keyExchange != CipherSuite.KeyExchange_DH_anon_EXPORT
+ && keyExchange != CipherSuite.KeyExchange_DH_anon) {
+ size = in.readUint16();
+ hash = in.read(size);
+ this.length += 2 + hash.length;
+ } else {
+ hash = null;
+ }
+ if (this.length != length) {
+ fatalAlert(AlertProtocol.DECODE_ERROR,
+ "DECODE ERROR: incorrect ServerKeyExchange");
+ }
+ }
+
+ /**
+ * Sends message
+ * @param out
+ */
+ public void send(HandshakeIODataStream out) {
+ out.writeUint16(bytes1.length);
+ out.write(bytes1);
+ out.writeUint16(bytes2.length);
+ out.write(bytes2);
+ if (bytes3 != null) {
+ out.writeUint16(bytes3.length);
+ out.write(bytes3);
+ }
+ if (hash != null) {
+ out.writeUint16(hash.length);
+ out.write(hash);
+ }
+ }
+
+ /**
+ * Returns RSAPublicKey generated using ServerRSAParams
+ * (rsa_modulus and rsa_exponent).
+ *
+ * @return
+ */
+ public RSAPublicKey getRSAPublicKey() {
+ if (key != null) {
+ return key;
+ }
+ try {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ key = (RSAPublicKey) kf.generatePublic(new RSAPublicKeySpec(par1,
+ par2));
+ } catch (Exception e) {
+ return null;
+ }
+ return key;
+ }
+
+ /**
+ * Returns message type
+ * @return
+ */
+ public int getType() {
+ return Handshake.SERVER_KEY_EXCHANGE;
+ }
+
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java
new file mode 100644
index 0000000..b96d1ab
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.ManagerFactoryParameters;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactorySpi;
+
+/**
+ *
+ * TrustManagerFactory service provider interface implementation.
+ *
+ * @see javax.net.ssl.TrustManagerFactorySpi
+ */
+public class TrustManagerFactoryImpl extends TrustManagerFactorySpi {
+
+ private KeyStore keyStore;
+
+ /**
+ * @see javax.net.ssl.TrustManagerFactorySpi#engineInit(KeyStore)
+ */
+ public void engineInit(KeyStore ks) throws KeyStoreException {
+ if (ks != null) {
+ keyStore = ks;
+ } else {
+ // BEGIN android-added
+ if (System.getProperty("javax.net.ssl.trustStore") == null) {
+ String file = System.getProperty("java.home")
+ + java.io.File.separator + "etc" + java.io.File.separator
+ + "security" + java.io.File.separator
+ + "cacerts.bks";
+
+ System.setProperty("javax.net.ssl.trustStore", file);
+ }
+ // END android-added
+ keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ String keyStoreName = AccessController
+ .doPrivileged(new java.security.PrivilegedAction<String>() {
+ public String run() {
+ return System
+ .getProperty("javax.net.ssl.trustStore");
+ }
+ });
+ String keyStorePwd = null;
+ if (keyStoreName == null || keyStoreName.equalsIgnoreCase("NONE")
+ || keyStoreName.length() == 0) {
+ try {
+ keyStore.load(null, null);
+ } catch (IOException e) {
+ throw new KeyStoreException(e);
+ } catch (CertificateException e) {
+ throw new KeyStoreException(e);
+ } catch (NoSuchAlgorithmException e) {
+ throw new KeyStoreException(e);
+ }
+ } else {
+ keyStorePwd = AccessController
+ .doPrivileged(new java.security.PrivilegedAction<String>() {
+ public String run() {
+ return System
+ .getProperty("javax.net.ssl.trustStorePassword");
+ }
+ });
+ char[] pwd;
+ if (keyStorePwd == null) {
+ pwd = new char[0];
+ } else {
+ pwd = keyStorePwd.toCharArray();
+ }
+ try {
+ keyStore.load(new FileInputStream(new File(keyStoreName)), pwd);
+ } catch (FileNotFoundException e) {
+ throw new KeyStoreException(e);
+ } catch (IOException e) {
+ throw new KeyStoreException(e);
+ } catch (CertificateException e) {
+ throw new KeyStoreException(e);
+ } catch (NoSuchAlgorithmException e) {
+ throw new KeyStoreException(e);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * @see javax.net.ssl#engineInit(ManagerFactoryParameters)
+ */
+ public void engineInit(ManagerFactoryParameters spec)
+ throws InvalidAlgorithmParameterException {
+ throw new InvalidAlgorithmParameterException(
+ "ManagerFactoryParameters not supported");
+ }
+
+ /**
+ * @see javax.net.ssl#engineGetTrustManagers()
+ */
+ public TrustManager[] engineGetTrustManagers() {
+ if (keyStore == null) {
+ throw new IllegalStateException(
+ "TrustManagerFactory is not initialized");
+ }
+ return new TrustManager[] { new TrustManagerImpl(keyStore) };
+ }
+}
diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java
new file mode 100644
index 0000000..1c45fb8
--- /dev/null
+++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Boris Kuznetsov
+ * @version $Revision$
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.lang.reflect.Method;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.net.ssl.X509TrustManager;
+
+/**
+ *
+ * TrustManager implementation. The implementation is based on CertPathValidator
+ * PKIX and CertificateFactory X509 implementations. This implementations should
+ * be provided by some certification provider.
+ *
+ * @see javax.net.ssl.X509TrustManager
+ */
+public class TrustManagerImpl implements X509TrustManager {
+
+ private CertPathValidator validator;
+
+ private PKIXParameters params;
+
+ private Exception err = null;
+
+ private CertificateFactory factory;
+
+ /**
+ * Creates trust manager implementation
+ *
+ * @param ks
+ */
+ public TrustManagerImpl(KeyStore ks) {
+ try {
+ validator = CertPathValidator.getInstance("PKIX");
+ factory = CertificateFactory.getInstance("X509");
+ String alias;
+ X509Certificate cert;
+ byte[] nameConstrains = null;
+ Set trusted = new HashSet();
+ for (Enumeration en = ks.aliases(); en.hasMoreElements();) {
+ alias = (String) en.nextElement();
+ cert = (X509Certificate) ks.getCertificate(alias);
+ if (cert != null) {
+ trusted.add(new TrustAnchor(cert, nameConstrains));
+ }
+ }
+ params = new PKIXParameters(trusted);
+ params.setRevocationEnabled(false);
+ } catch (Exception e) {
+ err = e;
+ }
+ }
+
+ /**
+ * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],
+ * String)
+ */
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ if (chain == null || chain.length == 0 || authType == null
+ || authType.length() == 0) {
+ throw new IllegalArgumentException("null or zero-length parameter");
+ }
+ if (err != null) {
+ throw new CertificateException(err);
+ }
+
+ // Cater for degenerate special case where we can't
+ // establish an actual certificate chain the usual way,
+ // but have the peer certificate in our trust store.
+ if (isDirectlyTrustedCert(chain)) {
+ return;
+ }
+
+ try {
+ CertPath certPath = factory.generateCertPath(Arrays.asList(chain));
+ if (!Arrays.equals(chain[0].getEncoded(),
+ ((X509Certificate)certPath.getCertificates().get(0)).getEncoded())) {
+ // sanity check failed (shouldn't ever happen, but we are using pretty remote code)
+ throw new CertificateException("Certificate chain error");
+ }
+ validator.validate(certPath, params);
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new CertificateException(e);
+ } catch (CertPathValidatorException e) {
+ throw new CertificateException(e);
+ }
+ }
+
+ /**
+ * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],
+ * String)
+ */
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ if (chain == null || chain.length == 0 || authType == null
+ || authType.length() == 0) {
+ throw new IllegalArgumentException("null or zero-length parameter");
+ }
+ if (err != null) {
+ throw new CertificateException(err);
+ }
+
+ // Cater for degenerate special case where we can't
+ // establish an actual certificate chain the usual way,
+ // but have the peer certificate in our trust store.
+ if (isDirectlyTrustedCert(chain)) {
+ return;
+ }
+
+ try {
+ CertPath certPath = factory.generateCertPath(Arrays.asList(chain));
+ if (!Arrays.equals(chain[0].getEncoded(),
+ ((X509Certificate)certPath.getCertificates().get(0)).getEncoded())) {
+ // sanity check failed (shouldn't ever happen, but we are using pretty remote code)
+ throw new CertificateException("Certificate chain error");
+ }
+ validator.validate(certPath, params);
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new CertificateException(e);
+ } catch (CertPathValidatorException e) {
+ throw new CertificateException(e);
+ }
+ }
+
+ /**
+ * Checks whether the given chain is just a certificate
+ * that we have in our trust store.
+ *
+ * @param chain The certificate chain.
+ *
+ * @return True if the certificate is in our trust store, false otherwise.
+ */
+ private boolean isDirectlyTrustedCert(X509Certificate[] chain) {
+ byte[] questionable;
+
+ if (chain.length == 1) {
+ try {
+ questionable = chain[0].getEncoded();
+ Set<TrustAnchor> anchors = params.getTrustAnchors();
+
+ for (TrustAnchor trustAnchor : anchors) {
+ byte[] trusted = trustAnchor.getTrustedCert().getEncoded();
+
+ if (Arrays.equals(questionable, trusted)) {
+ return true;
+ }
+ }
+ } catch (CertificateEncodingException e) {
+ // Ignore.
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
+ */
+ public X509Certificate[] getAcceptedIssuers() {
+ if (params == null) {
+ return new X509Certificate[0];
+ }
+ Set anchors = params.getTrustAnchors();
+ X509Certificate[] certs = new X509Certificate[anchors.size()];
+ int i = 0;
+ for (Iterator it = anchors.iterator(); it.hasNext();) {
+ certs[i++] = ((TrustAnchor) it.next()).getTrustedCert();
+ }
+ return certs;
+ }
+}
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
new file mode 100644
index 0000000..59859ba
--- /dev/null
+++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Native glue for Java class org.apache.harmony.xnet.provider.jsse.NativeCrypto
+ */
+
+#define LOG_TAG "NativeCrypto"
+
+#include <jni.h>
+#include <JNIHelp.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/dsa.h>
+#include <openssl/rsa.h>
+
+/**
+ * Frees the SSL error state.
+ *
+ * OpenSSL keeps an "error stack" per thread, and given that this code
+ * can be called from arbitrary threads that we don't keep track of,
+ * we err on the side of freeing the error state promptly (instead of,
+ * say, at thread death).
+ */
+static void freeSslErrorState(void) {
+ ERR_clear_error();
+ ERR_remove_state(0);
+}
+
+/**
+ * Throws a NullPointerException without any message.
+ */
+static void throwNullPointerException(JNIEnv* env) {
+ jniThrowException(env, "java/lang/NullPointerException", NULL);
+}
+
+/**
+ * Throws a RuntimeException with a human-readable error message.
+ */
+static void throwRuntimeException(JNIEnv* env, const char* message) {
+ jniThrowException(env, "java/lang/RuntimeException", message);
+}
+
+/*
+ * Checks this thread's OpenSSL error queue and throws a RuntimeException if
+ * necessary.
+ *
+ * @return 1 if an exception was thrown, 0 if not.
+ */
+static int throwExceptionIfNecessary(JNIEnv* env) {
+ int error = ERR_get_error();
+ int result = 0;
+
+ if (error != 0) {
+ char message[50];
+ ERR_error_string_n(error, message, sizeof(message));
+ LOGD("OpenSSL error %d: %s", error, message);
+ throwRuntimeException(env, message);
+ result = 1;
+ }
+
+ freeSslErrorState();
+ return result;
+}
+
+/**
+ * Converts a Java byte[] to an OpenSSL BIGNUM, allocating the BIGNUM on the
+ * fly.
+ */
+static BIGNUM* arrayToBignum(JNIEnv* env, jbyteArray source) {
+ // LOGD("Entering arrayToBignum()");
+
+ jbyte* sourceBytes = env->GetByteArrayElements(source, NULL);
+ int sourceLength = env->GetArrayLength(source);
+ BIGNUM* bignum = BN_bin2bn((unsigned char*) sourceBytes, sourceLength, NULL);
+ env->ReleaseByteArrayElements(source, sourceBytes, JNI_ABORT);
+ return bignum;
+}
+
+static void rsaDestroyKey(JNIEnv* env, jclass clazz, RSA* rsa);
+
+/**
+ * private static native int rsaCreatePublicKey(byte[] n, byte[] e);
+ */
+static RSA* rsaCreatePublicKey(JNIEnv* env, jclass clazz, jbyteArray n, jbyteArray e) {
+ // LOGD("Entering rsaCreatePublicKey()");
+
+ RSA* rsa = RSA_new();
+
+ rsa->n = arrayToBignum(env, n);
+ rsa->e = arrayToBignum(env, e);
+
+ if (rsa->n == NULL || rsa->e == NULL) {
+ rsaDestroyKey(env, clazz, rsa);
+ throwRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
+ return NULL;
+ }
+
+ return rsa;
+}
+
+/**
+ * private static native int rsaCreatePrivateKey(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q);
+ */
+static RSA* rsaCreatePrivateKey(JNIEnv* env, jclass clazz, jbyteArray n, jbyteArray e, jbyteArray d, jbyteArray p, jbyteArray q) {
+ // LOGD("Entering rsaCreatePrivateKey()");
+
+ RSA* rsa = RSA_new();
+
+ rsa->n = arrayToBignum(env, n);
+ rsa->e = arrayToBignum(env, e);
+ rsa->d = arrayToBignum(env, d);
+ rsa->p = arrayToBignum(env, p);
+ rsa->q = arrayToBignum(env, q);
+
+ int check = RSA_check_key(rsa);
+ LOGI("RSA_check_key returns %d", check);
+
+ if (rsa->n == NULL || rsa->e == NULL || rsa->d == NULL || rsa->p == NULL || rsa->q == NULL) {
+ rsaDestroyKey(env, clazz, rsa);
+ throwRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
+ return NULL;
+ }
+
+ return rsa;
+}
+
+/**
+ * private static native void rsaDestroyKey(int rsa);
+ */
+static void rsaDestroyKey(JNIEnv* env, jclass clazz, RSA* rsa) {
+ // LOGD("Entering rsaDestroyKey()");
+
+ if (rsa != NULL) {
+ RSA_free(rsa);
+ }
+}
+
+/**
+ * private static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g, byte[] pub_key, byte[] priv_key);
+ */
+static EVP_PKEY* NativeCrypto_EVP_PKEY_new_DSA(JNIEnv* env, jclass clazz, jbyteArray p, jbyteArray q, jbyteArray g, jbyteArray pub_key, jbyteArray priv_key) {
+ // LOGD("Entering EVP_PKEY_new_DSA()");
+
+ DSA* dsa = DSA_new();
+
+ dsa->p = arrayToBignum(env, p);
+ dsa->q = arrayToBignum(env, q);
+ dsa->g = arrayToBignum(env, g);
+ dsa->pub_key = arrayToBignum(env, pub_key);
+
+ if (priv_key != NULL) {
+ dsa->priv_key = arrayToBignum(env, priv_key);
+ }
+
+ if (dsa->p == NULL || dsa->q == NULL || dsa->g == NULL || dsa->pub_key == NULL) {
+ DSA_free(dsa);
+ throwRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
+ return NULL;
+ }
+
+ EVP_PKEY* pkey = EVP_PKEY_new();
+ EVP_PKEY_assign_DSA(pkey, dsa);
+
+ return pkey;
+}
+
+/**
+ * private static native int EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q);
+ */
+static EVP_PKEY* NativeCrypto_EVP_PKEY_new_RSA(JNIEnv* env, jclass clazz, jbyteArray n, jbyteArray e, jbyteArray d, jbyteArray p, jbyteArray q) {
+ // LOGD("Entering EVP_PKEY_new_RSA()");
+
+ RSA* rsa = RSA_new();
+
+ rsa->n = arrayToBignum(env, n);
+ rsa->e = arrayToBignum(env, e);
+
+ if (d != NULL) {
+ rsa->d = arrayToBignum(env, d);
+ }
+
+ if (p != NULL) {
+ rsa->p = arrayToBignum(env, p);
+ }
+
+ if (q != NULL) {
+ rsa->q = arrayToBignum(env, q);
+ }
+
+ // int check = RSA_check_key(rsa);
+ // LOGI("RSA_check_key returns %d", check);
+
+ if (rsa->n == NULL || rsa->e == NULL) {
+ RSA_free(rsa);
+ throwRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
+ return NULL;
+ }
+
+ EVP_PKEY* pkey = EVP_PKEY_new();
+ EVP_PKEY_assign_RSA(pkey, rsa);
+
+ return pkey;
+}
+
+/**
+ * private static native void EVP_PKEY_free(int pkey);
+ */
+static void NativeCrypto_EVP_PKEY_free(JNIEnv* env, jclass clazz, EVP_PKEY* pkey) {
+ // LOGD("Entering EVP_PKEY_free()");
+
+ if (pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ }
+}
+
+/*
+ * public static native int EVP_new()
+ */
+static jint NativeCrypto_EVP_new(JNIEnv* env, jclass clazz) {
+ // LOGI("NativeCrypto_EVP_DigestNew");
+
+ return (jint)EVP_MD_CTX_create();
+}
+
+/*
+ * public static native void EVP_free(int)
+ */
+static void NativeCrypto_EVP_free(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) {
+ // LOGI("NativeCrypto_EVP_DigestFree");
+
+ if (ctx != NULL) {
+ EVP_MD_CTX_destroy(ctx);
+ }
+}
+
+/*
+ * public static native int EVP_DigestFinal(int, byte[], int)
+ */
+static jint NativeCrypto_EVP_DigestFinal(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray hash, jint offset) {
+ // LOGI("NativeCrypto_EVP_DigestFinal%x, %x, %d, %d", ctx, hash, offset);
+
+ if (ctx == NULL || hash == NULL) {
+ throwNullPointerException(env);
+ return -1;
+ }
+
+ int result = -1;
+
+ jbyte* hashBytes = env->GetByteArrayElements(hash, NULL);
+ EVP_DigestFinal(ctx, (unsigned char*) (hashBytes + offset), (unsigned int*)&result);
+ env->ReleaseByteArrayElements(hash, hashBytes, 0);
+
+ throwExceptionIfNecessary(env);
+
+ return result;
+}
+
+/*
+ * public static native void EVP_DigestInit(int, java.lang.String)
+ */
+static void NativeCrypto_EVP_DigestInit(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jstring algorithm) {
+ // LOGI("NativeCrypto_EVP_DigestInit");
+
+ if (ctx == NULL || algorithm == NULL) {
+ throwNullPointerException(env);
+ return;
+ }
+
+ const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL);
+
+ const EVP_MD *digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars));
+ env->ReleaseStringUTFChars(algorithm, algorithmChars);
+
+ if (digest == NULL) {
+ throwRuntimeException(env, "Hash algorithm not found");
+ return;
+ }
+
+ EVP_DigestInit(ctx, digest);
+
+ throwExceptionIfNecessary(env);
+}
+
+/*
+ * public static native void EVP_DigestReset(int)
+ */
+static jint NativeCrypto_EVP_DigestSize(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) {
+ // LOGI("NativeCrypto_EVP_DigestSize");
+
+ if (ctx == NULL) {
+ throwNullPointerException(env);
+ return -1;
+ }
+
+ int result = EVP_MD_CTX_size(ctx);
+
+ throwExceptionIfNecessary(env);
+
+ return result;
+}
+
+/*
+ * public static native void EVP_DigestReset(int)
+ */
+static jint NativeCrypto_EVP_DigestBlockSize(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx) {
+ // LOGI("NativeCrypto_EVP_DigestBlockSize");
+
+ if (ctx == NULL) {
+ throwNullPointerException(env);
+ return -1;
+ }
+
+ int result = EVP_MD_CTX_block_size(ctx);
+
+ throwExceptionIfNecessary(env);
+
+ return result;
+}
+
+/*
+ * public static native void EVP_DigestUpdate(int, byte[], int, int)
+ */
+static void NativeCrypto_EVP_DigestUpdate(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray buffer, jint offset, jint length) {
+ // LOGI("NativeCrypto_EVP_DigestUpdate %x, %x, %d, %d", ctx, buffer, offset, length);
+
+ if (ctx == NULL || buffer == NULL) {
+ throwNullPointerException(env);
+ return;
+ }
+
+ jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL);
+ EVP_DigestUpdate(ctx, (unsigned char*) (bufferBytes + offset), length);
+ env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT);
+
+ throwExceptionIfNecessary(env);
+}
+
+/*
+ * public static native void EVP_VerifyInit(int, java.lang.String)
+ */
+static void NativeCrypto_EVP_VerifyInit(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jstring algorithm) {
+ // LOGI("NativeCrypto_EVP_VerifyInit");
+
+ if (ctx == NULL || algorithm == NULL) {
+ throwNullPointerException(env);
+ return;
+ }
+
+ const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL);
+
+ const EVP_MD *digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars));
+ env->ReleaseStringUTFChars(algorithm, algorithmChars);
+
+ if (digest == NULL) {
+ throwRuntimeException(env, "Hash algorithm not found");
+ return;
+ }
+
+ EVP_VerifyInit(ctx, digest);
+
+ throwExceptionIfNecessary(env);
+}
+
+/*
+ * public static native void EVP_VerifyUpdate(int, byte[], int, int)
+ */
+static void NativeCrypto_EVP_VerifyUpdate(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray buffer, jint offset, jint length) {
+ // LOGI("NativeCrypto_EVP_VerifyUpdate %x, %x, %d, %d", ctx, buffer, offset, length);
+
+ if (ctx == NULL || buffer == NULL) {
+ throwNullPointerException(env);
+ return;
+ }
+
+ jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL);
+ EVP_VerifyUpdate(ctx, (unsigned char*) (bufferBytes + offset), length);
+ env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT);
+
+ throwExceptionIfNecessary(env);
+}
+
+/*
+ * public static native void EVP_VerifyFinal(int, byte[], int, int, int)
+ */
+static int NativeCrypto_EVP_VerifyFinal(JNIEnv* env, jclass clazz, EVP_MD_CTX* ctx, jbyteArray buffer, jint offset, jint length, EVP_PKEY* pkey) {
+ // LOGI("NativeCrypto_EVP_VerifyFinal %x, %x, %d, %d %x", ctx, buffer, offset, length, pkey);
+
+ if (ctx == NULL || buffer == NULL || pkey == NULL) {
+ throwNullPointerException(env);
+ return -1;
+ }
+
+ jbyte* bufferBytes = env->GetByteArrayElements(buffer, NULL);
+ int result = EVP_VerifyFinal(ctx, (unsigned char*) (bufferBytes + offset), length, pkey);
+ env->ReleaseByteArrayElements(buffer, bufferBytes, JNI_ABORT);
+
+ throwExceptionIfNecessary(env);
+
+ return result;
+}
+
+/*
+ * Defines the mapping from Java methods and their signatures
+ * to native functions. Order is (1) Java name, (2) signature,
+ * (3) pointer to C function.
+ */
+static JNINativeMethod methods[] = {
+/*
+ { "dsaCreatePublicKey", "([B[B[B[B)I", (void*)dsaCreatePublicKey },
+ { "dsaCreatePrivateKey", "([B[B[B[B[B)I", (void*)dsaCreatePrivateKey },
+ { "dsaDestroyKey", "(I)V", (void*)dsaDestroyKey },
+ { "rsaCreatePublicKey", "([B[B)I", (void*)rsaCreatePublicKey },
+ { "rsaCreatePrivateKey", "([B[B[B[B[B)I", (void*)rsaCreatePrivateKey },
+ { "rsaDestroyKey", "(I)V", (void*)rsaDestroyKey },
+*/
+ { "EVP_PKEY_new_DSA", "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_DSA },
+ { "EVP_PKEY_new_RSA", "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_RSA },
+ { "EVP_PKEY_free", "(I)V", (void*)NativeCrypto_EVP_PKEY_free },
+ { "EVP_new", "()I", (void*)NativeCrypto_EVP_new },
+ { "EVP_free", "(I)V", (void*)NativeCrypto_EVP_free },
+ { "EVP_DigestFinal", "(I[BI)I", (void*)NativeCrypto_EVP_DigestFinal },
+ { "EVP_DigestInit", "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_DigestInit },
+ { "EVP_DigestBlockSize", "(I)I", (void*)NativeCrypto_EVP_DigestBlockSize },
+ { "EVP_DigestSize", "(I)I", (void*)NativeCrypto_EVP_DigestSize },
+ { "EVP_DigestUpdate", "(I[BII)V", (void*)NativeCrypto_EVP_DigestUpdate },
+ { "EVP_VerifyInit", "(ILjava/lang/String;)V", (void*)NativeCrypto_EVP_VerifyInit },
+ { "EVP_VerifyUpdate", "(I[BII)V", (void*)NativeCrypto_EVP_VerifyUpdate },
+ { "EVP_VerifyFinal", "(I[BIII)I", (void*)NativeCrypto_EVP_VerifyFinal }
+};
+
+/*
+ * Peforms the actual registration of the native methods.
+ * Also looks up the fields that belong to the class (if
+ * any) and stores the field IDs. Simply remove what you
+ * don't need.
+ */
+extern "C" int register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(JNIEnv* env) {
+ int result;
+ result = jniRegisterNativeMethods(env, "org/apache/harmony/xnet/provider/jsse/NativeCrypto", methods, NELEM(methods));
+ if (result == -1) {
+ return -1;
+ }
+
+ jclass clazz;
+ clazz = env->FindClass("org/apache/harmony/xnet/provider/jsse/NativeCrypto");
+ if (clazz == NULL) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp
new file mode 100644
index 0000000..13a1e61
--- /dev/null
+++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenSSLServerSocketImpl"
+
+#include <jni.h>
+#include <JNIHelp.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+
+#include "org_apache_harmony_xnet_provider_jsse_common.h"
+
+/**
+ * Module scope variables initialized during JNI registration.
+ */
+static jfieldID field_ssl_ctx;
+
+/**
+ * Throws java.io.IOexception with the provided message.
+ */
+static void throwIOExceptionStr(JNIEnv* env, const char* message)
+{
+ jclass exClass = env->FindClass("java/io/IOException");
+
+ if (exClass == NULL)
+ {
+ LOGE("Unable to find class java/io/IOException");
+ }
+ else
+ {
+ env->ThrowNew(exClass, message);
+ }
+}
+
+/**
+ * Initialization phase of OpenSSL: Loads the Error strings, the crypto algorithms and reset the OpenSSL library
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_initstatic(JNIEnv* env, jobject obj)
+{
+ SSL_load_error_strings();
+ ERR_load_crypto_strings();
+ SSL_library_init();
+ OpenSSL_add_all_algorithms();
+}
+
+/**
+ * Initialization phase for a server socket with OpenSSL. The server's private key and X509 certificate are read and
+ * the Linux /dev/random file is loaded as RNG for the session keys.
+ *
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_init(JNIEnv* env, jobject object,
+ jstring privatekey, jstring certificates, jbyteArray seed)
+{
+ SSL_CTX *ssl_ctx;
+ const char *privatekeychar;
+ const char *certificateschar;
+ EVP_PKEY * privatekeyevp;
+
+ BIO *privatekeybio;
+ BIO *certificatesbio;
+
+ // 'seed == null' when no SecureRandom Object is set
+ // in the SSLContext.
+ if (seed != NULL) {
+ jboolean iscopy = JNI_FALSE;
+ jbyte* randseed = env->GetByteArrayElements(seed, &iscopy);
+ RAND_seed((unsigned char*) randseed, 1024);
+ } else {
+ RAND_load_file("/dev/urandom", 1024);
+ }
+
+ ssl_ctx = SSL_CTX_new(SSLv23_server_method());
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
+
+ privatekeychar = env->GetStringUTFChars((jstring)privatekey, NULL);
+ privatekeybio = BIO_new_mem_buf((void*)privatekeychar, -1);
+
+ privatekeyevp = PEM_read_bio_PrivateKey(privatekeybio, NULL, 0, NULL);
+ env->ReleaseStringUTFChars(privatekey, privatekeychar);
+
+ if (privatekeyevp == NULL) {
+ LOGE(ERR_error_string(ERR_get_error(), NULL));
+ throwIOExceptionStr(env, "Error parsing the private key");
+ return;
+ }
+
+ certificateschar = env->GetStringUTFChars((jstring)certificates, NULL);
+ certificatesbio = BIO_new_mem_buf((void*)certificateschar, -1);
+
+ X509 * certificatesx509 = PEM_read_bio_X509(certificatesbio, NULL, 0, NULL);
+ env->ReleaseStringUTFChars(certificates, certificateschar);
+
+ if (certificatesx509 == NULL) {
+ LOGE(ERR_error_string(ERR_get_error(), NULL));
+ throwIOExceptionStr(env, "Error parsing the certificates");
+ return;
+ }
+
+ if (!SSL_CTX_use_certificate(ssl_ctx, certificatesx509)) {
+ LOGE(ERR_error_string(ERR_get_error(), NULL));
+ throwIOExceptionStr(env, "Error setting the certificates");
+ return;
+ }
+
+ if (!SSL_CTX_use_PrivateKey(ssl_ctx, privatekeyevp)) {
+ LOGE(ERR_error_string(ERR_get_error(), NULL));
+ throwIOExceptionStr(env, "Error setting the private key");
+ return;
+ }
+
+ if (!SSL_CTX_check_private_key(ssl_ctx)) {
+ LOGE(ERR_error_string(ERR_get_error(), NULL));
+ throwIOExceptionStr(env, "Error checking private key");
+ return;
+ }
+
+ env->SetIntField(object, field_ssl_ctx, (int)ssl_ctx);
+}
+
+/**
+ * Loads the desired protocol for the OpenSSL server and enables it.
+ * For example SSL_OP_NO_TLSv1 means do not use TLS v. 1.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledprotocols(JNIEnv* env,
+ jobject object, jlong protocol)
+{
+ if (protocol != 0x00000000L) {
+ if (protocol & SSL_OP_NO_SSLv3)
+ LOGD("SSL_OP_NO_SSLv3 is set");
+ if (protocol & SSL_OP_NO_TLSv1)
+ LOGD("SSL_OP_NO_TLSv1 is set");
+
+ SSL_CTX* ctx = (SSL_CTX*)env->GetIntField(object, field_ssl_ctx);
+ SSL_CTX_set_options((SSL_CTX*)ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2|(long)protocol);
+ }
+}
+
+/**
+ * Loads the ciphers suites that are supported by the OpenSSL server
+ * and returns them in a string array.
+ */
+static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getsupportedciphersuites(JNIEnv* env,
+ jobject object)
+{
+ SSL_CTX* ctx;
+ SSL* ssl;
+ STACK_OF(SSL_CIPHER) *sk;
+ jobjectArray ret;
+ int i;
+ const char *c;
+
+ ctx = SSL_CTX_new(SSLv23_server_method());
+ ssl = SSL_new(ctx);
+ sk=SSL_get_ciphers(ssl);
+
+ ret= (jobjectArray)env->NewObjectArray(5,
+ env->FindClass("java/lang/String"),
+ env->NewStringUTF(""));
+
+ i = 0;
+ while (SSL_get_cipher_list(ssl,i) != NULL) {
+ i++;
+ }
+
+ ret = (jobjectArray)env->NewObjectArray(i,
+ env->FindClass("java/lang/String"),
+ env->NewStringUTF(""));
+
+ for (i=0; ; i++) {
+ c=SSL_get_cipher_list(ssl,i);
+ if (c == NULL) break;
+
+ env->SetObjectArrayElement(ret,i,env->NewStringUTF(c));
+ }
+
+ return ret;
+}
+
+/**
+ * Loads the ciphers suites that are enabled in the OpenSSL server
+ * and returns them in a string array.
+ */
+static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getenabledciphersuites(JNIEnv* env,
+ jobject object)
+{
+ SSL_CTX* ctx;
+ SSL* ssl;
+ jobjectArray ret;
+ int i;
+ const char *c;
+
+ ctx = (SSL_CTX*)env->GetIntField(object, field_ssl_ctx);
+ ssl = SSL_new(ctx);
+
+ i = 0;
+ while (SSL_get_cipher_list(ssl,i) != NULL) {
+ i++;
+ }
+
+ ret = (jobjectArray)env->NewObjectArray(i,
+ env->FindClass("java/lang/String"),
+ env->NewStringUTF(""));
+
+ for (i = 0; ; i++) {
+ c = SSL_get_cipher_list(ssl,i);
+ if (c == NULL) break;
+
+ env->SetObjectArrayElement(ret,i,env->NewStringUTF(c));
+ }
+
+ return ret;
+}
+
+/**
+ * Sets the ciphers suites that are enabled in the OpenSSL server.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledciphersuites(JNIEnv* env,
+ jobject object, jstring controlstring)
+{
+ SSL_CTX* ctx;
+ const char *str;
+ int ret;
+
+ ctx = (SSL_CTX*)env->GetIntField(object, field_ssl_ctx);
+ str = env->GetStringUTFChars(controlstring, 0);
+ ret = SSL_CTX_set_cipher_list(ctx, str);
+
+ if(ret == 0) {
+ jclass exClass = env->FindClass("java/lang/IllegalArgumentException");
+ env->ThrowNew(exClass, "Illegal cipher suite strings.");
+ }
+}
+
+/**
+ * Sets the client's credentials and the depth of theirs verification.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth(JNIEnv* env,
+ jobject object, jint value)
+{
+ SSL_CTX *ssl_ctx = (SSL_CTX *)env->GetIntField(object, field_ssl_ctx);
+ SSL_CTX_set_verify(ssl_ctx, (int)value, verify_callback);
+}
+
+/**
+ * The actual SSL context is reset.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativefree(JNIEnv* env, jobject object)
+{
+ SSL_CTX *ctx = (SSL_CTX *)env->GetIntField(object, field_ssl_ctx);
+ SSL_CTX_free(ctx);
+ env->SetIntField(object, field_ssl_ctx, 0);
+}
+
+/**
+ * The actual JNI methods' mapping table for the class OpenSSLServerSocketImpl.
+ */
+static JNINativeMethod sMethods[] =
+{
+ {"nativeinitstatic", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_initstatic},
+ {"nativeinit", "(Ljava/lang/String;Ljava/lang/String;[B)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_init},
+ {"nativesetenabledprotocols", "(J)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledprotocols},
+ {"nativegetsupportedciphersuites", "()[Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getsupportedciphersuites},
+ {"nativegetenabledciphersuites", "()[Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getenabledciphersuites},
+ {"nativesetenabledciphersuites", "(Ljava/lang/String;)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledciphersuites},
+ {"nativesetclientauth", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth},
+ {"nativefree", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativefree}
+};
+
+/**
+ * Register the native methods with JNI for the class OpenSSLServerSocketImpl.
+ */
+extern "C" int register_org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl(JNIEnv* env)
+{
+ int ret;
+ jclass clazz;
+
+ clazz = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl");
+
+ if (clazz == NULL) {
+ LOGE("Can't find org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl");
+ return -1;
+ }
+
+ ret = jniRegisterNativeMethods(env, "org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl",
+ sMethods, NELEM(sMethods));
+
+ if (ret >= 0) {
+ // Note: do these after the registration of native methods, because
+ // there is a static method "initstatic" that's called when the
+ // OpenSSLServerSocketImpl class is first loaded, and that required
+ // a native method to be associated with it.
+ field_ssl_ctx = env->GetFieldID(clazz, "ssl_ctx", "I");
+ if (field_ssl_ctx == NULL) {
+ LOGE("Can't find OpenSSLServerSocketImpl.ssl_ctx");
+ return -1;
+ }
+ }
+ return ret;
+}
diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp
new file mode 100644
index 0000000..42c4e77
--- /dev/null
+++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenSSLSessionImpl"
+
+#include <jni.h>
+#include <JNIHelp.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+
+#include "org_apache_harmony_xnet_provider_jsse_common.h"
+
+/**
+ * Module scope variables initialized during JNI registration.
+ */
+static jfieldID field_session;
+
+static SSL_SESSION *getSslSessionPointer(JNIEnv* env, jobject object) {
+ SSL_SESSION* session = (SSL_SESSION *)env->GetIntField(object, field_session);
+
+ return session;
+}
+
+/**
+ * Throws java.io.IOexception with the provided message.
+ */
+static void throwIOExceptionStr(JNIEnv* env, const char* message)
+{
+ jclass exClass = env->FindClass("java/io/IOException");
+
+ if (exClass == NULL) {
+ LOGE("Unable to find class java/io/IOException");
+ } else {
+ env->ThrowNew(exClass, message);
+ }
+}
+
+/**
+ * Gets the peer certificate in the chain and fills a byte array with the
+ * information therein.
+ */
+static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getpeercertificates(JNIEnv* env,
+ jobject object, jint jssl)
+{
+ SSL_SESSION *ssl_session;
+ SSL_CTX* ssl_ctx;
+ SSL* ssl;
+ STACK_OF(X509) *chain;
+ jobjectArray objectArray;
+
+ ssl_session = getSslSessionPointer(env, object);
+
+ ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+ ssl = SSL_new(ssl_ctx);
+
+ SSL_set_session(ssl, ssl_session);
+
+ chain = SSL_get_peer_cert_chain(ssl);
+
+ objectArray = getcertificatebytes(env, chain);
+
+ SSL_free(ssl);
+ SSL_CTX_free(ssl_ctx);
+
+ return objectArray;
+}
+
+/**
+ * Gets and returns in a byte array the ID of the actual SSL session.
+ */
+static jbyteArray org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getid(JNIEnv* env, jobject object)
+{
+ SSL_SESSION * ssl_session;
+ jbyteArray bytes;
+ jbyte *tmp;
+
+ ssl_session = getSslSessionPointer(env, object);
+
+ bytes = env->NewByteArray(ssl_session->session_id_length);
+ if (bytes != NULL) {
+ tmp = env->GetByteArrayElements(bytes, NULL);
+ memcpy(tmp, ssl_session->session_id, ssl_session->session_id_length);
+ env->ReleaseByteArrayElements(bytes, tmp, 0);
+ }
+
+ return bytes;
+}
+
+/**
+ * Gets and returns in a long integer the creation's time of the
+ * actual SSL session.
+ */
+static jlong org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getcreationtime(JNIEnv* env, jobject object)
+{
+ SSL_SESSION * ssl_session;
+
+ ssl_session = getSslSessionPointer(env, object);
+
+ // convert the creation time from seconds to milliseconds
+ return (jlong)(1000L * ssl_session->time);
+}
+
+/**
+ * Gets and returns in a string the peer's host's name.
+ */
+static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getpeerhost(JNIEnv* env, jobject object)
+{
+ SSL_CTX *ssl_ctx;
+ SSL *ssl;
+ SSL_SESSION *ssl_session;
+ BIO *bio;
+ char* hostname;
+ jstring result;
+
+ ssl_session = getSslSessionPointer(env, object);
+
+ ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+ ssl = SSL_new(ssl_ctx);
+
+ SSL_set_session(ssl, ssl_session);
+
+ bio = SSL_get_rbio(ssl);
+
+ hostname = BIO_get_conn_hostname(bio);
+
+ /* Notice: hostname can be NULL */
+ result = env->NewStringUTF(hostname);
+
+ SSL_free(ssl);
+ SSL_CTX_free(ssl_ctx);
+
+ return result;
+}
+
+/**
+ * Gets and returns in a string the peer's port name (https, ftp, etc.).
+ */
+static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getpeerport(JNIEnv* env, jobject object)
+{
+ SSL_CTX *ssl_ctx;
+ SSL *ssl;
+ SSL_SESSION *ssl_session;
+ BIO *bio;
+ char *port;
+ jstring result;
+
+ ssl_session = getSslSessionPointer(env, object);
+
+ ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+ ssl = SSL_new(ssl_ctx);
+
+ SSL_set_session(ssl, ssl_session);
+
+ bio = SSL_get_rbio(ssl);
+ port = BIO_get_conn_port(bio);
+
+ /* Notice: port name can be NULL */
+ result = env->NewStringUTF(port);
+
+ SSL_free(ssl);
+ SSL_CTX_free(ssl_ctx);
+
+ return result;
+}
+
+/**
+ * Gets and returns in a string the version of the SSL protocol. If it
+ * returns the string "unknown" it means that no connection is established.
+ */
+static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getprotocol(JNIEnv* env, jobject object)
+{
+ SSL_CTX *ssl_ctx;
+ SSL *ssl;
+ SSL_SESSION *ssl_session;
+ const char* protocol;
+ jstring result;
+
+ ssl_session = getSslSessionPointer(env, object);
+
+ ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+ ssl = SSL_new(ssl_ctx);
+
+ SSL_set_session(ssl, ssl_session);
+
+ protocol = SSL_get_version(ssl);
+
+ result = env->NewStringUTF(protocol);
+
+ SSL_free(ssl);
+ SSL_CTX_free(ssl_ctx);
+
+ return result;
+}
+
+/**
+ * Gets and returns in a string the set of ciphers the actual SSL session uses.
+ */
+static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getciphersuite(JNIEnv* env, jobject object)
+{
+ SSL_CTX *ssl_ctx;
+ SSL *ssl;
+ SSL_SESSION *ssl_session;
+
+ ssl_session = getSslSessionPointer(env, object);
+
+ ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+ ssl = SSL_new(ssl_ctx);
+
+ SSL_set_session(ssl, ssl_session);
+
+ SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
+ jstring result = env->NewStringUTF(SSL_CIPHER_get_name(cipher));
+
+ SSL_free(ssl);
+ SSL_CTX_free(ssl_ctx);
+}
+
+/**
+ * Frees the SSL session.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_free(JNIEnv* env, jobject object, jint session)
+{
+ LOGD("Freeing OpenSSL session");
+ SSL_SESSION* ssl_session;
+ ssl_session = (SSL_SESSION*) session;
+ SSL_SESSION_free(ssl_session);
+}
+
+/**
+ * The actual JNI methods' mapping table for the class OpenSSLSessionImpl.
+ */
+static JNINativeMethod sMethods[] =
+{
+ {"nativegetid", "()[B", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getid},
+ {"nativegetcreationtime", "()J", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getcreationtime},
+ {"nativegetpeerhost", "()Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getpeerhost},
+ {"nativegetpeerport", "()Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getpeerport},
+ {"nativegetprotocol", "()Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getprotocol},
+ {"nativegetpeercertificates", "()[[B", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getpeercertificates},
+ {"nativefree", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_free}
+};
+
+/**
+ * Register the native methods with JNI for the class OpenSSLSessionImpl.
+ */
+extern "C" int register_org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl(JNIEnv* env)
+{
+ int ret;
+ jclass clazz;
+
+ clazz = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl");
+
+ if (clazz == NULL) {
+ LOGE("Can't find org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl");
+ return -1;
+ }
+
+ field_session = env->GetFieldID(clazz, "session", "I");
+
+ ret = jniRegisterNativeMethods(env, "org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl",
+ sMethods, NELEM(sMethods));
+
+ return ret;
+}
diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp
new file mode 100644
index 0000000..b1ce1bc
--- /dev/null
+++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp
@@ -0,0 +1,1929 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenSSLSocketImpl"
+
+#include <cutils/log.h>
+#include <jni.h>
+#include <JNIHelp.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/select.h>
+
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+
+#include <utils/LogSocket.h>
+
+#include "org_apache_harmony_xnet_provider_jsse_common.h"
+
+/**
+ * Global variable used in module org_apache_harmony_xnet_provider_jsse_common.h.
+ * It is correctly updated in the function accept().
+ */
+int verify_callback_mydata_index = 0;
+
+/**
+ * Module scope variables initialized during JNI registration.
+ */
+static jfieldID field_ssl_ctx;
+static jfieldID field_ssl;
+static jfieldID field_descriptor;
+static jfieldID field_mImpl;
+static jfieldID field_mFD;
+static jfieldID field_timeout;
+
+/**
+ * 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) {
+ BIO *result = BIO_new(BIO_s_mem());
+ jsize length = env->GetStringUTFLength(string);
+ char buf[length + 1];
+
+ env->GetStringUTFRegion(string, 0, env->GetStringLength(string), buf);
+ buf[length] = '\0';
+
+ BIO_puts(result, buf);
+ return result;
+}
+
+/**
+ * Throws a NullPointerException without any message.
+ */
+static void throwNullPointerException(JNIEnv* env) {
+ if (jniThrowException(env, "java/lang/NullPointerException", NULL)) {
+ LOGE("Unable to throw");
+ }
+}
+
+/**
+ * Throws a RuntimeException with a human-readable error message.
+ */
+static void throwRuntimeException(JNIEnv* env, const char* message) {
+ if (jniThrowException(env, "java/lang/RuntimeException", message)) {
+ LOGE("Unable to throw");
+ }
+}
+
+/**
+ * Throws an SocketTimeoutException with the given string as a message.
+ */
+static void throwSocketTimeoutException(JNIEnv* env, const char* message) {
+ if (jniThrowException(env, "java/net/SocketTimeoutException", message)) {
+ LOGE("Unable to throw");
+ }
+}
+
+/**
+ * Throws an IOException with the given string as a message.
+ */
+static void throwIOExceptionStr(JNIEnv* env, const char* message) {
+ if (jniThrowException(env, "java/io/IOException", message)) {
+ LOGE("Unable to throw");
+ }
+}
+
+/**
+ * Frees the SSL error state.
+ *
+ * OpenSSL keeps an "error stack" per thread, and given that this code
+ * can be called from arbitrary threads that we don't keep track of,
+ * we err on the side of freeing the error state promptly (instead of,
+ * say, at thread death).
+ */
+static void freeSslErrorState(void) {
+ ERR_clear_error();
+ ERR_remove_state(0);
+}
+
+/**
+ * Throws an IOException with a message constructed from the current
+ * SSL errors. This will also log the errors.
+ *
+ * @param env the JNI environment
+ * @param sslReturnCode return code from failing SSL function
+ * @param sslErrorCode error code returned from SSL_get_error()
+ * @param message null-ok; general error message
+ */
+static void throwIOExceptionWithSslErrors(JNIEnv* env, int sslReturnCode,
+ int sslErrorCode, const char* message) {
+ const char* messageStr = NULL;
+ char* str;
+ int ret;
+
+ // First consult the SSL error code for the general message.
+ switch (sslErrorCode) {
+ case SSL_ERROR_NONE:
+ messageStr = "Ok";
+ break;
+ case SSL_ERROR_SSL:
+ messageStr = "Failure in SSL library, usually a protocol error";
+ break;
+ case SSL_ERROR_WANT_READ:
+ messageStr = "SSL_ERROR_WANT_READ occured. You should never see this.";
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ messageStr = "SSL_ERROR_WANT_WRITE occured. You should never see this.";
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ messageStr = "SSL_ERROR_WANT_X509_LOOKUP occured. You should never see this.";
+ break;
+ case SSL_ERROR_SYSCALL:
+ messageStr = "I/O error during system call";
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ messageStr = "SSL_ERROR_ZERO_RETURN occured. You should never see this.";
+ break;
+ case SSL_ERROR_WANT_CONNECT:
+ messageStr = "SSL_ERROR_WANT_CONNECT occured. You should never see this.";
+ break;
+ case SSL_ERROR_WANT_ACCEPT:
+ messageStr = "SSL_ERROR_WANT_ACCEPT occured. You should never see this.";
+ break;
+ default:
+ messageStr = "Unknown SSL error";
+ }
+
+ // Prepend either our explicit message or a default one.
+ if (asprintf(&str, "%s: %s",
+ (message != NULL) ? message : "SSL error", messageStr) == 0) {
+ throwIOExceptionStr(env, messageStr);
+ LOGV("%s", messageStr);
+ freeSslErrorState();
+ return;
+ }
+
+ char* allocStr = str;
+
+ // For SSL protocol errors, SSL might have more information.
+ if (sslErrorCode == SSL_ERROR_SSL) {
+ // Append each error as an additional line to the message.
+ for (;;) {
+ char errStr[256];
+ const char* file;
+ int line;
+ const char* data;
+ int flags;
+ unsigned long err =
+ ERR_get_error_line_data(&file, &line, &data, &flags);
+
+ if (err == 0) {
+ break;
+ }
+
+ ERR_error_string_n(err, errStr, sizeof(errStr));
+
+ ret = asprintf(&str, "%s\n%s (%s:%d %p:0x%08x)",
+ (allocStr == NULL) ? "" : allocStr,
+ errStr,
+ file,
+ line,
+ data,
+ flags);
+
+ if (ret < 0) {
+ break;
+ }
+
+ free(allocStr);
+ allocStr = str;
+ }
+ // For errors during system calls, errno might be our friend.
+ } else if (sslErrorCode == SSL_ERROR_SYSCALL) {
+ if (asprintf(&str, "%s, %s", allocStr, strerror(errno)) >= 0) {
+ free(allocStr);
+ allocStr = str;
+ }
+ // If the error code is invalid, print it.
+ } else if (sslErrorCode > SSL_ERROR_WANT_ACCEPT) {
+ if (asprintf(&str, ", error code is %d", sslErrorCode) >= 0) {
+ free(allocStr);
+ allocStr = str;
+ }
+ }
+
+ throwIOExceptionStr(env, allocStr);
+
+ LOGV("%s", allocStr);
+ free(allocStr);
+ freeSslErrorState();
+}
+
+/**
+ * Helper function that grabs the ssl pointer out of the given object.
+ * 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
+ * NULL, a caller of this function should simply return and allow JNI
+ * to do its thing.
+ *
+ * @param env non-null; the JNI environment
+ * @param obj non-null; socket object
+ * @param throwIfNull whether to throw if the SSL pointer is NULL
+ * @returns the pointer, which may be NULL
+ */
+static SSL *getSslPointer(JNIEnv* env, jobject obj, bool throwIfNull) {
+ SSL *ssl = (SSL *)env->GetIntField(obj, field_ssl);
+
+ if ((ssl == NULL) && throwIfNull) {
+ throwIOExceptionStr(env, "null SSL pointer");
+ }
+
+ return ssl;
+}
+
+// ============================================================================
+// === OpenSSL-related helper stuff begins here. ==============================
+// ============================================================================
+
+/**
+ * OpenSSL locking support. Taken from the O'Reilly book by Viega et al., but I
+ * suppose there are not many other ways to do this on a Linux system (modulo
+ * isomorphism).
+ */
+#define MUTEX_TYPE pthread_mutex_t
+#define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL)
+#define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x))
+#define MUTEX_LOCK(x) pthread_mutex_lock(&(x))
+#define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x))
+#define THREAD_ID pthread_self()
+#define THROW_EXCEPTION (-2)
+#define THROW_SOCKETTIMEOUTEXCEPTION (-3)
+
+static MUTEX_TYPE *mutex_buf = NULL;
+
+static void locking_function(int mode, int n, const char * file, int line) {
+ if (mode & CRYPTO_LOCK) {
+ MUTEX_LOCK(mutex_buf[n]);
+ } else {
+ MUTEX_UNLOCK(mutex_buf[n]);
+ }
+}
+
+static unsigned long id_function(void) {
+ return ((unsigned long)THREAD_ID);
+}
+
+int THREAD_setup(void) {
+ int i;
+
+ mutex_buf = (MUTEX_TYPE *)malloc(CRYPTO_num_locks( ) * sizeof(MUTEX_TYPE));
+
+ if(!mutex_buf) {
+ return 0;
+ }
+
+ for (i = 0; i < CRYPTO_num_locks( ); i++) {
+ MUTEX_SETUP(mutex_buf[i]);
+ }
+
+ CRYPTO_set_id_callback(id_function);
+ CRYPTO_set_locking_callback(locking_function);
+
+ return 1;
+}
+
+int THREAD_cleanup(void) {
+ int i;
+
+ if (!mutex_buf) {
+ return 0;
+ }
+
+ CRYPTO_set_id_callback(NULL);
+ CRYPTO_set_locking_callback(NULL);
+
+ for (i = 0; i < CRYPTO_num_locks( ); i++) {
+ MUTEX_CLEANUP(mutex_buf[i]);
+ }
+
+ free(mutex_buf);
+ mutex_buf = NULL;
+
+ return 1;
+}
+
+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
+
+/**
+ * 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.
+ */
+typedef struct app_data {
+ int aliveAndKicking;
+ int waitingThreads;
+ int fdsEmergency[2];
+ MUTEX_TYPE mutex;
+} APP_DATA;
+
+/**
+ * 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.
+ */
+static int sslCreateAppData(SSL* ssl) {
+ APP_DATA* data = (APP_DATA*) malloc(sizeof(APP_DATA));
+
+ memset(data, sizeof(APP_DATA), 0);
+
+ data->aliveAndKicking = 1;
+ data->waitingThreads = 0;
+ data->fdsEmergency[0] = -1;
+ data->fdsEmergency[1] = -1;
+
+ if (pipe(data->fdsEmergency) == -1) {
+ return -1;
+ }
+
+ if (MUTEX_SETUP(data->mutex) == -1) {
+ return -1;
+ }
+
+ SSL_set_app_data(ssl, (char*) data);
+
+ 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);
+
+ if (data != NULL) {
+ SSL_set_app_data(ssl, NULL);
+
+ data -> aliveAndKicking = 0;
+
+ if (data->fdsEmergency[0] != -1) {
+ close(data->fdsEmergency[0]);
+ }
+
+ if (data->fdsEmergency[1] != -1) {
+ close(data->fdsEmergency[1]);
+ }
+
+ MUTEX_CLEANUP(data->mutex);
+
+ free(data);
+ }
+}
+
+
+/**
+ * Frees the SSL_CTX struct for the given instance.
+ */
+static void free_ssl_ctx(JNIEnv* env, jobject object) {
+ /*
+ * Preserve and restore the exception state around this call, so
+ * that GetIntField and SetIntField will operate without complaint.
+ */
+ jthrowable exception = env->ExceptionOccurred();
+
+ if (exception != NULL) {
+ env->ExceptionClear();
+ }
+
+ SSL_CTX *ctx = (SSL_CTX *)env->GetIntField(object, field_ssl_ctx);
+
+ if (ctx != NULL) {
+ SSL_CTX_free(ctx);
+ env->SetIntField(object, field_ssl_ctx, (int) NULL);
+ }
+
+ if (exception != NULL) {
+ env->Throw(exception);
+ }
+}
+
+/**
+ * Frees the SSL struct for the given instance.
+ */
+static void free_ssl(JNIEnv* env, jobject object) {
+ /*
+ * Preserve and restore the exception state around this call, so
+ * that GetIntField and SetIntField will operate without complaint.
+ */
+ jthrowable exception = env->ExceptionOccurred();
+
+ if (exception != NULL) {
+ env->ExceptionClear();
+ }
+
+ SSL *ssl = (SSL *)env->GetIntField(object, field_ssl);
+
+ if (ssl != NULL) {
+ sslDestroyAppData(ssl);
+ SSL_free(ssl);
+ env->SetIntField(object, field_ssl, (int) NULL);
+ }
+
+ if (exception != NULL) {
+ env->Throw(exception);
+ }
+}
+
+/**
+ * Constructs the SSL struct for the given instance, replacing one
+ * that was already made, if any.
+ */
+static SSL* create_ssl(JNIEnv* env, jobject object, SSL_CTX* ssl_ctx) {
+ free_ssl(env, object);
+
+ SSL *ssl = SSL_new(ssl_ctx);
+ env->SetIntField(object, field_ssl, (int) ssl);
+ return ssl;
+}
+
+/**
+ * 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.
+ *
+ * @param ssl non-null; the SSL context
+ * @param buf non-null; buffer to read into
+ * @param len length of the buffer, in bytes
+ * @param sslReturnCode original SSL return code
+ * @param sslErrorCode filled in with the SSL error code in case of error
+ * @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,
+ int* sslErrorCode, int timeout) {
+
+ // LOGD("Entering sslRead, caller requests to read %d bytes...", len);
+
+ if (len == 0) {
+ // Don't bother doing anything in this case.
+ return 0;
+ }
+
+ int fd = SSL_get_fd(ssl);
+ BIO *bio = SSL_get_rbio(ssl);
+
+ APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
+
+ while (data->aliveAndKicking) {
+ errno = 0;
+
+ // Lock
+ if (MUTEX_LOCK(data->mutex) == -1) {
+ return -1;
+ }
+
+ unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
+
+ // LOGD("Doing SSL_Read()");
+ int result = SSL_read(ssl, buf, len);
+ int error = SSL_get_error(ssl, result);
+ freeSslErrorState();
+ // LOGD("Returned from SSL_Read() with result %d, error code %d", result, error);
+
+ // 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 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++;
+ }
+
+ // Unlock
+ MUTEX_UNLOCK(data->mutex);
+
+ switch (error) {
+ // Sucessfully read at least one byte.
+ case SSL_ERROR_NONE: {
+ add_recv_stats(fd, result);
+ return result;
+ }
+
+ // Read zero bytes. End of stream reached.
+ case SSL_ERROR_ZERO_RETURN: {
+ return -1;
+ }
+
+ // 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);
+ if (selectResult == -1) {
+ *sslReturnCode = -1;
+ *sslErrorCode = error;
+ return THROW_EXCEPTION;
+ } else if (selectResult == 0) {
+ return THROW_SOCKETTIMEOUTEXCEPTION;
+ }
+
+ break;
+ }
+
+ // A problem occured during a system call, but this is not
+ // necessarily an error.
+ case SSL_ERROR_SYSCALL: {
+ // Connection closed without proper shutdown. Tell caller we
+ // have reached end-of-stream.
+ if (result == 0) {
+ return -1;
+ }
+
+ // System call has been interrupted. Simply retry.
+ if (errno == EINTR) {
+ break;
+ }
+
+ // Note that for all other system call errors we fall through
+ // to the default case, which results in an Exception.
+ }
+
+ // Everything else is basically an error.
+ default: {
+ *sslReturnCode = result;
+ *sslErrorCode = error;
+ return THROW_EXCEPTION;
+ }
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * Helper function which does the actual writing. The Java layer guarantees that
+ * at most one thread will enter this function at any given time.
+ *
+ * @param ssl non-null; the SSL context
+ * @param buf non-null; buffer to write
+ * @param len length of the buffer, in bytes
+ * @param sslReturnCode original SSL return code
+ * @param sslErrorCode filled in with the SSL error code in case of error
+ * @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,
+ int* sslErrorCode) {
+
+ // LOGD("Entering sslWrite(), caller requests to write %d bytes...", len);
+
+ if (len == 0) {
+ // Don't bother doing anything in this case.
+ return 0;
+ }
+
+ int fd = SSL_get_fd(ssl);
+ BIO *bio = SSL_get_wbio(ssl);
+
+ APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
+
+ int count = len;
+
+ while(data->aliveAndKicking && len > 0) {
+ errno = 0;
+ if (MUTEX_LOCK(data->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);
+ int result = SSL_write(ssl, buf, len);
+ int error = SSL_get_error(ssl, result);
+ freeSslErrorState();
+ // LOGD("Returned from SSL_write() with result %d, error code %d", result, error);
+
+ // 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 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++;
+ }
+
+ MUTEX_UNLOCK(data->mutex);
+
+ switch (error) {
+ // Sucessfully write at least one byte.
+ case SSL_ERROR_NONE: {
+ buf += result;
+ len -= result;
+ break;
+ }
+
+ // Wrote zero bytes. End of stream reached.
+ case SSL_ERROR_ZERO_RETURN: {
+ return -1;
+ }
+
+ // Need to wait for availability of underlying layer, then retry.
+ // The concept of a write timeout doesn't really make sense, and
+ // 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);
+ if (selectResult == -1) {
+ *sslReturnCode = -1;
+ *sslErrorCode = error;
+ return THROW_EXCEPTION;
+ } else if (selectResult == 0) {
+ return THROW_SOCKETTIMEOUTEXCEPTION;
+ }
+
+ break;
+ }
+
+ // An problem occured during a system call, but this is not
+ // necessarily an error.
+ case SSL_ERROR_SYSCALL: {
+ // Connection closed without proper shutdown. Tell caller we
+ // have reached end-of-stream.
+ if (result == 0) {
+ return -1;
+ }
+
+ // System call has been interrupted. Simply retry.
+ if (errno == EINTR) {
+ break;
+ }
+
+ // Note that for all other system call errors we fall through
+ // to the default case, which results in an Exception.
+ }
+
+ // Everything else is basically an error.
+ default: {
+ *sslReturnCode = result;
+ *sslErrorCode = error;
+ return THROW_EXCEPTION;
+ }
+ }
+ }
+ add_send_stats(fd, count);
+ // LOGD("Successfully wrote %d bytes", count);
+
+ return count;
+}
+
+/**
+ * Helper function that creates an RSA public key from two buffers containing
+ * the big-endian bit representation of the modulus and the public exponent.
+ *
+ * @param mod The data of the modulus
+ * @param modLen The length of the modulus data
+ * @param exp The data of the exponent
+ * @param expLen The length of the exponent data
+ *
+ * @return A pointer to the new RSA structure, or NULL on error
+ */
+static RSA* rsaCreateKey(unsigned char* mod, int modLen, unsigned char* exp, int expLen) {
+ // LOGD("Entering rsaCreateKey()");
+
+ RSA* rsa = RSA_new();
+
+ rsa->n = BN_bin2bn((unsigned char*) mod, modLen, NULL);
+ rsa->e = BN_bin2bn((unsigned char*) exp, expLen, NULL);
+
+ if (rsa->n == NULL || rsa->e == NULL) {
+ RSA_free(rsa);
+ return NULL;
+ }
+
+ return rsa;
+}
+
+/**
+ * Helper function that frees an RSA key. Just calls the corresponding OpenSSL
+ * function.
+ *
+ * @param rsa The pointer to the new RSA structure to free.
+ */
+static void rsaFreeKey(RSA* rsa) {
+ // LOGD("Entering rsaFreeKey()");
+
+ if (rsa != NULL) {
+ RSA_free(rsa);
+ }
+}
+
+/**
+ * Helper function that verifies a given RSA signature for a given message.
+ *
+ * @param msg The message to verify
+ * @param msgLen The length of the message
+ * @param sig The signature to verify
+ * @param sigLen The length of the signature
+ * @param algorithm The name of the hash/sign algorithm to use, e.g. "RSA-SHA1"
+ * @param rsa The RSA public key to use
+ *
+ * @return 1 on success, 0 on failure, -1 on error (check SSL errors then)
+ *
+ */
+static int rsaVerify(unsigned char* msg, unsigned int msgLen, unsigned char* sig,
+ unsigned int sigLen, char* algorithm, RSA* rsa) {
+
+ // LOGD("Entering rsaVerify(%x, %d, %x, %d, %s, %x)", msg, msgLen, sig, sigLen, algorithm, rsa);
+
+ int result = -1;
+
+ EVP_PKEY* key = EVP_PKEY_new();
+ EVP_PKEY_set1_RSA(key, rsa);
+
+ const EVP_MD *type = EVP_get_digestbyname(algorithm);
+ if (type == NULL) {
+ goto cleanup;
+ }
+
+ EVP_MD_CTX ctx;
+
+ EVP_MD_CTX_init(&ctx);
+ if (EVP_VerifyInit_ex(&ctx, type, NULL) == 0) {
+ goto cleanup;
+ }
+
+ EVP_VerifyUpdate(&ctx, msg, msgLen);
+ result = EVP_VerifyFinal(&ctx, sig, sigLen, key);
+ EVP_MD_CTX_cleanup(&ctx);
+
+ cleanup:
+
+ if (key != NULL) {
+ EVP_PKEY_free(key);
+ }
+
+ return result;
+}
+
+// ============================================================================
+// === OpenSSL-related helper stuff ends here. JNI glue follows. ==============
+// ============================================================================
+
+/**
+ * Initialization phase for every OpenSSL job: Loads the Error strings, the
+ * crypto algorithms and reset the OpenSSL library
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_initstatic(JNIEnv* env, jobject obj)
+{
+ SSL_load_error_strings();
+ ERR_load_crypto_strings();
+ SSL_library_init();
+ OpenSSL_add_all_algorithms();
+ THREAD_setup();
+}
+
+/**
+ * Initialization phase for a socket with OpenSSL. The server's private key
+ * and X509 certificate are read and the Linux /dev/urandom file is loaded
+ * as RNG for the session keys.
+ *
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_init(JNIEnv* env, jobject object,
+ jstring privatekey, jstring certificates, jbyteArray seed)
+{
+ SSL_CTX* ssl_ctx;
+
+ // 'seed == null' when no SecureRandom Object is set
+ // in the SSLContext.
+ if (seed != NULL) {
+ jboolean iscopy = JNI_FALSE;
+ jbyte* randseed = env->GetByteArrayElements(seed, &iscopy);
+ RAND_seed((unsigned char*) randseed, 1024);
+ } else {
+ RAND_load_file("/dev/urandom", 1024);
+ }
+
+ ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+
+ // Note: We explicitly do not allow SSLv2 to be used. It
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
+
+ /* Java code in class OpenSSLSocketImpl does the verification. Meaning of
+ * SSL_VERIFY_NONE flag in client mode: if not using an anonymous cipher
+ * (by default disabled), the server will send a certificate which will
+ * be checked. The result of the certificate verification process can be
+ * checked after the TLS/SSL handshake using the SSL_get_verify_result(3)
+ * function. The handshake will be continued regardless of the
+ * verification result.
+ */
+ SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
+
+ if (privatekey != NULL) {
+ BIO* privatekeybio = stringToMemBuf(env, (jstring) privatekey);
+ EVP_PKEY* privatekeyevp =
+ PEM_read_bio_PrivateKey(privatekeybio, NULL, 0, NULL);
+ BIO_free(privatekeybio);
+
+ if (privatekeyevp == NULL) {
+ throwIOExceptionWithSslErrors(env, 0, 0,
+ "Error parsing the private key");
+ SSL_CTX_free(ssl_ctx);
+ return;
+ }
+
+ BIO* certificatesbio = stringToMemBuf(env, (jstring) certificates);
+ X509* certificatesx509 =
+ PEM_read_bio_X509(certificatesbio, NULL, 0, NULL);
+ BIO_free(certificatesbio);
+
+ if (certificatesx509 == NULL) {
+ throwIOExceptionWithSslErrors(env, 0, 0,
+ "Error parsing the certificates");
+ EVP_PKEY_free(privatekeyevp);
+ SSL_CTX_free(ssl_ctx);
+ return;
+ }
+
+ int ret = SSL_CTX_use_certificate(ssl_ctx, certificatesx509);
+ if (ret != 1) {
+ throwIOExceptionWithSslErrors(env, ret, 0,
+ "Error setting the certificates");
+ X509_free(certificatesx509);
+ EVP_PKEY_free(privatekeyevp);
+ SSL_CTX_free(ssl_ctx);
+ return;
+ }
+
+ ret = SSL_CTX_use_PrivateKey(ssl_ctx, privatekeyevp);
+ if (ret != 1) {
+ throwIOExceptionWithSslErrors(env, ret, 0,
+ "Error setting the private key");
+ X509_free(certificatesx509);
+ EVP_PKEY_free(privatekeyevp);
+ SSL_CTX_free(ssl_ctx);
+ return;
+ }
+
+ ret = SSL_CTX_check_private_key(ssl_ctx);
+ if (ret != 1) {
+ throwIOExceptionWithSslErrors(env, ret, 0,
+ "Error checking the private key");
+ X509_free(certificatesx509);
+ EVP_PKEY_free(privatekeyevp);
+ SSL_CTX_free(ssl_ctx);
+ return;
+ }
+ }
+
+ env->SetIntField(object, field_ssl_ctx, (int)ssl_ctx);
+}
+
+/**
+ * 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, jobject object,
+ jint ctx, jobject socketObject, jboolean client_mode, jint session)
+{
+ // LOGD("ENTER connect");
+ int ret, fd;
+ SSL_CTX* ssl_ctx;
+ SSL* ssl;
+ SSL_SESSION* ssl_session;
+
+ ssl_ctx = (SSL_CTX*)env->GetIntField(object, field_ssl_ctx);
+
+ ssl = create_ssl(env, object, ssl_ctx);
+ if (ssl == NULL) {
+ throwIOExceptionWithSslErrors(env, 0, 0,
+ "Unable to create SSL structure");
+ free_ssl_ctx(env, object);
+ return (jboolean) false;
+ }
+
+ jobject socketImplObject = env->GetObjectField(socketObject, field_mImpl);
+ if (socketImplObject == NULL) {
+ free_ssl(env, object);
+ free_ssl_ctx(env, object);
+ throwIOExceptionStr(env,
+ "couldn't get the socket impl from the socket");
+ return (jboolean) false;
+ }
+
+ jobject fdObject = env->GetObjectField(socketImplObject, field_mFD);
+ if (fdObject == NULL) {
+ free_ssl(env, object);
+ free_ssl_ctx(env, object);
+ throwIOExceptionStr(env,
+ "couldn't get the file descriptor from the socket impl");
+ return (jboolean) false;
+ }
+
+ fd = jniGetFDFromFileDescriptor(env, fdObject);
+
+ /*
+ * Turn on "partial write" mode. This means that SSL_write() will
+ * behave like Posix write() and possibly return after only
+ * writing a partial buffer. Note: The alternative, perhaps
+ * surprisingly, is not that SSL_write() always does full writes
+ * but that it will force you to retry write calls having
+ * preserved the full state of the original call. (This is icky
+ * and undesirable.)
+ */
+ SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
+
+ ssl_session = (SSL_SESSION *) session;
+
+ ret = SSL_set_fd(ssl, fd);
+
+ if (ret != 1) {
+ throwIOExceptionWithSslErrors(env, ret, 0,
+ "Error setting the file descriptor");
+ free_ssl(env, object);
+ free_ssl_ctx(env, object);
+ return (jboolean) false;
+ }
+
+ if (ssl_session != NULL) {
+ 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");
+ free_ssl(env, object);
+ free_ssl_ctx(env, object);
+ 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");
+ free_ssl(env, object);
+ free_ssl_ctx(env, object);
+ return (jboolean) false;
+ }
+
+ /*
+ * Create our special application data.
+ */
+ if (sslCreateAppData(ssl) == -1) {
+ throwIOExceptionStr(env, "Unable to create application data");
+ free_ssl(env, object);
+ free_ssl_ctx(env, object);
+ // TODO
+ return (jboolean) false;
+ }
+
+ APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
+ env->SetIntField(object, field_ssl, (int)ssl);
+
+ int timeout = (int)env->GetIntField(object, field_timeout);
+
+ 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");
+ free_ssl(env, object);
+ free_ssl_ctx(env, object);
+ return (jboolean) false;
+ } else if (selectResult == 0) {
+ throwSocketTimeoutException(env, "SSL handshake timed out");
+ freeSslErrorState();
+ free_ssl(env, object);
+ free_ssl_ctx(env, object);
+ 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");
+ free_ssl(env, object);
+ free_ssl_ctx(env, object);
+ return (jboolean) false;
+ }
+ }
+
+ if (ssl_session != NULL) {
+ ret = SSL_session_reused(ssl);
+ // if (ret == 1) LOGD("A session was reused");
+ // else LOGD("A new session was negotiated");
+ return (jboolean) ret;
+ } else {
+ // LOGD("A new session was negotiated");
+ return (jboolean) 0;
+ }
+ // LOGD("LEAVE connect");
+}
+
+static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession(JNIEnv* env, jobject object,
+ jint jssl)
+{
+ return (jint) SSL_get1_session((SSL *) jssl);
+}
+
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept(JNIEnv* env, jobject object,
+ jobject socketObject, jint jssl_ctx, jboolean client_mode)
+{
+ int sd, ret;
+ BIO *bio;
+ SSL *ssl;
+ SSL_CTX *ssl_ctx;
+ mydata_t mydata;
+
+ char name[] = "mydata index";
+
+ ssl_ctx = (SSL_CTX *)jssl_ctx;
+
+ ssl = create_ssl(env, object, ssl_ctx);
+ if (ssl == NULL) {
+ throwIOExceptionWithSslErrors(env, 0, 0,
+ "Unable to create SSL structure");
+ return;
+ }
+
+ jobject socketImplObject = env->GetObjectField(socketObject, field_mImpl);
+ if (socketImplObject == NULL) {
+ free_ssl(env, object);
+ throwIOExceptionStr(env, "couldn't get the socket impl from the socket");
+ return;
+ }
+
+ jobject fdObject = env->GetObjectField(socketImplObject, field_mFD);
+ if (fdObject == NULL) {
+ free_ssl(env, object);
+ throwIOExceptionStr(env, "couldn't get the file descriptor from the socket impl");
+ return;
+ }
+
+
+ sd = jniGetFDFromFileDescriptor(env, fdObject);
+
+ bio = BIO_new_socket(sd, BIO_NOCLOSE);
+
+ /* The parameter client_mode must be 1 */
+ if (client_mode != 0)
+ client_mode = 1;
+ BIO_set_ssl_mode(bio, client_mode);
+
+ SSL_set_bio(ssl, bio, bio);
+
+ /* Call to "register" some new application specific data. It takes three
+ * optional function pointers which are called when the parent structure
+ * (in this case an RSA structure) is initially created, when it is copied
+ * and when it is freed up. If any or all of these function pointer
+ * arguments are not used they should be set to NULL. Here we simply
+ * register a dummy callback application with the index 0.
+ */
+ verify_callback_mydata_index = SSL_get_ex_new_index(0, name, NULL, NULL, NULL);
+
+ /* Fill in the mydata structure */
+ mydata.env = env;
+ mydata.object = object;
+ SSL_set_ex_data(ssl, verify_callback_mydata_index, &mydata);
+
+ ret = SSL_accept(ssl);
+
+ 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,
+ "Trouble accepting connection");
+ free_ssl(env, object);
+ }
+ }
+
+ /*
+ * 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");
+ free_ssl(env, object);
+ return;
+ }
+
+ /*
+ * Create our special application data.
+ */
+ if (sslCreateAppData(ssl) == -1) {
+ throwIOExceptionStr(env, "Unable to create application data");
+ free_ssl(env, object);
+ return;
+ }
+}
+
+/**
+ * Loads the desired protocol for the OpenSSL client and enables it.
+ * For example SSL_OP_NO_TLSv1 means do not use TLS v. 1.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_setenabledprotocols(JNIEnv* env, jobject object,
+ jlong protocol)
+{
+ if (protocol != 0x00000000L) {
+ if (protocol & SSL_OP_NO_SSLv3)
+ LOGD("SSL_OP_NO_SSLv3 is set");
+ if (protocol & SSL_OP_NO_TLSv1)
+ LOGD("SSL_OP_NO_TLSv1 is set");
+
+ SSL_CTX* ctx = (SSL_CTX*)env->GetIntField(object, field_ssl_ctx);
+ int options = SSL_CTX_get_options(ctx);
+ options |= protocol; // Note: SSLv2 disabled earlier.
+ SSL_CTX_set_options(ctx, options);
+ }
+}
+
+/**
+ * Loads the ciphers suites that are supported by the OpenSSL client
+ * and returns them in a string array.
+ */
+static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsupportedciphersuites(JNIEnv* env,
+ jobject object)
+{
+ SSL_CTX* ssl_ctx;
+ SSL* ssl;
+ jobjectArray ret;
+ int i;
+ const char *c;
+
+ ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+
+ if (ssl_ctx == NULL) {
+ return NULL;
+ }
+
+ ssl = SSL_new(ssl_ctx);
+
+ if (ssl == NULL) {
+ SSL_CTX_free(ssl_ctx);
+ return NULL;
+ }
+
+ i = 0;
+ while (SSL_get_cipher_list(ssl,i) != NULL) {
+ i++;
+ }
+
+ ret = (jobjectArray)env->NewObjectArray(i,
+ env->FindClass("java/lang/String"),
+ env->NewStringUTF(""));
+
+ for (i=0; ; i++) {
+ c=SSL_get_cipher_list(ssl,i);
+ if (c == NULL) break;
+
+ env->SetObjectArrayElement(ret,i,env->NewStringUTF(c));
+ }
+
+ SSL_free(ssl);
+ SSL_CTX_free(ssl_ctx);
+
+ return ret;
+}
+
+/**
+ * Loads the ciphers suites that are enabled in the OpenSSL client
+ * and returns them in a string array.
+ */
+static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getenabledciphersuites(JNIEnv* env,
+ jobject object)
+{
+ SSL_CTX* ssl_ctx;
+ SSL* ssl;
+ jobjectArray ret;
+ int i;
+ const char *c;
+
+ ssl = getSslPointer(env, object, false);
+ if (ssl == NULL) {
+ ssl_ctx = (SSL_CTX*)env->GetIntField(object, field_ssl_ctx);
+ ssl = SSL_new(ssl_ctx);
+ env->SetIntField(object, field_ssl, (int)ssl);
+ }
+
+ i = 0;
+ while (SSL_get_cipher_list(ssl,i) != NULL) {
+ i++;
+ }
+
+ ret = (jobjectArray)env->NewObjectArray(i,
+ env->FindClass("java/lang/String"),
+ env->NewStringUTF(""));
+
+ for (i = 0; ; i++) {
+ c = SSL_get_cipher_list(ssl,i);
+ if (c == NULL) break;
+
+ env->SetObjectArrayElement(ret,i,env->NewStringUTF(c));
+ }
+
+ return ret;
+}
+
+/**
+ * Sets the ciphers suites that are enabled in the OpenSSL client.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_setenabledciphersuites(JNIEnv* env, jobject object,
+ jstring controlstring)
+{
+ SSL_CTX* ctx;
+ const char *str;
+ int ret;
+
+ ctx = (SSL_CTX*)env->GetIntField(object, field_ssl_ctx);
+ str = env->GetStringUTFChars(controlstring, 0);
+ ret = SSL_CTX_set_cipher_list(ctx, str);
+
+ if (ret == 0) {
+ freeSslErrorState();
+ jclass exClass = env->FindClass("java/lang/IllegalArgumentException");
+ env->ThrowNew(exClass, "Illegal cipher suite strings.");
+ }
+}
+
+#define SSL_AUTH_MASK 0x00007F00L
+#define SSL_aRSA 0x00000100L /* Authenticate with RSA */
+#define SSL_aDSS 0x00000200L /* Authenticate with DSS */
+#define SSL_DSS SSL_aDSS
+#define SSL_aFZA 0x00000400L
+#define SSL_aNULL 0x00000800L /* no Authenticate, ADH */
+#define SSL_aDH 0x00001000L /* no Authenticate, ADH */
+#define SSL_aKRB5 0x00002000L /* Authenticate with KRB5 */
+#define SSL_aECDSA 0x00004000L /* Authenticate with ECDSA */
+
+/**
+ * Sets the client's crypto algorithms and authentication methods.
+ */
+static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod(JNIEnv* env,
+ jobject object)
+{
+ SSL* ssl;
+ SSL_CIPHER *cipher;
+ jstring ret;
+ char buf[512];
+ unsigned long alg;
+ const char *au;
+
+ ssl = getSslPointer(env, object, true);
+ if (ssl == NULL) {
+ return NULL;
+ }
+
+ cipher = SSL_get_current_cipher(ssl);
+
+ alg = cipher->algorithms;
+
+ switch (alg&SSL_AUTH_MASK) {
+ case SSL_aRSA:
+ au="RSA";
+ break;
+ case SSL_aDSS:
+ au="DSS";
+ break;
+ case SSL_aDH:
+ au="DH";
+ break;
+ case SSL_aFZA:
+ au = "FZA";
+ break;
+ case SSL_aNULL:
+ au="None";
+ break;
+ case SSL_aECDSA:
+ au="ECDSA";
+ break;
+ default:
+ au="unknown";
+ break;
+ }
+
+ ret = env->NewStringUTF(au);
+
+ return ret;
+}
+
+/**
+ * OpenSSL read function (1): only one chunk is read (returned as jint).
+ */
+static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read(JNIEnv* env, jobject object, jint timeout)
+{
+ SSL *ssl = getSslPointer(env, object, true);
+ if (ssl == NULL) {
+ return 0;
+ }
+
+ unsigned char byteRead;
+ int returnCode = 0;
+ int errorCode = 0;
+
+ int ret = sslRead(ssl, (char *) &byteRead, 1, &returnCode, &errorCode, timeout);
+
+ switch (ret) {
+ case THROW_EXCEPTION:
+ // See sslRead() regarding improper failure to handle normal cases.
+ throwIOExceptionWithSslErrors(env, returnCode, errorCode,
+ "Read error");
+ return -1;
+ case THROW_SOCKETTIMEOUTEXCEPTION:
+ throwSocketTimeoutException(env, "Read timed out");
+ return -1;
+ case -1:
+ // Propagate EOF upwards.
+ return -1;
+ default:
+ // Return the actual char read, make sure it stays 8 bits wide.
+ return ((jint) byteRead) & 0xFF;
+ }
+}
+
+/**
+ * OpenSSL read function (2): read into buffer at offset n chunks.
+ * Returns 1 (success) or value <= 0 (failure).
+ */
+static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba(JNIEnv* env, jobject obj, jbyteArray dest, jint offset, jint len, jint timeout)
+{
+ SSL *ssl = getSslPointer(env, obj, true);
+ if (ssl == NULL) {
+ return 0;
+ }
+
+ jbyte* bytes = env->GetByteArrayElements(dest, NULL);
+ int returnCode = 0;
+ int errorCode = 0;
+
+ int ret =
+ sslRead(ssl, (char*) (bytes + offset), len, &returnCode, &errorCode, timeout);
+
+ env->ReleaseByteArrayElements(dest, bytes, 0);
+
+ if (ret == THROW_EXCEPTION) {
+ // See sslRead() regarding improper failure to handle normal cases.
+ throwIOExceptionWithSslErrors(env, returnCode, errorCode,
+ "Read error");
+ return -1;
+ } else if(ret == THROW_SOCKETTIMEOUTEXCEPTION) {
+ throwSocketTimeoutException(env, "Read timed out");
+ return -1;
+ }
+
+ return ret;
+}
+
+/**
+ * OpenSSL write function (1): only one chunk is written.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write(JNIEnv* env, jobject object, jint b)
+{
+ SSL *ssl = getSslPointer(env, object, true);
+ if (ssl == NULL) {
+ return;
+ }
+
+ int returnCode = 0;
+ int errorCode = 0;
+ char buf[1] = { (char) b };
+ int ret = sslWrite(ssl, buf, 1, &returnCode, &errorCode);
+
+ if (ret == THROW_EXCEPTION) {
+ // See sslWrite() regarding improper failure to handle normal cases.
+ throwIOExceptionWithSslErrors(env, returnCode, errorCode,
+ "Write error");
+ } else if(ret == THROW_SOCKETTIMEOUTEXCEPTION) {
+ throwSocketTimeoutException(env, "Write timed out");
+ }
+}
+
+/**
+ * OpenSSL write function (2): write into buffer at offset n chunks.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba(JNIEnv* env, jobject obj,
+ jbyteArray dest, jint offset, jint len)
+{
+ SSL *ssl = getSslPointer(env, obj, true);
+ if (ssl == NULL) {
+ return;
+ }
+
+ jbyte* bytes = env->GetByteArrayElements(dest, NULL);
+ int returnCode = 0;
+ int errorCode = 0;
+ int timeout = (int)env->GetIntField(obj, field_timeout);
+ int ret = sslWrite(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,
+ "Write error");
+ } else if(ret == THROW_SOCKETTIMEOUTEXCEPTION) {
+ throwSocketTimeoutException(env, "Write timed out");
+ }
+}
+
+/**
+ * Interrupt any pending IO before closing the socket.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt(
+ JNIEnv* env, jobject object) {
+ SSL *ssl = getSslPointer(env, object, false);
+ if (ssl == NULL) {
+ return;
+ }
+
+ /*
+ * 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;
+
+ // At most two threads can be waiting.
+ sslNotify(data);
+ sslNotify(data);
+ }
+}
+
+/**
+ * OpenSSL close SSL socket function.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close(
+ JNIEnv* env, jobject object) {
+ SSL *ssl = getSslPointer(env, object, false);
+ if (ssl == NULL) {
+ return;
+ }
+
+ /*
+ * Try to make socket blocking again. OpenSSL literature recommends this.
+ */
+ int fd = SSL_get_fd(ssl);
+ 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");
+// LOGW("Unable to make socket blocking again");
+ }
+ }
+
+ int ret = SSL_shutdown(ssl);
+ switch (ret) {
+ case 0:
+ /*
+ * Shutdown was not successful (yet), but there also
+ * is no error. Since we can't know whether the remote
+ * server is actually still there, and we don't want to
+ * get stuck forever in a second SSL_shutdown() call, we
+ * simply return. This is not security a problem as long
+ * as we close the underlying socket, which we actually
+ * do, because that's where we are just coming from.
+ */
+ break;
+ case 1:
+ /*
+ * Shutdown was sucessful. We can safely return. Hooray!
+ */
+ break;
+ default:
+ /*
+ * Everything else is a real error condition. We should
+ * let the Java layer know about this by throwing an
+ * exception.
+ */
+ throwIOExceptionWithSslErrors(env, ret, 0, "SSL shutdown failed.");
+ break;
+ }
+
+ freeSslErrorState();
+ free_ssl(env, object);
+ free_ssl_ctx(env, object);
+}
+
+/**
+ * OpenSSL free SSL socket function.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_free(JNIEnv* env, jobject object)
+{
+ free_ssl(env, object);
+ free_ssl_ctx(env, object);
+}
+
+/**
+ * Verifies an RSA signature.
+ */
+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()");
+
+ if (msg == NULL || sig == NULL || algorithm == NULL || mod == NULL || exp == NULL) {
+ throwNullPointerException(env);
+ return -1;
+ }
+
+ int result = -1;
+
+ jbyte* msgBytes = env->GetByteArrayElements(msg, NULL);
+ jint msgLength = env->GetArrayLength(msg);
+
+ jbyte* sigBytes = env->GetByteArrayElements(sig, NULL);
+ jint sigLength = env->GetArrayLength(sig);
+
+ jbyte* modBytes = env->GetByteArrayElements(mod, NULL);
+ jint modLength = env->GetArrayLength(mod);
+
+ jbyte* expBytes = env->GetByteArrayElements(exp, NULL);
+ jint expLength = env->GetArrayLength(exp);
+
+ const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL);
+
+ RSA* rsa = rsaCreateKey((unsigned char*) modBytes, modLength, (unsigned char*) expBytes, expLength);
+ if (rsa != NULL) {
+ result = rsaVerify((unsigned char*) msgBytes, msgLength, (unsigned char*) sigBytes, sigLength,
+ (char*) algorithmChars, rsa);
+ rsaFreeKey(rsa);
+ }
+
+ env->ReleaseStringUTFChars(algorithm, algorithmChars);
+
+ env->ReleaseByteArrayElements(exp, expBytes, JNI_ABORT);
+ env->ReleaseByteArrayElements(mod, modBytes, JNI_ABORT);
+ env->ReleaseByteArrayElements(sig, sigBytes, JNI_ABORT);
+ env->ReleaseByteArrayElements(msg, msgBytes, JNI_ABORT);
+
+ if (result == -1) {
+ int error = ERR_get_error();
+ if (error != 0) {
+ char message[50];
+ ERR_error_string_n(error, message, sizeof(message));
+ throwRuntimeException(env, message);
+ } else {
+ throwRuntimeException(env, "Internal error during verification");
+ }
+ freeSslErrorState();
+ }
+
+ return result;
+}
+
+/**
+ * The actual JNI methods' mapping table for the class OpenSSLSocketImpl.
+ */
+static JNINativeMethod sMethods[] =
+{
+ {"nativeinitstatic", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_initstatic},
+ {"nativeinit", "(Ljava/lang/String;Ljava/lang/String;[B)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_init},
+ {"nativeconnect", "(ILjava/net/Socket;ZI)Z", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect},
+ {"nativegetsslsession", "(I)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession},
+ {"nativeread", "(I)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read},
+ {"nativeread", "([BIII)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba},
+ {"nativewrite", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write},
+ {"nativewrite", "([BII)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba},
+ {"nativeaccept", "(Ljava/net/Socket;IZ)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept},
+ {"nativesetenabledprotocols", "(J)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_setenabledprotocols},
+ {"nativegetsupportedciphersuites", "()[Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsupportedciphersuites},
+ {"nativegetenabledciphersuites", "()[Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getenabledciphersuites},
+ {"nativesetenabledciphersuites", "(Ljava/lang/String;)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_setenabledciphersuites},
+ {"nativecipherauthenticationmethod", "()Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod},
+ {"nativeinterrupt", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt},
+ {"nativeclose", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close},
+ {"nativefree", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_free},
+ {"nativeverifysignature", "([B[BLjava/lang/String;[B[B)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_verifysignature},
+};
+
+/**
+ * Register the native methods with JNI for the class OpenSSLSocketImpl.
+ */
+extern "C" int register_org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl(JNIEnv* env)
+{
+ int ret;
+ jclass clazz;
+
+ clazz = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl");
+
+ if (clazz == NULL) {
+ LOGE("Can't find org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl");
+ return -1;
+ }
+
+ jclass socketClass = env->FindClass("java/net/Socket");
+
+ if (socketClass == NULL) {
+ LOGE("Can't find class java.net.Socket");
+ return -1;
+ }
+
+ field_mImpl = env->GetFieldID(socketClass, "impl", "Ljava/net/SocketImpl;");
+
+ if (field_mImpl == NULL) {
+ LOGE("Can't find field impl in class java.net.Socket");
+ return -1;
+ }
+
+ jclass socketImplClass = env->FindClass("java/net/SocketImpl");
+
+ if(socketImplClass == NULL) {
+ LOGE("Can't find class java.net.SocketImpl");
+ return -1;
+ }
+
+ field_mFD = env->GetFieldID(socketImplClass, "fd", "Ljava/io/FileDescriptor;");
+
+ if (field_mFD == NULL) {
+ LOGE("Can't find field fd in java.net.SocketImpl");
+ return -1;
+ }
+
+ jclass fdclazz = env->FindClass("java/io/FileDescriptor");
+
+ if (fdclazz == NULL)
+ {
+ LOGE("Can't find java/io/FileDescriptor");
+ return -1;
+ }
+
+ field_descriptor = env->GetFieldID(fdclazz, "descriptor", "I");
+
+ if (field_descriptor == NULL) {
+ LOGE("Can't find FileDescriptor.descriptor");
+ return -1;
+ }
+
+ ret = jniRegisterNativeMethods(env, "org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl",
+ sMethods, NELEM(sMethods));
+
+ if (ret >= 0) {
+ // Note: do these after the registration of native methods, because
+ // there is a static method "initstatic" that's called when the
+ // OpenSSLSocketImpl class is first loaded, and that required
+ // a native method to be associated with it.
+ field_ssl_ctx = env->GetFieldID(clazz, "ssl_ctx", "I");
+ if (field_ssl_ctx == NULL) {
+ LOGE("Can't find OpenSSLSocketImpl.ssl_ctx");
+ return -1;
+ }
+
+ field_ssl = env->GetFieldID(clazz, "ssl", "I");
+ if (field_ssl == NULL) {
+ LOGE("Can't find OpenSSLSocketImpl.ssl");
+ return -1;
+ }
+
+ field_timeout = env->GetFieldID(clazz, "timeout", "I");
+ if (field_timeout == NULL) {
+ LOGE("Can't find OpenSSLSocketImpl.timeout");
+ return -1;
+ }
+ }
+ return ret;
+}
diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h
new file mode 100644
index 0000000..16ecf9d
--- /dev/null
+++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef org_apache_harmony_xnet_provider_jsse_common_h
+#define org_apache_harmony_xnet_provider_jsse_common_h
+
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+
+#include <stdio.h>
+
+/**
+ * Structure to hold together useful JNI variables.
+ */
+typedef struct {
+ JNIEnv* env;
+ jobject object;
+} mydata_t;
+
+/**
+ * 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 *tmp = env->GetByteArrayElements(bytes, NULL);
+ memcpy(tmp, bptr->data, bptr->length);
+ env->ReleaseByteArrayElements(bytes, tmp, 0);
+ env->SetObjectArrayElement(joa, i, bytes);
+ }
+ }
+
+ // LOGD("Certificate fetching complete");
+ BIO_free(bio);
+ return joa;
+ } else {
+ return NULL;
+ }
+}
+
+extern int verify_callback_mydata_index;
+
+/**
+ * Verify the X509 certificate.
+ */
+static int verify_callback(int preverify_ok, X509_STORE_CTX *x509_store_ctx)
+{
+ SSL *ssl;
+ mydata_t *mydata;
+ jclass cls;
+
+ jobjectArray objectArray;
+
+ /* Get the correct index to the SSLobject stored into X509_STORE_CTX. */
+ ssl = (SSL*)X509_STORE_CTX_get_ex_data(x509_store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+
+ mydata = (mydata_t*)SSL_get_ex_data(ssl, verify_callback_mydata_index);
+
+ cls = mydata->env->GetObjectClass(mydata->object);
+
+ jmethodID methodID = mydata->env->GetMethodID(cls, "verify_callback", "([[B)I");
+
+ objectArray = getcertificatebytes(mydata->env, x509_store_ctx->untrusted);
+
+ mydata->env->CallIntMethod(mydata->object, methodID, objectArray);
+
+ return 1;
+}
+
+#endif
diff --git a/x-net/src/main/native/sub.mk b/x-net/src/main/native/sub.mk
new file mode 100644
index 0000000..4aeb41e
--- /dev/null
+++ b/x-net/src/main/native/sub.mk
@@ -0,0 +1,24 @@
+# This file is included by the top-level libcore Android.mk.
+# It's not a normal makefile, so we don't include CLEAR_VARS
+# or BUILD_*_LIBRARY.
+
+LOCAL_SRC_FILES := \
+ org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp \
+ org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp \
+ org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp \
+ org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp
+
+LOCAL_C_INCLUDES += \
+ external/openssl/include
+
+
+# Any shared/static libs that are listed here must also
+# be listed in libs/nativehelper/Android.mk.
+# TODO: fix this requirement
+
+LOCAL_SHARED_LIBRARIES += \
+ libcrypto \
+ libssl \
+ libutils
+
+LOCAL_STATIC_LIBRARIES +=