1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.conscrypt;
18
19import java.security.cert.CertificateException;
20import java.security.cert.X509Certificate;
21import java.security.interfaces.DSAPublicKey;
22import java.security.interfaces.ECPublicKey;
23import java.security.interfaces.RSAPublicKey;
24import java.util.List;
25
26/**
27 * Analyzes the cryptographic strength of a chain of X.509 certificates.
28 *
29 * @hide
30 */
31@Internal
32public final class ChainStrengthAnalyzer {
33
34    private static final int MIN_RSA_MODULUS_LEN_BITS = 1024;
35
36    private static final int MIN_EC_FIELD_SIZE_BITS = 160;
37
38    private static final int MIN_DSA_P_LEN_BITS = 1024;
39    private static final int MIN_DSA_Q_LEN_BITS = 160;
40
41    private static final String[] SIGNATURE_ALGORITHM_OID_BLACKLIST = {
42        "1.2.840.113549.1.1.2", // md2WithRSAEncryption
43        "1.2.840.113549.1.1.3", // md4WithRSAEncryption
44        "1.2.840.113549.1.1.4", // md5WithRSAEncryption
45    };
46
47    public static final void check(X509Certificate[] chain) throws CertificateException {
48        for (X509Certificate cert : chain) {
49            try {
50                checkCert(cert);
51            } catch (CertificateException e) {
52                throw new CertificateException("Unacceptable certificate: "
53                        + cert.getSubjectX500Principal(), e);
54            }
55        }
56    }
57
58    public static final void check(List<X509Certificate> chain) throws CertificateException {
59        for (X509Certificate cert : chain) {
60            try {
61                checkCert(cert);
62            } catch (CertificateException e) {
63                throw new CertificateException("Unacceptable certificate: "
64                        + cert.getSubjectX500Principal(), e);
65            }
66        }
67    }
68
69    public static final void checkCert(X509Certificate cert) throws CertificateException {
70        checkKeyLength(cert);
71        checkSignatureAlgorithm(cert);
72    }
73
74    private static final void checkKeyLength(X509Certificate cert) throws CertificateException {
75        Object pubkey = cert.getPublicKey();
76        if (pubkey instanceof RSAPublicKey) {
77            int modulusLength = ((RSAPublicKey) pubkey).getModulus().bitLength();
78            if (modulusLength < MIN_RSA_MODULUS_LEN_BITS) {
79                throw new CertificateException(
80                        "RSA modulus is < " + MIN_RSA_MODULUS_LEN_BITS + " bits");
81            }
82        } else if (pubkey instanceof ECPublicKey) {
83            int fieldSizeBits =
84                    ((ECPublicKey) pubkey).getParams().getCurve().getField().getFieldSize();
85            if (fieldSizeBits < MIN_EC_FIELD_SIZE_BITS) {
86                throw new CertificateException(
87                        "EC key field size is < " + MIN_EC_FIELD_SIZE_BITS + " bits");
88            }
89        } else if (pubkey instanceof DSAPublicKey) {
90            int pLength = ((DSAPublicKey) pubkey).getParams().getP().bitLength();
91            int qLength = ((DSAPublicKey) pubkey).getParams().getQ().bitLength();
92            if ((pLength < MIN_DSA_P_LEN_BITS) || (qLength < MIN_DSA_Q_LEN_BITS)) {
93                throw new CertificateException(
94                        "DSA key length is < (" + MIN_DSA_P_LEN_BITS + ", " + MIN_DSA_Q_LEN_BITS
95                        + ") bits");
96            }
97        } else {
98            // Unknown keys will be of type X509PublicKey.
99            throw new CertificateException("Rejecting unknown key class " + pubkey.getClass().getName());
100        }
101    }
102
103    private static final void checkSignatureAlgorithm(
104            X509Certificate cert) throws CertificateException {
105        String oid = cert.getSigAlgOID();
106        for (String blacklisted : SIGNATURE_ALGORITHM_OID_BLACKLIST) {
107            if (oid.equals(blacklisted)) {
108                throw new CertificateException("Signature uses an insecure hash function: " + oid);
109            }
110        }
111    }
112}
113
114