Accueil > Non classé > SAML 2 et Liferay – partie 4

SAML 2 et Liferay – partie 4

Dans le précédent épisode article, nous avions mis en place une boite aux lettres pour recevoir du courrier. Et le message que l’on reçoit doit être signé d’un superbe autographe.

Un autographe authentique ?

Dans la mesure où, la réponse doit être signée, il pourrait être judicieux de vérifier la signature. Dans le cas du HTTP Post Binding, il s’agit d’une classique signature XML. Pour la valider, nous allons pouvoir nous servir de la « Java XML Digital Signature API » (JSR 105) :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public static boolean validateXmlSignature(String xml) {

    try {

        // Instantiate the document to be validated

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        org.w3c.dom.Document doc = dbf.newDocumentBuilder().parse(
                new ByteArrayInputStream(xml.getBytes(StringPool.UTF8)));

        // Find Signature element

        NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, SAMLConstants.SIGNATURE);
        if (nl.getLength() == 0) {
            _log.error("Cannot find Signature element");
            return false;
        }

        // Create a DOM XMLSignatureFactory that will be used to
        // unmarshal the document containing the XMLSignature

        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

        for(int i = 0; i < nl.getLength(); ++i) {

            // Create a DOMValidateContext and specify KeyValue, KeySelector and document context

            DOMValidateContext valContext = new DOMValidateContext(new SAMLKeySelector(), nl.item(i));

            // unmarshal the XMLSignature

            XMLSignature signature = fac.unmarshalXMLSignature(valContext);

            // Validate the XMLSignature

            if (signature.validate(valContext)) {
                if (_log.isDebugEnabled()) {
                    _log.debug("Signature " + i + " passed core validation");
                }
            } else {
                _log.error("Signature " + i + " failed core validation");

                if (_log.isInfoEnabled()) {
                    boolean sv = signature.getSignatureValue().validate(valContext);
                    _log.info("signature validation status: " + sv);

                    // check the validation status of each Reference

                    Iterator<Reference> itRef = signature.getSignedInfo().getReferences().iterator();
                    for (int j=0; itRef.hasNext(); j++) {
                        boolean refValid = itRef.next().validate(valContext);
                        _log.info("ref[" + j + "] validity status: " + refValid);
                    }
                }

                return false;
            }
        }
    }
    catch (Exception e) {
        _log.error(e, e);
        return false;
    }

    return true;
}


Avec les import correspondant :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

Et le mécanisme de sélection de la clé publique du fournisseur d’identité :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.liferay.portal.servlet.filters.sso.saml;

import java.security.cert.Certificate;

import javax.xml.crypto.AlgorithmMethod;
import javax.xml.crypto.KeySelector;
import javax.xml.crypto.KeySelectorException;
import javax.xml.crypto.KeySelectorResult;
import javax.xml.crypto.XMLCryptoContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;

/**
 *
 * @author Denis Vaumoron
 */

class SAMLKeySelector extends KeySelector {

    public KeySelectorResult select(
            KeyInfo keyInfo, KeySelector.Purpose purpose,
            AlgorithmMethod method, XMLCryptoContext context)
        throws KeySelectorException {

        Certificate certificate = SAMLMetadataManager.getIDPSigningCertificate();

                if (certificate == null) {
                        throw new KeySelectorException("IDP Certificate not found");
                }

                return new SimpleKeySelectorResult(certificate.getPublicKey());
        }

}

Comme dans l’exemple de signature précédent, la méthode getIDPSigningCertificate de la classe SAMLMetadataManager, qui permet de récupérer le certificat du fournisseur d’identité (on continue avec les surprises), peut dans un premier temps, lire un keystore dans lequel celle-ci aura été manuellement importé.

C’est fini pour aujourd’hui

Maintenant que nous savons envoyer et recevoir les messages SAML, vérifier leur validité, nous pourrions croire que nous avons fini, hé bien non, il ne s’agissait que du gros œuvre, le prochain article concernera les finitions à apporter pour respecter le standard SAML.

A bientôt pour de nouvelles aventures sur le blog Excilys…

 

Share
  1. Pas encore de commentaire
  1. Pas encore de trackbacks