是否可以从 Java Servlet 访问存储在本地机器存储(而不是当前用户)中的证书?我尝试使用 MSCAPI 提供程序打开“Windows-MY”和“Windows-ROOT”存储,但都不包含来自本地机器存储的证书。
4 回答
默认的 JDK 实现相当有限。AFAIK 它只会带回 RSA 密钥和证书。它不是 MSCAPI 的通用适配器。我已经能够使用您描述的机制取回一些证书。
如果您要使用任何特定于 Windows 的程序,我使用 JNA 使用相同的 Windows 对话框访问证书 - 这可能无法回答您的问题,但肯定可以让您提供以“Windows 方式”访问任何内容的选项:
NativeLibrary cryptUI = NativeLibrary.getInstance("Cryptui");
NativeLibrary crypt32 = NativeLibrary.getInstance("Crypt32");
Function functionCertOpenSystemStore = crypt32.getFunction("CertOpenSystemStoreA");
Object[] argsCertOpenSystemStore = new Object[] { 0, "CA"};
HANDLE h = (HANDLE) functionCertOpenSystemStore.invoke(HANDLE.class, argsCertOpenSystemStore);
Function functionCryptUIDlgSelectCertificateFromStore = cryptUI.getFunction("CryptUIDlgSelectCertificateFromStore");
System.out.println(functionCryptUIDlgSelectCertificateFromStore.getName());
Object[] argsCryptUIDlgSelectCertificateFromStore = new Object[] { h, 0, 0, 0, 16, 0, 0};
Pointer ptrCertContext = (Pointer) functionCryptUIDlgSelectCertificateFromStore.invoke(Pointer.class, argsCryptUIDlgSelectCertificateFromStore);
Function functionCertGetNameString = crypt32.getFunction("CertGetNameStringW");
char[] ptrName = new char[128];
Object[] argsCertGetNameString = new Object[] { ptrCertContext, 5, 0, 0, ptrName, 128};
functionCertGetNameString.invoke(argsCertGetNameString);
System.out.println("Selected certificate is " + new String(ptrName));
Function functionCertFreeCertificateContext = crypt32.getFunction("CertFreeCertificateContext");
Object[] argsCertFreeCertificateContext = new Object[] { ptrCertContext};
functionCertFreeCertificateContext.invoke(argsCertFreeCertificateContext);
Function functionCertCloseStore = crypt32.getFunction("CertCloseStore");
Object[] argsCertCloseStore = new Object[] { h, 0};
functionCertCloseStore.invoke(argsCertCloseStore);
它只是一段有效的代码;随意应用您的编码实践。
正如其他人所提到的,MSCAPI 提供程序不提供对存储在“本地计算机”证书存储中的证书和密钥的访问。原因是 MSCAPI 使用 Microsoft CryptoAPI 函数CertOpenSystemStore
来访问证书和密钥。此函数的文档明确指出“使用此方法只能访问当前用户证书,而不是本地机器存储”。如果您想跟踪此问题的进度,可以关注此 OpenJDK 错误。
如果您想要一个适当的解决方案,您可以购买商业的Pheox JCAPI库。
如果您可以忍受黑客攻击,我创建了一个简单的实用程序,它拦截 JDK 调用CertOpenSystemStore
并返回虚拟证书存储的句柄,允许对“当前用户”和“本地机器”中的证书和密钥进行只读访问" 证书存储。这解决了我的问题,但请注意此实用程序的局限性。
您要查找的证书在 java keystore 文件中或在启动服务器时传递到 tomcat
http://tomcat.apache.org/tomcat-4.0-doc/ssl-howto.html
如果您尝试将它们加载到您的应用程序中,请在此处查找以发出 HTTPS 请求,然后 HTTPClient 文档将帮助您入门
不确定这是否对您有帮助,但如果您可以提供更多详细信息,那么您可能会得到更具体的答案
public class KeyStoreLookup {
public static void main(String args[]) {
try {
KeyStore ks =
KeyStore.getInstance(KeyStore.getDefaultType());
String fname = System.getProperty("user.home") +
File.separator + ".keystore";
FileInputStream fis = new FileInputStream(fname);
ks.load(fis, null);
if (ks.isKeyEntry(args[0])) {
System.out.println(args[0] +
" is a key entry in the keystore");
char c[] = new char[args[1].length()];
args[1].getChars(0, c.length, c, 0);
System.out.println("The private key for" + args[0] +
" is " + ks.getKey(args[0], c));
Certificate certs[] = ks.getCertificateChain(args[0]);
if (certs[0] instanceof X509Certificate) {
X509Certificate x509 = (X509Certificate) certs[0];
System.out.println(args[0] + " is really " +
x509.getSubjectDN());
}
if (certs[certs.length - 1] instanceof
X509Certificate) {
X509Certificate x509 = (X509Certificate)
certs[certs.length - 1];
System.out.println(args[0] + " was verified by " +
x509.getIssuerDN());
}
}
else if (ks.isCertificateEntry(args[0])) {
System.out.println(args[0] +
" is a certificate entry in the keystore");
Certificate c = ks.getCertificate(args[0]);
if (c instanceof X509Certificate) {
X509Certificate x509 = (X509Certificate) c;
System.out.println(args[0] + " is really " +
x509.getSubjectDN());
System.out.println(args[0] + " was verified by " +
x509.getIssuerDN());
}
}
else {
System.out.println(args[0] +
" is unknown to this keystore");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}