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. **
- 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(); ....
- 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();
List the alias of the certificates that we get from cardKeyStore in the same JTable that we used above, along with the softKeyStore characters.
When the user selects a row in JTable , get the selected alias and save it in a static field.
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; }
- SSL handshaking, sending messages, receiving, subscribing, etc.