【问题标题】:Using BouncyCastle for a simple HTTPS query使用 BouncyCastle 进行简单的 HTTPS 查询
【发布时间】:2011-12-31 14:09:58
【问题描述】:

这是我用来执行简单 HTTPS 请求的代码的简化版本:

// Assume the variables host, file and postData have valid String values

final URL url = new URL("https", host, file);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();

connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("Content-length", String.valueOf(postData.length()));

final DataOutputStream output = new DataOutputStream(connection.getOutputStream());
output.writeBytes(postData);
output.close();

final InputStream input = new DataInputStream(connection.getInputStream());

for (int c = input.read(); c != -1; c = input.read()) {
  System.out.print((char) c);
}

System.out.println();

input.close();

这在连接到我们的服务器时效果很好(如果我使用 http 作为协议,仍然可以),直到最近完成了一些安全升级。

现在它给了我这个问题中提到的“无法生成 DH 密钥对”和“Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive)”的错误:

Java: Why does SSL handshake give 'Could not generate DH keypair' exception?

原来这是 Java 中的一个已知错误,建议使用 BouncyCastle 的 JCE 实现。

我的问题是……我该如何使用 BouncyCastle 来做这样的事情?还是有更多的选择?

免责声明:我对密码学和使 HTTPS 查询成为可能的底层技术知之甚少,也没有兴趣。相反,我更愿意专注于我的应用程序逻辑,让各种库来处理低级问题。

我查看了 BouncyCastle 网站和文档,并在 Google 上搜索了有关 JCE 等的更多信息,但总而言之,这非常令人难以抗拒,而且我无法找到任何简单的代码示例来执行上述代码之类的操作。

【问题讨论】:

    标签: java ssl https bouncycastle jce


    【解决方案1】:

    Here 是一个关于如何注册充气城堡 JCE 提供商的页面。如果您选择第二个选项,请确保 N(优先顺序)的值位于 Sun JCE 提供者之前(并相应地调整 Sun JCE 提供者注册的优先顺序)。

    【讨论】:

    • 看起来该页面最有用的部分是我最初错过的一个注释 - “注意:要充分利用提供程序,您必须在您正在使用的 JVM 中安装无限策略文件 - 这些可以从java.sun.com下载。”。在我这样做之后,我什至不需要 BouncyCastle :D。我会将其作为完整答案发布。
    【解决方案2】:

    我找到了一个甚至不需要使用 BouncyCastle 的不同解决方案(尽管通过 srkavin 发布的链接):

    Java download site 下载“Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files”并替换我的 JRE 中的文件后,上述代码无需任何修改即可开始工作。

    【讨论】:

      【解决方案3】:

      以下示例代码使用 jdk1.6.0_45 和 bcprov-jdk15on-153.jar 进行简单的 https 查询:

      import java.io.IOException;
      import java.io.BufferedReader;
      import java.io.InputStreamReader;
      import java.net.Socket;
      
      import org.bouncycastle.crypto.tls.CertificateRequest;
      import org.bouncycastle.crypto.tls.DefaultTlsClient;
      import org.bouncycastle.crypto.tls.TlsAuthentication;
      import org.bouncycastle.crypto.tls.TlsClientProtocol;
      import org.bouncycastle.crypto.tls.TlsCredentials;
      
      public class TestHttpClient {
          // Reference: http://boredwookie.net/index.php/blog/how-to-use-bouncy-castle-lightweight-api-s-tlsclient/
          //            bcprov-jdk15on-153.tar\src\org\bouncycastle\crypto\tls\test\TlsClientTest.java
          public static void main(String[] args) throws Exception {
              java.security.SecureRandom secureRandom = new java.security.SecureRandom();
              Socket socket = new Socket(java.net.InetAddress.getByName("www.google.com"), 443);
              TlsClientProtocol protocol = new TlsClientProtocol(socket.getInputStream(), socket.getOutputStream(),secureRandom);
              DefaultTlsClient client = new DefaultTlsClient() {
                  public TlsAuthentication getAuthentication() throws IOException {
                      TlsAuthentication auth = new TlsAuthentication() {
                          // Capture the server certificate information!
                          public void notifyServerCertificate(org.bouncycastle.crypto.tls.Certificate serverCertificate) throws IOException {
                          }
      
                          public TlsCredentials getClientCredentials(CertificateRequest certificateRequest) throws IOException {
                              return null;
                          }
                      };
                      return auth;
                  }
              };
              protocol.connect(client);
      
              java.io.OutputStream output = protocol.getOutputStream();
              output.write("GET / HTTP/1.1\r\n".getBytes("UTF-8"));
              output.write("Host: www.google.com\r\n".getBytes("UTF-8"));
              output.write("Connection: close\r\n".getBytes("UTF-8")); // So the server will close socket immediately.
              output.write("\r\n".getBytes("UTF-8")); // HTTP1.1 requirement: last line must be empty line.
              output.flush();
      
              java.io.InputStream input = protocol.getInputStream();
              BufferedReader reader = new BufferedReader(new InputStreamReader(input));
              String line;
              while ((line = reader.readLine()) != null)
              {
                  System.out.println(line);
              }
          }
      }
      

      【讨论】:

      • 如何在 apache HTTP 客户端 3.1 中使用它
      • 这个答案是使用普通的 JDK 和 Bouncy Castle。您可以搜索“apache httpclient bouncy castle”来查找您的问题。在我看来,由于 Httpclient 和 Bouncy Castle 正在推进不同的版本和 JDK 支持,因此找到完美匹配可能具有挑战性。
      • 在打印出谷歌的部分响应后,您的代码以“org.bouncycastle.crypto.tls.TlsNoCloseNotifyException: No close_notify alert received before connection closed”结尾。
      • 我记得 TlsNoCloseNotifyException 与“连接:关闭”有关。如果我们没有显式请求服务器关闭连接,服务器实际上仍然与这个 Java Socket 客户端保持活动连接,但是这个示例代码已经完成了 System.out.println 并退出了。这已经杀死了 TLS 层的活动连接并导致 TlsNoCloseNotifyException 抛出。
      • @oraclesoon 说的对,只是因为流没有关闭;休息一切正常。所以在最后添加行reader.close();,你应该很好。
      【解决方案4】:

      我有同样的问题并解决,包括这行

      Security.addProvider(new BouncyCastleProvider());
      System.setProperty("https.protocols", "TLSv1");
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-11-25
        • 1970-01-01
        • 2016-12-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-15
        相关资源
        最近更新 更多