• 【Java密码学】Java SE 6中XML数字签名的实现


    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();
           }
       }
    
    }
  • 相关阅读:
    mysql-day06
    C语言 输出二进制数
    Python学习笔记(一)
    数组指针与指针数组
    重装系统--小白版
    Java 面对对象阶段练手项目【飞机大战】
    Java环境的配置
    在Linux环境下运行C语言程序
    Torrent文件
    ubuntu下载速度慢的解决办法--修改下载源
  • 原文地址:https://www.cnblogs.com/qiuxiangmuyu/p/6202043.html
Copyright © 2020-2023  润新知