(Java)以编程方式访问" System Roots"在Mac OS X上

时间:2017-10-28 04:48:37

标签: java macos security x509certificate digital-certificate

我正在编写一个Java应用程序,它使用远程Https站点进行Api调用。远程站点由可信证书签名。它在Windows上运行良好,但由于SSL证书问题,它无法在OS X上运行。

我做了一些挖掘,发现原因与我如何使用getInstance调用在我的代码中初始化KeyStore对象有关。它只读取来自" System"的证书。钥匙串,但不是来自" System Roots"钥匙扣。下面是打印出密钥库中所有证书的代码片段。

// In windows use "WINDOWS-ROOT"
KeyStore osTrustManager = KeyStore.getInstance("KeychainStore");
osTrustManager.load(null, null);

Enumeration<String> enumerator = osTrustManager.aliases();
while (enumerator.hasMoreElements()) {
    String alias = enumerator.nextElement();
    if (osTrustManager.isCertificateEntry(alias)) {
        m_logger.info(String.format("%s (certificate)\n", alias));
    }
}

如何更改代码以实现这一目标?感谢是否有人可以加入。

这是&#34; System Roots&#34;下的证书样本。 Screenshot from OS X

2 个答案:

答案 0 :(得分:4)

我不知道是否有某种 t a b d 1 1 a1 b1 1_2 2 2 a2 b1 2_3 3 3 a1 b1 3_5 4 5 a1 b1 NA 5 1 a1 b2 1_3 6 3 a1 b2 3_2 7 2 a1 b2 2_5 8 5 a1 b2 NA 允许您访问Mac OS X系统根证书,但是您可以尝试另一种方法。

在Mac OS X中,您可以使用KeyStore命令从任何钥匙串中获取证书列表。

例如,此命令将为您提供有关系统根钥匙串中安装的不同证书的信息:

security

此实用程序有两个标志,security find-certificate -a "/System/Library/Keychains/SystemRootCertificates.keychain" -p,这两个标志将以PEM编码输出每个证书,这使我们能够按名称过滤结果-由于数量众多,因此很方便系统中安装的CA的数量。

想法是从Java使用此实用程序。

不久前,我遇到了一个名为clienteafirma的库,该库旨在处理数字签名。

此库具有一个名为AppleScript的类。此类基本上是-a的包装,它使我们可以运行任意命令。

以下代码使用该类和Process命令来获取所有由security颁发的证书:

VeriSign

答案 1 :(得分:2)

要求

  • 获取macOS锚根证书
  • 为其获取一个Java X509Certificate实例

可能的解决方案

据我所知,还没有纯Java的方法。但是,您可以创建一个本机C库,该库通过操作系统特定的调用来检索证书,然后通过JNI将其返回给Java。

从macOS 10.3开始,安全框架中就有一个功能SecTrustCopyAnchorCertificates,

获取由macOS存储的锚(根)证书。

请参阅https://developer.apple.com/documentation/security/1401507-sectrustcopyanchorcertificates?language=objc

要构建Java X509Certificate实例,您需要DER编码格式的证书数据,请参见 https://docs.oracle.com/javase/8/docs/api/java/security/cert/CertificateFactory.html#generateCertificate-java.io.InputStream-

在macOS端,您可以通过SecCertificateCopyData获得DER编码的证书数据。

注意:由于SecTrustCopyAnchorCertificates和SecCertificateCopyData这两个函数都包含单词“ Copy”,因此使用后必须调用CFRelease以避免产生内存泄漏。

每个证书的数据可以存储在Java字节数组中,并返回给Java调用方。

在Java方面,您可以通过调用CertificateFactory.getInstance("X.509")获得CertificateFactory。然后,您可以通过调用certFactory.generateCertificate(in)将字节转换为X509Certificate,其中in是ByteArrayInputStream,其中证书字节实际上来自本地C库。

这是一个独立的示例:

本地C库

#include <stdio.h>
#include <string.h>
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
#include "com_software7_test_MacOSX509Certificates.h"


