You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2013/08/16 08:02:56 UTC

git commit: CAMEL-6640: Migrate XML Security key cipher algorithm away from RSA v1.5. Thanks to Colm for the patch.

Updated Branches:
  refs/heads/master 91622a51e -> 5d69e4658


CAMEL-6640: Migrate XML Security key cipher algorithm away from RSA v1.5. Thanks to Colm for the patch.


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/5d69e465
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/5d69e465
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/5d69e465

Branch: refs/heads/master
Commit: 5d69e4658e75370da2ad1ac08e7df94ffa51713c
Parents: 91622a5
Author: Claus Ibsen <da...@apache.org>
Authored: Fri Aug 16 08:02:34 2013 +0200
Committer: Claus Ibsen <da...@apache.org>
Committed: Fri Aug 16 08:02:34 2013 +0200

----------------------------------------------------------------------
 .../xmlsecurity/XMLSecurityDataFormat.java      | 116 +++++++++++++++++--
 .../xmlsecurity/XMLSecurityDataFormatTest.java  |  83 +++++++++++++
 2 files changed, 187 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/5d69e465/components/camel-xmlsecurity/src/main/java/org/apache/camel/dataformat/xmlsecurity/XMLSecurityDataFormat.java
----------------------------------------------------------------------
diff --git a/components/camel-xmlsecurity/src/main/java/org/apache/camel/dataformat/xmlsecurity/XMLSecurityDataFormat.java b/components/camel-xmlsecurity/src/main/java/org/apache/camel/dataformat/xmlsecurity/XMLSecurityDataFormat.java
index 22437a6..dfc541c 100755
--- a/components/camel-xmlsecurity/src/main/java/org/apache/camel/dataformat/xmlsecurity/XMLSecurityDataFormat.java
+++ b/components/camel-xmlsecurity/src/main/java/org/apache/camel/dataformat/xmlsecurity/XMLSecurityDataFormat.java
@@ -57,6 +57,8 @@ import org.apache.xml.security.encryption.EncryptedKey;
 import org.apache.xml.security.encryption.XMLCipher;
 import org.apache.xml.security.encryption.XMLEncryptionException;
 import org.apache.xml.security.keys.KeyInfo;
+import org.apache.xml.security.utils.Constants;
+import org.apache.xml.security.utils.EncryptionConstants;
 import org.apache.xml.security.utils.XMLUtils;
 
 
