Home | History | Annotate | Download | only in conscrypt
      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 
     17 package org.conscrypt;
     18 
     19 import java.security.cert.CertificateException;
     20 import java.security.cert.X509Certificate;
     21 import java.security.interfaces.DSAPublicKey;
     22 import java.security.interfaces.ECPublicKey;
     23 import java.security.interfaces.RSAPublicKey;
     24 import java.util.List;
     25 
     26 /**
     27  * Analyzes the cryptographic strength of a chain of X.509 certificates.
     28  *
     29  * @hide
     30  */
     31 @Internal
     32 public 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 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 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