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

org.opensaml.xmlsec.keyinfo.KeyInfoSupport Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show 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.xmlsec.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.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.LinkedList;
import java.util.List;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import net.shibboleth.utilities.java.support.codec.Base64Support;
import net.shibboleth.utilities.java.support.logic.Constraint;

import org.opensaml.core.xml.XMLObjectBuilder;
import org.opensaml.core.xml.XMLObjectBuilderFactory;
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.x509.X509Support;
import org.opensaml.xmlsec.algorithm.AlgorithmSupport;
import org.opensaml.xmlsec.signature.DEREncodedKeyValue;
import org.opensaml.xmlsec.signature.DSAKeyValue;
import org.opensaml.xmlsec.signature.Exponent;
import org.opensaml.xmlsec.signature.G;
import org.opensaml.xmlsec.signature.KeyInfo;
import org.opensaml.xmlsec.signature.KeyName;
import org.opensaml.xmlsec.signature.KeyValue;
import org.opensaml.xmlsec.signature.Modulus;
import org.opensaml.xmlsec.signature.P;
import org.opensaml.xmlsec.signature.Q;
import org.opensaml.xmlsec.signature.RSAKeyValue;
import org.opensaml.xmlsec.signature.X509Data;
import org.opensaml.xmlsec.signature.X509Digest;
import org.opensaml.xmlsec.signature.X509IssuerName;
import org.opensaml.xmlsec.signature.X509IssuerSerial;
import org.opensaml.xmlsec.signature.X509SKI;
import org.opensaml.xmlsec.signature.X509SerialNumber;
import org.opensaml.xmlsec.signature.X509SubjectName;
import org.opensaml.xmlsec.signature.Y;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Strings;

