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

org.opensaml.xml.security.keyinfo.KeyInfoHelper Maven / Gradle / Ivy

Go to download

XMLTooling-J is a low-level library that may be used to construct libraries that allow developers to work with XML in a Java beans manner.

The newest version!
/*
 * Licensed to the University Corporation for Advanced Internet Development, 
 * Inc. (UCAID) under one or more contributor license agreements.  See the 
 * NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The UCAID licenses this file to You 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.opensaml.xml.security.keyinfo;

import java.math.BigInteger;
import java.security.KeyException;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CRLException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.DSAParameterSpec;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import org.opensaml.xml.Configuration;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.XMLObjectBuilderFactory;
import org.opensaml.xml.security.SecurityHelper;
import org.opensaml.xml.security.x509.X509Util;
import org.opensaml.xml.signature.DEREncodedKeyValue;
import org.opensaml.xml.signature.DSAKeyValue;
import org.opensaml.xml.signature.Exponent;
import org.opensaml.xml.signature.G;
import org.opensaml.xml.signature.KeyInfo;
import org.opensaml.xml.signature.KeyName;
import org.opensaml.xml.signature.KeyValue;
import org.opensaml.xml.signature.Modulus;
import org.opensaml.xml.signature.P;
import org.opensaml.xml.signature.Q;
import org.opensaml.xml.signature.RSAKeyValue;
import org.opensaml.xml.signature.X509Data;
import org.opensaml.xml.signature.X509Digest;
import org.opensaml.xml.signature.X509IssuerName;
import org.opensaml.xml.signature.X509IssuerSerial;
import org.opensaml.xml.signature.X509SKI;
import org.opensaml.xml.signature.X509SerialNumber;
import org.opensaml.xml.signature.X509SubjectName;
import org.opensaml.xml.signature.Y;
import org.opensaml.xml.util.Base64;
import org.opensaml.xml.util.DatatypeHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Utility class for working with data inside a KeyInfo object.
 * 
 * Methods are provided for converting the representation stored in the XMLTooling KeyInfo to Java
 * java.security native types, and for storing these Java native types inside a KeyInfo.
 */
public class KeyInfoHelper {

    /** Factory for {@link java.security.cert.X509Certificate} and
     * {@link java.security.cert.X509CRL} creation. */
    private static CertificateFactory x509CertFactory;
    
    /** Constructor. */
    protected KeyInfoHelper(){
        
    }    

    /**
     * Get the set of key names inside the specified {@link KeyInfo} as a list of strings.
     * 
     * @param keyInfo {@link KeyInfo} to retrieve key names from
     * 
     * @return a list of key name strings
     */
    public static List getKeyNames(KeyInfo keyInfo) {
        List keynameList = new LinkedList();

        if (keyInfo == null) {
            return keynameList;
        }

        List keyNames = keyInfo.getKeyNames();
        for (KeyName keyName : keyNames) {
            if (keyName.getValue() != null) {
                keynameList.add(keyName.getValue());
            }
        }

        return keynameList;
    }
    
    /**
     * Add a new {@link KeyName} value to a KeyInfo.
     * 
     * @param keyInfo the KeyInfo to which to add the new value
     * @param keyNameValue the new key name value to add
     */
    public static void addKeyName(KeyInfo keyInfo, String keyNameValue) {
        KeyName keyName = (KeyName) Configuration.getBuilderFactory()
            .getBuilder(KeyName.DEFAULT_ELEMENT_NAME)
            .buildObject(KeyName.DEFAULT_ELEMENT_NAME);
        keyName.setValue(keyNameValue);
        keyInfo.getKeyNames().add(keyName);
    }

    /**
     * Get a list of the Java {@link java.security.cert.X509Certificate} within the given KeyInfo.
     * 
     * @param keyInfo key info to extract the certificates from
     * 
     * @return a list of Java {@link java.security.cert.X509Certificate}s
     * 
     * @throws CertificateException thrown if there is a problem converting the 
     *          X509 data into {@link java.security.cert.X509Certificate}s.
     */
    public static List getCertificates(KeyInfo keyInfo) throws CertificateException {
        List certList = new LinkedList();

        if (keyInfo == null) {
            return certList;
        }

        List x509Datas = keyInfo.getX509Datas();
        for (X509Data x509Data : x509Datas) {
            if (x509Data != null) {
                certList.addAll(getCertificates(x509Data));
            }
        }

        return certList;
    }

