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+
26+package sun.security.provider;
27+
28+import java.io.*;
29+import java.util.*;
30+import java.security.cert.*;
31+import sun.security.x509.X509CertImpl;
32+import sun.security.x509.X509CRLImpl;
33+import sun.security.pkcs.PKCS7;
34+import sun.security.provider.certpath.X509CertPath;
35+import sun.security.provider.certpath.X509CertificatePair;
36+import sun.security.util.DerValue;
37+import sun.security.util.Cache;
38+import sun.misc.BASE64Decoder;
39+import 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+
60+public 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