Reputation: 547
have a question on how to include a certificate into Bouncy Castle TLSSocketConnectionFactory?
For example, I have used this code in previous version and it works well with TLS1.0:
SSLContext sslcontext = SSLContext.getInstance("TLS");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream is = new FileInputStream("c:/cert/test-tls.cer");
InputStream caInput = new BufferedInputStream(is);
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
} finally {
caInput.close();
}
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
TrustManager[] tm = tmf.getTrustManagers();
sslcontext.init(kmf.getKeyManagers(), tm, null);
SSLSocketFactory sslSocketFactory = sslcontext.getSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
now I have to use Bouncy Castle because Java 1.6 does not support TLS1.1 / TLS1.2
so the first step is to extend SSLSocketFactory Class..
mine is:
@Override
public void startHandshake() throws IOException {
tlsClientProtocol.connect(new DefaultTlsClient() {
@Override
public Hashtable<Integer, byte[]> getClientExtensions() throws IOException {
Hashtable<Integer, byte[]> clientExtensions = super.getClientExtensions();
if (clientExtensions == null) {
clientExtensions = new Hashtable<Integer, byte[]>();
}
//Add host_name
byte[] host_name = host.getBytes();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final DataOutputStream dos = new DataOutputStream(baos);
dos.writeShort(host_name.length + 3); // entry size
dos.writeByte(0); // name type = hostname
dos.writeShort(host_name.length);
dos.write(host_name);
dos.close();
clientExtensions.put(ExtensionType.server_name, baos.toByteArray());
return clientExtensions;
}
@Override
public TlsAuthentication getAuthentication()
throws IOException {
return new TlsAuthentication() {
@Override
public void notifyServerCertificate(Certificate serverCertificate) throws IOException {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream is = new FileInputStream("c:/ascert/test-tls.cer"); //"c:/cert/test-tls.cer");
InputStream caInput = new BufferedInputStream(is);
java.security.cert.Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((java.security.cert.X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
List<java.security.cert.Certificate> certs = new LinkedList<java.security.cert.Certificate>();
for (org.bouncycastle.asn1.x509.Certificate c : serverCertificate.getCertificateList()) {
certs.add(cf.generateCertificate(new ByteArrayInputStream(c.getEncoded())));
System.out.println(c.getIssuer());
}
peertCerts = certs.toArray(new java.security.cert.Certificate[0]);
} catch (Exception e) {
System.out.println("Failed to cache server certs" + e);
throw new IOException(e);
}
}
@Override
public TlsCredentials getClientCredentials(CertificateRequest arg0)
throws IOException {
return null;
}
};
When I run a simple example:
public class Test {
public static void main(String[] args)
{
String url = "https://blagajne-test.fu.gov.si:9002/v1/cash_registers";
try {
URLConnection connection = new URL(url).openConnection();
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) connection;
httpsURLConnection.setSSLSocketFactory(new TSLSocketConnectionFactory());
int responseCode = httpsURLConnection.getResponseCode();
} catch (MalformedURLException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
I get this error:
SEVERE: null
org.bouncycastle.crypto.tls.TlsFatalAlertReceived: handshake_failure(40)
at org.bouncycastle.crypto.tls.TlsProtocol.handleAlertMessage(Unknown Source)
I don't know how to properly write SSLSocketFactory to use a certificate for the authentication with a remote server. Thank you for any help.
Upvotes: 3
Views: 6545
Reputation: 4042
The latest version of BouncyCastle includes a JSSE provider ("BCJSSE", in the class org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
) in the bctls-jdk15on-159 jar. If you install this provider with higher priority than the default one, then your original code sample should automatically pick up the BCJSSE provider.
BCJSSE supports TLS 1.2 back to JDK 1.5. There are also some BCJSSE-specific extensions in the org.bouncycastle.jsse package for accessing TLS features not supported by SunJSSE in earlier JDKs (or at all), e.g. Server Name Indication.
There are still minor features missing compared to SunJSSE, but for the majority of users it should be a straightforward replacement, certainly a better option than implementing SSLSocketFactory yourself.
Upvotes: 5