    /**
     * Get a list of the Java {@link java.security.cert.X509Certificate} within the given {@link X509Data}.
     * 
     * @param x509Data {@link X509Data} from which to extract the certificate
     * 
     * @return a list of Java {@link java.security.cert.X509Certificate}s
     * 
     * @throws CertificateException thrown if there is a problem converting the 
     *          X509 data into {@link java.security.cert.X509Certificate}s.
     */
    public static List getCertificates(X509Data x509Data) throws CertificateException {
        List certList = new LinkedList();

        if (x509Data == null) {
            return certList;
        }

        for (org.opensaml.xml.signature.X509Certificate xmlCert : x509Data.getX509Certificates()) {
            if (xmlCert != null && xmlCert.getValue() != null) {
                X509Certificate newCert = getCertificate(xmlCert);
                certList.add(newCert);
            }
        }

        return certList;
    }

    /**
     * Convert an {@link org.opensaml.xml.signature.X509Certificate} into a native Java representation.
     * 
     * @param xmlCert an {@link org.opensaml.xml.signature.X509Certificate}
     * 
     * @return a {@link java.security.cert.X509Certificate}
     * 
     * @throws CertificateException thrown if there is a problem converting the 
     *           X509 data into {@link java.security.cert.X509Certificate}s.
     */
    public static X509Certificate getCertificate(org.opensaml.xml.signature.X509Certificate xmlCert)
            throws CertificateException {

        if (xmlCert == null || xmlCert.getValue() == null) {
            return null;
        }

        Collection certs = X509Util.decodeCertificate(Base64.decode(xmlCert.getValue()));
        if (certs != null && certs.iterator().hasNext()) {
            return certs.iterator().next();
        } else {
            return null;
        }
    }

    /**
     * Get a list of the Java {@link java.security.cert.X509CRL}s within the given {@link KeyInfo}.
     * 
     * @param keyInfo the {@link KeyInfo} to extract the CRL's from
     * 
     * @return a list of Java {@link java.security.cert.X509CRL}s
     * 
     * @throws CRLException thrown if there is a problem converting the 
     *          CRL data into {@link java.security.cert.X509CRL}s
     */
    public static List getCRLs(KeyInfo keyInfo) throws CRLException {
        List crlList = new LinkedList();

        if (keyInfo == null) {
            return crlList;
        }

        List x509Datas = keyInfo.getX509Datas();
        for (X509Data x509Data : x509Datas) {
            if (x509Data != null) {
                crlList.addAll(getCRLs(x509Data));
            }
        }

        return crlList;
    }

    /**
     * Get a list of the Java {@link java.security.cert.X509CRL}s within the given {@link X509Data}.
     * 
     * @param x509Data {@link X509Data} to extract the CRLs from
     * 
     * @return a list of Java {@link java.security.cert.X509CRL}s
     * 
     * @throws CRLException thrown if there is a problem converting the 
     *          CRL data into {@link java.security.cert.X509CRL}s
     */
    public static List getCRLs(X509Data x509Data) throws CRLException {
        List crlList = new LinkedList();

        if (x509Data == null) {
            return crlList;
        }

        for (org.opensaml.xml.signature.X509CRL xmlCRL : x509Data.getX509CRLs()) {
            if (xmlCRL != null && xmlCRL.getValue() != null) {
                X509CRL newCRL = getCRL(xmlCRL);
                crlList.add(newCRL);
            }
        }

        return crlList;
    }

