NSS / JSS: upload imported user certificate along with PKCS # 11 smart card in Java

Scenario

I am working on a Java Swing project where I need to develop a certificate listing feature for users to opt for SSL authentication against the server.

These certificates must contain user certificates imported into Firefox, and if a smart card is inserted, they will also be indicated on the card. The environment is Linux / MacOS. In Windows, Internet Explorer handles all this, and what we would like to achieve is very similar to what is happening in Windows: specify all the certificates along with the cards for users.


Situation

When using Mozilla's NSS (Network Security Services) in Ubuntu, I found that I was lost. Without code examples for using JSS in Java, I can make it work partially, depending on how the configuration file is loaded for the provider.

Now I do the following:

  • read the certificate in firefox (with KeyStore , Provider and KeyStore.Builder by downloading softokn.so as a library).

  • Download the certificate from the card using CryptoManager and get all its modules. ( CryptoManager.initialize(profileDir) , cm.getModules() , module.getTokens() , etc.)


Problem

Approach 1

If I load the provider using libsoftoken3.so , I can see the user certificates. But, when I initialize CryptoManager after building the Provider , external modules (like my smart cards) are not listed in cryptoManager.getModules() .

 config = "library=" + NSS_JSS_Utils.NSS_LIB_DIR + "/libsoftokn3.so\n" + "name=\"Soft Token\"\n" + "slot=2\n" //for softoken, can only be 2. + "attributes=compatibility\n" + "allowSingleThreadedModules=true\n" + "showInfo=true\n" + "nssArgs=\"configdir='" + NSS_JSS_Utils.getFireFoxProfilePath() + "' " + "certPrefix='' " + "keyPrefix='' " + "secmod='secmod.db' " + "flags='readOnly'\"" // + "flags='noDb'\"" + "\n"; 

Approach 2

If I load the provider using NNS secmod.db , the map will be indicated, even if it is not present / inserted, in the KeyStore built with this Provider . When it is inserted, in the second step above, I can see the external modules, but then the map is displayed twice, with the same alias.

 config = "name=\"NSS Module\"\n" + "attributes=compatibility\n" + "showInfo=true\n" + "allowSingleThreadedModules=true\n" + "nssUseSecmod=true\n" + "nssSecmodDirectory=" + NSS_JSS_Utils.getFireFoxProfilePath(); 

Question:

  • How can I easily download the entire certificate in a simple way, and not separately with JSS?

  • If this is not possible, how to configure the provider to download them separately, but without repetition?

+5
source share
1 answer

For some reason I managed to solve it. 80% of my questions are resolved by me ......... I think this is normal.

Basically, it consists in building two instances of KeyStore and two instances of Provider , each for each, one for user certificates, and the other for smart cards.

  • Create a provider with libsoftokn.so , for example, the first config in my question, and paste it. Using KeyStore.Builder and this provider, build a KeyStore softKeyStore . In this keystore, you have all user certificates. Extract the information of these certificates and list them in JTable .

  • Insert a smart card for the first time CryptoManager . (If not, the card will be ignored until the application restarts.)

  • Initialize CryptoManager . Here are a few tricks to break the dead loop of AlreadyInitializedException / NotInitializedException :

