Digital Signing & Validating

Hi... I wrote a DigitalSigDealer class that's suposed to perform 2 basic functions...

1) Sign a document using a certificate and a keystore file.

2) Validate a signed document

My problem is that... my "sign" method signes the hole document... and I would like to be able to sign only a certain node, for example:

<doc>

<head>Something here</head>

<body>

<bodypart1>

</bodypart1>

<restofit>

Something here...

</restofit>

</body>

</doc>

so... I would like to be able to sign only what's in the "restofit" node... or, what's in the "body" node.

Right now, my "sign" method gets a parameter called nodeToSign, and puts the <header> node (of the signature) right before the nodeToSign, but other than that, I think it's the hole document what is being signed.

Can someone help me with this?

Here is my class (big part of it):

publicclass DigitalSigDealer{

/**

* Tipo de KeyStore PKCS12

*/

publicstaticfinal String KEYSTORE_TYPE_PKCS12 ="PKCS12";

/**

* Tipo de KeyStore JKS (Sun)

*/

publicstaticfinal String KEYSTORE_TYPE_JKS ="JKS";

/**

* <pre>

* Este metodo firma un documento XML utilizando un certificado

* existente y un keystore conteniendo la llave privada.

*

* Detecta automaticamente los alias del KeyStore, necesitando

* solo el password.

* </pre>

*

* @param documentPath

* @param signedDocumentPath

* @param nodeToSign

* @param certificateFilePath

* @param keyStorePath

* @param keyStoreType

* @param keyStorePassword

* @param keyPassword

* @returnnull, si el documento fue firmado correctamente

*o un String describiendo el error ocurrido.

*/

public String signDocument(

String documentPath,

String signedDocumentPath,

String nodeToSign,

String certificateFilePath,

String keyStorePath,

String keyStoreType,

String keyStorePassword,

String keyPassword){

return signDocument(

documentPath,

signedDocumentPath,

nodeToSign,

certificateFilePath,

keyStorePath,

keyStoreType,

keyStorePassword,

null,

keyPassword);

}

/**

* <pre>

* Este metodo firma un documento XML utilizando un certificado

* existente y un keystore conteniendo la llave privada.

*

* Para la firma, el documento a firmar puede ser cualquier

* documento XML. Un ejemplo de un documento valido para firmar

* es:

*

*<Documento>

*Contenido libre

*<Encabezado>

* Encabezado del documento

*</Encabezado>

*<Cuerpo>

* Cuerpo del documento

*</Cuerpo>

*</Documento>

* </pre>

*

* @param documentPath

* @param signedDocumentPath

* @param nodeToSign

* @param certificateFilePath

* @param keyStorePath

* @param keyStoreType

* @param keyStorePassword

* @param keyAlias

* @param keyPassword

* @returnnull, si el documento fue firmado correctamente

*o un String describiendo el error ocurrido.

*/

public String signDocument(

String documentPath,

String signedDocumentPath,

String nodeToSign,

String certificateFilePath,

String keyStorePath,

String keyStoreType,

String keyStorePassword,

String keyAlias,

String keyPassword){

try{

// Inicializar la biblioteca de apache security

org.apache.xml.security.Init.init();

// Cargar el documento a firmar

Document doc = loadDocument(documentPath);

// Preparar el elemento Header

Element headerElement = doc.createElementNS(

"http://schemas.xmlsoap.org/soap/envelope/",

"Header"

);

// Encontrar el nodo a firmar (el primero)

NodeList nodes = doc.getElementsByTagName(nodeToSign);

Element elemToSign =null;

if(nodes !=null){

elemToSign = (Element)nodes.item(0);

if(elemToSign!=null){

// Agregar al final el nodo Header

headerElement.setPrefix(elemToSign.getPrefix());

elemToSign.appendChild(headerElement);

}

else{

return"No existe el nodo "+nodeToSign;

}

}

else{

return"No existe el nodo "+nodeToSign;

}

// Crear el nodo Signature y agregarlo al nodo Header

XMLSignature sig =new XMLSignature(

doc,

"",

XMLSignature.ALGO_ID_SIGNATURE_RSA);

headerElement.appendChild(sig.getElement());

Transforms transforms =new Transforms(doc);

transforms.addTransform(

Transforms.TRANSFORM_ENVELOPED_SIGNATURE);

transforms.addTransform(

Transforms.TRANSFORM_C14N_WITH_COMMENTS);

sig.addDocument(

"", transforms, Constants.ALGO_ID_DIGEST_SHA1);

// Obtener la llave privada con la que se firmara

PrivateKey privateKey =null;

if(keyAlias==null)

privateKey = getPrivateKeyFromKeyStore(

keyStorePath,

keyStoreType,

keyStorePassword,

keyPassword);

else{

KeyStore ks = KeyStore.getInstance(keyStoreType);

FileInputStream fis =

new FileInputStream(keyStorePath);

ks.load(fis, keyStorePassword.toCharArray());

privateKey = (PrivateKey)ks.getKey(

keyAlias,

keyPassword.toCharArray());

}

// Obtener el certificado y la llave publica

X509Certificate cert =

(X509Certificate)getCertificateFromFile(

new File(certificateFilePath));

sig.addKeyInfo(cert);

sig.addKeyInfo(cert.getPublicKey());

// Firmar el documento

sig.sign(privateKey);

// Escribir el documento en el archivo de salida

FileOutputStream f =

new FileOutputStream(signedDocumentPath);

f.write("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n".getBytes());

XMLUtils.outputDOMc14nWithComments(doc, f);

f.close();

returnnull;

}catch(Exception e){

return e.getMessage();

}

}

/**

* <pre>

* Este metodo valida la(s) firma(s) digital(es) en un

* documento XML.

* </pre>

*

* @paramdocumentPathEl documento firmado a validar

* @return true, si el documento contiene al menos un nodo

* Signature y su SignedInfo contiene un certificado

* o una llave publica y la firma es valida para esa

* llave. Retorna false en cualquier otro caso.

*/

publicboolean validateDigitalSignature(String documentPath){

try{

org.apache.xml.security.Init.init();

Document doc = loadDocument(documentPath);

// Buscar los nodos Signature en el documento

Element elem =null;

NodeList nodes = doc.getElementsByTagNameNS(

Constants.SignatureSpecNS,

"Signature"

);

if(nodes.getLength()==0)// No hay nodos Signature

returnfalse;

/*

* Un documento puede tener varias firmas

* => el documento es valido si todas sus

* firmas lo son.

*/

for(int i = 0;i<nodes.getLength();i++){

elem = (Element)nodes.item(i);

XMLSignature sig =new XMLSignature(elem,"");

// Obtener KeyInfo asociado a la firma

KeyInfo ki = sig.getKeyInfo();

if(ki==null)

returnfalse;

else{

// Obtener certificado

X509Certificate cert =

sig.getKeyInfo().getX509Certificate();

explainLite(cert);

if(cert !=null){

// Checkear la firma

if(!sig.checkSignatureValue(cert))

returnfalse;

}

else{

// Checkear contra la llave publica

PublicKey pk =

sig.getKeyInfo().getPublicKey();

if(pk==null)

returnfalse;

else{

if(!sig.checkSignatureValue(pk))

returnfalse;

}

}

}

}

returntrue;

}

catch(Exception e){

e.printStackTrace();

returnfalse;

}

}

/* ... some utility methods here ... */

}

