Author: anil.saldhana(a)jboss.com
Date: 2009-03-26 11:55:17 -0400 (Thu, 26 Mar 2009)
New Revision: 401
Added:
identity-federation/trunk/identity-xmlsecmodel/src/main/java/org/jboss/identity/xmlsec/util/
identity-federation/trunk/identity-xmlsecmodel/src/main/java/org/jboss/identity/xmlsec/util/XMLEncryptionUtil.java
Log:
add xml enc util
Added:
identity-federation/trunk/identity-xmlsecmodel/src/main/java/org/jboss/identity/xmlsec/util/XMLEncryptionUtil.java
===================================================================
---
identity-federation/trunk/identity-xmlsecmodel/src/main/java/org/jboss/identity/xmlsec/util/XMLEncryptionUtil.java
(rev 0)
+++
identity-federation/trunk/identity-xmlsecmodel/src/main/java/org/jboss/identity/xmlsec/util/XMLEncryptionUtil.java 2009-03-26
15:55:17 UTC (rev 401)
@@ -0,0 +1,395 @@
+/*
+ * 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.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 Exception
+ */
+ public static EncryptedKey encryptKey(Document document,
+ SecretKey keyToBeEncrypted, PublicKey keyUsedToEncryptSecretKey,
+ int keySize) throws Exception
+ {
+ 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