Java 将second.p12导入密钥库后握手失败且CA未知

Java 将second.p12导入密钥库后握手失败且CA未知,java,soap,ssl-certificate,soapui,keytool,Java,Soap,Ssl Certificate,Soapui,Keytool,我向密钥库添加了一个.p12证书(到目前为止还没有证书) 我在SOAPUI中使用了keyStore.p12,密码为myDestPassword,一切正常 现在,我在现有密钥库中导入了第二个.p12(参数相同): 导入成功了,但在我尝试使用更新的keyStore.p12(与之前相同的wsdl和地址)进行SOAP调用之后,我得到了 有人知道为什么吗 更新: 我还尝试在Java应用程序中使用这个密钥存储。行为是一样的。我添加了第一个证书-->它可以工作。我添加了第二个证书,但我得到了一个错误,但现在,

我向密钥库添加了一个.p12证书(到目前为止还没有证书)

我在SOAPUI中使用了keyStore.p12,密码为myDestPassword,一切正常

现在,我在现有密钥库中导入了第二个.p12(参数相同):

导入成功了,但在我尝试使用更新的keyStore.p12(与之前相同的wsdl和地址)进行SOAP调用之后,我得到了

有人知道为什么吗

更新:

我还尝试在Java应用程序中使用这个密钥存储。行为是一样的。我添加了第一个证书-->它可以工作。我添加了第二个证书,但我得到了一个错误,但现在,我的错误是:

Exception in thread "main" java.lang.Exception:  myService.uploadParameters call FAILED with: com.sun.xml.internal.ws.client.ClientTransportException: HTTP-Transportfehler: javax.net.ssl.SSLHandshakeException: Received fatal alert: unknown_ca
更新2 这里是我的密钥库的内容

C:\>keytool -list -keystore KeyStore.p12 -storetype PKCS12
Keystore-Kennwort eingeben:

Keystore-Typ: PKCS12
Keystore-Provider: SunJSSE

Keystore enthält 2 Einträge

muster max (btt/beat2), 23.03.2020, PrivateKeyEntry,
Zertifikat-Fingerprint (SHA1): 03:A0:3C:0F:4A:91:1A:40:2D:C3:EC:A0:23:D5:E6:44:C5:29:34:DC
client_cert_muster, 23.03.2020, PrivateKeyEntry,
Zertifikat-Fingerprint (SHA1): 06:13:A2:8A:84:E8:F9:74:50:E8:BA:D3:79:9F:FB:5F:CB:09:1E:D0

我认为问题在于您试图将两个密钥对放在同一个中。p12和客户端通常只需要密钥对的公共部分,编码为X509证书。当.p12中有多个
PrivateKeyEntry
条目时,它似乎对该做什么感到困惑

要获取仅包含证书的PKCS12文件,请尝试以下操作:

keytool -exportcert -keystore FIRSTCert.p12 -storetype PKCS12 -storepass mySrcPassword -file firstcert.cer
keytool -exportcert -keystore SECONDCert.p12 -storetype PKCS12 -storepass 12345 -file secondcert.cer
keytool -importcert -keystore keyStore.p12 -storetype PKCS12 -storepass myDestPassword -file firstcert.cer
keytool -importcert -keystore keyStore.p12 -storetype PKCS12 -storepass myDestPassword -file secondcert.cer -alias mykey2
现在,当你做一个

keytool -list -keystore keyStore.p12 -storetype PKCS12 -storepass myDestPassword 


Keystore type: PKCS12
Keystore provider: SUN

Your keystore contains 2 entries

mykey, 23 march 2020, trustedCertEntry,
Certificate fingerprint (SHA-256): 26:FB:B4:8F:42:9C:48:5E:EE:8D:35:2E:A5:1C:79:EA:23:B5:36:50:47:62:4D:BC:D1:3D:26:61:04:1C:1A:32
mykey2, 23 march 2020, trustedCertEntry,
Certificate fingerprint (SHA-256): C1:06:CA:06:F1:29:80:72:16:1F:F6:72:04:39:16:17:31:E0:7A:5B:28:BB:41:4C:77:8E:94:F1:C5:8D:70:F8
它应该有2个条目,它们的类型应该是
trustedCertEntry
。这是您的信任库,它应该是您的客户建立TLS连接所需的唯一信任


