Home | History | Annotate | Download | only in jsse
      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.apache.harmony.xnet.provider.jsse;
     18 
     19 import java.security.InvalidAlgorithmParameterException;
     20 import java.security.KeyStoreException;
     21 import java.security.cert.CertPathValidatorException;
     22 import java.security.cert.CertificateEncodingException;
     23 import java.security.cert.PKIXParameters;
     24 import java.security.cert.TrustAnchor;
     25 import java.security.cert.X509Certificate;
     26 import java.util.ArrayList;
     27 import java.util.Arrays;
     28 import java.util.HashMap;
     29 import java.util.List;
     30 import java.util.Map;
     31 import java.util.Set;
     32 import java.util.logging.Level;
     33 import java.util.logging.Logger;
     34 import javax.security.auth.x500.X500Principal;
     35 
     36 /**
     37  * Indexes trust anchors so they can be found in O(1) time instead of O(N).
     38  */
     39 public class IndexedPKIXParameters extends PKIXParameters {
     40 
     41     final Map<Bytes, TrustAnchor> encodings
     42             = new HashMap<Bytes, TrustAnchor>();
     43     final Map<X500Principal, TrustAnchor> bySubject
     44             = new HashMap<X500Principal, TrustAnchor>();
     45     final Map<X500Principal, List<TrustAnchor>> byCA
     46             = new HashMap<X500Principal, List<TrustAnchor>>();
     47 
     48     public IndexedPKIXParameters(Set<TrustAnchor> anchors)
     49             throws KeyStoreException, InvalidAlgorithmParameterException,
     50             CertificateEncodingException {
     51         super(anchors);
     52 
     53         for (TrustAnchor anchor : anchors) {
     54             X509Certificate cert = anchor.getTrustedCert();
     55 
     56             Bytes encoded = new Bytes(cert.getEncoded());
     57             encodings.put(encoded, anchor);
     58 
     59             X500Principal subject = cert.getSubjectX500Principal();
     60             if (bySubject.put(subject, anchor) != null) {
     61                 // TODO: Should we allow this?
     62                 throw new KeyStoreException("Two certs have the same subject: "
     63                         + subject);
     64             }
     65 
     66             X500Principal ca = anchor.getCA();
     67             List<TrustAnchor> caAnchors = byCA.get(ca);
     68             if (caAnchors == null) {
     69                 caAnchors = new ArrayList<TrustAnchor>();
     70                 byCA.put(ca, caAnchors);
     71             }
     72             caAnchors.add(anchor);
     73         }
     74     }
     75 
     76     public TrustAnchor findTrustAnchor(X509Certificate cert)
     77             throws CertPathValidatorException {
     78         // Mimic the alg in CertPathValidatorUtilities.findTrustAnchor().
     79         Exception verificationException = null;
     80         X500Principal issuer = cert.getIssuerX500Principal();
     81 
     82         List<TrustAnchor> anchors = byCA.get(issuer);
     83         if (anchors != null) {
     84             for (TrustAnchor caAnchor : anchors) {
     85                 try {
     86                     cert.verify(caAnchor.getCAPublicKey());
     87                     return caAnchor;
     88                 } catch (Exception e) {
     89                     verificationException = e;
     90                 }
     91             }
     92         }
     93 
     94         TrustAnchor anchor = bySubject.get(issuer);
     95         if (anchor != null) {
     96             try {
     97                 cert.verify(anchor.getTrustedCert().getPublicKey());
     98                 return anchor;
     99             } catch (Exception e) {
    100                 verificationException = e;
    101             }
    102         }
    103 
    104         try {
    105             Bytes encoded = new Bytes(cert.getEncoded());
    106             anchor = encodings.get(encoded);
    107             if (anchor != null) {
    108                 return anchor;
    109             }
    110         } catch (Exception e) {
    111             Logger.getLogger(IndexedPKIXParameters.class.getName()).log(
    112                     Level.WARNING, "Error encoding cert.", e);
    113         }
    114 
    115         // Throw last verification exception.
    116         if (verificationException != null) {
    117             throw new CertPathValidatorException("TrustAnchor found but"
    118                     + " certificate verification failed.",
    119                     verificationException);
    120         }
    121 
    122         return null;
    123     }
    124 
    125     /**
    126      * Returns true if the given certificate is found in the trusted key
    127      * store.
    128      */
    129     public boolean isDirectlyTrusted(X509Certificate cert) {
    130         try {
    131             Bytes encoded = new Bytes(cert.getEncoded());
    132             return encodings.containsKey(encoded);
    133         } catch (Exception e) {
    134             Logger.getLogger(IndexedPKIXParameters.class.getName()).log(
    135                     Level.WARNING, "Error encoding cert.", e);
    136             return false;
    137         }
    138     }
    139 
    140     /**
    141      * Wraps a byte[] and adds equals() and hashCode() support.
    142      */
    143     static class Bytes {
    144         final byte[] bytes;
    145         final int hash;
    146         Bytes(byte[] bytes) {
    147             this.bytes = bytes;
    148             this.hash = Arrays.hashCode(bytes);
    149         }
    150         @Override public int hashCode() {
    151             return hash;
    152         }
    153         @Override public boolean equals(Object o) {
    154             return Arrays.equals(bytes, ((Bytes) o).bytes);
    155         }
    156     }
    157 }
    158