You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cxf.apache.org by emilSverige <es...@gmail.com> on 2009/11/26 13:39:32 UTC

Signing an MTOM attachment only, not the entire message

Hi, I want to sign an MTOM attachment only, not the soap message. I am aware
that the WSS4J-interceptor doesn't sign the attachment, so I'm trying to do
it in plain java instead.

Please note that the soap message should be unsigned, only the attachment
should be signed.

So far I have managed to sign an attachment and save it to disk, then read
it and send with cxf. No problem, but I would like to get rid of the "save
to disk" part since it seems unnecessary.

Unfortunately though, I don't know how to. I have no idea how a Datahandler
can handle a  DeferredDocumentImpl. Any help is appreciated!

This is the code that signs, saves to disk, reads from disk and prepares the
request:

/**
 * @param fileInputStream The data to sign
 * @return A signed document
 */
DeferredDocumentImpl createSignedDocument(FileInputStream fileInputStream){
		// Create a DOM XMLSignatureFactory that will be used to generate the
		// enveloped signature
		XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
		
		// Create a Reference to the enveloped document (in this case we are
		// signing the whole document, so a URI of "" signifies that) and
		// also specify the SHA1 digest algorithm and the ENVELOPED Transform.
		Reference ref = fac.newReference("", fac.newDigestMethod(
				DigestMethod.SHA1, null), Collections.singletonList(fac
				.newTransform(Transform.ENVELOPED,
				(TransformParameterSpec) null)), null, null);
		
		// Create the SignedInfo
		SignedInfo si = fac
				.newSignedInfo(fac.newCanonicalizationMethod(
				CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
				(C14NMethodParameterSpec) null), fac
				.newSignatureMethod(SignatureMethod.RSA_SHA1, null),
				Collections.singletonList(ref));
		
		KeyStore keystore = KeyStore.getInstance("JKS");
		keystore.load(new FileInputStream("myKeystore.jks"),
"aPassword".toCharArray());
		
		PrivateKey privateKey = (PrivateKey)keystore.getKey( "MyAlias",
"aPassword".toCharArray());
		
		KeyStore.PasswordProtection password = new
KeyStore.PasswordProtection("aPassword".toCharArray());
		KeyStore.PrivateKeyEntry thePrivKeyEntry = (KeyStore.PrivateKeyEntry)
keystore.getEntry("myAlias", password);
		X509Certificate cert = (X509Certificate) thePrivKeyEntry.getCertificate();
		
		// Create the KeyInfo containing the X509Data.
		KeyInfoFactory kif = fac.getKeyInfoFactory();
		List x509Content = new ArrayList();
		x509Content.add(cert.getSubjectX500Principal().getName());
		x509Content.add(cert);
		X509Data xd = kif.newX509Data(x509Content);
		KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
		
		
		// Instantiate the document to be signed
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		dbf.setNamespaceAware(true);
		Document doc = dbf.newDocumentBuilder().parse(fileInputStream);
		
		// Create a DOMSignContext and specify the DSA PrivateKey and
		// location of the resulting XMLSignature's parent element
		DOMSignContext dsc = new DOMSignContext(privateKey, doc
				.getDocumentElement());
		
		// Create the XMLSignature (but don't sign it yet)
		XMLSignature signature = fac.newXMLSignature(si, ki);
		
		// Marshal, generate (and sign) the enveloped signature
		signature.sign(dsc);
		
		return doc;
	}

public void sendAnAttachmentWithSignature(){
	// Sign the dokument
	DeferredDocumentImpl signedDoc = new XmlSign().createSignedDocument( new
FileInputStream("a_file.xml") );
	
	// Save it to disk
	OutputStream os = new FileOutputStream( "a_signed_file.xml" );
			TransformerFactory tf = TransformerFactory.newInstance();
			Transformer trans = tf.newTransformer();
			trans.transform(new DOMSource(signedDoc ), new StreamResult(os));
	
	// Create a DataHandler to attach to the web service klient
	DataHandler dh = new DataHandler( 
					new FileDataSource(
					new File("a_signed_file.xml" )) );
	
	// Add the datahandler to the request object
	WebServiceRequest req = new WebServiceRequest();
	req.setData( dh );
					
	// Then send the request...
	
}
-- 
View this message in context: http://old.nabble.com/Signing-an-MTOM-attachment-only%2C-not-the-entire-message-tp26528881p26528881.html
Sent from the cxf-user mailing list archive at Nabble.com.


Re: Signing an MTOM attachment only, not the entire message

Posted by Daniel Kulp <dk...@apache.org>.

You can PROBABLY achieve this by creating a subclass of DataHandler and 
overriding the writeTo.... method.   Something like:

final DeferredDocumentImpl signedDoc 
      = new XmlSign().createSignedDocument(new FileInputStream("a_file.xml"));
DataHandler dh = new DataHandler(signedDoc, "text/xml") {
   public void writeTo(OutputStream out) {
         TransformerFactory tf = TransformerFactory.newInstance();
         Transformer trans = tf.newTransformer();
         trans.transform(new DOMSource(signedDoc ), new StreamResult(out));
   }
}


