1/*
2 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.security.provider.certpath.ssl;
27
28import java.io.IOException;
29import java.net.URI;
30import java.util.ArrayList;
31import java.util.Arrays;
32import java.util.Collection;
33import java.util.Collections;
34import java.util.List;
35import java.security.GeneralSecurityException;
36import java.security.InvalidAlgorithmParameterException;
37import java.security.Provider;
38import java.security.cert.CertificateException;
39import java.security.cert.CertSelector;
40import java.security.cert.CertStore;
41import java.security.cert.CertStoreException;
42import java.security.cert.CertStoreParameters;
43import java.security.cert.CertStoreSpi;
44import java.security.cert.CRLSelector;
45import java.security.cert.X509Certificate;
46import java.security.cert.X509CRL;
47import java.net.Socket;
48import java.net.URLConnection;
49import javax.net.ssl.HostnameVerifier;
50import javax.net.ssl.HttpsURLConnection;
51import javax.net.ssl.SSLContext;
52import javax.net.ssl.SSLSession;
53import javax.net.ssl.SSLEngine;
54import javax.net.ssl.SSLSocketFactory;
55import javax.net.ssl.TrustManager;
56import javax.net.ssl.X509ExtendedTrustManager;
57
58/**
59 * A CertStore that retrieves an SSL server's certificate chain.
60 */
61public final class SSLServerCertStore extends CertStoreSpi {
62
63    private final URI uri;
64    private static final GetChainTrustManager trustManager;
65    private static final SSLSocketFactory socketFactory;
66    private static final HostnameVerifier hostnameVerifier;
67
68    static {
69        trustManager = new GetChainTrustManager();
70        hostnameVerifier = new HostnameVerifier() {
71            public boolean verify(String hostname, SSLSession session) {
72                return true;
73            }
74        };
75
76        SSLSocketFactory tempFactory;
77        try {
78            SSLContext context = SSLContext.getInstance("SSL");
79            context.init(null, new TrustManager[] { trustManager }, null);
80            tempFactory = context.getSocketFactory();
81        } catch (GeneralSecurityException gse) {
82            tempFactory = null;
83        }
84
85        socketFactory = tempFactory;
86    }
87
88    SSLServerCertStore(URI uri) throws InvalidAlgorithmParameterException {
89        super(null);
90        this.uri = uri;
91    }
92
93    public Collection<X509Certificate> engineGetCertificates
94            (CertSelector selector) throws CertStoreException {
95
96        try {
97            URLConnection urlConn = uri.toURL().openConnection();
98            if (urlConn instanceof HttpsURLConnection) {
99                if (socketFactory == null) {
100                    throw new CertStoreException(
101                        "No initialized SSLSocketFactory");
102                }
103
104                HttpsURLConnection https = (HttpsURLConnection)urlConn;
105                https.setSSLSocketFactory(socketFactory);
106                https.setHostnameVerifier(hostnameVerifier);
107                synchronized (trustManager) {
108                    try {
109                        https.connect();
110                        return getMatchingCerts(
111                            trustManager.serverChain, selector);
112                    } catch (IOException ioe) {
113                        // If the server certificate has already been
114                        // retrieved, don't mind the connection state.
115                        if (trustManager.exchangedServerCerts) {
116                            return getMatchingCerts(
117                                trustManager.serverChain, selector);
118                        }
119
120                        // otherwise, rethrow the exception
121                        throw ioe;
122                    } finally {
123                        trustManager.cleanup();
124                    }
125                }
126            }
127        } catch (IOException ioe) {
128            throw new CertStoreException(ioe);
129        }
130
131        return Collections.<X509Certificate>emptySet();
132    }
133
134    private static List<X509Certificate> getMatchingCerts
135        (List<X509Certificate> certs, CertSelector selector)
136    {
137        // if selector not specified, all certs match
138        if (selector == null) {
139            return certs;
140        }
141        List<X509Certificate> matchedCerts = new ArrayList<>(certs.size());
142        for (X509Certificate cert : certs) {
143            if (selector.match(cert)) {
144                matchedCerts.add(cert);
145            }
146        }
147        return matchedCerts;
148    }
149
150    public Collection<X509CRL> engineGetCRLs(CRLSelector selector)
151        throws CertStoreException
152    {
153        throw new UnsupportedOperationException();
154    }
155
156    public static CertStore getInstance(URI uri)
157        throws InvalidAlgorithmParameterException
158    {
159        return new CS(new SSLServerCertStore(uri), null, "SSLServer", null);
160    }
161
162    /*
163     * An X509ExtendedTrustManager that ignores the server certificate
164     * validation.
165     */
166    private static class GetChainTrustManager
167            extends X509ExtendedTrustManager {
168
169        private List<X509Certificate> serverChain =
170                        Collections.<X509Certificate>emptyList();
171        private boolean exchangedServerCerts = false;
172
173        @Override
174        public X509Certificate[] getAcceptedIssuers() {
175            return new X509Certificate[0];
176        }
177
178        @Override
179        public void checkClientTrusted(X509Certificate[] chain,
180                String authType) throws CertificateException {
181
182            throw new UnsupportedOperationException();
183        }
184
185        @Override
186        public void checkClientTrusted(X509Certificate[] chain, String authType,
187                Socket socket) throws CertificateException {
188
189            throw new UnsupportedOperationException();
190        }
191
192        @Override
193        public void checkClientTrusted(X509Certificate[] chain, String authType,
194                SSLEngine engine) throws CertificateException {
195
196            throw new UnsupportedOperationException();
197        }
198
199        @Override
200        public void checkServerTrusted(X509Certificate[] chain,
201                String authType) throws CertificateException {
202
203            exchangedServerCerts = true;
204            this.serverChain = (chain == null)
205                           ? Collections.<X509Certificate>emptyList()
206                           : Arrays.<X509Certificate>asList(chain);
207
208        }
209
210        @Override
211        public void checkServerTrusted(X509Certificate[] chain, String authType,
212                Socket socket) throws CertificateException {
213
214            checkServerTrusted(chain, authType);
215        }
216
217        @Override
218        public void checkServerTrusted(X509Certificate[] chain, String authType,
219                SSLEngine engine) throws CertificateException {
220
221            checkServerTrusted(chain, authType);
222        }
223
224        void cleanup() {
225            exchangedServerCerts = false;
226            serverChain = Collections.<X509Certificate>emptyList();
227        }
228    }
229
230    /**
231     * This class allows the SSLServerCertStore to be accessed as a CertStore.
232     */
233    private static class CS extends CertStore {
234        protected CS(CertStoreSpi spi, Provider p, String type,
235                     CertStoreParameters params)
236        {
237            super(spi, p, type, params);
238        }
239    }
240}
241