    /**
     * Convert an {@link org.opensaml.xml.signature.X509CRL} into a native Java representation.
     * 
     * @param xmlCRL object to extract the CRL from
     * 
     * @return a native Java {@link java.security.cert.X509CRL} object
     * 
     * @throws CRLException thrown if there is a problem converting the 
     *          CRL data into {@link java.security.cert.X509CRL}s
     */
    public static X509CRL getCRL(org.opensaml.xml.signature.X509CRL xmlCRL) throws CRLException {

        if (xmlCRL == null || xmlCRL.getValue() == null) {
            return null;
        }
        
        Collection crls = X509Util.decodeCRLs(Base64.decode(xmlCRL.getValue()));
        return crls.iterator().next();
    }

    /**
     * Converts a native Java {@link java.security.cert.X509Certificate} into the corresponding 
     * XMLObject and stores it in a {@link KeyInfo} in the first {@link X509Data} element. 
     * The X509Data element will be created if necessary.
     * 
     * @param keyInfo the {@link KeyInfo} object into which to add the certificate
     * @param cert the Java {@link java.security.cert.X509Certificate} to add
     * @throws CertificateEncodingException thrown when there is an error converting the Java 
     *           certificate representation to the XMLObject representation
     */
    public static void addCertificate(KeyInfo keyInfo, X509Certificate cert) throws CertificateEncodingException {
        X509Data x509Data;
        if (keyInfo.getX509Datas().size() == 0) {
            x509Data = (X509Data) Configuration.getBuilderFactory()
                .getBuilder(X509Data.DEFAULT_ELEMENT_NAME)
                .buildObject(X509Data.DEFAULT_ELEMENT_NAME);
            keyInfo.getX509Datas().add(x509Data);
        } else {
            x509Data = keyInfo.getX509Datas().get(0);
        }
        x509Data.getX509Certificates().add(buildX509Certificate(cert));
    }

    /**
     * Converts a native Java {@link java.security.cert.X509CRL} into the corresponding XMLObject and stores it
     * in a {@link KeyInfo} in the first {@link X509Data} element.  The X509Data element
     * will be created if necessary.
     * 
     * @param keyInfo the {@link KeyInfo} object into which to add the CRL
     * @param crl the Java {@link java.security.cert.X509CRL} to add
     * @throws CRLException thrown when there is an error converting the Java 
     *           CRL representation to the XMLObject representation
     */
    public static void addCRL(KeyInfo keyInfo, X509CRL crl) throws CRLException {
        X509Data x509Data;
        if (keyInfo.getX509Datas().size() == 0) {
            x509Data = (X509Data) Configuration.getBuilderFactory()
                .getBuilder(X509Data.DEFAULT_ELEMENT_NAME)
                .buildObject(X509Data.DEFAULT_ELEMENT_NAME);
            keyInfo.getX509Datas().add(x509Data);
        } else {
            x509Data = keyInfo.getX509Datas().get(0);
        }
        x509Data.getX509CRLs().add(buildX509CRL(crl));
    }
    
    /**
     * Builds an {@link org.opensaml.xml.signature.X509Certificate} XMLObject from a native 
     * Java {@link java.security.cert.X509Certificate}.
     * 
     * @param cert the Java {@link java.security.cert.X509Certificate} to convert
     * @return a {@link org.opensaml.xml.signature.X509Certificate} XMLObject
     * @throws CertificateEncodingException thrown when there is an error converting the Java 
     *           certificate representation to the XMLObject representation
     */
    public static org.opensaml.xml.signature.X509Certificate 
    buildX509Certificate(X509Certificate cert) throws CertificateEncodingException {
        org.opensaml.xml.signature.X509Certificate xmlCert =
            (org.opensaml.xml.signature.X509Certificate) Configuration.getBuilderFactory()
            .getBuilder(org.opensaml.xml.signature.X509Certificate.DEFAULT_ELEMENT_NAME)
            .buildObject(org.opensaml.xml.signature.X509Certificate.DEFAULT_ELEMENT_NAME);
        
        xmlCert.setValue(Base64.encodeBytes(cert.getEncoded()));
        
        return xmlCert;
    }
    