@@ -133,7 +135,6 @@ public class XMLSecurityDataFormat implements DataFormat, CamelContextAware {
     private CamelContext camelContext;
     private DefaultNamespaceContext nsContext = new DefaultNamespaceContext();
         
-
     public XMLSecurityDataFormat() {
         this.xmlCipherAlgorithm = XMLCipher.TRIPLEDES;
         // set a default pass phrase as its required
@@ -465,7 +466,7 @@ public class XMLSecurityDataFormat implements DataFormat, CamelContextAware {
         if (null != this.getKeyCipherAlgorithm()) {
             keyCipher = XMLCipher.getInstance(this.getKeyCipherAlgorithm(), null, digestAlgorithm);
         } else {
-            keyCipher = XMLCipher.getInstance(XMLCipher.RSA_v1dot5, null, digestAlgorithm);
+            keyCipher = XMLCipher.getInstance(XMLCipher.RSA_OAEP, null, digestAlgorithm);
         }
         keyCipher.init(XMLCipher.WRAP_MODE, keyEncryptionKey);
         encrypt(exchange, document, stream, dataEncryptionKey, keyCipher);
@@ -606,6 +607,7 @@ public class XMLSecurityDataFormat implements DataFormat, CamelContextAware {
         xmlCipher.setKEK(keyEncryptionKey);
 
         if (secureTag.equalsIgnoreCase("")) {
+            checkEncryptionAlgorithm(keyEncryptionKey, encodedDocument.getDocumentElement());
             encodedDocument = xmlCipher.doFinal(encodedDocument, encodedDocument.getDocumentElement());
         } else {
 
@@ -618,6 +620,7 @@ public class XMLSecurityDataFormat implements DataFormat, CamelContextAware {
                 Node node = nodeList.item(i);
                 encodedDocument = node.getOwnerDocument();
                 if (getSecureTagContents()) {
+                    checkEncryptionAlgorithm(keyEncryptionKey, (Element)node);
                     Document temp = xmlCipher.doFinal(encodedDocument, (Element) node, true);
                     encodedDocument.importNode(temp.getDocumentElement().cloneNode(true), true);
                 } else {
@@ -625,6 +628,7 @@ public class XMLSecurityDataFormat implements DataFormat, CamelContextAware {
                     for (int j = 0; j < childNodes.getLength(); j++) {
                         Node childNode = childNodes.item(j);
                         if (childNode.getLocalName().equals("EncryptedData")) {
+                            checkEncryptionAlgorithm(keyEncryptionKey, (Element) childNode);
                             Document temp = xmlCipher.doFinal(encodedDocument, (Element) childNode, false);
                             encodedDocument.importNode(temp.getDocumentElement().cloneNode(true), true);
                         }    
@@ -676,16 +680,17 @@ public class XMLSecurityDataFormat implements DataFormat, CamelContextAware {
             keyGenerator = KeyGenerator.getInstance("DESede");
         } else {
             keyGenerator = KeyGenerator.getInstance("AES");
-        }
-        if (xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_128)
-            || xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_128_GCM)) {
-            keyGenerator.init(128);
-        } else if (xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_192)
-            || xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_192_GCM)) {
-            keyGenerator.init(192);
-        } else if (xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_256)
-            || xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_256_GCM)) {
-            keyGenerator.init(256);
+        
+            if (xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_128)
+                || xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_128_GCM)) {
+                keyGenerator.init(128);
+            } else if (xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_192)
+                || xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_192_GCM)) {
+                keyGenerator.init(192);
+            } else if (xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_256)
+                || xmlCipherAlgorithm.equalsIgnoreCase(XMLCipher.AES_256_GCM)) {
+                keyGenerator.init(256);
+            }
         }
         return keyGenerator.generateKey();
     }
@@ -727,6 +732,92 @@ public class XMLSecurityDataFormat implements DataFormat, CamelContextAware {
         }
         return alias;
     }
