【问题标题】:Calling web service with ssl certificates使用 ssl 证书调用 Web 服务
【发布时间】:2019-01-05 23:48:28
【问题描述】:

如何在 Java 中使用下面提到的 ssl 证书调用相同的 Web 服务?

curl -v --cert kkk.cer --key kkk.private --pass kkk --cacert sslservercachain.cer -H "Content-Type: application/x-www-form-urlencoded" https://hello-signs.api-dev.myname.com/v2/oauth2/client_credential/accesstoken?grant_type=client_credentials -d "client_id=xxccvvbbnniioopp&client_secret=zoopopopopopppp"

【问题讨论】:

    标签: java rest web-services curl ssl-certificate


    【解决方案1】:

    Meta:我相信这至少部分是骗人的——我已经看到(相当多)它的变体,但现在找不到。如果我愿意,可以稍后添加(或者其他任何人都可以随意添加)。

    对此可能有数千种可能的答案,因为似乎几乎每个拥有计算机的人都编写了自己的 HTTP 客户端(包括 HTTPS)库,而且实际上有几种 SSL/TLS 实现可在各种情况下从 Java 中使用.为简单起见,我将展示唯一标准 Sun/Oracle/OpenJDK Java 内置:基于JSSE SSL/TLS 实现的HttpsURLConnection 类。 (Android、IBM 和 Apple 等 Java 的其他实现在这里可能会有所不同,但至少 Android 可能不会使用 curl。)

    它还取决于您当前拥有的文件是什么,而这些文件又取决于您使用的 curl 的构建——curl 支持大约十几个 SSL/TLS 堆栈,它们使用各种文件格式。由于您没有提供此信息,我猜您的 curl 使用 openssl(或其分支之一),而后者又使用 PEM 格式的文件来处理--cert --key --cacert。如果没有,请编辑您的问题以包含此信息(并安全地 ping 我;我不确定我是否会收到关于 Q 编辑的通知)。

    遵守这些警告:

    要在 JSSE(和 HttpsURLConnection)中使用 SSL/TLS 证书和密钥,您需要它们位于 Java 格式文件或内存中合适的 Java 对象中。前者通常更容易,所以我将展示这一点。首先使用 OpenSSL 将客户端密钥 PLUS 证书转换为 PKCS12

    openssl pkcs12 -export -in kkk.cer -inkey kkk.private -out kkk.pkcs12 
    # use a different filename if you prefer
    # enter passwords as needed; do _not_ use an empty password for the pkcs12
    # (if so OpenSSL will create a file Java cannot decrypt, defeating your goal)
    

    对于 Java 9 以上就足够了;他们默认使用 PKCS12 格式的密钥库。旧版本默认使用 JKS 格式,尽管 Java 8 的最新更新有一个问题:即使您指定或默认格式为 JKS,它实际上也可以读取 PKCS12 格式——参见文件 JRE/lib/security/java.security 中的项目 keystore.compat .甚至在此之前,Java 允许您在使用时指定 PKCS12 格式,但如果您觉得不这样做更方便,您可以将 PKCS12 格式转换为 JKS 格式:

    keytool -importkeystore -srckeystore kkk.pkcs12 -srcstoretype pkcs12 -destkeystore kkk.jks 
    # use the same password for output (jks) as input (p12) otherwise 
    # you may create a privatekey entry that cannot be used later 
    

    还需要用于验证服务器的证书在密钥库文件中;这通常是一个单独的文件,为了清楚起见,通常称为“信任库”而不是“密钥库”。在这种情况下,您需要使用keytool,并且一次只能做一个证书;如果sslservercachain 顾名思义包含多个证书,则必须将它们拆分为单独的文件(在大多数 Linux 和其他一些 Unix 上,csplit 可以轻松做到这一点,否则任何文本编辑工具都应该管理),然后为每个文件做

    keytool -import -keystore trust.<see below> -alias <unique> -file <filename> 
    # use a unique alias for each cert: if you know what they are, 
    # names like intermediate, root, bridge, etc may be helpful;
    # otherwise just use arbitrary names like a b c or 1 2 3 .
    

    对于 Java 9+,这将创建 pkcs12,您应该相应地命名它。在 Java 8 中,它将默认为 JKS,但您可以指定 -storetype pkcs12(并相应地再次命名)。在 Java 8 以下,您必须使用 JKS(这些版本中的提供程序确实支持 pkcs12 用于 privatekey 条目,但不支持 trustcert 条目)。

    准备好这些文件并将其设置为系统属性,使用URL 创建一个带有所需标题、正文和方法的HttpsURLConnectioncurl -d 自动设置POST -- 顺便说一下它还设置了 form-urlencoded,因此您实际上不需要在 curl 中显式执行此操作,但您确实需要在 Java 中执行此操作)。请参阅基类 URLConnection 及其链接子类(HttpURLConnection 和 HttpsURLConnection)的 javadoc;如果您有 Java IDE(如 Eclipse 或 Intellij),这些 javadocs 应该会在您开始输入代码时自动出现。

    // once at he beginning of your program (before any use of default SSLContext)
    // or instead can be done on the command line with -Dprop=value 
    System.setProperty ("javax.net.ssl.keyStore", filepath);
    System.setProperty ("javax.net.ssl.keyStorePassword", password);
    System.setProperty ("javax.net.ssl.keyStoreType", "PKCS12" or "JKS"); -- if not default
    System.setProperty ("javax.net.ssl.trustStore", filepath);
    System.setProperty ("javax.net.ssl.trustStorePassword", password);
    System.setProperty ("javax.net.ssl.trustStoreType", "PKCS12" or "JKS"); -- if not default
    
    // when (each time) you want to make a request:
    URL url = new URL ("https://hello-signs.api-dev.myname.com/v2/oauth2/client_credential/accesstoken?grant_type=client_credentials");
    HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
    // we aren't actually using S-specific items, so could instead use 
    // HttpURLConnection especially if you want to mix HTTPS and HTTP;
    // or you could use the Https version to tweak some SSL/TLS parameters
    conn.setRequestMethod ("POST");
    conn.setRequestHeader ("Content-Type","application/x-www-form-urlencoded");
    conn.setDoOutput (true);
    OutputStream os = conn.getOutputStream(); 
    os.write ("client_id=xxccvvbbnniioopp&client_secret=zoopopopopopppp".getBytes());
    os.close();
    os.connect(); // optional, can help localize exceptions
    
    // depending on what you want to do with the result(s):
    ... conn.getResponseCode() and/or conn.getResponseMessage() ...
    ... conn.getHeaderField{,Int,Long,s}("id") ...
    ... conn.getContent{Type,Length[Long],Encoding,}() ... 
    ... conn.getInputStream() then read from it, including decoding various formats or charsets as needed/desired ...
    conn.disconnect(); // when done
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-04-14
      • 2010-10-30
      • 2012-02-21
      • 2023-04-07
      • 2015-12-15
      • 1970-01-01
      • 1970-01-01
      • 2013-05-24
      相关资源
      最近更新 更多