/**
 * 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 KeyInfoSupport {

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

    /** Constructor. */
    protected KeyInfoSupport() {

    }

    /**
     * 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
     */
    @Nonnull public static List getKeyNames(@Nullable final 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(@Nonnull final KeyInfo keyInfo, @Nullable final String keyNameValue) {
        Constraint.isNotNull(keyInfo, "KeyInfo cannot be null");

        XMLObjectBuilder keyNameBuilder = (XMLObjectBuilder)
                XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(KeyName.DEFAULT_ELEMENT_NAME);
        KeyName keyName = Constraint.isNotNull(keyNameBuilder, "KeyName builder not available").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.
     */
    @Nonnull public static List getCertificates(@Nullable final KeyInfo keyInfo)
            throws CertificateException {
        List certList = new LinkedList<>();

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

        List x509Datas = keyInfo.getX509Datas();
        for (X509Data x509Data : x509Datas) {
            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.
     */
    @Nonnull public static List getCertificates(@Nullable final X509Data x509Data)
            throws CertificateException {
        List certList = new LinkedList<>();

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

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

        return certList;
    }

    /**
     * Convert an {@link org.opensaml.xmlsec.signature.X509Certificate} into a native Java representation.
     * 
     * @param xmlCert an {@link org.opensaml.xmlsec.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.
     */
    @Nullable public static X509Certificate getCertificate(
            @Nullable final org.opensaml.xmlsec.signature.X509Certificate xmlCert) throws CertificateException {

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

        return X509Support.decodeCertificate(xmlCert.getValue());
    }

    /**
     * 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 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
     */
    @Nonnull public static List getCRLs(@Nullable final KeyInfo keyInfo) throws CRLException {
        List crlList = new LinkedList<>();

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

        List x509Datas = keyInfo.getX509Datas();
        for (X509Data x509Data : x509Datas) {
            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
     */
    @Nonnull public static List getCRLs(@Nullable final X509Data x509Data) throws CRLException {
        List crlList = new LinkedList<>();

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

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

        return crlList;
    }

    /**
     * Convert an {@link org.opensaml.xmlsec.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}
     */
    @Nullable public static X509CRL getCRL(@Nullable final org.opensaml.xmlsec.signature.X509CRL xmlCRL)
            throws CRLException {

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

        try {
            return X509Support.decodeCRL(xmlCRL.getValue());
        } catch (CertificateException e) {
            throw new CRLException("Certificate error attempting to decode CRL", e);
        }
    }

    /**
     * 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(@Nonnull final KeyInfo keyInfo, @Nonnull final X509Certificate cert)
            throws CertificateEncodingException {
        Constraint.isNotNull(keyInfo, "KeyInfo cannot be null");
        
        X509Data x509Data;
        if (keyInfo.getX509Datas().size() == 0) {
            XMLObjectBuilder x509DataBuilder = (XMLObjectBuilder)
                    XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(X509Data.DEFAULT_ELEMENT_NAME);
            x509Data = Constraint.isNotNull(x509DataBuilder, "X509Data builder not available").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(@Nonnull final KeyInfo keyInfo, @Nonnull final X509CRL crl) throws CRLException {
        Constraint.isNotNull(keyInfo, "KeyInfo cannot be null");
        
        X509Data x509Data;
        if (keyInfo.getX509Datas().size() == 0) {
            XMLObjectBuilder x509DataBuilder = (XMLObjectBuilder)
                    XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(X509Data.DEFAULT_ELEMENT_NAME);
            x509Data = Constraint.isNotNull(x509DataBuilder, "X509Data builder not available").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.xmlsec.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.xmlsec.signature.X509Certificate} XMLObject
     * @throws CertificateEncodingException thrown when there is an error converting the Java certificate representation
     *             to the XMLObject representation
     */
    @Nonnull public static org.opensaml.xmlsec.signature.X509Certificate buildX509Certificate(X509Certificate cert)
            throws CertificateEncodingException {
        Constraint.isNotNull(cert, "X.509 certificate cannot be null");
        
        XMLObjectBuilder xmlCertBuilder =
                (XMLObjectBuilder)
                    XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(
                        org.opensaml.xmlsec.signature.X509Certificate.DEFAULT_ELEMENT_NAME);
        org.opensaml.xmlsec.signature.X509Certificate xmlCert =
                Constraint.isNotNull(xmlCertBuilder, "X509Certificate builder not available").buildObject(
                        org.opensaml.xmlsec.signature.X509Certificate.DEFAULT_ELEMENT_NAME);
        xmlCert.setValue(Base64Support.encode(cert.getEncoded(), Base64Support.CHUNKED));

        return xmlCert;
    }

    /**
     * Builds an {@link org.opensaml.xmlsec.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.xmlsec.signature.X509CRL} XMLObject
     * @throws CRLException thrown when there is an error converting the Java CRL representation to the XMLObject
     *             representation
     */
    @Nonnull public static org.opensaml.xmlsec.signature.X509CRL buildX509CRL(X509CRL crl) throws CRLException {
        Constraint.isNotNull(crl, "X.509 CRL cannot be null");
        
        XMLObjectBuilder xmlCRLBuilder =
                (XMLObjectBuilder)
                    XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(
                        org.opensaml.xmlsec.signature.X509CRL.DEFAULT_ELEMENT_NAME);
        org.opensaml.xmlsec.signature.X509CRL xmlCRL =
                Constraint.isNotNull(xmlCRLBuilder, "X509Certificate builder not available").buildObject(
                        org.opensaml.xmlsec.signature.X509CRL.DEFAULT_ELEMENT_NAME);
        xmlCRL.setValue(Base64Support.encode(crl.getEncoded(), Base64Support.CHUNKED));

        return xmlCRL;
    }

    /**
     * Build an {@link X509SubjectName} containing a given subject name.
     * 
     * @param subjectName the name content
     * @return the new X509SubjectName
     */
    @Nonnull public static X509SubjectName buildX509SubjectName(@Nullable final String subjectName) {
        XMLObjectBuilder xmlSubjectNameBuilder = (XMLObjectBuilder)
                XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(X509SubjectName.DEFAULT_ELEMENT_NAME);
        X509SubjectName xmlSubjectName =
                Constraint.isNotNull(xmlSubjectNameBuilder, "X509SubjectName builder not available").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
     */
    @Nonnull public static X509IssuerSerial buildX509IssuerSerial(@Nullable final String issuerName,
            @Nullable final BigInteger serialNumber) {
        XMLObjectBuilderFactory builderFactory = XMLObjectProviderRegistrySupport.getBuilderFactory();
        
        XMLObjectBuilder xmlIssuerNameBuilder = (XMLObjectBuilder)
                builderFactory.getBuilder(X509IssuerName.DEFAULT_ELEMENT_NAME);
        X509IssuerName xmlIssuerName =
                Constraint.isNotNull(xmlIssuerNameBuilder, "X509IssuerName builder not available").buildObject(
                        X509IssuerName.DEFAULT_ELEMENT_NAME);
        xmlIssuerName.setValue(issuerName);

        XMLObjectBuilder xmlSerialNumberBuilder = (XMLObjectBuilder)
                builderFactory.getBuilder(X509SerialNumber.DEFAULT_ELEMENT_NAME);
        X509SerialNumber xmlSerialNumber =
                Constraint.isNotNull(xmlSerialNumberBuilder, "X509SerialNumber builder not available").buildObject(
                        X509SerialNumber.DEFAULT_ELEMENT_NAME);
        xmlSerialNumber.setValue(serialNumber);

        XMLObjectBuilder xmlIssuerSerialBuilder = (XMLObjectBuilder)
                builderFactory.getBuilder(X509IssuerSerial.DEFAULT_ELEMENT_NAME);
        X509IssuerSerial xmlIssuerSerial =
                Constraint.isNotNull(xmlIssuerSerialBuilder, "X509IssuerSerial builder not available").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
     */
    @Nullable public static X509SKI buildX509SKI(@Nonnull final X509Certificate javaCert) {
        byte[] skiPlainValue = X509Support.getSubjectKeyIdentifier(javaCert);
        if (skiPlainValue == null || skiPlainValue.length == 0) {
            return null;
        }

        XMLObjectBuilder xmlSKIBuilder = (XMLObjectBuilder)
                XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(X509SKI.DEFAULT_ELEMENT_NAME);
        X509SKI xmlSKI = Constraint.isNotNull(xmlSKIBuilder, "X509SKI builder not available").buildObject(
                X509SKI.DEFAULT_ELEMENT_NAME);
        xmlSKI.setValue(Base64Support.encode(skiPlainValue, Base64Support.CHUNKED));

        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
     */
    @Nonnull public static X509Digest buildX509Digest(@Nonnull final X509Certificate javaCert,
            @Nonnull final String algorithmURI) throws NoSuchAlgorithmException, CertificateEncodingException {
        Constraint.isNotNull(javaCert, "Certificate cannot be null");

        String jceAlg = AlgorithmSupport.getAlgorithmID(algorithmURI);
        if (jceAlg == null) {
            throw new NoSuchAlgorithmException("No JCE algorithm found for " + algorithmURI);
        }
        MessageDigest md = MessageDigest.getInstance(jceAlg);
        byte[] hash = md.digest(javaCert.getEncoded());
        
        XMLObjectBuilder builder = (XMLObjectBuilder)
                XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(X509Digest.DEFAULT_ELEMENT_NAME);
        X509Digest xmlDigest = Constraint.isNotNull(builder, "X509Digest builder not available").buildObject(
                X509Digest.DEFAULT_ELEMENT_NAME);
        xmlDigest.setAlgorithm(algorithmURI);
        xmlDigest.setValue(Base64Support.encode(hash, Base64Support.CHUNKED));
        
        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
     */
    public static void addPublicKey(@Nonnull final KeyInfo keyInfo, @Nullable final PublicKey pk) {
        Constraint.isNotNull(keyInfo, "KeyInfo cannot be null");
        
        XMLObjectBuilder keyValueBuilder = (XMLObjectBuilder)
                XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(KeyValue.DEFAULT_ELEMENT_NAME);
        KeyValue keyValue = Constraint.isNotNull(keyValueBuilder, "KeyValue builder not available").buildObject(
                KeyValue.DEFAULT_ELEMENT_NAME);

        // TODO handle ECKeyValue
        
        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
     */
    @Nonnull public static RSAKeyValue buildRSAKeyValue(@Nonnull final RSAPublicKey rsaPubKey) {
        Constraint.isNotNull(rsaPubKey, "RSA public key cannot be null");
        
        XMLObjectBuilderFactory builderFactory = XMLObjectProviderRegistrySupport.getBuilderFactory();
        
        XMLObjectBuilder rsaKeyValueBuilder = (XMLObjectBuilder)
                builderFactory.getBuilder(RSAKeyValue.DEFAULT_ELEMENT_NAME);
        RSAKeyValue rsaKeyValue =
                Constraint.isNotNull(rsaKeyValueBuilder, "RSAKeyValue builder not available").buildObject(
                        RSAKeyValue.DEFAULT_ELEMENT_NAME);
        
        XMLObjectBuilder modulusBuilder = (XMLObjectBuilder)
                builderFactory.getBuilder(Modulus.DEFAULT_ELEMENT_NAME);
        Modulus modulus = Constraint.isNotNull(modulusBuilder, "Modulus builder not available").buildObject(
                Modulus.DEFAULT_ELEMENT_NAME);
        
        XMLObjectBuilder exponentBuilder = (XMLObjectBuilder)
                builderFactory.getBuilder(Exponent.DEFAULT_ELEMENT_NAME);
        Exponent exponent = Constraint.isNotNull(exponentBuilder, "Exponent builder not available").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
     */
    @Nonnull public static DSAKeyValue buildDSAKeyValue(@Nonnull final DSAPublicKey dsaPubKey) {
        Constraint.isNotNull(dsaPubKey, "DSA public key cannot be null");
        
        XMLObjectBuilderFactory builderFactory = XMLObjectProviderRegistrySupport.getBuilderFactory();

        XMLObjectBuilder dsaKeyValueBuilder = (XMLObjectBuilder)
                builderFactory.getBuilder(DSAKeyValue.DEFAULT_ELEMENT_NAME);
        DSAKeyValue dsaKeyValue =
                Constraint.isNotNull(dsaKeyValueBuilder, "DSAKeyValue builder not available").buildObject(
                        DSAKeyValue.DEFAULT_ELEMENT_NAME);

        XMLObjectBuilder yBuilder = (XMLObjectBuilder) builderFactory.getBuilder(Y.DEFAULT_ELEMENT_NAME);
        XMLObjectBuilder gBuilder = (XMLObjectBuilder) builderFactory.getBuilder(G.DEFAULT_ELEMENT_NAME);
        XMLObjectBuilder

pBuilder = (XMLObjectBuilder

) builderFactory.getBuilder(P.DEFAULT_ELEMENT_NAME); XMLObjectBuilder qBuilder = (XMLObjectBuilder) builderFactory.getBuilder(Q.DEFAULT_ELEMENT_NAME); Y y = Constraint.isNotNull(yBuilder, "Y builder not available").buildObject(Y.DEFAULT_ELEMENT_NAME); G g = Constraint.isNotNull(gBuilder, "G builder not available").buildObject(G.DEFAULT_ELEMENT_NAME); P p = Constraint.isNotNull(pBuilder, "P builder not available").buildObject(P.DEFAULT_ELEMENT_NAME); Q q = Constraint.isNotNull(qBuilder, "Q builder not available").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 the corresponding XMLObject and stores it in a {@link KeyInfo} in a * new {@link DEREncodedKeyValue} element. * * @param keyInfo the {@link KeyInfo} element to which to add the key * @param pk the native Java {@link PublicKey} to convert * @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(@Nonnull final KeyInfo keyInfo, @Nonnull final PublicKey pk) throws NoSuchAlgorithmException, InvalidKeySpecException { Constraint.isNotNull(keyInfo, "KeyInfo cannot be null"); Constraint.isNotNull(pk, "Public key cannot be null"); XMLObjectBuilder builder = (XMLObjectBuilder) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder( DEREncodedKeyValue.DEFAULT_ELEMENT_NAME); DEREncodedKeyValue keyValue = Constraint.isNotNull(builder, "DEREncodedKeyValue builder not available").buildObject(DEREncodedKeyValue.DEFAULT_ELEMENT_NAME); KeyFactory keyFactory = KeyFactory.getInstance(pk.getAlgorithm()); X509EncodedKeySpec keySpec = keyFactory.getKeySpec(pk, X509EncodedKeySpec.class); keyValue.setValue(Base64Support.encode(keySpec.getEncoded(), Base64Support.CHUNKED)); keyInfo.getDEREncodedKeyValues().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 out of * * @return a list of native Java {@link PublicKey} objects * * @throws KeyException thrown if the given key data can not be converted into {@link PublicKey} */ @Nonnull public static List getPublicKeys(@Nullable final KeyInfo keyInfo) throws KeyException { // TODO support ECKeyValue and DEREncodedKeyValue List keys = new LinkedList<>(); if (keyInfo == null) { return keys; } for (KeyValue keyDescriptor : keyInfo.getKeyValues()) { PublicKey newKey = getKey(keyDescriptor); if (newKey != null) { keys.add(newKey); } } for (DEREncodedKeyValue keyDescriptor : keyInfo.getDEREncodedKeyValues()) { PublicKey newKey = getKey(keyDescriptor); if (newKey != null) { keys.add(newKey); } } 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} */ @Nullable public static PublicKey getKey(@Nonnull final KeyValue keyValue) throws KeyException { Constraint.isNotNull(keyValue, "KeyValue cannot be null"); 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 */ @Nonnull public static PublicKey getDSAKey(@Nonnull final 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 */ @Nonnull public static PublicKey getDSAKey(@Nonnull final DSAKeyValue keyDescriptor, @Nonnull final DSAParams dsaParams) throws KeyException { Constraint.isNotNull(keyDescriptor, "DSAKeyValue cannot be null"); Constraint.isNotNull(dsaParams, "DSAParams cannot be null"); 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(@Nullable final DSAKeyValue keyDescriptor) { if (keyDescriptor == null || keyDescriptor.getG() == null || Strings.isNullOrEmpty(keyDescriptor.getG().getValue()) || keyDescriptor.getP() == null || Strings.isNullOrEmpty(keyDescriptor.getP().getValue()) || keyDescriptor.getQ() == null || Strings.isNullOrEmpty(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 */ @Nonnull public static PublicKey getRSAKey(@Nonnull final RSAKeyValue keyDescriptor) throws KeyException { Constraint.isNotNull(keyDescriptor, "RSAKeyValue cannot be null"); 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 */ @Nonnull public static final BigInteger decodeBigIntegerFromCryptoBinary(@Nonnull final String base64Value) { return new BigInteger(1, Base64Support.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 */ @Nonnull public static final String encodeCryptoBinaryFromBigInteger(@Nonnull final BigInteger bigInt) { Constraint.isNotNull(bigInt, "BigInteger cannot be null"); // 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 Base64Support.encode(bigIntBytes, Base64Support.UNCHUNKED); } /** * 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 */ @Nonnull protected static PublicKey buildKey(@Nonnull final KeySpec keySpec, @Nonnull final String keyAlgorithm) throws KeyException { final Logger log = getLogger(); try { final KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm); return keyFactory.generatePublic(keySpec); } catch (final NoSuchAlgorithmException e) { final String msg = keyAlgorithm + " algorithm is not supported by this JCE"; log.error(msg, e); throw new KeyException(msg, e); } catch (final 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 can not be converted into {@link PublicKey} */ @Nonnull public static PublicKey getKey(@Nonnull final DEREncodedKeyValue keyValue) throws KeyException{ final String[] supportedKeyTypes = { "RSA", "DSA", "EC"}; Constraint.isNotNull(keyValue, "DEREncodedKeyValue cannot be null"); if (keyValue.getValue() == null) { throw new KeyException("No data found in key value element"); } final byte[] encodedKey = Base64Support.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 (final NoSuchAlgorithmException | InvalidKeySpecException e) { // Do nothing, try the next type } } 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 */ @Nonnull protected static CertificateFactory getX509CertFactory() throws CertificateException { if (x509CertFactory == null) { x509CertFactory = CertificateFactory.getInstance("X.509"); } return x509CertFactory; } /** * Obtains a {@link KeyInfoGenerator} for the specified {@link Credential}. * *

* The KeyInfoGenerator returned is resolved via the supplied {@link NamedKeyInfoGeneratorManager} * and is determined by the type of the signing credential and an optional KeyInfo generator profile configuration * name. If the latter is ommited, the default manager ({@link NamedKeyInfoGeneratorManager#getDefaultManager()}) * of the security configuration's named generator manager will be used. *

* * @param credential the credential for which a generator is desired * @param manager the NamedKeyInfoGeneratorManager instance to use * @param keyInfoProfileName the named KeyInfoGeneratorManager configuration to use (may be null) * @return a KeyInfoGenerator appropriate for the specified credential */ @Nullable public static KeyInfoGenerator getKeyInfoGenerator(@Nonnull final Credential credential, @Nonnull final NamedKeyInfoGeneratorManager manager, @Nullable final String keyInfoProfileName) { Constraint.isNotNull(credential, "Credential may not be null"); Constraint.isNotNull(manager, "NamedKeyInfoGeneratorManager may not be null"); Logger log = getLogger(); KeyInfoGeneratorFactory factory = null; if (keyInfoProfileName != null) { log.trace("Resolving KeyInfoGeneratorFactory using profile name: {}", keyInfoProfileName); factory = manager.getFactory(keyInfoProfileName, credential); } else { log.trace("Resolving KeyInfoGeneratorFactory using default manager: {}", keyInfoProfileName); factory = manager.getDefaultManager().getFactory(credential); } if (factory != null) { log.trace("Found KeyInfoGeneratorFactory: {}", factory.getClass().getName()); return factory.newInstance(); } log.trace("Unable to resolve KeyInfoGeneratorFactory for credential"); return null; } /** * Get an SLF4J Logger. * * @return a Logger instance */ @Nonnull private static Logger getLogger() { return LoggerFactory.getLogger(KeyInfoSupport.class); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy