/* * 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[0]); } if ((ssl_op_no & SSL_OP_NO_TLSv1) == 0x00000000L) { array.add(supportedProtocols[1]); } 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(); } }