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