com.xwiki.authentication.sts.STSTokenValidator.java Source code

Java tutorial

Introduction

Here is the source code for com.xwiki.authentication.sts.STSTokenValidator.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * 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.
 * 
 * Part of the code in this file is copied from: https://github.com/auth10/auth10-java
 * which is based on Microsoft libraries in: https://github.com/WindowsAzure/azure-sdk-for-java-samples. 
 * 
 */

package com.xwiki.authentication.sts;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joda.time.Duration;
import org.joda.time.Instant;
import org.opensaml.Configuration;
import org.opensaml.DefaultBootstrap;
import org.opensaml.common.SignableSAMLObject;
import org.opensaml.xml.ConfigurationException;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.io.Unmarshaller;
import org.opensaml.xml.io.UnmarshallingException;
import org.opensaml.xml.security.CriteriaSet;
import org.opensaml.xml.security.SecurityException;
import org.opensaml.xml.security.SecurityTestHelper;
import org.opensaml.xml.security.credential.CollectionCredentialResolver;
import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.security.criteria.EntityIDCriteria;
import org.opensaml.xml.security.keyinfo.KeyInfoCredentialResolver;
import org.opensaml.xml.security.keyinfo.KeyInfoHelper;
import org.opensaml.xml.security.x509.BasicX509Credential;
import org.opensaml.xml.signature.KeyInfo;
import org.opensaml.xml.signature.Signature;
import org.opensaml.xml.signature.impl.ExplicitKeySignatureTrustEngine;
import org.opensaml.xml.validation.ValidationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/*
 For new authentication method to work  this lines in xwiki/WEB-INF/xwiki.cfg were changed
    
 < xwiki.authentication.sts.issuer=http://www.latvija.lv/trust
 ---
 > xwiki.authentication.sts.issuer=http://www.latvija.lv/sts
 705c705
 < xwiki.authentication.sts.entity_id=http://www.latvija.lv/trust
 ---
 > xwiki.authentication.sts.entity_id=http://www.latvija.lv/sts
 707c707
 < xwiki.authentication.sts.issuer_dn=CN=IVIS Root CA
 ---
 > xwiki.authentication.sts.issuer_dn=CN=VISS Root CA, DC=viss, DC=int
 709c709
 < xwiki.authentication.sts.subject_dns=EMAILADDRESS=cisu.help@vraa.gov.lv, CN=IVIS.LVP.STS_PROD, OU=VPISD, O=VRAA, L=Riga, ST=Riga, C=LV
 ---
 > xwiki.authentication.sts.subject_dns=EMAILADDRESS=cisu.help@vraa.gov.lv, CN=VISS.LVP.STS, OU=VPISD, O=VRAA, L=Riga, ST=Riga, C=LV
 */

/**
 * Validates STSToken
 * Have main method which, validates token and have some utility private methods helping to
 * validation process
 * 
 * @version 1.0
 */
@SuppressWarnings("deprecation")
public class STSTokenValidator {
    /**
      * Log log - log - from LogFactory 
      */
    private static Log log = LogFactory.getLog(STSTokenValidator.class);
    /**
      * max ClockSkew - using to check time intervals / Before / After as a deviation
      */
    private int maxClockSkew;
    /**
      * max ClockSkew - max time interval in which may be a value
      */
    private List<String> trustedSubjectDNs;
    /**
      * max ClockSkew - http/https urls - 
      */
    private List<URI> audienceUris;
    /**
      * max ClockSkew - http/https urls - 
      */
    private boolean validateExpiration = true;
    /**
    * entityId - ID of the entity used for set entity id of the sertificate
    */
    private static String entityId;
    /**
    * IssuerDN value from the certificate (will be extracted from samlToken)
    */
    private String issuerDN;
    /**
     * context - containng data for validation
     */
    private String context;
    /** 
     * Name of Issuer DN - getIssuerDN
     */
    private String issuer;
    /** 
     * Name of Issuer DN - getIssuerDN
     */
    X509Certificate certificate;
    /** 
     * Collecting errors in Array - save, convert to string error list. Checking if passed object is Throwable class or something else.
     */
    STSErrorCollector errorCollector;

    public STSTokenValidator() throws ConfigurationException {
        this(new ArrayList<String>(), new ArrayList<URI>());
    }

