net.ripe.rpki.commons.crypto.x509cert.X509CertificateUtil.java Source code

Java tutorial

Introduction

Here is the source code for net.ripe.rpki.commons.crypto.x509cert.X509CertificateUtil.java

Source

/**
 * The BSD License
 *
 * Copyright (c) 2010-2012 RIPE NCC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   - Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *   - Neither the name of the RIPE NCC nor the names of its contributors may be
 *     used to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package net.ripe.rpki.commons.crypto.x509cert;

import net.ripe.rpki.commons.crypto.ValidityPeriod;
import net.ripe.rpki.commons.crypto.util.Asn1Util;
import net.ripe.rpki.commons.validation.ValidationResult;
import org.apache.commons.lang.Validate;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.CRLDistPoint;
import org.bouncycastle.asn1.x509.DistributionPoint;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.TBSCertificateStructure;
import org.bouncycastle.util.encoders.Base64Encoder;
import org.bouncycastle.x509.extension.X509ExtensionUtil;

import javax.security.auth.x500.X500Principal;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URI;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.cert.X509Extension;
import java.util.ArrayList;
import java.util.List;

import static net.ripe.rpki.commons.crypto.x509cert.X509CertificateBuilderHelper.DEFAULT_SIGNATURE_PROVIDER;

public final class X509CertificateUtil {

    private X509CertificateUtil() {
        //Utility classes should not have a public or default constructor.
    }

    public static byte[] getSubjectKeyIdentifier(X509Extension certificate) {
        try {
            byte[] extensionValue = certificate
                    .getExtensionValue(org.bouncycastle.asn1.x509.X509Extension.subjectKeyIdentifier.getId());
            if (extensionValue == null) {
                return null;
            }
            return SubjectKeyIdentifier.getInstance(X509ExtensionUtil.fromExtensionValue(extensionValue))
                    .getKeyIdentifier();
        } catch (IOException e) {
            throw new X509CertificateOperationException("Cannot get SubjectKeyIdentifier for certificate", e);
        }
    }

    public static byte[] getAuthorityKeyIdentifier(X509Extension certificate) {
        try {
            byte[] extensionValue = certificate
                    .getExtensionValue(org.bouncycastle.asn1.x509.X509Extension.authorityKeyIdentifier.getId());
            if (extensionValue == null) {
                return null;
            }
            return AuthorityKeyIdentifier.getInstance(X509ExtensionUtil.fromExtensionValue(extensionValue))
                    .getKeyIdentifier();
        } catch (IOException e) {
            throw new X509CertificateOperationException("Can not get AuthorityKeyIdentifier for certificate", e);
        }
    }

    public static X509ResourceCertificate parseDerEncoded(byte[] encoded) {
        X509ResourceCertificateParser parser = new X509ResourceCertificateParser();
        parser.parse(ValidationResult.withLocation("unknown.cer"), encoded);
        return parser.getCertificate();
    }

    /**
     * Get a base 64-encoded, DER-encoded X.509 subjectPublicKeyInfo as used for the Trust Anchor Locator (TAL)
     *
     * @throws X509CertificateOperationException
     *
     * @throws IOException
     */
    public static String getEncodedSubjectPublicKeyInfo(X509Certificate certificate) {

        byte[] tbsCertificate;
        try {
            tbsCertificate = certificate.getTBSCertificate();
        } catch (CertificateEncodingException e) {
            throw new X509CertificateOperationException("Can't extract TBSCertificate from certificate", e);
        }
        ASN1Sequence tbsCertificateSequence = (ASN1Sequence) Asn1Util.decode(tbsCertificate);
        TBSCertificateStructure tbsCertificateStructure = new TBSCertificateStructure(tbsCertificateSequence);
        SubjectPublicKeyInfo subjectPublicKeyInfo = tbsCertificateStructure.getSubjectPublicKeyInfo();

        try {
            byte[] data = subjectPublicKeyInfo.getEncoded();
            Base64Encoder encoder = new Base64Encoder();
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            encoder.encode(data, 0, data.length, out);
            out.flush();
            return out.toString();
        } catch (IOException e) {
            throw new X509CertificateOperationException("Can't encode SubjectPublicKeyInfo for certificate", e);
        }
    }

    public static boolean isRoot(X509Certificate certificate) {
        return certificate.getSubjectX500Principal().equals(certificate.getIssuerX500Principal());
    }

    public static boolean isCa(X509Certificate certificate) {
        try {
            byte[] basicConstraintsExtension = certificate
                    .getExtensionValue(org.bouncycastle.asn1.x509.X509Extension.basicConstraints.getId());
            if (basicConstraintsExtension == null) {
                /**
                 * The Basic Constraints extension field [...] MUST be present when
                 * the Subject is a CA, and MUST NOT be present otherwise.
                 * http://tools.ietf.org/html/draft-ietf-sidr-res-certs-21#section-4.9.1
                 */
                return false;
            }
            BasicConstraints constraints = BasicConstraints
                    .getInstance(X509ExtensionUtil.fromExtensionValue(basicConstraintsExtension));
            return constraints.isCA();
        } catch (IOException e) {
            throw new X509CertificateOperationException(e);
        }
    }

    public static boolean isEe(X509Certificate certificate) {
        return !isCa(certificate);
    }

    public static X509CertificateInformationAccessDescriptor[] getAuthorityInformationAccess(
            X509Certificate certificate) {
        try {
            byte[] extensionValue = certificate
                    .getExtensionValue(org.bouncycastle.asn1.x509.X509Extension.authorityInfoAccess.getId());
            if (extensionValue == null) {
                return null;
            }
            AccessDescription[] accessDescriptions = AuthorityInformationAccess
                    .getInstance(X509ExtensionUtil.fromExtensionValue(extensionValue)).getAccessDescriptions();
            return X509CertificateInformationAccessDescriptor.convertAccessDescriptors(accessDescriptions);
        } catch (IOException e) {
            throw new X509CertificateOperationException(e);
        }
    }

    public static X509CertificateInformationAccessDescriptor[] getSubjectInformationAccess(
            X509Certificate certificate) {
        try {
            byte[] extensionValue = certificate
                    .getExtensionValue(org.bouncycastle.asn1.x509.X509Extension.subjectInfoAccess.getId());
            if (extensionValue == null) {
                return null;
            }
            AccessDescription[] accessDescriptions = AuthorityInformationAccess
                    .getInstance(X509ExtensionUtil.fromExtensionValue(extensionValue)).getAccessDescriptions();
            return X509CertificateInformationAccessDescriptor.convertAccessDescriptors(accessDescriptions);
        } catch (IOException e) {
            throw new X509CertificateOperationException(e);
        }
    }

    public static URI findFirstAuthorityInformationAccessByMethod(X509Certificate certificate,
            ASN1ObjectIdentifier method) {
        Validate.notNull(method, "method is null");
        return findFirstByMethod(method, "rsync", getAuthorityInformationAccess(certificate));
    }

    public static URI findFirstSubjectInformationAccessByMethod(X509Certificate certificate,
            ASN1ObjectIdentifier method) {
        Validate.notNull(method, "method is null");
        return findFirstByMethod(method, "rsync", getSubjectInformationAccess(certificate));
    }

    private static URI findFirstByMethod(ASN1ObjectIdentifier method, String scheme,
            X509CertificateInformationAccessDescriptor[] accessDescriptor) {
        if (accessDescriptor == null) {
            return null;
        }
        for (X509CertificateInformationAccessDescriptor ad : accessDescriptor) {
            if ((method.equals(ad.getMethod())) && (ad.getLocation().getScheme().equals(scheme))) {
                return ad.getLocation();
            }
        }
        return null;
    }

    public static URI[] getCrlDistributionPoints(X509Certificate certificate) {
        byte[] extensionValue = certificate
                .getExtensionValue(org.bouncycastle.asn1.x509.X509Extension.cRLDistributionPoints.getId());
        if (extensionValue == null) {
            return null;
        }
        try {
            CRLDistPoint crldp = CRLDistPoint.getInstance(X509ExtensionUtil.fromExtensionValue(extensionValue));
            return convertCrlDistributionPointToUris(crldp);
        } catch (IOException e) {
            return null;
        }
    }

    private static URI[] convertCrlDistributionPointToUris(CRLDistPoint crldp) {
        List<URI> result = new ArrayList<URI>();
        for (DistributionPoint dp : crldp.getDistributionPoints()) {
            GeneralNames names = (GeneralNames) dp.getDistributionPoint().getName();
            for (GeneralName name : names.getNames()) {
                DERIA5String uri = (DERIA5String) name.getName();
                result.add(URI.create(uri.getString()));
            }
        }
        return result.toArray(new URI[result.size()]);
    }

    public static URI findFirstRsyncCrlDistributionPoint(X509Certificate certificate) {
        URI[] crlDistributionPoints = getCrlDistributionPoints(certificate);
        if (crlDistributionPoints == null) {
            return null;
        }
        for (URI uri : crlDistributionPoints) {
            if (uri.getScheme().equals("rsync")) {
                return uri;
            }
        }
        return null;
    }

    public static URI getManifestUri(X509Certificate certificate) {
        return findFirstSubjectInformationAccessByMethod(certificate,
                X509CertificateInformationAccessDescriptor.ID_AD_RPKI_MANIFEST);
    }

    public static URI getRepositoryUri(X509Certificate certificate) {
        return findFirstSubjectInformationAccessByMethod(certificate,
                X509CertificateInformationAccessDescriptor.ID_AD_CA_REPOSITORY);
    }

    public static boolean isObjectIssuer(X509Certificate certificate) {
        return getManifestUri(certificate) != null;
    }

    public static ValidityPeriod getValidityPeriod(X509Certificate certificate) {
        return new ValidityPeriod(certificate.getNotBefore(), certificate.getNotAfter());
    }

    public static BigInteger getSerialNumber(X509Certificate certificate) {
        return certificate.getSerialNumber();
    }

    public static X500Principal getSubject(X509Certificate certificate) {
        return certificate.getSubjectX500Principal();
    }

    public static X500Principal getIssuer(X509Certificate certificate) {
        return certificate.getIssuerX500Principal();
    }

    public static PublicKey getPublicKey(X509Certificate certificate) {
        return certificate.getPublicKey();
    }

    public static void verify(X509Certificate certificate, PublicKey publicKey)
            throws InvalidKeyException, SignatureException {
        try {
            certificate.verify(publicKey, DEFAULT_SIGNATURE_PROVIDER);
        } catch (CertificateException e) {
            throw new IllegalArgumentException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException(e);
        } catch (NoSuchProviderException e) {
            throw new IllegalArgumentException(e);
        }
    }
}