Author: anil.saldhana(a)jboss.com
Date: 2009-05-26 11:43:37 -0400 (Tue, 26 May 2009)
New Revision: 536
Removed:
identity-federation/trunk/jboss-identity-xmlsec-model/src/main/java/org/jboss/identity/xmlsec/util/XMLEncryptionUtil.java
Modified:
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPRedirectWithSignatureValve.java
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/sp/SPRedirectSignatureFormAuthenticator.java
identity-federation/trunk/jboss-identity-fed-api/src/test/java/org/jboss/test/identity/federation/api/util/XMLEncryptionUnitTestCase.java
identity-federation/trunk/jboss-identity-fed-core/src/main/java/org/jboss/identity/federation/core/util/XMLEncryptionUtil.java
identity-federation/trunk/jboss-identity-fed-model/pom.xml
Log:
JBID-112: consolidate the xmlenc utility
Modified:
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPRedirectWithSignatureValve.java
===================================================================
---
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPRedirectWithSignatureValve.java 2009-05-26
15:25:25 UTC (rev 535)
+++
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPRedirectWithSignatureValve.java 2009-05-26
15:43:37 UTC (rev 536)
@@ -56,7 +56,7 @@
import org.jboss.identity.federation.core.saml.v2.util.SignatureUtil;
import org.jboss.identity.federation.saml.v2.assertion.EncryptedElementType;
import org.jboss.identity.federation.saml.v2.protocol.ResponseType;
-import org.jboss.identity.xmlsec.util.XMLEncryptionUtil;
+import org.jboss.identity.federation.core.util.XMLEncryptionUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
Modified:
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/sp/SPRedirectSignatureFormAuthenticator.java
===================================================================
---
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/sp/SPRedirectSignatureFormAuthenticator.java 2009-05-26
15:25:25 UTC (rev 535)
+++
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/sp/SPRedirectSignatureFormAuthenticator.java 2009-05-26
15:43:37 UTC (rev 536)
@@ -45,7 +45,7 @@
import org.jboss.identity.federation.core.saml.v2.util.SignatureUtil;
import org.jboss.identity.federation.saml.v2.assertion.EncryptedElementType;
import org.jboss.identity.federation.saml.v2.protocol.ResponseType;
-import org.jboss.identity.xmlsec.util.XMLEncryptionUtil;
+import org.jboss.identity.federation.core.util.XMLEncryptionUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
Modified:
identity-federation/trunk/jboss-identity-fed-api/src/test/java/org/jboss/test/identity/federation/api/util/XMLEncryptionUnitTestCase.java
===================================================================
---
identity-federation/trunk/jboss-identity-fed-api/src/test/java/org/jboss/test/identity/federation/api/util/XMLEncryptionUnitTestCase.java 2009-05-26
15:25:25 UTC (rev 535)
+++
identity-federation/trunk/jboss-identity-fed-api/src/test/java/org/jboss/test/identity/federation/api/util/XMLEncryptionUnitTestCase.java 2009-05-26
15:43:37 UTC (rev 536)
@@ -43,11 +43,11 @@
import org.jboss.identity.federation.core.saml.v2.holders.IssuerInfoHolder;
import org.jboss.identity.federation.core.saml.v2.holders.SPInfoHolder;
import org.jboss.identity.federation.core.saml.v2.util.DocumentUtil;
+import org.jboss.identity.federation.core.util.XMLEncryptionUtil;
import org.jboss.identity.federation.saml.v2.assertion.AssertionType;
import org.jboss.identity.federation.saml.v2.assertion.AttributeStatementType;
import org.jboss.identity.federation.saml.v2.assertion.EncryptedElementType;
-import org.jboss.identity.federation.saml.v2.protocol.ResponseType;
-import org.jboss.identity.xmlsec.util.XMLEncryptionUtil;
+import org.jboss.identity.federation.saml.v2.protocol.ResponseType;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
Modified:
identity-federation/trunk/jboss-identity-fed-core/src/main/java/org/jboss/identity/federation/core/util/XMLEncryptionUtil.java
===================================================================
---
identity-federation/trunk/jboss-identity-fed-core/src/main/java/org/jboss/identity/federation/core/util/XMLEncryptionUtil.java 2009-05-26
15:25:25 UTC (rev 535)
+++
identity-federation/trunk/jboss-identity-fed-core/src/main/java/org/jboss/identity/federation/core/util/XMLEncryptionUtil.java 2009-05-26
15:43:37 UTC (rev 536)
@@ -21,17 +21,49 @@
*/
package org.jboss.identity.federation.core.util;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
import java.util.HashMap;
+import javax.crypto.SecretKey;
+import javax.xml.namespace.QName;
+
+import org.apache.xml.security.encryption.EncryptedData;
+import org.apache.xml.security.encryption.EncryptedKey;
import org.apache.xml.security.encryption.XMLCipher;
+import org.apache.xml.security.exceptions.XMLSecurityException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
/**
* Utility for XML Encryption
+ * <b>Note: </b> This utility is currently using Apache XML Security
+ * library API. JSR-106 is not yet final. Until that happens,we
+ * rely on the non-standard API.
+ *
* @author Anil.Saldhana(a)redhat.com
* @since May 4, 2009
*/
public class XMLEncryptionUtil
{
+ static
+ {
+ //Initialize the Apache XML Security Library
+ org.apache.xml.security.Init.init();
+ }
+
+ public static final String CIPHER_DATA_LOCALNAME = "CipherData";
+ public static final String ENCRYPTED_KEY_LOCALNAME = "EncryptedKey";
+ public static final String DS_KEY_INFO = "ds:KeyInfo";
+
+ public static final String XMLNS = "http://www.w3.org/2000/xmlns/";
+ public static String XMLSIG_NS = "http://www.w3.org/2000/09/xmldsig#";
+ public static String XMLENC_NS = "http://www.w3.org/2001/04/xmlenc#";
+
+
private static HashMap <String, EncryptionAlgorithm> algorithms
= new HashMap<String, EncryptionAlgorithm>(4);
@@ -85,4 +117,338 @@
throw new RuntimeException("Unknown jce algorithm:" + certAlgo);
return ea.size;
}
+
+
+
+ /**
+ * <p>
+ * Encrypt the Key to be transported
+ * </p>
+ * <p>
+ * Data is encrypted with a SecretKey. Then the key needs to be
+ * transported to the other end where it is needed for decryption.
+ * For the Key transport, the SecretKey is encrypted with the
+ * recipient's public key. At the receiving end, the receiver
+ * can decrypt the Secret Key using his private key.s
+ * </p>
+ * @param document
+ * @param keyToBeEncrypted Symmetric Key (SecretKey)
+ * @param keyUsedToEncryptSecretKey Asymmetric Key (Public Key)
+ * @param keySize Length of the key
+ * @return
+ * @throws XMLSecurityException
+ */
+ public static EncryptedKey encryptKey(Document document,
+ SecretKey keyToBeEncrypted, PublicKey keyUsedToEncryptSecretKey,
+ int keySize) throws XMLSecurityException
+ {
+ XMLCipher keyCipher = null;
+ String pubKeyAlg = keyUsedToEncryptSecretKey.getAlgorithm();
+
+ String keyWrapAlgo = getXMLEncryptionURLForKeyUnwrap(pubKeyAlg, keySize);
+ keyCipher = XMLCipher.getInstance(keyWrapAlgo);
+
+ keyCipher.init(XMLCipher.WRAP_MODE, keyUsedToEncryptSecretKey);
+ return keyCipher.encryptKey(document, keyToBeEncrypted);
+ }
+
+ /**
+ * Given an element in a Document, encrypt the element and replace
+ * the element in the document with the encrypted data
+ * @param elementQName QName of the element that we like to encrypt
+ * @param publicKey
+ * @param secretKey
+ * @param keySize
+ * @param wrappingElementQName A QName of an element that will wrap the encrypted
element
+ * @param addEncryptedKeyInKeyInfo Need for the EncryptedKey to be placed in
ds:KeyInfo
+ * @return
+ * @throws Exception
+ */
+ public static void encryptElement(QName elementQName,
+ Document document,
+ PublicKey publicKey,
+ SecretKey secretKey, int keySize, QName wrappingElementQName,
+ boolean addEncryptedKeyInKeyInfo) throws Exception
+ {
+ if(elementQName == null)
+ throw new IllegalArgumentException("elementQName is null");
+ if(document == null)
+ throw new IllegalArgumentException("document is null");
+ String wrappingElementPrefix = wrappingElementQName.getPrefix();
+ if(wrappingElementPrefix == null || wrappingElementPrefix == "")
+ throw new IllegalArgumentException("Wrapping element prefix
invalid");
+
+ NodeList elements = document.getElementsByTagName(elementQName.toString());
+ if(elements == null || elements.getLength() > 1)
+ throw new IllegalStateException("Element was either null or more than
one:"+elements);
+ Element documentElement = (Element) elements.item(0);
+
+ if(documentElement == null)
+ throw new IllegalStateException("Element could not be found in the
document:"+ elementQName.toString());
+
+ XMLCipher cipher = null;
+ EncryptedKey encryptedKey = encryptKey(document, secretKey, publicKey, keySize);
+
+ String encryptionAlgorithm = getXMLEncryptionURL(secretKey.getAlgorithm(),
keySize);
+ //Encrypt the Document
+ cipher = XMLCipher.getInstance(encryptionAlgorithm);
+ cipher.init(XMLCipher.ENCRYPT_MODE, secretKey);
+
+ Document encryptedDoc = cipher.doFinal(document, documentElement);
+
+ // The EncryptedKey element is added
+ Element encryptedKeyElement = cipher.martial(document, encryptedKey);
+
+ String wrappingElementName = wrappingElementPrefix + ":" +
wrappingElementQName.getLocalPart();
+
+ //Create the wrapping element and set its attribute NS
+ Element wrappingElement =
encryptedDoc.createElementNS(wrappingElementQName.getNamespaceURI(),
+ wrappingElementName);
+
+ if(wrappingElementPrefix == null || wrappingElementPrefix == "")
+ {
+ wrappingElementName = wrappingElementQName.getLocalPart();
+ }
+ wrappingElement.setAttributeNS(XMLNS,
+ "xmlns:" + wrappingElementPrefix,
wrappingElementQName.getNamespaceURI());
+
+ //Get Hold of the Cipher Data
+ NodeList cipherElements = encryptedDoc.getElementsByTagNameNS(XMLENC_NS,
"EncryptedData");
+ if(cipherElements == null || cipherElements.getLength() == 0)
+ throw new IllegalStateException("xenc:EncryptedData Element
Missing");
+ Element encryptedDataElement = (Element) cipherElements.item(0);
+
+ Node parentOfEncNode = encryptedDataElement.getParentNode();
+ parentOfEncNode.replaceChild(wrappingElement, encryptedDataElement);
+
+ wrappingElement.appendChild(encryptedDataElement);
+
+ if (addEncryptedKeyInKeyInfo)
+ {
+ // Outer ds:KeyInfo Element to hold the EncryptionKey
+ Element sigElement = encryptedDoc.createElementNS(XMLSIG_NS, DS_KEY_INFO);
+ sigElement.setAttributeNS(XMLNS, "xmlns:ds", XMLSIG_NS);
+ sigElement.appendChild(encryptedKeyElement);
+
+ //Insert the Encrypted key before the CipherData element
+ NodeList nodeList = encryptedDoc.getElementsByTagNameNS(XMLENC_NS,
CIPHER_DATA_LOCALNAME);
+ if (nodeList == null || nodeList.getLength() == 0)
+ throw new IllegalStateException("xenc:CipherData Element Missing");
+ Element cipherDataElement = (Element) nodeList.item(0);
+ Node cipherParent = cipherDataElement.getParentNode();
+ cipherParent.insertBefore(sigElement, cipherDataElement);
+ }
+ else
+ {
+ //Add the encrypted key as a child of the wrapping element
+ wrappingElement.appendChild(encryptedKeyElement);
+ }
+ }
+
+
+ /**
+ * Encrypt the root document element inside a Document.
+ * <b>NOTE:</> The document root element will be replaced
+ * by the wrapping element.
+ *
+ * @param document Document that contains an element to encrypt
+ * @param publicKey The Public Key used to encrypt the secret encryption key
+ * @param secretKey The secret encryption key
+ * @param keySize Length of key
+ * @param wrappingElementQName QName of the element to be used to wrap around
+ * the cipher data.
+ * @param addEncryptedKeyInKeyInfo Should the encrypted key be inside a KeyInfo
+ * or added as a peer of Cipher Data
+ * @return An element that has the wrappingElementQName
+ * @throws Exception
+ */
+ public static Element encryptElementInDocument(Document document,
+ PublicKey publicKey,
+ SecretKey secretKey, int keySize, QName wrappingElementQName,
+ boolean addEncryptedKeyInKeyInfo) throws Exception
+ {
+ String wrappingElementPrefix = wrappingElementQName.getPrefix();
+ if(wrappingElementPrefix == null || wrappingElementPrefix == "")
+ throw new IllegalArgumentException("Wrapping element prefix
invalid");
+
+ XMLCipher cipher = null;
+ EncryptedKey encryptedKey = encryptKey(document, secretKey, publicKey, keySize);
+
+ String encryptionAlgorithm = getXMLEncryptionURL(secretKey.getAlgorithm(),
keySize);
+ //Encrypt the Document
+ cipher = XMLCipher.getInstance(encryptionAlgorithm);
+ cipher.init(XMLCipher.ENCRYPT_MODE, secretKey);
+
+ Document encryptedDoc = cipher.doFinal(document, document.getDocumentElement());
+
+ // The EncryptedKey element is added
+ Element encryptedKeyElement = cipher.martial(document, encryptedKey);
+
+ String wrappingElementName = wrappingElementPrefix + ":" +
wrappingElementQName.getLocalPart();
+
+ //Create the wrapping element and set its attribute NS
+ Element wrappingElement =
encryptedDoc.createElementNS(wrappingElementQName.getNamespaceURI(),
+ wrappingElementName);
+
+ if(wrappingElementPrefix == null || wrappingElementPrefix == "")
+ {
+ wrappingElementName = wrappingElementQName.getLocalPart();
+ }
+ wrappingElement.setAttributeNS(XMLNS,
+ "xmlns:" + wrappingElementPrefix,
wrappingElementQName.getNamespaceURI());
+
+ Element encryptedDocRootElement = encryptedDoc.getDocumentElement();
+ //Bring in the encrypted wrapping element to wrap the root node
+ encryptedDoc.replaceChild(wrappingElement, encryptedDocRootElement);
+
+ wrappingElement.appendChild(encryptedDocRootElement);
+
+ if (addEncryptedKeyInKeyInfo)
+ {
+ // Outer ds:KeyInfo Element to hold the EncryptionKey
+ Element sigElement = encryptedDoc.createElementNS(XMLSIG_NS, DS_KEY_INFO);
+ sigElement.setAttributeNS(XMLNS, "xmlns:ds", XMLSIG_NS);
+ sigElement.appendChild(encryptedKeyElement);
+
+ //Insert the Encrypted key before the CipherData element
+ NodeList nodeList = encryptedDocRootElement.getElementsByTagNameNS(XMLENC_NS,
CIPHER_DATA_LOCALNAME);
+ if (nodeList == null || nodeList.getLength() == 0)
+ throw new IllegalStateException("xenc:CipherData Element Missing");
+
+ Element cipherDataElement = (Element) nodeList.item(0);
+ encryptedDocRootElement.insertBefore(sigElement, cipherDataElement);
+ }
+ else
+ {
+ //Add the encrypted key as a child of the wrapping element
+ wrappingElement.appendChild(encryptedKeyElement);
+ }
+
+ return encryptedDoc.getDocumentElement();
+ }
+
+ /**
+ * Decrypt an encrypted element inside a document
+ * @param documentWithEncryptedElement
+ * @param privateKey key need to unwrap the encryption key
+ * @return the document with the encrypted element replaced by the data element
+ * @throws Exception
+ */
+ public static Element decryptElementInDocument(Document documentWithEncryptedElement,
+ PrivateKey privateKey) throws Exception
+ {
+ if(documentWithEncryptedElement == null)
+ throw new IllegalArgumentException("Input document is null");
+
+ //Look for encrypted data element
+ Element documentRoot = documentWithEncryptedElement.getDocumentElement();
+ Element encDataElement = getNextElementNode(documentRoot.getFirstChild());
+ if(encDataElement == null)
+ throw new IllegalStateException("No element representing the encrypted data
found");
+
+ //Look at siblings for the key
+ Element encKeyElement = getNextElementNode(encDataElement.getNextSibling());
+ if(encKeyElement == null)
+ {
+ //Search the enc data element for enc key
+ NodeList nodeList = encDataElement.getElementsByTagNameNS( XMLENC_NS,
ENCRYPTED_KEY_LOCALNAME);
+
+ if(nodeList == null || nodeList.getLength() == 0)
+ throw new IllegalStateException("Encrypted Key not found in the enc
data");
+
+ encKeyElement = (Element) nodeList.item(0);
+ }
+
+ XMLCipher cipher = XMLCipher.getInstance();
+ cipher.init(XMLCipher.DECRYPT_MODE, null);
+ EncryptedData encryptedData =
cipher.loadEncryptedData(documentWithEncryptedElement, encDataElement);
+ EncryptedKey encryptedKey = cipher.loadEncryptedKey(documentWithEncryptedElement,
encKeyElement);
+
+ Document decryptedDoc = null;
+
+ if (encryptedData != null && encryptedKey != null)
+ {
+ String encAlgoURL = encryptedData.getEncryptionMethod().getAlgorithm();
+ XMLCipher keyCipher = XMLCipher.getInstance();
+ keyCipher.init(XMLCipher.UNWRAP_MODE, privateKey);
+ Key encryptionKey = keyCipher.decryptKey( encryptedKey, encAlgoURL );
+ cipher = XMLCipher.getInstance();
+ cipher.init(XMLCipher.DECRYPT_MODE, encryptionKey);
+ decryptedDoc = cipher.doFinal(documentWithEncryptedElement, encDataElement);
+ }
+
+ Element decryptedRoot = decryptedDoc.getDocumentElement();
+ Element dataElement = getNextElementNode(decryptedRoot.getFirstChild());
+ if (dataElement == null)
+ throw new IllegalStateException("Data Element after encryption is
null");
+
+ decryptedRoot.removeChild(dataElement);
+ decryptedDoc.replaceChild(dataElement, decryptedRoot);
+
+ return decryptedDoc.getDocumentElement();
+ }
+
+ /**
+ * From the secret key, get the W3C XML Encryption URL
+ * @param publicKeyAlgo
+ * @param keySize
+ * @return
+ */
+ private static String getXMLEncryptionURLForKeyUnwrap(String publicKeyAlgo, int
keySize)
+ {
+ if("AES".equals(publicKeyAlgo))
+ {
+ switch(keySize)
+ {
+ case 192: return XMLCipher.AES_192_KeyWrap;
+ case 256: return XMLCipher.AES_256_KeyWrap;
+ default:
+ return XMLCipher.AES_128_KeyWrap;
+ }
+ }
+ if(publicKeyAlgo.contains("RSA"))
+ return XMLCipher.RSA_v1dot5;
+ if(publicKeyAlgo.contains("DES"))
+ return XMLCipher.TRIPLEDES_KeyWrap;
+ throw new IllegalArgumentException("unsupported publicKey Algo:" +
publicKeyAlgo);
+ }
+
+ /**
+ * From the secret key, get the W3C XML Encryption URL
+ * @param secretKey
+ * @param keySize
+ * @return
+ */
+ private static String getXMLEncryptionURL(String algo, int keySize)
+ {
+ if("AES".equals(algo))
+ {
+ switch(keySize)
+ {
+ case 192: return XMLCipher.AES_192;
+ case 256: return XMLCipher.AES_256;
+ default:
+ return XMLCipher.AES_128;
+ }
+ }
+ if(algo.contains("RSA"))
+ return XMLCipher.RSA_v1dot5;
+ if(algo.contains("DES"))
+ return XMLCipher.TRIPLEDES_KeyWrap;
+ throw new IllegalArgumentException("Secret Key with unsupported algo:" +
algo);
+ }
+
+ /**
+ * Returns the next Element node.
+ */
+ private static Element getNextElementNode(Node node)
+ {
+ while(node != null)
+ {
+ if(Node.ELEMENT_NODE == node.getNodeType())
+ return (Element) node;
+ node = node.getNextSibling();
+ }
+ return null;
+ }
}
\ No newline at end of file
Modified: identity-federation/trunk/jboss-identity-fed-model/pom.xml
===================================================================
--- identity-federation/trunk/jboss-identity-fed-model/pom.xml 2009-05-26 15:25:25 UTC
(rev 535)
+++ identity-federation/trunk/jboss-identity-fed-model/pom.xml 2009-05-26 15:43:37 UTC
(rev 536)
@@ -58,10 +58,6 @@
<artifactId>log4j</artifactId>
</dependency>
<dependency>
- <groupId>org.apache</groupId>
- <artifactId>xmlsec</artifactId>
- </dependency>
- <dependency>
<groupId>apache-logging</groupId>
<artifactId>commons-logging-api</artifactId>
</dependency>
Deleted:
identity-federation/trunk/jboss-identity-xmlsec-model/src/main/java/org/jboss/identity/xmlsec/util/XMLEncryptionUtil.java
===================================================================
---
identity-federation/trunk/jboss-identity-xmlsec-model/src/main/java/org/jboss/identity/xmlsec/util/XMLEncryptionUtil.java 2009-05-26
15:25:25 UTC (rev 535)
+++
identity-federation/trunk/jboss-identity-xmlsec-model/src/main/java/org/jboss/identity/xmlsec/util/XMLEncryptionUtil.java 2009-05-26
15:43:37 UTC (rev 536)
@@ -1,396 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2008, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
- */
-package org.jboss.identity.xmlsec.util;
-
-import java.security.Key;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
-import javax.crypto.SecretKey;
-import javax.xml.namespace.QName;
-
-import org.apache.xml.security.encryption.EncryptedData;
-import org.apache.xml.security.encryption.EncryptedKey;
-import org.apache.xml.security.encryption.XMLCipher;
-import org.apache.xml.security.exceptions.XMLSecurityException;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-/**
- * XML Encryption Util
- * <b>Note: </b> This utility is currently using Apache XML Security
- * library API. JSR-106 is not yet final. Until that happens,we
- * rely on the non-standard API.
- *
- * @author Anil.Saldhana(a)redhat.com
- * @since Feb 4, 2009
- */
-public class XMLEncryptionUtil
-{
- public static final String CIPHER_DATA_LOCALNAME = "CipherData";
- public static final String ENCRYPTED_KEY_LOCALNAME = "EncryptedKey";
- public static final String DS_KEY_INFO = "ds:KeyInfo";
-
- public static final String XMLNS = "http://www.w3.org/2000/xmlns/";
- public static String XMLSIG_NS = "http://www.w3.org/2000/09/xmldsig#";
- public static String XMLENC_NS = "http://www.w3.org/2001/04/xmlenc#";
-
- static
- {
- //Initialize the Apache XML Security Library
- org.apache.xml.security.Init.init();
- }
-
- /**
- * <p>
- * Encrypt the Key to be transported
- * </p>
- * <p>
- * Data is encrypted with a SecretKey. Then the key needs to be
- * transported to the other end where it is needed for decryption.
- * For the Key transport, the SecretKey is encrypted with the
- * recipient's public key. At the receiving end, the receiver
- * can decrypt the Secret Key using his private key.s
- * </p>
- * @param document
- * @param keyToBeEncrypted Symmetric Key (SecretKey)
- * @param keyUsedToEncryptSecretKey Asymmetric Key (Public Key)
- * @param keySize Length of the key
- * @return
- * @throws XMLSecurityException
- */
- public static EncryptedKey encryptKey(Document document,
- SecretKey keyToBeEncrypted, PublicKey keyUsedToEncryptSecretKey,
- int keySize) throws XMLSecurityException
- {
- XMLCipher keyCipher = null;
- String pubKeyAlg = keyUsedToEncryptSecretKey.getAlgorithm();
-
- String keyWrapAlgo = getXMLEncryptionURLForKeyUnwrap(pubKeyAlg, keySize);
- keyCipher = XMLCipher.getInstance(keyWrapAlgo);
-
- keyCipher.init(XMLCipher.WRAP_MODE, keyUsedToEncryptSecretKey);
- return keyCipher.encryptKey(document, keyToBeEncrypted);
- }
-
- /**
- * Given an element in a Document, encrypt the element and replace
- * the element in the document with the encrypted data
- * @param elementQName QName of the element that we like to encrypt
- * @param publicKey
- * @param secretKey
- * @param keySize
- * @param wrappingElementQName A QName of an element that will wrap the encrypted
element
- * @param addEncryptedKeyInKeyInfo Need for the EncryptedKey to be placed in
ds:KeyInfo
- * @return
- * @throws Exception
- */
- public static void encryptElement(QName elementQName,
- Document document,
- PublicKey publicKey,
- SecretKey secretKey, int keySize, QName wrappingElementQName,
- boolean addEncryptedKeyInKeyInfo) throws Exception
- {
- if(elementQName == null)
- throw new IllegalArgumentException("elementQName is null");
- if(document == null)
- throw new IllegalArgumentException("document is null");
- String wrappingElementPrefix = wrappingElementQName.getPrefix();
- if(wrappingElementPrefix == null || wrappingElementPrefix == "")
- throw new IllegalArgumentException("Wrapping element prefix
invalid");
-
- NodeList elements = document.getElementsByTagName(elementQName.toString());
- if(elements == null || elements.getLength() > 1)
- throw new IllegalStateException("Element was either null or more than
one:"+elements);
- Element documentElement = (Element) elements.item(0);
-
- if(documentElement == null)
- throw new IllegalStateException("Element could not be found in the
document:"+ elementQName.toString());
-
- XMLCipher cipher = null;
- EncryptedKey encryptedKey = encryptKey(document, secretKey, publicKey, keySize);
-
- String encryptionAlgorithm = getXMLEncryptionURL(secretKey.getAlgorithm(),
keySize);
- //Encrypt the Document
- cipher = XMLCipher.getInstance(encryptionAlgorithm);
- cipher.init(XMLCipher.ENCRYPT_MODE, secretKey);
-
- Document encryptedDoc = cipher.doFinal(document, documentElement);
-
- // The EncryptedKey element is added
- Element encryptedKeyElement = cipher.martial(document, encryptedKey);
-
- String wrappingElementName = wrappingElementPrefix + ":" +
wrappingElementQName.getLocalPart();
-
- //Create the wrapping element and set its attribute NS
- Element wrappingElement =
encryptedDoc.createElementNS(wrappingElementQName.getNamespaceURI(),
- wrappingElementName);
-
- if(wrappingElementPrefix == null || wrappingElementPrefix == "")
- {
- wrappingElementName = wrappingElementQName.getLocalPart();
- }
- wrappingElement.setAttributeNS(XMLNS,
- "xmlns:" + wrappingElementPrefix,
wrappingElementQName.getNamespaceURI());
-
- //Get Hold of the Cipher Data
- NodeList cipherElements = encryptedDoc.getElementsByTagNameNS(XMLENC_NS,
"EncryptedData");
- if(cipherElements == null || cipherElements.getLength() == 0)
- throw new IllegalStateException("xenc:EncryptedData Element
Missing");
- Element encryptedDataElement = (Element) cipherElements.item(0);
-
- Node parentOfEncNode = encryptedDataElement.getParentNode();
- parentOfEncNode.replaceChild(wrappingElement, encryptedDataElement);
-
- wrappingElement.appendChild(encryptedDataElement);
-
- if (addEncryptedKeyInKeyInfo)
- {
- // Outer ds:KeyInfo Element to hold the EncryptionKey
- Element sigElement = encryptedDoc.createElementNS(XMLSIG_NS, DS_KEY_INFO);
- sigElement.setAttributeNS(XMLNS, "xmlns:ds", XMLSIG_NS);
- sigElement.appendChild(encryptedKeyElement);
-
- //Insert the Encrypted key before the CipherData element
- NodeList nodeList = encryptedDoc.getElementsByTagNameNS(XMLENC_NS,
CIPHER_DATA_LOCALNAME);
- if (nodeList == null || nodeList.getLength() == 0)
- throw new IllegalStateException("xenc:CipherData Element Missing");
- Element cipherDataElement = (Element) nodeList.item(0);
- Node cipherParent = cipherDataElement.getParentNode();
- cipherParent.insertBefore(sigElement, cipherDataElement);
- }
- else
- {
- //Add the encrypted key as a child of the wrapping element
- wrappingElement.appendChild(encryptedKeyElement);
- }
- }
-
-
- /**
- * Encrypt the root document element inside a Document.
- * <b>NOTE:</> The document root element will be replaced
- * by the wrapping element.
- *
- * @param document Document that contains an element to encrypt
- * @param publicKey The Public Key used to encrypt the secret encryption key
- * @param secretKey The secret encryption key
- * @param keySize Length of key
- * @param wrappingElementQName QName of the element to be used to wrap around
- * the cipher data.
- * @param addEncryptedKeyInKeyInfo Should the encrypted key be inside a KeyInfo
- * or added as a peer of Cipher Data
- * @return An element that has the wrappingElementQName
- * @throws Exception
- */
- public static Element encryptElementInDocument(Document document,
- PublicKey publicKey,
- SecretKey secretKey, int keySize, QName wrappingElementQName,
- boolean addEncryptedKeyInKeyInfo) throws Exception
- {
- String wrappingElementPrefix = wrappingElementQName.getPrefix();
- if(wrappingElementPrefix == null || wrappingElementPrefix == "")
- throw new IllegalArgumentException("Wrapping element prefix
invalid");
-
- XMLCipher cipher = null;
- EncryptedKey encryptedKey = encryptKey(document, secretKey, publicKey, keySize);
-
- String encryptionAlgorithm = getXMLEncryptionURL(secretKey.getAlgorithm(),
keySize);
- //Encrypt the Document
- cipher = XMLCipher.getInstance(encryptionAlgorithm);
- cipher.init(XMLCipher.ENCRYPT_MODE, secretKey);
-
- Document encryptedDoc = cipher.doFinal(document, document.getDocumentElement());
-
- // The EncryptedKey element is added
- Element encryptedKeyElement = cipher.martial(document, encryptedKey);
-
- String wrappingElementName = wrappingElementPrefix + ":" +
wrappingElementQName.getLocalPart();
-
- //Create the wrapping element and set its attribute NS
- Element wrappingElement =
encryptedDoc.createElementNS(wrappingElementQName.getNamespaceURI(),
- wrappingElementName);
-
- if(wrappingElementPrefix == null || wrappingElementPrefix == "")
- {
- wrappingElementName = wrappingElementQName.getLocalPart();
- }
- wrappingElement.setAttributeNS(XMLNS,
- "xmlns:" + wrappingElementPrefix,
wrappingElementQName.getNamespaceURI());
-
- Element encryptedDocRootElement = encryptedDoc.getDocumentElement();
- //Bring in the encrypted wrapping element to wrap the root node
- encryptedDoc.replaceChild(wrappingElement, encryptedDocRootElement);
-
- wrappingElement.appendChild(encryptedDocRootElement);
-
- if (addEncryptedKeyInKeyInfo)
- {
- // Outer ds:KeyInfo Element to hold the EncryptionKey
- Element sigElement = encryptedDoc.createElementNS(XMLSIG_NS, DS_KEY_INFO);
- sigElement.setAttributeNS(XMLNS, "xmlns:ds", XMLSIG_NS);
- sigElement.appendChild(encryptedKeyElement);
-
- //Insert the Encrypted key before the CipherData element
- NodeList nodeList = encryptedDocRootElement.getElementsByTagNameNS(XMLENC_NS,
CIPHER_DATA_LOCALNAME);
- if (nodeList == null || nodeList.getLength() == 0)
- throw new IllegalStateException("xenc:CipherData Element Missing");
-
- Element cipherDataElement = (Element) nodeList.item(0);
- encryptedDocRootElement.insertBefore(sigElement, cipherDataElement);
- }
- else
- {
- //Add the encrypted key as a child of the wrapping element
- wrappingElement.appendChild(encryptedKeyElement);
- }
-
- return encryptedDoc.getDocumentElement();
- }
-
- /**
- * Decrypt an encrypted element inside a document
- * @param documentWithEncryptedElement
- * @param privateKey key need to unwrap the encryption key
- * @return the document with the encrypted element replaced by the data element
- * @throws Exception
- */
- public static Element decryptElementInDocument(Document documentWithEncryptedElement,
- PrivateKey privateKey) throws Exception
- {
- if(documentWithEncryptedElement == null)
- throw new IllegalArgumentException("Input document is null");
-
- //Look for encrypted data element
- Element documentRoot = documentWithEncryptedElement.getDocumentElement();
- Element encDataElement = getNextElementNode(documentRoot.getFirstChild());
- if(encDataElement == null)
- throw new IllegalStateException("No element representing the encrypted data
found");
-
- //Look at siblings for the key
- Element encKeyElement = getNextElementNode(encDataElement.getNextSibling());
- if(encKeyElement == null)
- {
- //Search the enc data element for enc key
- NodeList nodeList = encDataElement.getElementsByTagNameNS( XMLENC_NS,
ENCRYPTED_KEY_LOCALNAME);
-
- if(nodeList == null || nodeList.getLength() == 0)
- throw new IllegalStateException("Encrypted Key not found in the enc
data");
-
- encKeyElement = (Element) nodeList.item(0);
- }
-
- XMLCipher cipher = XMLCipher.getInstance();
- cipher.init(XMLCipher.DECRYPT_MODE, null);
- EncryptedData encryptedData =
cipher.loadEncryptedData(documentWithEncryptedElement, encDataElement);
- EncryptedKey encryptedKey = cipher.loadEncryptedKey(documentWithEncryptedElement,
encKeyElement);
-
- Document decryptedDoc = null;
-
- if (encryptedData != null && encryptedKey != null)
- {
- String encAlgoURL = encryptedData.getEncryptionMethod().getAlgorithm();
- XMLCipher keyCipher = XMLCipher.getInstance();
- keyCipher.init(XMLCipher.UNWRAP_MODE, privateKey);
- Key encryptionKey = keyCipher.decryptKey( encryptedKey, encAlgoURL );
- cipher = XMLCipher.getInstance();
- cipher.init(XMLCipher.DECRYPT_MODE, encryptionKey);
- decryptedDoc = cipher.doFinal(documentWithEncryptedElement, encDataElement);
- }
-
- Element decryptedRoot = decryptedDoc.getDocumentElement();
- Element dataElement = getNextElementNode(decryptedRoot.getFirstChild());
- if (dataElement == null)
- throw new IllegalStateException("Data Element after encryption is
null");
-
- decryptedRoot.removeChild(dataElement);
- decryptedDoc.replaceChild(dataElement, decryptedRoot);
-
- return decryptedDoc.getDocumentElement();
- }
-
- /**
- * From the secret key, get the W3C XML Encryption URL
- * @param publicKeyAlgo
- * @param keySize
- * @return
- */
- private static String getXMLEncryptionURLForKeyUnwrap(String publicKeyAlgo, int
keySize)
- {
- if("AES".equals(publicKeyAlgo))
- {
- switch(keySize)
- {
- case 192: return XMLCipher.AES_192_KeyWrap;
- case 256: return XMLCipher.AES_256_KeyWrap;
- default:
- return XMLCipher.AES_128_KeyWrap;
- }
- }
- if(publicKeyAlgo.contains("RSA"))
- return XMLCipher.RSA_v1dot5;
- if(publicKeyAlgo.contains("DES"))
- return XMLCipher.TRIPLEDES_KeyWrap;
- throw new IllegalArgumentException("unsupported publicKey Algo:" +
publicKeyAlgo);
- }
-
- /**
- * From the secret key, get the W3C XML Encryption URL
- * @param secretKey
- * @param keySize
- * @return
- */
- private static String getXMLEncryptionURL(String algo, int keySize)
- {
- if("AES".equals(algo))
- {
- switch(keySize)
- {
- case 192: return XMLCipher.AES_192;
- case 256: return XMLCipher.AES_256;
- default:
- return XMLCipher.AES_128;
- }
- }
- if(algo.contains("RSA"))
- return XMLCipher.RSA_v1dot5;
- if(algo.contains("DES"))
- return XMLCipher.TRIPLEDES_KeyWrap;
- throw new IllegalArgumentException("Secret Key with unsupported algo:" +
algo);
- }
-
- /**
- * Returns the next Element node.
- */
- private static Element getNextElementNode(Node node)
- {
- while(node != null)
- {
- if(Node.ELEMENT_NODE == node.getNodeType())
- return (Element) node;
- node = node.getNextSibling();
- }
- return null;
- }
-}
\ No newline at end of file