Clients: Java Cryptography Extension (JCE) Provider

Download

The Fortanix Data Security Manager (DSM) JCE Provider for all platforms can be downloaded here.

Installation

System wide install

  • Move the downloaded bundled provider jar file sdkms-jce-provider-bundled-x.xx.xxxx.jar to ${JAVA_HOME}/jre/lib/ext directory

  • 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
  • Add the provider to your code:
    import com.fortanix.sdkms.jce.provider.SdkmsJCE;

    SdkmsJCE provider = new SdkmsJCE();
    Security.addProvider(provider);
  • Alternatively, the provider can be added in ${JAVA_HOME}/jre/lib/security/java.security file, as the last provider in the list. This will allow integration with non program based usage, eg. keytool, jarsigner etc. 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.sun.crypto.provider.SunJCE
    security.provider.6=sun.security.jgss.SunProvider
    security.provider.7=com.sun.security.sasl.Provider
    security.provider.8=org.jcp.xml.dsig.internal.dom.XMLDSigRI
    security.provider.9=sun.security.smartcardio.SunPCSC
    security.provider.10=com.fortanix.sdkms.jce.provider.SdkmsJCE

Maven based install

  • This option is available starting 3.19.1352 version.
  • Add dependency to JCE provider in the pom definition xml:
    <dependency>
    <groupId>com.fortanix</groupId>
    <artifactId>sdkms-jce-provider</artifactId>
    <version>x.xx.xxxx</version>
    </dependency>
  • Add the provider to your code:
    import com.fortanix.sdkms.jce.provider.SdkmsJCE;

    SdkmsJCE provider = new SdkmsJCE();
    Security.addProvider(provider);

Configuration

For the JCE Provider to connect to Fortanix DSM, one needs to provide the server URL and an API key of an application that is used for authentication. These can be set as environment variables. For example:

export FORTANIX_API_ENDPOINT=https://sdkms.fortanix.com
export FORTANIX_API_KEY=<your API key>

Certificated based authentication (mTLS)

Alternatively, the application can authenticate using Client certificate instead of API Key for Mutual TLS verified connection. See how to set Certificate based auth in Fortanix DSM in the Authentication Guide

If the client private key and certificate is in PEM format (provided by your CA or openssl), convert it into Java keystore format.

openssl pkcs12 -export -in client-cert.pem -inkey client-key.pem -name "my-sdkms-app" -out client-sdkms.p12

Note: this commands asks for password to be set for the local keystore. Provide some password in this sample, say PASSWORD.

Provide the above generated keystore as a JVM argument to your Java program

java -Djavax.net.ssl.keyStoreType=pkcs12 -Djavax.net.ssl.keyStore=client-sdkms.p12 -Djavax.net.ssl.keyStorePassword=PASSWORD MyJCEProgram.java

Or programatically set as System properties:

