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

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

  2. Apply Unlimited Strength Jurisdiction Policy Files by downloading the Policy file from the Java website. Extract the downloaded zip file, and copy the following files to the ${JAVA_HOME}/jre/lib/security directory.
    • local_policy.jar
    • US_export_policy.jar
  3. 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, for example, keytool, jarsigner, and so on.
    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

NOTE
This option is available starting from 3.19.1352 version.

Maven projects define dependencies within the pom.xml file in source under the <dependencies> tag. This file is located in the base directory of the maven project.

  1. Add the following dependency to JCE provider in the pom.xml file:
    <dependency>
    <groupId>com.fortanix</groupId>
    <artifactId>sdkms-jce-provider</artifactId>
    <version>x.xx.xxxx</version>
    </dependency>
    For more information on the detailed project dependencies of the JCE provider, refer to the pom.xml files attached to the article.
  2. Add the following 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 a Client Certificate instead of an API key for Mutual TLS verified connection. For more information on certificate based authentication, refer to User's Guide: Authentication.

If the client's private key and certificate are in PEM format (provided by your CA or openssl), convert it into Java KeyStore (JKS) format.

openssl pkcs12 -export -in client-cert.pem -inkey client-key.pem -name "my-sdkms-app" -out client-sdkms.p12
NOTE
This command asks for a password to be set for the local KeyStore. Enter any password of your choice, for example, '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 programmatically 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 take JVM arguments as follows:

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 TrustStore 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 programmatically 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 take JVM arguments as follows:

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, NOPADDING
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,OAEPPADDING
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  

Signature

DSA with SHA  

SHA1withDSA,
SHA256withDSA,
SHA384withDSA,
SHA512withDSA

 

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.

Connection Pooling Keep-Alive

The environment variable FORTANIX_CONN_KEEPALIVE is a configurable property that is used to keep the duration of a live connection open for reuse in case of subsequent requests.

The default value is 5000 milliseconds (5 seconds) in case it is not configured.

Configuration

To configure keep-alive, declare it as a system environment variable. For example,

export FORTANIX_CONN_KEEPALIVE=4000  

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 the Fortanix DSM service.

Fortanix DSM JCE supports two types of keystores SDKMS-Local and SDKMS:

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

SDKMS

The Fortanix DSM groups are an abstraction for different KeyStores for a user, hence you can list all the keys stored in a particular group using the Java KeyStore. In order to do that, you need to first create a non-empty file locally, and then pass the group-id as the KeyStore password in the keytool command.

For example:
  • Export the FORTANIX_API_KEY and the FORTANIX_API_ENDPOINT environment variables in the command line.
  • Create a non-empty file (say keystore1) locally.
  • Pass the filename in the -keystore option and the group-id as the store password in the keytool command.
    keytool -list -v -keystore keystore1 -providerName sdkms-jce -storetype SDKMS -storepass d7e89ac7-f0dc-49d1-9196-75aaf6afc47c

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

Jarsigner

To generate an entity's signature for a file, the entity must first have a public/private key pair associated with it and one or more certificates that authenticate its public key. The jarsignercommand uses key and certificate information from a keystore to generate digital signatures for JAR files. For more details, refer to the Oracle jarsigner documentation.

Syntax

jarsigner -keystore <keystore_file> -storepass <storepassword> <filenameTosigned> <alias> 

Examples

jarsigner -keystore $KEYSTORE_PATH -storepass $JCE_SIGNING_PASSWD $JARFILE $ALIAS 
jarsigner -keystore $KEYSTORE -providerName $PROVIDER_NAME -sigalg SHA256withRSA -storetype $TYPE -storepass $PASSWD $RESOURCES_PATH"/JCETest-0.0.1.jar" jcetest 
jarsigner -verify $RESOURCES_PATH"/JCETest-0.0.1.jar" -providerName $PROVIDER_NAME -sigalg SHA256withRSA 

Environment Setup

Set the environment variables FORTANIX_API_ENDPOINT  and FORTANIX_API_KEY using the following commands:

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

Add the sdkms-jce-provider JAR inside the folder $JAVA_HOME/jre/lib/ext/ .

You can generate a private key and its public certificate using the keytool command as shown below. This command will generate the file  keystore1 locally and a private key named jcetest in Fortanix DSM
You must provide the Fortanix DSM Group ID as the storepass option.

keytool -genkey -alias jcetest -keyalg RSA -keystore keystore1 -keysize 1024 -providername sdkms-jce -storetype SDKMS -storepass d7e89ac7-f0dc-49d1-9196-75aaf6afc47c 

Sign the JAR using the following command:

jarsigner -keystore keystore1 -providerName sdkms-jce -sigalg SHA256withRSA -storetype SDKMS -storepass d7e89ac7-f0dc-49d1-9196-75aaf6afc47c a.jar jcetest

Jarsigner Using Existing Keys

To sign the JAR using existing keys, import the existing private key along with its certificate chain into Fortanix DSM using the sdkms-cli tool, and then use the private key alias for signing the JAR.

Java Examples

Generate RSA Keys

//Generate RSA Keys:
SdkmsJCE provider = SdkmsJCE.getInstance();
String algorithm = AlgorithmParameters.RSA;
KeyPairGenerator kpg = KeyPairGenerator.getInstance(algorithm, provider);
SecurityObjectParameterSpec parameterSpec = new SecurityObjectParameterSpec(false);
kpg.initialize(keySize);
kpg.initialize(parameterSpec, null);
KeyPair keyPair = kpg.genKeyPair();
 

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 = "test".getBytes("UTF8");
Signature sig = Signature.getInstance(“SHA256withRSA”, provider);
sig.initSign(keyPair.getPrivate());
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();

Import DSA Keys

import com.fortanix.sdkms.jce.provider; 
import java.security.*;
import java.security.spec.DSAGenParameterSpec;

Generate DSA Keys

int keySize = 2048; 
int subGroupSize = 224;
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA", provider);
DSAGenParameterSpec dsaGenParameterSpec = new DSAGenParameterSpec(keySize, subGroupSize);
SecurityObjectParameterSpec parameterSpec = new SecurityObjectParameterSpec(dsaGenParameterSpec, false);
kpg.initialize(parameterSpec, null);
KeyPair keyPair = kpg.genKeyPair();

DSA Sign

byte[] data = "test".getBytes("UTF8");
Signature sig = Signature.getInstance("SHA1withDSA", provider);
sig.initSign(keyPair.getPrivate());
sig.update(data);
byte[] signatureBytes = sig.sign();

DSA Verify

sig.initVerify(keyPair.getPublic());
sig.update(data);

assertNotNull(sig);
assertEquals(true, sig.verify(signatureBytes));

Comments

Please sign in to leave a comment.

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