> From my point of view I shouldn't add this namespace attribute to
generate a valid signature.

The problem is that without adding the attribute, the XML you are producing
is not valid as the "ds" prefix used to define KeyInfo can't be resolved.

Santuario appears to be adding in the default XML Signature namespace
associated with the "ds" prefix on the receiving side, hence the signature
validation failure. I guess this could be considered a bug, but not a very
important one, as the real problem is that your XML is not valid.

Colm.

On Sun, Nov 30, 2014 at 9:43 AM, Roman Podstawa <[email protected]> wrote:

> Hello,
>
> I’m working on code which should first encrypt Body element content (xml
> is a soap message) and then sign this encrypted xml. I’ve noticed a strange
> behavior. I’ve prepared a unit tests to show where the problem is:
>
> public class MyTest extends org.junit.Assert
> {
>     String
> mainKey="MIIC+DCCAeSgAwIBAgIQAw19soViIYFA0LfhGDibeTAJBgUrDgMCHQUAMBUxEzARBgNVBAMTClJvb3RDQVRlc3QwHhcNMTQwNDE0MDczMzUyWhcNMzkxMjMxMjM1OTU5WjAXMRUwEwYDVQQDEwxyb2JvY2lrUHJhY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQClHbEhJZiBIjWW0pc2Efpns/xTxXvSl11NlNGqCd6teCVpJd/PAySG/f8oUFyf3LW7wHLhKGbAtg1Lf4pO4KJbWR9BxFyuImwtIqtLU7YAVnPkIpOmn1QFul8KqUl4W5pklh+8HLm7qZHZWOsF0y6gGy4cN2rI/yiSy+65ee7sQUCVWk9AKFoexyf5V3x2WnDZr22BOCrxfMnyhk8Exxj2AsLSt+q94BKzmdh9hVOcvaZivmMrqgRCrF9HZZtF3cek2VFkXIRiOVkiDqKl++k611nKtroeKTRMeJr3seowh6ZfYKdMluf/v3YwDrhEvAzX9wUxWTqxML3Ve0NGPTSJAgMBAAGjSjBIMEYGA1UdAQQ/MD2AEDuq/TTQAqG/IzsZKVWFIF6hFzAVMRMwEQYDVQQDEwpSb290Q0FUZXN0ghAE58iucYM4tULQsZhppop1MAkGBSsOAwIdBQADggEBABy5DeYmLY5L2Zp5JeiwNov1oteej1h01j8fSHjj2JgTiITAHRI4IVcKpa/r4zSYXrtnXsrfmmMZorz6viFBpog4f6BmNIQTzaW1CNEvbaRAEr0U0NRc5gfDL/albdnx8cFu+4QZ9elQd7s7Xf5De0ceC34m1k9gRyzn5axNf7BwSxLeHp1PbFDQPIKDtzNF0/bvoJmDVQi8pGEJIp/LhWeQ7vWNZovGpFmlvuDmOsZp4U+m3/AcRZlKfHn5naWKZUYRMR7VE0UMnirJ3Lfc2r6Ur0AHqQ5PHb7YqgGL3RZSGdh30JUYPlbsyU+iaNRRiQswDZnmgmqHyg/9Q8XJtU0=";
>     SecretKey secretKey;
>     PublicKey pk;
>     String xml="<v:Envelope xmlns:v=\"
> http://www.w3.org/2003/05/soap-envelope\"; xmlns:i=\"
> http://www.w3.org/2001/XMLSchema-instance\"; xmlns:d=\"
> http://www.w3.org/2001/XMLSchema\"; xmlns:c=\"
> http://www.w3.org/2003/05/soap-encoding\"; xmlns:u=\"
> http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\";
> xmlns:a=\"http://www.w3.org/2005/08/addressing\"; xmlns:s=\"
> http://www.w3.org/2003/05/soap-envelope\";><v:Header><n0:Action xmlns:n0=\"
> http://www.w3.org/2005/08/addressing\"; v:mustUnderstand=\"1\"
> u:Id=\"uuid-5061ec6e-f05b-4c3f-960d-815ee74ba8ad\">
> http://tempuri.org/IWS_StarService/HelloWorld</n0:Action><n1:MessageID
> xmlns:n1=\"http://www.w3.org/2005/08/addressing\";
> u:Id=\"uuid-a5a15cfa-21cb-4842-b9b3-83ca109cb4ca\">urn:uuid:814a0516-6d5c-4812-86dc-7f3f5d0b2ed2</n1:MessageID><n2:ReplyTo
> xmlns:n2=\"http://www.w3.org/2005/08/addressing\";
> u:Id=\"uuid-5f957f93-d071-440c-9278-24b79fd8c611\"><n2:Address>
> http://www.w3.org/2005/08/addressing/anonymous</n2:Address></n2:ReplyTo><n3:To
> xmlns:n3=\"http://www.w3.org/2005/08/addressing\"; v:mustUnderstand=\"1\"
> u:Id=\"uuid-01269a2c-cf46-49bf-97ec-bf6bc6e248b6\">
> http://localhost:4774/API/WS_StarService.svc/Test</n3:To><Security
> xmlns=\"
> http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\";
> s:mustUnderstand=\"1\"><u:Timestamp
> u:Id=\"uuid-3367eb13-35c7-4b60-8ea9-a83e31de989f\"><u:Created>2014-11-29T08:12:50.297Z</u:Created><u:Expires>2014-11-29T14:12:50.297Z</u:Expires></u:Timestamp></Security></v:Header><v:Body
> u:Id=\"_0\"><n4:HelloWorld xmlns:n4=\"http://tempuri.org/\";><n4:message>my
> message</n4:message></n4:HelloWorld></v:Body></v:Envelope>";
>
>     public MyTest() throws NoSuchAlgorithmException, CertificateException,
> Base64DecodingException {
>         InputStream mainKeyStream = new
> ByteArrayInputStream(Base64.decode(mainKey) );
>         CertificateFactory cf = CertificateFactory.getInstance("X.509");
>         X509Certificate cert =
> (X509Certificate)cf.generateCertificate(mainKeyStream);
>         pk =cert.getPublicKey();
>
>         KeyGenerator keyGen = KeyGenerator.getInstance("AES");
>         secretKey = keyGen.generateKey();
>         org.apache.xml.security.Init.init();
>     }
>
>     @org.junit.Test
>     public void test_sign_document_after_writing_and_reading_from_stream()
> throws Exception {
>
>         Document doc1=signDocument(false);//DIFFERENCE IN THE LAST
> PARAMETER!
>
>         //here we first write xml to stream and read it again. This
> simulates sending soap message and receiving it on the another side
>         InputStream outputStream = documentToStream(doc1);
>         doc1=streamToDocument(outputStream);
>
>         boolean isValid1=ensureValidSignature(doc1,secretKey);
>
>         assertTrue(isValid1);
>     }
>
>     @org.junit.Test
>     public void
> test_sign_document_with_additional_NS_after_writing_and_reading_from_stream()
> throws Exception {
>
>         Document doc1=signDocument(true);//DIFFERENCE IN THE LAST
> PARAMETER!
>
>         //here we first write xml to stream and read it again. This
> simulates sending soap message and receiving it on the another side
>         InputStream outputStream = documentToStream(doc1);
>         doc1=streamToDocument(outputStream);
>
>         boolean isValid1=ensureValidSignature(doc1,secretKey);
>
>         assertTrue(isValid1);
>     }
>
>     @org.junit.Test
>     public void test_sign_document() throws Exception {
>
>         Document doc1=signDocument(false);//DIFFERENCE IN THE LAST
> PARAMETER!
>
>         boolean isValid1=ensureValidSignature(doc1,secretKey);
>
>         assertTrue(isValid1);
>     }
>
>     @org.junit.Test
>     public void test_sign_document_with_additional_NS() throws Exception {
>
>         Document doc1=signDocument(true);//DIFFERENCE IN THE LAST
> PARAMETER!
>
>         boolean isValid1=ensureValidSignature(doc1,secretKey);
>
>         assertTrue(isValid1);
>     }
>
>     Document signDocument(boolean addNamespaceAttribute) throws Exception {
>         InputStream stream = new ByteArrayInputStream(xml.getBytes() );
>         Document doc=streamToDocument(stream);
>
>         NodeList nodes = doc.getElementsByTagNameNS("
> http://www.w3.org/2003/05/soap-envelope","Body";);
>         Element bodyElement= (Element) nodes.item(0);
>         bodyElement.setAttributeNS("
> http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";,
> "u:Id", "_0");
>         bodyElement.setIdAttributeNS("
> http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";,
> "Id", true);
>
>         XMLCipher clipper = XMLCipher.getInstance(XMLCipher.RSA_v1dot5);
>         clipper.init(XMLCipher.WRAP_MODE,pk);
>         clipper.setKEK(pk);
>
>         EncryptedKey encryptedKey=clipper.encryptKey(doc,secretKey);
>         Element encryptedKeyElement=clipper.martial(encryptedKey);
>
>         Element securityElement= (Element) doc.getElementsByTagNameNS("
> http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd
> ","Security").item(0);
>         securityElement.appendChild(encryptedKeyElement);
>
>         clipper = XMLCipher.getInstance(XMLCipher.AES_128);
>         clipper.init(XMLCipher.ENCRYPT_MODE,secretKey);
>         clipper.setKEK(secretKey);
>
>         EncryptedData encryptedBody=clipper.getEncryptedData();
>         String encryptedBodyId="uuid-"+ UUID.randomUUID().toString();
>
>
>         bodyElement= (Element) doc.getElementsByTagNameNS("
> http://www.w3.org/2003/05/soap-envelope","Body";).item(0);
>         encryptedBody.setId(encryptedBodyId);
>
>         KeyInfo keyInfo = new KeyInfo(doc);
>         encryptedBody.setKeyInfo(keyInfo);
>         Element securityTokenReferenceElement=doc.createElementNS("
> http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd
> ","SecurityTokenReference");
>         securityTokenReferenceElement.setAttributeNS("
> http://www.w3.org/2000/xmlns/","xmlns",";
> http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd
> ");
>         securityTokenReferenceElement.setAttributeNS("
> http://www.w3.org/2000/xmlns/","xmlns:k",";
> http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd";);
>         securityTokenReferenceElement.setAttributeNS("
> http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd
> ","k:TokenType","
> http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey
> ");
>         keyInfo.addUnknownElement(securityTokenReferenceElement);
>
>         if(addNamespaceAttribute)
>         {
>             //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! THIS IS ONLY ONE DIFFERENCE.
> WE ADD A NAMESPACE ATTRIBUTE EXPLICITLY
>             Element keyInfoElement=keyInfo.getElement();
>             keyInfoElement.setAttributeNS("http://www.w3.org/2000/xmlns/
> ","xmlns:ds","http://www.w3.org/2000/09/xmldsig#";);
>         }
>
>         clipper.doFinal(doc,bodyElement,true);
>
>         //now sign xml
>         XMLSignature sig =  new XMLSignature(doc, "",
> XMLSignature.ALGO_ID_MAC_HMAC_SHA1,
> Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
>
>         org.apache.xml.security.transforms.Transforms transforms = new
> org.apache.xml.security.transforms.Transforms(doc);
>
> transforms.addTransform(org.apache.xml.security.transforms.Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
>         sig.addDocument("#_0", transforms);
>
>         Element elemt=sig.getElement();
>
>         securityElement.appendChild(elemt);
>
>         sig.sign(secretKey);
>         return doc;
>     }
>
>     Document streamToDocument(InputStream stream) throws
> ParserConfigurationException, IOException, SAXException {
>         DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
>         f.setNamespaceAware(true);
>
>         DocumentBuilder builder = f.newDocumentBuilder();
>         builder.setErrorHandler(new
> org.apache.xml.security.utils.IgnoreAllErrorHandler());
>
>         Document doc = builder.parse(new BufferedInputStream(stream));
>         return doc;
>     }
>
>     InputStream documentToStream(Document doc) throws TransformerException
> {
>         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
>         Source xmlSource = new DOMSource(doc);
>         javax.xml.transform.Result outputTarget = new
> StreamResult(outputStream);
>
> TransformerFactory.newInstance().newTransformer().transform(xmlSource,
> outputTarget);
>         InputStream is = new
> ByteArrayInputStream(outputStream.toByteArray());
>         return is;
>     }
>
>     boolean ensureValidSignature(Document doc,SecretKey secretKey) throws
> XPathExpressionException, XMLSecurityException {
>         NodeList
> signs1=doc.getElementsByTagNameNS(Constants.SignatureSpecNS, "Signature");
>         if(signs1.getLength()>0)
>         {
>             NodeList nodes = doc.getElementsByTagNameNS("
> http://www.w3.org/2003/05/soap-envelope";, "Body");
>             Element bodyElement= (Element) nodes.item(0);
>             bodyElement.setIdAttributeNS("
> http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";,
> "Id", true);
>
>             Element signElem= (Element) signs1.item(0);
>             XMLSignature signature = new XMLSignature(signElem, "test");
>             boolean isValid=signature.checkSignatureValue(secretKey);
>             return isValid;
>         }
>         return false;
>     }
> }
>
> Entire code is quite long but the problem is easy to explain. So there are
> 4 tests. 3 of them pass, 1 fails (at least on my machine). Basically when I
> create a DOM Document, encrypt and sign it and then verify a signature then
> everything works great (test: test_sign_document and
>  test_sign_document_with_additional_NS). But when I first write the signed
> DOM document to stream and then read it again (and create a DOM Document)
> and then I verify a signature then results are different. Two additional
> tests shows this situation and as you can see one test pass and second
> fails. When you see how xml looks in both tests (after writing them to file
> or String variable) then you will notice that they are almost the same (I
> mean the Body part). In both xml (after serialization)
> Envelope\Body\EncryptedData\KeyInfo there is a namespace attribute (
> http://www.w3.org/2000/09/xmldsig#). But code to calculate signature
> create a digest value without this namespace attribute. In other words when
> you compare testes:
> test_sign_document_after_writing_and_reading_from_stream and
> test_sign_document_with_additional_NS_after_writing_and_reading_from_stream
> you will see that the only difference is that in working tests I add this
> attribute manually:
>
> if(addNamespaceAttribute)
> {
>     //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! THIS IS ONLY ONE DIFFERENCE. WE ADD
> A NAMESPACE ATTRIBUTE EXPLICITLY
>     Element keyInfoElement=keyInfo.getElement();
> keyInfoElement.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:ds",";
> http://www.w3.org/2000/09/xmldsig#";);
> }
> But as I mentioned after writing xml to String this attribute is in both
> xml content. From my point of view I shouldn't add this namespace attribute
> to generate a valid signature.
>
> Is this a bug?
>
> Thanks
> Romek
>



-- 
Colm O hEigeartaigh

Talend Community Coder
http://coders.talend.com

Reply via email to