106fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom/*
206fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom * Copyright (C) 2009 The Android Open Source Project
306fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom *
406fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom * Licensed under the Apache License, Version 2.0 (the "License");
506fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom * you may not use this file except in compliance with the License.
606fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom * You may obtain a copy of the License at
706fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom *
806fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom *      http://www.apache.org/licenses/LICENSE-2.0
906fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom *
1006fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom * Unless required by applicable law or agreed to in writing, software
1106fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom * distributed under the License is distributed on an "AS IS" BASIS,
1206fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1306fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom * See the License for the specific language governing permissions and
1406fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom * limitations under the License.
1506fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom */
1606fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom
1738375a4d0b3d34e2babbd2f6a013976c7c439696Kenny Rootpackage org.conscrypt;
1806fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom
19a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstromimport java.security.PublicKey;
2006fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstromimport java.security.cert.CertPathValidatorException;
2106fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstromimport java.security.cert.TrustAnchor;
2206fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstromimport java.security.cert.X509Certificate;
2306fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstromimport java.util.ArrayList;
24a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstromimport java.util.Collection;
2506fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstromimport java.util.HashMap;
2606fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstromimport java.util.List;
2706fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstromimport java.util.Map;
2806fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstromimport java.util.Set;
297365de1056414750d0a7d1fdd26025fd247f0d04Jesse Wilsonimport javax.security.auth.x500.X500Principal;
3006fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom
3106fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom/**
32c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom * Indexes {@code TrustAnchor} instances so they can be found in O(1)
33c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom * time instead of O(N).
3406fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom */
35c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrompublic final class TrustedCertificateIndex {
3606fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom
37a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom    private final Map<X500Principal, List<TrustAnchor>> subjectToTrustAnchors
3806fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom            = new HashMap<X500Principal, List<TrustAnchor>>();
3906fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom
40c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom    public TrustedCertificateIndex() {}
411b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
42c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom    public TrustedCertificateIndex(Set<TrustAnchor> anchors) {
43c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom        index(anchors);
44a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom    }
4506fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom
46c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom    private void index(Set<TrustAnchor> anchors) {
47c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom        for (TrustAnchor anchor : anchors) {
48c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom            index(anchor);
491b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom        }
501b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom    }
511b3c5388d0fffde4392007eb1b0be011a5dfae82Brian Carlstrom
52c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom    public TrustAnchor index(X509Certificate cert) {
53c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom        TrustAnchor anchor = new TrustAnchor(cert, null);
54c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom        index(anchor);
55c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom        return anchor;
5690ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom    }
5790ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom
5890ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom    public void index(TrustAnchor anchor) {
5990ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom        X500Principal subject;
6090ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom        X509Certificate cert = anchor.getTrustedCert();
6190ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom        if (cert != null) {
6290ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom            subject = cert.getSubjectX500Principal();
6390ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom        } else {
6490ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom            subject = anchor.getCA();
6590ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom        }
6606fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom
6790ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom        synchronized (subjectToTrustAnchors) {
68a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom            List<TrustAnchor> anchors = subjectToTrustAnchors.get(subject);
69a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom            if (anchors == null) {
7021f21e9f56ab8c9abfd8728473533fcaafafeac0Jesse Wilson                anchors = new ArrayList<TrustAnchor>(1);
71a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom                subjectToTrustAnchors.put(subject, anchors);
7206fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom            }
73a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom            anchors.add(anchor);
7406fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom        }
7506fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom    }
7606fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom
777a61ad51ba5f5a0b439b2f3eacb1e0f99f909606Selim Gurun    public void reset() {
787a61ad51ba5f5a0b439b2f3eacb1e0f99f909606Selim Gurun        synchronized (subjectToTrustAnchors) {
797a61ad51ba5f5a0b439b2f3eacb1e0f99f909606Selim Gurun            subjectToTrustAnchors.clear();
807a61ad51ba5f5a0b439b2f3eacb1e0f99f909606Selim Gurun        }
817a61ad51ba5f5a0b439b2f3eacb1e0f99f909606Selim Gurun    }
827a61ad51ba5f5a0b439b2f3eacb1e0f99f909606Selim Gurun
837a61ad51ba5f5a0b439b2f3eacb1e0f99f909606Selim Gurun    public void reset(Set<TrustAnchor> anchors) {
847a61ad51ba5f5a0b439b2f3eacb1e0f99f909606Selim Gurun        synchronized (subjectToTrustAnchors) {
857a61ad51ba5f5a0b439b2f3eacb1e0f99f909606Selim Gurun            reset();
867a61ad51ba5f5a0b439b2f3eacb1e0f99f909606Selim Gurun            index(anchors);
877a61ad51ba5f5a0b439b2f3eacb1e0f99f909606Selim Gurun        }
887a61ad51ba5f5a0b439b2f3eacb1e0f99f909606Selim Gurun    }
897a61ad51ba5f5a0b439b2f3eacb1e0f99f909606Selim Gurun
90c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom    public TrustAnchor findByIssuerAndSignature(X509Certificate cert) {
9106fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom        X500Principal issuer = cert.getIssuerX500Principal();
9290ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom        synchronized (subjectToTrustAnchors) {
9390ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom            List<TrustAnchor> anchors = subjectToTrustAnchors.get(issuer);
9490ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom            if (anchors == null) {
9590ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom                return null;
9690ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom            }
9790ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom
9890ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom            for (TrustAnchor anchor : anchors) {
9990ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom                PublicKey publicKey;
10090ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom                try {
10190ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom                    X509Certificate caCert = anchor.getTrustedCert();
10290ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom                    if (caCert != null) {
10390ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom                        publicKey = caCert.getPublicKey();
10490ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom                    } else {
10590ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom                        publicKey = anchor.getCAPublicKey();
10690ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom                    }
10790ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom                    cert.verify(publicKey);
10890ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom                    return anchor;
109c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom                } catch (Exception ignored) {
110a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom                }
11106fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom            }
11206fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom        }
11306fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom        return null;
11406fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom    }
11506fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom
116c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom    public TrustAnchor findBySubjectAndPublicKey(X509Certificate cert) {
117a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom        X500Principal subject = cert.getSubjectX500Principal();
11890ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom        synchronized (subjectToTrustAnchors) {
11990ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom            List<TrustAnchor> anchors = subjectToTrustAnchors.get(subject);
12090ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom            if (anchors == null) {
121c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom                return null;
12290ff8e2c017c4332686ff79ea9968a009a703b7eBrian Carlstrom            }
123c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom            return findBySubjectAndPublicKey(cert, anchors);
12406fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom        }
125a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom    }
126a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom
127c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom    private static TrustAnchor findBySubjectAndPublicKey(X509Certificate cert,
128c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom                                                         Collection<TrustAnchor> anchors) {
129a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom        PublicKey certPublicKey = cert.getPublicKey();
130a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom        for (TrustAnchor anchor : anchors) {
131a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom            PublicKey caPublicKey;
132a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom            try {
133a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom                X509Certificate caCert = anchor.getTrustedCert();
134a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom                if (caCert != null) {
135a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom                    caPublicKey = caCert.getPublicKey();
136a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom                } else {
137a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom                    caPublicKey = anchor.getCAPublicKey();
138a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom                }
139a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom                if (caPublicKey.equals(certPublicKey)) {
140c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom                    return anchor;
141a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom                }
142a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom            } catch (Exception e) {
143a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom                // can happen with unsupported public key types
144a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom            }
145a5c608e59f9d574ea4bc65e9dff44aae2f34fd26Brian Carlstrom        }
146c77290eaef032e5e8952d65e0456b091b6b50804Brian Carlstrom        return null;
14706fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom    }
14806fb2e026572e4f67ac80c927d30e9be787bbe6eBrian Carlstrom}
149