    /**
     * <b>STSTokenValidator</b> - constructor for making  STSTokenValidator
     *
     * @param trustedSubjectDNs List<String>,
     * @param audienceUris List<URI>
     * @throws ConfigurationException - exception of open SAML's configuration
     */
    public STSTokenValidator(List<String> trustedSubjectDNs, List<URI> audienceUris) throws ConfigurationException {
        super();
        this.trustedSubjectDNs = trustedSubjectDNs;
        this.audienceUris = audienceUris;
        DefaultBootstrap.bootstrap();
    }

    public void setSubjectDNs(List<String> subjectDNs) {
        this.trustedSubjectDNs = subjectDNs;
    }

    public void setAudienceUris(List<URI> audienceUris) {
        this.audienceUris = audienceUris;
    }

    public void setValidateExpiration(boolean value) {
        this.validateExpiration = value;
    }

    public static void setEntityId(String value) {
        entityId = value;
    }

    /**
     * validate - Validate Token. It's taking envelopedToken as a parameter. This token - is a token
     * which is recived to this method - from VRA. And checks it's trust level using utility
     * method of this class. It uses some additional methods implemented in this class. 
     * And mothods from other auxiliary classes.
     * 
     * @param envelopedToken String
     * @return List<STSClaim> 
     * @throws ParserConfigurationException, SAXException, IOException, STSException, ConfigurationException, CertificateException, KeyException, SecurityException, ValidationException, UnmarshallingException, URISyntaxException, NoSuchAlgorithmException
     */
    public List<STSClaim> validate(String envelopedToken)
            throws ParserConfigurationException, SAXException, IOException, STSException, ConfigurationException,
            CertificateException, KeyException, SecurityException, ValidationException, UnmarshallingException,
            URISyntaxException, NoSuchAlgorithmException {

        SignableSAMLObject samlToken;
        boolean trusted = false;
        STSException stsException = null;

        // Check token metadata
        if (envelopedToken.contains("RequestSecurityTokenResponse")) {
            samlToken = getSamlTokenFromRstr(envelopedToken);
        } else {
            samlToken = getSamlTokenFromSamlResponse(envelopedToken);
        }

        log.debug("\n===== envelopedToken ========\n" + samlToken.getDOM().getTextContent() + "\n==========");
        String currentContext = getAttrVal(envelopedToken, "t:RequestSecurityTokenResponse", "Context");
        if (!context.equals(currentContext)) {
            errorCollector.addError(
                    new Throwable("Wrong token Context. Suspected: " + context + " got: " + currentContext));
            stsException = new STSException(
                    "Wrong token Context. Suspected: " + context + " got: " + currentContext);
        }

        if (this.validateExpiration) {
            Instant created = new Instant(getElementVal(envelopedToken, "wsu:Created"));
            Instant expires = new Instant(getElementVal(envelopedToken, "wsu:Expires"));
            if (!checkExpiration(created, expires)) {
                errorCollector.addError(new Throwable("Token Created or Expires elements have been expired"));
                stsException = new STSException("Token Created or Expires elements have been expired");
            }
        } else {
            log.warn("Token time was not validated. To validate, set xwiki.authentication.sts.wct=1");
        }

        if (certificate == null) {
            log.debug("\n");
            log.debug("STSTokenValidator: cert is null, using old method");

            if (issuer != null && issuerDN != null && !trustedSubjectDNs.isEmpty()) {

                if (!issuer.equals(getAttrVal(envelopedToken, "saml:Assertion", "Issuer"))) {
                    errorCollector.addError(new Throwable("Wrong token Issuer"));
                    stsException = new STSException("Wrong token Issuer");
                }

                // Check SAML assertions
                if (!validateIssuerDN(samlToken, issuerDN)) {
                    errorCollector.addError(new Throwable("Wrong token IssuerDN"));
                    stsException = new STSException("Wrong token IssuerDN");
                }

                for (String subjectDN : this.trustedSubjectDNs) {
                    trusted |= validateSubjectDN(samlToken, subjectDN);
                }

                if (!trusted) {
                    errorCollector.addError(new Throwable("Wrong token SubjectDN"));
                    stsException = new STSException("Wrong token SubjectDN");
                }
            } else {
                log.debug("\n");
                log.debug("STSTokenValidator: Nothing to validate against");
                errorCollector.addError(new Throwable("Nothing to validate against"));
                stsException = new STSException("Nothing to validate against");
            }

        } else {
            log.debug("\n");
            log.debug("STSTokenValidator: Using cert equals");
            if (!certificate.equals(certFromToken(samlToken))) {
                errorCollector.addError(new Throwable("Local certificate didn't match the user suplied one"));
                stsException = new STSException("Local certificate didn't match the user suplied one");
            }
        }

        String address = null;
        if (samlToken instanceof org.opensaml.saml1.core.Assertion) {
            address = getAudienceUri((org.opensaml.saml1.core.Assertion) samlToken);
        }

        URI audience = new URI(address);

        boolean validAudience = false;
        for (URI audienceUri : audienceUris) {
            validAudience |= audience.equals(audienceUri);
        }

        if (!validAudience) {
            errorCollector.addError(new Throwable("The token applies to an untrusted audience"));
            stsException = new STSException(
                    String.format("The token applies to an untrusted audience: %s", new Object[] { audience }));
        }

        List<STSClaim> claims = null;
        if (samlToken instanceof org.opensaml.saml1.core.Assertion) {
            claims = getClaims((org.opensaml.saml1.core.Assertion) samlToken);
        }

        if (this.validateExpiration && samlToken instanceof org.opensaml.saml1.core.Assertion) {
            Instant notBefore = ((org.opensaml.saml1.core.Assertion) samlToken).getConditions().getNotBefore()
                    .toInstant();
            Instant notOnOrAfter = ((org.opensaml.saml1.core.Assertion) samlToken).getConditions().getNotOnOrAfter()
                    .toInstant();
            if (!checkExpiration(notBefore, notOnOrAfter)) {
                errorCollector.addError(new Throwable("Token Created or Expires elements have been expired"));
                stsException = new STSException("Token Created or Expires elements have been expired");
            }
        }

        // Check token certificate and signature
        boolean valid = validateToken(samlToken);
        if (!valid) {
            errorCollector.addError(new Throwable("Invalid signature"));
            stsException = new STSException("Invalid signature");
        }

        if (!(stsException == null))
            throw stsException;

        return claims;
    }

    /**
      * getSamlTokenFromSamlResponse (String samlResponse)
      * 
      * Function is getting samlResponse String - 
      * SAML - Object is object of Security Assertion Markup Languages type is an XML-based, 
      * open-standard data format for exchanging authentication and authorization data between parties
      * And is returning SignableSAMLObject on success or throws exception on fault.
      *      
      * @param samlResponse - SAML Text Response (String)
      * 
      * @return SignableSAMLObject (Security Assertion Markup Language) 
      * @throws ParserConfigurationException - Indicates a serious configuration error,
      * @throws SAXException - Encapsulate a general SAX error or warning, IOException, 
      * @throws UnmarshallingException - thrown whenever an IOException is thrown during the unmarshalling process of request/response from the wire. 
      */
    private static SignableSAMLObject getSamlTokenFromSamlResponse(String samlResponse)
            throws ParserConfigurationException, SAXException, IOException, UnmarshallingException {
        Document document = getDocument(samlResponse);

        Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory()
                .getUnmarshaller(document.getDocumentElement());
        org.opensaml.saml2.core.Response response = (org.opensaml.saml2.core.Response) unmarshaller
                .unmarshall(document.getDocumentElement());
        return response.getAssertions().get(0);
    }

    /**
     * getSamlTokenFromRstr (String rstr) - get SAML Token from some XML Document.
     * First of all - there is creating a document from rstr - parametr - then - 
     * it is parsing it to extract a SingableSAMLObject.
     * it is using try/catch construction and throws new STSException("SAML token was not found");
     * if can't find SAML token.
     *  
     * @param rstr - String -  XML - Document's string from which will be extracted an information of a SamlToken
     * @return SignableSAMLObject - an instance of SAMLObject (Security Assertion Markup Language) 
     * @throws ParserConfigurationException, SAXException, IOException, UnmarshallingException, STSException 
     */
    private static SignableSAMLObject getSamlTokenFromRstr(String rstr)
            throws ParserConfigurationException, SAXException, IOException, UnmarshallingException, STSException {
        Document document = getDocument(rstr);

        String xpath = "//*[local-name() = 'Assertion']";

        NodeList nodes = null;

        try {
            nodes = org.apache.xpath.XPathAPI.selectNodeList(document, xpath);
        } catch (TransformerException e) {
            log.error(e);
        }

        if (nodes == null || nodes.getLength() == 0) {
            throw new STSException("SAML token was not found");
        } else {
            Element samlTokenElement = (Element) nodes.item(0);
            Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(samlTokenElement);
            return (SignableSAMLObject) unmarshaller.unmarshall(samlTokenElement);
        }
    }

