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
17package org.conscrypt;
18
19import java.security.PublicKey;
20import java.security.cert.TrustAnchor;
21import java.security.cert.X509Certificate;
22import java.util.ArrayList;
23import java.util.Collection;
24import java.util.HashMap;
25import java.util.List;
26import java.util.Map;
27import java.util.Set;
28import javax.security.auth.x500.X500Principal;
29
30/**
31 * Indexes {@code TrustAnchor} instances so they can be found in O(1)
32 * time instead of O(N).
33 */
34public final class TrustedCertificateIndex {
35
36    private final Map<X500Principal, List<TrustAnchor>> subjectToTrustAnchors
37            = new HashMap<X500Principal, List<TrustAnchor>>();
38
39    public TrustedCertificateIndex() {}
40
41    public TrustedCertificateIndex(Set<TrustAnchor> anchors) {
42        index(anchors);
43    }
44
45    private void index(Set<TrustAnchor> anchors) {
46        for (TrustAnchor anchor : anchors) {
47            index(anchor);
48        }
49    }
50
51    public TrustAnchor index(X509Certificate cert) {
52        TrustAnchor anchor = new TrustAnchor(cert, null);
53        index(anchor);
54        return anchor;
55    }
56
57    public void index(TrustAnchor anchor) {
58        X500Principal subject;
59        X509Certificate cert = anchor.getTrustedCert();
60        if (cert != null) {
61            subject = cert.getSubjectX500Principal();
62        } else {
63            subject = anchor.getCA();
64        }
65
66        synchronized (subjectToTrustAnchors) {
67            List<TrustAnchor> anchors = subjectToTrustAnchors.get(subject);
68            if (anchors == null) {
69                anchors = new ArrayList<TrustAnchor>(1);
70                subjectToTrustAnchors.put(subject, anchors);
71            }
72            anchors.add(anchor);
73        }
74    }
75
76    public void reset() {
77        synchronized (subjectToTrustAnchors) {
78            subjectToTrustAnchors.clear();
79        }
80    }
81
82    public void reset(Set<TrustAnchor> anchors) {
83        synchronized (subjectToTrustAnchors) {
84            reset();
85            index(anchors);
86        }
87    }
88
89    public TrustAnchor findByIssuerAndSignature(X509Certificate cert) {
90        X500Principal issuer = cert.getIssuerX500Principal();
91        synchronized (subjectToTrustAnchors) {
92            List<TrustAnchor> anchors = subjectToTrustAnchors.get(issuer);
93            if (anchors == null) {
94                return null;
95            }
96
97            for (TrustAnchor anchor : anchors) {
98                PublicKey publicKey;
99                try {
100                    X509Certificate caCert = anchor.getTrustedCert();
101                    if (caCert != null) {
102                        publicKey = caCert.getPublicKey();
103                    } else {
104                        publicKey = anchor.getCAPublicKey();
105                    }
106                    cert.verify(publicKey);
107                    return anchor;
108                } catch (Exception ignored) {
109                }
110            }
111        }
112        return null;
113    }
114
115    public TrustAnchor findBySubjectAndPublicKey(X509Certificate cert) {
116        X500Principal subject = cert.getSubjectX500Principal();
117        synchronized (subjectToTrustAnchors) {
118            List<TrustAnchor> anchors = subjectToTrustAnchors.get(subject);
119            if (anchors == null) {
120                return null;
121            }
122            return findBySubjectAndPublicKey(cert, anchors);
123        }
124    }
125
126    private static TrustAnchor findBySubjectAndPublicKey(X509Certificate cert,
127                                                         Collection<TrustAnchor> anchors) {
128        PublicKey certPublicKey = cert.getPublicKey();
129        for (TrustAnchor anchor : anchors) {
130            PublicKey caPublicKey;
131            try {
132                X509Certificate caCert = anchor.getTrustedCert();
133                if (caCert != null) {
134                    caPublicKey = caCert.getPublicKey();
135                } else {
136                    caPublicKey = anchor.getCAPublicKey();
137                }
138                if (caPublicKey.equals(certPublicKey)) {
139                    return anchor;
140                }
141            } catch (Exception e) {
142                // can happen with unsupported public key types
143            }
144        }
145        return null;
146    }
147}
148