001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Alexander Y. Kleymenov
020: * @version $Revision$
021: */package org.apache.harmony.security.provider.cert;
022:
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.security.cert.CRL;
026: import java.security.cert.CRLException;
027: import java.security.cert.CertPath;
028: import java.security.cert.Certificate;
029: import java.security.cert.CertificateException;
030: import java.security.cert.CertificateFactorySpi;
031: import java.security.cert.X509CRL;
032: import java.util.ArrayList;
033: import java.util.Collection;
034: import java.util.Iterator;
035: import java.util.List;
036:
037: import org.apache.harmony.luni.util.Base64;
038: import org.apache.harmony.security.asn1.ASN1Constants;
039: import org.apache.harmony.security.asn1.BerInputStream;
040: import org.apache.harmony.security.internal.nls.Messages;
041: import org.apache.harmony.security.pkcs7.ContentInfo;
042: import org.apache.harmony.security.pkcs7.SignedData;
043: import org.apache.harmony.security.x509.CertificateList;
044:
045: /**
046: * X509 Certificate Factory Service Provider Interface Implementation.
047: * It supports CRLs and Certificates in (PEM) ASN.1 DER encoded form,
048: * and Certification Paths in PkiPath and PKCS7 formats.
049: * For Certificates and CRLs factory maintains the caching
050: * mechanisms allowing to speed up repeated Certificate/CRL
051: * generation.
052: * @see Cache
053: */
054: public class X509CertFactoryImpl extends CertificateFactorySpi {
055:
056: // number of leading/trailing bytes used for cert hash computation
057: private static int CERT_CACHE_SEED_LENGTH = 28;
058: // certificate cache
059: private static Cache CERT_CACHE = new Cache(CERT_CACHE_SEED_LENGTH);
060: // number of leading/trailing bytes used for crl hash computation
061: private static int CRL_CACHE_SEED_LENGTH = 24;
062: // crl cache
063: private static Cache CRL_CACHE = new Cache(CRL_CACHE_SEED_LENGTH);
064:
065: /**
066: * Default constructor.
067: * Creates the instance of Certificate Factory SPI ready for use.
068: */
069: public X509CertFactoryImpl() {
070: }
071:
072: /**
073: * Generates the X.509 certificate from the data in the stream.
074: * The data in the stream can be either in ASN.1 DER encoded X.509
075: * certificate, or PEM (Base64 encoding bounded by
076: * <code>"-----BEGIN CERTIFICATE-----"</code> at the beginning and
077: * <code>"-----END CERTIFICATE-----"</code> at the end) representation
078: * of the former encoded form.
079: *
080: * Before the generation the encoded form is looked up in
081: * the cache. If the cache contains the certificate with requested encoded
082: * form it is returned from it, otherwise it is generated by ASN.1
083: * decoder.
084: *
085: * @see java.security.cert.CertificateFactorySpi#engineGenerateCertificate(InputStream)
086: * method documentation for more info
087: */
088: public Certificate engineGenerateCertificate(InputStream inStream)
089: throws CertificateException {
090: if (inStream == null) {
091: throw new CertificateException(Messages
092: .getString("security.153")); //$NON-NLS-1$
093: }
094: try {
095: if (!inStream.markSupported()) {
096: // create the mark supporting wrapper
097: inStream = new RestoringInputStream(inStream);
098: }
099: // mark is needed to recognize the format of the provided encoding
100: // (ASN.1 or PEM)
101: inStream.mark(1);
102: // check whether the provided certificate is in PEM encoded form
103: if (inStream.read() == '-') {
104: // decode PEM, retrieve CRL
105: return getCertificate(decodePEM(inStream,
106: CERT_BOUND_SUFFIX));
107: } else {
108: inStream.reset();
109: // retrieve CRL
110: return getCertificate(inStream);
111: }
112: } catch (IOException e) {
113: throw new CertificateException(e);
114: }
115: }
116:
117: /**
118: * Generates the collection of the certificates on the base of provided
119: * via input stream encodings.
120: * @see java.security.cert.CertificateFactorySpi#engineGenerateCertificates(InputStream)
121: * method documentation for more info
122: */
123: public Collection<? extends Certificate> engineGenerateCertificates(
124: InputStream inStream) throws CertificateException {
125: if (inStream == null) {
126: throw new CertificateException(Messages
127: .getString("security.153")); //$NON-NLS-1$
128: }
129: ArrayList result = new ArrayList();
130: try {
131: if (!inStream.markSupported()) {
132: // create the mark supporting wrapper
133: inStream = new RestoringInputStream(inStream);
134: }
135: // if it is PEM encoded form this array will contain the encoding
136: // so ((it is PEM) <-> (encoding != null))
137: byte[] encoding = null;
138: // The following by SEQUENCE ASN.1 tag, used for
139: // recognizing the data format
140: // (is it PKCS7 ContentInfo structure, X.509 Certificate, or
141: // unsupported encoding)
142: int second_asn1_tag = -1;
143: inStream.mark(1);
144: int ch;
145: while ((ch = inStream.read()) != -1) {
146: // check if it is PEM encoded form
147: if (ch == '-') { // beginning of PEM encoding ('-' char)
148: // decode PEM chunk and store its content (ASN.1 encoding)
149: encoding = decodePEM(inStream, FREE_BOUND_SUFFIX);
150: } else if (ch == 0x30) { // beginning of ASN.1 sequence (0x30)
151: encoding = null;
152: inStream.reset();
153: // prepare for data format determination
154: inStream.mark(CERT_CACHE_SEED_LENGTH);
155: } else { // unsupported data
156: if (result.size() == 0) {
157: throw new CertificateException(Messages
158: .getString("security.15F")); //$NON-NLS-1$
159: } else {
160: // it can be trailing user data,
161: // so keep it in the stream
162: inStream.reset();
163: return result;
164: }
165: }
166: // Check the data format
167: BerInputStream in = (encoding == null) ? new BerInputStream(
168: inStream)
169: : new BerInputStream(encoding);
170: // read the next ASN.1 tag
171: second_asn1_tag = in.next(); // inStream position changed
172: if (encoding == null) {
173: // keep whole structure in the stream
174: inStream.reset();
175: }
176: // check if it is a TBSCertificate structure
177: if (second_asn1_tag != ASN1Constants.TAG_C_SEQUENCE) {
178: if (result.size() == 0) {
179: // there were not read X.509 Certificates, so
180: // break the cycle and check
181: // whether it is PKCS7 structure
182: break;
183: } else {
184: // it can be trailing user data,
185: // so return what we already read
186: return result;
187: }
188: } else {
189: if (encoding == null) {
190: result.add(getCertificate(inStream));
191: } else {
192: result.add(getCertificate(encoding));
193: }
194: }
195: // mark for the next iteration
196: inStream.mark(1);
197: }
198: if (result.size() != 0) {
199: // some Certificates have been read
200: return result;
201: } else if (ch == -1) {
202: throw new CertificateException(Messages
203: .getString("security.155")); //$NON-NLS-1$
204: }
205: // else: check if it is PKCS7
206: if (second_asn1_tag == ASN1Constants.TAG_OID) {
207: // it is PKCS7 ContentInfo structure, so decode it
208: ContentInfo info = (ContentInfo) ((encoding != null) ? ContentInfo.ASN1
209: .decode(encoding)
210: : ContentInfo.ASN1.decode(inStream));
211: // retrieve SignedData
212: SignedData data = info.getSignedData();
213: if (data == null) {
214: throw new CertificateException(Messages
215: .getString("security.154")); //$NON-NLS-1$
216: }
217: List certs = data.getCertificates();
218: if (certs != null) {
219: for (int i = 0; i < certs.size(); i++) {
220: result
221: .add(new X509CertImpl(
222: (org.apache.harmony.security.x509.Certificate) certs
223: .get(i)));
224: }
225: }
226: return result;
227: }
228: // else: Unknown data format
229: throw new CertificateException(Messages
230: .getString("security.15F")); //$NON-NLS-1$
231: } catch (IOException e) {
232: throw new CertificateException(e);
233: }
234: }
235:
236: /**
237: * @see java.security.cert.CertificateFactorySpi#engineGenerateCRL(InputStream)
238: * method documentation for more info
239: */
240: public CRL engineGenerateCRL(InputStream inStream)
241: throws CRLException {
242: if (inStream == null) {
243: throw new CRLException(Messages.getString("security.153")); //$NON-NLS-1$
244: }
245: try {
246: if (!inStream.markSupported()) {
247: // Create the mark supporting wrapper
248: // Mark is needed to recognize the format
249: // of provided encoding form (ASN.1 or PEM)
250: inStream = new RestoringInputStream(inStream);
251: }
252: inStream.mark(1);
253: // check whether the provided crl is in PEM encoded form
254: if (inStream.read() == '-') {
255: // decode PEM, retrieve CRL
256: return getCRL(decodePEM(inStream, FREE_BOUND_SUFFIX));
257: } else {
258: inStream.reset();
259: // retrieve CRL
260: return getCRL(inStream);
261: }
262: } catch (IOException e) {
263: throw new CRLException(e);
264: }
265: }
266:
267: /**
268: * @see java.security.cert.CertificateFactorySpi#engineGenerateCRLs(InputStream)
269: * method documentation for more info
270: */
271: public Collection<? extends CRL> engineGenerateCRLs(
272: InputStream inStream) throws CRLException {
273: if (inStream == null) {
274: throw new CRLException(Messages.getString("security.153")); //$NON-NLS-1$
275: }
276: ArrayList result = new ArrayList();
277: try {
278: if (!inStream.markSupported()) {
279: inStream = new RestoringInputStream(inStream);
280: }
281: // if it is PEM encoded form this array will contain the encoding
282: // so ((it is PEM) <-> (encoding != null))
283: byte[] encoding = null;
284: // The following by SEQUENCE ASN.1 tag, used for
285: // recognizing the data format
286: // (is it PKCS7 ContentInfo structure, X.509 CRL, or
287: // unsupported encoding)
288: int second_asn1_tag = -1;
289: inStream.mark(1);
290: int ch;
291: while ((ch = inStream.read()) != -1) {
292: // check if it is PEM encoded form
293: if (ch == '-') { // beginning of PEM encoding ('-' char)
294: // decode PEM chunk and store its content (ASN.1 encoding)
295: encoding = decodePEM(inStream, FREE_BOUND_SUFFIX);
296: } else if (ch == 0x30) { // beginning of ASN.1 sequence (0x30)
297: encoding = null;
298: inStream.reset();
299: // prepare for data format determination
300: inStream.mark(CRL_CACHE_SEED_LENGTH);
301: } else { // unsupported data
302: if (result.size() == 0) {
303: throw new CRLException(Messages
304: .getString("security.15F")); //$NON-NLS-1$
305: } else {
306: // it can be trailing user data,
307: // so keep it in the stream
308: inStream.reset();
309: return result;
310: }
311: }
312: // Check the data format
313: BerInputStream in = (encoding == null) ? new BerInputStream(
314: inStream)
315: : new BerInputStream(encoding);
316: // read the next ASN.1 tag
317: second_asn1_tag = in.next();
318: if (encoding == null) {
319: // keep whole structure in the stream
320: inStream.reset();
321: }
322: // check if it is a TBSCertList structure
323: if (second_asn1_tag != ASN1Constants.TAG_C_SEQUENCE) {
324: if (result.size() == 0) {
325: // there were not read X.509 CRLs, so
326: // break the cycle and check
327: // whether it is PKCS7 structure
328: break;
329: } else {
330: // it can be trailing user data,
331: // so return what we already read
332: return result;
333: }
334: } else {
335: if (encoding == null) {
336: result.add(getCRL(inStream));
337: } else {
338: result.add(getCRL(encoding));
339: }
340: }
341: inStream.mark(1);
342: }
343: if (result.size() != 0) {
344: // the stream was read out
345: return result;
346: } else if (ch == -1) {
347: throw new CRLException(Messages
348: .getString("security.155")); //$NON-NLS-1$
349: }
350: // else: check if it is PKCS7
351: if (second_asn1_tag == ASN1Constants.TAG_OID) {
352: // it is PKCS7 ContentInfo structure, so decode it
353: ContentInfo info = (ContentInfo) ((encoding != null) ? ContentInfo.ASN1
354: .decode(encoding)
355: : ContentInfo.ASN1.decode(inStream));
356: // retrieve SignedData
357: SignedData data = info.getSignedData();
358: if (data == null) {
359: throw new CRLException(Messages
360: .getString("security.154")); //$NON-NLS-1$
361: }
362: List crls = data.getCRLs();
363: if (crls != null) {
364: for (int i = 0; i < crls.size(); i++) {
365: result.add(new X509CRLImpl(
366: (CertificateList) crls.get(i)));
367: }
368: }
369: return result;
370: }
371: // else: Unknown data format
372: throw new CRLException(Messages.getString("security.15F")); //$NON-NLS-1$
373: } catch (IOException e) {
374: throw new CRLException(e);
375: }
376: }
377:
378: /**
379: * @see java.security.cert.CertificateFactorySpi#engineGenerateCertPath(InputStream)
380: * method documentation for more info
381: */
382: public CertPath engineGenerateCertPath(InputStream inStream)
383: throws CertificateException {
384: if (inStream == null) {
385: throw new CertificateException(Messages
386: .getString("security.153")); //$NON-NLS-1$
387: }
388: return engineGenerateCertPath(inStream, "PkiPath"); //$NON-NLS-1$
389: }
390:
391: /**
392: * @see java.security.cert.CertificateFactorySpi#engineGenerateCertPath(InputStream,String)
393: * method documentation for more info
394: */
395: public CertPath engineGenerateCertPath(InputStream inStream,
396: String encoding) throws CertificateException {
397: if (inStream == null) {
398: throw new CertificateException(Messages
399: .getString("security.153")); //$NON-NLS-1$
400: }
401: if (!inStream.markSupported()) {
402: inStream = new RestoringInputStream(inStream);
403: }
404: try {
405: inStream.mark(1);
406: int ch;
407:
408: // check if it is PEM encoded form
409: if ((ch = inStream.read()) == '-') {
410: // decode PEM chunk into ASN.1 form and decode CertPath object
411: return X509CertPathImpl.getInstance(decodePEM(inStream,
412: FREE_BOUND_SUFFIX), encoding);
413: } else if (ch == 0x30) { // ASN.1 Sequence
414: inStream.reset();
415: // decode ASN.1 form
416: return X509CertPathImpl.getInstance(inStream, encoding);
417: } else {
418: throw new CertificateException(Messages
419: .getString("security.15F")); //$NON-NLS-1$
420: }
421: } catch (IOException e) {
422: throw new CertificateException(e);
423: }
424: }
425:
426: /**
427: * @see java.security.cert.CertificateFactorySpi#engineGenerateCertPath(List)
428: * method documentation for more info
429: */
430: public CertPath engineGenerateCertPath(List certificates)
431: throws CertificateException {
432: return new X509CertPathImpl(certificates);
433: }
434:
435: /**
436: * @see java.security.cert.CertificateFactorySpi#engineGetCertPathEncodings()
437: * method documentation for more info
438: */
439: public Iterator<String> engineGetCertPathEncodings() {
440: return X509CertPathImpl.encodings.iterator();
441: }
442:
443: // ---------------------------------------------------------------------
444: // ------------------------ Staff methods ------------------------------
445: // ---------------------------------------------------------------------
446:
447: private static byte[] pemBegin = "-----BEGIN".getBytes(); //$NON-NLS-1$
448: private static byte[] pemClose = "-----END".getBytes(); //$NON-NLS-1$
449: /**
450: * Code describing free format for PEM boundary suffix:
451: * "^-----BEGIN.*\n" at the beginning, and<br>
452: * "\n-----END.*(EOF|\n)$" at the end.
453: */
454: private static byte[] FREE_BOUND_SUFFIX = null;
455: /**
456: * Code describing PEM boundary suffix for X.509 certificate:
457: * "^-----BEGIN CERTIFICATE-----\n" at the beginning, and<br>
458: * "\n-----END CERTIFICATE-----" at the end.
459: */
460: private static byte[] CERT_BOUND_SUFFIX = " CERTIFICATE-----".getBytes(); //$NON-NLS-1$
461:
462: /**
463: * Method retrieves the PEM encoded data from the stream
464: * and returns its decoded representation.
465: * Method checks correctness of PEM boundaries. It supposes that
466: * the first '-' of the opening boundary has already been read from
467: * the stream. So first of all it checks that the leading bytes
468: * are equal to "-----BEGIN" boundary prefix. Than if boundary_suffix
469: * is not null, it checks that next bytes equal to boundary_suffix
470: * + new line char[s] ([CR]LF).
471: * If boundary_suffix parameter is null, method supposes free suffix
472: * format and skips any bytes until the new line.<br>
473: * After the opening boundary has been read and checked, the method
474: * read Base64 encoded data until closing PEM boundary is not reached.<br>
475: * Than it checks closing boundary - it should start with new line +
476: * "-----END" + boundary_suffix. If boundary_suffix is null,
477: * any characters are skipped until the new line.<br>
478: * After this any trailing new line characters are skipped from the stream,
479: * Base64 encoding is decoded and returned.
480: * @param inStream the stream containing the PEM encoding.
481: * @param boundary_suffix the suffix of expected PEM multipart
482: * boundary delimiter.<br>
483: * If it is null, that any character sequences are accepted.
484: * @throws IOException If PEM boundary delimiter does not comply
485: * with expected or some I/O or decoding problems occur.
486: */
487: private byte[] decodePEM(InputStream inStream,
488: byte[] boundary_suffix) throws IOException {
489: int ch; // the char to be read
490: // check and skip opening boundary delimiter
491: // (first '-' is supposed as already read)
492: for (int i = 1; i < pemBegin.length; i++) {
493: if (pemBegin[i] != (ch = inStream.read())) {
494: throw new IOException(
495: "Incorrect PEM encoding: '-----BEGIN"
496: + ((boundary_suffix == null) ? ""
497: : new String(boundary_suffix))
498: + "' is expected as opening delimiter boundary.");
499: }
500: }
501: if (boundary_suffix == null) {
502: // read (skip) the trailing characters of
503: // the beginning PEM boundary delimiter
504: while ((ch = inStream.read()) != '\n') {
505: if (ch == -1) {
506: throw new IOException(Messages
507: .getString("security.156")); //$NON-NLS-1$
508: }
509: }
510: } else {
511: for (int i = 0; i < boundary_suffix.length; i++) {
512: if (boundary_suffix[i] != inStream.read()) {
513: throw new IOException(Messages.getString(
514: "security.15B", //$NON-NLS-1$
515: new String(boundary_suffix))); //$NON-NLS-1$
516: }
517: }
518: // read new line characters
519: if ((ch = inStream.read()) == '\r') {
520: // CR has been read, now read LF character
521: ch = inStream.read();
522: }
523: if (ch != '\n') {
524: throw new IOException(Messages
525: .getString("security.15B2")); //$NON-NLS-1$
526: }
527: }
528: int size = 1024; // the size of the buffer containing Base64 data
529: byte[] buff = new byte[size];
530: int index = 0;
531: // read bytes while ending boundary delimiter is not reached
532: while ((ch = inStream.read()) != '-') {
533: if (ch == -1) {
534: throw new IOException(Messages
535: .getString("security.157")); //$NON-NLS-1$
536: }
537: buff[index++] = (byte) ch;
538: if (index == size) {
539: // enlarge the buffer
540: byte[] newbuff = new byte[size + 1024];
541: System.arraycopy(buff, 0, newbuff, 0, size);
542: buff = newbuff;
543: size += 1024;
544: }
545: }
546: if (buff[index - 1] != '\n') {
547: throw new IOException(Messages.getString("security.158")); //$NON-NLS-1$
548: }
549: // check and skip closing boundary delimiter prefix
550: // (first '-' was read)
551: for (int i = 1; i < pemClose.length; i++) {
552: if (pemClose[i] != inStream.read()) {
553: throw new IOException(Messages.getString(
554: "security.15B1", //$NON-NLS-1$
555: ((boundary_suffix == null) ? "" : new String(
556: boundary_suffix)))); //$NON-NLS-1$
557: }
558: }
559: if (boundary_suffix == null) {
560: // read (skip) the trailing characters of
561: // the closing PEM boundary delimiter
562: while (((ch = inStream.read()) != -1) && (ch != '\n')
563: && (ch != '\r')) {
564: }
565: } else {
566: for (int i = 0; i < boundary_suffix.length; i++) {
567: if (boundary_suffix[i] != inStream.read()) {
568: throw new IOException(Messages.getString(
569: "security.15B1", //$NON-NLS-1$
570: new String(boundary_suffix))); //$NON-NLS-1$
571: }
572: }
573: }
574: // skip trailing line breaks
575: inStream.mark(1);
576: while (((ch = inStream.read()) != -1)
577: && (ch == '\n' || ch == '\r')) {
578: inStream.mark(1);
579: }
580: inStream.reset();
581: buff = Base64.decode(buff, index);
582: if (buff == null) {
583: throw new IOException(Messages.getString("security.159")); //$NON-NLS-1$
584: }
585: return buff;
586: };
587:
588: /**
589: * Reads the data of specified length from source
590: * and returns it as an array.
591: * @return the byte array contained read data or
592: * null if the stream contains not enough data
593: * @throws IOException if some I/O error has been occurred.
594: */
595: private static byte[] readBytes(InputStream source, int length)
596: throws IOException {
597: byte[] result = new byte[length];
598: for (int i = 0; i < length; i++) {
599: int bytik = source.read();
600: if (bytik == -1) {
601: return null;
602: }
603: result[i] = (byte) bytik;
604: }
605: return result;
606: }
607:
608: /**
609: * Returns the Certificate object corresponding to the provided encoding.
610: * Resulting object is retrieved from the cache
611: * if it contains such correspondence
612: * and is constructed on the base of encoding
613: * and stored in the cache otherwise.
614: * @throws IOException if some decoding errors occur
615: * (in the case of cache miss).
616: */
617: private static Certificate getCertificate(byte[] encoding)
618: throws CertificateException, IOException {
619: if (encoding.length < CERT_CACHE_SEED_LENGTH) {
620: throw new CertificateException(Messages
621: .getString("security.152")); //$NON-NLS-1$
622: }
623: synchronized (CERT_CACHE) {
624: long hash = CERT_CACHE.getHash(encoding);
625: if (CERT_CACHE.contains(hash)) {
626: Certificate res = (Certificate) CERT_CACHE.get(hash,
627: encoding);
628: if (res != null) {
629: return res;
630: }
631: }
632: Certificate res = new X509CertImpl(encoding);
633: CERT_CACHE.put(hash, encoding, res);
634: return res;
635: }
636: }
637:
638: /**
639: * Returns the Certificate object corresponding to the encoding provided
640: * by the stream.
641: * Resulting object is retrieved from the cache
642: * if it contains such correspondence
643: * and is constructed on the base of encoding
644: * and stored in the cache otherwise.
645: * @throws IOException if some decoding errors occur
646: * (in the case of cache miss).
647: */
648: private static Certificate getCertificate(InputStream inStream)
649: throws CertificateException, IOException {
650: synchronized (CERT_CACHE) {
651: inStream.mark(CERT_CACHE_SEED_LENGTH);
652: // read the prefix of the encoding
653: byte[] buff = readBytes(inStream, CERT_CACHE_SEED_LENGTH);
654: inStream.reset();
655: if (buff == null) {
656: throw new CertificateException(Messages
657: .getString("security.152")); //$NON-NLS-1$
658: }
659: long hash = CERT_CACHE.getHash(buff);
660: if (CERT_CACHE.contains(hash)) {
661: byte[] encoding = new byte[BerInputStream
662: .getLength(buff)];
663: if (encoding.length < CERT_CACHE_SEED_LENGTH) {
664: throw new CertificateException(Messages
665: .getString("security.15B3")); //$NON-NLS-1$
666: }
667: inStream.read(encoding);
668: Certificate res = (Certificate) CERT_CACHE.get(hash,
669: encoding);
670: if (res != null) {
671: return res;
672: }
673: res = new X509CertImpl(encoding);
674: CERT_CACHE.put(hash, encoding, res);
675: return res;
676: } else {
677: inStream.reset();
678: Certificate res = new X509CertImpl(inStream);
679: CERT_CACHE.put(hash, res.getEncoded(), res);
680: return res;
681: }
682: }
683: }
684:
685: /**
686: * Returns the CRL object corresponding to the provided encoding.
687: * Resulting object is retrieved from the cache
688: * if it contains such correspondence
689: * and is constructed on the base of encoding
690: * and stored in the cache otherwise.
691: * @throws IOException if some decoding errors occur
692: * (in the case of cache miss).
693: */
694: private static CRL getCRL(byte[] encoding) throws CRLException,
695: IOException {
696: if (encoding.length < CRL_CACHE_SEED_LENGTH) {
697: throw new CRLException(Messages.getString("security.152")); //$NON-NLS-1$
698: }
699: synchronized (CRL_CACHE) {
700: long hash = CRL_CACHE.getHash(encoding);
701: if (CRL_CACHE.contains(hash)) {
702: X509CRL res = (X509CRL) CRL_CACHE.get(hash, encoding);
703: if (res != null) {
704: return res;
705: }
706: }
707: X509CRL res = new X509CRLImpl(encoding);
708: CRL_CACHE.put(hash, encoding, res);
709: return res;
710: }
711: }
712:
713: /**
714: * Returns the CRL object corresponding to the encoding provided
715: * by the stream.
716: * Resulting object is retrieved from the cache
717: * if it contains such correspondence
718: * and is constructed on the base of encoding
719: * and stored in the cache otherwise.
720: * @throws IOException if some decoding errors occur
721: * (in the case of cache miss).
722: */
723: private static CRL getCRL(InputStream inStream)
724: throws CRLException, IOException {
725: synchronized (CRL_CACHE) {
726: inStream.mark(CRL_CACHE_SEED_LENGTH);
727: byte[] buff = readBytes(inStream, CRL_CACHE_SEED_LENGTH);
728: // read the prefix of the encoding
729: inStream.reset();
730: if (buff == null) {
731: throw new CRLException(Messages
732: .getString("security.152")); //$NON-NLS-1$
733: }
734: long hash = CRL_CACHE.getHash(buff);
735: if (CRL_CACHE.contains(hash)) {
736: byte[] encoding = new byte[BerInputStream
737: .getLength(buff)];
738: if (encoding.length < CRL_CACHE_SEED_LENGTH) {
739: throw new CRLException(Messages
740: .getString("security.15B4")); //$NON-NLS-1$
741: }
742: inStream.read(encoding);
743: CRL res = (CRL) CRL_CACHE.get(hash, encoding);
744: if (res != null) {
745: return res;
746: }
747: res = new X509CRLImpl(encoding);
748: CRL_CACHE.put(hash, encoding, res);
749: return res;
750: } else {
751: X509CRL res = new X509CRLImpl(inStream);
752: CRL_CACHE.put(hash, res.getEncoded(), res);
753: return res;
754: }
755: }
756: }
757:
758: /*
759: * This class extends any existing input stream with
760: * mark functionality. It acts as a wrapper over the
761: * stream and supports reset to the
762: * marked state with readlimit no more than BUFF_SIZE.
763: */
764: private static class RestoringInputStream extends InputStream {
765:
766: // wrapped input stream
767: private final InputStream inStream;
768: // specifies how much of the read data is buffered
769: // after the mark has been set up
770: private static final int BUFF_SIZE = 32;
771: // buffer to keep the bytes read after the mark has been set up
772: private final int[] buff = new int[BUFF_SIZE * 2];
773: // position of the next byte to read,
774: // the value of -1 indicates that the buffer is not used
775: // (mark was not set up or was invalidated, or reset to the marked
776: // position has been done and all the buffered data was read out)
777: private int pos = -1;
778: // position of the last buffered byte
779: private int bar = 0;
780: // position in the buffer where the mark becomes invalidated
781: private int end = 0;
782:
783: /**
784: * Creates the mark supporting wrapper over the stream.
785: */
786: public RestoringInputStream(InputStream inStream) {
787: this .inStream = inStream;
788: }
789:
790: /**
791: * @see java.io.InputStream#available()
792: * method documentation for more info
793: */
794: public int available() throws IOException {
795: return (bar - pos) + inStream.available();
796: }
797:
798: /**
799: * @see java.io.InputStream#close()
800: * method documentation for more info
801: */
802: public void close() throws IOException {
803: inStream.close();
804: }
805:
806: /**
807: * @see java.io.InputStream#mark(int readlimit)
808: * method documentation for more info
809: */
810: public void mark(int readlimit) {
811: if (pos < 0) {
812: pos = 0;
813: bar = 0;
814: end = BUFF_SIZE - 1;
815: } else {
816: end = (pos + BUFF_SIZE - 1) % BUFF_SIZE;
817: }
818: }
819:
820: /**
821: * @see java.io.InputStream#markSupported()
822: * method documentation for more info
823: */
824: public boolean markSupported() {
825: return true;
826: }
827:
828: /**
829: * Reads the byte from the stream. If mark has been set up
830: * and was not invalidated byte is read from the underlying
831: * stream and saved into the buffer. If the current read position
832: * has been reset to the marked position and there are remaining
833: * bytes in the buffer, the byte is taken from it. In the other cases
834: * (if mark has been invalidated, or there are no buffered bytes)
835: * the byte is taken directly from the underlying stream and it is
836: * returned without saving to the buffer.
837: *
838: * @see java.io.InputStream#read()
839: * method documentation for more info
840: */
841: public int read() throws IOException {
842: // if buffer is currently used
843: if (pos >= 0) {
844: // current position in the buffer
845: int cur = pos % BUFF_SIZE;
846: // check whether the buffer contains the data to be read
847: if (cur < bar) {
848: // return the data from the buffer
849: pos++;
850: return buff[cur];
851: }
852: // check whether buffer has free space
853: if (cur != end) {
854: // it has, so read the data from the wrapped stream
855: // and place it in the buffer
856: buff[cur] = inStream.read();
857: bar = cur + 1;
858: pos++;
859: return buff[cur];
860: } else {
861: // buffer if full and can not operate
862: // any more, so invalidate the mark position
863: // and turn off the using of buffer
864: pos = -1;
865: }
866: }
867: // buffer is not used, so return the data from the wrapped stream
868: return inStream.read();
869: }
870:
871: /**
872: * @see java.io.InputStream#read(byte[] b)
873: * method documentation for more info
874: */
875: public int read(byte[] b) throws IOException {
876: return read(b, 0, b.length);
877: }
878:
879: /**
880: * @see java.io.InputStream#read(byte[] b, int off, int len)
881: * method documentation for more info
882: */
883: public int read(byte[] b, int off, int len) throws IOException {
884: int read_b;
885: int i;
886: for (i = 0; i < len; i++) {
887: if ((read_b = read()) == -1) {
888: return (i == 0) ? -1 : i;
889: }
890: b[off + i] = (byte) read_b;
891: }
892: return i;
893: }
894:
895: /**
896: * @see java.io.InputStream#reset()
897: * method documentation for more info
898: */
899: public void reset() throws IOException {
900: if (pos >= 0) {
901: pos = (end + 1) % BUFF_SIZE;
902: } else {
903: throw new IOException(Messages
904: .getString("security.15A")); //$NON-NLS-1$
905: }
906: }
907:
908: /**
909: * @see java.io.InputStream#skip(long n)
910: * method documentation for more info
911: */
912: public long skip(long n) throws IOException {
913: if (pos >= 0) {
914: long i = 0;
915: int av = available();
916: if (av < n) {
917: n = av;
918: }
919: while ((i < n) && (read() != -1)) {
920: i++;
921: }
922: return i;
923: } else {
924: return inStream.skip(n);
925: }
926: }
927: }
928: }
|