NSS/JSS:在Java中加载用户导入的证书以及PKCS#11智能卡 [英] NSS/JSS: load user imported cert along with PKCS#11 smartcard in Java

查看:141
本文介绍了NSS/JSS:在Java中加载用户导入的证书以及PKCS#11智能卡的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究Java Swing项目,在该项目中,我必须开发一种列出证书的功能,以便用户选择针对服务器通过SSL进行身份验证.

I am working on a Java Swing project, where I must develop a feature of listing certificates for users to choose for authentication via SSL against the server.

这些证书必须包含用户在Firefox中导入的证书,并且如果插入了智能卡,卡中的证书也将列出.环境是Linux/MacOS.在Windows中,Internet Explorer可以处理所有事情,我们想要实现的功能与Windows中的情况非常相似:列出所有证书以及卡中的证书,供用户选择.

These certificates must contain the user imported ones in Firefox, and if a smartcard is inserted, those in the card will be listed, too. The environment is Linux/MacOS. In Windows the Internet Explorer handles it all, and what we would like to achieve is much like what happens in Windows: list all certificates, along with those in card, for users to choose.

在Ubuntu中使用Mozilla的NSS(网络安全服务)时,我发现自己迷路了.由于没有用于在Java中使用JSS的代码示例,所以只能使其部分工作,具体取决于我如何为提供程序加载配置文件.

When using NSS (Network Security Service) of Mozilla in Ubuntu, I found I am lost. With no code samples for using JSS in Java, I can only get it to work partially, depending on the way how I load the config file for the provider.

我现在要做的是:

  1. 在firefox中读取证书(带有KeyStoreProviderKeyStore.Builder,将softokn.so加载为库).

  1. read the cert in firefox (with KeyStore, Provider and KeyStore.Builder, loading the softokn.so as the library).

使用CryptoManager从卡中加载证书,并获取其所有模块. (CryptoManager.initialize(profileDir)cm.getModules()module.getTokens()等)

Load the cert from card with CryptoManager and get all its modules. (CryptoManager.initialize(profileDir), cm.getModules(), module.getTokens(), etc. )


问题

方法1

如果我用libsoftoken3.so加载提供程序,则可以看到用户证书.但是,当我在构造provider之后初始化CryptoManager时,cryptoManager.getModules()中未列出外部模块(例如,我的智能卡).


Problem

Approach 1

If I load the provider with libsoftoken3.so, I can see the user certificates. But, when I initialize the CryptoManager after constructing the provider, the external modules (e.g., 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";

方法2

如果我将NNS的secmod.db加载到提供商中,则该卡将在provider构造的keyStore中列出,即使该卡不存在/未插入.插入后,在上面的第二步中,我可以看到外部模块,但是随后该卡以相同的别名列出了两次.

Approach 2

If I load the provider with NNS's secmod.db, the card will be listed, even if it's not present/inserted, in the keyStore constructed with this provider. When it's inserted, in the second step above, I can see the external modules, but then the card is listed 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:

  • 我如何以一种简单的方式轻松地加载所有证书,而不是与JSS分开加载?

  • How can I easily load all certificate in a simple way, not separately with JSS?

如果不可能,我该如何配置提供程序以分别加载它们而不重复执行?

If it's not possible, how can I configure the provider to load them separately but without repetition?

推荐答案

我以某种方式成功解决了该问题.我有80%的问题都由我自己解决了......我认为这很正常.

I have somehow succeeded in solving it. 80% of my questions are solved by myself.........I think it's normal.

基本上,它包括构造两个KeyStore实例和两个Provider实例,每个实例一个,一个用于用户证书,另一个用于智能卡.

Basically it consists in constructing two instances of KeyStore and two instances of Provider, each for each, one for user certificates and the other for smartcard.

  1. 使用libsoftokn.so(例如,我所问的第一个config)构造一个提供程序,并将其插入.使用KeyStore.Builder和此提供程序,构建KeyStore softKeyStore.在此密钥库中,您拥有所有用户证书.提取这些证书的信息,并在JTable中列出它们.

  1. Construct a provider with libsoftokn.so, e.g., the first config in my question, and insert it. With KeyStore.Builder and this provider, build a KeyStore softKeyStore. In this keystore you have all the user certificates. Extract the information of these certificates and list them in a JTable.

在第一次初始化CryptoManager之前插入智能卡. (否则,卡将被忽略,直到重新启动应用程序为止.)

Insert the smartcard before the first time CryptoManager is initialized. (If not, the card will be ignored until restarting the app.)

初始化CryptoManager.这里有一些技巧可以打破AlreadyInitializedException/NotInitializedException的死循环:

Initialize CryptoManager. Here some tricks to break the dead loop of AlreadyInitializedException/NotInitializedException:

我们有:

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.
        }
    }
}

现在,我们可以执行cm.getModules()module.getTokens()来识别卡. **只有插入卡后,相关模块及其令牌才会出现. **

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

  1. 当我们获取卡的令牌时,请检查其是否需要登录以及是否已登录.而且,我们必须排除InternalCryptoTokenInternalKeyStorageToken.

所以:

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()) {
                ....
            }

登录令牌后,使用Runtime.getRuntime().exec(command)执行shell脚本以使用NSS附带的modutil.

When the token is logged in, execute shell script with Runtime.getRuntime().exec(command) to use modutil shipped with NSS.

在外壳中是这样的:

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

此命令以可读格式显示secmod.db中包含的信息.

This command shows you information contained in secmod.db in 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 ] }  "

因此,您可以分析输出并在module.getName()所在的行中获取库的位置.我们可以使用StringTokenizer.

So you can analyze the output and get the library location in the line where your module.getName() are located. 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. 然后,使用.so驱动程序的文件路径,构造一个config字符串以加载另一个提供程序.
  1. Then, with the file path of the .so driver, construct a config string to load another provider.

我们将拥有:

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

moduleName最好用"\"进行转义,因为它通常包含空格.如果需要空格,请转义libPath.最好不要有空格.

moduleName is better escaped with "\", because it usually contains spaces. libPath should be escaped, if there would like to be spaces. Better not to have spaces.

插入此提供程序,并使用相同的提供程序构造一个cardKeyStore.

Insert this provider, and construct a 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. 列出与以上使用的JTable相同的从cardKeyStore获得的证书的别名.

  1. List the alias of the certificates we get from cardKeyStore in the same JTable we used above, along with those of softKeyStore.

当用户在JTable中选择一行时,获取所选别名并将其存储在静态字段中.

When the user select a row in the JTable, get the selected alias and store it in a static field.

当我们需要密钥库来构造KeyManagerFactoryX509KeyManager进行SSL通信时,使用静态alias时,我们先在softKeyStore中查找它,然后在cardKeyStore中查找.

When we need a keystore to construct KeyManagerFactory and X509KeyManager for SSL communication, with the static alias we look it up in softKeyStore and then cardKeyStore.

赞:

if (softKeyStore.containsAlias(alias)) {
    return softKeyStore;
} else if (cardKeyStore.containsAlias(alias)) {
    return cardKeyStore;
}

  1. SSL握手,发送消息,接收,签名等

这篇关于NSS/JSS:在Java中加载用户导入的证书以及PKCS#11智能卡的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