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