我有一个调用
System.setProperty("javax.net.ssl.trustStoreType", "WINDOWS-ROOT");
的 Java 应用程序,从而触发 JRE 最终调用 Windows CertOpenSystemStore
方法,然后枚举并加载来自 HKLM 支持的受信任根证书颁发机构证书存储区的证书以用于其内部验证。
这很棒,除了处理非常干净/最近安装的 Windows 计算机时,其中受信任的根证书颁发机构仅包含约 16 个条目。我最近才了解到 Windows 的证书信任列表 (CTL) 功能,这是一种隐藏机制,可以根据 Windows 从 Windows 更新(或其他地方,如果被覆盖,例如通过 GPO)下载的受信任根列表来透明地检查证书链。这使得网络浏览器能够毫无怨言地与多个网站一起工作,并且数字签名的二进制文件能够在没有警告的情况下执行。某些操作(例如访问 Internet Explorer(但不是 Edge)中的站点)还会导致 CA 从 CTL 迁移到受信任的根证书颁发机构存储。
但是,我推断 CTL 机制不会在 Java 工作流程中触发,因为 Java 不会要求 Windows 验证 TLS 证书链,它只是根据从受信任的根证书颁发机构枚举的证书来验证该链(
CertOpenSystemStore
)。调用sun.security.validator.ValidatorException: PKIX path building failed: : sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
后最终结果是HttpURLConnection::getOutputStream().write(postDataBytes);
。
有谁知道触发 Windows 的 CTL 到受信任的根证书颁发机构迁移机制的方法吗?显然,如果有一种本机 Java 方法可以做到这一点,那将是理想的,但即使有人知道什么 Windows C API 会触发此操作,所以我可以编写一些 JNI,这也比告诉用户的替代方案更好在 Internet Explorer 中访问站点,以确保其根 CA 受到 Java 应用程序的信任。
谢谢!
没有“java”方式可以完成您所要求的操作,您需要使用 C++ 或 C 代码来调用以下内容,并具有巴拿马附带的功能 项目。
import java.lang.invoke.MethodHandle;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
public class WindowsCertValidator {
// Define the library path for Advapi32.dll and crypt32.dll (the libraries needed for Cert APIs)
private static final String ADVAPI32 = "Advapi32";
private static final String CRYPT32 = "Crypt32";
// Foreign linker to access native functions
private static final Linker linker = Linker.nativeLinker();
// Define function signatures for CertOpenSystemStore and CertGetCertificateChain
static final MethodHandle CertOpenSystemStore;
static final MethodHandle CertGetCertificateChain;
static final MethodHandle CertCloseStore;
static {
// Load the native methods using Panama Foreign API
var lookup = SymbolLookup.loaderLookup();
// CertOpenSystemStore (from crypt32.dll)
CertOpenSystemStore = linker.downcallHandle(
lookup.find("CertOpenSystemStoreA").orElseThrow(),
FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS)
);
// CertGetCertificateChain (from crypt32.dll)
CertGetCertificateChain = linker.downcallHandle(
lookup.find("CertGetCertificateChain").orElseThrow(),
FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS,
ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS)
);
// CertCloseStore (from crypt32.dll)
CertCloseStore = linker.downcallHandle(
lookup.find("CertCloseStore").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_INT)
);
}
public static MemorySegment openCertStore(String storeName) throws Throwable {
MemorySegment storeNameSegment = SegmentAllocator.implicitAllocator()
.allocateUtf8String(storeName);
MemorySegment certStore = (MemorySegment) CertOpenSystemStore.invokeExact(MemoryAddress.NULL, storeNameSegment);
if (certStore.address().toRawLongValue() == 0) {
throw new IllegalStateException("Failed to open cert store");
}
return certStore;
}
public static void closeCertStore(MemorySegment certStore) throws Throwable {
int success = (int) CertCloseStore.invokeExact(certStore, 0);
if (success == 0) {
throw new IllegalStateException("Failed to close cert store");
}
}
public static void main(String[] args) {
try {
// Open the Trusted Root Certification Authorities store
MemorySegment certStore = openCertStore("ROOT");
System.out.println("Successfully opened Trusted Root Certification Authorities store.");
// Normally, you would invoke CertGetCertificateChain here to trigger validation
// and interact with the cert chain, using similar downcalls as CertOpenSystemStore.
// Close the cert store when done
closeCertStore(certStore);
System.out.println("Successfully closed the cert store.");
} catch (Throwable e) {
e.printStackTrace();
}
}
}