package at.gv.egovernment.moa.spss.tsl.connector; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.security.cert.X509Certificate; import java.util.Iterator; import java.util.ListIterator; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBIntrospector; import javax.xml.crypto.Data; import javax.xml.crypto.MarshalException; import javax.xml.crypto.NodeSetData; import javax.xml.crypto.URIReferenceException; import javax.xml.crypto.dom.DOMCryptoContext; import javax.xml.crypto.dsig.Reference; import javax.xml.crypto.dsig.SignedInfo; import javax.xml.crypto.dsig.Transform; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import iaik.server.modules.xml.MOAXSecProvider; import iaik.xml.crypto.tsl.TSLConstants; import iaik.xml.crypto.tsl.TSLContext; import iaik.xml.crypto.tsl.TSLEngine; import iaik.xml.crypto.tsl.ex.SeverityAspect.Severity; import iaik.xml.crypto.tsl.ex.TSLSecurityException; import iaik.xml.crypto.tsl.ex.TSLVerificationException; import iaik.xml.crypto.tsl.gen.TrustStatusListType; import iaik.xml.crypto.tsl.verify.ITSLVerifier; import iaik.xml.crypto.utils.URIDereferencerImpl; public class MOATSLVerifier implements ITSLVerifier { private static final Logger logger = LoggerFactory.getLogger(MOATSLVerifier.class); private static iaik.xml.crypto.xmldsig.gen.ObjectFactory dsOf = new iaik.xml.crypto.xmldsig.gen.ObjectFactory(); private static JAXBIntrospector JI = TSLEngine.jc.createJAXBIntrospector(); public Boolean verifyTSL(Document tslDoc, TSLContext tslContext, ListIterator euTslCertsHash) { boolean coreValidity = false; try { // Signature s = new Signature(); // TrustServiceStatusList tssl = new TrustServiceStatusList(); JAXBElement s = dsOf.createSignature(new iaik.xml.crypto.xmldsig.gen.SignatureType()); // _l.debug(""+JI.getElementName(s)); JAXBElement tssl = TSLConstants.TSL_OF.createTrustServiceStatusList(new TrustStatusListType()); // _l.debug(""+JI.getElementName(tssl)); Element tsslE = tslDoc.getDocumentElement(); if (tsslE == null) { tslContext.throwException(new TSLVerificationException("Empty XML File", Severity.xml_failed)); // } else if (!tsslE.getNamespaceURI().equals(tssl.getName().getNamespaceURI())) { } else if (!tsslE.getNamespaceURI().equals(JI.getElementName(tssl).getNamespaceURI())) { tslContext.throwException(new TSLVerificationException("Incorrect Namespace", Severity.xml_failed)); // } else if (!tsslE.getLocalName().equals(tssl.getName().getLocalPart())) { } else if (!tsslE.getLocalName().equals(JI.getElementName(tssl).getLocalPart())) { tslContext.throwException(new TSLVerificationException("Wrong Document Element in document "+tslDoc.getDocumentURI(), Severity.xml_failed)); } //now we can be sure the right document element is in place, Schema validation does not assure this for us //Schema validation however assures that the internal Structure of TrustServicesStatus List is correct // B.6 1) It MUST be an enveloped signature. Node n = tsslE.getLastChild(); while ( n != null && ! (n instanceof Element) ) { n = n.getPreviousSibling(); } Element sig = (Element) n; if (sig == null || // ! sig.getNamespaceURI().equals(s.getName().getNamespaceURI()) || // ! sig.getLocalName().equals(s.getName().getLocalPart())) { ! sig.getNamespaceURI().equals(JI.getElementName(s).getNamespaceURI()) || ! sig.getLocalName().equals(JI.getElementName(s).getLocalPart())) { tslContext.throwException( new TSLVerificationException( TSLSecurityException.Type.NO_TSL_SIGNATURE) ); } else { NodeList cn = tsslE.getChildNodes(); for (int j = 0; j < cn.getLength(); j++) { cn.item(j); } //TODO assure connection with the PKI Module DOMValidateContext valContext = new DOMValidateContext( new MOATslKeySelector(euTslCertsHash, tslContext), sig); if (valContext.getURIDereferencer() == null) { valContext.setURIDereferencer(new URIDereferencerImpl()); } // valContext.setProperty("iaik.xml.crypto.debug.OutputStream", System.out); valContext.setProperty("javax.xml.crypto.dsig.cacheReference", Boolean.TRUE); XMLSignatureFactory fac = MOAXSecProvider.getXMLSignatureFactory(); // unmarshal the XMLSignature XMLSignature signature = fac.unmarshalXMLSignature(valContext); // Validate the XMLSignature (generated above) coreValidity = signature.validate(valContext); // Check core validation status if (coreValidity == false) { debug(valContext, "Signature failed core validation"); boolean sv = signature.getSignatureValue().validate(valContext); debug(valContext, "signature validation status: " + sv); // check the validation status of each Reference Iterator it = signature.getSignedInfo().getReferences().iterator(); for (int j = 0; it.hasNext(); j++) { boolean refValid = ((Reference) it.next()).validate(valContext); debug(valContext, "ref[" + j + "] validity status: " + refValid); } tslContext.throwException(new TSLVerificationException("Signature failed core validation", Severity.signature_failed)); } SignedInfo si = signature.getSignedInfo(); Iterator it = si.getReferences().iterator(); // 2) Its ds:SignedInfo element MUST contain a ds:Reference element with the // URI attribute set to a value referencing the TrustServiceStatusList // element enveloping the signature itself. This ds:Reference element MUST // satisfy the following requirements: // a) It MUST contain only one ds:Transforms element. // b) This ds:Transforms element MUST contain two ds:Transform elements. The // first one will be one whose Algorithm attribute indicates the enveloped // transformation with the value: // "http://www.w3.org/2000/09/xmldsig#enveloped-signature". The second one // will be one whose Algorithm attribute instructs to perform the exclusive // canonicalization "http://www.w3.org/2001/10/xml-exc-c14n#" boolean found_proper_tsslE_reference = false; for (int j = 0; it.hasNext(); j++) { Reference ref = ((Reference) it.next()); Data d = valContext.getURIDereferencer().dereference(ref, valContext); if(!(d instanceof NodeSetData)) { continue; } else { NodeSetData nsd = (NodeSetData) d; if (nsd.iterator().next() == tsslE) { //Assured by XMLSchema //throw new TSLException("B.6 2 a) It MUST contain only one ds:Transforms element."); if(ref.getTransforms().size() != 2) { tslContext.throwException( new TSLVerificationException(TSLSecurityException.Type.NON_CONFORMANT_TRANSFORMS_IN_TSL_SIGNATURE) ); } else { Transform[] transforms = (Transform[]) ref.getTransforms().toArray(new Transform[2]); //TODO assign severity, code some heuristic showing the problems if (! transforms[0].getAlgorithm().equals("http://www.w3.org/2000/09/xmldsig#enveloped-signature")) { tslContext.throwException( new TSLVerificationException(TSLSecurityException.Type.NON_CONFORMANT_TRANSFORM_IN_TSL_SIGNATURE) ); } //TODO assign severity, code some heuristic showing the problems if (! transforms[1].getAlgorithm().equals("http://www.w3.org/2001/10/xml-exc-c14n#")) { tslContext.throwException( new TSLVerificationException(TSLSecurityException.Type.NON_CONFORMANT_C14N_IN_TSL_SIGNATURE) ); } } found_proper_tsslE_reference = true; }//if (nsd.iterator().next() == tsslE) } } if(!found_proper_tsslE_reference) { tslContext.throwException( new TSLVerificationException(TSLSecurityException.Type.NON_CONFORMANT_REFERENCE_IN_TSL_SIGNATURE) ); } // 3) ds:CanonicalizationMethod MUST be // "http://www.w3.org/2001/10/xml-exc-c14n#". if (! si.getCanonicalizationMethod().getAlgorithm().equals("http://www.w3.org/2001/10/xml-exc-c14n#")){ tslContext.throwException( new TSLVerificationException(TSLSecurityException.Type.NON_CONFORMANT_C14N_IN_CANONICALIZATION_METHOD) ); } // 4) It MAY have other ds:Reference elements. } } catch (URIReferenceException e) { tslContext.throwException(new TSLVerificationException(e)); } catch (MarshalException e) { tslContext.throwException(new TSLVerificationException(e)); } catch (XMLSignatureException e) { logger.error("Failed to verify XML Signature for TSL!", e); return (Boolean) tslContext.throwException( new TSLSecurityException(TSLSecurityException.Type.ERRORS_IN_TSL_SIGNATURE), //we need an anonymous class to find the enclosing Method (new Object(){}).getClass().getEnclosingMethod(), null, new Object[] {tslDoc, tslContext, euTslCertsHash} ); } return coreValidity; } public static void debug(DOMCryptoContext context, String message) { Object propDebug = context.getProperty("iaik.xml.crypto.debug.OutputStream"); if ( propDebug == null) { return; } if (! (propDebug instanceof OutputStream)) { System.err.println("Failed to write to debug output stream. " + "DOMCryptoContext's Property (\"iaik.xml.crypto.debug.OutputStream\") " + "has to be of type OutputStream." ); } else { OutputStream os = (OutputStream) propDebug; try { (new OutputStreamWriter(os)).write(message); } catch (IOException e) { System.err.println("Failed to write to debug output stream. " + e.getMessage()); //TODO we cannot close the output stream here ... } } } }