blautenb 2004/02/07 16:24:23
Modified: src/org/apache/xml/security/encryption XMLCipher.java
Log:
Werner Dittman's fix for serialisation problems - see bugzilla #26660
Revision Changes Path
1.18 +324 -161
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.17
retrieving revision 1.18
diff -u -r1.17 -r1.18
--- XMLCipher.java 19 Dec 2003 11:53:55 -0000 1.17
+++ XMLCipher.java 8 Feb 2004 00:24:23 -0000 1.18
@@ -93,6 +93,7 @@
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.c14n.InvalidCanonicalizerException;
import org.apache.xml.security.transforms.Transform;
import org.apache.xml.security.utils.ElementProxy;
import org.apache.xml.security.exceptions.Base64DecodingException;
@@ -203,6 +204,8 @@
private String _algorithm = null;
/** Cryptographic provider requested by caller */
private String _requestedJCEProvider = null;
+ /** Holds c14n to serialize, if initialized then _always_ use this c14n
to serialize */
+ private Canonicalizer _canon;
/** Used for creation of DOM nodes in WRAP and ENCRYPT modes */
private Document _contextDocument;
/** Instance of factory used to create XML Encryption objects */
@@ -305,6 +308,17 @@
instance._key = null;
instance._kek = null;
+
+ /* Create a canonicaliser - used when serialising DOM to octets
+ * prior to encryption (and for the reverse) */
+
+ try {
+ instance._canon = Canonicalizer.getInstance
+ (Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS);
+ } catch (InvalidCanonicalizerException ice) {
+ throw new XMLEncryptionException("empty", ice);
+ }
+
try {
String jceAlgorithm =
JCEMapper.translateURItoJCEID(transformation).getAlgorithmID();
@@ -322,6 +336,39 @@
return (instance);
}
+ /**
+ * Returns an <code>XMLCipher</code> that implements the specified
+ * transformation, operates on the specified context document and
serializes
+ * the document with the specified canonicalization algorithm before it
+ * encrypts the document.
+ * <p>
+ *
+ * @param transformation the name of the transformation, e.g.,
+ *
<code>XMLCipher.TRIPLEDES</code> which is
+ * shorthand for
+ *
"http://www.w3.org/2001/04/xmlenc#tripledes-cbc"
+ * @param canon the name of the c14n algorithm,
if
+ *
<code>null</code> use standard serializer
+ * @return
+ * @throws XMLEncryptionException
+ */
+
+ public static XMLCipher getInstance(String transformation, String canon)
+ throws XMLEncryptionException {
+ XMLCipher instance = XMLCipher.getInstance(transformation);
+
+ if (canon != null) {
+ try {
+ instance._canon =
Canonicalizer.getInstance(canon);
+ } catch (InvalidCanonicalizerException ice) {
+ throw new XMLEncryptionException("empty", ice);
+ }
+ }
+
+ return instance;
+ }
+
+
/**
* Returns an <code>XMLCipher</code> that implements the specified
* transformation and operates on the specified context document.
@@ -332,6 +379,7 @@
* @param provider the JCE provider that supplies the transformation
* @throws <code>XMLEncryptionException</code>.
*/
+
public static XMLCipher getProviderInstance(String transformation,
String provider)
throws XMLEncryptionException {
// sanity checks
@@ -352,6 +400,16 @@
instance._key = null;
instance._kek = null;
+ /* Create a canonicaliser - used when serialising DOM to octets
+ * prior to encryption (and for the reverse) */
+
+ try {
+ instance._canon = Canonicalizer.getInstance
+ (Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS);
+ } catch (InvalidCanonicalizerException ice) {
+ throw new XMLEncryptionException("empty", ice);
+ }
+
try {
String jceAlgorithm =
JCEMapper.translateURItoJCEID(transformation).getAlgorithmID();
@@ -371,6 +429,40 @@
return (instance);
}
+
+ /**
+ * Returns an <code>XMLCipher</code> that implements the specified
+ * transformation, operates on the specified context document and
serializes
+ * the document with the specified canonicalization algorithm before it
+ * encrypts the document.
+ * <p>
+ *
+ * @param transformation the name of the transformation, e.g.,
+ *
<code>XMLCipher.TRIPLEDES</code> which is
+ * shorthand for
+ *
"http://www.w3.org/2001/04/xmlenc#tripledes-cbc"
+ * @param provider the JCE provider that supplies the
transformation
+ * @param canon the name of the c14n algorithm,
if
+ *
<code>null</code> use standard serializer
+ * @return
+ * @throws Exception
+ */
+ public static XMLCipher getProviderInstance(
+ String transformation,
+ String provider,
+ String canon)
+ throws XMLEncryptionException {
+
+ XMLCipher instance =
XMLCipher.getProviderInstance(transformation, provider);
+ if (canon != null) {
+ try {
+ instance._canon =
Canonicalizer.getInstance(canon);
+ } catch (InvalidCanonicalizerException ice) {
+ throw new XMLEncryptionException("empty", ice);
+ }
+ }
+ return instance;
+ }
/**
* Returns an <code>XMLCipher</code> that implements no specific
@@ -394,6 +486,16 @@
instance._kek = null;
instance._contextCipher = null;
+ /* Create a canonicaliser - used when serialising DOM to octets
+ * prior to encryption (and for the reverse) */
+
+ try {
+ instance._canon = Canonicalizer.getInstance
+ (Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS);
+ } catch (InvalidCanonicalizerException ice) {
+ throw new XMLEncryptionException("empty", ice);
+ }
+
return (instance);
}
@@ -429,6 +531,13 @@
instance._kek = null;
instance._contextCipher = null;
+ try {
+ instance._canon = Canonicalizer.getInstance
+ (Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS);
+ } catch (InvalidCanonicalizerException ice) {
+ throw new XMLEncryptionException("empty", ice);
+ }
+
return (instance);
}
@@ -627,17 +736,17 @@
*/
private Document encryptElement(Element element) throws
- XMLEncryptionException {
+ /* XMLEncryption */Exception {
logger.debug("Encrypting element...");
if(null == element)
logger.error("Element unexpectedly null...");
if(_cipherMode != ENCRYPT_MODE)
logger.error("XMLCipher unexpectedly not in ENCRYPT_MODE...");
- if (_algorithm == null) {
- throw new XMLEncryptionException("XMLCipher instance without
transformation specified");
- }
- encryptData(_contextDocument, element, false);
+ if (_algorithm == null) {
+ throw new XMLEncryptionException("XMLCipher instance without
transformation specified");
+ }
+ encryptData(_contextDocument, element, false);
Element encryptedElement = _factory.toElement(_ed);
@@ -661,17 +770,17 @@
* <code>Element</code>.
*/
private Document encryptElementContent(Element element) throws
- XMLEncryptionException {
+ /* XMLEncryption */Exception {
logger.debug("Encrypting element content...");
if(null == element)
logger.error("Element unexpectedly null...");
if(_cipherMode != ENCRYPT_MODE)
logger.error("XMLCipher unexpectedly not in ENCRYPT_MODE...");
- if (_algorithm == null) {
- throw new XMLEncryptionException("XMLCipher instance without
transformation specified");
- }
- encryptData(_contextDocument, element, true);
+ if (_algorithm == null) {
+ throw new XMLEncryptionException("XMLCipher instance without
transformation specified");
+ }
+ encryptData(_contextDocument, element, true);
Element encryptedElement = _factory.toElement(_ed);
@@ -691,7 +800,7 @@
* @throws XMLEnccryptionException to indicate any exceptional
conditions.
*/
public Document doFinal(Document context, Document source) throws
- XMLEncryptionException {
+ /* XMLEncryption */Exception {
logger.debug("Processing source document...");
if(null == context)
logger.error("Context document unexpectedly null...");
@@ -731,7 +840,7 @@
* @throws XMLEnccryptionException to indicate any exceptional
conditions.
*/
public Document doFinal(Document context, Element element) throws
- XMLEncryptionException {
+ /* XMLEncryption */Exception {
logger.debug("Processing source element...");
if(null == context)
logger.error("Context document unexpectedly null...");
@@ -773,7 +882,7 @@
* @throws XMLEnccryptionException to indicate any exceptional
conditions.
*/
public Document doFinal(Document context, Element element, boolean
content)
- throws XMLEncryptionException {
+ throws /* XMLEncryption*/ Exception {
logger.debug("Processing source element...");
if(null == context)
logger.error("Context document unexpectedly null...");
@@ -868,69 +977,70 @@
*/
public EncryptedData encryptData(Document context, Element element)
throws
- XMLEncryptionException {
+ /* XMLEncryption */Exception {
return encryptData(context, element, false);
}
private EncryptedData encryptData(Document context, Element element,
boolean contentMode) throws
- XMLEncryptionException {
- logger.debug("Encrypting element...");
- if(null == context)
- logger.error("Context document unexpectedly null...");
- if(null == element)
- logger.error("Element unexpectedly null...");
- if(_cipherMode != ENCRYPT_MODE)
- logger.error("XMLCipher unexpectedly not in ENCRYPT_MODE...");
+ /* XMLEncryption */ Exception {
+ logger.debug("Encrypting element...");
+ if (null == context)
+ logger.error("Context document unexpectedly null...");
+ if (null == element)
+ logger.error("Element unexpectedly null...");
+ if (_cipherMode != ENCRYPT_MODE)
+ logger.error("XMLCipher unexpectedly not in
ENCRYPT_MODE...");
- _contextDocument = context;
+ _contextDocument = context;
- if (_algorithm == null) {
- throw new XMLEncryptionException("XMLCipher instance without
transformation specified");
- }
-
+ if (_algorithm == null) {
+ throw new XMLEncryptionException("XMLCipher instance
without transformation specified");
+ }
- String serializedOctets = null;
- if (contentMode) {
- NodeList children = element.getChildNodes();
- if ((null != children)) {
- serializedOctets = _serializer.serialize(children);
- }
- else {
- Object exArgs[] = {"Element has no content."};
- throw new XMLEncryptionException("empty", exArgs);
- }
- }
- else {
- serializedOctets = _serializer.serialize(element);
- }
- logger.debug("Serialized octets:\n" + serializedOctets);
+ String serializedOctets = null;
+ if (contentMode) {
+ NodeList children = element.getChildNodes();
+ if ((null != children)) {
+ serializedOctets =
_serializer.serialize(children);
+ } else {
+ Object exArgs[] = { "Element has no content." };
+ throw new XMLEncryptionException("empty",
exArgs);
+ }
+ } else {
+ serializedOctets = _serializer.serialize(element);
+ }
+ logger.debug("Serialized octets:\n" + serializedOctets);
byte[] encryptedBytes = null;
- // Now create the working cipher
- String jceAlgorithm =
-
JCEMapper.translateURItoJCEID(_algorithm).getAlgorithmID();
- String provider;
+ // Now create the working cipher if none was created already
+ Cipher c;
+ if (_contextCipher == null) {
+ String jceAlgorithm =
+
JCEMapper.translateURItoJCEID(_algorithm).getAlgorithmID();
+ String provider;
- if (_requestedJCEProvider == null)
- provider =
-
JCEMapper.translateURItoJCEID(_algorithm).getProviderId();
- else
- provider = _requestedJCEProvider;
+ if (_requestedJCEProvider == null)
+ provider =
+
JCEMapper.translateURItoJCEID(_algorithm).getProviderId();
+ else
+ provider = _requestedJCEProvider;
- logger.debug("provider = " + provider + "alg = " +
jceAlgorithm);
+ 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);
+ 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);
+ }
+ }
+ else {
+ c = _contextCipher;
}
-
// Now perform the encryption
try {
@@ -978,24 +1088,25 @@
logger.debug("Encrypted octets length = " +
base64EncodedEncryptedOctets.length());
- try {
- CipherData cd = _ed.getCipherData();
- CipherValue cv = cd.getCipherValue();
- // cv.setValue(base64EncodedEncryptedOctets.getBytes());
- cv.setValue(base64EncodedEncryptedOctets);
-
- if (contentMode) {
- _ed.setType(new
URI(EncryptionConstants.TYPE_CONTENT).toString());
- }
- else {
- _ed.setType(new
URI(EncryptionConstants.TYPE_ELEMENT).toString());
- }
- EncryptionMethod method = _factory.newEncryptionMethod(
- new URI(_algorithm).toString());
- _ed.setEncryptionMethod(method);
- } catch (URI.MalformedURIException mfue) {
- throw new XMLEncryptionException("empty", mfue);
- }
+ try {
+ CipherData cd = _ed.getCipherData();
+ CipherValue cv = cd.getCipherValue();
+ // cv.setValue(base64EncodedEncryptedOctets.getBytes());
+ cv.setValue(base64EncodedEncryptedOctets);
+
+ if (contentMode) {
+ _ed.setType(
+ new
URI(EncryptionConstants.TYPE_CONTENT).toString());
+ } else {
+ _ed.setType(
+ new
URI(EncryptionConstants.TYPE_ELEMENT).toString());
+ }
+ EncryptionMethod method =
+ _factory.newEncryptionMethod(new
URI(_algorithm).toString());
+ _ed.setEncryptionMethod(method);
+ } catch (URI.MalformedURIException mfue) {
+ throw new XMLEncryptionException("empty", mfue);
+ }
return (_ed);
}
@@ -1091,32 +1202,36 @@
_contextDocument = doc;
- byte[] encryptedBytes = null;
- // Now create the working cipher
+ byte[] encryptedBytes = null;
+ Cipher c;
- String jceAlgorithm =
-
JCEMapper.translateURItoJCEID(_algorithm).getAlgorithmID();
- String provider;
+ if (_contextCipher == null) {
+ // Now create the working cipher
- if (_requestedJCEProvider == null)
- provider =
-
JCEMapper.translateURItoJCEID(_algorithm).getProviderId();
- else
- provider = _requestedJCEProvider;
+ String jceAlgorithm =
+
JCEMapper.translateURItoJCEID(_algorithm).getAlgorithmID();
+ String provider;
- logger.debug("provider = " + provider + "alg = " +
jceAlgorithm);
+ if (_requestedJCEProvider == null)
+ provider =
+
JCEMapper.translateURItoJCEID(_algorithm).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);
- }
+ logger.debug("provider = " + provider + "alg = " +
jceAlgorithm);
+ 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);
+ }
+ } else {
+ c = _contextCipher;
+ }
// Now perform the encryption
try {
@@ -1193,36 +1308,43 @@
XMLCipherInput cipherInput = new XMLCipherInput(encryptedKey);
byte [] encryptedBytes = cipherInput.getBytes();
- // Now create the working cipher
-
- String jceAlgorithm =
-
JCEMapper.translateURItoJCEID(encryptedKey.getEncryptionMethod()
-
.getAlgorithm()).getAlgorithmID();
String provider;
-
if (_requestedJCEProvider == null)
provider =
- JCEMapper.translateURItoJCEID(encryptedKey
-
.getEncryptionMethod()
-
.getAlgorithm())
- .getProviderId();
+ JCEMapper
+ .translateURItoJCEID(
+
encryptedKey.getEncryptionMethod().getAlgorithm())
+ .getProviderId();
else
provider = _requestedJCEProvider;
- String jceKeyAlgorithm =
+ String jceKeyAlgorithm =
JCEMapper.getJCEKeyAlgorithmFromURI(algorithm,
provider);
logger.debug("JCE Provider = " + provider);
- logger.debug("JCE Algorithm = " + 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);
+ if (_contextCipher == null) {
+ // Now create the working cipher
+
+ String jceAlgorithm =
+ JCEMapper
+ .translateURItoJCEID(
+
encryptedKey.getEncryptionMethod().getAlgorithm())
+ .getAlgorithmID();
+
+ logger.debug("JCE Algorithm = " + jceAlgorithm);
+
+ 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);
+ }
+ } else {
+ c = _contextCipher;
}
Key ret;
@@ -1691,27 +1813,44 @@
/**
* Initialize the <code>XMLSerializer</code> with the specified
context
* <code>Document</code>.
- *
+ * <p/>
+ * Setup OutputFormat in a way that the serialization does <b>not</b>
+ * modifiy the contents, that is it shall not do any pretty printing
+ * and so on. This would destroy the original content before
+ * encryption. If that content was signed before encryption and the
+ * serialization modifies the content the signature verification will
+ * fail.
+ *
* @param document the context <code>Document</code>.
*/
Serializer() {
format = new OutputFormat();
format.setEncoding("UTF-8");
format.setOmitDocumentType(true);
- format.setOmitXMLDeclaration(true);
+ format.setOmitXMLDeclaration(true);
+ format.setOmitComments(false);
format.setPreserveSpace(true);
+ format.setIndenting(false);
+
}
/**
* Returns a <code>String</code> representation of the specified
* <code>Document</code>.
+ * <p/>
+ * Refer also to comments about setup of format.
*
* @param doc the <code>Document</code> to serialize.
* @return the <code>String</code> representation of the serilaized
* <code>Document</code>.
* @throws
*/
- String serialize(Document document) throws XMLEncryptionException {
+ String serialize(Document document) throws Exception {
+
+ if (_canon != null) {
+ return canonSerialize(document);
+ }
+
StringWriter output = new StringWriter();
_serializer = new XMLSerializer(output, format);
@@ -1727,66 +1866,90 @@
/**
* Returns a <code>String</code> representation of the specified
* <code>Element</code>.
+ * <p/>
+ * Refer also to comments about setup of format.
*
* @param doc the <code>Element</code> to serialize.
* @return the <code>String</code> representation of the serilaized
* <code>Element</code>.
* @throws XMLEncryptionException
*/
- String serialize(Element element) throws XMLEncryptionException {
- // StringWriter output = new StringWriter();
- ByteArrayOutputStream output = new
ByteArrayOutputStream();
- _serializer = new XMLSerializer(output, format);
+ String serialize(Element element) throws Exception {
- try {
- _serializer.serialize(element);
- } catch (IOException ioe) {
- throw new XMLEncryptionException("empty", ioe);
- }
+ if (_canon != null) {
+ return canonSerialize(element);
+ }
+ StringWriter output = new StringWriter();
+ _serializer = new XMLSerializer(output, format);
- String ret = null;
try {
- ret = output.toString("UTF-8");
- } catch (UnsupportedEncodingException uee) {
- throw new XMLEncryptionException("empty", uee);
+ _serializer.serialize(element);
+ } catch (IOException ioe) {
+ throw new XMLEncryptionException("empty", ioe);
}
- return ret;
- }
+ return (output.toString());
+ }
/**
* Returns a <code>String</code> representation of the specified
* <code>NodeList</code>.
- *
+ * <p/>
+ * This is a special case because the NodeList may represent a
+ * <code>DocumentFragment</code>. A document fragement may be a
+ * non-valid XML document (refer to appropriate description of
+ * W3C) because it my start with a non-element node, e.g. a text
+ * node.
+ * <p/>
+ * The methods first converts the node list into a document fragment.
+ * Special care is taken to not destroy the current document, thus
+ * the method clones the nodes (deep cloning) before it appends
+ * them to the document fragment.
+ * <p/>
+ * Refer also to comments about setup of format.
+ *
* @param doc the <code>NodeList</code> to serialize.
* @return the <code>String</code> representation of the serilaized
* <code>NodeList</code>.
* @throws
*/
- String serialize(NodeList content) throws XMLEncryptionException {
- StringWriter output = new StringWriter();
- _serializer = new XMLSerializer(output, format);
-
- try {
- for (int i =0; i < content.getLength(); i++) {
- Node n = content.item(i);
- if (null != n) {
- int nodeType = n.getNodeType();
- if (nodeType == Node.ELEMENT_NODE) {
- _serializer.serialize((Element) n);
+ String serialize(NodeList content) throws Exception {
//XMLEncryptionException {
+
+ if (_canon != null) {
+ ByteArrayOutputStream baos = new
ByteArrayOutputStream();
+ for (int i = 0; i < content.getLength(); i++) {
+ byte[] canonBytes =
+
_canon.canonicalizeSubtree(content.item(i));
+ baos.write(canonBytes);
+ }
+ baos.close();
+ return baos.toString("UTF-8");
}
- else if (nodeType == Node.TEXT_NODE) {
- output.write(n.getNodeValue());
+
+ StringWriter output = new StringWriter();
+ _serializer = new XMLSerializer(output, format);
+
+ DocumentFragment parts =
_contextDocument.createDocumentFragment();
+ for (int i = 0; i < content.getLength(); i++) {
+ Node n = content.item(i);
+ Node newN = n.cloneNode(true);
+ parts.appendChild(newN);
}
- }
- }
- } catch (IOException ioe) {
- throw new XMLEncryptionException("empty", ioe);
- }
-
- return (output.toString());
+ try {
+ _serializer.serialize((DocumentFragment)parts);
+ } catch (IOException ioe) {
+ throw new XMLEncryptionException("empty", ioe);
+ }
+ return (output.toString());
}
+ String canonSerialize(Node node) throws Exception {
+ ByteArrayOutputStream baos = new
ByteArrayOutputStream();
+ byte[] canonBytes = _canon.canonicalizeSubtree(node);
+ baos.write(canonBytes);
+ baos.close();
+ return baos.toString("UTF-8");
+ }
/**
*
*/
@@ -1817,7 +1980,7 @@
for (int i = 0 ; i < length ; ++i) {
Node att = atts.item(i);
if
(att.getNodeName().startsWith("xmlns:") ||
- att.getNodeName() == "xmlns") {
+
att.getNodeName().equals("xmlns")) {
// Check to see if this node
has already been found
Node p = ctx;