    /**
     * Builds an {@link org.opensaml.xml.signature.X509CRL} XMLObject from
     * a native Java {@link java.security.cert.X509CRL}.
     * 
     * @param crl the Java {@link java.security.cert.X509CRL} to convert
     * @return a {@link org.opensaml.xml.signature.X509CRL} XMLObject
     * @throws CRLException thrown when there is an error converting the Java 
     *           CRL representation to the XMLObject representation
     */
    public static org.opensaml.xml.signature.X509CRL buildX509CRL(X509CRL crl) throws CRLException {
        org.opensaml.xml.signature.X509CRL xmlCRL =
            (org.opensaml.xml.signature.X509CRL) Configuration.getBuilderFactory()
            .getBuilder(org.opensaml.xml.signature.X509CRL.DEFAULT_ELEMENT_NAME)
            .buildObject(org.opensaml.xml.signature.X509CRL.DEFAULT_ELEMENT_NAME);
        
        xmlCRL.setValue(Base64.encodeBytes(crl.getEncoded()));
        
        return xmlCRL;
    }
    
    /**
     * Build an {@link X509SubjectName} containing a given subject name.
     * 
     * @param subjectName the name content
     * @return the new X509SubjectName
     */
    public static X509SubjectName buildX509SubjectName(String subjectName) {
        X509SubjectName xmlSubjectName = (X509SubjectName) Configuration.getBuilderFactory()
            .getBuilder(X509SubjectName.DEFAULT_ELEMENT_NAME)
            .buildObject(X509SubjectName.DEFAULT_ELEMENT_NAME);
        xmlSubjectName.setValue(subjectName); 
        return xmlSubjectName;
    }
    
    /**
     * Build an {@link X509IssuerSerial} containing a given issuer name and serial number.
     * 
     * @param issuerName the name content
     * @param serialNumber the serial number content
     * @return the new X509IssuerSerial
     */
    public static X509IssuerSerial buildX509IssuerSerial(String issuerName, BigInteger serialNumber) {
        X509IssuerName xmlIssuerName = (X509IssuerName) Configuration.getBuilderFactory()
            .getBuilder(X509IssuerName.DEFAULT_ELEMENT_NAME)
        .buildObject(X509IssuerName.DEFAULT_ELEMENT_NAME);
        xmlIssuerName.setValue(issuerName);
        
        X509SerialNumber xmlSerialNumber = (X509SerialNumber) Configuration.getBuilderFactory()
            .getBuilder(X509SerialNumber.DEFAULT_ELEMENT_NAME)
            .buildObject(X509SerialNumber.DEFAULT_ELEMENT_NAME);
        xmlSerialNumber.setValue(serialNumber);
        
        X509IssuerSerial xmlIssuerSerial = (X509IssuerSerial) Configuration.getBuilderFactory()
            .getBuilder(X509IssuerSerial.DEFAULT_ELEMENT_NAME)
            .buildObject(X509IssuerSerial.DEFAULT_ELEMENT_NAME);
        xmlIssuerSerial.setX509IssuerName(xmlIssuerName);
        xmlIssuerSerial.setX509SerialNumber(xmlSerialNumber);
        
        return xmlIssuerSerial;
    }
    
    /**
     * Build an {@link X509SKI} containing the subject key identifier extension value contained within
     * a certificate.
     * 
     * @param javaCert the Java X509Certificate from which to extract the subject key identifier value.
     * @return a new X509SKI object, or null if the certificate did not contain the subject key identifier extension
     */
    public static X509SKI buildX509SKI(X509Certificate javaCert) {
        byte[] skiPlainValue = X509Util.getSubjectKeyIdentifier(javaCert);
        if (skiPlainValue == null || skiPlainValue.length == 0) {
            return null;
        }
        
        X509SKI xmlSKI = (X509SKI) Configuration.getBuilderFactory()
            .getBuilder(X509SKI.DEFAULT_ELEMENT_NAME)
            .buildObject(X509SKI.DEFAULT_ELEMENT_NAME);
        xmlSKI.setValue(Base64.encodeBytes(skiPlainValue));
        
        return xmlSKI;
    }
    