除非你真的知道自己在做什么,否则不要将服务器的完整密钥对移出机器;它应该只有服务器知道。

我认为问题在于您试图将两个密钥对放在同一个中。p12和客户端通常只需要密钥对的公共部分,编码为X509证书。当.p12中有多个
PrivateKeyEntry
条目时,它似乎对该做什么感到困惑

要获取仅包含证书的PKCS12文件,请尝试以下操作:

keytool -exportcert -keystore FIRSTCert.p12 -storetype PKCS12 -storepass mySrcPassword -file firstcert.cer
keytool -exportcert -keystore SECONDCert.p12 -storetype PKCS12 -storepass 12345 -file secondcert.cer
keytool -importcert -keystore keyStore.p12 -storetype PKCS12 -storepass myDestPassword -file firstcert.cer
keytool -importcert -keystore keyStore.p12 -storetype PKCS12 -storepass myDestPassword -file secondcert.cer -alias mykey2
现在,当你做一个

keytool -list -keystore keyStore.p12 -storetype PKCS12 -storepass myDestPassword 


Keystore type: PKCS12
Keystore provider: SUN

Your keystore contains 2 entries

mykey, 23 march 2020, trustedCertEntry,
Certificate fingerprint (SHA-256): 26:FB:B4:8F:42:9C:48:5E:EE:8D:35:2E:A5:1C:79:EA:23:B5:36:50:47:62:4D:BC:D1:3D:26:61:04:1C:1A:32
mykey2, 23 march 2020, trustedCertEntry,
Certificate fingerprint (SHA-256): C1:06:CA:06:F1:29:80:72:16:1F:F6:72:04:39:16:17:31:E0:7A:5B:28:BB:41:4C:77:8E:94:F1:C5:8D:70:F8
它应该有2个条目,它们的类型应该是
trustedCertEntry
。这是您的信任库,它应该是您的客户建立TLS连接所需的唯一信任


除非你真的知道自己在做什么,否则不要将服务器的完整密钥对移出机器;它应该只被服务器知道。

Thx@Daniel-alot感谢他的帮助。 我终于找到了办法。我不知道这是不是正确的,但它是有效的。回答我。 启动应用程序时,我仅使用信任库初始化TLS:

  static void initTLS(String trustStore, String trustStorePW ){
    System.setProperty("https.protocols", "TLSv1.2")
    System.setProperty("javax.net.ssl.trustStore",trustStore)
    System.setProperty("javax.net.ssl.trustStorePassword", trustStorePW)    
  }
现在是新的:在每次(SOAP)调用之前,我都会创建一个initKeyStore:

static void initKeyStore( String aliasName ){
    String keyStorePW = "abc..."
    String keyStorePath = "path/to/keyStore"

    KeyStore keystore = KeyStore.getInstance("PKCS12");
    char[] pwdArray = keyStorePW.toCharArray();
    keystore.load(new FileInputStream(keyStorePath), pwdArray);
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kmf.init(keystore, keyStorePW.toCharArray());

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

      @Override
      public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
         return aliasName
      }

      @Override
      public X509Certificate[] getCertificateChain(String alias) {
        return origKm.getCertificateChain(alias);
      }

      @Override
      public String[] getClientAliases(String keyType, Principal[] issuers) {
        return origKm.getClientAliases(keyType, issuers)
      }

      @Override
      public String[] getServerAliases(String keyType, Principal[] issuers) {
        return origKm.getServerAliases(keyType, issuers)
      }

      @Override
      public PrivateKey getPrivateKey(String alias) {
        return origKm.getPrivateKey(alias);
      }

      @Override
      public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
        return origKm.chooseServerAlias(keyType, issuers, socket );
      }
    }

    SSLContext sslContext = SSLContext.getInstance("TLS");
    KeyManager[] kms = [km] 
    sslContext.init(kms, null, null);
    HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
参数
aliasName
允许我控制密钥库中的哪个.p12证书将被获取。
我认为这是相当肮脏的,我将不得不优化这段代码。但最终还是成功了。