+    
+    // Check to see if the asymmetric key transport algorithm is allowed
+    private void checkEncryptionAlgorithm(Key keyEncryptionKey, Element parentElement) throws Exception {
+        if (XMLCipher.RSA_v1dot5.equals(keyCipherAlgorithm)
+            || keyCipherAlgorithm == null
+            || !(keyEncryptionKey instanceof PrivateKey)) {
+            // This only applies for Asymmetric Encryption
+            return;
+        }
+        Element encryptedElement = findEncryptedDataElement(parentElement);
+        if (encryptedElement == null) {
+            return;
+        }
+        
+        // The EncryptedKey EncryptionMethod algorithm
+        String foundEncryptedKeyMethod = findEncryptedKeyMethod(encryptedElement);
+        if (XMLCipher.RSA_v1dot5.equals(foundEncryptedKeyMethod)) {
+            String errorMessage = "The found key transport encryption method is not allowed"; 
+            throw new XMLEncryptionException(errorMessage);
+        }
+    }
+    
+    private Element findEncryptedDataElement(Element element) {
+        // First check the Element itself
+        if (EncryptionConstants._TAG_ENCRYPTEDDATA.equals(element.getLocalName())
+            && EncryptionConstants.EncryptionSpecNS.equals(element.getNamespaceURI())) {
+            return element;
+        }
+        
+        // Now check the child nodes
+        Node child = element.getFirstChild();
+        while (child != null) {
+            if (child.getNodeType() == Node.ELEMENT_NODE) {
+                Element childElement = (Element)child;
+                if (EncryptionConstants._TAG_ENCRYPTEDDATA.equals(childElement.getLocalName())
+                    && EncryptionConstants.EncryptionSpecNS.equals(childElement.getNamespaceURI())) {
+                    return childElement;
+                }
+            }
+            child = child.getNextSibling();
+        }
+        
+        return null;
+    }
+    
+    private String findEncryptionMethod(Element element) {
+        Node child = element.getFirstChild();
+        while (child != null) {
+            if (child.getNodeType() == Node.ELEMENT_NODE) {
+                Element childElement = (Element)child;
+                if (EncryptionConstants._TAG_ENCRYPTIONMETHOD.equals(childElement.getLocalName())
+                    && EncryptionConstants.EncryptionSpecNS.equals(childElement.getNamespaceURI())) {
+                    return childElement.getAttributeNS(null, EncryptionConstants._ATT_ALGORITHM);
+                }
+            }
+            child = child.getNextSibling();
+        }
+        
+        return null;
+    }
+    
+    private String findEncryptedKeyMethod(Element element) {
+        Node child = element.getFirstChild();
+        while (child != null) {
+            if (child.getNodeType() == Node.ELEMENT_NODE) {
+                Element childElement = (Element)child;
+                if (Constants._TAG_KEYINFO.equals(childElement.getLocalName())
+                    && Constants.SignatureSpecNS.equals(childElement.getNamespaceURI())) {
+                    Node keyInfoChild = child.getFirstChild();
+                    while (keyInfoChild != null) {
+                        if (child.getNodeType() == Node.ELEMENT_NODE) {
+                            childElement = (Element)keyInfoChild;
+                            if (EncryptionConstants._TAG_ENCRYPTEDKEY.equals(childElement.getLocalName())
+                                && EncryptionConstants.EncryptionSpecNS.equals(childElement.getNamespaceURI())) {
+                                return findEncryptionMethod(childElement);
+                            }
+                        }
+                        keyInfoChild = keyInfoChild.getNextSibling();
+                    }
+                }
+            }
+            child = child.getNextSibling();
+        }
+        
+        return null;
+    }
      
     private DefaultNamespaceContext getNamespaceContext() {
         return this.nsContext;
@@ -923,4 +1014,5 @@ public class XMLSecurityDataFormat implements DataFormat, CamelContextAware {
     public void setMgfAlgorithm(String mgfAlgorithm) {
         this.mgfAlgorithm = mgfAlgorithm;
     }
+
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/5d69e465/components/camel-xmlsecurity/src/test/java/org/apache/camel/dataformat/xmlsecurity/XMLSecurityDataFormatTest.java
----------------------------------------------------------------------
diff --git a/components/camel-xmlsecurity/src/test/java/org/apache/camel/dataformat/xmlsecurity/XMLSecurityDataFormatTest.java b/components/camel-xmlsecurity/src/test/java/org/apache/camel/dataformat/xmlsecurity/XMLSecurityDataFormatTest.java
index 4e1c3cf..a1f0b83 100755
--- a/components/camel-xmlsecurity/src/test/java/org/apache/camel/dataformat/xmlsecurity/XMLSecurityDataFormatTest.java
+++ b/components/camel-xmlsecurity/src/test/java/org/apache/camel/dataformat/xmlsecurity/XMLSecurityDataFormatTest.java
@@ -372,4 +372,87 @@ public class XMLSecurityDataFormatTest extends CamelTestSupport {
         xmlsecTestHelper.testDecryption(TestHelper.NS_XML_FRAGMENT, context);
     }
     
+    @Test
+    public void testAsymmetricEncryptionAlgorithmFullPayload() throws Exception {
+                      
+        final KeyStoreParameters tsParameters = new KeyStoreParameters();
+        tsParameters.setPassword("password");
+        tsParameters.setResource("sender.ts");
+        
+        final KeyStoreParameters ksParameters = new KeyStoreParameters();
+        ksParameters.setPassword("password");
+        ksParameters.setResource("recipient.ks");
+
+        // RSA v1.5 is not allowed unless explicitly configured
+        context.addRoutes(new RouteBuilder() {
+            public void configure() {
+                from("direct:start")
+                    .marshal().secureXML("", true, "recipient", testCypherAlgorithm, XMLCipher.RSA_v1dot5, tsParameters).to("mock:encrypted")
+                    .unmarshal().secureXML("", true, "recipient", testCypherAlgorithm, XMLCipher.RSA_OAEP, ksParameters).to("mock:decrypted");
+            }
+        });
+        
+        MockEndpoint resultEndpoint = context.getEndpoint("mock:decrypted", MockEndpoint.class);
+        resultEndpoint.setExpectedMessageCount(0);
+        // verify that the message was encrypted before checking that it is decrypted
+        xmlsecTestHelper.testEncryption(TestHelper.XML_FRAGMENT, context);
+        
+        resultEndpoint.assertIsSatisfied(100);
+    }
+    
+    @Test
+    public void testAsymmetricEncryptionAlgorithmPartialPayload() throws Exception {
+                      
+        final KeyStoreParameters tsParameters = new KeyStoreParameters();
+        tsParameters.setPassword("password");
+        tsParameters.setResource("sender.ts");
+        
+        final KeyStoreParameters ksParameters = new KeyStoreParameters();
+        ksParameters.setPassword("password");
+        ksParameters.setResource("recipient.ks");
+
+        // RSA v1.5 is not allowed unless explicitly configured
+        context.addRoutes(new RouteBuilder() {
+            public void configure() {
+                from("direct:start")
+                    .marshal().secureXML("//cheesesites/italy", true, "recipient", testCypherAlgorithm, XMLCipher.RSA_v1dot5, tsParameters).to("mock:encrypted")
+                    .unmarshal().secureXML("//cheesesites/italy", true, "recipient", testCypherAlgorithm, XMLCipher.RSA_OAEP, ksParameters).to("mock:decrypted");
+            }
+        });
+        
+        MockEndpoint resultEndpoint = context.getEndpoint("mock:decrypted", MockEndpoint.class);
+        resultEndpoint.setExpectedMessageCount(0);
+        // verify that the message was encrypted before checking that it is decrypted
+        xmlsecTestHelper.testEncryption(TestHelper.XML_FRAGMENT, context);
+        
+        resultEndpoint.assertIsSatisfied(100);
+    }
+    
+    @Test
+    public void testAsymmetricEncryptionAlgorithmPartialPayloadElement() throws Exception {
+                      
+        final KeyStoreParameters tsParameters = new KeyStoreParameters();
+        tsParameters.setPassword("password");
+        tsParameters.setResource("sender.ts");
+        
+        final KeyStoreParameters ksParameters = new KeyStoreParameters();
+        ksParameters.setPassword("password");
+        ksParameters.setResource("recipient.ks");
+
+        // RSA v1.5 is not allowed unless explicitly configured
+        context.addRoutes(new RouteBuilder() {
+            public void configure() {
+                from("direct:start")
+                    .marshal().secureXML("//cheesesites/france/cheese", false, "recipient", testCypherAlgorithm, XMLCipher.RSA_v1dot5, tsParameters).to("mock:encrypted")
+                    .unmarshal().secureXML("//cheesesites/france", false, "recipient", testCypherAlgorithm, XMLCipher.RSA_OAEP, ksParameters).to("mock:decrypted");
+            }
+        });
+        
+        MockEndpoint resultEndpoint = context.getEndpoint("mock:decrypted", MockEndpoint.class);
+        resultEndpoint.setExpectedMessageCount(0);
+        // verify that the message was encrypted before checking that it is decrypted
+        xmlsecTestHelper.testEncryption(TestHelper.XML_FRAGMENT, context);
+        
+        resultEndpoint.assertIsSatisfied(100);
+    }
 }