    /**
     * Build an {@link X509Digest} containing the digest of the specified certificate.
     * 
     * @param javaCert the Java X509Certificate to digest
     * @param algorithmURI  digest algorithm URI
     * @return a new X509Digest object
     * @throws NoSuchAlgorithmException if the algorithm specified cannot be used
     * @throws CertificateEncodingException if the certificate cannot be encoded
     */
    public static X509Digest buildX509Digest(X509Certificate javaCert, String algorithmURI)
            throws NoSuchAlgorithmException, CertificateEncodingException {
        
        String jceAlg = SecurityHelper.getAlgorithmIDFromURI(algorithmURI);
        if (jceAlg == null) {
            throw new NoSuchAlgorithmException("No JCE algorithm found for " + algorithmURI);
        }
        MessageDigest md = MessageDigest.getInstance(jceAlg);
        byte[] hash = md.digest(javaCert.getEncoded());
        
        X509Digest xmlDigest = (X509Digest) Configuration.getBuilderFactory()
            .getBuilder(X509Digest.DEFAULT_ELEMENT_NAME)
            .buildObject(X509Digest.DEFAULT_ELEMENT_NAME);
        xmlDigest.setAlgorithm(algorithmURI);
        xmlDigest.setValue(Base64.encodeBytes(hash));
        
        return xmlDigest;
    }
    
    /**
     * Converts a Java DSA or RSA public key into the corresponding XMLObject and stores it
     * in a {@link KeyInfo} in a new {@link KeyValue} element.
     * 
     * As input, only supports {@link PublicKey}s which are instances of either
     * {@link java.security.interfaces.DSAPublicKey} or
     * {@link java.security.interfaces.RSAPublicKey}
     * 
     * @param keyInfo the {@link KeyInfo} element to which to add the key
     * @param pk the native Java {@link PublicKey} to add
     * @throws IllegalArgumentException thrown if an unsupported public key
     *          type is passed
     */
    public static void addPublicKey(KeyInfo keyInfo, PublicKey pk) throws IllegalArgumentException {
        KeyValue keyValue = (KeyValue) Configuration.getBuilderFactory()
            .getBuilder(KeyValue.DEFAULT_ELEMENT_NAME)
            .buildObject(KeyValue.DEFAULT_ELEMENT_NAME);
        
        if (pk instanceof RSAPublicKey) {
            keyValue.setRSAKeyValue(buildRSAKeyValue((RSAPublicKey) pk));
        } else if (pk instanceof DSAPublicKey) {
            keyValue.setDSAKeyValue(buildDSAKeyValue((DSAPublicKey) pk));
        } else {
           throw new IllegalArgumentException("Only RSAPublicKey and DSAPublicKey are supported");
        }
        
        keyInfo.getKeyValues().add(keyValue);
    }
    
    /**
     * Builds an {@link RSAKeyValue} XMLObject from the Java security RSA public key type.
     * 
     * @param rsaPubKey a native Java {@link RSAPublicKey}
     * @return an {@link RSAKeyValue} XMLObject
     */
    public static RSAKeyValue buildRSAKeyValue(RSAPublicKey rsaPubKey) {
        XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();
        RSAKeyValue rsaKeyValue = (RSAKeyValue) builderFactory
            .getBuilder(RSAKeyValue.DEFAULT_ELEMENT_NAME)
            .buildObject(RSAKeyValue.DEFAULT_ELEMENT_NAME);
        Modulus modulus = (Modulus) builderFactory
            .getBuilder(Modulus.DEFAULT_ELEMENT_NAME)
            .buildObject(Modulus.DEFAULT_ELEMENT_NAME);
        Exponent exponent = (Exponent) builderFactory
            .getBuilder(Exponent.DEFAULT_ELEMENT_NAME)
            .buildObject(Exponent.DEFAULT_ELEMENT_NAME);
        
        modulus.setValueBigInt(rsaPubKey.getModulus());
        rsaKeyValue.setModulus(modulus);
        
        exponent.setValueBigInt(rsaPubKey.getPublicExponent());
        rsaKeyValue.setExponent(exponent);
        
        return rsaKeyValue;
    }
    
    /**
     * Builds a {@link DSAKeyValue} XMLObject from the Java security DSA public key type.
     * 
     * @param dsaPubKey a native Java {@link DSAPublicKey}
     * @return an {@link DSAKeyValue} XMLObject
     */
    public static DSAKeyValue buildDSAKeyValue(DSAPublicKey dsaPubKey) {
        XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();
        DSAKeyValue dsaKeyValue = (DSAKeyValue) builderFactory
            .getBuilder(DSAKeyValue.DEFAULT_ELEMENT_NAME)
            .buildObject(DSAKeyValue.DEFAULT_ELEMENT_NAME);
        Y y = (Y) builderFactory.getBuilder(Y.DEFAULT_ELEMENT_NAME).buildObject(Y.DEFAULT_ELEMENT_NAME);
        G g = (G) builderFactory.getBuilder(G.DEFAULT_ELEMENT_NAME).buildObject(G.DEFAULT_ELEMENT_NAME);
        P p = (P) builderFactory.getBuilder(P.DEFAULT_ELEMENT_NAME).buildObject(P.DEFAULT_ELEMENT_NAME);
        Q q = (Q) builderFactory.getBuilder(Q.DEFAULT_ELEMENT_NAME).buildObject(Q.DEFAULT_ELEMENT_NAME);
        
        y.setValueBigInt(dsaPubKey.getY());
        dsaKeyValue.setY(y);
        
        g.setValueBigInt(dsaPubKey.getParams().getG());
        dsaKeyValue.setG(g);
        
        p.setValueBigInt(dsaPubKey.getParams().getP());
        dsaKeyValue.setP(p);
        
        q.setValueBigInt(dsaPubKey.getParams().getQ());
        dsaKeyValue.setQ(q);
        
        return dsaKeyValue;
    }

    /**
     * Converts a Java public key into a {@link DEREncodedKeyValue} element and adds it to
     * a {@link KeyInfo}.
     * 
     * @param keyInfo the {@link KeyInfo} element to which to add the key
     * @param pk the native Java {@link PublicKey} to add
     * @throws NoSuchAlgorithmException if the key type is unsupported
     * @throws InvalidKeySpecException if the key type does not support X.509 SPKI encoding
     */
    public static void addDEREncodedPublicKey(KeyInfo keyInfo, PublicKey pk)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        DEREncodedKeyValue keyValue = (DEREncodedKeyValue) Configuration.getBuilderFactory()
            .getBuilder(DEREncodedKeyValue.DEFAULT_ELEMENT_NAME)
            .buildObject(DEREncodedKeyValue.DEFAULT_ELEMENT_NAME);
        
