Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/security/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使用MSCAPI提供程序进行客户端SSL身份验证_Java_Security_Authentication_Ssl - Fatal编程技术网

Java 如何使用MSCAPI提供程序进行客户端SSL身份验证

Java 如何使用MSCAPI提供程序进行客户端SSL身份验证,java,security,authentication,ssl,Java,Security,Authentication,Ssl,我正在编写一个程序,需要将HTTPS连接到需要使用SSL客户端身份验证的web服务器 此程序的用户将使用windows环境中的证书进行身份验证 我已经找到了很多示例,展示了如何设置客户机身份验证,如果我首先将证书导出为pkcs12格式,效果会很好,但我不想强迫用户这样做。但是,当我尝试使用MSCAPI时,它总是会爆炸,但有一个例外: javax.net.ssl.SSLHandshakeException: Error signing certificate verify at s

我正在编写一个程序,需要将HTTPS连接到需要使用SSL客户端身份验证的web服务器

此程序的用户将使用windows环境中的证书进行身份验证

我已经找到了很多示例,展示了如何设置客户机身份验证,如果我首先将证书导出为pkcs12格式,效果会很好,但我不想强迫用户这样做。但是,当我尝试使用MSCAPI时,它总是会爆炸,但有一个例外:

javax.net.ssl.SSLHandshakeException: Error signing certificate verify
        at sun.security.ssl.Alerts.getSSLException(Unknown Source)
        at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
        at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
        at sun.security.ssl.ClientHandshaker.serverHelloDone(Unknown Source)
        at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source)
        at sun.security.ssl.Handshaker.processLoop(Unknown Source)
        at sun.security.ssl.Handshaker.process_record(Unknown Source)
        at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
        at sun.security.ssl.SSLSocketImpl.readDataRecord(Unknown Source)
        at sun.security.ssl.AppInputStream.read(Unknown Source)
        at java.io.BufferedInputStream.fill(Unknown Source)
        at java.io.BufferedInputStream.read1(Unknown Source)
        at java.io.BufferedInputStream.read(Unknown Source)
        at sun.net.www.http.HttpClient.parseHTTPHeader(Unknown Source)
        at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
        at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
        at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(Unknown Source)
        at com.example.Win2.main(Win2.java:62)
Caused by: java.security.SignatureException: Bad Key.

        at sun.security.mscapi.RSASignature.signHash(Native Method)
        at sun.security.mscapi.RSASignature.engineSign(RSASignature.java:390)
        at java.security.Signature$Delegate.engineSign(Unknown Source)
        at java.security.Signature.sign(Unknown Source)
        at sun.security.ssl.RSASignature.engineSign(Unknown Source)
        at java.security.Signature$Delegate.engineSign(Unknown Source)
        at java.security.Signature.sign(Unknown Source)
        at sun.security.ssl.HandshakeMessage$CertificateVerify.<init>(Unknown Source)
        ... 16 more
很明显,这里有些关于API的东西我不理解。它如何知道我要使用密钥库中的哪些密钥?我原以为Windows会提示我,就像其他需要验证的应用程序一样,但我怀疑它只是在选择它找到的第一个应用程序

我是否需要实现自己的密钥管理器,以便它可以选择使用哪个密钥

如果我遍历密钥库,我可以看到其中的密钥,并且可以通过调用getKey来提取一个密钥。更复杂的是,存储中有多个具有相同别名(但有效性不同)的密钥。其他(非java)应用程序,例如Chrome,似乎能够确定以某种方式使用哪些键


编辑:我忘了提到,我正在使用Java 1.7。

您可能想尝试使用不同的上下文。为Windows应用商店提供的安全性可能无法识别“TLS”。可能改为“SSL_TLS”?

负责选择密钥库中使用哪个密钥的是KeyManager对象。 您可以通过调用keyManagerFactory.getKeyManagers()获得这些对象的向量

这些库通常会获得它们在存储中找到的第一个密钥项(可能与本例中提供的服务器证书兼容)。MS-CAPI API也不例外

