All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.conscrypt.OpenSSLX509CertificateFactory Maven / Gradle / Ivy

There is a newer version: 2.0.42.rc
Show newest version
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.conscrypt;

import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.security.cert.CRL;
import java.security.cert.CRLException;
import java.security.cert.CertPath;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactorySpi;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * An implementation of {@link java.security.cert.CertificateFactory} based on BoringSSL.
 *
 * @hide
 */
@Internal
public class OpenSSLX509CertificateFactory extends CertificateFactorySpi {
    private static final byte[] PKCS7_MARKER = new byte[] {
            '-', '-', '-', '-', '-', 'B', 'E', 'G', 'I', 'N', ' ', 'P', 'K', 'C', 'S', '7'
    };

    private static final int PUSHBACK_SIZE = 64;

    static class ParsingException extends Exception {
        private static final long serialVersionUID = 8390802697728301325L;

        ParsingException(String message) {
            super(message);
        }

        ParsingException(Exception cause) {
            super(cause);
        }

        ParsingException(String message, Exception cause) {
            super(message, cause);
        }
    }

    /**
     * The code for X509 Certificates and CRL is pretty much the same. We use
     * this abstract class to share the code between them. This makes it ugly,
     * but it's already written in this language anyway.
     */
    private static abstract class Parser {
        T generateItem(InputStream inStream) throws ParsingException {
            if (inStream == null) {
                throw new ParsingException("inStream == null");
            }

            final boolean markable = inStream.markSupported();
            if (markable) {
                inStream.mark(PKCS7_MARKER.length);
            }

            final PushbackInputStream pbis = new PushbackInputStream(inStream, PUSHBACK_SIZE);
            try {
                final byte[] buffer = new byte[PKCS7_MARKER.length];

                final int len = pbis.read(buffer);
                if (len < 0) {
                    /* No need to reset here. The stream was empty or EOF. */
                    throw new ParsingException("inStream is empty");
                }
                pbis.unread(buffer, 0, len);

                if (buffer[0] == '-') {
                    if (len == PKCS7_MARKER.length && Arrays.equals(PKCS7_MARKER, buffer)) {
                        List items = fromPkcs7PemInputStream(pbis);
                        if (items.size() == 0) {
                            return null;
                        }
                        items.get(0);
                    } else {
                        return fromX509PemInputStream(pbis);
                    }
                }

                /* PKCS#7 bags have a byte 0x06 at position 4 in the stream. */
                if (buffer[4] == 0x06) {
                    List certs = fromPkcs7DerInputStream(pbis);
                    if (certs.size() == 0) {
                        return null;
                    }
                    return certs.get(0);
                } else {
                    return fromX509DerInputStream(pbis);
                }
            } catch (Exception e) {
                if (markable) {
                    try {
                        inStream.reset();
                    } catch (IOException ignored) {
                    }
                }
                throw new ParsingException(e);
            }
        }

        Collection generateItems(InputStream inStream)
                throws ParsingException {
            if (inStream == null) {
                throw new ParsingException("inStream == null");
            }
            try {
                if (inStream.available() == 0) {
                    return Collections.emptyList();
                }
            } catch (IOException e) {
                throw new ParsingException("Problem reading input stream", e);
            }

            final boolean markable = inStream.markSupported();
            if (markable) {
                inStream.mark(PUSHBACK_SIZE);
            }

            /* Attempt to see if this is a PKCS#7 bag. */
            final PushbackInputStream pbis = new PushbackInputStream(inStream, PUSHBACK_SIZE);
            try {
                final byte[] buffer = new byte[PKCS7_MARKER.length];

                final int len = pbis.read(buffer);
                if (len < 0) {
                    /* No need to reset here. The stream was empty or EOF. */
                    throw new ParsingException("inStream is empty");
                }
                pbis.unread(buffer, 0, len);

                if (len == PKCS7_MARKER.length && Arrays.equals(PKCS7_MARKER, buffer)) {
                    return fromPkcs7PemInputStream(pbis);
                }

                /* PKCS#7 bags have a byte 0x06 at position 4 in the stream. */
                if (buffer[4] == 0x06) {
                    return fromPkcs7DerInputStream(pbis);
                }
            } catch (Exception e) {
                if (markable) {
                    try {
                        inStream.reset();
                    } catch (IOException ignored) {
                    }
                }
                throw new ParsingException(e);
            }

            /*
             * It wasn't, so just try to keep grabbing certificates until we
             * can't anymore.
             */
            final List coll = new ArrayList();
            T c;
            do {
                /*
                 * If this stream supports marking, try to mark here in case
                 * there is an error during certificate generation.
                 */
                if (markable) {
                    inStream.mark(PUSHBACK_SIZE);
                }

                try {
                    c = generateItem(pbis);
                    coll.add(c);
                } catch (ParsingException e) {
                    /*
                     * If this stream supports marking, attempt to reset it to
                     * the mark before the failure.
                     */
                    if (markable) {
                        try {
                            inStream.reset();
                        } catch (IOException ignored) {
                        }
                    }

                    c = null;
                }
            } while (c != null);

            return coll;
        }