        KeyFactory keyFactory = KeyFactory.getInstance(pk.getAlgorithm());
        X509EncodedKeySpec keySpec = keyFactory.getKeySpec(pk, X509EncodedKeySpec.class);
        keyValue.setValue(Base64.encodeBytes(keySpec.getEncoded()));
        keyInfo.getXMLObjects().add(keyValue);
    }    
    
    /**
     * Extracts all the public keys within the given {@link KeyInfo}'s {@link KeyValue}s and
     * {@link DEREncodedKeyValue}s.
     * 
     * @param keyInfo {@link KeyInfo} to extract the keys from
     * 
     * @return a list of native Java {@link PublicKey} objects
     * 
     * @throws KeyException thrown if the given key data can not be converted into {@link PublicKey}
     */
    public static List getPublicKeys(KeyInfo keyInfo) throws KeyException{
        List keys = new LinkedList();

        if (keyInfo == null) {
            return keys;
        }
        
        for (KeyValue keyDescriptor : keyInfo.getKeyValues()) {
            keys.add(getKey(keyDescriptor));
        }

        for (XMLObject keyDescriptor : keyInfo.getXMLObjects(DEREncodedKeyValue.DEFAULT_ELEMENT_NAME)) {
            keys.add(getKey((DEREncodedKeyValue) keyDescriptor));
        }
        
        return keys;
    }

    /**
     * Extracts the DSA or RSA public key within the {@link KeyValue}.
     * 
     * @param keyValue the {@link KeyValue} to extract the key from
     * 
     * @return a native Java security {@link java.security.Key} object
     * 
     * @throws KeyException thrown if the given key data can not be converted into {@link PublicKey}
     */
    public static PublicKey getKey(KeyValue keyValue) throws KeyException{
        if(keyValue.getDSAKeyValue() != null){
            return getDSAKey(keyValue.getDSAKeyValue());
        }else if(keyValue.getRSAKeyValue() != null){
            return getRSAKey(keyValue.getRSAKeyValue());
        }else{
            return null;
        }
    }
    
    /**
     * Builds an DSA key from a {@link DSAKeyValue} element.  The element must contain values
     * for all required DSA public key parameters, including values for shared key family
     * values P, Q and G.
     * 
     * @param keyDescriptor the {@link DSAKeyValue} key descriptor
     * 
     * @return a new {@link DSAPublicKey} instance of {@link PublicKey}
     * 
     * @throws KeyException thrown if the key algorithm is not supported by the JCE or the key spec does not
     *             contain valid information
     */
    public static PublicKey getDSAKey(DSAKeyValue keyDescriptor) throws KeyException {
        if (! hasCompleteDSAParams(keyDescriptor)) {
            throw new KeyException("DSAKeyValue element did not contain at least one of DSA parameters P, Q or G");
        }
        
        BigInteger gComponent = keyDescriptor.getG().getValueBigInt();
        BigInteger pComponent = keyDescriptor.getP().getValueBigInt();
        BigInteger qComponent = keyDescriptor.getQ().getValueBigInt();

        DSAParams  dsaParams = new DSAParameterSpec(pComponent, qComponent, gComponent);
        return getDSAKey(keyDescriptor, dsaParams);
    }
    
    /**
     * Builds a DSA key from an {@link DSAKeyValue} element and the supplied Java {@link DSAParams},
     * which supplies key material from a shared key family.
     * 
     * @param keyDescriptor the {@link DSAKeyValue} key descriptor
     * @param dsaParams the {@link DSAParams} DSA key family parameters
     * 
     * @return a new {@link DSAPublicKey} instance of {@link PublicKey}
     * 
     * @throws KeyException thrown if the key algorithm is not supported by the JCE or the key spec does not
     *             contain valid information
     */
    public static PublicKey getDSAKey(DSAKeyValue keyDescriptor, DSAParams dsaParams) throws KeyException {
        BigInteger yComponent = keyDescriptor.getY().getValueBigInt();

        DSAPublicKeySpec keySpec = 
            new DSAPublicKeySpec(yComponent, dsaParams.getP(), dsaParams.getQ(), dsaParams.getG());
        return buildKey(keySpec, "DSA");
    }
    
    /**
     * Check whether the specified {@link DSAKeyValue} element has the all optional DSA
     * values which can be shared amongst many keys in a DSA "key family", and
     * are presumed to be known from context.
     * 
     * @param keyDescriptor the {@link DSAKeyValue} element to check
     * @return true if all parameters are present and non-empty, false otherwise
     */
    public static boolean hasCompleteDSAParams(DSAKeyValue keyDescriptor) {
        if (       keyDescriptor.getG() == null || DatatypeHelper.isEmpty(keyDescriptor.getG().getValue())
                || keyDescriptor.getP() == null || DatatypeHelper.isEmpty(keyDescriptor.getP().getValue())
                || keyDescriptor.getQ() == null || DatatypeHelper.isEmpty(keyDescriptor.getQ().getValue())
        ) {
            return false;
        }
        return true;
    }

    /**
     * Builds an RSA key from an {@link RSAKeyValue} element.
     * 
     * @param keyDescriptor the {@link RSAKeyValue} key descriptor
     * 
     * @return a new {@link RSAPublicKey} instance of {@link PublicKey}
     * 
     * @throws KeyException thrown if the key algorithm is not supported by the JCE or the key spec does not
     *             contain valid information
     */
    public static PublicKey getRSAKey(RSAKeyValue keyDescriptor) throws KeyException {
        BigInteger modulus = keyDescriptor.getModulus().getValueBigInt();
        BigInteger exponent = keyDescriptor.getExponent().getValueBigInt();

        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent);
        return buildKey(keySpec, "RSA");
    }
    
    /**
     * Decode a base64-encoded ds:CryptoBinary value to a native Java BigInteger type.
     *
     * @param base64Value base64-encoded CryptoBinary value
     * @return the decoded BigInteger
     */
    public static final BigInteger decodeBigIntegerFromCryptoBinary(String base64Value) {
       return new BigInteger(1, Base64.decode(base64Value));
    }
    
    /**
     * Encode a native Java BigInteger type to a base64-encoded ds:CryptoBinary value.
     *
     * @param bigInt the BigInteger value
     * @return the encoded CryptoBinary value
     */
    public static final String encodeCryptoBinaryFromBigInteger(BigInteger bigInt) {
        // This code is really complicated, for now just use the Apache xmlsec lib code directly.
        byte[] bigIntBytes = org.apache.xml.security.utils.Base64.encode(bigInt, bigInt.bitLength());
        return Base64.encodeBytes(bigIntBytes);
    }

    /**
     * Generates a public key from the given key spec.
     * 
     * @param keySpec {@link KeySpec} specification for the key
     * @param keyAlgorithm key generation algorithm, only DSA and RSA supported
     * 
     * @return the generated {@link PublicKey}
     * 
     * @throws KeyException thrown if the key algorithm is not supported by the JCE or the key spec does not
     *             contain valid information
     */
    protected static PublicKey buildKey(KeySpec keySpec, String keyAlgorithm) throws KeyException {
        Logger log = getLogger();
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
            return keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            log.error(keyAlgorithm + " algorithm is not supported by this VM", e);
            throw new KeyException(keyAlgorithm + "algorithm is not supported by the JCE", e);
        } catch (InvalidKeySpecException e) {
            log.error("Invalid key information", e);
            throw new KeyException("Invalid key information", e);
        }
    }

    /**
     * Extracts the public key within the {@link DEREncodedKeyValue}.
     * 
     * @param keyValue the {@link DEREncodedKeyValue} to extract the key from
     * 
     * @return a native Java security {@link java.security.Key} object
     * 
     * @throws KeyException thrown if the given key data cannot be converted into {@link PublicKey}
     */
    public static PublicKey getKey(DEREncodedKeyValue keyValue) throws KeyException{
        String[] supportedKeyTypes = { "RSA", "DSA", "EC"};
        
        if (keyValue.getValue() == null) {
            throw new KeyException("No data found in key value element");
        }
        byte[] encodedKey = Base64.decode(keyValue.getValue());

        // Iterate over the supported key types until one produces a public key.
        for (String keyType : supportedKeyTypes) {
            try {
                KeyFactory keyFactory = KeyFactory.getInstance(keyType);
                X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey);
                PublicKey publicKey = keyFactory.generatePublic(keySpec);
                if (publicKey != null) {
                    return publicKey;
                }
            } catch (NoSuchAlgorithmException e) {
                // ignore
            } catch (InvalidKeySpecException e) {
                // ignore
            }
        }
        throw new KeyException("DEREncodedKeyValue did not contain a supported key type");
    }
    
    /**
     * Get the Java certificate factory singleton.
     * 
     * @return {@link CertificateFactory} the factory used to create X509 certificate objects
     * 
     * @throws CertificateException thrown if the factory can not be created
     */
    protected static CertificateFactory getX509CertFactory() throws CertificateException {

        if (x509CertFactory == null) {
            x509CertFactory = CertificateFactory.getInstance("X.509");
        }

        return x509CertFactory;
    }
    
    /**
     * Get an SLF4J Logger.
     * 
     * @return a Logger instance
     */
    private static Logger getLogger() {
        return LoggerFactory.getLogger(KeyInfoHelper.class);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy