通过Java访问Windows证书存储?

20

我想编写一个程序,可以列举并使用CurrentUser/My和LocalMachine/My中的证书(用于签名),但是我无法在Windows证书存储中找到任何信息,只有Java自己的秘密存储。 这个链接 看起来很有希望,但我只能使用Java提供的内容。

我在SO上找到了这个问题,但它是五年前的,计算机领域的时间很长。谢谢!

4个回答

36

页面不存在。 - yesIamFaded
4
我今天(2020年9月14日)能够打开该页面。 - Manabu Tokunaga
我没有所有的细节,但Windows会根据需要自动安装新的受信任CA。因此,通过Edge或Chrome首次访问服务器/域可能会安装新的CA证书。然而,通过Java访问该域不会安装该CA。因此,如果Java出现无法验证服务器证书的典型错误,请先在浏览器中打开它。Firefox不起作用,因为它有自己的证书存储。在使用该标志时要记住这一点。 - Frettman

2
Java的跨平台性也有其缺点——您不能访问某些(或很多)操作系统特定的内容,除非使用外部库。仅通过CryptoAPI本机函数才能访问Windows证书存储,而Java默认安装不支持这些函数。
您可以参考这个帖子:Calling Win32 API method from Java 如果您可以使用JNA,则可以使用crypt32.dll中的各种证书和证书存储函数枚举证书并执行签名操作。

JNA听起来是一个不错的选择...但我在理解那里的示例代码方面遇到了麻烦。你知道有没有任何用于C#/.NET的方法吗? - Benjin
我希望微软能够通过允许非Windows平台上的Java应用程序读取由Windows服务器通过组策略更新推送的信任存储来表达善意。比如,可以从域服务器上提供PEM捆绑包供下载使用 :-) - eel ghEEz

1
KeyStore keyStore = KeyStore.getInstance(getKeyStoreType(), "SunMSCAPI");
keyStore.load(null, null);

try {
    Field field = keyStore.getClass().getDeclaredField("keyStoreSpi");
    field.setAccessible(true);

    KeyStoreSpi keyStoreVeritable = (KeyStoreSpi)field.get(keyStore);
    field = keyStoreVeritable.getClass().getEnclosingClass().getDeclaredField("entries");
    field.setAccessible(true);
} catch (Exception e) {
    LOGGER.log(Level.SEVERE, "Set accessible keyStoreSpi problem", e);
}

Enumeration enumeration = keyStore.aliases();

它是如何确定在Windows证书存储中枚举哪个路径的?我预期会看到"LocalMachine"或"CurrentUser"的某个地方。 - Benjin
我猜你可能无法获取这些信息。但是,也许我错了。当你弄清楚时,请告诉我。 - Krzysiek
如果您查看SunMSCAPI提供程序文档,它将告诉您应使用哪个密钥库别名来访问。 - sunnychan77

1

我从Crypt32离开的地方开始,使用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);

这只是一段可用的代码,欢迎使用你自己的编程实践。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接