0N/A/*
3909N/A * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.provider;
0N/A
0N/Aimport java.io.*;
0N/Aimport java.util.*;
0N/Aimport java.security.cert.*;
0N/Aimport sun.security.x509.X509CertImpl;
0N/Aimport sun.security.x509.X509CRLImpl;
0N/Aimport sun.security.pkcs.PKCS7;
0N/Aimport sun.security.provider.certpath.X509CertPath;
0N/Aimport sun.security.provider.certpath.X509CertificatePair;
0N/Aimport sun.security.util.DerValue;
0N/Aimport sun.security.util.Cache;
0N/Aimport sun.misc.BASE64Decoder;
2243N/Aimport sun.security.pkcs.ParsingException;
0N/A
0N/A/**
0N/A * This class defines a certificate factory for X.509 v3 certificates &
0N/A * certification paths, and X.509 v2 certificate revocation lists (CRLs).
0N/A *
0N/A * @author Jan Luehe
0N/A * @author Hemma Prafullchandra
0N/A * @author Sean Mullan
0N/A *
0N/A *
0N/A * @see java.security.cert.CertificateFactorySpi
0N/A * @see java.security.cert.Certificate
0N/A * @see java.security.cert.CertPath
0N/A * @see java.security.cert.CRL
0N/A * @see java.security.cert.X509Certificate
0N/A * @see java.security.cert.X509CRL
0N/A * @see sun.security.x509.X509CertImpl
0N/A * @see sun.security.x509.X509CRLImpl
0N/A */
0N/A
0N/Apublic class X509Factory extends CertificateFactorySpi {
0N/A
0N/A public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
0N/A public static final String END_CERT = "-----END CERTIFICATE-----";
0N/A
0N/A private static final int ENC_MAX_LENGTH = 4096 * 1024; // 4 MB MAX
0N/A
0N/A private static final Cache certCache = Cache.newSoftMemoryCache(750);
0N/A private static final Cache crlCache = Cache.newSoftMemoryCache(750);
0N/A
0N/A /**
0N/A * Generates an X.509 certificate object and initializes it with
0N/A * the data read from the input stream <code>is</code>.
0N/A *
0N/A * @param is an input stream with the certificate data.
0N/A *
0N/A * @return an X.509 certificate object initialized with the data
0N/A * from the input stream.
0N/A *
0N/A * @exception CertificateException on parsing errors.
0N/A */
0N/A public Certificate engineGenerateCertificate(InputStream is)
0N/A throws CertificateException
0N/A {
0N/A if (is == null) {
0N/A // clear the caches (for debugging)
0N/A certCache.clear();
0N/A X509CertificatePair.clearCache();
0N/A throw new CertificateException("Missing input stream");
0N/A }
0N/A try {
2243N/A byte[] encoding = readOneBlock(is);
0N/A if (encoding != null) {
0N/A X509CertImpl cert = (X509CertImpl)getFromCache(certCache, encoding);
0N/A if (cert != null) {
0N/A return cert;
0N/A }
0N/A cert = new X509CertImpl(encoding);
0N/A addToCache(certCache, cert.getEncodedInternal(), cert);
0N/A return cert;
0N/A } else {
2243N/A throw new IOException("Empty input");
0N/A }
0N/A } catch (IOException ioe) {
0N/A throw (CertificateException)new CertificateException
0N/A ("Could not parse certificate: " + ioe.toString()).initCause(ioe);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Read from the stream until length bytes have been read or EOF has
0N/A * been reached. Return the number of bytes actually read.
0N/A */
2243N/A private static int readFully(InputStream in, ByteArrayOutputStream bout,
0N/A int length) throws IOException {
0N/A int read = 0;
2243N/A byte[] buffer = new byte[2048];
0N/A while (length > 0) {
2243N/A int n = in.read(buffer, 0, length<2048?length:2048);
0N/A if (n <= 0) {
0N/A break;
0N/A }
2243N/A bout.write(buffer, 0, n);
0N/A read += n;
0N/A length -= n;
0N/A }
0N/A return read;
0N/A }
0N/A
0N/A /**
0N/A * Return an interned X509CertImpl for the given certificate.
0N/A * If the given X509Certificate or X509CertImpl is already present
0N/A * in the cert cache, the cached object is returned. Otherwise,
0N/A * if it is a X509Certificate, it is first converted to a X509CertImpl.
0N/A * Then the X509CertImpl is added to the cache and returned.
0N/A *
0N/A * Note that all certificates created via generateCertificate(InputStream)
0N/A * are already interned and this method does not need to be called.
0N/A * It is useful for certificates that cannot be created via
0N/A * generateCertificate() and for converting other X509Certificate
0N/A * implementations to an X509CertImpl.
0N/A */
0N/A public static synchronized X509CertImpl intern(X509Certificate c)
0N/A throws CertificateException {
0N/A if (c == null) {
0N/A return null;
0N/A }
0N/A boolean isImpl = c instanceof X509CertImpl;
0N/A byte[] encoding;
0N/A if (isImpl) {
0N/A encoding = ((X509CertImpl)c).getEncodedInternal();
0N/A } else {
0N/A encoding = c.getEncoded();
0N/A }
0N/A X509CertImpl newC = (X509CertImpl)getFromCache(certCache, encoding);
0N/A if (newC != null) {
0N/A return newC;
0N/A }
0N/A if (isImpl) {
0N/A newC = (X509CertImpl)c;
0N/A } else {
0N/A newC = new X509CertImpl(encoding);
0N/A encoding = newC.getEncodedInternal();
0N/A }
0N/A addToCache(certCache, encoding, newC);
0N/A return newC;
0N/A }
0N/A
0N/A /**
0N/A * Return an interned X509CRLImpl for the given certificate.
0N/A * For more information, see intern(X509Certificate).
0N/A */
0N/A public static synchronized X509CRLImpl intern(X509CRL c)
0N/A throws CRLException {
0N/A if (c == null) {
0N/A return null;
0N/A }
0N/A boolean isImpl = c instanceof X509CRLImpl;
0N/A byte[] encoding;
0N/A if (isImpl) {
0N/A encoding = ((X509CRLImpl)c).getEncodedInternal();
0N/A } else {
0N/A encoding = c.getEncoded();
0N/A }
0N/A X509CRLImpl newC = (X509CRLImpl)getFromCache(crlCache, encoding);
0N/A if (newC != null) {
0N/A return newC;
0N/A }
0N/A if (isImpl) {
0N/A newC = (X509CRLImpl)c;
0N/A } else {
0N/A newC = new X509CRLImpl(encoding);
0N/A encoding = newC.getEncodedInternal();
0N/A }
0N/A addToCache(crlCache, encoding, newC);
0N/A return newC;
0N/A }
0N/A
0N/A /**
0N/A * Get the X509CertImpl or X509CRLImpl from the cache.
0N/A */
0N/A private static synchronized Object getFromCache(Cache cache,
0N/A byte[] encoding) {
0N/A Object key = new Cache.EqualByteArray(encoding);
0N/A Object value = cache.get(key);
0N/A return value;
0N/A }
0N/A
0N/A /**
0N/A * Add the X509CertImpl or X509CRLImpl to the cache.
0N/A */
0N/A private static synchronized void addToCache(Cache cache, byte[] encoding,
0N/A Object value) {
0N/A if (encoding.length > ENC_MAX_LENGTH) {
0N/A return;
0N/A }
0N/A Object key = new Cache.EqualByteArray(encoding);
0N/A cache.put(key, value);
0N/A }
0N/A
0N/A /**
0N/A * Generates a <code>CertPath</code> object and initializes it with
0N/A * the data read from the <code>InputStream</code> inStream. The data
0N/A * is assumed to be in the default encoding.
0N/A *
0N/A * @param inStream an <code>InputStream</code> containing the data
0N/A * @return a <code>CertPath</code> initialized with the data from the
0N/A * <code>InputStream</code>
0N/A * @exception CertificateException if an exception occurs while decoding
0N/A * @since 1.4
0N/A */
0N/A public CertPath engineGenerateCertPath(InputStream inStream)
0N/A throws CertificateException
0N/A {
0N/A if (inStream == null) {
0N/A throw new CertificateException("Missing input stream");
0N/A }
0N/A try {
2243N/A byte[] encoding = readOneBlock(inStream);
2243N/A if (encoding != null) {
2243N/A return new X509CertPath(new ByteArrayInputStream(encoding));
0N/A } else {
2243N/A throw new IOException("Empty input");
0N/A }
0N/A } catch (IOException ioe) {
0N/A throw new CertificateException(ioe.getMessage());
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Generates a <code>CertPath</code> object and initializes it with
0N/A * the data read from the <code>InputStream</code> inStream. The data
0N/A * is assumed to be in the specified encoding.
0N/A *
0N/A * @param inStream an <code>InputStream</code> containing the data
0N/A * @param encoding the encoding used for the data
0N/A * @return a <code>CertPath</code> initialized with the data from the
0N/A * <code>InputStream</code>
0N/A * @exception CertificateException if an exception occurs while decoding or
0N/A * the encoding requested is not supported
0N/A * @since 1.4
0N/A */
0N/A public CertPath engineGenerateCertPath(InputStream inStream,
0N/A String encoding) throws CertificateException
0N/A {
0N/A if (inStream == null) {
0N/A throw new CertificateException("Missing input stream");
0N/A }
0N/A try {
2243N/A byte[] data = readOneBlock(inStream);
2243N/A if (data != null) {
0N/A return new X509CertPath(new ByteArrayInputStream(data), encoding);
0N/A } else {
2243N/A throw new IOException("Empty input");
0N/A }
0N/A } catch (IOException ioe) {
0N/A throw new CertificateException(ioe.getMessage());
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Generates a <code>CertPath</code> object and initializes it with
0N/A * a <code>List</code> of <code>Certificate</code>s.
0N/A * <p>
0N/A * The certificates supplied must be of a type supported by the
0N/A * <code>CertificateFactory</code>. They will be copied out of the supplied
0N/A * <code>List</code> object.
0N/A *
0N/A * @param certificates a <code>List</code> of <code>Certificate</code>s
0N/A * @return a <code>CertPath</code> initialized with the supplied list of
0N/A * certificates
0N/A * @exception CertificateException if an exception occurs
0N/A * @since 1.4
0N/A */
0N/A public CertPath
0N/A engineGenerateCertPath(List<? extends Certificate> certificates)
0N/A throws CertificateException
0N/A {
0N/A return(new X509CertPath(certificates));
0N/A }
0N/A
0N/A /**
0N/A * Returns an iteration of the <code>CertPath</code> encodings supported
0N/A * by this certificate factory, with the default encoding first.
0N/A * <p>
0N/A * Attempts to modify the returned <code>Iterator</code> via its
0N/A * <code>remove</code> method result in an
0N/A * <code>UnsupportedOperationException</code>.
0N/A *
0N/A * @return an <code>Iterator</code> over the names of the supported
0N/A * <code>CertPath</code> encodings (as <code>String</code>s)
0N/A * @since 1.4
0N/A */
0N/A public Iterator<String> engineGetCertPathEncodings() {
0N/A return(X509CertPath.getEncodingsStatic());
0N/A }
0N/A
0N/A /**
0N/A * Returns a (possibly empty) collection view of X.509 certificates read
0N/A * from the given input stream <code>is</code>.
0N/A *
0N/A * @param is the input stream with the certificates.
0N/A *
0N/A * @return a (possibly empty) collection view of X.509 certificate objects
0N/A * initialized with the data from the input stream.
0N/A *
0N/A * @exception CertificateException on parsing errors.
0N/A */
0N/A public Collection<? extends java.security.cert.Certificate>
0N/A engineGenerateCertificates(InputStream is)
0N/A throws CertificateException {
0N/A if (is == null) {
0N/A throw new CertificateException("Missing input stream");
0N/A }
0N/A try {
0N/A return parseX509orPKCS7Cert(is);
0N/A } catch (IOException ioe) {
0N/A throw new CertificateException(ioe);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Generates an X.509 certificate revocation list (CRL) object and
0N/A * initializes it with the data read from the given input stream
0N/A * <code>is</code>.
0N/A *
0N/A * @param is an input stream with the CRL data.
0N/A *
0N/A * @return an X.509 CRL object initialized with the data
0N/A * from the input stream.
0N/A *
0N/A * @exception CRLException on parsing errors.
0N/A */
0N/A public CRL engineGenerateCRL(InputStream is)
0N/A throws CRLException
0N/A {
0N/A if (is == null) {
0N/A // clear the cache (for debugging)
0N/A crlCache.clear();
0N/A throw new CRLException("Missing input stream");
0N/A }
0N/A try {
2243N/A byte[] encoding = readOneBlock(is);
0N/A if (encoding != null) {
0N/A X509CRLImpl crl = (X509CRLImpl)getFromCache(crlCache, encoding);
0N/A if (crl != null) {
0N/A return crl;
0N/A }
0N/A crl = new X509CRLImpl(encoding);
0N/A addToCache(crlCache, crl.getEncodedInternal(), crl);
0N/A return crl;
0N/A } else {
2243N/A throw new IOException("Empty input");
0N/A }
0N/A } catch (IOException ioe) {
0N/A throw new CRLException(ioe.getMessage());
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns a (possibly empty) collection view of X.509 CRLs read
0N/A * from the given input stream <code>is</code>.
0N/A *
0N/A * @param is the input stream with the CRLs.
0N/A *
0N/A * @return a (possibly empty) collection view of X.509 CRL objects
0N/A * initialized with the data from the input stream.
0N/A *
0N/A * @exception CRLException on parsing errors.
0N/A */
2243N/A public Collection<? extends java.security.cert.CRL> engineGenerateCRLs(
2243N/A InputStream is) throws CRLException
0N/A {
0N/A if (is == null) {
0N/A throw new CRLException("Missing input stream");
0N/A }
0N/A try {
0N/A return parseX509orPKCS7CRL(is);
0N/A } catch (IOException ioe) {
0N/A throw new CRLException(ioe.getMessage());
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Parses the data in the given input stream as a sequence of DER
0N/A * encoded X.509 certificates (in binary or base 64 encoded format) OR
0N/A * as a single PKCS#7 encoded blob (in binary or base64 encoded format).
0N/A */
0N/A private Collection<? extends java.security.cert.Certificate>
0N/A parseX509orPKCS7Cert(InputStream is)
0N/A throws CertificateException, IOException
0N/A {
3384N/A Collection<X509CertImpl> coll = new ArrayList<>();
2243N/A byte[] data = readOneBlock(is);
2243N/A if (data == null) {
3384N/A return new ArrayList<>(0);
2243N/A }
2243N/A try {
2243N/A PKCS7 pkcs7 = new PKCS7(data);
2243N/A X509Certificate[] certs = pkcs7.getCertificates();
2243N/A // certs are optional in PKCS #7
2243N/A if (certs != null) {
2243N/A return Arrays.asList(certs);
2243N/A } else {
2243N/A // no crls provided
3384N/A return new ArrayList<>(0);
0N/A }
2243N/A } catch (ParsingException e) {
2243N/A while (data != null) {
2243N/A coll.add(new X509CertImpl(data));
2243N/A data = readOneBlock(is);
0N/A }
0N/A }
0N/A return coll;
0N/A }
0N/A
0N/A /*
0N/A * Parses the data in the given input stream as a sequence of DER encoded
0N/A * X.509 CRLs (in binary or base 64 encoded format) OR as a single PKCS#7
0N/A * encoded blob (in binary or base 64 encoded format).
0N/A */
0N/A private Collection<? extends java.security.cert.CRL>
0N/A parseX509orPKCS7CRL(InputStream is)
0N/A throws CRLException, IOException
0N/A {
3384N/A Collection<X509CRLImpl> coll = new ArrayList<>();
2243N/A byte[] data = readOneBlock(is);
2243N/A if (data == null) {
3384N/A return new ArrayList<>(0);
2243N/A }
2243N/A try {
2243N/A PKCS7 pkcs7 = new PKCS7(data);
2243N/A X509CRL[] crls = pkcs7.getCRLs();
2243N/A // CRLs are optional in PKCS #7
2243N/A if (crls != null) {
2243N/A return Arrays.asList(crls);
2243N/A } else {
2243N/A // no crls provided
3384N/A return new ArrayList<>(0);
0N/A }
2243N/A } catch (ParsingException e) {
2243N/A while (data != null) {
2243N/A coll.add(new X509CRLImpl(data));
2243N/A data = readOneBlock(is);
0N/A }
0N/A }
0N/A return coll;
0N/A }
0N/A
2243N/A /**
2243N/A * Returns an ASN.1 SEQUENCE from a stream, which might be a BER-encoded
2243N/A * binary block or a PEM-style BASE64-encoded ASCII data. In the latter
2243N/A * case, it's de-BASE64'ed before return.
2243N/A *
2243N/A * After the reading, the input stream pointer is after the BER block, or
2243N/A * after the newline character after the -----END SOMETHING----- line.
2243N/A *
2243N/A * @param is the InputStream
2243N/A * @returns byte block or null if end of stream
2243N/A * @throws IOException If any parsing error
0N/A */
2243N/A private static byte[] readOneBlock(InputStream is) throws IOException {
0N/A
2243N/A // The first character of a BLOCK.
2243N/A int c = is.read();
2243N/A if (c == -1) {
2243N/A return null;
2243N/A }
2243N/A if (c == DerValue.tag_Sequence) {
2243N/A ByteArrayOutputStream bout = new ByteArrayOutputStream(2048);
2243N/A bout.write(c);
2243N/A readBERInternal(is, bout, c);
2243N/A return bout.toByteArray();
2243N/A } else {
2243N/A // Read BASE64 encoded data, might skip info at the beginning
2243N/A char[] data = new char[2048];
2243N/A int pos = 0;
2243N/A
2243N/A // Step 1: Read until header is found
2243N/A int hyphen = (c=='-') ? 1: 0; // count of consequent hyphens
2243N/A int last = (c=='-') ? -1: c; // the char before hyphen
2243N/A while (true) {
2243N/A int next = is.read();
2243N/A if (next == -1) {
2243N/A // We accept useless data after the last block,
2243N/A // say, empty lines.
2243N/A return null;
2243N/A }
2243N/A if (next == '-') {
2243N/A hyphen++;
2243N/A } else {
2243N/A hyphen = 0;
2243N/A last = next;
2243N/A }
2243N/A if (hyphen == 5 && (last==-1 || last=='\r' || last=='\n')) {
2243N/A break;
2243N/A }
899N/A }
0N/A
2243N/A // Step 2: Read the rest of header, determine the line end
2243N/A int end;
2407N/A StringBuffer header = new StringBuffer("-----");
2243N/A while (true) {
2243N/A int next = is.read();
2243N/A if (next == -1) {
2243N/A throw new IOException("Incomplete data");
2243N/A }
2243N/A if (next == '\n') {
2243N/A end = '\n';
2243N/A break;
2243N/A }
2243N/A if (next == '\r') {
2243N/A next = is.read();
2243N/A if (next == -1) {
2243N/A throw new IOException("Incomplete data");
2243N/A }
2243N/A if (next == '\n') {
2243N/A end = '\n';
2243N/A } else {
2243N/A end = '\r';
2243N/A data[pos++] = (char)next;
2243N/A }
2243N/A break;
2243N/A }
2407N/A header.append((char)next);
2243N/A }
0N/A
2243N/A // Step 3: Read the data
2243N/A while (true) {
2243N/A int next = is.read();
2243N/A if (next == -1) {
2243N/A throw new IOException("Incomplete data");
2243N/A }
2243N/A if (next != '-') {
2243N/A data[pos++] = (char)next;
2243N/A if (pos >= data.length) {
2243N/A data = Arrays.copyOf(data, data.length+1024);
2243N/A }
2243N/A } else {
2243N/A break;
2243N/A }
2243N/A }
0N/A
2243N/A // Step 4: Consume the footer
2407N/A StringBuffer footer = new StringBuffer("-");
2243N/A while (true) {
2243N/A int next = is.read();
2243N/A // Add next == '\n' for maximum safety, in case endline
2243N/A // is not consistent.
2243N/A if (next == -1 || next == end || next == '\n') {
2243N/A break;
2243N/A }
2407N/A if (next != '\r') footer.append((char)next);
0N/A }
2243N/A
2407N/A checkHeaderFooter(header.toString(), footer.toString());
2407N/A
2243N/A BASE64Decoder decoder = new BASE64Decoder();
2243N/A return decoder.decodeBuffer(new String(data, 0, pos));
0N/A }
0N/A }
0N/A
2407N/A private static void checkHeaderFooter(String header,
2407N/A String footer) throws IOException {
2407N/A if (header.length() < 16 || !header.startsWith("-----BEGIN ") ||
2407N/A !header.endsWith("-----")) {
2407N/A throw new IOException("Illegal header: " + header);
2407N/A }
2407N/A if (footer.length() < 14 || !footer.startsWith("-----END ") ||
2407N/A !footer.endsWith("-----")) {
2407N/A throw new IOException("Illegal footer: " + footer);
2407N/A }
2407N/A String headerType = header.substring(11, header.length()-5);
2407N/A String footerType = footer.substring(9, footer.length()-5);
2407N/A if (!headerType.equals(footerType)) {
2407N/A throw new IOException("Header and footer do not match: " +
2407N/A header + " " + footer);
2407N/A }
2407N/A }
2407N/A
2243N/A /**
2243N/A * Read one BER data block. This method is aware of indefinite-length BER
2243N/A * encoding and will read all of the sub-sections in a recursive way
0N/A *
2243N/A * @param is Read from this InputStream
2243N/A * @param bout Write into this OutputStream
2243N/A * @param tag Tag already read (-1 mean not read)
2243N/A * @returns The current tag, used to check EOC in indefinite-length BER
2243N/A * @throws IOException Any parsing error
0N/A */
2243N/A private static int readBERInternal(InputStream is,
2243N/A ByteArrayOutputStream bout, int tag) throws IOException {
2243N/A
2243N/A if (tag == -1) { // Not read before the call, read now
2243N/A tag = is.read();
2243N/A if (tag == -1) {
2243N/A throw new IOException("BER/DER tag info absent");
2243N/A }
2243N/A if ((tag & 0x1f) == 0x1f) {
2243N/A throw new IOException("Multi octets tag not supported");
2243N/A }
2243N/A bout.write(tag);
2243N/A }
2243N/A
2243N/A int n = is.read();
2243N/A if (n == -1) {
2243N/A throw new IOException("BER/DER length info ansent");
2243N/A }
2243N/A bout.write(n);
2243N/A
2243N/A int length;
2243N/A
2243N/A if (n == 0x80) { // Indefinite-length encoding
2243N/A if ((tag & 0x20) != 0x20) {
2243N/A throw new IOException(
2243N/A "Non constructed encoding must have definite length");
2243N/A }
2243N/A while (true) {
2243N/A int subTag = readBERInternal(is, bout, -1);
2243N/A if (subTag == 0) { // EOC, end of indefinite-length section
2243N/A break;
2243N/A }
0N/A }
2243N/A } else {
2243N/A if (n < 0x80) {
2243N/A length = n;
2243N/A } else if (n == 0x81) {
2243N/A length = is.read();
2243N/A if (length == -1) {
2243N/A throw new IOException("Incomplete BER/DER length info");
2243N/A }
2243N/A bout.write(length);
2243N/A } else if (n == 0x82) {
2243N/A int highByte = is.read();
2243N/A int lowByte = is.read();
2243N/A if (lowByte == -1) {
2243N/A throw new IOException("Incomplete BER/DER length info");
2243N/A }
2243N/A bout.write(highByte);
2243N/A bout.write(lowByte);
2243N/A length = (highByte << 8) | lowByte;
2243N/A } else if (n == 0x83) {
2243N/A int highByte = is.read();
2243N/A int midByte = is.read();
2243N/A int lowByte = is.read();
2243N/A if (lowByte == -1) {
2243N/A throw new IOException("Incomplete BER/DER length info");
2243N/A }
2243N/A bout.write(highByte);
2243N/A bout.write(midByte);
2243N/A bout.write(lowByte);
2243N/A length = (highByte << 16) | (midByte << 8) | lowByte;
4489N/A } else if (n == 0x84) {
4489N/A int highByte = is.read();
4489N/A int nextByte = is.read();
4489N/A int midByte = is.read();
4489N/A int lowByte = is.read();
4489N/A if (lowByte == -1) {
4489N/A throw new IOException("Incomplete BER/DER length info");
4489N/A }
4489N/A if (highByte > 127) {
4489N/A throw new IOException("Invalid BER/DER data (a little huge?)");
4489N/A }
4489N/A bout.write(highByte);
4489N/A bout.write(nextByte);
4489N/A bout.write(midByte);
4489N/A bout.write(lowByte);
4489N/A length = (highByte << 24 ) | (nextByte << 16) |
4489N/A (midByte << 8) | lowByte;
2243N/A } else { // ignore longer length forms
2243N/A throw new IOException("Invalid BER/DER data (too huge?)");
2243N/A }
2243N/A if (readFully(is, bout, length) != length) {
2243N/A throw new IOException("Incomplete BER/DER data");
0N/A }
0N/A }
2243N/A return tag;
0N/A }
0N/A}