代码之家  ›  专栏  ›  技术社区  ›  x80486

使用服务器和客户端证书的Spring WS客户端身份验证

  •  4
  • x80486  · 技术社区  · 7 年前

    我需要能够为SOAP服务进行“客户端证书”身份验证。

    我正在使用Spring WS。我有:a my.key myCA.pem ,和 myClient.crt .

    这是我的相关Java代码(我知道它仍然凌乱,但我只是在尝试 首先):

    public TheResponse doIt(TheRequest request) {
      log.info("Sending request...");
      try {
        InputStream is = new FileInputStream(new File("src/main/resources/keystore.jks"));
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(is, "keystore!passwd".toCharArray());
        is.close();
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
    
        InputStream is1 = new FileInputStream(new File("src/main/resources/truststore.jks"));
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(is1, "truststore!passwd".toCharArray());
        is1.close();
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(trustStore);
    
        HttpsUrlConnectionMessageSender messageSender = new HttpsUrlConnectionMessageSender();
        messageSender.setKeyManagers(keyManagerFactory.getKeyManagers());
        messageSender.setTrustManagers(trustManagerFactory.getTrustManagers());
        setMessageSender(messageSender);
    
        return (TheResponse) getWebServiceTemplate().marshalSendAndReceive(request,
            new SoapActionCallback("https://domain/tld/icc/SomePathDownTheLine"));
      } catch (Throwable e) {
        log.error("Sh*t didn't work due to:", e);
        throw new GatewayConnectionException(String.format("Unexpected error while sending request [%s]", e.getMessage()));
      }
    }
    

    # KeyStore
    $ openssl pkcs12 -export -in myClient.crt -inkey my.key -out keystore.p12 -name my_key -CAfile myCA.pem -caname root
    
    $ keytool -importkeystore -deststorepass keystore!passwd -destkeypass keystore!passwd -destkeystore keystore.jks \
      -srckeystore keystore.p12 -srcstoretype PKCS12 -srcstorepass keystore!passwd -alias my_key
    
    # Trustore (using truststore!passwd)
    $ keytool -import -trustcacerts -alias my_ca -file myCA.pem -keystore truststore.jks
    
    $ keytool -import -trustcacerts -alias my_cc -file myClient.crt -keystore truststore.jks
    

    ...以下是验证步骤:

    $ keytool -list -keystore keystore.jks -storepass ********
    
    Keystore type: JKS
    Keystore provider: SUN
    
    Your keystore contains 1 entry
    
    my_key, Oct 17, 2017, PrivateKeyEntry,
    Certificate fingerprint (SHA1): 1A:9D:6A:65:. . .:E6:C1:90
    
    $ keytool -list -keystore truststore.jks -storepass ********
    
    Keystore type: JKS
    Keystore provider: SUN
    
    Your keystore contains 2 entries
    
    my_cc, Oct 17, 2017, trustedCertEntry,
    Certificate fingerprint (SHA1): 1A:9D:6A:65:. . .:E6:C1:90
    my_ca, Oct 17, 2017, trustedCertEntry,
    Certificate fingerprint (SHA1): 36:82:F7:AB:. . .:70:B2:6C
    

    ...但是,无论何时我请求SOAP操作,我都会得到一个HTTP 401(未经授权) org.springframework.ws.client.WebServiceTransportException: Unauthorized [401] .

    有什么线索吗?顺便说一句,我基本上是跟着 this


    更新

    SSL握手工作正常。我可以通过设置 -Djavax.net.debug=all VM选项。现在的情况是,不管这些安全性如何,服务器也需要用户名和密码。

    1 回复  |  直到 7 年前
        1
  •  4
  •   x80486    7 年前

    这一切都还好。最终 HTTP 401 (Unauthorized) 是因为需要服务 Basic auth

    所有的密钥库和信任库生成都是完美的。这是“最终”解决方案(使用Spring Web Services):

      //
      // Spring Config
    
      // Inject messageSender() into a WebServiceTemplate or,
      // Have a class that extends from WebServiceGatewaySupport
    
      @Bean
      public HttpsUrlConnectionMessageSender messageSender() throws Exception {
        HttpsUrlConnectionMessageSender messageSender = new BasicAuthHttpsConnectionMessageSender(username, password);
        messageSender.setTrustManagers(trustManagersFactoryBean().getObject());
        messageSender.setKeyManagers(keyManagersFactoryBean().getObject());
        return messageSender;
      }
    
      @Bean
      public TrustManagersFactoryBean trustManagersFactoryBean() {
        TrustManagersFactoryBean trustManagersFactoryBean = new TrustManagersFactoryBean();
        trustManagersFactoryBean.setKeyStore(trustStore().getObject());
        return trustManagersFactoryBean;
      }
    
      @Bean
      public KeyManagersFactoryBean keyManagersFactoryBean() {
        KeyManagersFactoryBean keyManagersFactoryBean = new KeyManagersFactoryBean();
        keyManagersFactoryBean.setKeyStore(keyStore().getObject());
        keyManagersFactoryBean.setPassword(keyStorePassword);
        return keyManagersFactoryBean;
      }
    
      @Bean
      public KeyStoreFactoryBean trustStore() {
        KeyStoreFactoryBean keyStoreFactoryBean = new KeyStoreFactoryBean();
        keyStoreFactoryBean.setLocation(new ClassPathResource("truststore.jks")); // Located in src/main/resources
        keyStoreFactoryBean.setPassword(trustStorePassword);
        return keyStoreFactoryBean;
      }
    
      @Bean
      public KeyStoreFactoryBean keyStore() {
        KeyStoreFactoryBean keyStoreFactoryBean = new KeyStoreFactoryBean();
        keyStoreFactoryBean.setLocation(new ClassPathResource("keystore.jks"));
        keyStoreFactoryBean.setPassword(keyStorePassword);
        return keyStoreFactoryBean;
      }
    

    // You might need org.springframework.ws:spring-ws-support in order to
    // have HttpsUrlConnectionMessageSender
    public final class BasicAuthHttpsConnectionMessageSender extends HttpsUrlConnectionMessageSender {
      private String b64Creds;
    
      public BasicAuthHttpsConnectionMessageSender(String username, String password) {
        b64Creds = Base64.getUrlEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
      }
    
      @Override
      protected void prepareConnection(HttpURLConnection connection) throws IOException {
        connection.setRequestProperty(HttpHeaders.AUTHORIZATION, String.format("Basic %s", b64Creds));
        super.prepareConnection(connection);
      }
    }
    

    one

    希望这能对以后的人有所帮助。我花了一段时间才把所有的东西都整理好。