System.setProperty("javax.net.ssl.keyStoreType", "pkcs12");
System.setProperty("javax.net.ssl.keyStore", <path to client-sdkms.p12 file>);
System.setProperty("javax.net.ssl.keyStorePassword, "PASSWORD");

Java tools like keytooland jarsigner takes JVM arguments as following:

jarsigner -J-Djavax.net.ssl.keyStoreType=pkcs12 -J-Djavax.net.ssl.keyStore=client-sdkms.p12 -J-Djavax.net.ssl.keyStorePassword=PASSWORD ...

Custom CA configuration

For on-premise Fortanix DSM installations, one may need to add the Organization CA to truststore for successful TLS communication with API.

Create a java trustsore which saves the CA certificate (PEM format)

keytool -import -alias SDKMS_CA -file sdkms-root-ca.crt -keystore sdkms-truststore.jks -deststorepass PASSWORD

Provide the truststore as a JVM argument to your Java program

java -Djavax.net.ssl.trustStoreType=jks -Djavax.net.ssl.trustStore=sdkms-truststore.jks - Djavax.net.ssl.trustStorePassword=PASSWORD MyJCEProgram.java

Or programatically set as System properties:

System.setProperty("javax.net.ssl.trustStoreType", "jks");
System.setProperty("javax.net.ssl.trustStore", <path to sdkms-truststore.jks file>);
System.setProperty("javax.net.ssl.trustStorePassword, "PASSWORD");

Java tools like keytooland jarsigner takes JVM arguments as following:

jarsigner -J-Djavax.net.ssl.trustStoreType=jks -J-Djavax.net.ssl.trustStore=client-sdkms.p12 -J-Djavax.net.ssl.trustStorePassword=PASSWORD ...

Supported Algorithms

Operation Algorithm Key Size (bits) Mode Padding Mode
Cipher AES 128, 192, 256 ECB, CBC NOPADDING, PKCS5PADDING
Cipher AES 128, 192, 256 CTR, CFB, GCM PKCS5PADDING
Cipher DES 56 ECB, CBC NOPADDING, PKCS5PADDING
Cipher DESede 168 ECB, CBC NOPADDING, PKCS5PADDING
Cipher TripleDES 168 ECB, CBC NOPADDING, PKCS5PADDING
Cipher RSA 1024, 2048 ECB PKCS5PADDING
KeyGenerator AES 128, 192, 256    
KeyGenerator DES 56    
KeyGenerator DESede 168    
Keygenerator HmacSHA1      
Keygenerator HmacSHA256      
Keygenerator HmacSHA384      
Keygenerator HmacSHA512      
KeyPairGenerator RSA 1024, 2048, 4196, 8192    
KeyPairGenerator EC   SecP192K1, SecP224K1, SecP256K1, NistP-192, NistP-224, NistP-256, NistP-384, NistP-512, Ed25519  
Signature RSA with SHA   SHA1withRSA, SHA256withRSA, SHA384withRSA, SHA512withRSA  
Signature EC with SHA   SHA1withECDSA, SHA256withECDSA, SHA384withECDSA, SHA512withECDSA  

For a full list of supported algorithms refer to Algorithm Support.

Connection Pooling

Fortanix DSM version 3.21 and above supports a new feature called Connection Pooling.

Connection pooling allows restriction and reuse of connections with a maximum limit specified.
This allows setting some safe limits on each JCE application so that no single application
can overwhelm the server.

With JCE Connection Pooling

The environment variable FORTANIX_CONN_MAX is set to the maximum number of connections from that instance of the JCE application.

Without JCE Connection Pooling

When the environment variable FORTANIX_CONN_MAX is not exported or is set to `0`, JCE will behave without any connection pooling/limit. This is similar to JCE behavior prior to version 3.21.

Scenarios

  • FORTANIX_CONN_MAX = 0.
    • Existing behavior: number of sockets is equal to the number of concurrent threads.
  • FORTANIX_CONN_MAX = X, Concurrent threads less than X.
    • Behavior: Less than X sockets open at a time.
    • Observation: The sockets are also being reused.
  • FORTANIX_CONN_MAX = X, Concurrent threads greater than X.
    • Behavior: Maximum X sockets open with reuse.
    • Observation: higher latency, which is expected since threads are now waiting for connections to get free.

Logging

With the 3.21 release, by default the logging option is disabled, and in order to enable it export the following environment variables:

  • To enable debug logs, set the environment variable:
    export FORTANIX_LOG_DEBUG=true
  • To enable only API logs, set the environment variable:
    export FORTANIX_LOG_API=true
  • To set a file location for local logs, set the environment variable:
    export FORTANIX_LOG_FOLDER="/path/to/logfile-folder"
    This will create a log file /path/to/logfile-folder/sdkms-jce.log

Keytool and KeyStores

The keytool utility can now use the Fortanix DSM JCE provider for the management of key pairs and certificates which are backed by Fortanix DSM service. Fortanix DSM 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 Fortanix DSM when keystore.store is called.

Usage

keytool <operations> -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 Fortanix DSM directly without storing any metadata locally. The Fortanix DSM 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

keytool <operation> -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();

Comments

Please sign in to leave a comment.

Was this article helpful?
0 out of 0 found this helpful