    /**
     * Function gets AudienceUri String from org.opensaml.saml1.core.Assertion samlAssertion.
     * 
     * @param samlAssertion - A Security Assertion Markup Language (SAML) authorization assertion contains 
     * @return String AudienceUri - extracted from samlAssertion.getConditions().getAudienceRestrictionConditions().get(0)
    *   .getAudiences().get(0);
     * 
     */
    private static String getAudienceUri(org.opensaml.saml1.core.Assertion samlAssertion) {
        org.opensaml.saml1.core.Audience audienceUri = samlAssertion.getConditions()
                .getAudienceRestrictionConditions().get(0).getAudiences().get(0);
        String audienceUriStr = audienceUri.getUri();
        log.trace("AudienceUri: " + audienceUriStr);
        return audienceUriStr;
    }

    /**
     * checkExpiration(Instant notBefore, Instant notOnOrAfter)
     * Function checks that date now is after (notBefore parameter)
     * and now is before notOnOrAfter (calculating Skew - which is new Duration(this.maxClockSkew) object )
     *      
     * @param notBefore  Instant now is after not Before (plus skew)
     * @param notOnOrAfter Instant now is before notOnOrAfter (minus skew)
     * @return true - if check  - is ok, or false if check is fault
     */
    private boolean checkExpiration(Instant notBefore, Instant notOnOrAfter) {
        Instant now = new Instant();
        Duration skew = new Duration(maxClockSkew);
        log.debug("Time expiration. Now:" + now + " now+sqew: " + now.plus(skew) + " now-sqew: " + now.minus(skew)
                + " notBefore: " + notBefore + " notAfter: " + notOnOrAfter);
        if (now.plus(skew).isAfter(notBefore) && now.minus(skew).isBefore(notOnOrAfter)) {
            log.debug("Time is in range");
            return true;
        }
        return false;
    }

