Home | History | Annotate | Download | only in jsse
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package org.apache.harmony.xnet.provider.jsse;
     19 
     20 import java.security.InvalidAlgorithmParameterException;
     21 import java.security.KeyStore;
     22 import java.security.KeyStoreException;
     23 import java.security.cert.CertPath;
     24 import java.security.cert.CertPathValidator;
     25 import java.security.cert.CertPathValidatorException;
     26 import java.security.cert.CertificateEncodingException;
     27 import java.security.cert.CertificateException;
     28 import java.security.cert.CertificateFactory;
     29 import java.security.cert.PKIXParameters;
     30 import java.security.cert.TrustAnchor;
     31 import java.security.cert.X509Certificate;
     32 import java.util.Arrays;
     33 import java.util.Enumeration;
     34 import java.util.HashSet;
     35 import java.util.Iterator;
     36 import java.util.Set;
     37 import javax.net.ssl.X509TrustManager;
     38 
     39 /**
     40  *
     41  * TrustManager implementation. The implementation is based on CertPathValidator
     42  * PKIX and CertificateFactory X509 implementations. This implementations should
     43  * be provided by some certification provider.
     44  *
     45  * @see javax.net.ssl.X509TrustManager
     46  */
     47 public class TrustManagerImpl implements X509TrustManager {
     48 
     49     private CertPathValidator validator;
     50 
     51     private PKIXParameters params;
     52 
     53     private Exception err = null;
     54 
     55     private CertificateFactory factory;
     56 
     57     /**
     58      * Creates trust manager implementation
     59      *
     60      * @param ks
     61      */
     62     public TrustManagerImpl(KeyStore ks) {
     63         try {
     64             validator = CertPathValidator.getInstance("PKIX");
     65             factory = CertificateFactory.getInstance("X509");
     66             byte[] nameConstrains = null;
     67             Set<TrustAnchor> trusted = new HashSet<TrustAnchor>();
     68             for (Enumeration<String> en = ks.aliases(); en.hasMoreElements();) {
     69                 final String alias = en.nextElement();
     70                 final X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
     71                 if (cert != null) {
     72                     trusted.add(new TrustAnchor(cert, nameConstrains));
     73                 }
     74             }
     75             params = new PKIXParameters(trusted);
     76             params.setRevocationEnabled(false);
     77         } catch (Exception e) {
     78             err = e;
     79         }
     80     }
     81 
     82 // BEGIN android-added
     83     /**
     84      * Indexes trust anchors so they can be found in O(1) instead of O(N) time.
     85      */
     86     public void indexTrustAnchors() throws CertificateEncodingException,
     87             InvalidAlgorithmParameterException, KeyStoreException {
     88         params = new IndexedPKIXParameters(params.getTrustAnchors());
     89         params.setRevocationEnabled(false);
     90     }
     91 // END android-added
     92 
     93     /**
     94      * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],
     95      *      String)
     96      */
     97     public void checkClientTrusted(X509Certificate[] chain, String authType)
     98             throws CertificateException {
     99         if (chain == null || chain.length == 0 || authType == null
    100                 || authType.length() == 0) {
    101             throw new IllegalArgumentException("null or zero-length parameter");
    102         }
    103         if (err != null) {
    104             throw new CertificateException(err);
    105         }
    106         // BEGIN android-added
    107         // Caters to degenerate special case where we can't
    108         // establish an actual certificate chain the usual way,
    109         // but have the peer certificate in our trust store.
    110         if (isDirectlyTrustedCert(chain)) {
    111             return;
    112         }
    113         // END android-added
    114         try {
    115             // BEGIN android-changed
    116             CertPath certPath = factory.generateCertPath(Arrays.asList(chain));
    117             if (!Arrays.equals(chain[0].getEncoded(),
    118                     ((X509Certificate)certPath.getCertificates().get(0))
    119                     .getEncoded())) {
    120                 // Sanity check failed (shouldn't ever happen, but we
    121                 // are using pretty remote code)
    122                 throw new CertificateException("Certificate chain error");
    123             }
    124             validator.validate(certPath, params);
    125             // END android-changed
    126         } catch (InvalidAlgorithmParameterException e) {
    127             throw new CertificateException(e);
    128         } catch (CertPathValidatorException e) {
    129             throw new CertificateException(e);
    130         }
    131     }
    132 
    133     /**
    134      * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],
    135      *      String)
    136      */
    137     public void checkServerTrusted(X509Certificate[] chain, String authType)
    138             throws CertificateException {
    139         if (chain == null || chain.length == 0 || authType == null
    140                 || authType.length() == 0) {
    141             throw new IllegalArgumentException("null or zero-length parameter");
    142         }
    143         if (err != null) {
    144             throw new CertificateException(err);
    145         }
    146         // BEGIN android-changed
    147         CertificateException ce = null;
    148         try {
    149             CertPath certPath = factory.generateCertPath(Arrays.asList(chain));
    150             if (!Arrays.equals(chain[0].getEncoded(),
    151                     certPath.getCertificates().get(0).getEncoded())) {
    152                 // Sanity check failed (shouldn't ever happen, but we
    153                 // are using pretty remote code)
    154                 throw new CertificateException("Certificate chain error");
    155             }
    156             validator.validate(certPath, params);
    157             // END android-changed
    158         } catch (InvalidAlgorithmParameterException e) {
    159             ce = new CertificateException(e);
    160         } catch (CertPathValidatorException e) {
    161             ce = new CertificateException(e);
    162         }
    163         // BEGIN android-added
    164         if (ce != null) {
    165             // Caters to degenerate special case where we can't
    166             // establish an actual certificate chain the usual way
    167             // but have the peer certificate in our trust store.
    168             if (!isDirectlyTrustedCert(chain)) {
    169                 throw ce;
    170             }
    171         }
    172         // END android-added
    173     }
    174 
    175     /**
    176      * Checks whether the given chain is just a certificate
    177      * that we have in our trust store.
    178      *
    179      * @param chain The certificate chain.
    180      *
    181      * @return True if the certificate is in our trust store, false otherwise.
    182      */
    183     private boolean isDirectlyTrustedCert(X509Certificate[] chain) {
    184         byte[] questionable;
    185 
    186         if (chain.length == 1) {
    187             if (params instanceof IndexedPKIXParameters) {
    188                 IndexedPKIXParameters index = (IndexedPKIXParameters) params;
    189                 return index.isDirectlyTrusted(chain[0]);
    190             } else {
    191                 try {
    192                     questionable = chain[0].getEncoded();
    193                     Set<TrustAnchor> anchors = params.getTrustAnchors();
    194 
    195                     for (TrustAnchor trustAnchor : anchors) {
    196                         byte[] trusted = trustAnchor.getTrustedCert()
    197                                 .getEncoded();
    198                         if (Arrays.equals(questionable, trusted)) {
    199                             return true;
    200                         }
    201                     }
    202                 } catch (CertificateEncodingException e) {
    203                     // Ignore.
    204                 }
    205             }
    206 
    207         }
    208 
    209         return false;
    210     }
    211 // END android-changed
    212 
    213     /**
    214      * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
    215      */
    216     public X509Certificate[] getAcceptedIssuers() {
    217         if (params == null) {
    218             return new X509Certificate[0];
    219         }
    220         Set<TrustAnchor> anchors = params.getTrustAnchors();
    221         X509Certificate[] certs = new X509Certificate[anchors.size()];
    222         int i = 0;
    223         for (Iterator<TrustAnchor> it = anchors.iterator(); it.hasNext();) {
    224             certs[i++] = it.next().getTrustedCert();
    225         }
    226         return certs;
    227     }
    228 
    229 }
    230