1 /*
2  * Copyright (c) 1998, 2018, 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 
26 package sun.security.provider;
27 
28 import java.io.*;
29 import java.security.PublicKey;
30 import java.util.*;
31 import java.security.cert.*;
32 
33 import jdk.internal.event.EventHelper;
34 import jdk.internal.event.X509CertificateEvent;
35 import sun.security.util.KeyUtil;
36 import sun.security.util.Pem;
37 import sun.security.x509.*;
38 import sun.security.pkcs.PKCS7;
39 import sun.security.provider.certpath.X509CertPath;
40 import sun.security.provider.certpath.X509CertificatePair;
41 import sun.security.util.DerValue;
42 import sun.security.util.Cache;
43 import java.util.Base64;
44 import sun.security.pkcs.ParsingException;
45 
46 /**
47  * This class defines a certificate factory for X.509 v3 certificates {@literal &}
48  * certification paths, and X.509 v2 certificate revocation lists (CRLs).
49  *
50  * @author Jan Luehe
51  * @author Hemma Prafullchandra
52  * @author Sean Mullan
53  *
54  *
55  * @see java.security.cert.CertificateFactorySpi
56  * @see java.security.cert.Certificate
57  * @see java.security.cert.CertPath
58  * @see java.security.cert.CRL
59  * @see java.security.cert.X509Certificate
60  * @see java.security.cert.X509CRL
61  * @see sun.security.x509.X509CertImpl
62  * @see sun.security.x509.X509CRLImpl
63  */
64 
65 public class X509Factory extends CertificateFactorySpi {
66 
67     public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
68     public static final String END_CERT = "-----END CERTIFICATE-----";
69 
70     private static final int ENC_MAX_LENGTH = 4096 * 1024; // 4 MB MAX
71 
72     private static final Cache<Object, X509CertImpl> certCache
73         = Cache.newSoftMemoryCache(750);
74     private static final Cache<Object, X509CRLImpl> crlCache
75         = Cache.newSoftMemoryCache(750);
76 
77     /**
78      * Generates an X.509 certificate object and initializes it with
79      * the data read from the input stream <code>is</code>.
80      *
81      * @param is an input stream with the certificate data.
82      *
83      * @return an X.509 certificate object initialized with the data
84      * from the input stream.
85      *
86      * @exception CertificateException on parsing errors.
87      */
88     @Override
engineGenerateCertificate(InputStream is)89     public Certificate engineGenerateCertificate(InputStream is)
90         throws CertificateException
91     {
92         if (is == null) {
93             // clear the caches (for debugging)
94             certCache.clear();
95             X509CertificatePair.clearCache();
96             throw new CertificateException("Missing input stream");
97         }
98         try {
99             byte[] encoding = readOneBlock(is);
100             if (encoding != null) {
101                 X509CertImpl cert = getFromCache(certCache, encoding);
102                 if (cert != null) {
103                     return cert;
104                 }
105                 cert = new X509CertImpl(encoding);
106                 addToCache(certCache, cert.getEncodedInternal(), cert);
107                 // record cert details if necessary
108                 commitEvent(cert);
109                 return cert;
110             } else {
111                 throw new IOException("Empty input");
112             }
113         } catch (IOException ioe) {
114             throw new CertificateException("Could not parse certificate: " +
115                     ioe.toString(), ioe);
116         }
117     }
118 
119     /**
120      * Read from the stream until length bytes have been read or EOF has
121      * been reached. Return the number of bytes actually read.
122      */
readFully(InputStream in, ByteArrayOutputStream bout, int length)123     private static int readFully(InputStream in, ByteArrayOutputStream bout,
124             int length) throws IOException {
125         int read = 0;
126         byte[] buffer = new byte[2048];
127         while (length > 0) {
128             int n = in.read(buffer, 0, length<2048?length:2048);
129             if (n <= 0) {
130                 break;
131             }
132             bout.write(buffer, 0, n);
133             read += n;
134             length -= n;
135         }
136         return read;
137     }
138 
139     /**
140      * Return an interned X509CertImpl for the given certificate.
141      * If the given X509Certificate or X509CertImpl is already present
142      * in the cert cache, the cached object is returned. Otherwise,
143      * if it is a X509Certificate, it is first converted to a X509CertImpl.
144      * Then the X509CertImpl is added to the cache and returned.
145      *
146      * Note that all certificates created via generateCertificate(InputStream)
147      * are already interned and this method does not need to be called.
148      * It is useful for certificates that cannot be created via
149      * generateCertificate() and for converting other X509Certificate
150      * implementations to an X509CertImpl.
151      *
152      * @param c The source X509Certificate
153      * @return An X509CertImpl object that is either a cached certificate or a
154      *      newly built X509CertImpl from the provided X509Certificate
155      * @throws CertificateException if failures occur while obtaining the DER
156      *      encoding for certificate data.
157      */
intern(X509Certificate c)158     public static synchronized X509CertImpl intern(X509Certificate c)
159             throws CertificateException {
160         if (c == null) {
161             return null;
162         }
163         boolean isImpl = c instanceof X509CertImpl;
164         byte[] encoding;
165         if (isImpl) {
166             encoding = ((X509CertImpl)c).getEncodedInternal();
167         } else {
168             encoding = c.getEncoded();
169         }
170         X509CertImpl newC = getFromCache(certCache, encoding);
171         if (newC != null) {
172             return newC;
173         }
174         if (isImpl) {
175             newC = (X509CertImpl)c;
176         } else {
177             newC = new X509CertImpl(encoding);
178             encoding = newC.getEncodedInternal();
179         }
180         addToCache(certCache, encoding, newC);
181         return newC;
182     }
183 
184     /**
185      * Return an interned X509CRLImpl for the given certificate.
186      * For more information, see intern(X509Certificate).
187      *
188      * @param c The source X509CRL
189      * @return An X509CRLImpl object that is either a cached CRL or a
190      *      newly built X509CRLImpl from the provided X509CRL
191      * @throws CRLException if failures occur while obtaining the DER
192      *      encoding for CRL data.
193      */
intern(X509CRL c)194     public static synchronized X509CRLImpl intern(X509CRL c)
195             throws CRLException {
196         if (c == null) {
197             return null;
198         }
199         boolean isImpl = c instanceof X509CRLImpl;
200         byte[] encoding;
201         if (isImpl) {
202             encoding = ((X509CRLImpl)c).getEncodedInternal();
203         } else {
204             encoding = c.getEncoded();
205         }
206         X509CRLImpl newC = getFromCache(crlCache, encoding);
207         if (newC != null) {
208             return newC;
209         }
210         if (isImpl) {
211             newC = (X509CRLImpl)c;
212         } else {
213             newC = new X509CRLImpl(encoding);
214             encoding = newC.getEncodedInternal();
215         }
216         addToCache(crlCache, encoding, newC);
217         return newC;
218     }
219 
220     /**
221      * Get the X509CertImpl or X509CRLImpl from the cache.
222      */
getFromCache(Cache<K,V> cache, byte[] encoding)223     private static synchronized <K,V> V getFromCache(Cache<K,V> cache,
224             byte[] encoding) {
225         Object key = new Cache.EqualByteArray(encoding);
226         return cache.get(key);
227     }
228 
229     /**
230      * Add the X509CertImpl or X509CRLImpl to the cache.
231      */
addToCache(Cache<Object, V> cache, byte[] encoding, V value)232     private static synchronized <V> void addToCache(Cache<Object, V> cache,
233             byte[] encoding, V value) {
234         if (encoding.length > ENC_MAX_LENGTH) {
235             return;
236         }
237         Object key = new Cache.EqualByteArray(encoding);
238         cache.put(key, value);
239     }
240 
241     /**
242      * Generates a <code>CertPath</code> object and initializes it with
243      * the data read from the <code>InputStream</code> inStream. The data
244      * is assumed to be in the default encoding.
245      *
246      * @param inStream an <code>InputStream</code> containing the data
247      * @return a <code>CertPath</code> initialized with the data from the
248      *   <code>InputStream</code>
249      * @exception CertificateException if an exception occurs while decoding
250      * @since 1.4
251      */
252     @Override
engineGenerateCertPath(InputStream inStream)253     public CertPath engineGenerateCertPath(InputStream inStream)
254         throws CertificateException
255     {
256         if (inStream == null) {
257             throw new CertificateException("Missing input stream");
258         }
259         try {
260             byte[] encoding = readOneBlock(inStream);
261             if (encoding != null) {
262                 return new X509CertPath(new ByteArrayInputStream(encoding));
263             } else {
264                 throw new IOException("Empty input");
265             }
266         } catch (IOException ioe) {
267             throw new CertificateException(ioe.getMessage());
268         }
269     }
270 
271     /**
272      * Generates a <code>CertPath</code> object and initializes it with
273      * the data read from the <code>InputStream</code> inStream. The data
274      * is assumed to be in the specified encoding.
275      *
276      * @param inStream an <code>InputStream</code> containing the data
277      * @param encoding the encoding used for the data
278      * @return a <code>CertPath</code> initialized with the data from the
279      *   <code>InputStream</code>
280      * @exception CertificateException if an exception occurs while decoding or
281      *   the encoding requested is not supported
282      * @since 1.4
283      */
284     @Override
engineGenerateCertPath(InputStream inStream, String encoding)285     public CertPath engineGenerateCertPath(InputStream inStream,
286         String encoding) throws CertificateException
287     {
288         if (inStream == null) {
289             throw new CertificateException("Missing input stream");
290         }
291         try {
292             byte[] data = readOneBlock(inStream);
293             if (data != null) {
294                 return new X509CertPath(new ByteArrayInputStream(data), encoding);
295             } else {
296                 throw new IOException("Empty input");
297             }
298         } catch (IOException ioe) {
299             throw new CertificateException(ioe.getMessage());
300         }
301     }
302 
303     /**
304      * Generates a <code>CertPath</code> object and initializes it with
305      * a <code>List</code> of <code>Certificate</code>s.
306      * <p>
307      * The certificates supplied must be of a type supported by the
308      * <code>CertificateFactory</code>. They will be copied out of the supplied
309      * <code>List</code> object.
310      *
311      * @param certificates a <code>List</code> of <code>Certificate</code>s
312      * @return a <code>CertPath</code> initialized with the supplied list of
313      *   certificates
314      * @exception CertificateException if an exception occurs
315      * @since 1.4
316      */
317     @Override
318     public CertPath
engineGenerateCertPath(List<? extends Certificate> certificates)319         engineGenerateCertPath(List<? extends Certificate> certificates)
320         throws CertificateException
321     {
322         return(new X509CertPath(certificates));
323     }
324 
325     /**
326      * Returns an iteration of the <code>CertPath</code> encodings supported
327      * by this certificate factory, with the default encoding first.
328      * <p>
329      * Attempts to modify the returned <code>Iterator</code> via its
330      * <code>remove</code> method result in an
331      * <code>UnsupportedOperationException</code>.
332      *
333      * @return an <code>Iterator</code> over the names of the supported
334      *         <code>CertPath</code> encodings (as <code>String</code>s)
335      * @since 1.4
336      */
337     @Override
engineGetCertPathEncodings()338     public Iterator<String> engineGetCertPathEncodings() {
339         return(X509CertPath.getEncodingsStatic());
340     }
341 
342     /**
343      * Returns a (possibly empty) collection view of X.509 certificates read
344      * from the given input stream <code>is</code>.
345      *
346      * @param is the input stream with the certificates.
347      *
348      * @return a (possibly empty) collection view of X.509 certificate objects
349      * initialized with the data from the input stream.
350      *
351      * @exception CertificateException on parsing errors.
352      */
353     @Override
354     public Collection<? extends java.security.cert.Certificate>
engineGenerateCertificates(InputStream is)355             engineGenerateCertificates(InputStream is)
356             throws CertificateException {
357         if (is == null) {
358             throw new CertificateException("Missing input stream");
359         }
360         try {
361             return parseX509orPKCS7Cert(is);
362         } catch (IOException ioe) {
363             throw new CertificateException(ioe);
364         }
365     }
366 
367     /**
368      * Generates an X.509 certificate revocation list (CRL) object and
369      * initializes it with the data read from the given input stream
370      * <code>is</code>.
371      *
372      * @param is an input stream with the CRL data.
373      *
374      * @return an X.509 CRL object initialized with the data
375      * from the input stream.
376      *
377      * @exception CRLException on parsing errors.
378      */
379     @Override
engineGenerateCRL(InputStream is)380     public CRL engineGenerateCRL(InputStream is)
381         throws CRLException
382     {
383         if (is == null) {
384             // clear the cache (for debugging)
385             crlCache.clear();
386             throw new CRLException("Missing input stream");
387         }
388         try {
389             byte[] encoding = readOneBlock(is);
390             if (encoding != null) {
391                 X509CRLImpl crl = getFromCache(crlCache, encoding);
392                 if (crl != null) {
393                     return crl;
394                 }
395                 crl = new X509CRLImpl(encoding);
396                 addToCache(crlCache, crl.getEncodedInternal(), crl);
397                 return crl;
398             } else {
399                 throw new IOException("Empty input");
400             }
401         } catch (IOException ioe) {
402             throw new CRLException(ioe.getMessage());
403         }
404     }
405 
406     /**
407      * Returns a (possibly empty) collection view of X.509 CRLs read
408      * from the given input stream <code>is</code>.
409      *
410      * @param is the input stream with the CRLs.
411      *
412      * @return a (possibly empty) collection view of X.509 CRL objects
413      * initialized with the data from the input stream.
414      *
415      * @exception CRLException on parsing errors.
416      */
417     @Override
engineGenerateCRLs( InputStream is)418     public Collection<? extends java.security.cert.CRL> engineGenerateCRLs(
419             InputStream is) throws CRLException
420     {
421         if (is == null) {
422             throw new CRLException("Missing input stream");
423         }
424         try {
425             return parseX509orPKCS7CRL(is);
426         } catch (IOException ioe) {
427             throw new CRLException(ioe.getMessage());
428         }
429     }
430 
431     /*
432      * Parses the data in the given input stream as a sequence of DER
433      * encoded X.509 certificates (in binary or base 64 encoded format) OR
434      * as a single PKCS#7 encoded blob (in binary or base64 encoded format).
435      */
436     private Collection<? extends java.security.cert.Certificate>
parseX509orPKCS7Cert(InputStream is)437         parseX509orPKCS7Cert(InputStream is)
438         throws CertificateException, IOException
439     {
440         int peekByte;
441         byte[] data;
442         PushbackInputStream pbis = new PushbackInputStream(is);
443         Collection<X509CertImpl> coll = new ArrayList<>();
444 
445         // Test the InputStream for end-of-stream.  If the stream's
446         // initial state is already at end-of-stream then return
447         // an empty collection.  Otherwise, push the byte back into the
448         // stream and let readOneBlock look for the first certificate.
449         peekByte = pbis.read();
450         if (peekByte == -1) {
451             return new ArrayList<>(0);
452         } else {
453             pbis.unread(peekByte);
454             data = readOneBlock(pbis);
455         }
456 
457         // If we end up with a null value after reading the first block
458         // then we know the end-of-stream has been reached and no certificate
459         // data has been found.
460         if (data == null) {
461             throw new CertificateException("No certificate data found");
462         }
463 
464         try {
465             PKCS7 pkcs7 = new PKCS7(data);
466             X509Certificate[] certs = pkcs7.getCertificates();
467             // certs are optional in PKCS #7
468             if (certs != null) {
469                 return Arrays.asList(certs);
470             } else {
471                 // no certificates provided
472                 return new ArrayList<>(0);
473             }
474         } catch (ParsingException e) {
475             while (data != null) {
476                 coll.add(new X509CertImpl(data));
477                 data = readOneBlock(pbis);
478             }
479         }
480         return coll;
481     }
482 
483     /*
484      * Parses the data in the given input stream as a sequence of DER encoded
485      * X.509 CRLs (in binary or base 64 encoded format) OR as a single PKCS#7
486      * encoded blob (in binary or base 64 encoded format).
487      */
488     private Collection<? extends java.security.cert.CRL>
parseX509orPKCS7CRL(InputStream is)489         parseX509orPKCS7CRL(InputStream is)
490         throws CRLException, IOException
491     {
492         int peekByte;
493         byte[] data;
494         PushbackInputStream pbis = new PushbackInputStream(is);
495         Collection<X509CRLImpl> coll = new ArrayList<>();
496 
497         // Test the InputStream for end-of-stream.  If the stream's
498         // initial state is already at end-of-stream then return
499         // an empty collection.  Otherwise, push the byte back into the
500         // stream and let readOneBlock look for the first CRL.
501         peekByte = pbis.read();
502         if (peekByte == -1) {
503             return new ArrayList<>(0);
504         } else {
505             pbis.unread(peekByte);
506             data = readOneBlock(pbis);
507         }
508 
509         // If we end up with a null value after reading the first block
510         // then we know the end-of-stream has been reached and no CRL
511         // data has been found.
512         if (data == null) {
513             throw new CRLException("No CRL data found");
514         }
515 
516         try {
517             PKCS7 pkcs7 = new PKCS7(data);
518             X509CRL[] crls = pkcs7.getCRLs();
519             // CRLs are optional in PKCS #7
520             if (crls != null) {
521                 return Arrays.asList(crls);
522             } else {
523                 // no crls provided
524                 return new ArrayList<>(0);
525             }
526         } catch (ParsingException e) {
527             while (data != null) {
528                 coll.add(new X509CRLImpl(data));
529                 data = readOneBlock(pbis);
530             }
531         }
532         return coll;
533     }
534 
535     /**
536      * Returns an ASN.1 SEQUENCE from a stream, which might be a BER-encoded
537      * binary block or a PEM-style BASE64-encoded ASCII data. In the latter
538      * case, it's de-BASE64'ed before return.
539      *
540      * After the reading, the input stream pointer is after the BER block, or
541      * after the newline character after the -----END SOMETHING----- line.
542      *
543      * @param is the InputStream
544      * @return byte block or null if end of stream
545      * @throws IOException If any parsing error
546      */
readOneBlock(InputStream is)547     private static byte[] readOneBlock(InputStream is) throws IOException {
548 
549         // The first character of a BLOCK.
550         int c = is.read();
551         if (c == -1) {
552             return null;
553         }
554         if (c == DerValue.tag_Sequence) {
555             ByteArrayOutputStream bout = new ByteArrayOutputStream(2048);
556             bout.write(c);
557             readBERInternal(is, bout, c);
558             return bout.toByteArray();
559         } else {
560             // Read BASE64 encoded data, might skip info at the beginning
561             ByteArrayOutputStream data = new ByteArrayOutputStream();
562 
563             // Step 1: Read until header is found
564             int hyphen = (c=='-') ? 1: 0;   // count of consequent hyphens
565             int last = (c=='-') ? -1: c;    // the char before hyphen
566             while (true) {
567                 int next = is.read();
568                 if (next == -1) {
569                     // We accept useless data after the last block,
570                     // say, empty lines.
571                     return null;
572                 }
573                 if (next == '-') {
574                     hyphen++;
575                 } else {
576                     hyphen = 0;
577                     last = next;
578                 }
579                 if (hyphen == 5 && (last == -1 || last == '\r' || last == '\n')) {
580                     break;
581                 }
582             }
583 
584             // Step 2: Read the rest of header, determine the line end
585             int end;
586             StringBuilder header = new StringBuilder("-----");
587             while (true) {
588                 int next = is.read();
589                 if (next == -1) {
590                     throw new IOException("Incomplete data");
591                 }
592                 if (next == '\n') {
593                     end = '\n';
594                     break;
595                 }
596                 if (next == '\r') {
597                     next = is.read();
598                     if (next == -1) {
599                         throw new IOException("Incomplete data");
600                     }
601                     if (next == '\n') {
602                         end = '\n';
603                     } else {
604                         end = '\r';
605                         // Skip all white space chars
606                         if (next != 9 && next != 10 && next != 13 && next != 32) {
607                             data.write(next);
608                         }
609                     }
610                     break;
611                 }
612                 header.append((char)next);
613             }
614 
615             // Step 3: Read the data
616             while (true) {
617                 int next = is.read();
618                 if (next == -1) {
619                     throw new IOException("Incomplete data");
620                 }
621                 if (next != '-') {
622                     // Skip all white space chars
623                     if (next != 9 && next != 10 && next != 13 && next != 32) {
624                         data.write(next);
625                     }
626                 } else {
627                     break;
628                 }
629             }
630 
631             // Step 4: Consume the footer
632             StringBuilder footer = new StringBuilder("-");
633             while (true) {
634                 int next = is.read();
635                 // Add next == '\n' for maximum safety, in case endline
636                 // is not consistent.
637                 if (next == -1 || next == end || next == '\n') {
638                     break;
639                 }
640                 if (next != '\r') footer.append((char)next);
641             }
642 
643             checkHeaderFooter(header.toString().stripTrailing(),
644                     footer.toString().stripTrailing());
645 
646             try {
647                 return Base64.getDecoder().decode(data.toByteArray());
648             } catch (IllegalArgumentException e) {
649                 throw new IOException(e);
650             }
651         }
652     }
653 
checkHeaderFooter(String header, String footer)654     private static void checkHeaderFooter(String header,
655             String footer) throws IOException {
656         if (header.length() < 16 || !header.startsWith("-----BEGIN ") ||
657                 !header.endsWith("-----")) {
658             throw new IOException("Illegal header: " + header);
659         }
660         if (footer.length() < 14 || !footer.startsWith("-----END ") ||
661                 !footer.endsWith("-----")) {
662             throw new IOException("Illegal footer: " + footer);
663         }
664         String headerType = header.substring(11, header.length()-5);
665         String footerType = footer.substring(9, footer.length()-5);
666         if (!headerType.equals(footerType)) {
667             throw new IOException("Header and footer do not match: " +
668                     header + " " + footer);
669         }
670     }
671 
672     /**
673      * Read one BER data block. This method is aware of indefinite-length BER
674      * encoding and will read all of the sub-sections in a recursive way
675      *
676      * @param is    Read from this InputStream
677      * @param bout  Write into this OutputStream
678      * @param tag   Tag already read (-1 mean not read)
679      * @return     The current tag, used to check EOC in indefinite-length BER
680      * @throws IOException Any parsing error
681      */
readBERInternal(InputStream is, ByteArrayOutputStream bout, int tag)682     private static int readBERInternal(InputStream is,
683             ByteArrayOutputStream bout, int tag) throws IOException {
684 
685         if (tag == -1) {        // Not read before the call, read now
686             tag = is.read();
687             if (tag == -1) {
688                 throw new IOException("BER/DER tag info absent");
689             }
690             if ((tag & 0x1f) == 0x1f) {
691                 throw new IOException("Multi octets tag not supported");
692             }
693             bout.write(tag);
694         }
695 
696         int n = is.read();
697         if (n == -1) {
698             throw new IOException("BER/DER length info absent");
699         }
700         bout.write(n);
701 
702         int length;
703 
704         if (n == 0x80) {        // Indefinite-length encoding
705             if ((tag & 0x20) != 0x20) {
706                 throw new IOException(
707                         "Non constructed encoding must have definite length");
708             }
709             while (true) {
710                 int subTag = readBERInternal(is, bout, -1);
711                 if (subTag == 0) {   // EOC, end of indefinite-length section
712                     break;
713                 }
714             }
715         } else {
716             if (n < 0x80) {
717                 length = n;
718             } else if (n == 0x81) {
719                 length = is.read();
720                 if (length == -1) {
721                     throw new IOException("Incomplete BER/DER length info");
722                 }
723                 bout.write(length);
724             } else if (n == 0x82) {
725                 int highByte = is.read();
726                 int lowByte = is.read();
727                 if (lowByte == -1) {
728                     throw new IOException("Incomplete BER/DER length info");
729                 }
730                 bout.write(highByte);
731                 bout.write(lowByte);
732                 length = (highByte << 8) | lowByte;
733             } else if (n == 0x83) {
734                 int highByte = is.read();
735                 int midByte = is.read();
736                 int lowByte = is.read();
737                 if (lowByte == -1) {
738                     throw new IOException("Incomplete BER/DER length info");
739                 }
740                 bout.write(highByte);
741                 bout.write(midByte);
742                 bout.write(lowByte);
743                 length = (highByte << 16) | (midByte << 8) | lowByte;
744             } else if (n == 0x84) {
745                 int highByte = is.read();
746                 int nextByte = is.read();
747                 int midByte = is.read();
748                 int lowByte = is.read();
749                 if (lowByte == -1) {
750                     throw new IOException("Incomplete BER/DER length info");
751                 }
752                 if (highByte > 127) {
753                     throw new IOException("Invalid BER/DER data (a little huge?)");
754                 }
755                 bout.write(highByte);
756                 bout.write(nextByte);
757                 bout.write(midByte);
758                 bout.write(lowByte);
759                 length = (highByte << 24 ) | (nextByte << 16) |
760                         (midByte << 8) | lowByte;
761             } else { // ignore longer length forms
762                 throw new IOException("Invalid BER/DER data (too huge?)");
763             }
764             if (readFully(is, bout, length) != length) {
765                 throw new IOException("Incomplete BER/DER data");
766             }
767         }
768         return tag;
769     }
770 
commitEvent(X509CertImpl info)771     private void commitEvent(X509CertImpl info) {
772         X509CertificateEvent xce = new X509CertificateEvent();
773         if (xce.shouldCommit() || EventHelper.isLoggingSecurity()) {
774             PublicKey pKey = info.getPublicKey();
775             String algId = info.getSigAlgName();
776             String serNum = info.getSerialNumber().toString(16);
777             String subject = info.getSubjectDN().getName();
778             String issuer = info.getIssuerDN().getName();
779             String keyType = pKey.getAlgorithm();
780             int length = KeyUtil.getKeySize(pKey);
781             int hashCode = info.hashCode();
782             long beginDate = info.getNotBefore().getTime();
783             long endDate = info.getNotAfter().getTime();
784             if (xce.shouldCommit()) {
785                 xce.algorithm = algId;
786                 xce.serialNumber = serNum;
787                 xce.subject = subject;
788                 xce.issuer = issuer;
789                 xce.keyType = keyType;
790                 xce.keyLength = length;
791                 xce.certificateId = hashCode;
792                 xce.validFrom = beginDate;
793                 xce.validUntil = endDate;
794                 xce.commit();
795             }
796             if (EventHelper.isLoggingSecurity()) {
797                 EventHelper.logX509CertificateEvent(algId,
798                         serNum,
799                         subject,
800                         issuer,
801                         keyType,
802                         length,
803                         hashCode,
804                         beginDate,
805                         endDate);
806             }
807         }
808     }
809 }
810