package test.xml.signature; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Collections; import java.util.List; import javax.xml.crypto.dom.DOMStructure; import javax.xml.crypto.dsig.CanonicalizationMethod; import javax.xml.crypto.dsig.DigestMethod; import javax.xml.crypto.dsig.Reference; import javax.xml.crypto.dsig.SignatureMethod; import javax.xml.crypto.dsig.SignedInfo; import javax.xml.crypto.dsig.Transform; import javax.xml.crypto.dsig.XMLSignature; import javax.xml.crypto.dsig.XMLSignatureFactory; import javax.xml.crypto.dsig.dom.DOMSignContext; import javax.xml.crypto.dsig.dom.DOMValidateContext; import javax.xml.crypto.dsig.keyinfo.KeyInfo; import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory; import javax.xml.crypto.dsig.keyinfo.KeyValue; import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; import javax.xml.crypto.dsig.spec.TransformParameterSpec; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class SignatureXML { public void saveKey(PublicKey publicKey, PrivateKey privateKey) throws Exception{ X509EncodedKeySpec ksp = new X509EncodedKeySpec(publicKey.getEncoded()); FileOutputStream fos = new FileOutputStream("C:\\public.key"); fos.write(ksp.getEncoded()); fos.close(); PKCS8EncodedKeySpec pks = new PKCS8EncodedKeySpec(privateKey.getEncoded()); fos = new FileOutputStream("C:\\private.key"); fos.write(pks.getEncoded()); fos.close(); } public Key LoadKeyFromFile(boolean ispk, String keyFile) { Key key = null; FileInputStream is = null; try { is = new FileInputStream(keyFile); byte[] buf = new byte[is.available()]; KeyFactory keyFactory = KeyFactory.getInstance("DSA"); is.read(buf); EncodedKeySpec keySpec; if (ispk) { keySpec = new PKCS8EncodedKeySpec(buf); } else { keySpec = new X509EncodedKeySpec(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(); } } return key; } public void SignatureXMLDocument(String docPath) throws Exception { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); Document doc = dbf.newDocumentBuilder().parse(new FileInputStream(docPath)); this.SignatureXMLDocument(doc); } public void SignatureXMLDocument(Document doc) throws Exception { XMLSignatureFactory fac = XMLSignatureFactory.getInstance(); /*建立 <Reference> 元素,引用整個 XML 文檔: *建立 Reference 的時候將 URI 參數指定爲 "" 表示對整個 XML 文檔進行引用; *摘要算法指定爲 SHA1;這裏將轉換方式指定爲 ENVELOPED , *這樣在對整個文檔進行引用並生成摘要值的時候,<Signature> 元素不會被計算在內。*/ 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); /*建立 <SignedInfo> 元素 *由於最終的數字簽名是針對 <SignedInfo> 元素而生成的,因此須要指定該 XML 元素的規範化方法, * 以肯定最終被處理的數據。這裏指定爲 INCLUSIVE_WITH_COMMENTS , * 表示在規範化 XML 內容的時候會將 XML 註釋也包含在內。 * 至此,待簽名的內容(<SignedInfo> 元素)已指定好,再只須要簽名所使用的密鑰就能夠建立數字簽名了。*/ 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 數字簽名規範規定了多種在 <KeyInfo> 中指定驗證密鑰的方式,好比 <KeyName>,<KeyValue>,<X509Data>,<PGPData> 等等。 * 這裏使用 XML 數字簽名規範規定必須實現的 <DSAKeyValue> 來指定驗證簽名所需的公共密鑰。 * 在程序中使用 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); //以公鑰爲參數建立 <KeyValue> 元素 KeyInfoFactory keyInfoFac = fac.getKeyInfoFactory(); KeyValue keyValue = keyInfoFac.newKeyValue(publicKey); //根據建立好的 <KeyValue> 元素建立 <KeyInfo> 元素: KeyInfo keyInfo = keyInfoFac.newKeyInfo(Collections.singletonList(keyValue)); /*這裏建立的密鑰對,其中的公鑰已經用於建立 <KeyInfo> 元素並存放在其中,供簽名驗證使用,而其中的私鑰則會在下一步被用於生成簽名。*/ //建立 <Signature> 元素 /*前面已經建立好 <SignedInfo> 和 <KeyInfo> 元素,爲了生成最終的數字簽名, * 須要根據這兩個元素先建立 <Signature> 元素,而後進行簽名, * 建立出 <SignatureValue> 元素。*/ XMLSignature signature = fac.newXMLSignature(signedInfo, keyInfo); /*XMLSignature 類中的 sign 方法用於對文檔進行簽名,在調用 sign 方法以前, * 還須要建立 DOMSignContext 對象,爲方法調用提供上下文信息, * 包括簽名所使用的私鑰和最後生成的 <Signature> 元素所在的目標父元素:*/ DOMSignContext dsc = new DOMSignContext(privateKey, doc.getDocumentElement()); //生成簽名 /*sign 方法會生成簽名值,並做爲元素值建立 <SignatureValue> 元素,而後將整個 <Signature> 元素加入爲待簽名文檔根元素的直接子元素。*/ signature.sign(dsc); TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); DOMSource source=new DOMSource(doc); transformer.transform(source, new StreamResult(System.out)); StreamResult result = new StreamResult(new File("C:\\old.xml")); transformer.transform(source,result); } private void validate(String signedFile) throws Exception { //Parse the signed XML document to unmarshal <Signature> object. DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); Document doc = dbf.newDocumentBuilder().parse(new FileInputStream(signedFile)); this.validate(doc); } private void validate(Document doc) throws Exception { // 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(new DOMStructure(signatureNode)); // Get the public key for signature validation KeyValue keyValue = (KeyValue) signature.getKeyInfo().getContent().get(0); PublicKey pubKey = keyValue.getPublicKey(); // Create ValidateContext DOMValidateContext valCtx = new DOMValidateContext(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 void main(String[] args) { SignatureXML signatureXML=new SignatureXML(); try { // signatureXML.SignatureXMLDocument("C:\\new.xml"); signatureXML.validate("C:\\old.xml"); } catch (Exception e) { e.printStackTrace(); } } }