Skip to main content

Login Sample

This Java code is a sample implementation for logging into the Securosys Primus Hardware Security Module (HSM) using the Java Cryptography Extension (JCE) provider. The code demonstrates the process of installing the Primus JCE provider, configuring it with HSM details, logging in with a setup password, fetching and blinding a permanent secret, writing the blinded secret to a file, and finally, logging in again using the file-based credentials.

note

This and the remaining samples assume direct connection to the HSM without reverse proxy. If you are using Securosys CloudHSM, other HSM as a service using reverse proxy, or your own reverse proxy, you will need to apply the lesson from CloudHSMSample in the remaining samples.


/*
* Copyright (C) 2015-2025, Securosys SA
*/

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.security.Security;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import com.securosys.primus.jce.PrimusBlinding;
import com.securosys.primus.jce.PrimusConfiguration;
import com.securosys.primus.jce.PrimusDevice;
import com.securosys.primus.jce.PrimusLogin;
import com.securosys.primus.jce.PrimusProvider;

/**
* Login sample for the Securosys Primus HSM JCE provider.
*/
public class LoginWriteSample {

public static void main(final String... args) throws Exception {

System.out.println("Securosys Primus HSM JCE provider login sample");

// HSM configuration

String host; // HSM host name, or address, or cluster HSM list
String port; // HSM TCP port
String user; // HSM partition/user
char[] password; // HSM partition/user login password, setup password, or permanent secret, or blinded version

String proxyuser = null; // CloudHSM service username, optional
char[] proxypassword = null; // CloudHSM service password, optional

// // HSM credentials, provided by HSM operator; in case of development program or CloudHSM by Securosys support
// user = "PRIMUSDEV999"; // user/partition
// // password not as a String, as not in string pool, as that can't be cleared after use
// password = ...;

// // cluster configurations
// // host = "ch01-api.cloudshsm.com,ch02-api.cloudshsm.com"; // loadbalancing: random, unweighted
// // host = "ch01-api.cloudshsm.com(.8),ch02-api.cloudshsm.com(.2)"; // loadbalancing: random, weighted
// // host = "ch01-api.cloudshsm.com;ch02-api.cloudshsm.com"; // no loadbalancing, only failover, prioritized
// // port = "2300";

host = "ch01-api.cloudshsm.com,ch02-api.cloudshsm.com"; // internal test/development HSM, not externally accessible
port = "2300";
user = "HSM_USER";

// setup password, will expire
password = "SETUP_PASSWORD".toCharArray();

// permanent secret, as also fetched further below
/*
password = "694d63f8e0b5116432a2307a552b0eeebd0b86b8960d91b0ba9ebb1d65897825".toCharArray();
*/

// for test setup convenience: be configurable
host = System.getProperty("host", host);
port = System.getProperty("port", port);
user = System.getProperty("user", user);
password = System.getProperty("password", (password == null ? "null" : new String(password))).toCharArray();
proxyuser = System.getProperty("proxyuser", proxyuser);
proxypassword = System.getProperty("proxypassword", (proxypassword == null ? "null" : new String(proxypassword))).toCharArray();

// install (load) Primus JCE provider (PrimusProvider) [JVM wide], and configure it with HSM address/port/user [thread local], and log in to HSM [JVM wide]

System.out.println("installing the Securosys Primus HSM JCE provider");
// instantiate Primus JCE provider
final PrimusProvider primusProvider = new PrimusProvider();
// install/register it
Security.addProvider(primusProvider);

System.out.println("Primus HSM JCE provider information: " + Security.getProvider(PrimusProvider.getProviderName()));
System.out.println("Primus HSM JCE provider version: " + Security.getProvider(PrimusProvider.getProviderName()).getVersion());

System.out.println("configuring HSM host and port: " + host + "/" + port + "/" + user);
// configure HSM to use
// PrimusConfiguration.setUseThreadLocalHsmConfiguration(false); // have the config on all threads
PrimusConfiguration.setHsmHost(host, port, user);

// optionally configure CloudHSM Primus proxy credentials
if (proxyuser != null) {
PrimusConfiguration.setProxyCredentials(proxyuser, proxypassword);
Arrays.fill(proxypassword, '*');
}

System.out.println("login state: " + PrimusLogin.isLoggedIn());

System.out.println("logging in with setup password");
// login with setup password (that will eventually expire)
PrimusLogin.login(user, password);
Arrays.fill(password, '*');

System.out.println("login state: " + PrimusLogin.isLoggedIn());

String deviceName = PrimusDevice.getDeviceName(); // e.g. "A-HM01"
String versionInfo = PrimusDevice.getVersionInfo(); // e.g. "RX-2.5.0-D"
System.out.println("device name: " + deviceName);
System.out.println("version info: " + versionInfo);

System.out.println("fetching and blinding permanent secret");
// fetch and blind permanent secret
final char[] usersecret = PrimusLogin.getUserSecretChars();
// blind the usersecret; each blinding will be different, as there's a random nonce involved
final char[] blindedusersecret = PrimusBlinding.blindChars(usersecret, PrimusBlinding.BlindingAlgorithm.AES);
Arrays.fill(usersecret, '*'); // clear usersecret memory

System.out.println("logging out");
PrimusLogin.logout();

System.out.println("login state: " + PrimusLogin.isLoggedIn());

System.out.println("writing blinded permanent secret to file");
final String file = ".secret";
// with owner-only-permissions: best effort only -- not all OSes may cope with this
final Path path = Paths.get(file);
Files.deleteIfExists(path);
try {
// set permissions/umask
final Set<PosixFilePermission> permissions = new HashSet<PosixFilePermission>();
permissions.add(PosixFilePermission.OWNER_READ);
permissions.add(PosixFilePermission.OWNER_WRITE);
final FileAttribute<?> ownerOnlyPermissions = PosixFilePermissions.asFileAttribute(permissions);
Files.createFile(path, ownerOnlyPermissions);
} catch (final UnsupportedOperationException e) {
// try without file permissions -- for the less gifted OSes
Files.createFile(path);
}
final ByteBuffer bb = StandardCharsets.UTF_8.encode(CharBuffer.wrap(blindedusersecret));
Arrays.fill(blindedusersecret, '*');
final byte[] b = new byte[bb.remaining()];
bb.get(b);
Arrays.fill(bb.array(), (byte)0);
Files.write(path, b);
Arrays.fill(b, (byte)0);

System.out.println("logging in by file");
PrimusLogin.login(user, ("file:" + file).toCharArray());
try { // guaranteed logout

System.out.println("login state: " + PrimusLogin.isLoggedIn());

deviceName = PrimusDevice.getDeviceName(); // e.g. "GRIMSEL"
versionInfo = PrimusDevice.getVersionInfo(); // e.g. "RX-2.5.0-D"
System.out.println("device name: " + deviceName);
System.out.println("version info: " + versionInfo);

} finally { // guaranteed logout
System.out.println("logging out");
PrimusLogin.logout();
}

System.out.println("login state: " + PrimusLogin.isLoggedIn());

System.out.println("sample done");
}

}

