September 20, 2019

Reading NSS DB from Java 11 with SunPKCS11

Introduction

In my previous blog (Reading NSS DB from Java 8 with SunPKCS11) I showed how to read a NSS DB from Java 8 with SunPKCS11. In Java 11 a lot of internal packages is no longer visible and you will get compilation error if you try to access them. The same is true for sun.security.pkcs11.SunPKCS11, since it is a sun class. But SunPKCS11 is still available. What they have done is to make SunPKCS11 always available, i.e. you do not need to add, i.e. Security.addProvider(provider);

Java 8


String configName = "/home/magnuskkarlsson/NetBeansProjects/example-nssdb/pkcs11.cfg";
sun.security.pkcs11.SunPKCS11 provider = new sun.security.pkcs11.SunPKCS11(configName);
Security.addProvider(provider);

Java 11


String configName = "/home/magnuskkarlsson/NetBeansProjects/example-nssdb/pkcs11.cfg";
Provider prototype = Security.getProvider("SunPKCS11");
Provider provider = prototype.configure(configName);

One gotcha with this in Java 11, is that you need to specify the provider when you try to do cryptographic operation like signature, otherwise you will get the following error.

Exception in thread "main" java.security.InvalidKeyException: No installed provider supports this key: sun.security.pkcs11.P11Key$P11PrivateKey

So how have they solved, so that SunPKCS11 is always loaded? Through the java.security which has also moved, due to changes in directory layout in Java 11.

Java 8: $JAVA_HOME/lib/security/java.security

Java 11: $JAVA_HOME/conf/security/java.security

So in Java 11 you can see the following


security.provider.12=SunPKCS11
#security.provider.1=SunPKCS11 ${java.home}/lib/security/nss.cfg

And if you look inside $JAVA_HOME/lib/security/nss.cfg, you see a ready to use EMPTY NSS DB configuration. To read about all SunPKCS11 NSS DB configuration see https://docs.oracle.com/en/java/javase/11/security/pkcs11-reference-guide1.html#GUID-7989F8B4-7260-4908-8203-99056B2D060E


name = NSS
nssLibraryDirectory = /usr/lib64
nssDbMode = noDb
attributes = compatibility
handleStartupErrors = ignoreMultipleInitialisation

Testing Time

Now lets test the SunPKCS11 with Java 11. First lets create a new NSS DB with the new SQLite format (cert9.db, key4.db, and pkcs11.txt) and then add a keypair and a self-signed certificate in the internal token.


$ echo "redhat123" > password.internal
$ mkdir nssdb_sql

$ certutil -N -d sql:nssdb_sql -f password.internal

$ certutil -S -x -d sql:nssdb_sql -f password.internal -n mkk -s 'CN=MKK, O=MKK Consultancy, C=SE' -k rsa -g 2048 -v 24 -Z SHA256 -t ',,'

And don't forget to add an empty secmod.db.


$ touch nssdb_sql/secmod.db

Then create the SunPKCS11 configuration file pkcs11.cfg.


name = NSScrypto
nssLibraryDirectory = /usr/lib64 
nssSecmodDirectory = /home/magnuskkarlsson/NetBeansProjects/example-nssdb/nssdb_sql
nssDbMode = readWrite
nssModule = keystore

package se.magnuskkarlsson.example.nssdb;

import static java.nio.charset.StandardCharsets.UTF_8;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.Base64;

import java.util.Enumeration;

// https://docs.oracle.com/javase/8/docs/technotes/guides/security/p11guide.html#NSS
// https://docs.oracle.com/en/java/javase/11/security/pkcs11-reference-guide1.html#GUID-85EA1017-E59C-49B9-9207-65B7B2BF171E
// https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Reference/NSS_tools_:_certutil
/*
          certutil supports two types of databases: the legacy
          security databases (cert8.db, key3.db, and secmod.db)
          and new SQLite databases (cert9.db, key4.db, and
          pkcs11.txt).
 */
 /*
name = NSScrypto
nssLibraryDirectory = /usr/lib64 
nssSecmodDirectory = /home/magnuskkarlsson/NetBeansProjects/example-nssdb/nssdb_sql
nssDbMode = readWrite
nssModule = keystore
 */
public class NSSSunPKCS11Tool {

    public static char[] password = "redhat123".toCharArray();

