Java 安全 - 使用 Public 密钥的 X509 证书验证

Java Security - X509 Certificate Verification with Public Key

我正在做的一个项目有很多安全操作。我以前从未遇到过安全问题。因此,我的问题可以是初级水平。

在我的问题中,我得到一个字节数组数据,其中包含证书和一些其他参数。我需要验证此证书及其签名。但是我无法处理签名验证。事实上,我不知道应该使用哪个 public 密钥来验证。

代码如下。 感谢您的帮助..!

public boolean startValidation(PublicKey publicKey) {
    CertificateFactory cf;
    try {
         cf = CertificateFactory.getInstance("X.509");
    } catch (CertificateException e) {
        e.printStackTrace();
        return false;
    }
    try {
        certificate = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certBytes));
    } catch (CertificateException e) {
        e.printStackTrace();
        setCertError(0);
        return false;
    }
    if (!checkProvider()){
        setCertError(1);
        return false;
    }

    boolean[] usages = certificate.getKeyUsage();
    boolean usage = usages[0] && usages[2];
    if (!usage){
        setCertError(2);
    }

    try {
        certificate.checkValidity();
    } catch (CertificateNotYetValidException e) {
        e.printStackTrace();
        setCertError(3);
        return false;
    } catch (CertificateExpiredException e) {
        e.printStackTrace();
        setCertError(4);
        return false;
    }

    System.out.println("Sign Algorithm Name " + certificate.getSigAlgName());
    //Output is SHA256WithRSAEncryption

   // Problem area
    try {
        Signature signature = Signature.getInstance("SHA256WithRSA");
        signature.initVerify(publicKey); //Should I use this certificate.getPublicKey() or what ??
        if(signature.verify(certificate.getSignature())){
            System.out.println("Accepted");
        }else System.out.println("Failure");
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (SignatureException e) {
        e.printStackTrace();
    }

    System.out.println("Public Key Format " + publicKey.getFormat() + "\nPublic Key Algorithm " + publicKey.getAlgorithm() );
    System.out.println("Subject DN -> " + certificate.getSubjectDN().getName());


    return true;
}

典型的 PKI 系统使用 Certificate Authorities 向主体颁发证书(通过签名)。通过签署证书颁发机构形成从 CA 到主体证书的链,如果 CA1(根 CA)发出 CA2 的(中间 CA[=31,则该链可以包含多个 CA =]) 证书,它又唱出主题的证书。这在 Internet(对于 SSL/TLS)和数字签名场景中很常见。

因此您很可能至少需要一个 CA 的证书,它是 public 验证主体证书的密钥。您的程序也可以支持多个独立的 CA。您的程序接受的 CA 通常称为 Trust Anchors. Keeping the Trust Anchors in a KeyStore 也很方便。

因此,您最终可能会得到一个包含信任锚和中间 CA 的密钥库。从那里开始,您需要形成链并验证 chin 中从根 CA(信任锚)到您要验证的主题证书的所有证书的签名。

为了正确的证书验证,除了链的签名之外,您还需要检查其他内容,例如密钥使用、基本约束、吊销信息等。所有这些都在证书路径的 RFC5280 中指定验证。

幸运的是,已经有 Java API 形式的 CertPath API 可以帮助您。一个适合您的用例的非常基本的示例是:

final CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");

final X509Certificate certificateToCheck = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(certBytes));

final KeyStore trustStore = KeyStore.getInstance("JKS");
InputStream keyStoreStream = ...
trustStore.load(keyStoreStrem, "your password".toCharArray());

final CertPathBuilder certPathBuilder = CertPathBuilder.getInstance("PKIX");
final X509CertSelector certSelector = new X509CertSelector();
certSelector.setCertificate(certificateToCheck);

final CertPathParameters certPathParameters = new PKIXBuilderParameters(trustStore, certSelector);
final CertPathBuilderResult certPathBuilderResult = certPathBuilder.build(certPathParameters);
final CertPath certPath = certPathBuilderResult.getCertPath();

final CertPathValidator certPathValidator = CertPathValidator.getInstance("PKIX");
final PKIXParameters validationParameters = new PKIXParameters(trustStore);
validationParameters.setRevocationEnabled(true); // if you want to check CRL
final X509CertSelector keyUsageSelector = new X509CertSelector();
keyUsageSelector.setKeyUsage(new boolean[] { true, false, true }); // to check digitalSignature and keyEncipherment bits
validationParameters.setTargetCertConstraints(keyUsageSelector);
final PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) certPathValidator.validate(certPath, validationParameters);

System.out.println(result);

但请注意,有许多 more nuances and details 必须妥善解决才能使实施真正安全。错误的证书路径检查是安全问题的常见原因。