Here is a breakdown of the key functionalities:

  1. HSM Configuration:

    • The user specifies the HSM host, TCP port, user/partition, and login credentials (setup password) for authentication.
    • Optional configurations include proxy user and password for CloudHSM service.
  2. Primus JCE Provider Installation:

    • The Primus JCE provider is instantiated (PrimusProvider) and registered with the Java Security framework (Security.addProvider(primusProvider)).
  3. HSM Configuration and Login:

    • HSM host, port, and user are configured using PrimusConfiguration.setHsmHost.
    • Optionally, CloudHSM proxy credentials are configured.
    • The login state is checked, and the setup password is used for the initial login.
    • Information about the device (HSM) is retrieved, such as the device name and version.
  4. Fetching and Blinding Permanent Secret:

    • The permanent secret is fetched using PrimusLogin.getUserSecretChars.
    • The fetched secret is blinded using the AES blinding algorithm (PrimusBlinding.blindChars).
  5. Logging Out:

    • The user logs out from the HSM using PrimusLogin.logout.
  6. Writing Blinded Permanent Secret to File:

    • The blinded permanent secret is written to a file with owner-only permissions.
    • File permissions are set to read and write for the owner only.
    • The blinded secret is encoded to bytes and written to the file using Files.write.
  7. Logging In Again Using File-based Credentials:

    • The user logs in again using the file-based credentials.
    • Device information is retrieved again after successful login.
  8. Logging Out Again:

    • The user logs out again to ensure a clean exit.
  9. Sample Completion:

    • The sample concludes, and a message is printed indicating the completion of the process.

The code is heavily commented, providing detailed explanations for each step. It showcases various operations related to HSM interaction, user authentication, and secure handling of credentials.