    public static void main(String[] args) throws Exception {
        String configName = "/home/magnuskkarlsson/NetBeansProjects/example-nssdb/pkcs11.cfg";

        // Java 8
//        sun.security.pkcs11.SunPKCS11 provider = new sun.security.pkcs11.SunPKCS11(configName);
//        Security.addProvider(provider);
//        
        // Java 11
        Provider prototype = Security.getProvider("SunPKCS11");
        Provider provider = prototype.configure(configName);

        KeyStore ks = KeyStore.getInstance("PKCS11", provider);
        ks.load(null, password);
        System.out.println("Successfully loaded NSS DB.");
        System.out.println("------------------------------");
        for (Enumeration aliases = ks.aliases(); aliases.hasMoreElements();) {
            String alias = aliases.nextElement();
            X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
            PublicKey publicKey = cert.getPublicKey();
            PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password);
            System.out.println("alias: " + alias);
            System.out.println("privateKey: " + privateKey);
            System.out.println("cert subject dn: " + cert.getSubjectX500Principal().toString());

            if (privateKey != null) {
                String plainText = "HELLO WORLD";

                Signature privateSignature = Signature.getInstance("SHA256withRSA", provider);
                privateSignature.initSign(privateKey);
                privateSignature.update(plainText.getBytes(UTF_8));
                byte[] signature = privateSignature.sign();
                String signatureBase64 = Base64.getEncoder().encodeToString(signature);
                System.out.println(signatureBase64);

                Signature publicSignature = Signature.getInstance("SHA256withRSA", provider);
                publicSignature.initVerify(publicKey);
                publicSignature.update(plainText.getBytes(UTF_8));
                boolean verify = publicSignature.verify(signature);
                System.out.println("verify: " + verify);
            }

            System.out.println("------------------------------");
        }
    }
}

Then lets run it. I have here added the debug flag -Djava.security.debug=sunpkcs11 which is of course not necessary, but useful when running labs to understand things better.


$ mvn clean install; java -cp target/example-nssdb-1.0.0-SNAPSHOT.jar -Djava.security.debug=sunpkcs11 se.magnuskkarlsson.example.nssdb.NSSSunPKCS11Tool

SunPKCS11 loading /home/magnuskkarlsson/NetBeansProjects/example-nssdb/pkcs11.cfg
NSS modules: [NSS Internal PKCS #11 Module (CRYPTO, /usr/lib64/libsoftokn3.so, slot 0), NSS Internal PKCS #11 Module (KEYSTORE, /usr/lib64/libsoftokn3.so, slot 1)]
sunpkcs11: Initializing PKCS#11 library /usr/lib64/libsoftokn3.so
Information for provider SunPKCS11-NSScrypto
Library info:
  cryptokiVersion: 2.20
  manufacturerID: Mozilla Foundation              
  flags: 0
  libraryDescription: NSS Internal Crypto Services    
  libraryVersion: 3.46
All slots: 1, 2
Slots with tokens: 1, 2
Slot info for slot 2:
  slotDescription: NSS User Private Key and Certificate Services                   
  manufacturerID: Mozilla Foundation              
  flags: CKF_TOKEN_PRESENT
  hardwareVersion: 3.46
  firmwareVersion: 0.00
Token info for token in slot 2:
  label: NSS Certificate DB              
  manufacturerID: Mozilla Foundation              
  model: NSS 3           
  serialNumber: 0000000000000000
  flags: CKF_RNG | CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_DUAL_CRYPTO_OPERATIONS | CKF_TOKEN_INITIALIZED
  ulMaxSessionCount: CK_EFFECTIVELY_INFINITE
  ulSessionCount: 1
  ulMaxRwSessionCount: CK_EFFECTIVELY_INFINITE
  ulRwSessionCount: 0
  ulMaxPinLen: 500
  ulMinPinLen: 0
  ulTotalPublicMemory: 1
  ulFreePublicMemory: 1
  ulTotalPrivateMemory: 1
  ulFreePrivateMemory: 1
  hardwareVersion: 0.00
  firmwareVersion: 0.00
  utcTime: 0000000000000000
...
sunpkcs11: login succeeded
Successfully loaded NSS DB.
------------------------------
alias: mkk
privateKey: SunPKCS11-NSScrypto RSA private key, 2048 bitstoken object, sensitive, extractable)
cert subject dn: CN=MKK, O=MKK Consultancy, C=SE
bKqsRr+exiclVEB1Q59/M/KrElu+dCFKDK9+mertjlj1lJ7LG7Gwrws6CX/m6l3A9bf4nt+yQNYYt/2x3WFITquuBsbMKfyV6J7UHYS7J92+EUNHNXaFR2QVDo5v3Ecy4oPD9ln7LATl1jJnfSs0kiYB7HbOIWjufxfrY65sgQUyR+I3uQaj0+PDJ8WbrUbqCvzdFv3MH+Jv9kDvdp1eBkrPD+yczLdIQy7kDJRmzN34gU6tW85RZ0PpgjZomV3TO3S6hJxeZqH/ijd5yLKlpfQAM2V4dADsnlGmS9KUrZ6JOU/eIFRX6CD0X/eNsCqgUp+vn7JD/SE4NJabUdrVQw==
Demoting session, active: 3
verify: true
------------------------------

No comments: