Home | History | Annotate | Download | only in conscrypt
      1 /*
      2  * Copyright (C) 2009 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.PublicKey;
     20 import java.security.cert.TrustAnchor;
     21 import java.security.cert.X509Certificate;
     22 import java.util.Arrays;
     23 import java.util.ArrayList;
     24 import java.util.Collection;
     25 import java.util.Collections;
     26 import java.util.HashMap;
     27 import java.util.HashSet;
     28 import java.util.List;
     29 import java.util.Map;
     30 import java.util.Set;
     31 import javax.security.auth.x500.X500Principal;
     32 
     33 /**
     34  * Indexes {@code TrustAnchor} instances so they can be found in O(1)
     35  * time instead of O(N).
     36  */
     37 public final class TrustedCertificateIndex {
     38 
     39     private final Map<X500Principal, List<TrustAnchor>> subjectToTrustAnchors
     40             = new HashMap<X500Principal, List<TrustAnchor>>();
     41 
     42     public TrustedCertificateIndex() {}
     43 
     44     public TrustedCertificateIndex(Set<TrustAnchor> anchors) {
     45         index(anchors);
     46     }
     47 
     48     private void index(Set<TrustAnchor> anchors) {
     49         for (TrustAnchor anchor : anchors) {
     50             index(anchor);
     51         }
     52     }
     53 
     54     public TrustAnchor index(X509Certificate cert) {
     55         TrustAnchor anchor = new TrustAnchor(cert, null);
     56         index(anchor);
     57         return anchor;
     58     }
     59 
     60     public void index(TrustAnchor anchor) {
     61         X500Principal subject;
     62         X509Certificate cert = anchor.getTrustedCert();
     63         if (cert != null) {
     64             subject = cert.getSubjectX500Principal();
     65         } else {
     66             subject = anchor.getCA();
     67         }
     68 
     69         synchronized (subjectToTrustAnchors) {
     70             List<TrustAnchor> anchors = subjectToTrustAnchors.get(subject);
     71             if (anchors == null) {
     72                 anchors = new ArrayList<TrustAnchor>(1);
     73                 subjectToTrustAnchors.put(subject, anchors);
     74             } else {
     75                 // Avoid indexing the same certificate multiple times
     76                 if (cert != null) {
     77                     for (TrustAnchor entry : anchors) {
     78                         if (cert.equals(entry.getTrustedCert())) {
     79                             return;
     80                         }
     81                     }
     82                 }
     83             }
     84             anchors.add(anchor);
     85         }
     86     }
     87 
     88     public void reset() {
     89         synchronized (subjectToTrustAnchors) {
     90             subjectToTrustAnchors.clear();
     91         }
     92     }
     93 
     94     public void reset(Set<TrustAnchor> anchors) {
     95         synchronized (subjectToTrustAnchors) {
     96             reset();
     97             index(anchors);
     98         }
     99     }
    100 
    101     public TrustAnchor findByIssuerAndSignature(X509Certificate cert) {
    102         X500Principal issuer = cert.getIssuerX500Principal();
    103         synchronized (subjectToTrustAnchors) {
    104             List<TrustAnchor> anchors = subjectToTrustAnchors.get(issuer);
    105             if (anchors == null) {
    106                 return null;
    107             }
    108 
    109             for (TrustAnchor anchor : anchors) {
    110                 PublicKey publicKey;
    111                 try {
    112                     X509Certificate caCert = anchor.getTrustedCert();
    113                     if (caCert != null) {
    114                         publicKey = caCert.getPublicKey();
    115                     } else {
    116                         publicKey = anchor.getCAPublicKey();
    117                     }
    118                     cert.verify(publicKey);
    119                     return anchor;
    120                 } catch (Exception ignored) {
    121                 }
    122             }
    123         }
    124         return null;
    125     }
    126 
    127     public TrustAnchor findBySubjectAndPublicKey(X509Certificate cert) {
    128         X500Principal subject = cert.getSubjectX500Principal();
    129         synchronized (subjectToTrustAnchors) {
    130             List<TrustAnchor> anchors = subjectToTrustAnchors.get(subject);
    131             if (anchors == null) {
    132                 return null;
    133             }
    134             return findBySubjectAndPublicKey(cert, anchors);
    135         }
    136     }
    137 
    138     private static TrustAnchor findBySubjectAndPublicKey(X509Certificate cert,
    139                                                          Collection<TrustAnchor> anchors) {
    140         PublicKey certPublicKey = cert.getPublicKey();
    141         for (TrustAnchor anchor : anchors) {
    142             PublicKey caPublicKey;
    143             try {
    144                 X509Certificate caCert = anchor.getTrustedCert();
    145                 if (caCert != null) {
    146                     caPublicKey = caCert.getPublicKey();
    147                 } else {
    148                     caPublicKey = anchor.getCAPublicKey();
    149                 }
    150                 if (caPublicKey.equals(certPublicKey)) {
    151                     return anchor;
    152                 } else {
    153                     // PublicKey.equals is not required to compare keys across providers. Fall back
    154                     // to checking using the encoded form.
    155                     if ("X.509".equals(caPublicKey.getFormat())
    156                             && "X.509".equals(certPublicKey.getFormat())) {
    157                         byte[] caPublicKeyEncoded = caPublicKey.getEncoded();
    158                         byte[] certPublicKeyEncoded = certPublicKey.getEncoded();
    159                         if (certPublicKeyEncoded != null
    160                                 && caPublicKeyEncoded != null
    161                                 && Arrays.equals(caPublicKeyEncoded, certPublicKeyEncoded)) {
    162                             return anchor;
    163                         }
    164                     }
    165                 }
    166             } catch (Exception e) {
    167                 // can happen with unsupported public key types
    168             }
    169         }
    170         return null;
    171     }
    172 
    173     public Set<TrustAnchor> findAllByIssuerAndSignature(X509Certificate cert) {
    174         X500Principal issuer = cert.getIssuerX500Principal();
    175         synchronized (subjectToTrustAnchors) {
    176             List<TrustAnchor> anchors = subjectToTrustAnchors.get(issuer);
    177             if (anchors == null) {
    178                 return Collections.<TrustAnchor>emptySet();
    179             }
    180 
    181             Set<TrustAnchor> result = new HashSet<TrustAnchor>();
    182             for (TrustAnchor anchor : anchors) {
    183                 try {
    184                     PublicKey publicKey;
    185                     X509Certificate caCert = anchor.getTrustedCert();
    186                     if (caCert != null) {
    187                         publicKey = caCert.getPublicKey();
    188                     } else {
    189                         publicKey = anchor.getCAPublicKey();
    190                     }
    191                     if (publicKey == null) {
    192                         continue;
    193                     }
    194                     cert.verify(publicKey);
    195                     result.add(anchor);
    196                 } catch (Exception ignored) {
    197                 }
    198             }
    199             return result;
    200         }
    201     }
    202 
    203 }
    204