FileDocCategorySizeDatePackage
X509CertFactoryImpl.javaAPI DocAndroid 1.5 API37972Wed May 06 22:41:06 BST 2009org.apache.harmony.security.provider.cert

X509CertFactoryImpl

public class X509CertFactoryImpl extends CertificateFactorySpi
X509 Certificate Factory Service Provider Interface Implementation. It supports CRLs and Certificates in (PEM) ASN.1 DER encoded form, and Certification Paths in PkiPath and PKCS7 formats. For Certificates and CRLs factory maintains the caching mechanisms allowing to speed up repeated Certificate/CRL generation.
see
Cache

Fields Summary
private static int
CERT_CACHE_SEED_LENGTH
private static Cache
CERT_CACHE
private static int
CRL_CACHE_SEED_LENGTH
private static Cache
CRL_CACHE
private static byte[]
pemBegin
private static byte[]
pemClose
private static byte[]
FREE_BOUND_SUFFIX
Code describing free format for PEM boundary suffix: "^-----BEGIN.*\n" at the beginning, and
"\n-----END.*(EOF|\n)$" at the end.
private static byte[]
CERT_BOUND_SUFFIX
Code describing PEM boundary suffix for X.509 certificate: "^-----BEGIN CERTIFICATE-----\n" at the beginning, and
"\n-----END CERTIFICATE-----" at the end.
Constructors Summary
public X509CertFactoryImpl()
Default constructor. Creates the instance of Certificate Factory SPI ready for use.


                     
       
Methods Summary
private byte[]decodePEM(java.io.InputStream inStream, byte[] boundary_suffix)
Method retrieves the PEM encoded data from the stream and returns its decoded representation. Method checks correctness of PEM boundaries. It supposes that the first '-' of the opening boundary has already been read from the stream. So first of all it checks that the leading bytes are equal to "-----BEGIN" boundary prefix. Than if boundary_suffix is not null, it checks that next bytes equal to boundary_suffix + new line char[s] ([CR]LF). If boundary_suffix parameter is null, method supposes free suffix format and skips any bytes until the new line.
After the opening boundary has been read and checked, the method read Base64 encoded data until closing PEM boundary is not reached.
Than it checks closing boundary - it should start with new line + "-----END" + boundary_suffix. If boundary_suffix is null, any characters are skipped until the new line.
After this any trailing new line characters are skipped from the stream, Base64 encoding is decoded and returned.