That said, that could be made automatic, I think.  You don't say where the 
WebServiceRequest object comes from.  Is that generated from WSDL?    I think 
if you add xmime:expectedContentTypes="text/xml" to the element that defines 
the base64binary data, the code generator would then generate it as a Document 
instead of a DataHandler.    Thus, you could just set the document in and be 
done.

Dan



On Thu November 26 2009 7:39:32 am emilSverige wrote:
> Hi, I want to sign an MTOM attachment only, not the soap message. I am
>  aware that the WSS4J-interceptor doesn't sign the attachment, so I'm
>  trying to do it in plain java instead.
> 
> Please note that the soap message should be unsigned, only the attachment
> should be signed.
> 
> So far I have managed to sign an attachment and save it to disk, then read
> it and send with cxf. No problem, but I would like to get rid of the "save
> to disk" part since it seems unnecessary.
> 
> Unfortunately though, I don't know how to. I have no idea how a Datahandler
> can handle a  DeferredDocumentImpl. Any help is appreciated!
> 
> This is the code that signs, saves to disk, reads from disk and prepares
>  the request:
> 
> /**
>  * @param fileInputStream The data to sign
>  * @return A signed document
>  */
> DeferredDocumentImpl createSignedDocument(FileInputStream fileInputStream){
> 		// Create a DOM XMLSignatureFactory that will be used to generate 
the
> 		// enveloped signature
> 		XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
> 
> 		// Create a Reference to the enveloped document (in this case we are
> 		// signing the whole document, so a URI of "" signifies that) and
> 		// also specify the SHA1 digest algorithm and the ENVELOPED 
Transform.
> 		Reference ref = fac.newReference("", fac.newDigestMethod(
> 				DigestMethod.SHA1, null), Collections.singletonList(fac
> 				.newTransform(Transform.ENVELOPED,
> 				(TransformParameterSpec) null)), null, null);
> 
> 		// Create the SignedInfo
> 		SignedInfo si = fac
> 				.newSignedInfo(fac.newCanonicalizationMethod(
> 				CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
> 				(C14NMethodParameterSpec) null), fac
> 				.newSignatureMethod(SignatureMethod.RSA_SHA1, null),
> 				Collections.singletonList(ref));
> 
> 		KeyStore keystore = KeyStore.getInstance("JKS");
> 		keystore.load(new FileInputStream("myKeystore.jks"),
> "aPassword".toCharArray());
> 
> 		PrivateKey privateKey = (PrivateKey)keystore.getKey( "MyAlias",
> "aPassword".toCharArray());
> 
> 		KeyStore.PasswordProtection password = new
> KeyStore.PasswordProtection("aPassword".toCharArray());
> 		KeyStore.PrivateKeyEntry thePrivKeyEntry = 
(KeyStore.PrivateKeyEntry)
> keystore.getEntry("myAlias", password);
> 		X509Certificate cert = (X509Certificate)
>  thePrivKeyEntry.getCertificate();
> 
> 		// Create the KeyInfo containing the X509Data.
> 		KeyInfoFactory kif = fac.getKeyInfoFactory();
> 		List x509Content = new ArrayList();
> 		x509Content.add(cert.getSubjectX500Principal().getName());
> 		x509Content.add(cert);
> 		X509Data xd = kif.newX509Data(x509Content);
> 		KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
> 
> 
> 		// Instantiate the document to be signed
> 		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
> 		dbf.setNamespaceAware(true);
> 		Document doc = dbf.newDocumentBuilder().parse(fileInputStream);
> 
> 		// Create a DOMSignContext and specify the DSA PrivateKey and
> 		// location of the resulting XMLSignature's parent element
> 		DOMSignContext dsc = new DOMSignContext(privateKey, doc
> 				.getDocumentElement());
> 
> 		// Create the XMLSignature (but don't sign it yet)
> 		XMLSignature signature = fac.newXMLSignature(si, ki);
> 
> 		// Marshal, generate (and sign) the enveloped signature
> 		signature.sign(dsc);
> 
> 		return doc;
> 	}
> 
> public void sendAnAttachmentWithSignature(){
> 	// Sign the dokument
> 	DeferredDocumentImpl signedDoc = new XmlSign().createSignedDocument( new
> FileInputStream("a_file.xml") );
> 
> 	// Save it to disk
> 	OutputStream os = new FileOutputStream( "a_signed_file.xml" );
> 			TransformerFactory tf = TransformerFactory.newInstance();
> 			Transformer trans = tf.newTransformer();
> 			trans.transform(new DOMSource(signedDoc ), new 
StreamResult(os));
> 
> 	// Create a DataHandler to attach to the web service klient
> 	DataHandler dh = new DataHandler(
> 					new FileDataSource(
> 					new File("a_signed_file.xml" )) );
> 
> 	// Add the datahandler to the request object
> 	WebServiceRequest req = new WebServiceRequest();
> 	req.setData( dh );
> 
> 	// Then send the request...
> 
> }
> 

-- 
Daniel Kulp
dkulp@apache.org
http://www.dankulp.com/blog