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 ... */
}
>