param
inStream the stream containing the PEM encoding.
param
boundary_suffix the suffix of expected PEM multipart boundary delimiter.
If it is null, that any character sequences are accepted.
throws
IOException If PEM boundary delimiter does not comply with expected or some I/O or decoding problems occur.

 //$NON-NLS-1$

                                                                                                                                                                                                                         
          
                                                          
        int ch; // the char to be read
        // check and skip opening boundary delimiter 
        // (first '-' is supposed as already read)
        for (int i=1; i<pemBegin.length; i++) {
            if (pemBegin[i] != (ch = inStream.read())) {
                throw new IOException(
                    "Incorrect PEM encoding: '-----BEGIN"
                    + ((boundary_suffix == null) 
                        ? "" : new String(boundary_suffix))
                    + "' is expected as opening delimiter boundary.");
            }
        }
        if (boundary_suffix == null) {
            // read (skip) the trailing characters of 
            // the beginning PEM boundary delimiter
            while ((ch = inStream.read()) != '\n") {
                if (ch == -1) {
                    throw new IOException(
                        Messages.getString("security.156")); //$NON-NLS-1$
                }
            }
        } else {
            for (int i=0; i<boundary_suffix.length; i++) {
                if (boundary_suffix[i] != inStream.read()) {
                    throw new IOException(
                        Messages.getString("security.15B", //$NON-NLS-1$
                            ((boundary_suffix == null) 
                                ? "" 
                                : new String(boundary_suffix)))); //$NON-NLS-1$
                }
            }
            // read new line characters
            if ((ch = inStream.read()) == '\r") {
                // CR has been read, now read LF character
                ch = inStream.read();
            }
            if (ch != '\n") {
                throw new IOException(
                    Messages.getString("security.15B2")); //$NON-NLS-1$
            }
        }
        int size = 1024; // the size of the buffer containing Base64 data
        byte[] buff = new byte[size];
        int index = 0;
        // read bytes while ending boundary delimiter is not reached
        while ((ch = inStream.read()) != '-") {
            if (ch == -1) {
                throw new IOException(
                        Messages.getString("security.157")); //$NON-NLS-1$
            }
            buff[index++] = (byte) ch;
            if (index == size) {
                // enlarge the buffer
                byte[] newbuff = new byte[size+1024];
                System.arraycopy(buff, 0, newbuff, 0, size);
                buff = newbuff;
                size += 1024;
            }
        }
        if (buff[index-1] != '\n") {
            throw new IOException(
                Messages.getString("security.158")); //$NON-NLS-1$
        }
        // check and skip closing boundary delimiter prefix
        // (first '-' was read)
        for (int i=1; i<pemClose.length; i++) {
            if (pemClose[i] != inStream.read()) {
                throw new IOException(
                    Messages.getString("security.15B1", //$NON-NLS-1$
                        ((boundary_suffix == null) 
                            ? "" 
                            : new String(boundary_suffix)))); //$NON-NLS-1$
            }
        }
        if (boundary_suffix == null) {
            // read (skip) the trailing characters of 
            // the closing PEM boundary delimiter
            while (((ch = inStream.read()) != -1)
                    && (ch != '\n") && (ch != '\r")) {
            }
        } else {
            for (int i=0; i<boundary_suffix.length; i++) {
                if (boundary_suffix[i] != inStream.read()) {
                    throw new IOException(
                        Messages.getString("security.15B1", //$NON-NLS-1$
                            ((boundary_suffix == null) 
                                ? "" 
                                : new String(boundary_suffix)))); //$NON-NLS-1$
                }
            }
        }
        // skip trailing line breaks
        inStream.mark(1);
        while (((ch = inStream.read()) != -1) && (ch == '\n" || ch == '\r")) {
            inStream.mark(1);
        }
        inStream.reset();
        buff = Base64.decode(buff, index);
        if (buff == null) {
            throw new IOException(Messages.getString("security.159")); //$NON-NLS-1$
        }
        return buff;
    
public java.security.cert.CRLengineGenerateCRL(java.io.InputStream inStream)

see
java.security.cert.CertificateFactorySpi#engineGenerateCRL(InputStream) method documentation for more info

        if (inStream == null) {
            throw new CRLException(Messages.getString("security.153")); //$NON-NLS-1$
        }
        try {
            if (!inStream.markSupported()) {
                // Create the mark supporting wrapper
                // Mark is needed to recognize the format 
                // of provided encoding form (ASN.1 or PEM)
                inStream = new RestoringInputStream(inStream);
            }
            inStream.mark(1);
            // check whether the provided crl is in PEM encoded form
            if (inStream.read() == '-") {
                // decode PEM, retrieve CRL
                return getCRL(decodePEM(inStream, FREE_BOUND_SUFFIX));
            } else {
                inStream.reset();
                // retrieve CRL
                return getCRL(inStream);
            }
        } catch (IOException e) {
            throw new CRLException(e);
        }
    
public java.util.CollectionengineGenerateCRLs(java.io.InputStream inStream)

see
java.security.cert.CertificateFactorySpi#engineGenerateCRLs(InputStream) method documentation for more info

        if (inStream == null) {
            throw new CRLException(Messages.getString("security.153")); //$NON-NLS-1$
        }
        ArrayList result = new ArrayList();
        try {
            if (!inStream.markSupported()) {
                inStream = new RestoringInputStream(inStream);
            }
            // if it is PEM encoded form this array will contain the encoding
            // so ((it is PEM) <-> (encoding != null))
            byte[] encoding = null;
            // The following by SEQUENCE ASN.1 tag, used for
            // recognizing the data format 
            // (is it PKCS7 ContentInfo structure, X.509 CRL, or
            // unsupported encoding)
            int second_asn1_tag = -1;
            inStream.mark(1);
            int ch;
            while ((ch = inStream.read()) != -1) {
                // check if it is PEM encoded form
                if (ch == '-") { // beginning of PEM encoding ('-' char)
                    // decode PEM chunk and store its content (ASN.1 encoding)
                    encoding = decodePEM(inStream, FREE_BOUND_SUFFIX);
                } else if (ch == 0x30) { // beginning of ASN.1 sequence (0x30)
                    encoding = null;
                    inStream.reset();
                    // prepare for data format determination
                    inStream.mark(CRL_CACHE_SEED_LENGTH);
                } else { // unsupported data
                    if (result.size() == 0) {
                        throw new CRLException(
                                Messages.getString("security.15F")); //$NON-NLS-1$
                    } else {
                        // it can be trailing user data,
                        // so keep it in the stream
                        inStream.reset();
                        return result;
                    }
                }
                // Check the data format
                BerInputStream in = (encoding == null)
                                        ? new BerInputStream(inStream)
                                        : new BerInputStream(encoding);
                // read the next ASN.1 tag
                second_asn1_tag = in.next();
                if (encoding == null) {
                    // keep whole structure in the stream
                    inStream.reset();
                }
                // check if it is a TBSCertList structure
                if (second_asn1_tag != ASN1Constants.TAG_C_SEQUENCE) {
                    if (result.size() == 0) {
                        // there were not read X.509 CRLs, so 
                        // break the cycle and check 
                        // whether it is PKCS7 structure
                        break;
                    } else {
                        // it can be trailing user data,
                        // so return what we already read
                        return result;
                    }
                } else {
                    if (encoding == null) {
                        result.add(getCRL(inStream));
                    } else {
                        result.add(getCRL(encoding));
                    }
                }
                inStream.mark(1);
            }
            if (result.size() != 0) {
                // the stream was read out
                return result;
            } else if (ch == -1) {
                throw new CRLException(
                        Messages.getString("security.155")); //$NON-NLS-1$
            }
            // else: check if it is PKCS7
            if (second_asn1_tag == ASN1Constants.TAG_OID) {
                // it is PKCS7 ContentInfo structure, so decode it
                ContentInfo info = (ContentInfo) 
                    ((encoding != null)
                        ? ContentInfo.ASN1.decode(encoding)
                        : ContentInfo.ASN1.decode(inStream));
                // retrieve SignedData
                SignedData data = info.getSignedData();
                if (data == null) {
                    throw new CRLException(
                            Messages.getString("security.154")); //$NON-NLS-1$
                }
                List crls = data.getCRLs();
                if (crls != null) {
                    for (int i = 0; i < crls.size(); i++) {
                        result.add(new X509CRLImpl(
                            (CertificateList) crls.get(i)));
                    }
                }
                return result;
            }
            // else: Unknown data format
            throw new CRLException(
                        Messages.getString("security.15F")); //$NON-NLS-1$
        } catch (IOException e) {
            throw new CRLException(e);
        }
    
public java.security.cert.CertPathengineGenerateCertPath(java.io.InputStream inStream)

see
java.security.cert.CertificateFactorySpi#engineGenerateCertPath(InputStream) method documentation for more info

        if (inStream == null) {
            throw new CertificateException(
                    Messages.getString("security.153")); //$NON-NLS-1$
        }
        return engineGenerateCertPath(inStream, "PkiPath"); //$NON-NLS-1$
    
public java.security.cert.CertPathengineGenerateCertPath(java.io.InputStream inStream, java.lang.String encoding)

see
java.security.cert.CertificateFactorySpi#engineGenerateCertPath(InputStream,String) method documentation for more info

        if (inStream == null) {
            throw new CertificateException(
                    Messages.getString("security.153")); //$NON-NLS-1$
        }
        if (!inStream.markSupported()) {
            inStream = new RestoringInputStream(inStream);
        }
        try {
            inStream.mark(1);
            int ch;

            // check if it is PEM encoded form
            if ((ch = inStream.read()) == '-") {
                // decode PEM chunk into ASN.1 form and decode CertPath object
                return X509CertPathImpl.getInstance(
                        decodePEM(inStream, FREE_BOUND_SUFFIX), encoding);
            } else if (ch == 0x30) { // ASN.1 Sequence
                inStream.reset();
                // decode ASN.1 form
                return X509CertPathImpl.getInstance(inStream, encoding);
            } else {
                throw new CertificateException(
                            Messages.getString("security.15F")); //$NON-NLS-1$
            }
        } catch (IOException e) {
            throw new CertificateException(e);
        }
    
public java.security.cert.CertPathengineGenerateCertPath(java.util.List certificates)

see
java.security.cert.CertificateFactorySpi#engineGenerateCertPath(List) method documentation for more info

        return new X509CertPathImpl(certificates);
    
public java.security.cert.CertificateengineGenerateCertificate(java.io.InputStream inStream)
Generates the X.509 certificate from the data in the stream. The data in the stream can be either in ASN.1 DER encoded X.509 certificate, or PEM (Base64 encoding bounded by "-----BEGIN CERTIFICATE-----" at the beginning and "-----END CERTIFICATE-----" at the end) representation of the former encoded form. Before the generation the encoded form is looked up in the cache. If the cache contains the certificate with requested encoded form it is returned from it, otherwise it is generated by ASN.1 decoder.

see
java.security.cert.CertificateFactorySpi#engineGenerateCertificate(InputStream) method documentation for more info

        if (inStream == null) {
            throw new CertificateException(Messages.getString("security.153")); //$NON-NLS-1$
        }
        try {
            if (!inStream.markSupported()) {
                // create the mark supporting wrapper
                inStream = new RestoringInputStream(inStream);
            }
            // mark is needed to recognize the format of the provided encoding
            // (ASN.1 or PEM)
            inStream.mark(1);
            // check whether the provided certificate is in PEM encoded form
            if (inStream.read() == '-") {
                // decode PEM, retrieve CRL
                return getCertificate(decodePEM(inStream, CERT_BOUND_SUFFIX));
            } else {
                inStream.reset();
                // retrieve CRL
                return getCertificate(inStream);
            }
        } catch (IOException e) {
            throw new CertificateException(e);
        }
    
public java.util.CollectionengineGenerateCertificates(java.io.InputStream inStream)
Generates the collection of the certificates on the base of provided via input stream encodings.

see
java.security.cert.CertificateFactorySpi#engineGenerateCertificates(InputStream) method documentation for more info

        if (inStream == null) {
            throw new CertificateException(Messages.getString("security.153")); //$NON-NLS-1$
        }
        ArrayList result = new ArrayList();
        try {
            if (!inStream.markSupported()) {
                // create the mark supporting wrapper
                inStream = new RestoringInputStream(inStream);
            }
            // if it is PEM encoded form this array will contain the encoding
            // so ((it is PEM) <-> (encoding != null))
            byte[] encoding = null;
            // The following by SEQUENCE ASN.1 tag, used for
            // recognizing the data format 
            // (is it PKCS7 ContentInfo structure, X.509 Certificate, or
            // unsupported encoding)
            int second_asn1_tag = -1;
            inStream.mark(1);
            int ch;
            while ((ch = inStream.read()) != -1) {
                // check if it is PEM encoded form
                if (ch == '-") { // beginning of PEM encoding ('-' char)
                    // decode PEM chunk and store its content (ASN.1 encoding)
                    encoding = decodePEM(inStream, FREE_BOUND_SUFFIX);
                } else if (ch == 0x30) { // beginning of ASN.1 sequence (0x30)
                    encoding = null;
                    inStream.reset();
                    // prepare for data format determination
                    inStream.mark(CERT_CACHE_SEED_LENGTH);
                } else { // unsupported data
                    if (result.size() == 0) {
                        throw new CertificateException(
                                Messages.getString("security.15F")); //$NON-NLS-1$
                    } else {
                        // it can be trailing user data,
                        // so keep it in the stream
                        inStream.reset();
                        return result;
                    }
                }
                // Check the data format
                BerInputStream in = (encoding == null)
                                        ? new BerInputStream(inStream)
                                        : new BerInputStream(encoding);
                // read the next ASN.1 tag
                second_asn1_tag = in.next(); // inStream position changed
                if (encoding == null) {
                    // keep whole structure in the stream
                    inStream.reset();
                }
                // check if it is a TBSCertificate structure
                if (second_asn1_tag != ASN1Constants.TAG_C_SEQUENCE) {
                    if (result.size() == 0) {
                        // there were not read X.509 Certificates, so 
                        // break the cycle and check 
                        // whether it is PKCS7 structure
                        break;
                    } else {
                        // it can be trailing user data,
                        // so return what we already read
                        return result;
                    }
                } else {
                    if (encoding == null) {
                        result.add(getCertificate(inStream));
                    } else {
                        result.add(getCertificate(encoding));
                    }
                }
                // mark for the next iteration
                inStream.mark(1);
            }
            if (result.size() != 0) {
                // some Certificates have been read
                return result;
            } else if (ch == -1) {
                throw new CertificateException(
                        Messages.getString("security.155")); //$NON-NLS-1$
            }
            // else: check if it is PKCS7
            if (second_asn1_tag == ASN1Constants.TAG_OID) {
                // it is PKCS7 ContentInfo structure, so decode it
                ContentInfo info = (ContentInfo) 
                    ((encoding != null)
                        ? ContentInfo.ASN1.decode(encoding)
                        : ContentInfo.ASN1.decode(inStream));
                // retrieve SignedData
                SignedData data = info.getSignedData();
                if (data == null) {
                    throw new CertificateException(
                            Messages.getString("security.154")); //$NON-NLS-1$
                }
                List certs = data.getCertificates();
                if (certs != null) {
                    for (int i = 0; i < certs.size(); i++) {
                        result.add(new X509CertImpl(
                            (org.apache.harmony.security.x509.Certificate)
                                certs.get(i)));
                    }
                }
                return result;
            }
            // else: Unknown data format
            throw new CertificateException(
                            Messages.getString("security.15F")); //$NON-NLS-1$
        } catch (IOException e) {
            throw new CertificateException(e);
        }
    
public java.util.IteratorengineGetCertPathEncodings()

see
java.security.cert.CertificateFactorySpi#engineGetCertPathEncodings() method documentation for more info

        return X509CertPathImpl.encodings.iterator();
    
private static java.security.cert.CRLgetCRL(byte[] encoding)
Returns the CRL object corresponding to the provided encoding. Resulting object is retrieved from the cache if it contains such correspondence and is constructed on the base of encoding and stored in the cache otherwise.

throws
IOException if some decoding errors occur (in the case of cache miss).

        if (encoding.length < CRL_CACHE_SEED_LENGTH) {
            throw new CRLException(
                    Messages.getString("security.152")); //$NON-NLS-1$
        }
        synchronized (CRL_CACHE) {
            long hash = CRL_CACHE.getHash(encoding);
            if (CRL_CACHE.contains(hash)) {
                X509CRL res = (X509CRL) CRL_CACHE.get(hash, encoding);
                if (res != null) {
                    return res;
                }
            }
            X509CRL res = new X509CRLImpl(encoding);
            CRL_CACHE.put(hash, encoding, res);
            return res;
        }
    
private static java.security.cert.CRLgetCRL(java.io.InputStream inStream)
Returns the CRL object corresponding to the encoding provided by the stream. Resulting object is retrieved from the cache if it contains such correspondence and is constructed on the base of encoding and stored in the cache otherwise.

throws
IOException if some decoding errors occur (in the case of cache miss).

        synchronized (CRL_CACHE) {
            inStream.mark(CRL_CACHE_SEED_LENGTH);
            byte[] buff = readBytes(inStream, CRL_CACHE_SEED_LENGTH);
            // read the prefix of the encoding
            inStream.reset();
            if (buff == null) {
                throw new CRLException(
                        Messages.getString("security.152")); //$NON-NLS-1$
            }
            long hash = CRL_CACHE.getHash(buff);
            if (CRL_CACHE.contains(hash)) {
                byte[] encoding = new byte[BerInputStream.getLength(buff)];
                if (encoding.length < CRL_CACHE_SEED_LENGTH) {
                    throw new CRLException(
                        Messages.getString("security.15B4")); //$NON-NLS-1$
                }
                inStream.read(encoding);
                CRL res = (CRL) CRL_CACHE.get(hash, encoding);
                if (res != null) {
                    return res;
                }
                res = new X509CRLImpl(encoding);
                CRL_CACHE.put(hash, encoding, res);
                return res;
            } else {
                X509CRL res = new X509CRLImpl(inStream);
                CRL_CACHE.put(hash, res.getEncoded(), res);
                return res;
            }
        }
    
private static java.security.cert.CertificategetCertificate(byte[] encoding)
Returns the Certificate object corresponding to the provided encoding. Resulting object is retrieved from the cache if it contains such correspondence and is constructed on the base of encoding and stored in the cache otherwise.

throws
IOException if some decoding errors occur (in the case of cache miss).

        if (encoding.length < CERT_CACHE_SEED_LENGTH) {
            throw new CertificateException(
                    Messages.getString("security.152")); //$NON-NLS-1$
        }
        synchronized (CERT_CACHE) {
            long hash = CERT_CACHE.getHash(encoding);
            if (CERT_CACHE.contains(hash)) {
                Certificate res = 
                    (Certificate) CERT_CACHE.get(hash, encoding);
                if (res != null) {
                    return res;
                }
            }
            Certificate res = new X509CertImpl(encoding);
            CERT_CACHE.put(hash, encoding, res);
            return res;
        }
    
private static java.security.cert.CertificategetCertificate(java.io.InputStream inStream)
Returns the Certificate object corresponding to the encoding provided by the stream. Resulting object is retrieved from the cache if it contains such correspondence and is constructed on the base of encoding and stored in the cache otherwise.

throws
IOException if some decoding errors occur (in the case of cache miss).

        synchronized (CERT_CACHE) {
            inStream.mark(CERT_CACHE_SEED_LENGTH);
            // read the prefix of the encoding
            byte[] buff = readBytes(inStream, CERT_CACHE_SEED_LENGTH);
            inStream.reset();
            if (buff == null) {
                throw new CertificateException(
                        Messages.getString("security.152")); //$NON-NLS-1$
            }
            long hash = CERT_CACHE.getHash(buff);
            if (CERT_CACHE.contains(hash)) {
                byte[] encoding = new byte[BerInputStream.getLength(buff)];
                if (encoding.length < CERT_CACHE_SEED_LENGTH) {
                    throw new CertificateException(
                        Messages.getString("security.15B3")); //$NON-NLS-1$
                }
                inStream.read(encoding);
                Certificate res = (Certificate) CERT_CACHE.get(hash, encoding);
                if (res != null) {
                    return res;
                }
                res = new X509CertImpl(encoding);
                CERT_CACHE.put(hash, encoding, res);
                return res;
            } else {
                inStream.reset();
                Certificate res = new X509CertImpl(inStream);
                CERT_CACHE.put(hash, res.getEncoded(), res);
                return res;
            }
        }
    
private static byte[]readBytes(java.io.InputStream source, int length)
Reads the data of specified length from source and returns it as an array.

return
the byte array contained read data or null if the stream contains not enough data
throws
IOException if some I/O error has been occurred.

        byte[] result = new byte[length];
        for (int i=0; i<length; i++) {
            int bytik = source.read();
            if (bytik == -1) {
                return null;
            }
            result[i] = (byte) bytik;
        }
        return result;