【问题标题】:how to use non password protected (.p12) ssl certificate in spring boot如何在spring boot中使用非密码保护(.p12)ssl证书
【发布时间】:2019-10-11 16:40:24
【问题描述】:

我有一个 .pem 证书,我必须在我的 Spring Boot 应用程序中使用它。 现在,由于 java 不理解 .pem 格式,我将其转换为 .p12 格式。

由于我们已经有一个不受任何密码保护的 .pem 证书,所以我也试图避免使用 .p12 的密码。

我创建了两个 .p12 文件,一个是使用以下命令的密码:

openssl x509 -signkey sslcert.key  -in sslcert.csr   -req -days 365
-out sslcert.pem

另一个没有密码使用下面的命令:

openssl pkcs12  -inkey sslcert.key  -in sslcert.pem  -export -passout
pass: -nokeys -out sslcert.p12

而且我可以在 Spring Boot 应用程序中使用以下属性使用带密码的密码成功获得结果:

server.port=8443  
server.ssl.enabled=true 
server.ssl.key-store-type=PKCS12 
server.ssl.key-store=keys/sslcert.p12 
server.ssl.key-store-password=password

但是当我尝试使用我喜欢的不带密码的那个时,我遇到了 sslhandshake 异常。

而且,我无法弄清楚如何将此信息传递给 ssl 不受密码保护的 spring boot。

到目前为止,我尝试了许多组合,例如不传递密码属性或将其保留为空,但在所有情况下都失败了。

server.port=8443 
server.ssl.enabled=true
server.ssl.key-store-type=PKCS12
server.ssl.key-store=keys/sslcert.p12

我遇到了异常:

javax.net.ssl.SSLHandshakeException: no cipher suites in common
    at sun.security.ssl.Handshaker.checkThrown(Unknown Source)
    at sun.security.ssl.SSLEngineImpl.checkTaskThrown(Unknown Source)
    at sun.security.ssl.SSLEngineImpl.writeAppRecord(Unknown Source)
    at sun.security.ssl.SSLEngineImpl.wrap(Unknown Source)
    at javax.net.ssl.SSLEngine.wrap(Unknown Source)
    at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.flush(SslConnection.java:864)
    at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.fill(SslConnection.java:515)
    at org.eclipse.jetty.server.HttpConnection.fillRequestBuffer(HttpConnection.java:331)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:243)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:305)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
    at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.onFillable(SslConnection.java:411)
    at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:305)
    at org.eclipse.jetty.io.ssl.SslConnection$2.succeeded(SslConnection.java:159)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
    at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:765)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:683)
    at java.lang.Thread.run(Unknown Source)

所以,任何人都可以了解如何在 Spring Boot 中使用不受密码保护的 .p12 证书。

所有 google 文章似乎都在讲述预期的密码。那么它是java中的强制性东西吗?

【问题讨论】:

  • 错误消息“javax.net.ssl.SSLHandshakeException: no cipher suites in common”当然与私钥(与证书关联)是否受密码保护这一事实无关。您的 2 个证书(可能)中有一些不同的东西会触发不同的客户端行为,因此会触发不同的服务器回复。
  • @PatrickMevzek 如果您查看命令,我只是更改为不使用任何密码..除此之外,一切都一样
  • 可以分享 SSL 日志吗?添加 -Djavax.net.debug=ssl
  • @PatrickMevzek+ 'no cipher suites in common' 导致 clienthello 被拒绝;服务器根本不发送任何证书,因此客户端不可能受到它的影响。如果声称的“密钥库”实际上只包含证书而不包含私钥,则会发生这种情况,因为显示的第二个命令 openssl pkcs12 -export -nokeys ... 将创建。 Onki:SSL/TLS 服务器必须具有证书和私钥(如果适用,链证书,但x509 -signkey 创建自签名证书不需要链证书)。我不确定Java是否可以在没有密码的情况下使用P12。
  • 好的,这取决于贵公司的政治。如果它是固定的,它就不是真正的密码,所以你可以提到这一点,因为它们可以使用未加密的密钥。或者您可以说密码“不适用”(这就是您的静态密码)或者问题可能是(实际上,这应该是问题)您没有强调他们的密钥是未加密的;他们不应该同意这一点,然后他们应该告诉你公司批准的保护私钥的方法。

标签: java spring spring-boot ssl


【解决方案1】:

所以经过所有研究,似乎 java 密钥库需要密码。 所以我不得不忽略我们没有密码的.p12。 并使用 pem 证书,我们将其转换为带有密码的 .p12。 我是从 spring 代码中执行此操作的,我将随机密码传递给 shell 脚本,该脚本使用该密码从 .pem 生成 .p12。

然后我将相同的密码从代码中传递给 ssl 属性。

一个缺点是每次重新启动应用程序时我都必须从 .pem 重新生成 .p12,但这就是我防止静态密码的方法。

【讨论】:

    【解决方案2】:

    不设置密码是不行的

    【讨论】:

      【解决方案3】:

      通过使用 WebServerFactoryCustomizer 和 BouncyCastle JCE 提供程序的自定义实现,我能够使用没有密码的 PKCS12 密钥库为 Spring Boot 应用程序提供 SSL。

      首先,我使用 BouncyCastle 提供程序 (org.bouncycastle:bcprov-jdk15on) 将无密码 PKCS12(在我的情况下,从 Azure Key Vault 检索)加载到密钥库中:

          ByteArrayInputStream keyStoreStream = new ByteArrayInputStream(pfxBytes);
      
          logger.info("Loading server cert keystore from Vault retrieved cert");
          KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
          keyStore.load(keyStoreStream, "".toCharArray());
      

      然后我把keyStore放到org.springframework.boot.web.server.SslStoreProvider的一个实现中:

      public class CustomSslStoreProvider implements SslStoreProvider {
      
          private KeyStore keyStore;
          private KeyStore trustStore;
      
          public CustomSslStoreProvider(KeyStore keyStore, KeyStore trustStore){
              this.keyStore = keyStore;
              this.trustStore = trustStore;
          }
      

      然后我将 CustomSslStoreProvider 注册为 bean,以便可以将其注入到我的 WebServerFactoryCustomizer 实现中,以便嵌入式服务器使用它:

      @Component
      public class CustomSSLServletContainer implements
              WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
      
      
          @Autowired(required = false)
          private CustomSslStoreProvider sslStoreProvider;
      
      
          public void customize(TomcatServletWebServerFactory factory) {
              if(sslStoreProvider != null) {
                  factory.setSslStoreProvider(sslStoreProvider);
              }
      

      通过这个实现,我仍然在 application.properties 中配置了密钥别名(无关的,我碰巧需要 ssl 客户端身份验证)。但是,您不需要提供任何 server.ssl.key-store 属性:

      server.ssl.key-alias=panda-asfafa=asfaf
      server.ssl.client-auth=need
      

      【讨论】:

        猜你喜欢
        • 2021-07-20
        • 2023-03-10
        • 2017-09-30
        • 2020-05-06
        • 2014-11-25
        • 2018-08-18
        • 1970-01-01
        • 2020-02-24
        • 1970-01-01
        相关资源
        最近更新 更多