SAML Assertion의 XML서명검증

<?xml version=”1.0″ encoding=”UTF-8″?> <samlp:Response Destination=”https://saml.centis1504.net/sp/acs.do” ID=”r5436a7fd9a3b4b3f9dbd01e04d6ddcbc” InResponseTo=”vIV8WvY8YQyP4c8vCyzBKV47kapv” IssueInstant=”2013-08-08T06:36:12.805Z” Version=”2.0″ xmlns:saml=”urn:oasis:names:tc:SAML:2.0:assertion” xmlns:samlp=”urn:oasis:names:tc:SAML:2.0:protocol”><saml:Issuer xmlns:saml=”urn:oasis:names:tc:SAML:2.0:assertion”>https://saml.centis1504.net</saml:Issuer><samlp:Status><samlp:StatusCode Value=”urn:oasis:names:tc:SAML:2.0:status:Success”/></samlp:Status><saml:Assertion ID=”aEo-hvlDqH0qz2MnnrP2D60mmNvlz” IssueInstant=”2013-08-08T06:36:12.804Z” Version=”2.0″ xmlns:saml=”urn:oasis:names:tc:SAML:2.0:assertion”><saml:Issuer>https://saml.centis1504.net</saml:Issuer><ds:Signature xmlns:ds=”http://www.w3.org/2000/09/xmldsig#”> <ds:SignedInfo xmlns:ds=”http://www.w3.org/2000/09/xmldsig#”> <ds:CanonicalizationMethod Algorithm=”http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments” xmlns:ds=”http://www.w3.org/2000/09/xmldsig#”/> <ds:SignatureMethod Algorithm=”http://www.w3.org/2000/09/xmldsig#rsa-sha1″ xmlns:ds=”http://www.w3.org/2000/09/xmldsig#”/> <ds:Reference URI=”#aEo-hvlDqH0qz2MnnrP2D60mmNvlz” xmlns:ds=”http://www.w3.org/2000/09/xmldsig#”> <ds:Transforms xmlns:ds=”http://www.w3.org/2000/09/xmldsig#”> <ds:Transform Algorithm=”http://www.w3.org/2000/09/xmldsig#enveloped-signature” xmlns:ds=”http://www.w3.org/2000/09/xmldsig#”/> <ds:Transform Algorithm=”http://www.w3.org/2001/10/xml-exc-c14n#” xmlns:ds=”http://www.w3.org/2000/09/xmldsig#”><ec:InclusiveNamespaces PrefixList=”ds saml xs xsi” xmlns:ec=”http://www.w3.org/2001/10/xml-exc-c14n#”/></ds:Transform> </ds:Transforms> <ds:DigestMethod Algorithm=”http://www.w3.org/2000/09/xmldsig#sha1″ xmlns:ds=”http://www.w3.org/2000/09/xmldsig#”/> <ds:DigestValue xmlns:ds=”http://www.w3.org/2000/09/xmldsig#”>Pnp8KGJg8Ny0lyeSwvLvlRunOTg=</ds:DigestValue> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue xmlns:ds=”http://www.w3.org/2000/09/xmldsig#”>B/KgR0EFNgxjmtlInJSnXqclmVbYkZkuPZnROKAcph+98avAT4jNgutfWXIMUUSrgsyOPKoig5l+peRVNOUX5Q==</ds:SignatureValue> <ds:KeyInfo><ds:KeyValue><ds:RSAKeyValue><ds:Modulus>nEVcq+Ph3ZkXQone59guZiEYybtxDdYVAhw4JsoSQoxuzBM8BMqjYD0xmcMF1rJV+7BNKDkOJz3kdsFgS05acw==</ds:Modulus><ds:Exponent>AQAB</ds:Exponent></ds:RSAKeyValue></ds:KeyValue></ds:KeyInfo></ds:Signature><saml:Subject><saml:NameID Format=”urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified” SPNameQualifier=”https://saml.centis1504.net/sp”>RpGaj4IRLtdR2-W1-YD3jTjzHhJF</saml:NameID><saml:SubjectConfirmation Method=”urn:oasis:names:tc:SAML:2.0:cm:bearer”><saml:SubjectConfirmationData InResponseTo=”vIV8WvY8YQyP4c8vCyzBKV47kapv” NotOnOrAfter=”2013-08-09T06:36:12.805Z” Recipient=”https://saml.centis1504.net/sp/acs.do”/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore=”2013-08-08T06:36:12.805Z” NotOnOrAfter=”2013-08-09T06:36:12.805Z”><saml:AudienceRestriction><saml:Audience>https://saml.centis1504.net/sp</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant=”2013-08-08T06:36:12.805Z”><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement><saml:Attribute Name=”mail” NameFormat=”urn:oasis:names:tc:SAML:2.0:attrname-format:uri”><saml:AttributeValue xmlns:xs=”http://www.w3.org/2001/XMLSchema” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:type=”xs:string”>jojh@centis1504.net</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion></samlp:Response> 위 내용을 c:/rsa-response.xml 에 저장한후에 아래와 같이 검증할 수 있다.

