• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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.io.IOException;
20 import java.io.InputStream;
21 import java.io.PushbackInputStream;
22 import java.security.cert.CRL;
23 import java.security.cert.CRLException;
24 import java.security.cert.CertPath;
25 import java.security.cert.Certificate;
26 import java.security.cert.CertificateException;
27 import java.security.cert.CertificateFactorySpi;
28 import java.security.cert.X509Certificate;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.Iterator;
34 import java.util.List;
35 
36 /**
37  * An implementation of {@link java.security.cert.CertificateFactory} based on BoringSSL.
38  *
39  * @hide
40  */
41 @Internal
42 public class OpenSSLX509CertificateFactory extends CertificateFactorySpi {
43     private static final byte[] PKCS7_MARKER = new byte[] {
44             '-', '-', '-', '-', '-', 'B', 'E', 'G', 'I', 'N', ' ', 'P', 'K', 'C', 'S', '7'
45     };
46 
47     private static final int PUSHBACK_SIZE = 64;
48 
49     static class ParsingException extends Exception {
50         private static final long serialVersionUID = 8390802697728301325L;
51 
ParsingException(String message)52         ParsingException(String message) {
53             super(message);
54         }
55 
ParsingException(Exception cause)56         ParsingException(Exception cause) {
57             super(cause);
58         }
59 
ParsingException(String message, Exception cause)60         ParsingException(String message, Exception cause) {
61             super(message, cause);
62         }
63     }
64 
65     /**
66      * The code for X509 Certificates and CRL is pretty much the same. We use
67      * this abstract class to share the code between them. This makes it ugly,
68      * but it's already written in this language anyway.
69      */
70     private static abstract class Parser<T> {
generateItem(InputStream inStream)71         T generateItem(InputStream inStream) throws ParsingException {
72             if (inStream == null) {
73                 throw new ParsingException("inStream == null");
74             }
75 
76             final boolean markable = inStream.markSupported();
77             if (markable) {
78                 inStream.mark(PKCS7_MARKER.length);
79             }
80 
81             final PushbackInputStream pbis = new PushbackInputStream(inStream, PUSHBACK_SIZE);
82             try {
83                 final byte[] buffer = new byte[PKCS7_MARKER.length];
84 
85                 final int len = pbis.read(buffer);
86                 if (len < 0) {
87                     /* No need to reset here. The stream was empty or EOF. */
88                     throw new ParsingException("inStream is empty");
89                 }
90                 pbis.unread(buffer, 0, len);
91 
92                 if (buffer[0] == '-') {
93                     if (len == PKCS7_MARKER.length && Arrays.equals(PKCS7_MARKER, buffer)) {
94                         List<? extends T> items = fromPkcs7PemInputStream(pbis);
95                         if (items.size() == 0) {
96                             return null;
97                         }
98                         items.get(0);
99                     } else {
100                         return fromX509PemInputStream(pbis);
101                     }
102                 }
103 
104                 /* PKCS#7 bags have a byte 0x06 at position 4 in the stream. */
105                 if (buffer[4] == 0x06) {
106                     List<? extends T> certs = fromPkcs7DerInputStream(pbis);
107                     if (certs.size() == 0) {
108                         return null;
109                     }
110                     return certs.get(0);
111                 } else {
112                     return fromX509DerInputStream(pbis);
113                 }
114             } catch (Exception e) {
115                 if (markable) {
116                     try {
117                         inStream.reset();
118                     } catch (IOException ignored) {
119                     }
120                 }
121                 throw new ParsingException(e);
122             }
123         }
124 
generateItems(InputStream inStream)125         Collection<? extends T> generateItems(InputStream inStream)
126                 throws ParsingException {
127             if (inStream == null) {
128                 throw new ParsingException("inStream == null");
129             }
130             try {
131                 if (inStream.available() == 0) {
132                     return Collections.emptyList();
133                 }
134             } catch (IOException e) {
135                 throw new ParsingException("Problem reading input stream", e);
136             }
137 
138             final boolean markable = inStream.markSupported();
139             if (markable) {
140                 inStream.mark(PUSHBACK_SIZE);
141             }
142 
143             /* Attempt to see if this is a PKCS#7 bag. */
144             final PushbackInputStream pbis = new PushbackInputStream(inStream, PUSHBACK_SIZE);
145             try {
146                 final byte[] buffer = new byte[PKCS7_MARKER.length];
147 
148                 final int len = pbis.read(buffer);
149                 if (len < 0) {
150                     /* No need to reset here. The stream was empty or EOF. */
151                     throw new ParsingException("inStream is empty");
152                 }
153                 pbis.unread(buffer, 0, len);
154 
155                 if (len == PKCS7_MARKER.length && Arrays.equals(PKCS7_MARKER, buffer)) {
156                     return fromPkcs7PemInputStream(pbis);
157                 }
158 
159                 /* PKCS#7 bags have a byte 0x06 at position 4 in the stream. */
160                 if (buffer[4] == 0x06) {
161                     return fromPkcs7DerInputStream(pbis);
162                 }
163             } catch (Exception e) {
164                 if (markable) {
165                     try {
166                         inStream.reset();
167                     } catch (IOException ignored) {
168                     }
169                 }
170                 throw new ParsingException(e);
171             }
172 
173             /*
174              * It wasn't, so just try to keep grabbing certificates until we
175              * can't anymore.
176              */
177             final List<T> coll = new ArrayList<T>();
178             T c;
179             do {
180                 /*
181                  * If this stream supports marking, try to mark here in case
182                  * there is an error during certificate generation.
183                  */
184                 if (markable) {
185                     inStream.mark(PUSHBACK_SIZE);
186                 }
187 
188                 try {
189                     c = generateItem(pbis);
190                     coll.add(c);
191                 } catch (ParsingException e) {
192                     /*
193                      * If this stream supports marking, attempt to reset it to
194                      * the mark before the failure.
195                      */
196                     if (markable) {
197                         try {
198                             inStream.reset();
199                         } catch (IOException ignored) {
200                         }
201                     }
202 
203                     c = null;
204                 }
205             } while (c != null);
206 
207             return coll;
208         }
209 
fromX509PemInputStream(InputStream pbis)210         protected abstract T fromX509PemInputStream(InputStream pbis) throws ParsingException;
211 
fromX509DerInputStream(InputStream pbis)212         protected abstract T fromX509DerInputStream(InputStream pbis) throws ParsingException;
213 
fromPkcs7PemInputStream(InputStream is)214         protected abstract List<? extends T> fromPkcs7PemInputStream(InputStream is)
215                 throws ParsingException;
216 
fromPkcs7DerInputStream(InputStream is)217         protected abstract List<? extends T> fromPkcs7DerInputStream(InputStream is)
218                 throws ParsingException;
219     }
220 
221     private Parser<OpenSSLX509Certificate> certificateParser =
222             new Parser<OpenSSLX509Certificate>() {
223                 @Override
224                 public OpenSSLX509Certificate fromX509PemInputStream(InputStream is)
225                         throws ParsingException {
226                     return OpenSSLX509Certificate.fromX509PemInputStream(is);
227                 }
228 
229                 @Override
230                 public OpenSSLX509Certificate fromX509DerInputStream(InputStream is)
231                         throws ParsingException {
232                     return OpenSSLX509Certificate.fromX509DerInputStream(is);
233                 }
234 
235                 @Override
236                 public List<? extends OpenSSLX509Certificate>
237                         fromPkcs7PemInputStream(InputStream is) throws ParsingException {
238                     return OpenSSLX509Certificate.fromPkcs7PemInputStream(is);
239                 }
240 
241                 @Override
242                 public List<? extends OpenSSLX509Certificate>
243                         fromPkcs7DerInputStream(InputStream is) throws ParsingException {
244                     return OpenSSLX509Certificate.fromPkcs7DerInputStream(is);
245                 }
246             };
247 
248     private Parser<OpenSSLX509CRL> crlParser =
249             new Parser<OpenSSLX509CRL>() {
250                 @Override
251                 public OpenSSLX509CRL fromX509PemInputStream(InputStream is)
252                         throws ParsingException {
253                     return OpenSSLX509CRL.fromX509PemInputStream(is);
254                 }
255 
256                 @Override
257                 public OpenSSLX509CRL fromX509DerInputStream(InputStream is)
258                         throws ParsingException {
259                     return OpenSSLX509CRL.fromX509DerInputStream(is);
260                 }
261 
262                 @Override
263                 public List<? extends OpenSSLX509CRL> fromPkcs7PemInputStream(InputStream is)
264                         throws ParsingException {
265                     return OpenSSLX509CRL.fromPkcs7PemInputStream(is);
266                 }
267 
268                 @Override
269                 public List<? extends OpenSSLX509CRL> fromPkcs7DerInputStream(InputStream is)
270                         throws ParsingException {
271                     return OpenSSLX509CRL.fromPkcs7DerInputStream(is);
272                 }
273             };
274 
275     @Override
engineGenerateCertificate(InputStream inStream)276     public Certificate engineGenerateCertificate(InputStream inStream) throws CertificateException {
277         try {
278             return certificateParser.generateItem(inStream);
279         } catch (ParsingException e) {
280             throw new CertificateException(e);
281         }
282     }
283 
284     @Override
engineGenerateCertificates( InputStream inStream)285     public Collection<? extends Certificate> engineGenerateCertificates(
286             InputStream inStream) throws CertificateException {
287         try {
288             return certificateParser.generateItems(inStream);
289         } catch (ParsingException e) {
290             throw new CertificateException(e);
291         }
292     }
293 
294     @Override
engineGenerateCRL(InputStream inStream)295     public CRL engineGenerateCRL(InputStream inStream) throws CRLException {
296         try {
297             return crlParser.generateItem(inStream);
298         } catch (ParsingException e) {
299             throw new CRLException(e);
300         }
301     }
302 
303     @Override
engineGenerateCRLs(InputStream inStream)304     public Collection<? extends CRL> engineGenerateCRLs(InputStream inStream) throws CRLException {
305         if (inStream == null) {
306             return Collections.emptyList();
307         }
308 
309         try {
310             return crlParser.generateItems(inStream);
311         } catch (ParsingException e) {
312             throw new CRLException(e);
313         }
314     }
315 
316     @Override
engineGetCertPathEncodings()317     public Iterator<String> engineGetCertPathEncodings() {
318         return OpenSSLX509CertPath.getEncodingsIterator();
319     }
320 
321     @Override
engineGenerateCertPath(InputStream inStream)322     public CertPath engineGenerateCertPath(InputStream inStream) throws CertificateException {
323         return OpenSSLX509CertPath.fromEncoding(inStream);
324     }
325 
326     @Override
engineGenerateCertPath(InputStream inStream, String encoding)327     public CertPath engineGenerateCertPath(InputStream inStream, String encoding)
328             throws CertificateException {
329         return OpenSSLX509CertPath.fromEncoding(inStream, encoding);
330     }
331 
332     @Override
engineGenerateCertPath(List<? extends Certificate> certificates)333     public CertPath engineGenerateCertPath(List<? extends Certificate> certificates)
334             throws CertificateException {
335         final List<X509Certificate> filtered = new ArrayList<X509Certificate>(certificates.size());
336         for (int i = 0; i < certificates.size(); i++) {
337             final Certificate c = certificates.get(i);
338 
339             if (!(c instanceof X509Certificate)) {
340                 throw new CertificateException("Certificate not X.509 type at index " + i);
341             }
342 
343             filtered.add((X509Certificate) c);
344         }
345 
346         return new OpenSSLX509CertPath(filtered);
347     }
348 }
349