    /**
      * validateToken(SignableSAMLObject samlToken)
      * Validates Token from SAMLlObject - returns boolen
      * Validates Token - exitracting sertificate from samlToken.
      * And validates it. Returning true or false according on validation results.
      * @param samlToken SignableSAMLObject
      * @return boolean valid => true, not valid => false
      */
    private static boolean validateToken(SignableSAMLObject samlToken)
            throws SecurityException, ValidationException, ConfigurationException, UnmarshallingException,
            CertificateException, KeyException {

        // Validate XML structure
        samlToken.validate(true);

        Signature signature = samlToken.getSignature();
        X509Certificate certificate = certFromToken(samlToken);

        // Certificate data
        log.debug("certificate issuerDN: " + certificate.getIssuerDN());
        log.debug("certificate issuerUniqueID: " + certificate.getIssuerUniqueID());
        log.debug("certificate issuerX500Principal: " + certificate.getIssuerX500Principal());
        log.debug("certificate notBefore: " + certificate.getNotBefore());
        log.debug("certificate notAfter: " + certificate.getNotAfter());
        log.debug("certificate serialNumber: " + certificate.getSerialNumber());
        log.debug("certificate sigAlgName: " + certificate.getSigAlgName());
        log.debug("certificate sigAlgOID: " + certificate.getSigAlgOID());
        log.debug("certificate signature: " + new String(certificate.getSignature()));
        log.debug("certificate issuerX500Principal: " + certificate.getIssuerX500Principal().toString());
        log.debug("certificate publicKey: " + certificate.getPublicKey());
        log.debug("certificate subjectDN: " + certificate.getSubjectDN());
        log.debug("certificate sigAlgOID: " + certificate.getSigAlgOID());
        log.debug("certificate version: " + certificate.getVersion());

        BasicX509Credential cred = new BasicX509Credential();
        cred.setEntityCertificate(certificate);

        // Credential data
        cred.setEntityId(entityId);
        log.debug("cred entityId: " + cred.getEntityId());
        log.debug("cred usageType: " + cred.getUsageType());
        log.debug("cred credentalContextSet: " + cred.getCredentalContextSet());
        log.debug("cred hashCode: " + cred.hashCode());
        log.debug("cred privateKey: " + cred.getPrivateKey());
        log.debug("cred publicKey: " + cred.getPublicKey());
        log.debug("cred secretKey: " + cred.getSecretKey());
        log.debug("cred entityCertificateChain: " + cred.getEntityCertificateChain());

        ArrayList<Credential> trustedCredentials = new ArrayList<Credential>();
        trustedCredentials.add(cred);

        CollectionCredentialResolver credResolver = new CollectionCredentialResolver(trustedCredentials);
        KeyInfoCredentialResolver kiResolver = SecurityTestHelper.buildBasicInlineKeyInfoResolver();
        ExplicitKeySignatureTrustEngine engine = new ExplicitKeySignatureTrustEngine(credResolver, kiResolver);

        CriteriaSet criteriaSet = new CriteriaSet();
        criteriaSet.add(new EntityIDCriteria(entityId));

        Base64 decoder = new Base64();
        // In trace mode write certificate in the file
        if (log.isTraceEnabled()) {
            String certEncoded = new String(decoder.encode(certificate.getEncoded()));
            try {
                FileUtils.writeStringToFile(new File("/tmp/Certificate.cer"),
                        "-----BEGIN CERTIFICATE-----\n" + certEncoded + "\n-----END CERTIFICATE-----");
                log.trace("Certificate file was saved in: /tmp/Certificate.cer");
            } catch (IOException e1) {
                log.error(e1);
            }
        }
        return engine.validate(signature, criteriaSet);
    }

    /**
     * validateSubjectDN(SignableSAMLObject samlToken, String subjectName)
     * Validates the subject (subject distinguished name) value from the certificate. 
     * @param samlToken SignableSAMLObject saml Token
     * @param subjectName subjectNamme name to Validate
     * @return boolean valid => true, not valid => false
     */
    private static boolean validateSubjectDN(SignableSAMLObject samlToken, String subjectName)
            throws UnmarshallingException, ValidationException, CertificateException {
        Signature signature = samlToken.getSignature();
        KeyInfo keyInfo = signature.getKeyInfo();
        X509Certificate pubKey = KeyInfoHelper.getCertificates(keyInfo).get(0);
        String subjectDN = pubKey.getSubjectDN().getName();
        log.trace("passed subjectName: '" + subjectName + "' certificate SubjectDN: '" + subjectDN);
        return subjectDN.equals(subjectName);
    }

    /**
     * validateIssuerDN(SignableSAMLObject samlToken, String subjectName)
     * Validates IssuerDN value from the certificate (extracted from samlToken). 
     * @param samlToken SignableSAMLObject - saml Token
     * @param issuerName issuer name validate to
     * @return valid  boolean => true, not valid => false
     * @throws UnmarshallingException, ValidationException, CertificateException 
     */
    private static boolean validateIssuerDN(SignableSAMLObject samlToken, String issuerName)
            throws UnmarshallingException, ValidationException, CertificateException {

        Signature signature = samlToken.getSignature();
        KeyInfo keyInfo = signature.getKeyInfo();
        X509Certificate pubKey = KeyInfoHelper.getCertificates(keyInfo).get(0);
        String issuer = pubKey.getIssuerDN().getName();
        log.trace("passed issuerName: '" + issuerName + "' certificate IssuerDN: '" + issuer + "'");
        return issuer.equals(issuerName);
    }

