Using BouncyCastle for a simple HTTPS query

19,402

Solution 1

I found a different solution (although via the link that srkavin posted) that doesn't even require using BouncyCastle:

After downloading "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files" from the Java download site and replacing the files in my JRE, the above code started working without any modifications.

Solution 2

The following sample code uses jdk1.6.0_45 and bcprov-jdk15on-153.jar to perform simple https query:

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);
        }
    }
}

Solution 3

Here is a page on how to register bouncy castle JCE provider. If you choose the second option, make sure the value of N (preference order) is such that it precedes the Sun JCE provider (and accordingly adjust the preference order of the Sun JCE provider registration).

Share:
19,402
mjomble
Author by

mjomble

Updated on June 25, 2022

Comments

  • mjomble
    mjomble almost 2 years

    Here's a simplified version of the code I'm using to perform simple HTTPS requests:

    // 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();
    

    This used to work well for connecting to our server (and still does if I use http as the protocol) until recently when some security upgrades were done.

    Now it's giving me the "Could not generate DH keypair" and "Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive)" errors mentioned in this question:

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

    Turns out it's a known bug in Java and the recommendation is to use BouncyCastle's JCE implementation.

    My question is... how do I use BouncyCastle for something like this? Or are there more alternatives?

    Disclaimer: I have very little knowledge of and interest in cryptology and the underlying technology that makes HTTPS queries possible. Rather, I'd prefer to focus on my application logic and let various libraries take care of low level issues.

    I checked out the BouncyCastle website and documentation and Googled to find out more about JCE etc, but all in all it's quite overwhelming and I haven't been able to find any simple code examples for doing something like the above code.