| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.security.provider;  | 
 | 
 | 
 | 
import java.io.*;  | 
 | 
import java.util.*;  | 
 | 
import java.security.cert.*;  | 
 | 
 | 
 | 
import sun.security.util.Pem;  | 
 | 
import sun.security.x509.X509CertImpl;  | 
 | 
import sun.security.x509.X509CRLImpl;  | 
 | 
import sun.security.pkcs.PKCS7;  | 
 | 
import sun.security.provider.certpath.X509CertPath;  | 
 | 
import sun.security.provider.certpath.X509CertificatePair;  | 
 | 
import sun.security.util.DerValue;  | 
 | 
import sun.security.util.Cache;  | 
 | 
import java.util.Base64;  | 
 | 
import sun.security.pkcs.ParsingException;  | 
 | 
 | 
 | 
/**  | 
 | 
 * This class defines a certificate factory for X.509 v3 certificates &  | 
 | 
 * certification paths, and X.509 v2 certificate revocation lists (CRLs).  | 
 | 
 *  | 
 | 
 * @author Jan Luehe  | 
 | 
 * @author Hemma Prafullchandra  | 
 | 
 * @author Sean Mullan  | 
 | 
 *  | 
 | 
 *  | 
 | 
 * @see java.security.cert.CertificateFactorySpi  | 
 | 
 * @see java.security.cert.Certificate  | 
 | 
 * @see java.security.cert.CertPath  | 
 | 
 * @see java.security.cert.CRL  | 
 | 
 * @see java.security.cert.X509Certificate  | 
 | 
 * @see java.security.cert.X509CRL  | 
 | 
 * @see sun.security.x509.X509CertImpl  | 
 | 
 * @see sun.security.x509.X509CRLImpl  | 
 | 
 */  | 
 | 
 | 
 | 
