X509认证路径验证

时间:2017-10-12 12:04:33

标签: java certificate x509

在我开发的应用程序中,我检查客户端在传入消息中发出的证书:除了基本证书有效性检查(证书签名,到期日期......)之外,我还检查客户端是否可信。< / p>

为此,我创建了一个仅包含可信证书的密钥库:如果收到的证书不在列表中,我拒绝传入的消息。 客户端证书位于证书路径中,我检查所有证书路径。

对于认证路径验证,我使用以下算法: (wikipedia

1)从客户证书开始

2)虽然当前证书不是根证书:

3)在密钥库中搜索父证书:为此,我在密钥库中搜索当前证书的SubjectDN = IssuerDN的证书。如果未找到,则测试的证书无效

4)使用父证书的公钥

检查当前证书的签名

因此,整个路径已经过验证

这里是验证器的完整代码: (感谢this article)的作者

请注意,此处,撤销列表(CRL)检查已禁用。

 public class CertificateChainValidator {
private static Logger logger = Logger.getLogger(CertificateChainValidator.class);

/**
 * path of the keystore.
 */
private static final String TRUSTED_KEYSTORE = "/trusted.jks";

/**
 * password for opening the keystore.
 */
private static final char[] TRUSTED_KEYSTORE_PWD = new char[] { '$' , '$' , '$','$','$','$','$'};

/**
 * the keystore loaded.
 */
private static KeyStore trustedKeystore;

/**
 * Validate keychain
 *
 * @param client
 *            is the client X509Certificate
 * @param keyStore
 *            containing all trusted certificate
 * @return true if validation until root certificate success, false
 *         otherwise
 * @throws KeyStoreException
 * @throws CertificateException
 * @throws InvalidAlgorithmParameterException
 * @throws NoSuchAlgorithmException
 * @throws NoSuchProviderException
 */
public static boolean validateKeyChain(final X509Certificate client, final KeyStore keyStore)
        throws KeyStoreException, CertificateException, InvalidAlgorithmParameterException,
        NoSuchAlgorithmException, NoSuchProviderException {
    X509Certificate[] certs = new X509Certificate[keyStore.size()];
    int i = 0;
    Enumeration<String> alias = keyStore.aliases();

    while (alias.hasMoreElements()) {
        certs[i++] = (X509Certificate) keyStore.getCertificate(alias.nextElement());
    }

    return validateKeyChain(client, certs);
}

/**
 * Validate keychain
 *
 * @param client
 *            is the client X509Certificate
 * @param trustedCerts
 *            is Array containing all trusted X509Certificate
 * @return true if validation until root certificate success, false
 *         otherwise
 * @throws CertificateException
 * @throws InvalidAlgorithmParameterException
 * @throws NoSuchAlgorithmException
 * @throws NoSuchProviderException
 */
@SuppressWarnings("unchecked")
private static boolean validateKeyChain(final X509Certificate client, final X509Certificate... trustedCerts)
        throws CertificateException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
        NoSuchProviderException {
    boolean found = false;
    int i = trustedCerts.length;
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    TrustAnchor anchor;
    @SuppressWarnings("rawtypes")
    Set anchors;
    CertPath path;
    @SuppressWarnings("rawtypes")
    List list;
    PKIXParameters params;
    CertPathValidator validator = CertPathValidator.getInstance("PKIX");

    while (!found && i > 0) {
        anchor = new TrustAnchor(trustedCerts[--i], null);
        anchors = Collections.singleton(anchor);

        list = Arrays.asList(new Certificate[] { client });
        path = cf.generateCertPath(list);

        params = new PKIXParameters(anchors);
        params.setRevocationEnabled(false);

        if (logger.isDebugEnabled()) {
            logger.debug(String.format("Test certificate chain : client=%s IssuerDN=%s", client.getIssuerDN(),
                    trustedCerts[i].getSubjectDN()));
        }

        if (client.getIssuerDN().equals(trustedCerts[i].getSubjectDN())) {

            if (logger.isDebugEnabled()) {
                logger.debug(String.format("Validation certificate : IssuerDN=%s", trustedCerts[i].getSubjectDN()));
            }

            try {
                validator.validate(path, params);
                if (isSelfSigned(trustedCerts[i])) {
                    // found root ca
                    if (logger.isDebugEnabled()) {
                        logger.debug(String.format("certificate root validated : IssuerDN=%s",
                                trustedCerts[i].getSubjectDN()));
                    }
                    found = true;
                } else if (!client.equals(trustedCerts[i])) {
                    // find parent ca
                    if (logger.isDebugEnabled()) {
                        logger.debug(String.format("certificate intermerdiart validated : IssuerDN=%s",
                                trustedCerts[i].getSubjectDN()));
                    }
                    found = validateKeyChain(trustedCerts[i], trustedCerts);
                }
            } catch (CertPathValidatorException e) {
                // validation fail, check next certifiacet in the
                // trustedCerts array
            }
        }
    }

    return found;
}

/**
 *
 * @param cert
 *            is X509Certificate that will be tested
 * @return true if cert is self signed, false otherwise
 * @throws CertificateException
 * @throws NoSuchAlgorithmException
 * @throws NoSuchProviderException
 */
private static boolean isSelfSigned(final X509Certificate cert)
        throws CertificateException, NoSuchAlgorithmException, NoSuchProviderException {
    try {
        PublicKey key = cert.getPublicKey();

        cert.verify(key);
        return true;
    } catch (SignatureException sigEx) {
        return false;
    } catch (InvalidKeyException keyEx) {
        return false;
    }
}

/**
 * donne le trusted keystore (le charge une seule fois).
 *
 * @return
 * @throws CertificateIsabelSignatureVerificationException
 */
private synchronized static KeyStore getTrustedKeystore() throws CertificateIsabelSignatureVerificationException {
    if (trustedKeystore == null) {
        trustedKeystore = loadKeystore(TRUSTED_KEYSTORE, TRUSTED_KEYSTORE_PWD);
    }
    return trustedKeystore;
}

/**
 * chargement du keystore spécifié.
 *
 * @param path
 *            chemin du keystore, dans le classpath.
 * @return le keystore chargé.
 * @throws CertificateIsabelSignatureVerificationException
 */
private static KeyStore loadKeystore(final String path, final char[] passsword)
        throws CertificateIsabelSignatureVerificationException {
    try {

        if (logger.isDebugEnabled()) {
            logger.debug(String.format("loading keystore %s ...", path));
        }

        KeyStore ks = KeyStore.getInstance("JKS");
        ks.load( //
                CertificateChainValidator.class.getResourceAsStream(path) //
                , passsword //
        );

        return ks;
    } catch (Exception ex) {
        throw new CertificateIsabelSignatureVerificationException(
                String.format("Echec lecture du keystore %s", path), ex);
    }
}

/**
 * Valide le certificat client : s'assure qu'il est valide et vérifie qu'il
 * est bien associé à un certifact root connu (dans trusted.jks).
 *
 * @param certEncoded
 * @throws CertificateIsabelSignatureVerificationException
 */
public static void validateClientCertificate(final byte[] certEncoded)
        throws CertificateIsabelSignatureVerificationException {

    try {
        // le keystore contenant tous les certificats reconnus
        KeyStore ks = getTrustedKeystore();

        // récupère le certificat client
        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        X509Certificate certificat = (X509Certificate) certFactory
                .generateCertificate(new ByteArrayInputStream(certEncoded));

        if (logger.isDebugEnabled()) {
            logger.debug(String.format("certificate validation : IssuerDN=%s", certificat.getSubjectDN()));
        }

        boolean valid = validateKeyChain(certificat, ks);
        if (logger.isDebugEnabled()) {
            logger.debug(String.format("certificate valid : %s", valid));
        }
        if (!valid) {
            throw new CertificateIsabelSignatureVerificationException("The certificate is not valid");
        }

    } catch (CertificateIsabelSignatureVerificationException ex) {
        throw ex;
    } catch (Exception ex) {
        throw new CertificateIsabelSignatureVerificationException("Error during validation", ex);
    }
}

}

1 个答案:

答案 0 :(得分:2)

您无法比较issuerDN,因为任何人都可以使用该字符串创建证书。

每个证书都已使用颁发者的私钥进行数字签名,因此您需要使用信任库中现有证书的公钥验证客户端证书的签名。如果匹配,那么您的证书将被信任&#34;,但继续使用认证链中的下一个证书。

注意:我没有检查你的代码。您可能需要查看建议的链接