>

[12447 byte] By [Juarisa] at [2007-10-1 11:29:21]
# 1

You need to look at transforms.

You can use XSLT to parse the document before signing and then include that XSLT in the signature for use by the validation engine. It will automatically parse and apply the XSLT.

So, have an external file holding the XSL

Bring that into a DOM document

Cast that into a Node

Node xslElement = transDoc.getDocumentElement();

Import that into the document to sign

Node xslElemImported = doc.importNode(xslElement, true);

Add that to your Transforms

transforms.addTransform(Transforms.TRANSFORM_XSLT,(org.w3c.dom.Element)xslElemImported);

Now when you sign you will sign the results of the transform.

Then add the transforms to the signature

sig.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1);

where "" is a NULL ReferenceURI

transforms is all signature/XSLT related transforms

and digest the digest mechanism.

For that matter you might be able to use an XPath inthe reference URI but I think you are better off with XSLT

grooa at 2007-7-10 13:00:37 > top of Java-index,Security,Other Security APIs, Tools, and Issues...
# 2
hi,are you able to successfully validate your document.I have an issue with validation.can you clarify
devteama at 2007-7-10 13:00:37 > top of Java-index,Security,Other Security APIs, Tools, and Issues...
# 3

Hi, congratulations for you code. I have two questions:

1.- how would you go if you to add another sign to an already signed documents? I need to have more than one person signing the same document, so that the signs are not nested.

2.- is there any way using Java, to show the available certificates window to the user so that he can select one, instead of signing through a .pks file?

Thank you

jsanjosema at 2007-7-10 13:00:37 > top of Java-index,Security,Other Security APIs, Tools, and Issues...