        protected abstract T fromX509PemInputStream(InputStream pbis) throws ParsingException;

        protected abstract T fromX509DerInputStream(InputStream pbis) throws ParsingException;

        protected abstract List fromPkcs7PemInputStream(InputStream is)
                throws ParsingException;

        protected abstract List fromPkcs7DerInputStream(InputStream is)
                throws ParsingException;
    }

    private Parser certificateParser =
            new Parser() {
                @Override
                public OpenSSLX509Certificate fromX509PemInputStream(InputStream is)
                        throws ParsingException {
                    return OpenSSLX509Certificate.fromX509PemInputStream(is);
                }

                @Override
                public OpenSSLX509Certificate fromX509DerInputStream(InputStream is)
                        throws ParsingException {
                    return OpenSSLX509Certificate.fromX509DerInputStream(is);
                }

                @Override
                public List
                        fromPkcs7PemInputStream(InputStream is) throws ParsingException {
                    return OpenSSLX509Certificate.fromPkcs7PemInputStream(is);
                }

                @Override
                public List
                        fromPkcs7DerInputStream(InputStream is) throws ParsingException {
                    return OpenSSLX509Certificate.fromPkcs7DerInputStream(is);
                }
            };

    private Parser crlParser =
            new Parser() {
                @Override
                public OpenSSLX509CRL fromX509PemInputStream(InputStream is)
                        throws ParsingException {
                    return OpenSSLX509CRL.fromX509PemInputStream(is);
                }

                @Override
                public OpenSSLX509CRL fromX509DerInputStream(InputStream is)
                        throws ParsingException {
                    return OpenSSLX509CRL.fromX509DerInputStream(is);
                }

                @Override
                public List fromPkcs7PemInputStream(InputStream is)
                        throws ParsingException {
                    return OpenSSLX509CRL.fromPkcs7PemInputStream(is);
                }

                @Override
                public List fromPkcs7DerInputStream(InputStream is)
                        throws ParsingException {
                    return OpenSSLX509CRL.fromPkcs7DerInputStream(is);
                }
            };

    @Override
    public Certificate engineGenerateCertificate(InputStream inStream) throws CertificateException {
        try {
            return certificateParser.generateItem(inStream);
        } catch (ParsingException e) {
            throw new CertificateException(e);
        }
    }

    @Override
    public Collection engineGenerateCertificates(
            InputStream inStream) throws CertificateException {
        try {
            return certificateParser.generateItems(inStream);
        } catch (ParsingException e) {
            throw new CertificateException(e);
        }
    }

    @Override
    public CRL engineGenerateCRL(InputStream inStream) throws CRLException {
        try {
            return crlParser.generateItem(inStream);
        } catch (ParsingException e) {
            throw new CRLException(e);
        }
    }

    @Override
    public Collection engineGenerateCRLs(InputStream inStream) throws CRLException {
        if (inStream == null) {
            return Collections.emptyList();
        }

        try {
            return crlParser.generateItems(inStream);
        } catch (ParsingException e) {
            throw new CRLException(e);
        }
    }

    @Override
    public Iterator engineGetCertPathEncodings() {
        return OpenSSLX509CertPath.getEncodingsIterator();
    }

    @Override
    public CertPath engineGenerateCertPath(InputStream inStream) throws CertificateException {
        return OpenSSLX509CertPath.fromEncoding(inStream);
    }

    @Override
    public CertPath engineGenerateCertPath(InputStream inStream, String encoding)
            throws CertificateException {
        return OpenSSLX509CertPath.fromEncoding(inStream, encoding);
    }

    @Override
    public CertPath engineGenerateCertPath(List certificates)
            throws CertificateException {
        final List filtered = new ArrayList(certificates.size());
        for (int i = 0; i < certificates.size(); i++) {
            final Certificate c = certificates.get(i);

            if (!(c instanceof X509Certificate)) {
                throw new CertificateException("Certificate not X.509 type at index " + i);
            }

            filtered.add((X509Certificate) c);
        }

        return new OpenSSLX509CertPath(filtered);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy