Package org.picketlink.identity.federation.core.wstrust

Source Code of org.picketlink.identity.federation.core.wstrust.WSTrustUtil

/*
* JBoss, Home of Professional Open Source.
* Copyright 2009, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This 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 software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.picketlink.identity.federation.core.wstrust;

import java.io.OutputStream;
import java.net.URI;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;

import org.apache.xml.security.encryption.EncryptedKey;
import org.apache.xml.security.encryption.XMLCipher;
import org.apache.xml.security.keys.KeyInfo;
import org.apache.xml.security.keys.content.X509Data;
import org.picketlink.identity.federation.PicketLinkLogger;
import org.picketlink.identity.federation.PicketLinkLoggerFactory;
import org.picketlink.identity.federation.core.config.STSType;
import org.picketlink.identity.federation.core.exceptions.ParsingException;
import org.picketlink.identity.federation.core.parsers.util.StaxParserUtil;
import org.picketlink.identity.federation.core.saml.v2.util.DocumentUtil;
import org.picketlink.identity.federation.core.util.Base64;
import org.picketlink.identity.federation.core.util.ProvidersUtil;
import org.picketlink.identity.federation.core.util.StringUtil;
import org.picketlink.identity.federation.core.util.SystemPropertiesUtil;
import org.picketlink.identity.federation.core.util.XMLEncryptionUtil;
import org.picketlink.identity.federation.core.util.XMLSignatureUtil;
import org.picketlink.identity.federation.core.wstrust.wrappers.Lifetime;
import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityToken;
import org.picketlink.identity.federation.ws.addressing.AttributedURIType;
import org.picketlink.identity.federation.ws.addressing.EndpointReferenceType;
import org.picketlink.identity.federation.ws.policy.AppliesTo;
import org.picketlink.identity.federation.ws.trust.BinarySecretType;
import org.picketlink.identity.federation.ws.trust.EntropyType;
import org.picketlink.identity.federation.ws.trust.OnBehalfOfType;
import org.picketlink.identity.federation.ws.trust.RenewingType;
import org.picketlink.identity.federation.ws.trust.RequestedReferenceType;
import org.picketlink.identity.federation.ws.wss.secext.AttributedString;
import org.picketlink.identity.federation.ws.wss.secext.KeyIdentifierType;
import org.picketlink.identity.federation.ws.wss.secext.SecurityTokenReferenceType;
import org.picketlink.identity.federation.ws.wss.secext.UsernameTokenType;
import org.picketlink.identity.xmlsec.w3.xmldsig.KeyInfoType;
import org.picketlink.identity.xmlsec.w3.xmldsig.KeyValueType;
import org.picketlink.identity.xmlsec.w3.xmldsig.X509CertificateType;
import org.picketlink.identity.xmlsec.w3.xmldsig.X509DataType;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
* <p>
* Utility class that provides methods for parsing/creating WS-Trust elements.
* </p>
*
* @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
*/
public class WSTrustUtil {

    private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
   
    // Set some system properties and Santuario providers. Run this block before any other class initialization.
    static {
        ProvidersUtil.ensure();
        SystemPropertiesUtil.ensure();
        String keyInfoProp = SystemPropertiesUtil.getSystemProperty("picketlink.encryption.includeKeyInfo", null);
        if (StringUtil.isNotNull(keyInfoProp)) {
            includeKeyInfoInEncryptedKey = Boolean.parseBoolean(keyInfoProp);
        }
    };
   
    /**
     * By default, we include the keyinfo in the EncryptedKey
     */
    private static boolean includeKeyInfoInEncryptedKey = true;
   
   
    /**
     * <p>
     * Creates an instance of {@code KeyIdentifierType} with the specified values.
     * </p>
     *
     * @param valueType a {@code String} representing the identifier value type.
     * @param value a {@code String} representing the identifier value.
     * @return the constructed {@code KeyIdentifierType} instance.
     */
    public static KeyIdentifierType createKeyIdentifier(String valueType, String value) {
        KeyIdentifierType keyIdentifier = new KeyIdentifierType();
        keyIdentifier.setValueType(valueType);
        keyIdentifier.setValue(value);
        return keyIdentifier;
    }

    /**
     * <p>
     * Creates an instance of {@code RequestedReferenceType} with the specified values. This method first creates a
     * {@code SecurityTokenReferenceType} with the specified key identifier and attributes and then use this reference to
     * construct the {@code RequestedReferenceType} that is returned.
     * </p>
     *
     * @param keyIdentifier the key identifier of the security token reference.
     * @param attributes the attributes to be set on the security token reference.
     * @return the constructed {@code RequestedReferenceType} instance.
     */
    public static RequestedReferenceType createRequestedReference(KeyIdentifierType keyIdentifier, Map<QName, String> attributes) {
        SecurityTokenReferenceType securityTokenReference = new SecurityTokenReferenceType();
        securityTokenReference.addAny(keyIdentifier);
        securityTokenReference.addOtherAttributes(attributes);
        RequestedReferenceType reference = new RequestedReferenceType();
        reference.setSecurityTokenReference(securityTokenReference);

        return reference;
    }

    /**
     * <p>
     * Creates an instance of {@code AppliesTo} using the specified endpoint address.
     * </p>
     *
     * @param endpointURI a {@code String} representing the endpoint URI.
     * @return the constructed {@code AppliesTo} instance.
     */
    public static AppliesTo createAppliesTo(String endpointURI) {
        AttributedURIType attributedURI = new AttributedURIType();
        attributedURI.setValue(endpointURI);
        EndpointReferenceType reference = new EndpointReferenceType();
        reference.setAddress(attributedURI);
        AppliesTo appliesTo = new AppliesTo();
        appliesTo.addAny(reference);

        return appliesTo;
    }

    /**
     * Given an address, create the WS-Addressing issuer
     *
     * @param addressUri
     * @return
     */
    public static EndpointReferenceType createIssuer(String addressUri) {
        AttributedURIType attributedURI = new AttributedURIType();
        attributedURI.setValue(addressUri);
        EndpointReferenceType endpointReference = new EndpointReferenceType();
        endpointReference.setAddress(attributedURI);
        return endpointReference;
    }

    /**
     * <p>
     * Parses the contents of the {@code AppliesTo} element and returns the address the uniquely identify the service provider.
     * </p>
     *
     * @param appliesTo the {@code AppliesTo} instance to be parsed.
     * @return the address of the service provider.
     */
    public static String parseAppliesTo(AppliesTo appliesTo) {
        EndpointReferenceType reference = null;
        for (Object obj : appliesTo.getAny()) {
            if (obj instanceof EndpointReferenceType)
                reference = (EndpointReferenceType) obj;
            else if (obj instanceof JAXBElement) {
                JAXBElement<?> element = (JAXBElement<?>) obj;
                if (element.getName().getLocalPart().equalsIgnoreCase("EndpointReference"))
                    reference = (EndpointReferenceType) element.getValue();
            }

            if (reference != null && reference.getAddress() != null)
                return reference.getAddress().getValue();
        }
        return null;
    }
   


    public static RenewingType parseRenewingType(XMLEventReader xmlEventReader) throws ParsingException {
        RenewingType renewingType = new RenewingType();

        StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
        StaxParserUtil.validate(startElement, WSTrustConstants.RENEWING);

        Attribute allowAttribute = startElement.getAttributeByName(new QName(WSTrustConstants.ALLOW));
        if (allowAttribute != null) {
            renewingType.setAllow(Boolean.parseBoolean(StaxParserUtil.getAttributeValue(allowAttribute)));
        }

        Attribute okAttribute = startElement.getAttributeByName(new QName(WSTrustConstants.OK));
        if (allowAttribute != null) {
            renewingType.setOK(Boolean.parseBoolean(StaxParserUtil.getAttributeValue(okAttribute)));
        }

        EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader);
        StaxParserUtil.validate(endElement, WSTrustConstants.RENEWING);
        return renewingType;
    }

    /**
     * <p>
     * Creates a {@code Lifetime} instance that specifies a range of time that starts at the current GMT time and has the
     * specified duration in milliseconds.
     * </p>
     *
     * @param tokenTimeout the token timeout value (in milliseconds).
     * @return the constructed {@code Lifetime} instance.
     */
    public static Lifetime createDefaultLifetime(long tokenTimeout) {
        GregorianCalendar created = new GregorianCalendar();
        GregorianCalendar expires = new GregorianCalendar();
        expires.setTimeInMillis(created.getTimeInMillis() + tokenTimeout);

        return new Lifetime(created, expires);
    }

    /**
     * <p>
     * Parses the contents of the {@code OnBehalfOf} element and returns a {@code Principal} representing the identity on behalf
     * of which the request was made.
     * </p>
     *
     * @param onBehalfOf the type that represents the {@code OnBehalfOf} element.
     * @return a {@code Principal} representing the extracted identity, or {@code null} if the contents of the
     *         {@code OnBehalfOf} element could not be parsed.
     */
    public static Principal getOnBehalfOfPrincipal(OnBehalfOfType onBehalfOf) {
        // if OnBehalfOfType contains a username token, return this username in the form of a principal.
        UsernameTokenType usernameToken = null;
        List<Object> theList = onBehalfOf.getAny();
        for (Object content : theList) {
            if (content instanceof UsernameTokenType)
                usernameToken = (UsernameTokenType) content;
            else if (content instanceof JAXBElement) {
                JAXBElement<?> element = (JAXBElement<?>) content;
                if (element.getName().getLocalPart().equalsIgnoreCase("UsernameToken"))
                    usernameToken = (UsernameTokenType) element.getValue();
            }
        }
        /*
         * Object content = onBehalfOf.getAny(); if (content instanceof UsernameTokenType) usernameToken = (UsernameTokenType)
         * content; else if (content instanceof JAXBElement) { JAXBElement<?> element = (JAXBElement<?>) content; if
         * (element.getName().getLocalPart().equalsIgnoreCase("UsernameToken")) usernameToken = (UsernameTokenType)
         * element.getValue(); }
         */
        if (usernameToken != null && usernameToken.getUsername() != null) {
            final String username = usernameToken.getUsername().getValue();
            return new Principal() {
                public String getName() {
                    return username;
                }
            };
        }

        logger.debug("Unable to parse the contents of the OnBehalfOfType: " + onBehalfOf.getAny());
       
        return null;
    }

    /**
     * <p>
     * Creates a {@code OnBehalfOfType} instance that contains a {@code UsernameTokenType}.
     * </p>
     *
     * @param username a {@code String} that represents the username of the {@code UsernameTokenType}.
     * @param id an optional {@code String} that uniquely identifies the {@code UsernameTokenType}.
     * @return the constructed {@code OnBehalfOfType} instance.
     */
    public static OnBehalfOfType createOnBehalfOfWithUsername(String username, String id) {
        AttributedString attrString = new AttributedString();
        attrString.setValue(username);
        UsernameTokenType usernameToken = new UsernameTokenType();
        usernameToken.setId(id);
        usernameToken.setUsername(attrString);
        // create the OnBehalfOfType and set the UsernameTokenType.
        OnBehalfOfType onBehalfOf = new OnBehalfOfType();
        onBehalfOf.add(usernameToken);
        return onBehalfOf;
    }

    /**
     * <p>
     * Parses the specified {@code EntropyType} and returns the first binary secret contained in the entropy.
     * </p>
     *
     * @param entropy a reference to the {@code EntropyType} that contains the binary secret.
     * @return a {@code byte[]} containing the secret; {@code null} if the specified entropy doesn't contain any secret.
     */
    public static byte[] getBinarySecret(EntropyType entropy) {
        byte[] secret = null;

        for (Object obj : entropy.getAny()) {
            if (obj instanceof BinarySecretType) {
                BinarySecretType binarySecret = (BinarySecretType) obj;
                secret = binarySecret.getValue();
                break;
            }
        }
        return secret;
    }

    /**
     * <p>
     * Marshall the {@code STSType} to an outputstream
     * </p>
     *
     * @param stsConfiguration
     * @param outputStream
     */
    public static void persistSTSConfiguration(STSType stsConfiguration, OutputStream outputStream) {
        throw new RuntimeException();

        /*
         * String pkgName = "org.picketlink.identity.federation.core.config"; Marshaller marshaller =
         * JAXBUtil.getMarshaller(pkgName); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
         * org.picketlink.identity.federation.core.config.ObjectFactory objectFactory = new
         * org.picketlink.identity.federation.core.config.ObjectFactory();
         * marshaller.marshal(objectFactory.createPicketLinkSTS(stsConfiguration), outputStream);
         */
    }

    /**
     * <p>
     * Creates a random {@code byte[]} secret of the specified size.
     * </p>
     *
     * @param size the size of the secret to be created, in bytes.
     * @return a {@code byte[]} containing the generated secret.
     */
    public static byte[] createRandomSecret(final int size) {
        SecureRandom random = new SecureRandom();
        byte[] secret = new byte[size];
        random.nextBytes(secret);
        return secret;
    }

    /**
     * <p>
     * This method implements the {@code P_SHA-1} function as defined in the <i>RFC 2246 - The TLS Protocol Version 1.0 Section
     * 5. HMAC and the pseudorandom function</i>:
     *
     * <pre>
     * P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
     *                        HMAC_hash(secret, A(2) + seed) +
     *                        HMAC_hash(secret, A(3) + seed) + ...
     *
     * Where + indicates concatenation.
     *
     * A() is defined as:
     *    A(0) = seed
     *    A(i) = HMAC_hash(secret, A(i-1))
     * </pre>
     *
     * </p>
     *
     * @param secret a {@code byte[]} that represents the HMAC secret.
     * @param seed a {@code byte[]} that represents the seed to be used.
     * @param requiredSize an {@code int} that specifies the size (in bytes) of the result.
     * @return a {@code byte[]} containing the result of the {@code P_SHA-1} function.
     * @throws NoSuchAlgorithmException if an error occurs while creating the {@code Mac} instance.
     * @throws InvalidKeyException if an error occurs while initializing the {@code Mac} instance.
     */
    public static byte[] P_SHA1(byte[] secret, byte[] seed, int requiredSize) throws NoSuchAlgorithmException,
            InvalidKeyException {
        int offset = 0, copySize;
        byte[] result = new byte[requiredSize];
        byte[] A, partialResult;

        SecretKeySpec key = new SecretKeySpec(secret, "HMACSHA1");
        Mac mac = Mac.getInstance("HMACSHA1");

        // initialize A - A(0) = seed
        A = seed;
        while (requiredSize > 0) {
            // calculate the value of A()
            mac.init(key);
            mac.update(A);
            A = mac.doFinal();

            // now calculate HMAC_hash(secret, A + seed)
            mac.reset();
            mac.init(key);
            mac.update(A);
            mac.update(seed);
            partialResult = mac.doFinal();

            // copy the necessary bytes to the result.
            copySize = Math.min(requiredSize, partialResult.length);
            System.arraycopy(partialResult, 0, result, offset, copySize);
            offset += copySize;
            requiredSize -= copySize;
        }
        return result;
    }

    /**
     * <p>
     * Creates a {@code KeyInfoType} that wraps the specified secret. If the {@code encryptionKey} parameter is not null, the
     * secret is encrypted using the specified public key before it is set in the {@code KeyInfoType}. It also create a
     * keyinfo with the information about the key used for the encryption
     * </p>
     *
     * @param secret a {@code byte[]} representing the secret (symmetric key).
     * @param encryptionKey the {@code PublicKey} that must be used to encrypt the secret.
     * @param keyWrapAlgo the key wrap algorithm to be used.
     * @return the constructed {@code KeyInfoType} instance.
     * @throws WSTrustException if an error occurs while creating the {@code KeyInfoType} object.
     */
    public static KeyInfoType createKeyInfo(byte[] secret, PublicKey encryptionKey, URI keyWrapAlgo, X509Certificate cer) throws WSTrustException {
        KeyInfoType keyInfo = null;

        // if a public key has been specified, encrypt the secret using the public key.
        if (encryptionKey != null) {
            try {
                Document document = DocumentUtil.createDocument();
                // TODO: XMLEncryptionUtil should allow for the specification of the key wrap algorithm.
                EncryptedKey key = XMLEncryptionUtil.encryptKey(document, new SecretKeySpec(secret, "AES"), encryptionKey,
                        secret.length * 8);
               
                //if certificate is not null provide the information about the key
                if(cer != null && includeKeyInfoInEncryptedKey == true) {
                  KeyInfo kiEnc = new KeyInfo(document);
                  X509Data xData = new X509Data(document);
                  xData.addIssuerSerial(cer.getIssuerDN().getName(), cer.getSerialNumber());
                  kiEnc.add(xData);
                    key.setKeyInfo(kiEnc);
                }
               
                Element encryptedKeyElement = XMLCipher.getInstance().martial(key);
                keyInfo = new KeyInfoType();
                keyInfo.addContent(encryptedKeyElement);
               
               
            } catch (Exception e) {
                throw logger.stsKeyInfoTypeCreationError(e);
            }
        } else {
            logger.stsSecretKeyNotEncrypted();
        }
        return keyInfo;
    }
   
    /**
     * <p>
     * Creates a {@code KeyInfoType} that wraps the specified secret. If the {@code encryptionKey} parameter is not null, the
     * secret is encrypted using the specified public key before it is set in the {@code KeyInfoType}.
     * </p>
     *
     * @param secret a {@code byte[]} representing the secret (symmetric key).
     * @param encryptionKey the {@code PublicKey} that must be used to encrypt the secret.
     * @param keyWrapAlgo the key wrap algorithm to be used.
     * @return the constructed {@code KeyInfoType} instance.
     * @throws WSTrustException if an error occurs while creating the {@code KeyInfoType} object.
     */
    public static KeyInfoType createKeyInfo(byte[] secret, PublicKey encryptionKey, URI keyWrapAlgo) throws WSTrustException {
      return createKeyInfo(secret, encryptionKey, keyWrapAlgo, null);
    }

    /**
     * <p>
     * Creates a {@code KeyInfoType} that wraps the specified certificate.
     * </p>
     *
     * @param certificate the {@code Certificate} to be wrapped as a {@code X509DataType} inside the {@code KeyInfoType}.
     * @return the constructed {@code KeyInfoType} object.
     * @throws WSTrustException if an error occurs while creating the {@code KeyInfoType}.
     */
    public static KeyInfoType createKeyInfo(Certificate certificate) throws WSTrustException {
        KeyInfoType keyInfo = null;
        try {
            // don't Base64 encode the certificate - JAXB marshaling performs the encoding.
            byte[] encodedCert = certificate.getEncoded();

            // first create a X509DataType that contains the encoded certificate.
            X509DataType x509 = new X509DataType();
            X509CertificateType cert = new X509CertificateType();
            cert.setEncodedCertificate(Base64.encodeBytes(encodedCert).getBytes());
            x509.add(cert);

            // set the X509DataType in the KeyInfoType.
            keyInfo = new KeyInfoType();
            keyInfo.addContent(x509);
        } catch (Exception e) {
            throw logger.stsKeyInfoTypeCreationError(e);
        }
        return keyInfo;
    }

    /**
     * <p>
     * Creates a {@code KeyValueType} that wraps the specified public key. This method supports DSA and RSA keys.
     * </p>
     *
     * @param key the {@code PublicKey} that will be represented as a {@code KeyValueType}.
     * @return the constructed {@code KeyValueType} or {@code null} if the specified key is neither a DSA nor a RSA key.
     */
    public static KeyValueType createKeyValue(PublicKey key) {
        return XMLSignatureUtil.createKeyValue(key);
    }

    public static String getServiceNameFromAppliesTo(RequestSecurityToken requestSecurityToken) {
        String serviceName = null;
        if (requestSecurityToken != null) {
            AppliesTo appliesTo = requestSecurityToken.getAppliesTo();
            if (appliesTo != null) {
                serviceName = WSTrustUtil.parseAppliesTo(appliesTo);
            }
        }
        return serviceName;
    }
}
TOP

Related Classes of org.picketlink.identity.federation.core.wstrust.WSTrustUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.