org.jmrtd.lds.SignedDataUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.jmrtd.lds.SignedDataUtil.java

Source

/*
 * JMRTD - A Java API for accessing machine readable travel documents.
 *
 * Copyright (C) 2006 - 2016  The JMRTD team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 * $Id: $
 */

package org.jmrtd.lds;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Signature;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.security.auth.x500.X500Principal;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.DLSet;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
import org.bouncycastle.asn1.cms.SignedData;
import org.bouncycastle.asn1.cms.SignerIdentifier;
import org.bouncycastle.asn1.cms.SignerInfo;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.jce.provider.X509CertificateObject;
import org.jmrtd.JMRTDSecurityProvider;

/**
 * Utility class for helping with CMS SignedData in security object document and
 * card security file.
 * 
 * This hopefully abstracts some of the BC dependencies away.
 * 
 * FIXME: WORK IN PROGRESS
 * 
 * @author The JMRTD team (info@jmrtd.org)
 * 
 * @version $Revision: $
 */
/* package-visible */ class SignedDataUtil {

    private static final Logger LOGGER = Logger.getLogger("org.jmrtd");

    private static final Provider BC_PROVIDER = JMRTDSecurityProvider.getBouncyCastleProvider();

    /** SignedData related object identifier. */
    public static final String RFC_3369_SIGNED_DATA_OID = "1.2.840.113549.1.7.2", /* id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 } */
            RFC_3369_CONTENT_TYPE_OID = "1.2.840.113549.1.9.3",
            RFC_3369_MESSAGE_DIGEST_OID = "1.2.840.113549.1.9.4", PKCS1_RSA_OID = "1.2.840.113549.1.1.1",
            PKCS1_MD2_WITH_RSA_OID = "1.2.840.113549.1.1.2", PKCS1_MD4_WITH_RSA_OID = "1.2.840.113549.1.1.3",
            PKCS1_MD5_WITH_RSA_OID = "1.2.840.113549.1.1.4", PKCS1_SHA1_WITH_RSA_OID = "1.2.840.113549.1.1.5",
            //  PKCS1_RSAOAEP_ENC_SET = "1.2.840.113549.1.1.6", // other identifier: ripemd160WithRSAEncryption
            //  PKCS1_RSAES_OAEP = "1.2.840.113549.1.1.7",
            PKCS1_SHA256_WITH_RSA_AND_MGF1 = "1.2.840.113549.1.1.8", PKCS1_RSASSA_PSS_OID = "1.2.840.113549.1.1.10",
            PKCS1_SHA256_WITH_RSA_OID = "1.2.840.113549.1.1.11",
            PKCS1_SHA384_WITH_RSA_OID = "1.2.840.113549.1.1.12",
            PKCS1_SHA512_WITH_RSA_OID = "1.2.840.113549.1.1.13",
            PKCS1_SHA224_WITH_RSA_OID = "1.2.840.113549.1.1.14", X9_SHA1_WITH_ECDSA_OID = "1.2.840.10045.4.1",
            X9_SHA224_WITH_ECDSA_OID = "1.2.840.10045.4.3.1", X9_SHA256_WITH_ECDSA_OID = "1.2.840.10045.4.3.2",
            IEEE_P1363_SHA1_OID = "1.3.14.3.2.26";

    /**
     * Prevents instantiation.
     */
    private SignedDataUtil() {
    }

    public static SignedData readSignedData(InputStream inputStream) throws IOException {
        ASN1InputStream asn1in = new ASN1InputStream(inputStream);
        ASN1Sequence sequence = (ASN1Sequence) asn1in.readObject();

        if (sequence.size() != 2) {
            throw new IOException(
                    "Was expecting a DER sequence of length 2, found a DER sequence of length " + sequence.size());
        }

        String contentTypeOID = ((ASN1ObjectIdentifier) sequence.getObjectAt(0)).getId();
        if (!SignedDataUtil.RFC_3369_SIGNED_DATA_OID.equals(contentTypeOID)) {
            throw new IOException("Was expecting signed-data content type OID ("
                    + SignedDataUtil.RFC_3369_SIGNED_DATA_OID + "), found " + contentTypeOID);
        }

        ASN1Primitive asn1SequenceWithSignedData = SignedDataUtil
                .getObjectFromTaggedObject(sequence.getObjectAt(1));

        if (!(asn1SequenceWithSignedData instanceof ASN1Sequence)) {
            throw new IOException("Was expecting an ASN.1 sequence as content");
        }

        return SignedData.getInstance(asn1SequenceWithSignedData);
    }

    public static void writeData(SignedData signedData, OutputStream outputStream) throws IOException {
        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(new ASN1ObjectIdentifier(SignedDataUtil.RFC_3369_SIGNED_DATA_OID));
        v.add(new DERTaggedObject(0, signedData));
        ASN1Sequence fileContentsObject = new DLSequence(v);
        byte[] fileContentsBytes = fileContentsObject.getEncoded(ASN1Encoding.DER);
        outputStream.write(fileContentsBytes);
    }

    public static ASN1Primitive getContent(SignedData signedData) {
        ContentInfo encapContentInfo = signedData.getEncapContentInfo();

        String contentType = encapContentInfo.getContentType().getId();

        DEROctetString eContent = (DEROctetString) encapContentInfo.getContent();

        ASN1InputStream inputStream = null;
        try {
            inputStream = new ASN1InputStream(new ByteArrayInputStream(eContent.getOctets()));
            ASN1Primitive firstObject = inputStream.readObject();
            return firstObject;
        } catch (IOException ioe) {
            LOGGER.log(Level.WARNING, "Unexpected exception", ioe);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException ioe) {
                    LOGGER.log(Level.WARNING, "Exception closing input stream");
                    /* At least we tried... */
                }
            }
        }

        return null;
    }

    /**
     * Removes the tag from a tagged object.
     * 
     * @param asn1Encodable the encoded tagged object
     * 
     * @return the object
     * 
     * @throws IOException if the input is not a tagged object or the tagNo is not 0
     */
    public static ASN1Primitive getObjectFromTaggedObject(ASN1Encodable asn1Encodable) throws IOException {
        if (!(asn1Encodable instanceof ASN1TaggedObject)) {
            throw new IOException(
                    "Was expecting an ASN1TaggedObject, found " + asn1Encodable.getClass().getCanonicalName());
        }

        ASN1TaggedObject asn1TaggedObject = (ASN1TaggedObject) asn1Encodable;

        int tagNo = asn1TaggedObject.getTagNo();
        if (tagNo != 0) {
            throw new IOException("Was expecting tag 0, found " + Integer.toHexString(tagNo));
        }

        return asn1TaggedObject.getObject();
    }

    public static String getSignerInfoDigestAlgorithm(SignedData signedData) {
        try {
            SignerInfo signerInfo = getSignerInfo(signedData);
            String digestAlgOID = signerInfo.getDigestAlgorithm().getAlgorithm().getId();
            return SignedDataUtil.lookupMnemonicByOID(digestAlgOID);
        } catch (NoSuchAlgorithmException nsae) {
            LOGGER.severe("Exception: " + nsae.getMessage());
            return null; // throw new IllegalStateException(nsae.toString());
        }
    }

    public static String getDigestEncryptionAlgorithm(SignedData signedData) {
        try {
            SignerInfo signerInfo = getSignerInfo(signedData);
            String digestEncryptionAlgorithmOID = signerInfo.getDigestEncryptionAlgorithm().getAlgorithm().getId();
            if (digestEncryptionAlgorithmOID == null) {
                return null;
            }
            return SignedDataUtil.lookupMnemonicByOID(digestEncryptionAlgorithmOID);
        } catch (NoSuchAlgorithmException nsae) {
            LOGGER.severe("Exception: " + nsae.getMessage());
            return null; // throw new IllegalStateException(nsae.toString());
        }
    }

    /**
     * Gets the contents of the signed data over which the
     * signature is to be computed.
     *
     * See RFC 3369, Cryptographic Message Syntax, August 2002,
     * Section 5.4 for details.
     *
     * FIXME: Maybe throw an exception instead of issuing warnings
     * on logger if signed attributes do not check out.
     *
     * @see #getDocSigningCertificate()
     * @see #getSignature()
     *
     * @return the contents of the security object over which the
     *         signature is to be computed
     */
    public static byte[] getEContent(SignedData signedData) {
        SignerInfo signerInfo = getSignerInfo(signedData);
        ASN1Set signedAttributesSet = signerInfo.getAuthenticatedAttributes();

        ContentInfo contentInfo = signedData.getEncapContentInfo();
        byte[] contentBytes = ((DEROctetString) contentInfo.getContent()).getOctets();

        if (signedAttributesSet.size() == 0) {
            /* Signed attributes absent, return content to be signed... */
            return contentBytes;
        }

        /* Signed attributes present (i.e. a structure containing a hash of the content), return that structure to be signed... */
        /* This option is taken by ICAO passports. */
        byte[] attributesBytes = null;
        String digAlg = signerInfo.getDigestAlgorithm().getAlgorithm().getId();

        try {
            attributesBytes = signedAttributesSet.getEncoded(ASN1Encoding.DER);

            checkEContent(getAttributes(signedAttributesSet), digAlg, contentBytes);

        } catch (NoSuchAlgorithmException nsae) {
            LOGGER.warning("Error checking signedAttributes in eContent! No such algorithm: \"" + digAlg + "\": "
                    + nsae.getMessage());
        } catch (IOException ioe) {
            LOGGER.severe("Error getting signedAttributes: " + ioe.getMessage());
        }

        return attributesBytes;
    }

    /* FIXME: Move this from lds package to verifier. -- MO */
    /* FIXME: This only warns on logger. */
    /**
     * Checks that the content actually digests to the hash value contained in the message digest attribute.
     * 
     * @param attributes the attributes, this should contain an attribute of type {@link #RFC_3369_MESSAGE_DIGEST_OID}
     * @param digAlg the digest algorithm
     * @param contentBytes the contents
     * 
     * @throws NoSuchAlgorithmException if the digest algorithm is unsupported
     */
    private static void checkEContent(Collection<Attribute> attributes, String digAlg, byte[] contentBytes)
            throws NoSuchAlgorithmException {
        for (Attribute attribute : attributes) {
            if (!RFC_3369_MESSAGE_DIGEST_OID.equals(attribute.getAttrType().getId())) {
                continue;
            }

            ASN1Set attrValuesSet = attribute.getAttrValues();
            if (attrValuesSet.size() != 1) {
                LOGGER.warning("Expected only one attribute value in signedAttribute message digest in eContent!");
            }
            byte[] storedDigestedContent = ((DEROctetString) attrValuesSet.getObjectAt(0)).getOctets();

            if (storedDigestedContent == null) {
                LOGGER.warning("Error extracting signedAttribute message digest in eContent!");
            }

            MessageDigest dig = MessageDigest.getInstance(digAlg);
            byte[] computedDigestedContent = dig.digest(contentBytes);
            if (!Arrays.equals(storedDigestedContent, computedDigestedContent)) {
                LOGGER.warning("Error checking signedAttribute message digest in eContent!");
            }
        }
    }

    private static List<Attribute> getAttributes(ASN1Set signedAttributesSet) {
        List<ASN1Sequence> attributeObjects = Collections.list(signedAttributesSet.getObjects());
        List<Attribute> attributes = new ArrayList(attributeObjects.size());
        for (ASN1Sequence attributeObject : attributeObjects) {
            Attribute attribute = Attribute.getInstance(attributeObject);
            attributes.add(attribute);
        }
        return attributes;
    }

    /**
     * Gets the stored signature of the security object.
     *
     * @see #getDocSigningCertificate()
     *
     * @return the signature
     */
    public static byte[] getEncryptedDigest(SignedData signedData) {
        SignerInfo signerInfo = getSignerInfo(signedData);
        return signerInfo.getEncryptedDigest().getOctets();
    }

    public static IssuerAndSerialNumber getIssuerAndSerialNumber(SignedData signedData) {
        SignerInfo signerInfo = getSignerInfo(signedData);
        SignerIdentifier signerIdentifier = signerInfo.getSID();
        IssuerAndSerialNumber issuerAndSerialNumber = IssuerAndSerialNumber.getInstance(signerIdentifier.getId());
        X500Name issuer = issuerAndSerialNumber.getName();
        BigInteger serialNumber = issuerAndSerialNumber.getSerialNumber().getValue();
        return new IssuerAndSerialNumber(issuer, serialNumber);
    }

    private static SignerInfo getSignerInfo(SignedData signedData) {
        ASN1Set signerInfos = signedData.getSignerInfos();
        if (signerInfos.size() > 1) {
            LOGGER.warning("Found " + signerInfos.size() + " signerInfos");
        }
        for (int i = 0; i < signerInfos.size(); i++) {
            SignerInfo info = new SignerInfo((ASN1Sequence) signerInfos.getObjectAt(i));
            return info;
        }
        return null;
    }

    public static X509Certificate getDocSigningCertificate(SignedData signedData) throws CertificateException {
        byte[] certSpec = null;
        ASN1Set certs = signedData.getCertificates();
        if (certs == null || certs.size() <= 0) {
            return null;
        }
        if (certs.size() != 1) {
            LOGGER.warning("Found " + certs.size() + " certificates");
        }
        X509CertificateObject certObject = null;
        for (int i = 0; i < certs.size(); i++) {
            org.bouncycastle.asn1.x509.Certificate certAsASN1Object = org.bouncycastle.asn1.x509.Certificate
                    .getInstance((ASN1Sequence) certs.getObjectAt(i));
            certObject = new X509CertificateObject(certAsASN1Object); // NOTE: >= BC 1.48
            //      certObject = new X509CertificateObject(X509CertificateStructure.getInstance(certAsASN1Object)); // NOTE: <= BC 1.47
            certSpec = certObject.getEncoded();
        }

        /*
         * NOTE: we could have just returned that X509CertificateObject here,
         * but by reconstructing it using the client's default provider we hide
         * the fact that we're using BC.
         */
        try {
            CertificateFactory factory = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate) factory
                    .generateCertificate(new ByteArrayInputStream(certSpec));
            return cert;
        } catch (Exception e) {
            /* NOTE: Reconstructing using preferred provider didn't work?!?! */
            return certObject;
        }
    }

    public static SignedData createSignedData(String digestAlgorithm, String digestEncryptionAlgorithm,
            String contentTypeOID, ContentInfo contentInfo, byte[] encryptedDigest,
            X509Certificate docSigningCertificate)
            throws NoSuchAlgorithmException, CertificateException, IOException {
        ASN1Set digestAlgorithmsSet = SignedDataUtil
                .createSingletonSet(SignedDataUtil.createDigestAlgorithms(digestAlgorithm));
        ASN1Set certificates = createSingletonSet(SignedDataUtil.createCertificate(docSigningCertificate));
        ASN1Set crls = null;
        ASN1Set signerInfos = createSingletonSet(createSignerInfo(digestAlgorithm, digestEncryptionAlgorithm,
                contentTypeOID, contentInfo, encryptedDigest, docSigningCertificate).toASN1Object());
        return new SignedData(digestAlgorithmsSet, contentInfo, certificates, crls, signerInfos);
    }

    public static SignerInfo createSignerInfo(String digestAlgorithm, String digestEncryptionAlgorithm,
            String contentTypeOID, ContentInfo contentInfo, byte[] encryptedDigest,
            X509Certificate docSigningCertificate) throws NoSuchAlgorithmException {
        /* Get the issuer name (CN, O, OU, C) from the cert and put it in a SignerIdentifier struct. */
        X500Principal docSignerPrincipal = ((X509Certificate) docSigningCertificate).getIssuerX500Principal();
        X500Name docSignerName = new X500Name(docSignerPrincipal.getName(X500Principal.RFC2253));
        BigInteger serial = ((X509Certificate) docSigningCertificate).getSerialNumber();
        SignerIdentifier sid = new SignerIdentifier(new IssuerAndSerialNumber(docSignerName, serial));

        AlgorithmIdentifier digestAlgorithmObject = new AlgorithmIdentifier(
                new ASN1ObjectIdentifier(SignedDataUtil.lookupOIDByMnemonic(digestAlgorithm)));
        AlgorithmIdentifier digestEncryptionAlgorithmObject = new AlgorithmIdentifier(
                new ASN1ObjectIdentifier(SignedDataUtil.lookupOIDByMnemonic(digestEncryptionAlgorithm)));

        ASN1Set authenticatedAttributes = createAuthenticatedAttributes(digestAlgorithm, contentTypeOID,
                contentInfo); // struct containing the hash of content
        ASN1OctetString encryptedDigestObject = new DEROctetString(encryptedDigest); // this is the signature
        ASN1Set unAuthenticatedAttributes = null; // should be empty set?
        return new SignerInfo(sid, digestAlgorithmObject, authenticatedAttributes, digestEncryptionAlgorithmObject,
                encryptedDigestObject, unAuthenticatedAttributes);
    }

    public static ASN1Set createAuthenticatedAttributes(String digestAlgorithm, String contentTypeOID,
            ContentInfo contentInfo) throws NoSuchAlgorithmException {
        /* Check bug found by Paulo Assumpco. */
        if ("SHA256".equals(digestAlgorithm)) {
            digestAlgorithm = "SHA-256";
        }
        MessageDigest dig = MessageDigest.getInstance(digestAlgorithm);
        byte[] contentBytes = ((DEROctetString) contentInfo.getContent()).getOctets();
        byte[] digestedContentBytes = dig.digest(contentBytes);
        ASN1OctetString digestedContent = new DEROctetString(digestedContentBytes);
        Attribute contentTypeAttribute = new Attribute(
                new ASN1ObjectIdentifier(SignedDataUtil.RFC_3369_CONTENT_TYPE_OID),
                createSingletonSet(new ASN1ObjectIdentifier(contentTypeOID)));
        Attribute messageDigestAttribute = new Attribute(
                new ASN1ObjectIdentifier(SignedDataUtil.RFC_3369_MESSAGE_DIGEST_OID),
                createSingletonSet(digestedContent));
        ASN1Object[] result = { contentTypeAttribute.toASN1Primitive(), messageDigestAttribute.toASN1Primitive() };
        return new DLSet(result);
    }

    public static ASN1Sequence createDigestAlgorithms(String digestAlgorithm) throws NoSuchAlgorithmException {
        ASN1ObjectIdentifier algorithmIdentifier = new ASN1ObjectIdentifier(
                SignedDataUtil.lookupOIDByMnemonic(digestAlgorithm));
        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(algorithmIdentifier);
        return new DLSequence(v);
    }

    public static ASN1Sequence createCertificate(X509Certificate cert) throws CertificateException {
        try {
            byte[] certSpec = cert.getEncoded();
            ASN1InputStream asn1In = new ASN1InputStream(certSpec);
            try {
                ASN1Sequence certSeq = (ASN1Sequence) asn1In.readObject();
                return certSeq;
            } finally {
                asn1In.close();
            }
        } catch (IOException ioe) {
            throw new CertificateException("Could not construct certificate byte stream");
        }
    }

    public static byte[] signData(String digestAlgorithm, String digestEncryptionAlgorithm, String contentTypeOID,
            ContentInfo contentInfo, PrivateKey privateKey, String provider) {
        byte[] encryptedDigest = null;
        try {
            byte[] dataToBeSigned = createAuthenticatedAttributes(digestAlgorithm, contentTypeOID, contentInfo)
                    .getEncoded(ASN1Encoding.DER);
            Signature s = null;
            if (provider != null) {
                s = Signature.getInstance(digestEncryptionAlgorithm, provider);
            } else {
                s = Signature.getInstance(digestEncryptionAlgorithm);
            }
            s.initSign(privateKey);
            s.update(dataToBeSigned);
            encryptedDigest = s.sign();
        } catch (Exception e) {
            LOGGER.severe("Exception: " + e.getMessage());
            return null;
        }
        return encryptedDigest;
    }

    private static ASN1Set createSingletonSet(ASN1Object e) {
        return new DLSet(new ASN1Encodable[] { e });
    }

    /**
     * Gets the common mnemonic string (such as "SHA1", "SHA256withRSA") given an OID.
     *
     * @param oid an OID
     *
     * @throws NoSuchAlgorithmException if the provided OID is not yet supported
     */
    public static String lookupMnemonicByOID(String oid) throws NoSuchAlgorithmException {
        if (oid == null) {
            return null;
        }
        if (oid.equals(X509ObjectIdentifiers.organization.getId())) {
            return "O";
        }
        if (oid.equals(X509ObjectIdentifiers.organizationalUnitName.getId())) {
            return "OU";
        }
        if (oid.equals(X509ObjectIdentifiers.commonName.getId())) {
            return "CN";
        }
        if (oid.equals(X509ObjectIdentifiers.countryName.getId())) {
            return "C";
        }
        if (oid.equals(X509ObjectIdentifiers.stateOrProvinceName.getId())) {
            return "ST";
        }
        if (oid.equals(X509ObjectIdentifiers.localityName.getId())) {
            return "L";
        }
        if (oid.equals(X509ObjectIdentifiers.id_SHA1.getId())) {
            return "SHA-1";
        }
        if (oid.equals(NISTObjectIdentifiers.id_sha224.getId())) {
            return "SHA-224";
        }
        if (oid.equals(NISTObjectIdentifiers.id_sha256.getId())) {
            return "SHA-256";
        }
        if (oid.equals(NISTObjectIdentifiers.id_sha384.getId())) {
            return "SHA-384";
        }
        if (oid.equals(NISTObjectIdentifiers.id_sha512.getId())) {
            return "SHA-512";
        }
        if (oid.equals(X9_SHA1_WITH_ECDSA_OID)) {
            return "SHA1withECDSA";
        }
        if (oid.equals(X9_SHA224_WITH_ECDSA_OID)) {
            return "SHA224withECDSA";
        }
        if (oid.equals(X9_SHA256_WITH_ECDSA_OID)) {
            return "SHA256withECDSA";
        }
        if (oid.equals(PKCS1_RSA_OID)) {
            return "RSA";
        }
        if (oid.equals(PKCS1_MD2_WITH_RSA_OID)) {
            return "MD2withRSA";
        }
        if (oid.equals(PKCS1_MD4_WITH_RSA_OID)) {
            return "MD4withRSA";
        }
        if (oid.equals(PKCS1_MD5_WITH_RSA_OID)) {
            return "MD5withRSA";
        }
        if (oid.equals(PKCS1_SHA1_WITH_RSA_OID)) {
            return "SHA1withRSA";
        }
        if (oid.equals(PKCS1_SHA256_WITH_RSA_OID)) {
            return "SHA256withRSA";
        }
        if (oid.equals(PKCS1_SHA384_WITH_RSA_OID)) {
            return "SHA384withRSA";
        }
        if (oid.equals(PKCS1_SHA512_WITH_RSA_OID)) {
            return "SHA512withRSA";
        }
        if (oid.equals(PKCS1_SHA224_WITH_RSA_OID)) {
            return "SHA224withRSA";
        }
        if (oid.equals(IEEE_P1363_SHA1_OID)) {
            return "SHA-1";
        }
        if (oid.equals(PKCS1_RSASSA_PSS_OID)) {
            return "SSAwithRSA/PSS";
        }
        if (oid.equals(PKCS1_SHA256_WITH_RSA_AND_MGF1)) {
            return "SHA256withRSAandMGF1";
        }
        throw new NoSuchAlgorithmException("Unknown OID " + oid);
    }

    public static String lookupOIDByMnemonic(String name) throws NoSuchAlgorithmException {
        if (name.equals("O")) {
            return X509ObjectIdentifiers.organization.getId();
        }
        if (name.equals("OU")) {
            return X509ObjectIdentifiers.organizationalUnitName.getId();
        }
        if (name.equals("CN")) {
            return X509ObjectIdentifiers.commonName.getId();
        }
        if (name.equals("C")) {
            return X509ObjectIdentifiers.countryName.getId();
        }
        if (name.equals("ST")) {
            return X509ObjectIdentifiers.stateOrProvinceName.getId();
        }
        if (name.equals("L")) {
            return X509ObjectIdentifiers.localityName.getId();
        }
        if (name.equalsIgnoreCase("SHA-1") || name.equalsIgnoreCase("SHA1")) {
            return X509ObjectIdentifiers.id_SHA1.getId();
        }
        if (name.equalsIgnoreCase("SHA-224") || name.equalsIgnoreCase("SHA224")) {
            return NISTObjectIdentifiers.id_sha224.getId();
        }
        if (name.equalsIgnoreCase("SHA-256") || name.equalsIgnoreCase("SHA256")) {
            return NISTObjectIdentifiers.id_sha256.getId();
        }
        if (name.equalsIgnoreCase("SHA-384") || name.equalsIgnoreCase("SHA384")) {
            return NISTObjectIdentifiers.id_sha384.getId();
        }
        if (name.equalsIgnoreCase("SHA-512") || name.equalsIgnoreCase("SHA512")) {
            return NISTObjectIdentifiers.id_sha512.getId();
        }
        if (name.equalsIgnoreCase("RSA")) {
            return PKCS1_RSA_OID;
        }
        if (name.equalsIgnoreCase("MD2withRSA")) {
            return PKCS1_MD2_WITH_RSA_OID;
        }
        if (name.equalsIgnoreCase("MD4withRSA")) {
            return PKCS1_MD4_WITH_RSA_OID;
        }
        if (name.equalsIgnoreCase("MD5withRSA")) {
            return PKCS1_MD5_WITH_RSA_OID;
        }
        if (name.equalsIgnoreCase("SHA1withRSA")) {
            return PKCS1_SHA1_WITH_RSA_OID;
        }
        if (name.equalsIgnoreCase("SHA256withRSA")) {
            return PKCS1_SHA256_WITH_RSA_OID;
        }
        if (name.equalsIgnoreCase("SHA384withRSA")) {
            return PKCS1_SHA384_WITH_RSA_OID;
        }
        if (name.equalsIgnoreCase("SHA512withRSA")) {
            return PKCS1_SHA512_WITH_RSA_OID;
        }
        if (name.equalsIgnoreCase("SHA224withRSA")) {
            return PKCS1_SHA224_WITH_RSA_OID;
        }
        if (name.equalsIgnoreCase("SHA1withECDSA")) {
            return X9_SHA1_WITH_ECDSA_OID;
        }
        if (name.equalsIgnoreCase("SHA224withECDSA")) {
            return X9_SHA224_WITH_ECDSA_OID;
        }
        if (name.equalsIgnoreCase("SHA256withECDSA")) {
            return X9_SHA256_WITH_ECDSA_OID;
        }
        if (name.equalsIgnoreCase("SAwithRSA/PSS")) {
            return PKCS1_RSASSA_PSS_OID;
        }
        if (name.equalsIgnoreCase("SSAwithRSA/PSS")) {
            return PKCS1_RSASSA_PSS_OID;
        }
        if (name.equalsIgnoreCase("RSASSA-PSS")) {
            return PKCS1_RSASSA_PSS_OID;
        }
        if (name.equalsIgnoreCase("SHA256withRSAandMGF1")) {
            return PKCS1_SHA256_WITH_RSA_AND_MGF1;
        }
        throw new NoSuchAlgorithmException("Unknown name " + name);
    }
}