谢谢丹尼尔的帮助。 我终于找到了办法。我不知道这是不是正确的,但它是有效的。回答我。 启动应用程序时,我仅使用信任库初始化TLS:

  static void initTLS(String trustStore, String trustStorePW ){
    System.setProperty("https.protocols", "TLSv1.2")
    System.setProperty("javax.net.ssl.trustStore",trustStore)
    System.setProperty("javax.net.ssl.trustStorePassword", trustStorePW)    
  }
现在是新的:在每次(SOAP)调用之前,我都会创建一个initKeyStore:

static void initKeyStore( String aliasName ){
    String keyStorePW = "abc..."
    String keyStorePath = "path/to/keyStore"

    KeyStore keystore = KeyStore.getInstance("PKCS12");
    char[] pwdArray = keyStorePW.toCharArray();
    keystore.load(new FileInputStream(keyStorePath), pwdArray);
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kmf.init(keystore, keyStorePW.toCharArray());

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

      @Override
      public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
         return aliasName
      }

      @Override
      public X509Certificate[] getCertificateChain(String alias) {
        return origKm.getCertificateChain(alias);
      }

      @Override
      public String[] getClientAliases(String keyType, Principal[] issuers) {
        return origKm.getClientAliases(keyType, issuers)
      }

      @Override
      public String[] getServerAliases(String keyType, Principal[] issuers) {
        return origKm.getServerAliases(keyType, issuers)
      }

      @Override
      public PrivateKey getPrivateKey(String alias) {
        return origKm.getPrivateKey(alias);
      }

      @Override
      public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
        return origKm.chooseServerAlias(keyType, issuers, socket );
      }
    }

    SSLContext sslContext = SSLContext.getInstance("TLS");
    KeyManager[] kms = [km] 
    sslContext.init(kms, null, null);
    HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
参数
aliasName
允许我控制密钥库中的哪个.p12证书将被获取。
我认为这是相当肮脏的,我将不得不优化这段代码。但最后它还是起作用了。

导入密钥库时是否指定了单独的别名?默认别名为
mykey
,第二次导入可能会重写第一次导入。通常,
keytool
会就此发出警告,并询问您是否要继续,但您指定了
-noprompt
,因此它可能只是按照您的指示进行了操作。别名不同,我也尝试了它,但没有“-noprompt”。相同的错误OK,为什么需要导入两个完整的密钥对?服务器如何知道使用哪一个密钥?通常,您会使用一个密钥库,其中包含一个
PrivateKeyEntry
,作为服务器标识和CA证书链的
TrustedCertificateEntry
。如果执行
-importkeystore
然后执行
-importcert
,是否会发生同样的情况?我有两个.p12证书,用于两台服务器。我的应用程序必须连接到两台服务器。所以使用
-importcert
我的私钥会丢失?等一下,这是给客户的吗?您是否可以执行
keytool-list-keystore keystore.p12-storetype pkcs12
并将输出包含在问题中?导入keystore时是否指定了单独的别名?默认别名为
mykey
,第二次导入可能会重写第一次导入。通常,
keytool
会就此发出警告,并询问您是否要继续,但您指定了
-noprompt
,因此它可能只是按照您的指示进行了操作。别名不同,我也尝试了它,但没有“-noprompt”。相同的错误OK,为什么需要导入两个完整的密钥对?服务器如何知道使用哪一个密钥?通常,您会使用一个密钥库,其中包含一个
PrivateKeyEntry
,作为服务器标识和CA证书链的
TrustedCertificateEntry
。如果执行
-importkeystore
然后执行
-importcert
,是否会发生同样的情况?我有两个.p12证书,用于两台服务器。我的应用程序必须连接到两台服务器。所以使用
-importcert
我的私钥会丢失?等一下,这是给客户的吗?你能做一个
keytool-list-keystore keystore.p12-storetype pkcs12
并在问题中包含输出吗?谢谢你的回答,但我想我需要客户端的keystore来向服务器进行身份验证。我已经有一家信托商店了。到目前为止,我们在全球范围内的常见做法是订购.p12证书,-->使用此p12证书进行身份验证并下载crt(全部通过浏览器)--->将.crt放入信任库,将.p12放入密钥库-->在AppOk中进行Java调用,但从未提及此设置。我很清楚,您是否在Java应用程序中通过TLS使用基于客户端的身份验证?是的,客户端必须进行身份验证