import java.io.FileInputStream;
import java.io.StringWriter;
import java.security.Key;
import java.security.KeyException;
import java.security.PublicKey;
import java.util.Iterator;
import java.util.List;
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.XMLStructure;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyValue;
import javax.xml.parsers.DocumentBuilderFactory;
import org.jcp.xml.dsig.internal.dom.DOMX509Data;
import org.opensaml.xml.util.XMLHelper;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
/**
 * This is a simple example of validating an XML Signature using the JSR 105
 * API. It assumes the key needed to validate the signature is contained in a
 * KeyValue KeyInfo.
 */
public class Validate {
	//
	// Synopsis: java Validate [document]
	//
	// where "document" is the name of a file containing the XML document
	// to be validated.
	//
	public static void main(String[] args) throws Exception {
		// Instantiate the document to be validated
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		dbf.setNamespaceAware(true);
		Document doc = dbf.newDocumentBuilder().parse(new FileInputStream("c:/saml-response.xml"));
		// dbf.newDocumentBuilder().parse(new FileInputStream(args[0]));
		StringWriter writer = new StringWriter();
		XMLHelper.writeNode(doc, writer);
		System.out.println(writer.toString());
		// Find Signature element
		NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
		if (nl.getLength() == 0) {
			throw new Exception("Cannot find Signature element");
		}
		// Create a DOM XMLSignatureFactory that will be used to unmarshal the
		// document containing the XMLSignature
		XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
		// Create a DOMValidateContext and specify a KeyValue KeySelector
		// and document context
		DOMValidateContext valContext = new DOMValidateContext(new KeyValueKeySelector(), nl.item(0));
		// unmarshal the XMLSignature
		XMLSignature signature = fac.unmarshalXMLSignature(valContext);
		// Validate the XMLSignature (generated above)
		boolean coreValidity = signature.validate(valContext);
		// Check core validation status
		if (coreValidity == false) {
			System.err.println("Signature failed core validation");
			boolean sv = signature.getSignatureValue().validate(valContext);
			System.out.println("signature validation status: " + sv);
			// check the validation status of each Reference
			Iterator i = signature.getSignedInfo().getReferences().iterator();
			Reference r = null;
			for (int j = 0; i.hasNext(); j++) {
				r = i.next();
				System.out.println(r.getURI() + " => "+r);
				boolean refValid = (r).validate(valContext);
				System.out.println("ref[" + j + "] validity status: " + refValid);
			}
		} else {
			System.out.println("Signature passed core validation");
		}
	}
	/**
	 * KeySelector which retrieves the public key out of the KeyValue element
	 * and returns it. NOTE: If the key algorithm doesn't match signature
	 * algorithm, then the public key will be ignored.
	 */
	private static class KeyValueKeySelector extends KeySelector {
		public KeySelectorResult select(KeyInfo keyInfo, KeySelector.Purpose purpose, AlgorithmMethod method, XMLCryptoContext context) throws KeySelectorException {
			System.out.println("keyInfo="+keyInfo);
			System.out.println("method="+method);
			if (keyInfo == null) {
				throw new KeySelectorException("Null KeyInfo object!");
			}
			SignatureMethod sm = (SignatureMethod) method;
			List list = keyInfo.getContent();
			for (int i = 0; i < list.size(); i++) {
				XMLStructure xmlStructure = (XMLStructure) list.get(i);
				System.out.println(xmlStructure);
				if (xmlStructure instanceof KeyValue) {
					PublicKey pk = null;
					try {
						pk = ((KeyValue) xmlStructure).getPublicKey();
					} catch (KeyException ke) {
						throw new KeySelectorException(ke);
					}
					// make sure algorithm is compatible with method
					if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) {
						return new SimpleKeySelectorResult(pk);
					}
				} else if (xmlStructure instanceof DOMX509Data) {
					sun.security.x509.X509CertImpl cert = (sun.security.x509.X509CertImpl) ((DOMX509Data)xmlStructure).getContent().get(0);
					PublicKey pk = cert.getPublicKey();
					System.out.println("SignatureMethod => " + sm.getAlgorithm());
					System.out.println("PublicKey => " + pk.getAlgorithm());
					if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) {
						return new SimpleKeySelectorResult(pk);
					}
				}
			}
			throw new KeySelectorException("No KeyValue element found!");
		}
		// @@@FIXME: this should also work for key types other than DSA/RSA
		static boolean algEquals(String algURI, String algName) {
			if (algName.equalsIgnoreCase("DSA") && algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) {
				return true;
			} else if (algName.equalsIgnoreCase("RSA") && algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) {
				return true;
			} else {
				return false;
			}
		}
	}
	private static class SimpleKeySelectorResult implements KeySelectorResult {
		private PublicKey pk;
		SimpleKeySelectorResult(PublicKey pk) {
			this.pk = pk;
		}
		public Key getKey() {
			return pk;
		}
	}
}
이 예제에서는 Assertion내부의 Key정보가 있는데 키값 또는 X509증명서로 검증을 할 수 있다. X509증명서를 이용하여 서명을 하는 부분은 아래를 참조하라. http://www.java-only.com/LoadTutorial.javaonly?id=67]]>

Related Posts