https://issues.apache.org/bugzilla/show_bug.cgi?id=51860

             Bug #: 51860
           Summary: HTTP/SSL with NIO won't work
           Product: Tomcat 7
           Version: 7.0.21
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: normal
          Priority: P2
         Component: Connectors
        AssignedTo: dev@tomcat.apache.org
        ReportedBy: roma...@inbox.ru
    Classification: Unclassified


Error reproduction conditions:
Tomcat 7.0.20 and 21.
Connector="...Http11NioProtocol"
SSLEnabled="true"
secure="true"
scheme="https"
clientAuth=true or false
JDK 1.6.0_27 X64.
All operation systems.

When user connects to https://, the SSL handshake fails with error:
javax.net.ssl.SSLHandshakeException: no cipher suites in common

If we have same connector settings,  but if we change it to BIO:
Connector="...Http11Protocol", everything works fine.

The problem cause is a differences in SSL behavior between BIO and NIO
handshake.
I've found the workaround here:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6448723


See the org.apache.tomcat.util.net.jsse.JSSESocketFactory. JSSEKeyManager wraps
the KeyManager.
In NIO mode, the KeyManager should have two extra methods:

        public java.lang.String chooseEngineClientAlias(java.lang.String[]
keyType, java.security.Principal[] issuers, javax.net.ssl.SSLEngine engine);

        public java.lang.String chooseEngineServerAlias(java.lang.String
keyType, java.security.Principal[] issuers, javax.net.ssl.SSLEngine engine);

We use a custom hand-made SSLInmplemention with extra features:
* Keypair storage on hardware device or database
* ExtendedKeyUsage verification in TrustManager
* CRL validation on CRL distribution point online synchronization
* ActiveDirectory account lookup by certificate

The complete source code of workaround:
package ru.yamoney.calypso.server.security.jsse;

import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.jsse.JSSESocketFactory;
import ru.yamoney.calypso.server.CommonKernel;

import javax.net.ssl.*;
import java.net.Socket;
import java.security.KeyStore;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;

/**
 * SSLImplementation for Tomcat-7-NIO
 *
 * @author Roman Tsirulnikov
 */
final class CalypsoSSLSocketFactory
        extends JSSESocketFactory {
    private final CalypsoKeyManager keyManager;
    private final X509TrustManager trustManager;

    public CalypsoSSLSocketFactory(AbstractEndpoint endpoint) {
        super(endpoint);
        keyManager =
CommonKernel.getInstance().getBean("server_calypsoKeyManager");
        trustManager =
CommonKernel.getInstance().getBean("server_calypsoTrustManager");
    }

    @Override
    public KeyManager[] getKeyManagers() {
        try {
            KeyStore ks = keyManager.getKeyStore();
            if (!ks.isKeyEntry(keyManager.getKeyAlias())) {
                throw new IllegalArgumentException("Keystore entry is not a
private keypair: " + keyManager.getKeyAlias());
            }

            KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(ks, keyManager.getKeystorePass().toCharArray());
            KeyManager[] kms = kmf.getKeyManagers();
            for (int i = 0; i < kms.length; i++) {
                kms[i] = new NIOKeyManagerWrapper((X509KeyManager) kms[i],
keyManager.getKeyAlias());
            }

            return kms;
        } catch (Exception e) {
            throw new IllegalArgumentException("SSLSocketFactory init: " +
e.getMessage(), e);
        }
    }

    @Override
    public TrustManager[] getTrustManagers() {
        return new TrustManager[]{trustManager};
    }

    /**
     * X509KeyManager wrapper
     * Workaround for the SSL-NIO engine bug
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6448723
     */
    private final class NIOKeyManagerWrapper extends X509ExtendedKeyManager {
        private X509KeyManager delegate;
        private String serverKeyAlias;

        /**
         * Constructor.
         *
         * @param mgr            The X509KeyManager used as a delegate
         * @param serverKeyAlias The alias name of the server's keypair and
         *                       supporting certificate chain
         */
        NIOKeyManagerWrapper(X509KeyManager mgr, String serverKeyAlias) {
            super();
            this.delegate = mgr;
            this.serverKeyAlias = serverKeyAlias;
        }

        /**
         * Choose an alias to authenticate the client side of a secure socket,
         * given the public key type and the list of certificate issuer
authorities
         * recognized by the peer (if any).
         *
         * @param keyType The key algorithm type name(s), ordered with the
         *                most-preferred key type first
         * @param issuers The list of acceptable CA issuer subject names, or
null
         *                if it does not matter which issuers are used
         * @param socket  The socket to be used for this connection. This
parameter
         *                can be null, in which case this method will return
the most generic
         *                alias to use
         * @return The alias name for the desired key, or null if there are no
         *         matches
         */
        @Override
        public String chooseClientAlias(String[] keyType, Principal[] issuers,
                                        Socket socket) {
            return delegate.chooseClientAlias(keyType, issuers, socket);
        }

        /**
         * Returns this key manager's server key alias that was provided in the
         * constructor.
         *
         * @param keyType The key algorithm type name (ignored)
         * @param issuers The list of acceptable CA issuer subject names, or
null
         *                if it does not matter which issuers are used
(ignored)
         * @param socket  The socket to be used for this connection. This
parameter
         *                can be null, in which case this method will return
the most generic
         *                alias to use (ignored)
         * @return Alias name for the desired key
         */
        @Override
        public String chooseServerAlias(String keyType, Principal[] issuers,
                                        Socket socket) {
            return serverKeyAlias;
        }

        /**
         * Returns the certificate chain associated with the given alias.
         *
         * @param alias The alias name
         * @return Certificate chain (ordered with the user's certificate first
         *         and the root certificate authority last), or null if the
alias can't be
         *         found
         */
        @Override
        public X509Certificate[] getCertificateChain(String alias) {
            return delegate.getCertificateChain(alias);
        }

        /**
         * Get the matching aliases for authenticating the client side of a
secure
         * socket, given the public key type and the list of certificate issuer
         * authorities recognized by the peer (if any).
         *
         * @param keyType The key algorithm type name
         * @param issuers The list of acceptable CA issuer subject names, or
null
         *                if it does not matter which issuers are used
         * @return Array of the matching alias names, or null if there were no
         *         matches
         */
        @Override
        public String[] getClientAliases(String keyType, Principal[] issuers) {
            return delegate.getClientAliases(keyType, issuers);
        }

        /**
         * Get the matching aliases for authenticating the server side of a
secure
         * socket, given the public key type and the list of certificate issuer
         * authorities recognized by the peer (if any).
         *
         * @param keyType The key algorithm type name
         * @param issuers The list of acceptable CA issuer subject names, or
null
         *                if it does not matter which issuers are used
         * @return Array of the matching alias names, or null if there were no
         *         matches
         */
        @Override
        public String[] getServerAliases(String keyType, Principal[] issuers) {
            return delegate.getServerAliases(keyType, issuers);
        }

        /**
         * Returns the key associated with the given alias.
         *
         * @param alias The alias name
         * @return The requested key, or null if the alias can't be found
         */
        @Override
        public PrivateKey getPrivateKey(String alias) {
            return delegate.getPrivateKey(alias);
        }

        public java.lang.String chooseEngineClientAlias(java.lang.String[]
keyType, java.security.Principal[] issuers, javax.net.ssl.SSLEngine engine) {
            return delegate.chooseClientAlias(keyType, issuers, null);
        }

        public java.lang.String chooseEngineServerAlias(java.lang.String
keyType, java.security.Principal[] issuers, javax.net.ssl.SSLEngine engine) {
            return serverKeyAlias;
        }

    }
}

-- 
Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to