JNIEXPORT jobjectArray JNICALL Java_com_software7_test_MacOSX509Certificates_retrieveCertificates
  (JNIEnv *env, jobject obj) {
      CFArrayRef certs = NULL;
      OSStatus status = SecTrustCopyAnchorCertificates(&certs);
      if (status != noErr) {
        jclass rte = (*env)->FindClass(env, "java/lang/RuntimeException");
        if (rte != NULL)
            (*env)->ThrowNew(env, rte, "error retrieving anchor certificates"); 
        (*env)->DeleteLocalRef(env, rte);
      }
  
      CFIndex ncerts = CFArrayGetCount(certs);
      jclass byteArrayClass = (*env)->FindClass(env, "[B");
      jobjectArray array = (*env)->NewObjectArray(env, ncerts, byteArrayClass, (*env)->NewByteArray(env, 0));

      for (int i = 0; i < ncerts; i++) {   
          SecCertificateRef certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i);
          CFDataRef certData = SecCertificateCopyData(certRef);
          int numBytes = CFDataGetLength(certData);
          jbyteArray jCert = (*env)->NewByteArray(env, numBytes);
          (*env)->SetByteArrayRegion(env, jCert, 0, numBytes, (const jbyte *)CFDataGetBytePtr(certData));         
          CFRelease(certData);
      
          (*env)->SetObjectArrayElement(env, array, i, jCert);
          (*env)->DeleteLocalRef(env, jCert);
      }
      CFRelease(certs);  
      return array;
  }
  

Java

package com.software7.test;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class MacOSX509Certificates {

    static {
        System.loadLibrary("maccerts");
    }

    private native byte[][] retrieveCertificates();

    public static void main(String[] args) {
        MacOSX509Certificates mc = new MacOSX509Certificates();
        mc.retrieveAndPrint();
    }

    private void retrieveAndPrint() {
        List<X509Certificate> x509Certificates = retrieve();
        for (X509Certificate x509c : x509Certificates) {
            System.out.println(x509c.getSubjectX500Principal());
        }
    }

    private List<X509Certificate> retrieve() {
        byte[][] certs = retrieveCertificates();
        return Arrays.stream(certs)
                .<X509Certificate>map(MacOSX509Certificates::convertToX509Certificate)
                .collect(Collectors.toList());
    }

    @SuppressWarnings("unchecked")
    private static <X509Certificate> X509Certificate convertToX509Certificate(byte[] bytes) {
        try {
            CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
            try (InputStream in = new ByteArrayInputStream(bytes)) {
                return (X509Certificate) certFactory.generateCertificate(in);
            }
        } catch (CertificateException | IOException e) {
            throw new RuntimeException(e);
        }
    }
}
  

构建

构建过程可以包括以下步骤:

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-14.0.1.jdk/Contents/Home/
javac -h . com/software7/test/MacOSX509Certificates.java
clang -c -fPIC -I$JAVA_HOME/include -I$JAVA_HOME/include/darwin com_software7_test_MacOSX509Certificates.c -o com_software7_test_MacOSX509Certificates.o
clang -dynamiclib -o libmaccerts.dylib com_software7_test_MacOSX509Certificates.o -lc -framework CoreFoundation -framework Security
mv libmaccerts.dylib ../out/production/read_mac_system_certs
rm com_software7_test_MacOSX509Certificates.o
rm com/software7/test/MacOSX509Certificates.class

测试

如果在Mac上运行此示例,它将返回:

CN=Go Daddy Root Certificate Authority - G2, O="GoDaddy.com, Inc.", L=Scottsdale, ST=Arizona, C=US
CN=SwissSign Platinum CA - G2, O=SwissSign AG, C=CH
CN=AddTrust Class 1 CA Root, OU=AddTrust TTP Network, O=AddTrust AB, C=SE
CN=Global Chambersign Root, OU=http://www.chambersign.org, O=AC Camerfirma SA CIF A82743287, C=EU
...