使用 Java (SunMSCAPI) 访问 Windows 本地计算机个人密钥库

问题描述 投票:0回答:2

有没有办法使用 SunMSCAPI 通过 Java 访问 Windows 本地计算机个人密钥存储?

通常,您可以使用WINDOWS-ROOT(大致相当于Java中的可信存储)或WINDOWS-MY(其中包含个人证书,大致相当于Java中的密钥存储)来检索来自 Windows 证书存储的证书。

这对于用户来说效果很好,但即使在模拟 SYSTEM 用户时,我也无法检索本地计算机的个人证书。

存在一些与此相关的问题使用JNA(我想避免,特别是因为从那里检索私钥似乎很复杂)。
此外,有人使用 psexec 来模拟 SYSTEM 用户(使用 psexec -s)。我也尝试过,但没有成功。
最后,Java Bug 系统中还有一个open bug

如果有人知道如何使用 Java 从 Windows 中的本地计算机个人存储中检索证书,我将不胜感激。

java windows certificate client-certificates mscapi
2个回答
1
投票

更新

经过 14 年的 bug 跟踪,JDK-6782021 终于在 Java 19(2022 年春季)中得到修复。这意味着这不再是问题。


原答案

否。您可以使用

wcsa
实用程序,它将拦截 JVM 对 Windows Crypto API 的调用,并让您访问本地计算机凭据。这当然是围绕十年前报道的真正问题JDK-6782021进行的黑客攻击。但它确实可以让您以非常轻松的方式访问本地计算机证书!

所以是的,可以访问它们,但是不行,不可能使用普通的 Java API 来访问它们。不过,您可以使用商业产品JCAPI使用普通Java访问它们,但对于大多数用途来说,

wcsa
实用程序就可以了。

Open JDK 维护者愿意打补丁,所以也许你可以尝试使用

wcsa
存储库中提供的代码来修复它:)


0
投票

这是一个独立于 Java 版本工作的替代方案,无论是 https://bugs.openjdk.org/browse/JDK-6782021 还是 https://bugs.openjdk.org/browse/JDK-8313367是固定的。它使用一个小的 Powershell 脚本将证书转储到一个临时文件夹,Java 可以在其中轻松读取它们。

import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.apache.commons.io.FileUtils;

public class WindowsCertificateService {
    private static List<X509Certificate> getWindowsLocalMachineCertificates(String certType) {
        // Theoretically we could use KeyStore.getInstance("Windows-ROOT-LOCALMACHINE") and
        // KeyStore.getInstance("Windows-MY-LOCALMACHINE") to accomplish this, which were added as part of
        // https://bugs.openjdk.org/browse/JDK-6782021. Unfortunately, we'll get an Access Denied error due to
        // https://bugs.openjdk.org/browse/JDK-8313367.

        Path tempFolder;
        List<X509Certificate> certificates = new ArrayList<>();
        try {
            tempFolder = Files.createTempDirectory("seeq-link-getWindowsLocalMachineCertificates");
        } catch (Throwable e) {
            LOG.error("Error creating temporary folder for Windows certificates", e);
            return Collections.emptyList();
        }

        try {
            String powershellScript = String.format("dir cert:\\localmachine\\%s | Foreach-Object { [system.IO" +
                    ".file]::WriteAllBytes(\".\\$($_.Thumbprint).cer\", ($_.Export('CERT', 'secret')) ) }", certType);
            Path scriptLocation = tempFolder.resolve("getWindowsLocalMachineCertificates.ps1");
            Files.write(scriptLocation, powershellScript.getBytes());
            ProcessBuilder processBuilder = new ProcessBuilder(
                    "powershell.exe", "-ExecutionPolicy", "Bypass", "-File", scriptLocation.toString());
            processBuilder.directory(tempFolder.toFile());
            processBuilder.redirectErrorStream(true);
            Process process = processBuilder.start();
            process.waitFor();
            if (process.exitValue() != 0) {
                LOG.warn("Error retrieving local machine Windows certificates for type '{}': Powershell script " +
                        "returned {} exit code. Script contents:\n{}", certType, process.exitValue(), powershellScript);
                return Collections.emptyList();
            }

            Collection<File> cerFiles = FileUtils.listFiles(tempFolder.toFile(), new String[] { "cer" }, false);
            for (File cerFile : cerFiles) {
                try (FileInputStream fis = new FileInputStream(cerFile)) {
                    CertificateFactory cf = CertificateFactory.getInstance("X.509");
                    Certificate cert = cf.generateCertificate(fis);
                    if (cert instanceof X509Certificate) {
                        certificates.add((X509Certificate) cert);
                    }
                } catch (Exception e) {
                    LOG.warn("Error reading Windows certificate from '{}'", cerFile, e);
                }
            }
            return certificates;
        } catch (Throwable e) {
            LOG.error("Error retrieving local machine Windows certificates", e);
            return Collections.emptyList();
        } finally {
            if (tempFolder != null) {
                try {
                    FileUtils.deleteDirectory(tempFolder.toFile());
                } catch (Exception e) {
                    LOG.error("Error deleting temporary folder for Windows certificates", e);
                }
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.