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