package test.xml.signature;importjava.io.File;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.IOException;importjava.io.ObjectOutputStream;importjava.security.Key;importjava.security.KeyFactory;importjava.security.KeyPair;importjava.security.KeyPairGenerator;importjava.security.PrivateKey;importjava.security.PublicKey;importjava.security.spec.EncodedKeySpec;importjava.security.spec.PKCS8EncodedKeySpec;importjava.security.spec.X509EncodedKeySpec;importjava.util.Collections;importjava.util.List;importjavax.xml.crypto.dom.DOMStructure;importjavax.xml.crypto.dsig.CanonicalizationMethod;importjavax.xml.crypto.dsig.DigestMethod;importjavax.xml.crypto.dsig.Reference;importjavax.xml.crypto.dsig.SignatureMethod;importjavax.xml.crypto.dsig.SignedInfo;importjavax.xml.crypto.dsig.Transform;importjavax.xml.crypto.dsig.XMLSignature;importjavax.xml.crypto.dsig.XMLSignatureFactory;importjavax.xml.crypto.dsig.dom.DOMSignContext;importjavax.xml.crypto.dsig.dom.DOMValidateContext;importjavax.xml.crypto.dsig.keyinfo.KeyInfo;importjavax.xml.crypto.dsig.keyinfo.KeyInfoFactory;importjavax.xml.crypto.dsig.keyinfo.KeyValue;importjavax.xml.crypto.dsig.spec.C14NMethodParameterSpec;importjavax.xml.crypto.dsig.spec.TransformParameterSpec;importjavax.xml.parsers.DocumentBuilderFactory;importjavax.xml.transform.Transformer;importjavax.xml.transform.TransformerFactory;importjavax.xml.transform.dom.DOMSource;importjavax.xml.transform.stream.StreamResult;importorg.w3c.dom.Document;importorg.w3c.dom.Node;importorg.w3c.dom.NodeList;public classSignatureXML {public void saveKey(PublicKey publicKey, PrivateKey privateKey) throwsException{
X509EncodedKeySpec ksp= newX509EncodedKeySpec(publicKey.getEncoded());
FileOutputStream fos= new FileOutputStream("C:\\public.key");
fos.write(ksp.getEncoded());
fos.close();
PKCS8EncodedKeySpec pks= newPKCS8EncodedKeySpec(privateKey.getEncoded());
fos= new FileOutputStream("C:\\private.key");
fos.write(pks.getEncoded());
fos.close();
}public Key LoadKeyFromFile(booleanispk, String keyFile) {
Key key= null;
FileInputStream is= null;try{
is= newFileInputStream(keyFile);byte[] buf = new byte[is.available()];
KeyFactory keyFactory= KeyFactory.getInstance("DSA");
is.read(buf);
EncodedKeySpec keySpec;if(ispk) {
keySpec= newPKCS8EncodedKeySpec(buf);
}else{
keySpec= newX509EncodedKeySpec(buf);
}
key= (!ispk ?(Key) keyFactory.generatePublic(keySpec) : (Key) keyFactory.generatePrivate(keySpec));
}catch(Exception e) {
e.printStackTrace();
}finally{try{
is.close();
}catch(IOException iex) {
iex.printStackTrace();
}
}returnkey;
}public void SignatureXMLDocument(String docPath) throwsException {
DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc= dbf.newDocumentBuilder().parse(newFileInputStream(docPath));this.SignatureXMLDocument(doc);
}public void SignatureXMLDocument(Document doc) throwsException {
XMLSignatureFactory fac=XMLSignatureFactory.getInstance();/*创建 元素,引用整个 XML 文档:
*创建 Reference 的时候将 URI 参数指定为 "" 表示对整个 XML 文档进行引用;
*摘要算法指定为 SHA1;这里将转换方式指定为 ENVELOPED ,
*这样在对整个文档进行引用并生成摘要值的时候, 元素不会被计算在内。*/Transform envelopedTransform= fac.newTransform(Transform.ENVELOPED,(TransformParameterSpec) null);
DigestMethod sha1DigMethod= fac.newDigestMethod(DigestMethod.SHA1, null);
Reference refToRootDoc= fac.newReference("", sha1DigMethod,Collections.singletonList(envelopedTransform), null, null);/*创建 元素
*因为最终的数字签名是针对 元素而生成的,所以需要指定该 XML 元素的规范化方法,
* 以确定最终被处理的数据。这里指定为 INCLUSIVE_WITH_COMMENTS ,
* 表示在规范化 XML 内容的时候会将 XML 注释也包含在内。
* 至此,待签名的内容( 元素)已指定好,再只需要签名所使用的密钥就可以创建数字签名了。*/CanonicalizationMethod c14nWithCommentMethod=fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,(C14NMethodParameterSpec)null);
SignatureMethod dsa_sha1SigMethod= fac.newSignatureMethod(SignatureMethod.DSA_SHA1, null);
SignedInfo signedInfo=fac.newSignedInfo(c14nWithCommentMethod,dsa_sha1SigMethod,Collections.singletonList(refToRootDoc));/*XML 数字签名规范规定了多种在 中指定验证密钥的方式,比如 ,,, 等等。
* 这里使用 XML 数字签名规范规定必须实现的 来指定验证签名所需的公共密钥。
* 在程序中使用 java.security 包生成 DSA 密钥对。*/
//创建密钥对
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("DSA");
kpGen.initialize(512);
KeyPair keyPair=kpGen.generateKeyPair();
PublicKey publicKey=keyPair.getPublic();
PrivateKey privateKey=keyPair.getPrivate();this.saveKey(publicKey, privateKey);//以公钥为参数创建 元素
KeyInfoFactory keyInfoFac =fac.getKeyInfoFactory();
KeyValue keyValue=keyInfoFac.newKeyValue(publicKey);//根据创建好的 元素创建 元素:
KeyInfo keyInfo =keyInfoFac.newKeyInfo(Collections.singletonList(keyValue));/*这里创建的密钥对,其中的公钥已经用于创建 元素并存放在其中,供签名验证使用,而其中的私钥则会在下一步被用于生成签名。*/
//创建 元素
/*前面已经创建好 和 元素,为了生成最终的数字签名,
* 需要根据这两个元素先创建 元素,然后进行签名,
* 创建出 元素。*/XMLSignature signature=fac.newXMLSignature(signedInfo, keyInfo);/*XMLSignature 类中的 sign 方法用于对文档进行签名,在调用 sign 方法之前,
* 还需要创建 DOMSignContext 对象,为方法调用提供上下文信息,
* 包括签名所使用的私钥和最后生成的 元素所在的目标父元素:*/DOMSignContext dsc= newDOMSignContext(privateKey, doc.getDocumentElement());//生成签名
/*sign 方法会生成签名值,并作为元素值创建 元素,然后将整个 元素加入为待签名文档根元素的直接子元素。*/signature.sign(dsc);
TransformerFactory tf=TransformerFactory.newInstance();
Transformer transformer=tf.newTransformer();
DOMSource source=newDOMSource(doc);
transformer.transform(source,newStreamResult(System.out));
StreamResult result= new StreamResult(new File("C:\\old.xml"));
transformer.transform(source,result);
}private void validate(String signedFile) throwsException {//Parse the signed XML document to unmarshal object.
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc= dbf.newDocumentBuilder().parse(newFileInputStream(signedFile));this.validate(doc);
}private void validate(Document doc) throwsException {//Search the Signature element
NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS,"Signature");if (nl.getLength() == 0) {throw new Exception("Cannot find Signature element");
}
Node signatureNode= nl.item(0);
XMLSignatureFactory fac= XMLSignatureFactory.getInstance("DOM");
XMLSignature signature= fac.unmarshalXMLSignature(newDOMStructure(signatureNode));//Get the public key for signature validation
KeyValue keyValue = (KeyValue) signature.getKeyInfo().getContent().get(0);
PublicKey pubKey=keyValue.getPublicKey();//Create ValidateContext
DOMValidateContext valCtx = newDOMValidateContext(pubKey,signatureNode);//Validate the XMLSignature
boolean coreValidity =signature.validate(valCtx);//Check core validation status
if (coreValidity == false) {
System.err.println("Core validation failed");//Check the signature validation status
boolean sv =signature.getSignatureValue().validate(valCtx);
System.out.println("Signature validation status: " +sv);//check the validation status of each Reference
List refs =signature.getSignedInfo().getReferences();for (int i = 0; i < refs.size(); i++) {
Reference ref=(Reference) refs.get(i);boolean refValid =ref.validate(valCtx);
System.out.println("Reference[" + i + "] validity status: " +refValid);
}
}else{
System.out.println("Signature passed core validation");
}
}public static voidmain(String[] args) {
SignatureXML signatureXML=newSignatureXML();try{//signatureXML.SignatureXMLDocument("C:\\new.xml");
signatureXML.validate("C:\\old.xml");
}catch(Exception e) {
e.printStackTrace();
}
}
}