/* * Copyright 2009 Federal Chancellery Austria and * Graz University of Technology * * 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 at.gv.egiz.mocca.id; import iaik.xml.crypto.dom.DOMCryptoContext; import iaik.xml.crypto.dsig.keyinfo.KeyValueType; import java.io.IOException; import java.io.InputStream; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.text.ParseException; import java.util.ArrayList; import java.util.List; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.crypto.MarshalException; import javax.xml.crypto.dom.DOMStructure; import javax.xml.crypto.dsig.Manifest; import javax.xml.crypto.dsig.Reference; import javax.xml.crypto.dsig.XMLObject; import javax.xml.crypto.dsig.XMLSignature; import javax.xml.crypto.dsig.XMLSignatureException; 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.X509Data; import oasis.names.tc.saml._1_0.assertion.AnyType; import oasis.names.tc.saml._1_0.assertion.AssertionType; import oasis.names.tc.saml._1_0.assertion.AttributeStatementType; import oasis.names.tc.saml._1_0.assertion.AttributeType; import oasis.names.tc.saml._1_0.assertion.StatementAbstractType; import oasis.names.tc.saml._1_0.assertion.SubjectConfirmationType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Element; import org.w3c.dom.Node; import at.gv.e_government.reference.namespace.persondata._20020228_.PhysicalPersonType; import at.gv.egiz.bku.utils.StreamUtil; public class IdLink { protected Logger log = LoggerFactory.getLogger(IdLink.class); /** * The IdLink is backed by a DOM. */ protected Node node; /** * The Assertion (root element) of the IdLink. */ protected AssertionType assertion; /** * The citizen's asserted public keys. */ protected List citizenPublicKeys; /** * The XMLSignature. */ protected XMLSignature signature; /** * The assertion's signer certificate. */ protected X509Certificate signerCert; /** * Is the assertion's signature manifest valid? */ protected Boolean manifestValid; /** * Is the assertion's signature valid? */ protected Boolean signatureValid; /** * The personal identifier */ protected IdLinkPersonData personData; public IdLink(Element node, AssertionType assertion) throws JAXBException { this.node = node; this.assertion = assertion; } public PhysicalPersonType getPhysicalPerson() { AttributeStatementType attributeStatement = getAttributeStatement(); if (attributeStatement != null) { JAXBElement subjectConfirmation = attributeStatement.getSubject().getContent().get(0); if (subjectConfirmation.getDeclaredType() == SubjectConfirmationType.class) { Object data = ((SubjectConfirmationType) subjectConfirmation.getValue()) .getSubjectConfirmationData().getContent().get(0); if (data instanceof JAXBElement && ((JAXBElement) data).getValue() instanceof PhysicalPersonType) { return (PhysicalPersonType) ((JAXBElement) data).getValue(); } } } return null; } public AttributeStatementType getAttributeStatement() { StatementAbstractType statement = assertion.getStatementOrSubjectStatementOrAuthenticationStatement().get(0); if (statement instanceof AttributeStatementType) { return (AttributeStatementType) statement; } return null; } public IdLinkPersonData getPersonData() throws MarshalException { if (personData == null) { try { personData = new IdLinkPersonData(getPhysicalPerson()); } catch (ParseException e) { throw new MarshalException(e); } } return personData; } public List getCitizenPublicKeys() throws MarshalException { if (citizenPublicKeys == null) { citizenPublicKeys = new ArrayList(); AttributeStatementType attributeStatement = getAttributeStatement(); if (attributeStatement != null) { List attributes = attributeStatement.getAttribute(); for (AttributeType attribute : attributes) { if ("urn:publicid:gv.at:namespaces:identitylink:1.2".equals(attribute.getAttributeNamespace()) && "CitizenPublicKey".equals(attribute.getAttributeName())) { List value = attribute.getAttributeValue(); if (value.size() == 1 && value.get(0).getContent().size() == 1) { Object object = value.get(0).getContent().get(0); if (object instanceof Element) { Element element = (Element) object; DOMStructure structure = iaik.xml.crypto.dom.DOMStructure.getInstance(element, new DOMCryptoContext()); if (structure instanceof KeyValueType) { citizenPublicKeys.add(((KeyValueType) structure).getPublicKey()); } } } } } } } return citizenPublicKeys; } public XMLSignature getXMLSignature() throws MarshalException { if (signature == null) { Node n = node.getLastChild(); while (n != null && n.getNodeType() != Node.ELEMENT_NODE) { n = n.getPreviousSibling(); } if (n != null && XMLSignature.XMLNS.equals(n.getNamespaceURI()) && "Signature".equals(n.getLocalName())) { XMLSignatureFactory signatureFactory = XMLSignatureFactory.getInstance(); signature = signatureFactory.unmarshalXMLSignature(new DOMStructure(n)); } } return signature; } public X509Certificate getSignerCert() throws MarshalException { if (signerCert == null) { if (getXMLSignature() != null) { KeyInfo keyInfo = signature.getKeyInfo(); if (keyInfo != null) { List content = keyInfo.getContent(); for (Object data : content) { if (data instanceof X509Data) { List x509Data = ((X509Data) data).getContent(); for (Object object : x509Data) { if (object instanceof X509Certificate) { signerCert = (X509Certificate) object; return signerCert; } } } } } } } return signerCert; } @SuppressWarnings("unchecked") public boolean verifySignature() throws MarshalException, XMLSignatureException { if (signatureValid == null) { if (getXMLSignature() != null && getSignerCert() != null) { DOMValidateContext validateContext = new DOMValidateContext(signerCert.getPublicKey(), node); validateContext.setProperty("javax.xml.crypto.dsig.cacheReference", Boolean.TRUE); signatureValid = signature.validate(validateContext); // logging if (!signatureValid && log.isTraceEnabled()) { List references = signature.getSignedInfo().getReferences(); for (Reference reference : references) { if (!Manifest.TYPE.equals(reference.getType())) { if (!reference.validate(validateContext)) { InputStream digestInputStream = reference.getDigestInputStream(); if (digestInputStream != null) { try { log.trace("SignedInfo's reference digest input:\n{}", StreamUtil.asString(digestInputStream, "UTF-8")); } catch (IOException e) { log.info("Failed to get SignedInfos's reference digest input", e.toString()); } } } else { try { log.trace("Signature canonicalized data:\n{}", StreamUtil.asString(signature .getSignedInfo().getCanonicalizedData(), "UTF-8")); } catch (IOException e) { log.info("Failed to get canonicalized data.", e); } } break; } } } } } return signatureValid; } @SuppressWarnings("unchecked") public boolean verifyManifest() throws MarshalException, XMLSignatureException { if (manifestValid == null) { if (getXMLSignature() != null && getSignerCert() != null) { DOMValidateContext validateContext = new DOMValidateContext(signerCert.getPublicKey(), node); if (log.isTraceEnabled()) { // enable reference caching in trace log-level validateContext.setProperty("javax.xml.crypto.dsig.cacheReference", Boolean.TRUE); } boolean valid = false; // validate manifest List objects = signature.getObjects(); for (XMLObject object : objects) { List content = object.getContent(); if (content.get(0) instanceof Manifest) { Manifest manifest = (Manifest) content.get(0); List references = manifest.getReferences(); for (Reference reference : references) { valid = reference.validate(validateContext); // logging if (!valid && log.isTraceEnabled()) { InputStream digestInputStream = reference.getDigestInputStream(); if (digestInputStream != null) { try { log.trace("Manifest's reference digest input:\n{}", StreamUtil.asString(digestInputStream, "UTF-8")); } catch (IOException e) { log.info("Failed to get Manifest's reference digest input", e.toString()); } } } break; } } } // validate reference to manifest if (valid) { List references = signature.getSignedInfo().getReferences(); for (Reference reference : references) { if (Manifest.TYPE.equals(reference.getType())) { boolean refValid = reference.validate(validateContext); // logging if (!refValid && log.isTraceEnabled()) { InputStream digestInputStream = reference.getDigestInputStream(); if (digestInputStream != null) { try { log.trace("SignedInfo's manifest reference digest input:\n{}", StreamUtil.asString(digestInputStream, "UTF-8")); } catch (IOException e) { log.info("Failed to get SignedInfos's manifest reference digest input", e.toString()); } } } valid &= refValid; } } } manifestValid = valid; } } return manifestValid; } }