要在密钥库中选择要使用的密钥,应执行3项操作:

  • 实现X509KeyManager接口

  • 使上述接口的方法返回所需的密钥别名

  • 将对象设置为SSLContext

  • 请注意,您的密钥库必须包含从您的个人证书到根颁发机构的所有证书链。您应该使用certmgr.msc程序导入证书和/或检查它们是否都存在于个人(您的证书)、中间证书颁发机构(链中的任何中间CA)和受信任的根证书颁发机构的文件夹中

    设置信任密钥库也很重要—它存储您信任的根证书,此存储将用于验证服务器证书。对于MS-CAPI,您将使用KeyStore.getInstance(“Windows根”)命令获取它(可信根证书颁发机构文件夹中的证书)

    修改代码以实现以下目标:

    URL url = new URL("https://.........");
    
    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    KeyStore keyStore = KeyStore.getInstance("Windows-MY");
    keyStore.load(null, null);
    keyManagerFactory.init(keyStore);
    
    /* You must also set your trust store */
    KeyStore ts = KeyStore.getInstance("Windows-ROOT");
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(ts);
    
    /* Here you can implement a way to set your key alias 
    ** You can run through all key entries and implement a way
    ** to prompt the user to choose one - for simplicity I just set a
    ** name*/
    String alias = "user1_alias";
    
    /* Get your current KeyManager from the factory */
    final X509KeyManager okm = (X509KeyManager)keyManagerFactory.getKeyManagers()[0];
    
    /* Implement the Interface X509KeyManager */
    X509KeyManager km = new X509KeyManager() {
        public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
             /* Implement your own logic to choose the alias
                according to the validity if the case, 
                or use the entry id or any other way, you can get 
                those values outside this class*/
             return alias;
        }
    
        public X509Certificate[] getCertificateChain(String alias) {
             return okm.getCertificateChain(alias);
        }
       /* Implement the other methods of the interface using the okm object */
    };
    SSLContext context = SSLContext.getInstance("TLS");
    /* set the keymanager in the SSLContext */
    context.init(new KeyManager[]{km}, tmf.getTrustManagers(), new SecureRandom());
    SSLSocketFactory socketFactory = context.getSocketFactory();
    
    HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
    conn.setSSLSocketFactory(socketFactory);
    

    有关于这次整合的消息吗?你找到解决办法了吗?@olator不,我没有。到目前为止,需要这样做的用户数量不足以保证投入更多的时间,因此这些用户不得不忍受将证书导出为pkcs12格式。如果可以的话,我真的很想在这方面有所改进!
    URL url = new URL("https://.........");
    
    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    KeyStore keyStore = KeyStore.getInstance("Windows-MY");
    keyStore.load(null, null);
    keyManagerFactory.init(keyStore);
    
    /* You must also set your trust store */
    KeyStore ts = KeyStore.getInstance("Windows-ROOT");
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(ts);
    
    /* Here you can implement a way to set your key alias 
    ** You can run through all key entries and implement a way
    ** to prompt the user to choose one - for simplicity I just set a
    ** name*/
    String alias = "user1_alias";
    
    /* Get your current KeyManager from the factory */
    final X509KeyManager okm = (X509KeyManager)keyManagerFactory.getKeyManagers()[0];
    
    /* Implement the Interface X509KeyManager */
    X509KeyManager km = new X509KeyManager() {
        public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
             /* Implement your own logic to choose the alias
                according to the validity if the case, 
                or use the entry id or any other way, you can get 
                those values outside this class*/
             return alias;
        }
    
        public X509Certificate[] getCertificateChain(String alias) {
             return okm.getCertificateChain(alias);
        }
       /* Implement the other methods of the interface using the okm object */
    };
    SSLContext context = SSLContext.getInstance("TLS");
    /* set the keymanager in the SSLContext */
    context.init(new KeyManager[]{km}, tmf.getTrustManagers(), new SecureRandom());
    SSLSocketFactory socketFactory = context.getSocketFactory();
    
    HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
    conn.setSSLSocketFactory(socketFactory);