在一个SSL套接字连接中使用两个私钥(密钥库)和两个公钥(信任库) [英] Using two private keys (keystore) and two public keys (truststore) in one SSL Socket Connection

查看:1055
本文介绍了在一个SSL套接字连接中使用两个私钥(密钥库)和两个公钥(信任库)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在一个套接字ssl连接中使用密钥对,而不会在客户端中进行任何更改。

为什么?

因为一个客户端在信任库中使用CN属性进行连接握手,而其他客户端使用同一属性中的另一个值以相同的方式处理相同的任务。

Because one client use a CN attribute in trust store for connection handshake and other clients using another value in the same attribute to process the same task in the same way.

所以我需要使用两个密钥存储(私有)具有不同的CN属性以及别名和共享两个不同的信任存储(公钥)具有不同的CN属性和别名。

So I need to use two key store (private) with distinct CN attributes and also aliases and share two different trust store (public key) with distinct CN attributes and also aliases too.

描述如下:

keyStore1

密钥库类型:JKS

密钥库提供商:SUN

Keystore provider: SUN

别名: identity1

所有者: CN = app1 。 ..

发行人:CN = app1 ...

Issuer: CN=app1 ...

trustStore1

别名: identity1

所有者: CN = app1 ...

发行人:CN = app1 ...

Issuer: CN=app1 ...

keyStore2

别名: identity2

所有者:CN = app2 ...

Owner: CN=app2 ...

发行人:CN = app2 ...

Issuer: CN=app2 ...

trustStore2

别名: identity2

所有者: CN = app2 ...

发行人:CN = app2 ...

Issuer: CN=app2 ...

我试过以这种方式实现此功能:

I tried to implement this feature in this way:

    KeyStore KeyStore1;

                try {
                    String keyStoreFile1 = "privatekey1";
                    String keyStoreType1 = "jks";
                    char[] keyStorePwd1 = "password".toCharArray();

                    keyStore1 = KeyStore.getInstance(keyStoreType1);
                    keyStore1.load(new FileInputStream(keyStoreFile1), keyStorePwd1);
                } catch (java.security.GeneralSecurityException thr) {
                    throw new IOException("Cannot load keystore (" + thr + ")");
                }

                KeyStore trustStore1;

                try {
                    String trustStoreFile1 = "publickey1";
                    String trustStoreType1 = "jks";
                    char[] trustStorePwd1 = "password".toCharArray();

                    trustStore1 = KeyStore.getInstance(trustStoreType1);
                    trustStore.load(new FileInputStream(trustStoreFile1), trustStorePwd1);
                } catch (java.security.GeneralSecurityException thr) {
                    throw new IOException("Cannot load truststore (" + thr + ")");
                }


                KeyStore keyStore2;

                try {
                    String keyStoreFile2 = "privatekey2";
                    String keyStoreType2 = "jks";
                    char[] keyStorePwd2 = "anotherpass".toCharArray();

                    keyStore2 = KeyStore.getInstance(key2StoreType);
                    keyStore2.load(new FileInputStream(keyStoreFile2), keyStorePwd2);
                } catch (java.security.GeneralSecurityException thr) {
                    throw new IOException("Cannot load keystore (" + thr + ")");
                }

                KeyStore trustStore2;

                try {
                    String trustStoreFile2 = "publickey2";
                    String trustStoreType2 = "jks";
                    char[] trustStorePwd2 = "anotherpass".toCharArray();

                    trustStore2 = KeyStore.getInstance(trustStoreType2);
                    trustStore2.load(new FileInputStream(trustStoreFile2), trustStorePwd2);
                } catch (java.security.GeneralSecurityException thr) {
                    throw new IOException("Cannot load truststore (" + thr + ")");
                }



                KeyManagerFactory kmfkey1 = KeyManagerFactory
.getInstance(KeyManagerFactory.getkey1Algorithm());

                kmfkey1.init(keyStore1, "password".toCharArray());

                TrustManagerFactory tmfkey1 =
                        TrustManagerFactory.getInstance(TrustManagerFactory.getkey1Algorithm());
                tmfkey1.init(trustStore1);


                SSLContext ctx = SSLContext.getInstance("SSL");
                ctx.init(kmfkey1.getKeyManagers(), tmfkey1.getTrustManagers(), null);


                KeyManagerFactory kmfkey2 =  KeyManagerFactory.
getInstance(KeyManagerFactory.getkey1Algorithm());

                kmfkey2.init(keyStore2, "password".toCharArray());

                TrustManagerFactory tmfkey2 =  TrustManagerFactory
.getInstance(TrustManagerFactory.getkey1Algorithm());

                tmfkey2.init(trustStore2);


                SSLContext ctxkey2 = SSLContext.getInstance("SSL");
                ctxkey2.init(kmfkey2.getKeyManagers(), tmfkey2.getTrustManagers(), null);

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

                serverSocket = sslSrvSockFact.createServerSocket(port);

...
但我收到此错误消息

... But I received this error message

...

java.security.KeyStoreException:java.security.KeyStore.aliases(KeyStore)中未初始化的密钥库
.java:941)
at com.sun.net.ssl.internal.ssl.SunX509KeyManagerImpl。(SunX509KeyManagerImpl.java:106)
at com.sun.net.ssl.internal.ssl.KeyManagerFactoryImpl $ SunX509.engineInit(KeyManagerFactoryImpl.java:41)
at javax.net.ssl.KeyManagerFactory.init(KeyManagerFactory.java:192)

...

所以我真的想知道是否可以在一个套接字连接中使用密钥对,或者以我看不到的另一种方式解决这个问题或处理。

So I really want to know if is possible to use to key-pairs in one socket connection or solve this in a different way that I can't see or deal with.

编辑1

布鲁诺,你能给我一个完整或完整的例子吗?拜托?

Bruno, could you give me an entire or complete example please?

因为我不清楚....

Because is not clear for me....

我尝试了两件事:

根据先前的建议,一个解决方案是将两个密钥放在私有密钥库中......但是不起作用,我收到了以下消息:

One solutions is put the two keys inside a private keystore following a previous suggestion... but doesn't work and I received the message bellow:

线程main中的异常java.lang.NoSuchMethodError:javax.net.ssl.SSLContext.setDefault(Ljavax / net / ssl / SSLContext;)
at ... initialiseManager (499)

你是对的......我仍然需要选择一个背景或

You was right ... I still need to choose one context or

javax.net.ssl.SSLException:
没有可用的证书或密钥对应于已启用的SSL密码套件。尝试向服务器发送消息时
...

其次我遵循了你的建议......但SSL套接字连接无法启动

Second I followed your suggestion...but SSL Socket connection doesn't start

我在许多其他人的帮助下实现了这一点,在这个网站上显示了他们的
代码...感谢所有

I implemented this with help of many other guys that showed their code here in this site...thanks for all

