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