Using two private keys (keystore) and two public keys (truststore) in one SSL Socket Connection

*爱你&永不变心* 提交于 2019-12-03 22:00:26
Bruno

The "obvious" problem is that you're not actually making any use of the SSLContexts you create:

SSLServerSocketFactory sslSrvSockFact =
    (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();

This should at least be:

SSLServerSocketFactory sslSrvSockFact =
    (SSLServerSocketFactory) ctx.getServerSocketFactory();

The problem is that you would have to choose between one context or the other...

The solution to your problem is in the answer I gave to your other similar question a couple of days ago: you need to implement your own X509KeyManager to be able to choose which key you're going to use.

Whether you want to use a single keystore or load your key/cert from two keystores doesn't matter that much: if you really want to, you can certainly implement getPrivateKey and getCertificateChain so that they load the keys/certs from two distinct keystores depending in the alias. It would be unnecessarily complicated, though. You will still have to do something based on the alias selection anyway, so you might as well load both keys/certificates from a single key store, using different aliases.

From the server point of view, the only way to choose one alias (and therefore key/cert pair) is to use what's available in the socket (or engine if you're using an X509ExtendedKeyManager). Since Java 7 doesn't support Server Name Indication (which would let the client tell which host name it's requesting ahead of this selection process), you may have to do this based on the client IP address, or on which of your server IP addresses is being used (if you have more than one).

Using two private keys (keystore) and two public keys (truststore)

You seem to be confused about what the keystore and the truststore are. Unless you're planning to use client-certificate authentication, you can ignore the trust store settings on the server. You can used the default (null) as the second parameter of your SSLContext.init(...). Your "keystore (keystore)" is the information used by the local party (your server in this case), the "truststore (keystore)" is used to determine which remote party to trust.

The public key (or, to be precised, the certificate) you're going to present to the client is also in your keystore, associated with your private key, not in the truststore.

EDIT:

Exception in thread "main" java.lang.NoSuchMethodError: javax.net.ssl.SSLContext.setDefault(Ljavax/net/ssl/SSLContext;) at ...initialiseManager(499)

NoSuchMethodError:

Thrown if an application tries to call a specified method of a class (either static or instance), and that class no longer has a definition of that method.

Normally, this error is caught by the compiler; this error can only occur at run time if the definition of a class has incompatibly changed.

This has nothing to do with your SSL settings. Not sure what you've done here, but it looks like you may be using code for Java 6 on a Java 5 JRE (Java 6 did not have a setDefault on SSLContext). More importantly, there's something wrong about the general way you seem to be using Java here.

javax.net.ssl.SSLException:

No available certificate or key corresponds to the SSL cipher suites which are enabled.

That could very well be explained by the fact you didn't seem to be using the SSLContexts you had initialised at all...

  • If you have two pairs of private keys/certificates in your keystore.

My answer here still stands. I'll try to make it a bit more explicit. I'm assuming here that one of your cert/private key is using alias1 and the other alias2. Find out using keyool -list if you're not sure. It's up to you to choose and set them up.

// Load the key store: change store type if needed
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream fis = new FileInputStream("/path/to/keystore");
try {
    ks.load(fis, keystorePassword);
} finally {
    if (fis != null) { fis.close(); }
}

// Get the default Key Manager
KeyManagerFactory kmf = KeyManagerFactory.getInstance(
   KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, keyPassword);

final X509KeyManager origKm = (X509KeyManager)kmf.getKeyManagers()[0];
X509KeyManager km = new X509KeyManager() {
    public String chooseServerAlias(String keyType, 
                                    Principal[] issuers, Socket socket) {
        InetAddress remoteAddress = socket.getInetAddress();
        if (/* remoteAddress has some conditions you need to define yourself */ {
            return "alias1";
        } else {
            return "alias2";
        }
    }

    public String chooseClientAlias(String[] keyType, 
                                    Principal[] issuers, Socket socket) {
       // Delegate this other methods to origKm.
        origKm.chooseClientAlias(keyType, issuers, socket);
    }

    // Delegate this other methods to origKm, in the same way as 
    // it was done for chooseClientAlias.
}

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(new KeyManager[] { km }, null, null);

SSLSocketFactory sslSocketFactory = sslContext.getSSLSocketFactory();
  • If you really want two distinct keystores.

Do the same and, on top of this, in getCertificateChain(String alias), choose which of the two keystores to use depending on the alias, and use it to get the certificate chain. Same thing for getPrivateKey(...).

The simples way would be to put everything in one key store - all keys and all trust certificates. That would eliminate your problem.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!