Wes*_*Gun 5 java nss client-certificates
我正在开发一个Java Swing项目,我必须开发一个列出证书的功能,供用户选择通过SSL对服务器进行身份验证.
这些证书必须包含Firefox中用户导入的证书,如果插入了智能卡,也会列出卡中的智能卡.环境是Linux/MacOS.在Windows中,Internet Explorer处理所有这些,我们希望实现的与Windows中发生的情况非常相似:列出所有证书以及卡中的证书,供用户选择.
在Ubuntu中使用Mozilla的NSS(网络安全服务)时,我发现我迷路了.由于没有在Java中使用JSS的代码示例,我只能部分地使用它,具体取决于我为提供程序加载配置文件的方式.
我现在做的是:
在firefox中读取证书(使用KeyStore,Provider并将KeyStore.Builder其softokn.so作为库加载).
从卡中加载证书CryptoManager并获取其所有模块.(CryptoManager.initialize(profileDir),cm.getModules(),module.getTokens()等)
如果我加载提供程序libsoftoken3.so,我可以看到用户证书.但是,当我CryptoManager在构建之后初始化时provider,外部模块(例如,我的智能卡)未列入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";
Run Code Online (Sandbox Code Playgroud)
如果我加载具有NNS的供应商secmod.db,该卡将上市,即使它不存在/插入,在keyStore此建造provider.当它插入时,在上面的第二步中,我可以看到外部模块,但随后卡被列出两次,具有相同的别名.
config = "name=\"NSS Module\"\n"
+ "attributes=compatibility\n"
+ "showInfo=true\n"
+ "allowSingleThreadedModules=true\n"
+ "nssUseSecmod=true\n"
+ "nssSecmodDirectory=" + NSS_JSS_Utils.getFireFoxProfilePath();
Run Code Online (Sandbox Code Playgroud)
题:
如何以简单的方式轻松加载所有证书,而不是单独使用JSS?
如果不可能,我如何配置提供程序以单独加载它们但不重复?
我以某种方式成功地解决了这个问题。我80%的问题都是自己解决的……我觉得很正常。
基本上,它包括构造 的两个实例KeyStore和 的两个实例Provider,每个实例对应一个,一个用于用户证书,另一个用于智能卡。
构造一个提供者libsoftokn.so,例如config我问题中的第一个,然后插入它。使用KeyStore.Builder此提供程序,构建一个KeyStore softKeyStore. 在此密钥库中,您拥有所有用户证书。提取这些证书的信息并将它们列在JTable.
首次CryptoManager初始化前插入智能卡。(如果没有,该卡将被忽略,直到重新启动应用程序为止。)
初始化CryptoManager. 这里有一些打破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.
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我们可以执行cm.getModules()和module.getTokens(),来识别该卡。**只有当卡插入时,相关模块及其令牌才会出现。**
InternalCryptoToken和InternalKeyStorageToken。所以:
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()) {
....
}
Run Code Online (Sandbox Code Playgroud)
当令牌登录时,执行shell脚本以Runtime.getRuntime().exec(command)使用modutilNSS 附带的功能。
在shell中是这样的:
modutil -dbdir /your/firefox/profile/dir -rawlist
Run Code Online (Sandbox Code Playgroud)
secmod.db此命令以可读格式显示包含的信息。
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 ] } "
Run Code Online (Sandbox Code Playgroud)
因此,您可以分析输出并在您module.getName()所在的行中获取库位置。我们可以用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();
....
Run Code Online (Sandbox Code Playgroud)
.so,构造一个config字符串来加载另一个提供程序。我们将有:
String config = "name=\"" + moduleName + "\"\n" + "library=" + libPath;
Run Code Online (Sandbox Code Playgroud)
moduleName最好用“\”进行转义,因为它通常包含空格。libPath如果想要有空格,应该转义。最好不要有空格。
插入此提供程序,并cardKeyStore使用相同的提供程序构造一个。
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();
Run Code Online (Sandbox Code Playgroud)
cardKeyStore列出我们从上面使用的相同证书中获得的证书的别名JTable,以及softKeyStore.
当用户在 中选择一行时JTable,获取所选别名并将其存储在静态字段中。
当我们需要一个密钥库来构建 KeyManagerFactorySSLX509KeyManager通信时,我们可以使用 static 来alias查找它softKeyStore,然后cardKeyStore.
喜欢:
if (softKeyStore.containsAlias(alias)) {
return softKeyStore;
} else if (cardKeyStore.containsAlias(alias)) {
return cardKeyStore;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
635 次 |
| 最近记录: |