Introduction
In my previous blog I showed you how to read a NSS DB with SunPKCS11.. The SunPKCS11 working for both the legacy NSS database format (cert8.db, key3.db, and secmod.db) and the new SQLite format (cert9.db, key4.db, and pkcs11.txt), but SunPKCS11 cannot only read entries in the internal NSS database, not other tokens, such HSM or SmartCard.
To be able to read other tokens in a NSS DB you need to use Network Security Services for Java (JSS) for Java - https://github.com/dogtagpki/jss.
Here in this blog I will use SoftHSM to simulate a real HSM.
SoftHSM v2
References:
Install
# yum install -y softhsm
Configure a new token in SoftHSM named Dogtag and set PIN and SO (Security Officer/Admin) PIN to the first available slot.
# softhsm2-util --init-token --label "Dogtag" --so-pin redhat321 --pin redhat321 --free
Slot 0 has a free/uninitialized token.
The token has been initialized and is reassigned to slot 634761745
List the files for SoftHSM
# ls -ald /var/lib/softhsm
drwxr-x---. 3 ods ods 20 Sep 15 00:53 /var/lib/softhsm
# ls -ald /var/lib/softhsm/tokens
drwxrwx--T. 3 ods ods 50 Sep 15 00:55 /var/lib/softhsm/tokens
# ls -ald /var/lib/softhsm/tokens/372c8761-c4e8-baab-fa9c-6eb4a5d5b211
drwx------. 2 root root 62 Sep 15 00:55 /var/lib/softhsm/tokens/372c8761-c4e8-baab-fa9c-6eb4a5d5b211
# ls -al /var/lib/softhsm/tokens/372c8761-c4e8-baab-fa9c-6eb4a5d5b211/
total 8
drwx------. 2 root root 62 Sep 15 00:55 .
drwxrwx--T. 3 ods ods 50 Sep 15 00:55 ..
-rw-------. 1 root root 8 Sep 15 00:55 generation
-rw-------. 1 root root 0 Sep 15 00:55 token.lock
-rw-------. 1 root root 320 Sep 15 00:55 token.object
Disable p11-kit
Here we are going to disable p11-kit and manually add SoftHSM module to a NSS DB.
Reference: https://pagure.io/freeipa/issue/7810
# rm -f /etc/crypto-policies/local.d/nss-p11-kit.config && update-crypto-policies
# reboot
NSS DB
Now lets create our NSS DB. We are going to use the new format SQLite and will explicitly use the prefix 'sql:' to mark that.
First install nss-tools.
# yum install -y nss-tools
Then create our new NSS DB.
# echo "redhat321" > password.softhsm
# echo "redhat123" > password.internal
# mkdir nssdb_sql_softhsm
# certutil -N -d sql:nssdb_sql_softhsm -f password.internal
Then we manually add the SoftHSM module to that NSS DB.
# rpm -ql softhsm | grep libsofthsm2.so
/usr/lib64/libsofthsm2.so
/usr/lib64/pkcs11/libsofthsm2.so
# modutil -dbdir sql:nssdb_sql_softhsm -add softhsm -libfile /usr/lib64/pkcs11/libsofthsm2.so -force
# modutil -dbdir sql:nssdb_sql_softhsm -add softhsm -libfile /usr/lib64/pkcs11/libsofthsm2.so -force
# certutil -U -d sql:nssdb_sql_softhsm
slot: NSS Internal Cryptographic Services
token: NSS Generic Crypto Services
uri: pkcs11:token=NSS%20Generic%20Crypto%20Services;manufacturer=Mozilla%20Foundation;serial=0000000000000000;model=NSS%203
slot: NSS User Private Key and Certificate Services
token: NSS Certificate DB
uri: pkcs11:token=NSS%20Certificate%20DB;manufacturer=Mozilla%20Foundation;serial=0000000000000000;model=NSS%203
slot: SoftHSM slot ID 0x25d5b211
token: Dogtag
uri: pkcs11:token=Dogtag;manufacturer=SoftHSM%20project;serial=fa9c6eb4a5d5b211;model=SoftHSM%20v2
Now lets create a RSA keypair and self-sign a certificate containing the public key. There are a lot of arguments for that so I copied the help text below, to easier follow.
# certutil -S -x -d sql:nssdb_sql_softhsm -h Dogtag -f password.softhsm -n mkk -s 'CN=MKK, O=MKK Consultancy, C=SE' -k rsa -g 2048 -v 24 -Z SHA256 -t ',,'
...
"certutil: could not change trust on certificate: SEC_ERROR_TOKEN_NOT_LOGGED_IN: The operation failed because the PKCS#11 token is not logged in."
# certutil --help
...
-S Make a certificate and add to database
-n key-name Specify the nickname of the cert
-s subject Specify the subject name (using RFC1485)
-c issuer-name The nickname of the issuer cert
-t trustargs Set the certificate trust attributes (see -A above)
-k key-type-or-id Type of key pair to generate ("dsa", "ec", "rsa" (default))
-h token-name Name of token in which to generate key (default is internal)
-g key-size Key size in bits, RSA keys only (min 512, max 8192, default 2048)
--pss Create a certificate restricted to RSA-PSS (rsa only)
-q pqgfile Name of file containing PQG parameters (dsa only)
-q curve-name Elliptic curve name (ec only)
See the "-G" option for a full list of supported names.
-x Self sign
--pss-sign Sign the certificate with RSA-PSS (the issuer key must be rsa)
-m serial-number Cert serial number
-w warp-months Time Warp
-v months-valid Months valid (default is 3)
-f pwfile Specify the password file
-d certdir Cert database directory (default is ~/.netscape)
-P dbprefix Cert & Key database prefix
-p phone Specify the contact phone number ("123-456-7890")
-Z hashAlg
Specify the hash algorithm to use. Possible keywords:
"MD2", "MD4", "MD5", "SHA1", "SHA224",
"SHA256", "SHA384", "SHA512"
-1 Create key usage extension
-2 Create basic constraint extension
-3 Create authority key ID extension
-4 Create crl distribution point extension
-5 Create netscape cert type extension
-6 Create extended key usage extension
-7 emailAddrs Create an email subject alt name extension
-8 DNS-names Create a DNS subject alt name extension
--extAIA Create an Authority Information Access extension
--extSIA Create a Subject Information Access extension
--extCP Create a Certificate Policies extension
--extPM Create a Policy Mappings extension
--extPC Create a Policy Constraints extension
--extIA Create an Inhibit Any Policy extension
--extSKID Create a subject key ID extension
See -G for available key flag options
--extNC Create a name constraints extension
--extSAN type:name[,type:name]...
Create a Subject Alt Name extension with one or multiple names
- type: directory, dn, dns, edi, ediparty, email, ip, ipaddr,
other, registerid, rfc822, uri, x400, x400addr
--extGeneric OID:critical-flag:filename[,OID:critical-flag:filename]...
Add one or multiple extensions that certutil cannot encode yet,
by loading their encodings from external files.
- OID (example): 1.2.3.4
- critical-flag: critical or not-critical
- filename: full path to a file containing an encoded extension
...
To test it we list the certificates (-L) and the private keys (-K) for the NSS DB.
# certutil -L -d sql:nssdb_sql_softhsm -h all -f password.softhsm
Certificate Nickname Trust Attributes
SSL,S/MIME,JAR/XPI
mkk u,u,u
Dogtag:mkk u,u,u
# certutil -K -d sql:nssdb_sql_softhsm -f password.internal
certutil: Checking token "NSS Certificate DB" in slot "NSS User Private Key and Certificate Services"
certutil: no keys found
# certutil -K -d sql:nssdb_sql_softhsm -h Dogtag -f password.softhsm
certutil: Checking token "Dogtag" in slot "SoftHSM slot ID 0x25d5b211"
< 0> rsa 1e1581006057a890e9a757314893352dc721a959 Dogtag:mkk
# ll nssdb_sql_softhsm/
total 68
-rw-------. 1 root root 28672 Sep 15 01:02 cert9.db
-rw-------. 1 root root 36864 Sep 15 00:59 key4.db
-rw-------. 1 root root 483 Sep 15 00:59 pkcs11.txt
Network Security Services, NSS, for Java (JSS)
So we have now set up our NSS DB with a SoftHSM token Dogtag. Now move onto the Java part with JSS. First install it jss and its dependency.
# yum install -y jss
# vi NSSJSSTool.java
import static java.nio.charset.StandardCharsets.UTF_8;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Arrays;
import java.util.Base64;
import java.util.Enumeration;
import org.mozilla.jss.CryptoManager;
import org.mozilla.jss.crypto.CryptoStore;
import org.mozilla.jss.crypto.CryptoToken;
import org.mozilla.jss.crypto.ObjectNotFoundException;
import org.mozilla.jss.crypto.TokenException;
// NEW project page: https://github.com/dogtagpki/jss
// https://github.com/dogtagpki/jss/blob/master/org/mozilla/jss/tests/KeyStoreTest.java
public class NSSJSSTool {
public static void main(String[] args) throws Exception {
String nssDatabasePath = "/home/magnuskkarlsson/NetBeansProjects/example-nssdb/nssdb_sql_softhsm";
CryptoManager.initialize(nssDatabasePath);
CryptoManager cryptoManager = CryptoManager.getInstance();
for (Enumeration tokens = cryptoManager.getAllTokens(); tokens.hasMoreElements();) {
CryptoToken token = tokens.nextElement();
System.out.println("----------------------------------");
System.out.println(token.getName());
System.out.println("----------------------------------");
CryptoStore store = token.getCryptoStore();
for (org.mozilla.jss.crypto.X509Certificate cert : store.getCertificates()) {
System.out.println(cert.getNickname() + "\t" + cert.getSubjectDN());
org.mozilla.jss.crypto.PrivateKey privateKey = findPrivateKey(store, cert);
if (privateKey != null) {
checkKeys(privateKey, cert.getPublicKey());
}
}
}
}
// loop through all private key to find match
public static org.mozilla.jss.crypto.PrivateKey findPrivateKey(CryptoStore store,
org.mozilla.jss.crypto.X509Certificate cert) throws TokenException, ObjectNotFoundException {
for (org.mozilla.jss.crypto.PrivateKey privateKey : store.getPrivateKeys()) {
// ObjectNotFoundException If the corresponding public key is not found.
java.security.PublicKey publicKey = store.findPublicKey(privateKey);
if (Arrays.equals(cert.getPublicKey().getEncoded(), publicKey.getEncoded())) {
System.out.println(privateKey);
return privateKey;
}
}
return null;
}
public static void checkKeys(java.security.PrivateKey privateKey, java.security.PublicKey publicKey) throws
InvalidKeyException, SignatureException, NoSuchAlgorithmException {
byte[] data = "HELLO WORLD".getBytes(UTF_8);
byte[] signatureBytes = sign(privateKey, data);
boolean verify = verify(publicKey, data, signatureBytes);
}
public static byte[] sign(java.security.PrivateKey privateKey, byte[] data) throws NoSuchAlgorithmException,
InvalidKeyException, SignatureException {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(data);
byte[] signatureBytes = signature.sign();
String signatureEncoded = Base64.getEncoder().encodeToString(signatureBytes);
System.out.println(signatureEncoded);
return signatureBytes;
}
public static boolean verify(java.security.PublicKey publicKey, byte[] data, byte[] signatureBytes) throws
NoSuchAlgorithmException, InvalidKeyException, SignatureException {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(data);
boolean verify = signature.verify(signatureBytes);
System.out.println("verify: " + verify);
return verify;
}
}
Now lets compile it and run it.
# javac -cp /usr/lib64/jss/jss4.jar:/usr/share/java/slf4j/slf4j-api.jar:/usr/share/java/slf4j/slf4j-simple.jar NSSJSSTool.java
# java -cp .:/usr/lib64/jss/jss4.jar:/usr/share/java/slf4j/slf4j-api.jar:/usr/share/java/slf4j/slf4j-simple.jar NSSJSSTool
[main] INFO org.mozilla.jss.CryptoManager - CryptoManager: loading JSS library
[main] INFO org.mozilla.jss.CryptoManager - CryptoManager: loaded JSS library from /usr/lib64/jss/libjss4.so
[main] INFO org.mozilla.jss.CryptoManager - CryptoManager: initializing NSS database at /root/nssdb_sql_softhsm
----------------------------------
NSS Generic Crypto Services
----------------------------------
----------------------------------
Internal Key Storage Token
----------------------------------
mkk CN=MKK,O=MKK Consultancy,C=SE
Enter password for Internal Key Storage Token
----------------------------------
Dogtag
----------------------------------
Enter password for Dogtag
Dogtag:mkk CN=MKK,O=MKK Consultancy,C=SE
org.mozilla.jss.pkcs11.PK11RSAPrivateKey@736e9adb
KaFqQUy+mv9nSrEkoIP/8rqG7D2bgXD9R48xw40eP/Sly4dkblSsVyBxIuZ1N89TLbFNoAQZVdP9T0sosPeZmiYOJOrxD6PMDRQoBzXsXLT1guFv6rBshV3FfcCEbdBOYKay8oEmxrg9Ks7/3PujwE0SwufPEtIqEReNFBjp4kQlegmBEGKYp+3lkMB5006kAoM66YlI8Rr68oBzTaf5fFXmIUFjPJJ1U+LiNWU/54qxEOsqePZxms9l1lU8uB01x5GV5r0sSXNPA2Nug3pQKhbVILJfNXXxqHmSKqGAGHgOPOMgiqlqtrNoqjXraZegXSg5RdvVi588FXS/tji9oA==
verify: true