blautenb 2003/07/15 05:34:30
Modified: src/org/apache/xml/security/encryption XMLCipher.java
Log:
Updated encryption code to correctly handle element content for interop tests
Revision Changes Path
1.3 +212 -34
xml-security/src/org/apache/xml/security/encryption/XMLCipher.java
Index: XMLCipher.java
===================================================================
RCS file:
/home/cvs/xml-security/src/org/apache/xml/security/encryption/XMLCipher.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- XMLCipher.java 29 Apr 2003 21:36:53 -0000 1.2
+++ XMLCipher.java 15 Jul 2003 12:34:29 -0000 1.3
@@ -62,8 +62,10 @@
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
+import java.lang.Integer;
import java.security.InvalidKeyException;
import java.security.Key;
+import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
@@ -76,6 +78,7 @@
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@@ -92,9 +95,11 @@
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.EncryptionConstants;
import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
+import org.apache.xml.security.algorithms.JCEMapper;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.transforms.Transform;
import org.apache.xml.security.utils.ElementProxy;
+import org.apache.xml.security.exceptions.Base64DecodingException;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.apache.xml.utils.URI;
@@ -102,6 +107,7 @@
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.DOMException;
+import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@@ -109,6 +115,7 @@
import org.xml.sax.SAXException;
import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;
+import org.apache.xml.security.utils.Base64;
/**
@@ -189,11 +196,14 @@
private Cipher contextCipher;
private int cipherMode = Integer.MIN_VALUE;
- private String algorithm;
+ private String algorithm = null; // URI for requested algorithm
+ private String requestedJCEProvider = null;
private Document contextDocument;
private Factory factory;
private Serializer serializer;
private Map enc2JCE;
+ private Map enc2IV;
+ private Key localKey;
/**
* Creates a new <code>XMLCipher</code>.
@@ -207,10 +217,11 @@
serializer = new Serializer();
// block encryption
enc2JCE = new HashMap();
- enc2JCE.put(TRIPLEDES, "DESede");
- enc2JCE.put(AES_128, "AES");
- enc2JCE.put(AES_256, "AES");
- enc2JCE.put(AES_192, "AES");
+ enc2JCE.put(TRIPLEDES, "DESede/CBC/NoPadding");
+ enc2JCE.put(AES_128, "AES/CBC/PKCS5Padding");
+ enc2JCE.put(AES_256, "AES/CBC/PKCS5Padding");
+ enc2JCE.put(AES_192, "AES/CBC/PKCS5Padding");
+
// key encryption
enc2JCE.put(RSA_v1dot5, "RSA/ECB/PKCS1Padding");
enc2JCE.put(RSA_OAEP, "RSA/ECB/OAEPPadding");
@@ -329,6 +340,7 @@
}
instance.algorithm = transformation;
+ instance.requestedJCEProvider = provider;
try {
String jceAlgorithm = (String)
instance.enc2JCE.get(transformation);
@@ -369,12 +381,8 @@
((opmode == ENCRYPT_MODE) ? "ENCRYPT_MODE" : "DECRYPT_MODE"));
cipherMode = opmode;
+ localKey = key;
- try {
- contextCipher.init(opmode, key);
- } catch (InvalidKeyException ike) {
- throw new XMLEncryptionException("empty", ike);
- }
}
/**
@@ -399,12 +407,47 @@
logger.debug("Serialized octets:\n" + serializedOctets);
byte[] encryptedBytes = null;
+ // Now create the working cipher
+
+ String jceAlgorithm =
+
JCEMapper.translateURItoJCEID(algorithm).getAlgorithmID();
+ String provider;
+
+ if (requestedJCEProvider == null)
+ provider =
+
JCEMapper.translateURItoJCEID(algorithm).getProviderId();
+ else
+ provider = requestedJCEProvider;
+
+ logger.debug("provider = " + provider + "alg = " +
jceAlgorithm);
+
+ Cipher c;
+ try {
+ c = Cipher.getInstance(jceAlgorithm, provider);
+ } catch (NoSuchAlgorithmException nsae) {
+ throw new XMLEncryptionException("empty", nsae);
+ } catch (NoSuchProviderException nspre) {
+ throw new XMLEncryptionException("empty", nspre);
+ } catch (NoSuchPaddingException nspae) {
+ throw new XMLEncryptionException("empty", nspae);
+ }
+
+ // Now perform the encryption
+
+ try {
+ // Should internally generate an IV
+ // todo - allow user to set an IV
+ c.init(cipherMode, localKey);
+ } catch (InvalidKeyException ike) {
+ throw new XMLEncryptionException("empty", ike);
+ }
+
try {
encryptedBytes =
- contextCipher.doFinal(serializedOctets.getBytes());
+ c.doFinal(serializedOctets.getBytes());
logger.debug("Expected cipher.outputSize = " +
- Integer.toString(contextCipher.getOutputSize(
+ Integer.toString(c.getOutputSize(
serializedOctets.getBytes().length)));
logger.debug("Actual cipher.outputSize = " +
Integer.toString(encryptedBytes.length));
@@ -416,8 +459,20 @@
throw new XMLEncryptionException("empty", bpe);
}
+ // Now build up to a properly XML Encryption encoded octet
stream
+ // IvParameterSpec iv;
+
+ byte[] iv = c.getIV();
+ byte[] finalEncryptedBytes =
+ new byte[iv.length + encryptedBytes.length];
+ System.arraycopy(iv, 0, finalEncryptedBytes, 0,
+ iv.length);
+ System.arraycopy(encryptedBytes, 0, finalEncryptedBytes,
+ iv.length,
+ encryptedBytes.length);
+
String base64EncodedEncryptedOctets = new BASE64Encoder().encode(
- encryptedBytes);
+ finalEncryptedBytes);
logger.debug("Encrypted octets:\n" + base64EncodedEncryptedOctets);
logger.debug("Encrypted octets length = " +
@@ -785,7 +840,9 @@
*/
private Document decryptElement(Element element) throws
XMLEncryptionException {
+
logger.info("Decrypting element...");
+
if(cipherMode != DECRYPT_MODE)
logger.error("XMLCipher unexpectedly not in DECRYPT_MODE...");
@@ -793,6 +850,7 @@
CipherData cipherData = encryptedData.getCipherData();
String base64EncodedEncryptedOctets = null;
+
if (cipherData.getDataType() == CipherData.REFERENCE_TYPE) {
// retrieve the cipher text
} else if (cipherData.getDataType() == CipherData.VALUE_TYPE) {
@@ -804,29 +862,91 @@
logger.debug("Encrypted octets:\n" + base64EncodedEncryptedOctets);
byte[] encryptedBytes = null;
+
try {
- encryptedBytes = new BASE64Decoder().decodeBuffer(
- base64EncodedEncryptedOctets);
- } catch (IOException ioe) {
- throw new XMLEncryptionException("empty", ioe);
- }
+ encryptedBytes =
Base64.decode(base64EncodedEncryptedOctets);
+ } catch (Base64DecodingException bde) {
+ throw new XMLEncryptionException("empty", bde);
+ }
+
+ // Now create the working cipher
+
+ String jceAlgorithm =
+
JCEMapper.translateURItoJCEID(encryptedData.getEncryptionMethod()
+
.getAlgorithm()).getAlgorithmID();
+ String provider;
+
+ if (requestedJCEProvider == null)
+ provider =
+ JCEMapper.translateURItoJCEID(encryptedData
+
.getEncryptionMethod()
+
.getAlgorithm())
+ .getProviderId();
+ else
+ provider = requestedJCEProvider;
+
+ Cipher c;
+ try {
+ c = Cipher.getInstance(jceAlgorithm, provider);
+ } catch (NoSuchAlgorithmException nsae) {
+ throw new XMLEncryptionException("empty", nsae);
+ } catch (NoSuchProviderException nspre) {
+ throw new XMLEncryptionException("empty", nspre);
+ } catch (NoSuchPaddingException nspae) {
+ throw new XMLEncryptionException("empty", nspae);
+ }
+
+
+ // Calculate the IV length and copy out
+
+ // For now, we only work with Block ciphers, so this will work.
+ // This should probably be put into the JCE mapper.
+
+ int ivLen = c.getBlockSize();
+ byte[] ivBytes = new byte[ivLen];
+
+ // You may be able to pass the entire piece in to
IvParameterSpec
+ // and it will only take the first x bytes, but no way to be
certain
+ // that this will work for every JCE provider, so lets copy the
+ // necessary bytes into a dedicated array.
+
+ System.arraycopy(encryptedBytes, 0, ivBytes, 0, ivLen);
+ IvParameterSpec iv = new IvParameterSpec(ivBytes);
+
+ try {
+ c.init(cipherMode, localKey, iv);
+ } catch (InvalidKeyException ike) {
+ throw new XMLEncryptionException("empty", ike);
+ } catch (InvalidAlgorithmParameterException iape) {
+ throw new XMLEncryptionException("empty", iape);
+ }
String octets = null;
+ byte[] plainBytes;
+
try {
- octets = new String(
- contextCipher.doFinal(encryptedBytes));
+ octets = new String(c.doFinal(encryptedBytes, ivLen,
+
encryptedBytes.length - ivLen));
} catch (IllegalBlockSizeException ibse) {
throw new XMLEncryptionException("empty", ibse);
} catch (BadPaddingException bpe) {
throw new XMLEncryptionException("empty", bpe);
}
+
+ // Now remove any padding
+ //octets = new String(padder.dePad(plainBytes));
+
logger.debug("Decrypted octets:\n" + octets);
- Element decryptedElement = serializer.deserialize(octets);
+ Element sourceParent = (Element) element.getParentNode();
+ DocumentFragment decryptedFragment =
+ serializer.deserialize(octets, sourceParent);
- Node sourceParent = element.getParentNode();
- sourceParent.replaceChild(decryptedElement, element);
+ // The de-serialiser returns a fragment whose children we need
to
+ // take on.
+
+ sourceParent.replaceChild(decryptedFragment, element);
return (contextDocument);
}
@@ -982,22 +1102,80 @@
/**
*
*/
- Element deserialize(String source) throws XMLEncryptionException {
- Element result = null;
-
+ DocumentFragment deserialize(String source, Element ctx) throws
XMLEncryptionException {
+ DocumentFragment result;
final String tagname = "fragment";
- String fragment = "<"+tagname+">" + source + "</"+tagname+">";
+
+ // Create the context to parse the document against
+ StringBuffer sb;
+
+ sb = new StringBuffer();
+ sb.append("<?xml version=\"1.0\"
encoding=\"UTF-8\"?><"+tagname);
+
+ // Run through each node up to the document node and
find any
+ // xmlns: nodes
+
+ Node wk = ctx;
+
+ while (wk != null) {
+
+ NamedNodeMap atts = wk.getAttributes();
+ int length;
+ if (atts != null)
+ length = atts.getLength();
+ else
+ length = 0;
+
+ for (int i = 0 ; i < length ; ++i) {
+ Node att = atts.item(i);
+ if
(att.getNodeName().startsWith("xmlns:") ||
+ att.getNodeName() == "xmlns") {
+
+ // Check to see if this node
has already been found
+ Node p = ctx;
+ boolean found = false;
+ while (p != wk) {
+ NamedNodeMap tstAtts =
p.getAttributes();
+ if (tstAtts != null &&
+
tstAtts.getNamedItem(att.getNodeName()) != null) {
+ found = true;
+ break;
+ }
+ p = p.getParentNode();
+ }
+ if (found == false) {
+
+ // This is an attribute
node
+ sb.append(" " +
att.getNodeName() + "=\"" +
+
att.getNodeValue() + "\"");
+ }
+ }
+ }
+ wk = wk.getParentNode();
+ }
+ sb.append(">" + source + "</" + tagname + ">");
+ String fragment = sb.toString();
+
try {
DocumentBuilderFactory dbf =
DocumentBuilderFactory.newInstance();
- DocumentBuilder db = dbf.newDocumentBuilder();
- Document d = db.parse(
- new InputSource(new StringReader(fragment)));
-
- Node n = contextDocument.importNode(
- d.getDocumentElement(), true);
+ dbf.setNamespaceAware(true);
+
dbf.setAttribute("http://xml.org/sax/features/namespaces", Boolean.TRUE);
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ Document d = db.parse(
+ new InputSource(new
StringReader(fragment)));
+
+ Element fragElt = (Element)
contextDocument.importNode(
+ d.getDocumentElement(), true);
+ result =
contextDocument.createDocumentFragment();
+ Node child = fragElt.getFirstChild();
+ while (child != null) {
+ fragElt.removeChild(child);
+ result.appendChild(child);
+ child = fragElt.getFirstChild();
+ }
+ String outp = serialize(d);
- result = (Element) n.getFirstChild();
} catch (SAXException se) {
throw new XMLEncryptionException("empty", se);
} catch (ParserConfigurationException pce) {