How to Create a Bks (Bouncycastle) Format Java Keystore That Contains a Client Certificate Chain

How to create a BKS (BouncyCastle) format Java Keystore that contains a client certificate chain

Detailed Step by Step instructions I followed to achieve this

  • Download bouncycastle JAR from
    http://repo2.maven.org/maven2/org/bouncycastle/bcprov-ext-jdk15on/1.46/bcprov-ext-jdk15on-1.46.jar
    or take it from the "doc" folder.
  • Configure BouncyCastle for PC using one of the below methods.
    • Adding the BC Provider Statically (Recommended)
      • Copy the bcprov-ext-jdk15on-1.46.jar to each
        • D:\tools\jdk1.5.0_09\jre\lib\ext (JDK (bundled JRE)
        • D:\tools\jre1.5.0_09\lib\ext (JRE)
        • C:\ (location to be used in env variable)
      • Modify the java.security file under
        • D:\tools\jdk1.5.0_09\jre\lib\security
        • D:\tools\jre1.5.0_09\lib\security
        • and add the following entry
          • security.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider
      • Add the following environment variable in "User Variables" section
        • CLASSPATH=%CLASSPATH%;c:\bcprov-ext-jdk15on-1.46.jar
    • Add bcprov-ext-jdk15on-1.46.jar to CLASSPATH of your project and Add the following line in your code
      • Security.addProvider(new BouncyCastleProvider());
  • Generate the Keystore using Bouncy Castle
    • Run the following command
      • keytool -genkey -alias myproject -keystore C:/myproject.keystore -storepass myproject -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider
    • This generates the file C:\myproject.keystore
    • Run the following command to check if it is properly generated or not
      • keytool -list -keystore C:\myproject.keystore -storetype BKS
  • Configure BouncyCastle for TOMCAT

    • Open D:\tools\apache-tomcat-6.0.35\conf\server.xml and add the following entry

      • <Connector
        port="8443"
        keystorePass="myproject"
        alias="myproject"
        keystore="c:/myproject.keystore"
        keystoreType="BKS"
        SSLEnabled="true"
        clientAuth="false"
        protocol="HTTP/1.1"
        scheme="https"
        secure="true"
        sslProtocol="TLS"
        sslImplementationName="org.bouncycastle.jce.provider.BouncyCastleProvider"/>
    • Restart the server after these changes.

  • Configure BouncyCastle for Android Client
    • No need to configure since Android supports Bouncy Castle Version 1.46 internally in the provided "android.jar".
    • Just implement your version of HTTP Client (MyHttpClient.java can be found below) and set the following in code
      • SSLSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
    • If you don't do this, it gives an exception as below
      • javax.net.ssl.SSLException: hostname in certificate didn't match: <192.168.104.66> !=
    • In production mode, change the above code to
      • SSLSocketFactory.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);

MyHttpClient.java

package com.arisglobal.aglite.network;

import java.io.InputStream;
import java.security.KeyStore;

import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.SingleClientConnManager;

import com.arisglobal.aglite.activity.R;

import android.content.Context;

public class MyHttpClient extends DefaultHttpClient {

final Context context;

public MyHttpClient(Context context) {
this.context = context;
}

@Override
protected ClientConnectionManager createClientConnectionManager() {
SchemeRegistry registry = new SchemeRegistry();

registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));

// Register for port 443 our SSLSocketFactory with our keystore to the ConnectionManager
registry.register(new Scheme("https", newSslSocketFactory(), 443));
return new SingleClientConnManager(getParams(), registry);
}

private SSLSocketFactory newSslSocketFactory() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");

// Get the raw resource, which contains the keystore with your trusted certificates (root and any intermediate certs)
InputStream in = context.getResources().openRawResource(R.raw.aglite);
try {
// Initialize the keystore with the provided trusted certificates.
// Also provide the password of the keystore
trusted.load(in, "aglite".toCharArray());
} finally {
in.close();
}

// Pass the keystore to the SSLSocketFactory. The factory is responsible for the verification of the server certificate.
SSLSocketFactory sf = new SSLSocketFactory(trusted);

// Hostname verification from certificate
// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}
}

How to invoke the above code in your Activity class:

DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpResponse response = client.execute(...);

WSO2-emm: How can i create a BKS file that is compatible with Android?

Please follow these steps: step 2 to step 8 https://docs.wso2.com/display/EMM110/iOS+Server+Configurations#iOSServerConfigurations-step2

After that, you can generate the BKS file using following command:

keytool -noprompt -import -v -trustcacerts -alias 'openssl x509 -inform PEM -subject_hash -noout -in ca_cert.pem' -file ca_cert.pem -keystore emm_truststore.bks -storetype BKS -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-jdk16-146.jar -storepass 'wso2carbon'

Note: you need to have bcprov-jdk16-146.jar in the folder in order to generate above bks file

Once you generate it copy emm_truststore.bks to Android res/raw folder. In android source code CommonUtilities.java change SERVER_PORT to 9443 and SERVER_PROTOCOL to https://

