/* * 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.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.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; import javax.net.ssl.HandshakeCompletedEvent; import javax.net.ssl.HandshakeCompletedListener; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.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 final 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<HandshakeCompletedListener> 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 final AtomicInteger instanceCount = new AtomicInteger(0); public static int getInstanceCount() { return instanceCount.get(); } private static void updateInstanceCount(int amount) { instanceCount.addAndGet(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 sslParameters Parameters for the SSL * context * @param ssl_op_no Parameter to set the enabled * protocols * @throws IOException 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 sslParameters Parameters for the SSL * context * @throws IOException if network fails */ protected OpenSSLSocketImpl(SSLParameters sslParameters) throws IOException { super(); this.sslParameters = sslParameters; init(); updateInstanceCount(1); } /** * Class constructor with 3 parameters * * @throws IOException if network fails * @throws java.net.UnknownHostException 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 * * @throws IOException if network fails * @throws java.net.UnknownHostException 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 * * @throws IOException if network fails * @throws java.net.UnknownHostException 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 * * @throws IOException if network fails * @throws java.net.UnknownHostException 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. * * @throws IOException 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 getCachedClientSession() { if (super.getInetAddress() == null || super.getInetAddress().getHostAddress() == null || super.getInetAddress().getHostName() == null) { return null; } ClientSessionContext sessionContext = sslParameters.getClientSessionContext(); return (OpenSSLSessionImpl) sessionContext.getSession( super.getInetAddress().getHostName(), super.getPort()); } /** * Ensures that logger is lazily loaded. The outer class seems to load * before logging is ready. */ static class LoggerHolder { static final Logger logger = Logger.getLogger( OpenSSLSocketImpl.class.getName()); } /** * 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; } } OpenSSLSessionImpl session = getCachedClientSession(); // Check if it's allowed to create a new session (default is true) if (session == null && !sslParameters.getEnableSessionCreation()) { throw new SSLHandshakeException("SSL Session may not be created"); } else { Socket socket = this.socket != null ? this.socket : this; int sessionId = session != null ? session.session : 0; boolean reusedSession; synchronized (OpenSSLSocketImpl.class) { reusedSession = nativeconnect(ssl_ctx, socket, sslParameters.getUseClientMode(), sessionId); } if (reusedSession) { // nativeconnect shouldn't return true if the session is not // done session.lastAccessedTime = System.currentTimeMillis(); sslSession = session; LoggerHolder.logger.fine("Reused cached session for " + getInetAddress().getHostName() + "."); } else { if (session != null) { LoggerHolder.logger.fine("Reuse of cached session for " + getInetAddress().getHostName() + " failed."); } else { LoggerHolder.logger.fine("Created new session for " + getInetAddress().getHostName() + "."); } ClientSessionContext sessionContext = sslParameters.getClientSessionContext(); synchronized (OpenSSLSocketImpl.class) { sessionId = nativegetsslsession(ssl); } if (address == null) { sslSession = new OpenSSLSessionImpl( sessionId, sslParameters, super.getInetAddress().getHostName(), super.getPort(), sessionContext); } else { sslSession = new OpenSSLSessionImpl( sessionId, sslParameters, address.getHostName(), address.getPort(), sessionContext); } try { X509Certificate[] peerCertificates = (X509Certificate[]) sslSession.getPeerCertificates(); if (peerCertificates == null || peerCertificates.length == 0) { throw new SSLException("Server sends no certificate"); } String authMethod; synchronized (OpenSSLSocketImpl.class) { authMethod = nativecipherauthenticationmethod(); } sslParameters.getTrustManager().checkServerTrusted( peerCertificates, authMethod); sessionContext.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++) { 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); ServerSessionContext sessionContext = sslParameters.getServerSessionContext(); sslSession = new OpenSSLSessionImpl(nativegetsslsession(ssl), sslParameters, super.getInetAddress().getHostName(), super.getPort(), sessionContext); sslSession.lastAccessedTime = System.currentTimeMillis(); sessionContext.putSession(sslSession); } /** * Callback methode for the OpenSSL native certificate verification process. * * @param bytes 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. * @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. * @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 flag 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 suites names of all the cipher suites to * put on use * @throws IllegalArgumentException 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 mode true if the socket starts in client * mode * @throws IllegalArgumentException 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 need 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 want 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 { // TODO: Close SSL sockets using a background thread so they close // gracefully. 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 OpenSSLSocketImpl#close} on behalf of instances being finalized, * since that call can take arbitrarily long (e.g., due to a slow network), * and an overly long-running finalizer will cause the process to be * totally aborted. */ private class Finalizer extends Thread { public void run() { Socket underlyingSocket = socket; // for error reporting try { close(); } catch (IOException ex) { /* * Clear interrupted status, so that the Logger call * immediately below won't get spuriously interrupted. */ Thread.interrupted(); Logger.global.log(Level.SEVERE, "Trouble finalizing SSL socket (" + OpenSSLSocketImpl.this + ", for " + underlyingSocket + ")", ex); } } } /** * 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); }