September 15, 2019

Reading NSS DB with SoftHSM Token with Java JSS

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

No comments: