Package org.springframework.security.saml.websso

Source Code of org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl

/* Copyright 2009 Vladimir Schäfer
*
* Licensed 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.springframework.security.saml.websso;

import org.opensaml.common.SAMLException;
import org.opensaml.saml2.core.*;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.encryption.DecryptionException;
import org.opensaml.xml.signature.KeyInfo;
import org.opensaml.xml.util.Base64;
import org.springframework.security.saml.SAMLConstants;
import org.springframework.security.saml.context.SAMLMessageContext;
import org.springframework.security.saml.util.SAMLUtil;
import org.springframework.util.Assert;

import java.security.cert.CertificateEncodingException;
import java.util.List;

/**
* Class implements processing of the SAML Holder-of-Key Browser SSO profile as per
* http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-holder-of-key-browser-sso-cs-02.pdf.
*
* @author Vladimir Schäfer
*/
public class WebSSOProfileConsumerHoKImpl extends WebSSOProfileConsumerImpl implements WebSSOProfileConsumer {

    @Override
    public String getProfileIdentifier() {
        return SAMLConstants.SAML2_HOK_WEBSSO_PROFILE_URI;
    }

    /**
     * Verifies validity of Subject element as per http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml2-holder-of-key-cs-02.pdf  and
     * http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-holder-of-key-browser-sso-cs-02.pdf.
     * <p>
     * Only verification based on X509Certificate content of the X509Data in KeyInfo is supported. Subject is deemed as
     * confirmed when at least one of the certificates present in the SubjectConfirmation matches the one used in TLS/SSL
     * client authentication. No verification on trust or validity of the certificate itself is performed.
     *
     * @param subject subject to validate
     * @param request request
     * @param context context
     * @throws org.opensaml.common.SAMLException
     *          error validating the object
     * @throws org.opensaml.xml.encryption.DecryptionException
     *          in case the NameID can't be decrypted
     */
    protected void verifySubject(Subject subject, AuthnRequest request, SAMLMessageContext context) throws SAMLException, DecryptionException {

        String userAgentCertificate = getUserAgentBase64Certificate(context);

        for (SubjectConfirmation confirmation : subject.getSubjectConfirmations()) {

            if (SubjectConfirmation.METHOD_HOLDER_OF_KEY.equals(confirmation.getMethod())) {

                log.debug("Processing Holder-of-Key subject confirmation");
                SubjectConfirmationData data = confirmation.getSubjectConfirmationData();

                // HoK must have confirmation 554
                if (data == null) {
                    log.debug("HoK SubjectConfirmation invalidated by missing confirmation data");
                    continue;
                }

                if (!(data instanceof KeyInfoConfirmationDataType)) {
                    log.debug("HoK SubjectConfirmation invalidated by confirmation data not being of KeyInformationDataType type");
                    continue;
                }

                // Verify found certificate corresponds to peer certificate from SSL/TLS
                KeyInfoConfirmationDataType keyInfoConfirmation = (KeyInfoConfirmationDataType) data;
                boolean foundUserAgent = false;
                info:
                for (XMLObject xmlInfo : keyInfoConfirmation.getKeyInfos()) {
                    KeyInfo keyInfo = (KeyInfo) xmlInfo;
                    List<String> certificates = SAMLUtil.getBase64EncodeCertificates(keyInfo);
                    for (String confirmationCert : certificates) {
                        log.debug("Comparing user agent certificate {} with certificate in HoK key info {}", userAgentCertificate, confirmationCert);
                        if (userAgentCertificate.equals(confirmationCert)) {
                            log.debug("User agent certificate confirmed");
                            foundUserAgent = true;
                            break info;
                        }
                    }
                }
                if (!foundUserAgent) {
                    log.debug("HoK SubjectConfirmation invalidated by confirmation keyInfo not corresponding to certificate supplied by user agent");
                    continue;
                }

                // Validate not before
                if (data.getNotBefore() != null && data.getNotBefore().isAfterNow()) {
                    log.debug("HoK SubjectConfirmation invalidated by notBefore field");
                    continue;
                }

                // Validate not on or after
                if (data.getNotBefore() != null && data.getNotOnOrAfter().isBeforeNow()) {
                    log.debug("HoK SubjectConfirmation invalidated by expired notOnOrAfter");
                    continue;
                }

                // Validate in response to if present
                if (request != null) {
                    if (data.getInResponseTo() != null) {
                        if (!data.getInResponseTo().equals(request.getID())) {
                            log.debug("HoK SubjectConfirmation invalidated by invalid in response to field");
                            continue;
                        }
                    }
                }

                // Validate recipient if present
                if (data.getRecipient() != null) {
                    try {
                        verifyEndpoint(context.getLocalEntityEndpoint(), data.getRecipient());
                    } catch (SAMLException e) {
                        log.debug("HoK SubjectConfirmation invalidated by recipient assertion consumer URL, found {}", data.getRecipient());
                        continue;
                    }
                }

                // Was the subject confirmed by this confirmation data? If so let's store the subject in context.
                NameID nameID;
                if (subject.getEncryptedID() != null) {
                    Assert.notNull(context.getLocalDecrypter(), "Can't decrypt NameID, no decrypter is set in the context");
                    nameID = (NameID) context.getLocalDecrypter().decrypt(subject.getEncryptedID());
                } else {
                    nameID = subject.getNameID();
                }
                context.setSubjectNameIdentifier(nameID);
                return;

            }

        }

        throw new SAMLException("Assertion invalidated by subject confirmation - can't be confirmed by holder-of-key method");

    }

    /**
     * Method locates user agent certificate used in SSL/TLS and encodes it using base64 for comparison in HoK
     * subject confirmation. Method fails when certificate can't be obtained or encoded.
     *
     * @param context context expected to contain certificate in peerSSLCredential field
     * @return base64 encoded peer certificate
     * @throws SAMLException in case certificate is missing or can't be encoded
     */
    protected String getUserAgentBase64Certificate(SAMLMessageContext context) throws SAMLException {

        if (context.getPeerSSLCredential() == null) {
            throw new SAMLException("Cannot verify Holder-of-Key Assertion, peer SSL/TLS credential is not set in the context");
        }

        try {
            return Base64.encodeBytes(context.getPeerSSLCredential().getEntityCertificate().getEncoded());
        } catch (CertificateEncodingException e) {
            throw new SAMLException("Error base64 encoding peer certificate");
        }

    }

}
TOP

Related Classes of org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl

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.