private KeyManager[] getKeyManagers() throws IOException, GeneralSecurityException {

        // First, get the default KeyManagerFactory.
        String alg = KeyManagerFactory.getDefaultAlgorithm();
        KeyManagerFactory kmFact = KeyManagerFactory.getInstance(alg);

        // Next, set up the KeyStore to use. We need to load the file into
        // a KeyStore instance.

        File keyFile = new File("privatekey1");

        FileInputStream fis = new FileInputStream(keyFile);
        LogManager.log("Loaded keystore privatekey1 " + keyFile.getAbsolutePath(),
LogManager.LOG_LOWEST_LEVEL);
        KeyStore ks = KeyStore.getInstance("jks");
        String keyStorePassword = "password";
        ks.load(fis, keyStorePassword.toCharArray());
        fis.close();
        // Now we initialise the KeyManagerFactory with this KeyStore
        kmFact.init(ks, keyStorePassword.toCharArray());


        KeyManagerFactory dkmFact = KeyManagerFactory.getInstance(alg);

        File keyFileTwo = new File("privatekey2");

        FileInputStream fisTwo = new FileInputStream(keyFileTwo);
        LogManager.log("Loaded keystore privatekey2 " + keyFileTwo.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL);
        KeyStore ksTwo = KeyStore.getInstance("jks");
        String keyStorePasswordTwo = "password";
        ksTwo.load(fisTwo, keyStorePasswordTwo.toCharArray());
        fisTwo.close();
        // Now we initialise the KeyManagerFactory with this KeyStore
        dkmFact.init(ksTwo, keyStorePasswordTwo.toCharArray());


        // default
        //KeyManagerFactory dkmFact = KeyManagerFactory.getInstance(alg);
        //dkmFact.init(null, null);


        // Get the first X509KeyManager in the list
        X509KeyManager customX509KeyManager = getX509KeyManager(alg, kmFact);
        X509KeyManager jvmX509KeyManager = getX509KeyManager(alg, dkmFact);

        KeyManager[] km = {new MultiKeyStoreManager(jvmX509KeyManager, customX509KeyManager)};
        LogManager.log("Number of key managers registered:" + km.length, LogManager.LOG_LOWEST_LEVEL);
        return km;
    }

    /**
     * Find a X509 Key Manager compatible with a particular algorithm
     * @param algorithm
     * @param kmFact
     * @return
     * @throws NoSuchAlgorithmException
     */
    private X509KeyManager getX509KeyManager(String algorithm, KeyManagerFactory kmFact)
            throws NoSuchAlgorithmException {
        KeyManager[] keyManagers = kmFact.getKeyManagers();

        if (keyManagers == null || keyManagers.length == 0) {
            throw new NoSuchAlgorithmException("The default algorithm :" + algorithm + " produced no key managers");
        }

        X509KeyManager x509KeyManager = null;

        for (int i = 0; i < keyManagers.length; i++) {
            if (keyManagers[i] instanceof X509KeyManager) {
                x509KeyManager = (X509KeyManager) keyManagers[i];
                break;
            }
        }

        if (x509KeyManager == null) {
            throw new NoSuchAlgorithmException("The default algorithm :" + algorithm + " did not produce a X509 Key manager");
        }
        return x509KeyManager;
    }

    private void initialiseManager(int iPort) throws IOException, GeneralSecurityException {
        // Next construct and initialise a SSLContext with the KeyStore and
        // the TrustStore. We use the default SecureRandom.

        // load your key store as a stream and initialize a KeyStore
        File trustFile = new File("publicKey");
        LogManager.log("Trust File Loaded " + trustFile.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL);

        InputStream trustStream = new FileInputStream(trustFile);
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());

        // if your store is password protected then declare it (it can be null however)
        char[] trustPassword = "password".toCharArray();

        // load the stream to your store
        trustStore.load(trustStream, trustPassword);

        File trustFileTwo = new File("publicKeyTwo");
        LogManager.log("Trust File Loaded " + trustFileTwo.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL);

        InputStream trustStreamTwo = new FileInputStream(trustFileTwo);
        KeyStore trustStoreTwo = KeyStore.getInstance(KeyStore.getDefaultType());

        // if your store is password protected then declare it (it can be null however)
        char[] trustPasswordTwo = "password".toCharArray();

        // load the stream to your store
        trustStoreTwo.load(trustStreamTwo, trustPasswordTwo);


        // initialize a trust manager factory with the trusted store
        TrustManagerFactory trustFactory =
                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustFactory.init(trustStore);
        trustFactory.init(trustStoreTwo);

        // get the trust managers from the factory
        TrustManager[] managers = trustFactory.getTrustManagers();


        SSLContext context = SSLContext.getInstance("SSL");
        context.init(getKeyManagers(), managers, new SecureRandom());
        SSLContext.setDefault(context);

        SSLServerSocketFactory sslSrvFact = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();

        serverSocket = sslSrvFact.createServerSocket(iPort);
        // this method didn't create a Socket Connection correctly 

    }

    class MultiKeyStoreManager implements X509KeyManager {

        private final X509KeyManager jvmKeyManager;
        private final X509KeyManager customKeyManager;

        public MultiKeyStoreManager(X509KeyManager jvmKeyManager, X509KeyManager customKeyManager) {
            this.jvmKeyManager = jvmKeyManager;
            this.customKeyManager = customKeyManager;
        }

        @Override
        public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
            // try the first key manager
            String alias = customKeyManager.chooseClientAlias(keyType, issuers, socket);
            if (alias == null) {
                alias = jvmKeyManager.chooseClientAlias(keyType, issuers, socket);
                LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL);
            }

            return alias;

        }

        @Override
        public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
            // try the first key manager
            String alias = customKeyManager.chooseServerAlias(keyType, issuers, socket);
            if (alias == null) {
                alias = jvmKeyManager.chooseServerAlias(keyType, issuers, socket);
                LogManager.log("Reverting to JVM Server alias : " + alias ,LogManager.LOG_LOWEST_LEVEL);
            }
            return alias;
        }

        @Override
        public String[] getClientAliases(String keyType, Principal[] issuers) {
            String[] cAliases = customKeyManager.getClientAliases(keyType, issuers);
            String[] jAliases = jvmKeyManager.getClientAliases(keyType, issuers);
            LogManager.log("Supported Client Aliases Custom: " + cAliases.length + " JVM : " + jAliases.length,
                    LogManager.LOG_LOWEST_LEVEL);
            return (String[]) ArrayUtils.addAll(cAliases, jAliases);
        }

        @Override
        public PrivateKey getPrivateKey(String alias) {
            PrivateKey key = customKeyManager.getPrivateKey(alias);
            if (key == null) {
                System.out.println("Reverting to JVM Key : " + alias);
                return jvmKeyManager.getPrivateKey(alias);
            } else {
                return key;
            }
        }

        @Override
        public String[] getServerAliases(String keyType, Principal[] issuers) {
            String[] cAliases = customKeyManager.getServerAliases(keyType, issuers);
            String[] jAliases = jvmKeyManager.getServerAliases(keyType, issuers);
            LogManager.log("Supported Server Aliases Custom: " + cAliases.length + " JVM : " + jAliases.length,
                    LogManager.LOG_LOWEST_LEVEL);
            return (String[]) ArrayUtils.addAll(cAliases, jAliases);
        }

        @Override
        public java.security.cert.X509Certificate[] getCertificateChain(String string) {
            java.security.cert.X509Certificate[] chain = customKeyManager.getCertificateChain("alias_key1");
            if (chain == null || chain.length == 0) {
                LogManager.log("Reverting to JVM Chain : " + string, LogManager.LOG_LOWEST_LEVEL);
                return jvmKeyManager.getCertificateChain("alias_key2");
            } else {
                return chain;
            }
        }
    }

and this gave me this status

* 2012.02.09 18:47:00激活SSL连接

*2012.02.09 18:47:00 Activating an SSL connection

2012.02.09 18:47:00 [... ::运行]

2012.02.09 18:47:00 [... ::run]

2012.02.09 18:47:00信任文件已加载publicKey

2012.02.09 18:47:00 Trust File Loaded publicKey

2012.02.09 18:47 :00 Trust File Loaded publicKeyTwo

2012.02.09 18:47:00 Trust File Loaded publicKeyTwo

2012.02.09 18:47:00 Loaded keystore privateKey privateKey

2012.02.09 18:47:00 Loaded keystore privateKey privateKey

2012.02。 09 18:47:00 Loaded keystore privateKey2 privateKeyTwo

2012.02.09 18:47:00 Loaded keystore privateKey2 privateKeyTwo

2012.02.09 18:47:00已注册的主要经理人数:1 *

2012.02.09 18:47:00 Number of key managers registered:1*

但是当我试图为服务器发送消息时没有发生任何事情......

But nothing happened when I tried to send a message for the server...

很难找到一个在这种情况下真正起作用的例子。

Is being hard to find an example that really works in this case.

编辑2

您好Bruno

你真的有关于这个主题,java和ssh的高级知识,我很感激你的
帮助。所有这些信息都有助于我更好地理解这个问题,同时向我展示方式...很多

Really you have advanced knowledge about this subject, java and ssh and I appreciate your help. All this information is help me understand better this issue while is showing for me the way... Thx a lot

再次你是对的......我正在使用Java 5 JRE上的Java 6代码

And again you are right...I'm using code for Java 6 on a Java 5 JRE

但是再次关注你的食谱我得到了这个代码:

But following again your recipe I got this code :

            // Load the key store: change store type if needed
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        FileInputStream fis = new FileInputStream("keyStore1");
        char[] keyPass = "passw".toCharArray();
        try {
            ks.load(fis, keyPass);
        } finally {
            if (fis != null) {
                fis.close();
            }
        }

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

        final X509KeyManager origKm = (X509KeyManager) kmf.getKeyManagers()[0];
        X509KeyManager km = new X509KeyManager() {

            public String chooseServerAlias(String[] keyType, Principal[] issuers, Socket socket) {
                String alias;

                InetAddress remoteAddress = socket.getInetAddress();

                if (remoteAddress.getHostAddress().equalsIgnoreCase("11.111.111.11")) {
                    alias = "alias1";
                    LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL);
                } else {
                    alias = "alias2";
                    LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL);
                }
                return alias;
            }

            public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
                // try this key manager
                String alias = origKm.chooseClientAlias(keyType, issuers, socket);
                LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL);
                return alias;
            }

            public String[] getClientAliases(String keyType, Principal[] issues) {

                String[] cAliases = origKm.getClientAliases(keyType, issues);
                LogManager.log("Supported Client Aliases : " + cAliases.length, LogManager.LOG_LOWEST_LEVEL);
                return cAliases;

            }

            public String[] getServerAliases(String keyType, Principal[] issues) {

                String[] sAliases = origKm.getServerAliases(keyType, issues);
                LogManager.log("Supported Server Aliases: " + sAliases.length, LogManager.LOG_LOWEST_LEVEL);
                return sAliases;

            }

            public String chooseServerAlias(String keyType, Principal[] issues, Socket socket) {

                // try this key manager
                String alias = origKm.chooseServerAlias(keyType, issues, socket);
                LogManager.log("Reverting to JVM Server alias : " + alias, LogManager.LOG_LOWEST_LEVEL);
                return alias;
            }

            public X509Certificate[] getCertificateChain(String keyType) {

                // here I could specify my other keystore, keystore2 how I could do this?

                // I'm thinking in the righ way when I implemented this code to get the correct private key? 

                X509Certificate[] chain = origKm.getCertificateChain("alias1");
                if (chain == null || chain.length == 0) {
                    LogManager.log("Reverting to JVM Chain : " + keyType, LogManager.LOG_LOWEST_LEVEL);

                    return origKm.getCertificateChain("alias2");
                } else {
                    return chain;
                }
            }

            public PrivateKey getPrivateKey(String alias) {

                PrivateKey key = origKm.getPrivateKey(alias);

                // here I could get my other keystore according the alias, for example 
                // keystore2 how I could do this?

                LogManager.log("Reverting to JVM Key : " + alias, LogManager.LOG_LOWEST_LEVEL);
                return key;
            }
        };

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

        SSLServerSocketFactory sslSrvFact = sslContext.getServerSocketFactory();
        objServerSocket = sslSrvFact.createServerSocket(iPort);

我是否需要实现目标?

编辑3

使用此方法,使用带有alias2的第二个
密钥库,在客户端和服务器之间进行握手客户中的公共信托商店2

Using this approach a got a handshake between client and server using the second keystore with alias2 using public trust store2 in client

...但我在客户端尝试使用信任存储1时仍然出错

...but I still got error when I tried to use trust store1 in client

...从[addr = / 11.111.111.11]收到消息
恢复到JVM服务器别名:alias2
恢复到JVM密钥:alias2
恢复时出错:
javax.net.ssl.SSLHandshakeException:收到致命警报:certificate_unknown

...received a message from [addr=/11.111.111.11] Reverting to JVM Server alias : alias2 Reverting to JVM Key : alias2 Error in retriving: javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown

当客户端使用时,我的代码不会更改服务器以使用私有密钥1和alias1 ... public truststore1 with cert alias1 ...

My code not change the server to use the private key1 with alias1 ... when the client is using the public truststore1 with cert alias1...

还有什么办法可以解决这个问题...我相信已接近最终解决方案...

What more is necessary to do for solve this...I believe is near the final solution...

thx!

编辑4

Bruno,我按照你的建议更改了getCertificateChain方法显示吼叫

Bruno , I changed the getCertificateChain method by following your suggestion showed bellow

public X509Certificate[] getCertificateChain(String alias) {
      X509Certificate[] chain = origKm.getCertificateChain(alias);
      return chain;
}

和方法

public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
                    String alias;

我也删除了重复的方法......

also I removed the duplicated method...

在成瘾中,使用旧信任库的客户端不验证主机名

And in the addiction the clients that use the old trust store don't verify the hostname

private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {

    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
};

但对于需要使用第二个信任存储的客户端进行此验证,这是一个原因,因为我需要处理密钥对证书...

But for the client that need to use the second trust store do this verification and it's a reason because I need to deal with to key-pair certificates...

EDIT5

我想知道如何实现服务器套接字,以便能够根据客户端使用的证书来识别和使用
正确的证书,以便与服务器进行
握手通信。

I'd like to know how I can implement a server socket to let it able to identify and use correct certificate according with the certificate being used by client to proceed handshake communication with the server.

在服务器端解释得更好:

Explaining better, in the server side is:

AppServerSideSocket.jar


  • 私钥库: privateKeyApp (键入JKS,使用keytool生成)

  • public keystore : publicKeyApp (输入JKS,与所有客户共享)

  • private keystore: privateKeyApp (type JKS, generated with keytool)
  • public keystore : publicKeyApp (type JKS, shared with all clients)

在客户端...

AppClientSideSocket.jar
- 公钥存储: publicKeyApp

AppServerSideSocket.jar 监听客户端请求并且一旦收到客户发送的
信息

The AppServerSideSocket.jar listening clients requests and once received process information sent by clients

AppClientSideSocket.jar 使用 publicKeyApp 使用SSL连接服务器,而不使用
验证服务器主机名
,并在握手后发送AppServerSideSocket应用程序的信息。

The AppClientSideSocket.jar connect with the server using SSL using publicKeyApp without verify server hostname and after handshake send information for the AppServerSideSocket application.

现在我有另一个客户端应用程序 AppClientSideSocketNEW.jar ,并且此验证服务器主机名与服务器进行
通信。在这种情况下,客户端公共证书中使用的CN
必须与 AppServerSideSocket.jar 所在的主机名匹配。

Now I've another client application, AppClientSideSocketNEW.jar, and this verify server hostname to make communication with the server. In this case, the CN used in the public certificate on the client side must be match with the hostname where AppServerSideSocket.jar are.

最初连接是在服务器端以这种方式配置的:

Originally the connection was configured in this way on the server side:

if (usingSSLConn) {
    System.setProperty("javax.net.ssl.keyStore", "privateKeyApp");
    System.setProperty("javax.net.ssl.keyStorePassword", "privateKeyAppPassword");

    SSLServerSocketFactory sslServerSocketFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
    ServerSocket serverSocketApp = sslServerSocketFactory.createServerSocket(Port);

} else
    serverSocketApp = new ServerSocket(Port);
}