TLS connection with self signed certificate

Ok so I finally managed to make it work!
This is what iv'e done:

  1. Create keystore file with the java key tool:

    keytool -genkey -alias tomcat -keyalg RSA

    type your host name when asked for first and last name(so if you use tomcat with localhost enter your ip). this will create .keystore file in your home dir.

  2. Move the .ketstore file to %CATALINA_HOME%/conf and add to server.xml file the following:

     <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
    SSLEnabled="true" maxThreads="150" scheme="https" secure="true"
    clientAuth="false" sslProtocol="TLS" keystoreFile="*%CATALINA_HOME%*\conf\.keystore"
    keystorePass="*yourpass*"/>
  3. Now for the client app you'll have to use bouncy castle because android has a built-in support for the .bks file it creates. It can be downloaded from here:
    http://www.bouncycastle.org/latest_releases.html
    you'll also need openssl to create the certificate bouncy castle needs.
    open console and type :

    openssl s_client -connect *yourhostname*:8443/>cert.pem

    when done open the cert.pem file and delete everything before BEGIN CERTIFICATE and everything after END CERTIFICATE.

  4. to create to .bks file, type in console :

     keytool -import -alias tomcat -file *pathtToCertificate*\cert.pem -keypass *yourPassword* -keystore *pathtToSaveBks*\*nameOfYourKey*.bks -storetype BKS -storepass *yourPassword* -providerClass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath *fullPathToTheBouncyCastleJar*

    put the .bks file in your appproject\res\raw folder (create it if dosn't exist).

  5. this is a basic app that creates a connection via https(credit to Vipul from here: How to create a BKS (BouncyCastle) format Java Keystore that contains a client certificate chain):

    import java.io.InputStream;
    import java.security.KeyStore;
    import org.apache.http.conn.ClientConnectionManager;
    import org.apache.http.conn.scheme.PlainSocketFactory;
    import org.apache.http.conn.scheme.Scheme;
    import org.apache.http.conn.scheme.SchemeRegistry;
    import org.apache.http.conn.ssl.SSLSocketFactory;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.impl.conn.SingleClientConnManager;
    import com.arisglobal.aglite.activity.R;
    import android.content.Context;

    public class MyHttpClient extends DefaultHttpClient{
    final Context context;

    public MyHttpClient(Context context) {
    this.context = context;
    }

    @Override
    protected ClientConnectionManager createClientConnectionManager() {
    SchemeRegistry registry = new SchemeRegistry();

    registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));

    // Register for port 443 our SSLSocketFactory with our keystore to the ConnectionManager
    registry.register(new Scheme("https", newSslSocketFactory(), 443));
    return new SingleClientConnManager(getParams(), registry);
    }

    private SSLSocketFactory newSslSocketFactory() {
    try {
    // Get an instance of the Bouncy Castle KeyStore format
    KeyStore trusted = KeyStore.getInstance("BKS");

    // Get the raw resource, which contains the keystore with your trusted certificates (root and any intermediate certs)
    InputStream in = context.getResources().openRawResource(R.raw.*nameOfYourKey*);
    try {
    // Initialize the keystore with the provided trusted certificates.
    // Also provide the password of the keystore
    trusted.load(in, *yourPassword*.toCharArray());
    } finally {
    in.close();
    }

    // Pass the keystore to the SSLSocketFactory. The factory is responsible for the verification of the server certificate.
    SSLSocketFactory sf = new SSLSocketFactory(trusted);

    // Hostname verification from certificate
    // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
    sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
    return sf;
    } catch (Exception e) {
    throw new AssertionError(e);
    }
    }
    }

    and in your main activity create new thread (network operations cannot be done on the main thread)that it's run method does:

    DefaultHttpClient client = new MyHttpClient(getApplicationContext());
    HttpResponse response = client.execute(https://*yourIP|HostName:8443);

    and start it. if you encounter problems with getApplicationContext() it's because you are calling it in the wrong place. add Context context to your function signature and use it instead of getApplicationContext()

KeyStore with BouncyCastleProvider: KeyStore integrity check failed

Make sure you are using the right password to open the keystore. I was having this error and turns out I was still using the password from the example code in trusted.load()

Java TLS client can't find certificate when accessing soap service

Here's the answer. In my WEB-INF/cxf-servlet.xml file for my WebApp I needed to add this chunk of XML and the associated namespaces. The Java options in code were ignored completely so I removed them. Once I added this and restarted Tomcat everything worked on the first try.

Namespaces:

xmlns:sec="http://cxf.apache.org/configuration/security"
xmlns:http="http://cxf.apache.org/transports/http/configuration"

Elements:

<http:conduit name="*.http-conduit">
<http:tlsClientParameters>
<sec:keyManagers keyPassword="XXX">
<sec:keyStore type="JKS"
password="password"
file="C:/tomcat/certs/tomcat.keystore"/>
</sec:keyManagers>
<sec:trustManagers>
<sec:keyStore type="JKS"
password="password"
file="C:\tomcat\certs\tomcat.truststore"/>
</sec:trustManagers>
</http:tlsClientParameters>
</http:conduit>

Using client/server certificates for two way authentication SSL socket on Android

Android supports certificates in the BKS, P12 and other formats.

For BKS format:
Use portecle to convert your certificates (.p12 and .crt) to .bks.

You need 2 files in your /res/raw folder:
truststore.bks trust certificate for the server (converted from .cer file)

client.bks/client.p12 - the client certificate (converted from a .p12 file that contains the client certificate and the client key)

import java.io.*;
import java.security.KeyStore;

import javax.net.ssl.*;

import org.apache.http.*;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.*;
import org.apache.http.conn.scheme.*;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.*;

import android.app.Activity;
import android.os.Bundle;

public class SslTestActivity extends Activity {

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

try {
// setup truststore to provide trust for the server certificate

// load truststore certificate
InputStream clientTruststoreIs = getResources().openRawResource(R.raw.truststore);
KeyStore trustStore = null;
trustStore = KeyStore.getInstance("BKS");
trustStore.load(clientTruststoreIs, "MyPassword".toCharArray());

System.out.println("Loaded server certificates: " + trustStore.size());

// initialize trust manager factory with the read truststore
TrustManagerFactory trustManagerFactory = null;
trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);

// setup client certificate

// load client certificate
InputStream keyStoreStream = getResources().openRawResource(R.raw.client);
KeyStore keyStore = null;
keyStore = KeyStore.getInstance("BKS");
keyStore.load(keyStoreStream, "MyPassword".toCharArray());

System.out.println("Loaded client certificates: " + keyStore.size());

// initialize key manager factory with the read client certificate
KeyManagerFactory keyManagerFactory = null;
keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, "MyPassword".toCharArray());

// initialize SSLSocketFactory to use the certificates
SSLSocketFactory socketFactory = null;
socketFactory = new SSLSocketFactory(SSLSocketFactory.TLS, keyStore, "MyTestPassword2010",
trustStore, null, null);

// Set basic data
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, "UTF-8");
HttpProtocolParams.setUseExpectContinue(params, true);
HttpProtocolParams.setUserAgent(params, "Android app/1.0.0");

// Make pool
ConnPerRoute connPerRoute = new ConnPerRouteBean(12);
ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);
ConnManagerParams.setMaxTotalConnections(params, 20);

// Set timeout
HttpConnectionParams.setStaleCheckingEnabled(params, false);
HttpConnectionParams.setConnectionTimeout(params, 20 * 1000);
HttpConnectionParams.setSoTimeout(params, 20 * 1000);
HttpConnectionParams.setSocketBufferSize(params, 8192);

// Some client params
HttpClientParams.setRedirecting(params, false);

// Register http/s shemas!
SchemeRegistry schReg = new SchemeRegistry();
schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schReg.register(new Scheme("https", socketFactory, 443));
ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);
DefaultHttpClient sClient = new DefaultHttpClient(conMgr, params);

HttpGet httpGet = new HttpGet("https://server/path/service.wsdl");
HttpResponse response = sClient.execute(httpGet);
HttpEntity httpEntity = response.getEntity();

InputStream is = httpEntity.getContent();
BufferedReader read = new BufferedReader(new InputStreamReader(is));
String query = null;
while ((query = read.readLine()) != null)
System.out.println(query);

} catch (Exception e) {
e.printStackTrace();
}
}

}

Update:

You can also load .crt files for the trust store directly without converting them to BKS:

    private static KeyStore loadTrustStore(String[] certificateFilenames) {
AssetManager assetsManager = GirdersApp.getInstance().getAssets();

int length = certificateFilenames.length;
List<Certificate> certificates = new ArrayList<Certificate>(length);
for (String certificateFilename : certificateFilenames) {
InputStream is;
try {
is = assetsManager.open(certificateFilename, AssetManager.ACCESS_BUFFER);
Certificate certificate = KeyStoreManager.loadX509Certificate(is);
certificates.add(certificate);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

Certificate[] certificatesArray = certificates.toArray(new Certificate[certificates.size()]);
return new generateKeystore(certificatesArray);
}

/**
* Generates keystore congaing the specified certificates.
*
* @param certificates certificates to add in keystore
* @return keystore with the specified certificates
* @throws KeyStoreException if keystore can not be generated.
*/
public KeyStore generateKeystore(Certificate[] certificates) throws RuntimeException {
// construct empty keystore
KeyStore keyStore = KeyStore.getInstance(keyStoreType);

// initialize keystore
keyStore.load(null, null);

// load certificates into keystore
int length = certificates.length;
for (int i = 0; i < length; i++) {
Certificate certificate = certificates[i];
keyStore.setEntry(String.valueOf(i), new KeyStore.TrustedCertificateEntry(certificate),
null);
}
return keyStore;
}

Same goes for the KeyStore with the client certificate, you can use the .p12 file directly without converting it to BKS.



Related Topics



Leave a reply



Submit