History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/security/provider/X509Factory.java (1 of 18352 files). Show all

Unified · Split

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

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t