所有客户端都收到相同的publicKeyApp并连接服务器而没有验证主机名,因此,如果在主机名为
badServer1.com的服务器中安装服务器套接字应用程序( AppServerSideSocket.jar )的服务器为private beyApp,则无关紧要
和publicKeyApp使用goodServer1.com设置,因为所有
客户端都不验证密钥的主机名或CN属性。

All the clients received the same publicKeyApp and connect with the server without verify hostname, so doesn't matter if the server where server socket application (AppServerSideSocket.jar) is installed in the server with hostname is badServer1.com and the CN of key in privateKeyApp and publicKeyApp is setted with goodServer1.com, because all the clients don't verify hostname or the CN attribute of the key.

Bellow显示一块这种连接

Bellow is showed a piece of this kind of connection

 private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {

        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
 };

System.setProperty("javax.net.ssl.trustStore", publicKey1);
        System.getProperties().setProperty("java.protocol.handler.pkgs", "javax.net.ssl.internal.www.protocol"); 
     HttpsURLConnection.setDefaultHostnameVerifier(DO_NOT_VERIFY);
     ...
     SOAPConnectionFactory soapConn = SOAPConnectionFactory.newInstance();
     SOAPConnection connection = soapConn.createConnection();
     ...     
     URL endpoint = new URL(hostname + ":" + port);

但是新客户端( AppClientSideSocketNEW.jar )必须执行此验证,现在有必要为这个客户端提供一个新的证书
,其中CN属性的新值反映了服务器套接字所在的正确主机名CN。

But the new client (AppClientSideSocketNEW.jar) do this verification obligatorily, now is necessary provide a new certificate for this client with new value for CN attribute reflecting the correct hostname CN where the server socket is.

我不知道有权访问第二个客户端,我确信它可以进行主机名验证。

I don't have access to second client and I'm sure that it do hostname verification.

所以我创建了两个新的密钥对证书( privateKeyAppNew和publicKeyAppNew ),显然通信发生了
,服务器之间成功在配置服务器使用这个新密钥对之后,使用这个新的密钥对和新客户端使用这个新的公共publicKeyAppNew密钥。

So I created two new key-pair certificates (privateKeyAppNew and publicKeyAppNew) and apparently the communications happened with success between the server using this new key-pair and new client using this new public publicKeyAppNew key, after configured the server to use this new key-pair of course.

但是我需要继续为旧客户使用旧密钥对。我想知道如何解决这个问题。

But I need continue to use the old key-pair for old clients. I'd like to know how can I deal with this.

使用密钥管理器让我能够在客户端尝试连接时验证服务器应用程序上的客户端证书,并且
选择适当的并使用正确的握手进行握手证书?

Using a keymanager let me able to verify the client certificate on the server app when client try to connect and choose the appropriate and do the handshake using the correct certificate?

或者我需要在不同的端口为不同类型的客户端提供不同的ssl套接字连接?

Or I need distinct ssl socket connection in different ports for which kind of clients?

推荐答案

显而易见的问题是你实际上没有使用你创建的 SSLContext

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

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

这至少应该是:

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

问题是您必须在一个上下文或另一个上下文中进行选择...

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

问题的解决方案是我给你的另一个类似问题的答案几天前:你需要实现自己的 X509KeyManager 可以选择您要使用的密钥。

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.

无论您是想使用单个密钥库还是从两个密钥库加载密钥/证书都无关紧要:如果您真的想要,您当然可以实现 getPrivateKey getCertificateChain ,以便根据别名从两个不同的密钥库加载密钥/证书。但是,这将是不必要的复杂。 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.

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).

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)

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.

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.

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.


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

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.

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.

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.

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:

javax.net.ssl.SSLException:

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

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...

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.

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(Strin g 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(...).

    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(...).

    这篇关于在一个SSL套接字连接中使用两个私钥(密钥库)和两个公钥(信任库)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