我在XML文件中验证数字签名时遇到问题。 它说核心验证失败了。 我知道有很多人问这个问题,因为我一直在寻找 也回答这个网站。大部分时间。 我几乎尝试了所有找到的方法,但结果总是一样的。 我希望你不要将这个问题标记为重复。 我将包含用于测试的文件(密钥库,xml文件源和签名的xml文件)。
签署xml文档的过程
守则:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import javax.xml.parsers.DocumentBuilderFactory;
import org.bouncycastle.util.encoders.Base64;
import org.w3c.dom.Document;
import com.sun.org.apache.xml.internal.security.Init;
import com.sun.org.apache.xml.internal.security.c14n.Canonicalizer;
import com.sun.org.apache.xml.internal.security.signature.XMLSignature;
import com.sun.org.apache.xml.internal.security.transforms.Transforms;
import com.sun.org.apache.xml.internal.security.utils.Constants;
import com.sun.org.apache.xml.internal.security.utils.ElementProxy;
public class SignDocuments {
public static void main(String[] args) throws Exception {
File originFile = new File("");
File keyStore = new File("");
String alias = "";
String pass = "";
int rc = signDocument(keyStore, originFile, alias, pass);
System.out.println("RC: " + rc);
}
private static int signDocument(File keyStore, File originFile, String alias, String pass) throws Exception {
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(originFile);
Init.init();
ElementProxy.setDefaultPrefix(Constants.SignatureSpecNS, "");
KeyStore keyStorez = KeyStore.getInstance("JKS");
keyStorez.load(new FileInputStream(keyStore), pass.toCharArray());
XMLSignature sig = new XMLSignature(doc, null, XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1);
Transforms transforms = new Transforms(doc);
transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
sig.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1);
PrivateKey privateKey = (PrivateKey)keyStorez.getKey(alias, pass.toCharArray());
X509Certificate cert = (X509Certificate)keyStorez.getCertificate(alias);
String zzzz = convertToPem(cert);
FileOutputStream foz = new FileOutputStream(new File("E:\\Innova\\new mayapada\\mayapadaX.cer"));
foz.write(zzzz.getBytes());
foz.close();
sig.addKeyInfo(cert);
sig.addKeyInfo(cert.getPublicKey());
sig.sign(privateKey);
doc.getDocumentElement().getElementsByTagName("SOAP-ENV:Header").item(0).appendChild(sig.getElement());
FileOutputStream fos = new FileOutputStream("E:\\Innova\\mayapada\\523097000990_BS_I_10030029993_20151019_140850SOAPSIGNED.xml");
fos.write(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS).canonicalizeSubtree(doc));
fos.close();
return 0;
}
private static String convertToPem(X509Certificate certificate) {
String certBegin = "-----BEGIN CERTIFICATE-----\n";
String certEnd = "\n-----END CERTIFICATE-----";
int i = 0;
byte[] der = null;
String certInPemFormat = null;
String tempData = null;
StringBuilder sb = new StringBuilder();
try {
der = certificate.getEncoded();
} catch (CertificateEncodingException e) {
e.printStackTrace();
return null;
}
tempData = new String(Base64.encode(der));
certInPemFormat = certBegin;
while (i <= tempData.length()) {
if (i > 0 && i % 64 == 0) {
sb.append("\n");
certInPemFormat += sb.toString();
sb = new StringBuilder();
}
sb.append(tempData.charAt(i));
i++;
if (i == tempData.length()) {
certInPemFormat += sb.toString();
break;
}
}
certInPemFormat += certEnd;
return certInPemFormat;
}
}
要验证,我已经尝试了两种方法。
中的方法守则:
import java.io.File;
import java.io.FileInputStream;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import com.sun.org.apache.xml.internal.security.Init;
import com.sun.org.apache.xml.internal.security.keys.KeyInfo;
import com.sun.org.apache.xml.internal.security.signature.XMLSignature;
import com.sun.org.apache.xml.internal.security.utils.Constants;
import com.sun.org.apache.xml.internal.security.utils.IgnoreAllErrorHandler;
import digitalcertificate.DigitalSignature;
public class CC {
public static void main(String[] args) throws Exception {
Init.init();
String signatureFileName = "F:\\2016\\zzz.xml";
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setAttribute("http://xml.org/sax/features/namespaces", Boolean.TRUE);
File f = new File(signatureFileName);
System.out.println("Verifying: " + signatureFileName);
DocumentBuilder db = dbf.newDocumentBuilder();
db.setErrorHandler(new IgnoreAllErrorHandler());
Document doc = db.parse(new FileInputStream(f));
Element sigElement = null;
NodeList nl = doc.getElementsByTagNameNS(Constants.SignatureSpecNS, "Signature");
if (nl.getLength() != 0) {
System.out.println("Found " + nl.getLength() + " Signature elements");
sigElement = (Element) nl.item(0);
XMLSignature signature = new XMLSignature(sigElement, "");
System.out.println("signature: " + DigitalSignature.encodeToBase64(signature.getSignatureValue()));
KeyInfo keyInfo = signature.getKeyInfo();
if (keyInfo != null) {
if (keyInfo.containsX509Data()) {
System.out.println("Could find a X509Data element in the KeyInfo");
X509Certificate cert = signature.getKeyInfo().getX509Certificate();
System.out.println("Certificate: \n" + DigitalSignature.convertCertToPemFormat(cert));
if (cert != null) {
System.out.println("The XML signature is " + (signature.checkSignatureValue(cert) ? "valid (good" : "Invalid!!!! (bad)"));
} else {
System.out.println("Did not find a certificate");
PublicKey pk = signature.getKeyInfo().getPublicKey();
if (pk != null) {
System.out.println("The XML signature is " + (signature.checkSignatureValue(pk) ? "valid (good" : "Invalid!!!! (bad)"));
} else {
System.out.println("Did not find a public key, so i can't check the signature");
}
}
}
}
}
}
}
以下是我使用此方法时的结果:
> The XML signature is Invalid!!!! (bad)Feb 02, 2016 2:18:05 PM com.sun.org.apache.xml.internal.security.signature.XMLSignature checkSignatureValue
WARNING: Signature verification failed.
第二种方法是指这里的帖子(stackoverflow),但我忘了链接。 呵呵.. 这是第二种方法:
package digitalcertificate;
import java.io.File;
import java.io.FileInputStream;
import java.security.Key;
import java.security.KeyException;
import java.security.PublicKey;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509CRL;
import java.util.Iterator;
import java.util.List;
import javax.xml.crypto.AlgorithmMethod;
import javax.xml.crypto.KeySelector;
import javax.xml.crypto.KeySelectorException;
import javax.xml.crypto.KeySelectorResult;
import javax.xml.crypto.XMLCryptoContext;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyValue;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import sun.security.x509.X509CertImpl;
public class ValidateXMLFile {
public static void main(String[] args) throws Exception {
String signedDocument = "F:\\2016\\zzz.xml";
int rc = validateDoc(signedDocument);
}
private static int validateDoc(String signedDocument) throws Exception {
// Instantiate the document to be validated.
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(new FileInputStream(new File(signedDocument)));
// Find signature element.
NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nl.getLength() == 0) {
throw new Exception("Cannot find signature element");
}
// Create a DOM XMLSignatureFactory that will be used to unmarshal
// document containing the XMLSignature.
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
// Create a DOMValidateContext and specify a KeyValue KeySelector
// and document context.
DOMValidateContext valContext = new DOMValidateContext(new KeyValueKeySelector(), nl.item(0));
// Unmarshal the signature.
XMLSignature signature = fac.unmarshalXMLSignature(valContext);
byte[] data = signature.getSignatureValue().getValue();
System.out.println("Signature: " + DigitalSignature.convertCertificateToPemFormat(data));
// Validate the XMLSignature.
boolean coreValidity = signature.validate(valContext);
// Check the core validation status.
if (!coreValidity) {
System.err.println("Signature failed core validation");
boolean sv = signature.getSignatureValue().validate(valContext);
System.out.println("Signature validation status: " + sv);
// Check the validation status of each reference.
Iterator<?> i = signature.getSignedInfo().getReferences().iterator();
for (int j = 0; i.hasNext(); j++) {
boolean refValid = ((Reference)i.next()).validate(valContext);
System.out.println("Ref[" + j +"] validity status: " + refValid);
}
} else {
System.out.println("Signature passed core validation");
}
return 0;
}
/**
* @author Akbar
* KeySelector which retrieves the public key out of the KeyValue
* element and returns it.
* NOTE: If the key algorithm doesn't match signature algorithm,
* then the public key will be ignored.
*/
private static class KeyValueKeySelector extends KeySelector {
@Override
public KeySelectorResult select(KeyInfo keyInfo, Purpose purpose,
AlgorithmMethod method, XMLCryptoContext context)
throws KeySelectorException {
if (keyInfo == null) {
throw new KeySelectorException("Null Key Info Object");
}
SignatureMethod sm = (SignatureMethod)method;
System.out.println("Signature method: " + sm.getAlgorithm());
X509CertImpl certImpl = null;
List<?> list = keyInfo.getContent();
for (int i = 0; i < list.size(); i++) {
XMLStructure xmlStructure = (XMLStructure)list.get(i);
if (xmlStructure instanceof X509Data) {
X509Data xd = (X509Data)xmlStructure;
Object[] entries = xd.getContent().toArray();
X509CRL crl = null;
for (int j = 0; j < entries.length; j++) {
if (entries[i] instanceof X509CRL) {
crl = (X509CRL)entries[i];
}
if (entries[i] instanceof X509CertImpl) {
certImpl = (X509CertImpl)entries[i];
try {
certImpl.checkValidity();
} catch (CertificateExpiredException e) {
e.printStackTrace();
} catch (CertificateNotYetValidException e) {
e.printStackTrace();
}
}
}
}
if (xmlStructure instanceof KeyValue) {
PublicKey pk = null;
try {
pk = ((KeyValue)xmlStructure).getPublicKey();
} catch (KeyException e) {
throw new KeySelectorException(e);
}
// Make sure algorithm is compatible with method.
if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) {
return new SimpleKeySelectorResult(pk);
}
}
}
throw new KeySelectorException("No KeyValue element found!");
}
private boolean algEquals(String algURI, String algName) {
if (algName.equalsIgnoreCase("DSA") &&
algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) {
return true;
} else if (algName.equalsIgnoreCase("RSA") &&
algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) {
return true;
} else {
return false;
}
}
}
private static class SimpleKeySelectorResult implements KeySelectorResult {
private PublicKey pk;
SimpleKeySelectorResult(PublicKey pk) {
this.pk = pk;
}
@Override
public Key getKey() {
return pk;
}
}
}
结果是:
Signature validation status: false
Signature failed core validation
Ref[0] validity status: true
https://drive.google.com/open?id=0B87pqq4ZNBHkNWwzN1dBRHhfWlE 这是用于测试的密钥库和xml文件的链接。 密钥库凭据: 别名= Akbar pass = dragon270389
我最诚挚的问候, 阿克