    /**
     * getClaims(org.opensaml.saml1.core.Assertion samlAssertion)
     * Get's List of STSClaims according to samlAssertion
     * @param samlAssertion org.opensaml.saml1.core.Assertion
     * @return ArrayList<STSClaim> (Claims-based identity is a common way for applications to acquire the identity information they need about users inside their organization)
     * @throws SecurityException, ValidationException, ConfigurationException, UnmarshallingException, CertificateException, KeyException
     * @throws UnmarshallingException, ValidationException, CertificateExceptio @throws UnmarshallingException, ValidationException, CertificateException n 
     */
    private static List<STSClaim> getClaims(org.opensaml.saml1.core.Assertion samlAssertion)
            throws SecurityException, ValidationException, ConfigurationException, UnmarshallingException,
            CertificateException, KeyException {

        ArrayList<STSClaim> claims = new ArrayList<STSClaim>();

        List<org.opensaml.saml1.core.AttributeStatement> attributeStmts = samlAssertion.getAttributeStatements();

        for (org.opensaml.saml1.core.AttributeStatement attributeStmt : attributeStmts) {
            List<org.opensaml.saml1.core.Attribute> attributes = attributeStmt.getAttributes();

            for (org.opensaml.saml1.core.Attribute attribute : attributes) {
                String claimType = attribute.getAttributeNamespace() + "/" + attribute.getAttributeName();
                String claimValue = getValueFrom(attribute.getAttributeValues());
                claims.add(new STSClaim(claimType, claimValue));
            }
        }
        log.trace("Claims: " + claims.toString());
        return claims;
    }

    /**
     * getValueFrom(List<XMLObject> attributeValues)
     * Gets all atribute's values from a list of XML objects
     * @param attributeValues List<XMLObject>
     * @return buffer.toString() - converted to string buffer of XML attribute's values
     */
    private static String getValueFrom(List<XMLObject> attributeValues) {

        StringBuilder buffer = new StringBuilder();

        for (XMLObject value : attributeValues) {
            if (buffer.length() > 0)
                buffer.append(',');
            buffer.append(value.getDOM().getTextContent());
        }
        log.trace("attributeValues: " + buffer.toString());
        return buffer.toString();
    }

    /**
     * getDocument(String doc)
     * Parse document from string
     * @param doc String string containing info for document builder parser
     * @return Document - parsed from input string document 
     */
    private static Document getDocument(String doc) throws ParserConfigurationException, SAXException, IOException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder documentbuilder = factory.newDocumentBuilder();
        return documentbuilder.parse(new InputSource(new StringReader(doc)));
    }

    /**
     * getAttrVal(String envelopedToken, String element, String attribute)
     * Gets value of Document value contained in evelopedToken
     * @param element String element to get value for
     * @param attribute String attribute to get value for
     * @return value String of element's attribute
     */
    private String getAttrVal(String envelopedToken, String element, String attribute)
            throws ParserConfigurationException, SAXException, IOException {
        Document doc = getDocument(envelopedToken);
        return doc.getElementsByTagName(element).item(0).getAttributes().getNamedItem(attribute).getNodeValue();
    }

    /**
     * getElementVal(String envelopedToken, String element)
     * Gets value of Document value contained in evelopedToken
     * @param element String  element to get value for
     * @param envelopedToken enveloped Token
     * @throws throws ParserConfigurationException, SAXException, IOException 
     * @return String - value of element's attribute
     */
    private String getElementVal(String envelopedToken, String element)
            throws ParserConfigurationException, SAXException, IOException {
        Document doc = getDocument(envelopedToken);
        return doc.getElementsByTagName(element).item(0).getTextContent();
    }

    public void setIssuerDN(String issuerDN) {
        this.issuerDN = issuerDN;
    }

    public void setContext(String context) {
        this.context = context;
    }

    public void setIssuer(String issuer) {
        this.issuer = issuer;
    }

    public void setMaxClockSkew(int maxClockSkew) {
        this.maxClockSkew = maxClockSkew;
    }

    public void setCertificate(X509Certificate cert) {
        this.certificate = cert;
    }

    /**
     * X509Certificate certFromToken(SignableSAMLObject token)
     * @param token SignableSAMLObject input token with sertificate inside
     * @throws throws ParserConfigurationException, SAXException, IOException 
     * @return X509Certificate - certificate extracted from SAMLToken
     */
    private static X509Certificate certFromToken(SignableSAMLObject token) {
        try {
            return KeyInfoHelper.getCertificates(token.getSignature().getKeyInfo()).get(0);
        } catch (CertificateException e) {
            log.error(e);
            return null;
        }
    }

    /**
     * Activizate STSErrorCollector - for error preserving
     */
    public void setSTSErrorCollector(STSErrorCollector errorCollector) {
        this.errorCollector = errorCollector;
    }
}