public class X509Factory extends CertificateFactorySpi { | 
 | 
 | 
 | 
    public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";  | 
 | 
    public static final String END_CERT = "-----END CERTIFICATE-----";  | 
 | 
 | 
 | 
    private static final int ENC_MAX_LENGTH = 4096 * 1024;   | 
 | 
 | 
 | 
    private static final Cache<Object, X509CertImpl> certCache  | 
 | 
        = Cache.newSoftMemoryCache(750);  | 
 | 
    private static final Cache<Object, X509CRLImpl> crlCache  | 
 | 
        = Cache.newSoftMemoryCache(750);  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public Certificate engineGenerateCertificate(InputStream is)  | 
 | 
        throws CertificateException  | 
 | 
    { | 
 | 
        if (is == null) { | 
 | 
              | 
 | 
            certCache.clear();  | 
 | 
            X509CertificatePair.clearCache();  | 
 | 
            throw new CertificateException("Missing input stream"); | 
 | 
        }  | 
 | 
        try { | 
 | 
            byte[] encoding = readOneBlock(is);  | 
 | 
            if (encoding != null) { | 
 | 
                X509CertImpl cert = getFromCache(certCache, encoding);  | 
 | 
                if (cert != null) { | 
 | 
                    return cert;  | 
 | 
                }  | 
 | 
                cert = new X509CertImpl(encoding);  | 
 | 
                addToCache(certCache, cert.getEncodedInternal(), cert);  | 
 | 
                return cert;  | 
 | 
            } else { | 
 | 
                throw new IOException("Empty input"); | 
 | 
            }  | 
 | 
        } catch (IOException ioe) { | 
 | 
            throw new CertificateException("Could not parse certificate: " + | 
 | 
                    ioe.toString(), ioe);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static int readFully(InputStream in, ByteArrayOutputStream bout,  | 
 | 
            int length) throws IOException { | 
 | 
        int read = 0;  | 
 | 
        byte[] buffer = new byte[2048];  | 
 | 
        while (length > 0) { | 
 | 
            int n = in.read(buffer, 0, length<2048?length:2048);  | 
 | 
            if (n <= 0) { | 
 | 
                break;  | 
 | 
            }  | 
 | 
            bout.write(buffer, 0, n);  | 
 | 
            read += n;  | 
 | 
            length -= n;  | 
 | 
        }  | 
 | 
        return read;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static synchronized X509CertImpl intern(X509Certificate c)  | 
 | 
            throws CertificateException { | 
 | 
        if (c == null) { | 
 | 
            return null;  | 
 | 
        }  | 
 | 
        boolean isImpl = c instanceof X509CertImpl;  | 
 | 
        byte[] encoding;  | 
 | 
        if (isImpl) { | 
 | 
            encoding = ((X509CertImpl)c).getEncodedInternal();  | 
 | 
        } else { | 
 | 
            encoding = c.getEncoded();  | 
 | 
        }  | 
 | 
        X509CertImpl newC = getFromCache(certCache, encoding);  | 
 | 
        if (newC != null) { | 
 | 
            return newC;  | 
 | 
        }  | 
 | 
        if (isImpl) { | 
 | 
            newC = (X509CertImpl)c;  | 
 | 
        } else { | 
 | 
            newC = new X509CertImpl(encoding);  | 
 | 
            encoding = newC.getEncodedInternal();  | 
 | 
        }  | 
 | 
        addToCache(certCache, encoding, newC);  | 
 | 
        return newC;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static synchronized X509CRLImpl intern(X509CRL c)  | 
 | 
            throws CRLException { | 
 | 
        if (c == null) { | 
 | 
            return null;  | 
 | 
        }  | 
 | 
        boolean isImpl = c instanceof X509CRLImpl;  | 
 | 
        byte[] encoding;  | 
 | 
        if (isImpl) { | 
 | 
            encoding = ((X509CRLImpl)c).getEncodedInternal();  | 
 | 
        } else { | 
 | 
            encoding = c.getEncoded();  | 
 | 
        }  | 
 | 
        X509CRLImpl newC = getFromCache(crlCache, encoding);  | 
 | 
        if (newC != null) { | 
 | 
            return newC;  | 
 | 
        }  | 
 | 
        if (isImpl) { | 
 | 
            newC = (X509CRLImpl)c;  | 
 | 
        } else { | 
 | 
            newC = new X509CRLImpl(encoding);  | 
 | 
            encoding = newC.getEncodedInternal();  | 
 | 
        }  | 
 | 
        addToCache(crlCache, encoding, newC);  | 
 | 
        return newC;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static synchronized <K,V> V getFromCache(Cache<K,V> cache,  | 
 | 
            byte[] encoding) { | 
 | 
        Object key = new Cache.EqualByteArray(encoding);  | 
 | 
        return cache.get(key);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static synchronized <V> void addToCache(Cache<Object, V> cache,  | 
 | 
            byte[] encoding, V value) { | 
 | 
        if (encoding.length > ENC_MAX_LENGTH) { | 
 | 
            return;  | 
 | 
        }  | 
 | 
        Object key = new Cache.EqualByteArray(encoding);  | 
 | 
        cache.put(key, value);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public CertPath engineGenerateCertPath(InputStream inStream)  | 
 | 
        throws CertificateException  | 
 | 
    { | 
 | 
        if (inStream == null) { | 
 | 
            throw new CertificateException("Missing input stream"); | 
 | 
        }  | 
 | 
        try { | 
 | 
            byte[] encoding = readOneBlock(inStream);  | 
 | 
            if (encoding != null) { | 
 | 
                return new X509CertPath(new ByteArrayInputStream(encoding));  | 
 | 
            } else { | 
 | 
                throw new IOException("Empty input"); | 
 | 
            }  | 
 | 
        } catch (IOException ioe) { | 
 | 
            throw new CertificateException(ioe.getMessage());  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public CertPath engineGenerateCertPath(InputStream inStream,  | 
 | 
        String encoding) throws CertificateException  | 
 | 
    { | 
 | 
        if (inStream == null) { | 
 | 
            throw new CertificateException("Missing input stream"); | 
 | 
        }  | 
 | 
        try { | 
 | 
            byte[] data = readOneBlock(inStream);  | 
 | 
            if (data != null) { | 
 | 
                return new X509CertPath(new ByteArrayInputStream(data), encoding);  | 
 | 
            } else { | 
 | 
                throw new IOException("Empty input"); | 
 | 
            }  | 
 | 
        } catch (IOException ioe) { | 
 | 
            throw new CertificateException(ioe.getMessage());  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public CertPath  | 
 | 
        engineGenerateCertPath(List<? extends Certificate> certificates)  | 
 | 
        throws CertificateException  | 
 | 
    { | 
 | 
        return(new X509CertPath(certificates));  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public Iterator<String> engineGetCertPathEncodings() { | 
 | 
        return(X509CertPath.getEncodingsStatic());  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public Collection<? extends java.security.cert.Certificate>  | 
 | 
            engineGenerateCertificates(InputStream is)  | 
 | 
            throws CertificateException { | 
 | 
        if (is == null) { | 
 | 
            throw new CertificateException("Missing input stream"); | 
 | 
        }  | 
 | 
        try { | 
 | 
            return parseX509orPKCS7Cert(is);  | 
 | 
        } catch (IOException ioe) { | 
 | 
            throw new CertificateException(ioe);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public CRL engineGenerateCRL(InputStream is)  | 
 | 
        throws CRLException  | 
 | 
    { | 
 | 
        if (is == null) { | 
 | 
              | 
 | 
            crlCache.clear();  | 
 | 
            throw new CRLException("Missing input stream"); | 
 | 
        }  | 
 | 
        try { | 
 | 
            byte[] encoding = readOneBlock(is);  | 
 | 
            if (encoding != null) { | 
 | 
                X509CRLImpl crl = getFromCache(crlCache, encoding);  | 
 | 
                if (crl != null) { | 
 | 
                    return crl;  | 
 | 
                }  | 
 | 
                crl = new X509CRLImpl(encoding);  | 
 | 
                addToCache(crlCache, crl.getEncodedInternal(), crl);  | 
 | 
                return crl;  | 
 | 
            } else { | 
 | 
                throw new IOException("Empty input"); | 
 | 
            }  | 
 | 
        } catch (IOException ioe) { | 
 | 
            throw new CRLException(ioe.getMessage());  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public Collection<? extends java.security.cert.CRL> engineGenerateCRLs(  | 
 | 
            InputStream is) throws CRLException  | 
 | 
    { | 
 | 
        if (is == null) { | 
 | 
            throw new CRLException("Missing input stream"); | 
 | 
        }  | 
 | 
        try { | 
 | 
            return parseX509orPKCS7CRL(is);  | 
 | 
        } catch (IOException ioe) { | 
 | 
            throw new CRLException(ioe.getMessage());  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private Collection<? extends java.security.cert.Certificate>  | 
 | 
        parseX509orPKCS7Cert(InputStream is)  | 
 | 
        throws CertificateException, IOException  | 
 | 
    { | 
 | 
        int peekByte;  | 
 | 
        byte[] data;  | 
 | 
        PushbackInputStream pbis = new PushbackInputStream(is);  | 
 | 
        Collection<X509CertImpl> coll = new ArrayList<>();  | 
 | 
 | 
 | 
        // Test the InputStream for end-of-stream.  If the stream's  | 
 | 
        // initial state is already at end-of-stream then return  | 
 | 
        // an empty collection.  Otherwise, push the byte back into the  | 
 | 
          | 
 | 
        peekByte = pbis.read();  | 
 | 
        if (peekByte == -1) { | 
 | 
            return new ArrayList<>(0);  | 
 | 
        } else { | 
 | 
            pbis.unread(peekByte);  | 
 | 
            data = readOneBlock(pbis);  | 
 | 
        }  | 
 | 
 | 
 | 
        // If we end up with a null value after reading the first block  | 
 | 
        // then we know the end-of-stream has been reached and no certificate  | 
 | 
          | 
 | 
        if (data == null) { | 
 | 
            throw new CertificateException("No certificate data found"); | 
 | 
        }  | 
 | 
 | 
 | 
        try { | 
 | 
            PKCS7 pkcs7 = new PKCS7(data);  | 
 | 
            X509Certificate[] certs = pkcs7.getCertificates();  | 
 | 
              | 
 | 
            if (certs != null) { | 
 | 
                return Arrays.asList(certs);  | 
 | 
            } else { | 
 | 
                  | 
 | 
                return new ArrayList<>(0);  | 
 | 
            }  | 
 | 
        } catch (ParsingException e) { | 
 | 
            while (data != null) { | 
 | 
                coll.add(new X509CertImpl(data));  | 
 | 
                data = readOneBlock(pbis);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return coll;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private Collection<? extends java.security.cert.CRL>  | 
 | 
        parseX509orPKCS7CRL(InputStream is)  | 
 | 
        throws CRLException, IOException  | 
 | 
    { | 
 | 
        int peekByte;  | 
 | 
        byte[] data;  | 
 | 
        PushbackInputStream pbis = new PushbackInputStream(is);  | 
 | 
        Collection<X509CRLImpl> coll = new ArrayList<>();  | 
 | 
 | 
 | 
        // Test the InputStream for end-of-stream.  If the stream's  | 
 | 
        // initial state is already at end-of-stream then return  | 
 | 
        // an empty collection.  Otherwise, push the byte back into the  | 
 | 
          | 
 | 
        peekByte = pbis.read();  | 
 | 
        if (peekByte == -1) { | 
 | 
            return new ArrayList<>(0);  | 
 | 
        } else { | 
 | 
            pbis.unread(peekByte);  | 
 | 
            data = readOneBlock(pbis);  | 
 | 
        }  | 
 | 
 | 
 | 
        // If we end up with a null value after reading the first block  | 
 | 
        // then we know the end-of-stream has been reached and no CRL  | 
 | 
          | 
 | 
        if (data == null) { | 
 | 
            throw new CRLException("No CRL data found"); | 
 | 
        }  | 
 | 
 | 
 | 
        try { | 
 | 
            PKCS7 pkcs7 = new PKCS7(data);  | 
 | 
            X509CRL[] crls = pkcs7.getCRLs();  | 
 | 
              | 
 | 
            if (crls != null) { | 
 | 
                return Arrays.asList(crls);  | 
 | 
            } else { | 
 | 
                  | 
 | 
                return new ArrayList<>(0);  | 
 | 
            }  | 
 | 
        } catch (ParsingException e) { | 
 | 
            while (data != null) { | 
 | 
                coll.add(new X509CRLImpl(data));  | 
 | 
                data = readOneBlock(pbis);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return coll;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static byte[] readOneBlock(InputStream is) throws IOException { | 
 | 
 | 
 | 
          | 
 | 
        int c = is.read();  | 
 | 
        if (c == -1) { | 
 | 
            return null;  | 
 | 
        }  | 
 | 
        if (c == DerValue.tag_Sequence) { | 
 | 
            ByteArrayOutputStream bout = new ByteArrayOutputStream(2048);  | 
 | 
            bout.write(c);  | 
 | 
            readBERInternal(is, bout, c);  | 
 | 
            return bout.toByteArray();  | 
 | 
        } else { | 
 | 
              | 
 | 
            char[] data = new char[2048];  | 
 | 
            int pos = 0;  | 
 | 
 | 
 | 
            // Step 1: Read until header is found  | 
 | 
            int hyphen = (c=='-') ? 1: 0;     | 
 | 
            int last = (c=='-') ? -1: c;      | 
 | 
            while (true) { | 
 | 
                int next = is.read();  | 
 | 
                if (next == -1) { | 
 | 
                    // We accept useless data after the last block,  | 
 | 
                      | 
 | 
                    return null;  | 
 | 
                }  | 
 | 
                if (next == '-') { | 
 | 
                    hyphen++;  | 
 | 
                } else { | 
 | 
                    hyphen = 0;  | 
 | 
                    last = next;  | 
 | 
                }  | 
 | 
                if (hyphen == 5 && (last == -1 || last == '\r' || last == '\n')) { | 
 | 
                    break;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            int end;  | 
 | 
            StringBuilder header = new StringBuilder("-----"); | 
 | 
            while (true) { | 
 | 
                int next = is.read();  | 
 | 
                if (next == -1) { | 
 | 
                    throw new IOException("Incomplete data"); | 
 | 
                }  | 
 | 
                if (next == '\n') { | 
 | 
                    end = '\n';  | 
 | 
                    break;  | 
 | 
                }  | 
 | 
                if (next == '\r') { | 
 | 
                    next = is.read();  | 
 | 
                    if (next == -1) { | 
 | 
                        throw new IOException("Incomplete data"); | 
 | 
                    }  | 
 | 
                    if (next == '\n') { | 
 | 
                        end = '\n';  | 
 | 
                    } else { | 
 | 
                        end = '\r';  | 
 | 
                        data[pos++] = (char)next;  | 
 | 
                    }  | 
 | 
                    break;  | 
 | 
                }  | 
 | 
                header.append((char)next);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            while (true) { | 
 | 
                int next = is.read();  | 
 | 
                if (next == -1) { | 
 | 
                    throw new IOException("Incomplete data"); | 
 | 
                }  | 
 | 
                if (next != '-') { | 
 | 
                    data[pos++] = (char)next;  | 
 | 
                    if (pos >= data.length) { | 
 | 
                        data = Arrays.copyOf(data, data.length+1024);  | 
 | 
                    }  | 
 | 
                } else { | 
 | 
                    break;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            StringBuilder footer = new StringBuilder("-"); | 
 | 
            while (true) { | 
 | 
                int next = is.read();  | 
 | 
                // Add next == '\n' for maximum safety, in case endline  | 
 | 
                  | 
 | 
                if (next == -1 || next == end || next == '\n') { | 
 | 
                    break;  | 
 | 
                }  | 
 | 
                if (next != '\r') footer.append((char)next);  | 
 | 
            }  | 
 | 
 | 
 | 
            checkHeaderFooter(header.toString(), footer.toString());  | 
 | 
 | 
 | 
            return Pem.decode(new String(data, 0, pos));  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private static void checkHeaderFooter(String header,  | 
 | 
            String footer) throws IOException { | 
 | 
        if (header.length() < 16 || !header.startsWith("-----BEGIN ") || | 
 | 
                !header.endsWith("-----")) { | 
 | 
            throw new IOException("Illegal header: " + header); | 
 | 
        }  | 
 | 
        if (footer.length() < 14 || !footer.startsWith("-----END ") || | 
 | 
                !footer.endsWith("-----")) { | 
 | 
            throw new IOException("Illegal footer: " + footer); | 
 | 
        }  | 
 | 
        String headerType = header.substring(11, header.length()-5);  | 
 | 
        String footerType = footer.substring(9, footer.length()-5);  | 
 | 
        if (!headerType.equals(footerType)) { | 
 | 
            throw new IOException("Header and footer do not match: " + | 
 | 
                    header + " " + footer);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static int readBERInternal(InputStream is,  | 
 | 
            ByteArrayOutputStream bout, int tag) throws IOException { | 
 | 
 | 
 | 
        if (tag == -1) {         | 
 | 
            tag = is.read();  | 
 | 
            if (tag == -1) { | 
 | 
                throw new IOException("BER/DER tag info absent"); | 
 | 
            }  | 
 | 
            if ((tag & 0x1f) == 0x1f) { | 
 | 
                throw new IOException("Multi octets tag not supported"); | 
 | 
            }  | 
 | 
            bout.write(tag);  | 
 | 
        }  | 
 | 
 | 
 | 
        int n = is.read();  | 
 | 
        if (n == -1) { | 
 | 
            throw new IOException("BER/DER length info absent"); | 
 | 
        }  | 
 | 
        bout.write(n);  | 
 | 
 | 
 | 
        int length;  | 
 | 
 | 
 | 
        if (n == 0x80) {         | 
 | 
            if ((tag & 0x20) != 0x20) { | 
 | 
                throw new IOException(  | 
 | 
                        "Non constructed encoding must have definite length");  | 
 | 
            }  | 
 | 
            while (true) { | 
 | 
                int subTag = readBERInternal(is, bout, -1);  | 
 | 
                if (subTag == 0) {    | 
 | 
                    break;  | 
 | 
                }  | 
 | 
            }  | 
 | 
        } else { | 
 | 
            if (n < 0x80) { | 
 | 
                length = n;  | 
 | 
            } else if (n == 0x81) { | 
 | 
                length = is.read();  | 
 | 
                if (length == -1) { | 
 | 
                    throw new IOException("Incomplete BER/DER length info"); | 
 | 
                }  | 
 | 
                bout.write(length);  | 
 | 
            } else if (n == 0x82) { | 
 | 
                int highByte = is.read();  | 
 | 
                int lowByte = is.read();  | 
 | 
                if (lowByte == -1) { | 
 | 
                    throw new IOException("Incomplete BER/DER length info"); | 
 | 
                }  | 
 | 
                bout.write(highByte);  | 
 | 
                bout.write(lowByte);  | 
 | 
                length = (highByte << 8) | lowByte;  | 
 | 
            } else if (n == 0x83) { | 
 | 
                int highByte = is.read();  | 
 | 
                int midByte = is.read();  | 
 | 
                int lowByte = is.read();  | 
 | 
                if (lowByte == -1) { | 
 | 
                    throw new IOException("Incomplete BER/DER length info"); | 
 | 
                }  | 
 | 
                bout.write(highByte);  | 
 | 
                bout.write(midByte);  | 
 | 
                bout.write(lowByte);  | 
 | 
                length = (highByte << 16) | (midByte << 8) | lowByte;  | 
 | 
            } else if (n == 0x84) { | 
 | 
                int highByte = is.read();  | 
 | 
                int nextByte = is.read();  | 
 | 
                int midByte = is.read();  | 
 | 
                int lowByte = is.read();  | 
 | 
                if (lowByte == -1) { | 
 | 
                    throw new IOException("Incomplete BER/DER length info"); | 
 | 
                }  | 
 | 
                if (highByte > 127) { | 
 | 
                    throw new IOException("Invalid BER/DER data (a little huge?)"); | 
 | 
                }  | 
 | 
                bout.write(highByte);  | 
 | 
                bout.write(nextByte);  | 
 | 
                bout.write(midByte);  | 
 | 
                bout.write(lowByte);  | 
 | 
                length = (highByte << 24 ) | (nextByte << 16) |  | 
 | 
                        (midByte << 8) | lowByte;  | 
 | 
            } else {  | 
 | 
                throw new IOException("Invalid BER/DER data (too huge?)"); | 
 | 
            }  | 
 | 
            if (readFully(is, bout, length) != length) { | 
 | 
                throw new IOException("Incomplete BER/DER data"); | 
 | 
            }  | 
 | 
        }  | 
 | 
        return tag;  | 
 | 
    }  | 
 | 
}  |