We have:

 private static void initializeCryptoManager() throws Exception { //load the NSS modules before creating the second keyStore. if (cm == null) { //cm is of type CryptoManager while (true) { //the trick. try { cm = CryptoManager.getInstance(); } catch (NotInitializedException e2) { try { InitializationValues iv = new InitializationValues(NSS_JSS_Utils.getFireFoxProfilePath()); //TEST iv.installJSSProvider = false; iv.removeSunProvider = false; iv.initializeJavaOnly = false; //must be false, or native C error if no provider is created. iv.cooperate = false; iv.readOnly = true; iv.noRootInit = true; iv.configDir = NSS_JSS_Utils.getFireFoxProfilePath(); iv.noModDB = false; // iv.noCertDB = false; // CustomPasswordCallback cpc = new CustomPasswordCallback(); // iv.passwordCallback = cpc; //no passwordcallback needed here. iv.forceOpen = false; iv.PK11Reload = false; CryptoManager.initialize(iv); continue; // continue to getInstance. } catch (KeyDatabaseException | CertDatabaseException | GeneralSecurityException e) { Traza.error(e); throw e; } catch (AlreadyInitializedException e1) { continue; //if is initialized, must go on to get cm. } } break; //if nothing is catched, must break to end the loop. } } } 

And now we can do cm.getModules() and module.getTokens() to recognize the map. ** Only when the card is inserted, will the corresponding module and its token be present. **

  1. When we get to the map marker, check if it needs a login and if it is registered. And we have to exclude InternalCryptoToken and InternalKeyStorageToken .

So:

 if (!token.isInternalCryptoToken() && !token.isInternalKeyStorageToken()){ // If not Internal Crypto service, neither Firefox CA store if (token.isPresent() ) { // when the card is inserted if (!token.isLoggedIn()){ // Try to login. 3 times. Traza.info("Reading the certificates from token " + token.getName() + ". Loggining... "); while (UtilTarjetas.tries <= 3) { try { //TEST token.setLoginMode(NSS_JSS_Utils.LOGIN_MODE_ONE_TIME); token.login((PasswordCallback) new CustomPasswordCallback()); UtilTarjetas.prevTryFailed = false; cm.setThreadToken(token); break; } catch (IncorrectPasswordException e){ UtilTarjetas.prevTryFailed = true; UtilTarjetas.tries ++; } catch (TokenException e) { UtilTarjetas.prevTryFailed = true; UtilTarjetas.tries ++; } } // if tries > 3 if (UtilTarjetas.tries > 3) { Traza.error("The token " + token.getName() + " is locked now. "); throw new IOException("You have tries 3 times and now the card is locked. "); } } if (token.isLoggedIn()) { .... } 

When the token is registered, run the shell script using Runtime.getRuntime().exec(command) to use the modutil supplied with NSS.

In the shell, it looks like this:

 modutil -dbdir /your/firefox/profile/dir -rawlist 

This command displays the information contained in secmod.db in a readable format.

  name="NSS Internal PKCS #11 Module" parameters="configdir=/home/easternfox/.mozilla/firefox/5yasix1g.default-1475600224376 certPrefix= keyPrefix= secmod=secmod.db flags=readOnly " NSS="trustOrder=75 cipherOrder=100 slotParams={0x00000001=[slotFlags=RSA,RC4,RC2,DES,DH,SHA1,MD5,MD2,SSL,TLS,AES,SHA256,SHA512,Camellia,SEED,RANDOM askpw=any timeout=30 ] 0x00000002=[ askpw=any timeout=0 ] } Flags=internal,critical" library=/usr/lib/libpkcs11-dnie.so name="DNIe NEW" library=/usr/local/lib/libbit4ipki.so name="Izenpe local" NSS=" slotParams={0x00000000=[ askpw=any timeout=0 ] } " 

So, you can analyze the output and get the location of the library in the line where your module.getName() . We can use StringTokenizer .

 //divide the line into strings with "=". StringTokenizer tz = new StringTokenizer(line, "="); //get the first part, "library". String token = tz.nextToken(); //get the second part, "/usr/local/lib/libbit4ipki.so name" token = tz.nextToken(); .... 
  1. Then, using the path to the .so driver file, create a config line to load another provider.

We'll have:

 String config = "name=\"" + moduleName + "\"\n" + "library=" + libPath; 

moduleName better escaped with "\" since it usually contains spaces. libPath must be escaped if you want to be spaces. Better not have spaces.

Paste this provider and build cardKeyStore with the same provider.

 Provider p = new SunPKCS11(new ByteArrayInputStream(config.getBytes())); Security.insertProviderAt(p, 1); KeyStore.Builder builder = null; builder = KeyStore.Builder.newInstance("PKCS11", p, new KeyStore.CallbackHandlerProtection(new UtilTarjetas().new CustomCallbackHandler())); cardKeyStore = builder.getKeyStore(); 
  1. List the alias of the certificates that we get from cardKeyStore in the same JTable that we used above, along with the softKeyStore characters.

  2. When the user selects a row in JTable , get the selected alias and save it in a static field.

  3. When we need a keystore to build KeyManagerFactory and X509KeyManager for SSL communication, with static alias we will look for it in softKeyStore and then cardKeyStore .

Like:

 if (softKeyStore.containsAlias(alias)) { return softKeyStore; } else if (cardKeyStore.containsAlias(alias)) { return cardKeyStore; } 
  1. SSL handshaking, sending messages, receiving, subscribing, etc.
0
source

Source: https://habr.com/ru/post/1259897/


All Articles