Java Cryptography Extension (JCE) Provider

Updated: Nov 30, 2017

Download

The SmartKey JCE Provider for all platforms can be downloaded here.

Installation

  • Move the Provider jar file smartkey-jce-provider.jar to ${JAVA_HOME}/jre/lib/ext directory

  • Add SdkmsJCE Provider to the list of providers in the ${JAVA_HOME}/jre/lib/security/java.security file. An example java.security file would look like
      security.provider.1=sun.security.provider.Sun
      security.provider.2=sun.security.rsa.SunRsaSign
      security.provider.3=sun.security.ec.SunEC
      security.provider.4=com.sun.net.ssl.internal.ssl.Provider
      security.provider.5=com.fortanix.sdkms.jce.provider.SdkmsJCE
      security.provider.6=com.sun.crypto.provider.SunJCE
      security.provider.7=sun.security.jgss.SunProvider
      security.provider.8=com.sun.security.sasl.Provider
      security.provider.9=org.jcp.xml.dsig.internal.dom.XMLDSigRI
      security.provider.10=sun.security.smartcardio.SunPCSC
    
  • Apply Unlimited Strength Jurisdiction Policy Files by downloading the Policy file from the Java website. Extract the downloaded zip file, and copy the following two files to ${JAVA_HOME}/jre/lib/security directory.
    • local_policy.jar
    • US_export_policy.jar

Configuration

For the JCE Provider to connect to SmartKey backend, it needs to know the server URL it can talk to, and an API key of an application that is used for authentication of the application. These can be set as environment variables. For example:

    export FORTANIX_API_ENDPOINT=https://smartkey.io
    export FORTANIX_API_KEY=<your API key>

Keytool and Keystores

keytool utility can now use the SDKMS JCE provider for management of key pairs and certificates which are backed by SmartKey service. SDKMS JCE supports two types of keystores:

1. SDKMS-local

This KeyStore can be used by clients who expect more-or-less JKS (the default keystore) semantics. All metadata will be stored locally and imported to SmartKey when keystore.store is called.

Usage

Flags: -storetype SDKMS-local -providerName sdkms-jce -storepass passwd -keypass passwd
  • Generate Asymmetric keys
keytool -genkeypair -alias alias -keyalg RSA -keystore keystore_file -keysize 1024 -providerName sdkms-jce -storetype SDKMS-local -storepass passwd -keypass passwd -dname "CN=fortanix,OU=fortanix,O=fortanix,L=Mountain View,ST=California,C=US"
  • Import Certificate
keytool -importcert -trustcacerts -alias alias -file certificate-path -keystore keystore_file -providerName sdkms-jce -storetype SDKMS-local -storepass passwd -noprompt -trustcacerts
  • Import Keystore
keytool -importkeystore -srcstoretype SDKMS-local -deststoretype SDKMS-local -srcalias alias -srcProviderName sdkms-jce -destProviderName sdkms-jce -srckeystore source_keystore_file -destkeystore dest_keystore_file -srcstorepass passwd -deststorepass passwd -srckeypass passwd -destkeypass passwd
  • Generate AES Symmetric Keys
keytool -genseckey -alias alias -keyalg AES -keysize 256 -storepass passwd -keypass passwd -keystore keystore_file -providerName sdkms-jce -storetype SDKMS-local
  • List
keytool -list -v -keystore keystore_file -providerName sdkms-jce -storetype SDKMS-local -storepass passwd
  • Delete
keytool -delete -alias alias -keystore keystore_file -providerName sdkms-jce -storetype SDKMS-local -storepass passwd

2. SDKMS

This KeyStore can be used when a user needs to interact with SmartKey directly without storing any metadata locally. SmartKey Groups is an abstraction for different keystore for a user, hence a groupId will be used while storing and retrieving the keys for this keystore type.

Usage

Flags: `-storetype SDKMS -providerName sdkms-jce -keypass <groupId> -storepass <groupId>`
  • Import Certificate
keytool -importcert -trustcacerts -alias alias -file certificate-path -keystore keystore_file -providerName sdkms-jce -storetype SDKMS -storepass 2ff36949-ee70-4145-bd57-7de1ada5c050 -noprompt -trustcacerts
  • Generate AES Symmetric Keys
keytool -genseckey -alias alias -keyalg AES -keysize 256 -storepass 2ff36949-ee70-4145-bd57-7de1ada5c050 -keypass 2ff36949-ee70-4145-bd57-7de1ada5c050 -keystore keystore_file -providerName sdkms-jce -storetype SDKMS
  • List the above generated AES Key
keytool -list -v -keystore keystore_file -providerName sdkms-jce -storetype SDKMS -storepass 2ff36949-ee70-4145-bd57-7de1ada5c050
  • Delete the above AES Key
keytool -delete -alias alias -keystore keystore_file -providerName sdkms-jce -storetype SDKMS -storepass 2ff36949-ee70-4145-bd57-7de1ada5c050

Java Examples

Generate RSA Keys

SdkmsJCE provider = SdkmsJCE.getInstance();
String algorithm = AlgorithmParameters.RSA;
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(algorithm, provider);
keyGenerator.initialize(2048);
KeyPair rsaKeyPair = keyGenerator.generateKeyPair();

RSA Sign and Verify

...
String algorithm = AlgorithmParameters.RSA;
KeyPair rsaKeyPair = keyGenerator.generateKeyPair();
Signature sig = Signature.getInstance(algorithm, provider);
sig.initSign(rsaKeyPair.getPrivate());

// sign
byte[] data = "testData".getBytes("UTF8");
sig.update(data);
byte[] signatureBytes = sig.sign();

//verify
sig.initVerify(keyPair.getPublic());
sig.update(data);
assertEquals(true, sig.verify(signatureBytes));

Generate AES Keys

SdkmsJCE provider = SdkmsJCE.getInstance();
String algorithm = AlgorithmParameters.AES;
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(algorithm, provider);
keyGenerator.initialize(256);
SecretKey aesKey = keyGenerator.generateKey();

AES Cipher Encryption and Decryption

...
String algorithm = AlgorithmParameters.AES;
String mode = CipherMode.ECB.toString();
String padding = ProviderConstants.PKCS5PADDING

SecretKey secretKey = keyGenerator.generateKey();

Cipher cipher = Cipher.getInstance(algorithm, provider);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);

String PLAIN = "testData";
// encryption
byte[] cipherBytes = cipher.doFinal(PLAIN.getBytes());

// decryption
cipher.init(Cipher.DECRYPT_MODE, key, params);
byte[] plainBytes = cipher.doFinal(cipherBytes);

// verify
assertEquals(new String(plainBytes), PLAIN);

Storing RSA Key in KeyStore

KeyStore keyStore = KeyStore.getInstance("SDKMS", provider); // here can you either use SDKMS or sdkms-local as provider 

keyStore.load(null, null);
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA", provider);
gen.initialize(2048);
KeyPair keyPair = gen.generateKeyPair();
keyStore.load(null, null);
keyStore.setKeyEntry(alias, keyPair.getPrivate(), SDKMS_GROUPID.toCharArray(), null);

Storing Secret Key in KeyStore

KeyGenerator gen = KeyGenerator.getInstance("AES", provider);
gen.init(128);
Key key = gen.generateKey();
keyStore.load(null, null);
keyStore.setKeyEntry(alias, key, SDKMS_GROUPID.toCharArray(), null);

Storing Certificate in KeyStore

...
 CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
 InputStream certificateInputStream = new FileInputStream("certificate.crt");
 Certificate certificate = certificateFactory.generateCertificate(certificateInputStream);
 keyStore.setCertificateEntry("certName", cert);

Listing Alias of KeyStore

...
Enumeration<String> keys = keyStore.aliases();
while (keys.hasMoreElements()) {
    keys.nextElement(); 
}

Deleting an Alias from KeyStore

...
keyStore.deleteEntry(alias);

Creating KeyStore for SSL/TLS

FileInputStream keyInputStream = new FileInputStream(PRIVATE_KEY_PATH);
byte[] keyBytes = new byte[keyInputStream.available()];
keyInputStream.read(keyBytes);

keyInputStream.close();

String privateKey = new String(keyBytes, "UTF-8");
privateKey = privateKey.replaceAll("(-+BEGIN PRIVATE KEY-+\\r?\\n|-+END PRIVATE KEY-+\\r?\\n?)", "");

BASE64Decoder decoder = new BASE64Decoder();
keyBytes = decoder.decodeBuffer(privateKey);

PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA", provider);
PrivateKey pk = kf.generatePrivate(privKeySpec);

CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
FileInputStream certInputStream = new FileInputStream(CERTIFICATE_PATH);
Certificate cert = certFactory.generateCertificate(certInputStream);
Certificate[] chain = new Certificate[1];

KeyStore keyStore = KeyStore.getInstance("SDKMS-local", provider);
keyStore.load(null,null);
keyStore.setKeyEntry(TLS_CLIENT_KEY_NAME, pk, null, chain);

OutputStream stream = new FileOutputStream(TLS_KEYSTOR_PATH);
keyStore.store(stream, null);

Setting SSL/TLS context

KeyStore keyStore = KeyStore.getInstance("SDKMS-local", provider);
InputStream inputStream = new FileInputStream(TLS_KEYSTORE_PATH);
keyStore.load(inputStream,null);
Enumeration<String> aliases = keyStore.aliases();
final String alias = aliases.nextElement();

// set ssl context
SSLContext sslContext = SSLContexts.custom()
    .loadKeyMaterial(keyStore, null, new PrivateKeyStrategy() {
        public String chooseAlias(Map<String, PrivateKeyDetails> aliases, Socket socket) {
            return alias;
        }
    }).loadTrustMaterial(null, new TrustStrategy() {
        public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            return true;
        }
    })
    .build();

SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
    new String[]{"TLSv1.2", "TLSv1.1"},
    null,
    SSLConnectionSocketFactory.getDefaultHostnameVerifier());

CloseableHttpClient client = HttpClients.custom()
    .setSSLSocketFactory(sslConnectionSocketFactory)
    .build();

HttpGet httpget = new HttpGet(TLS_ENDPOINT);

CloseableHttpResponse response = client.execute(httpget);
HttpEntity entity = response.getEntity();