2011-03-16 48 views
4

我已经编写了下面的.NET Framework 3.5 C#方法,它采用XML文档的位置和X509数字证书(使用私钥)的对象表示形式,并将XML文档作为具有作为根的第一个子元素嵌入的XML签名(XMLDsig)的对象。C#XML签名方法的Java等效

的事情是,我direly需要能够做同样的程序与Java SE 6中,但我没有写在任何年龄Java和不知道从哪里开始。

任何人都可以提供Java代码中的等效方法,它可以生成确切的相同的XML输出吗?

private static XmlDocument SignXmlDocument(string xmlFilePath, X509Certificate2 certificate) 
{ 
    // load xml from disk preserving whitespaces 
    XmlDocument xmlDocument = new XmlDocument { PreserveWhitespace = true }; 
    xmlDocument.Load(xmlFilePath); 

    // create signed xml with a same-document reference containing an enveloped-signature transform 
    SignedXml signedXml = new SignedXml(xmlDocument) { SigningKey = certificate.PrivateKey }; 
    Reference reference = new Reference { Uri = "" }; 
    XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();    
    reference.AddTransform(env); 
    signedXml.AddReference(reference); 

    // embed public key information for signature validation purposes 
    KeyInfo keyInfo = new KeyInfo(); 
    KeyInfoX509Data keyInfoX509Data = new KeyInfoX509Data(certificate, X509IncludeOption.ExcludeRoot); 
    keyInfo.AddClause(keyInfoX509Data); 
    signedXml.KeyInfo = keyInfo; 

    // compute and retreive the signature xml 
    signedXml.ComputeSignature();   
    XmlElement xmldsigXmlElement = signedXml.GetXml(); 

    // insert the signature xml into the xml document as first child of the root element 
    xmlDocument.DocumentElement.PrependChild(xmlDocument.ImportNode(xmldsigXmlElement, true)); 

    return xmlDocument; 
} 

回答

4

下确实在Java中同样的事情。它需要在磁盘上有一个PKCS12证书文件。

import java.util.*; 
import java.io.*; 
import java.security.KeyStore; 
import java.security.KeyStore.PrivateKeyEntry; 
import java.security.cert.X509Certificate; 
import javax.xml.crypto.dsig.*; 
import javax.xml.crypto.dsig.dom.DOMSignContext; 
import javax.xml.crypto.dsig.keyinfo.*; 
import javax.xml.crypto.dsig.spec.*; 
import javax.xml.parsers.DocumentBuilderFactory; 
import javax.xml.transform.*; 
import javax.xml.transform.dom.DOMSource; 
import javax.xml.transform.stream.StreamResult; 
import org.w3c.dom.Document; 

public class XMLSigner { 

    public static void signXmlDocumentOnDisk(String fileToBeSignedPath, String signedFileSavePath, String pkcs12CertificateFilePath, String password) throws Exception { 
     XMLSignatureFactory fac = getXMLSignatureFactory(); 
     Reference ref = getSHA1WholeDocumentEnvelopedTransformReference(fac); 
     SignedInfo si = getSignedInfo(fac, ref); 
     PrivateKeyEntry keyEntry = loadPKCS12KeyStoreAndGetSigningKeyEntry(pkcs12CertificateFilePath, password); 
     KeyInfo ki = getKeyInfoWithX509Data(keyEntry, fac); 
     Document doc = instantiateDocumentToBeSigned(fileToBeSignedPath); 
     signDocumentAndPlaceSignatureAsFirstChildElement(doc, keyEntry, fac, si, ki); 
     writeResultingDocument(doc, signedFileSavePath); 
    } 

    private static XMLSignatureFactory getXMLSignatureFactory() { 
     return XMLSignatureFactory.getInstance("DOM"); 
    } 

    private static Reference getSHA1WholeDocumentEnvelopedTransformReference(XMLSignatureFactory fac) throws Exception { 
     return 
      fac.newReference(
       "", 
       fac.newDigestMethod(DigestMethod.SHA1, null), 
       Collections.singletonList(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), 
       null, 
       null 
      ); 
    } 

    private static SignedInfo getSignedInfo(XMLSignatureFactory fac, Reference ref) throws Exception { 
     return 
      fac.newSignedInfo(
       fac.newCanonicalizationMethod(
        CanonicalizationMethod.INCLUSIVE, 
        (C14NMethodParameterSpec) null 
       ), 
       fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), 
       Collections.singletonList(ref) 
      ); 
    } 

    private static PrivateKeyEntry loadPKCS12KeyStoreAndGetSigningKeyEntry(String pkcs12CertificateFilePath, String password) throws Exception { 
     KeyStore ks = KeyStore.getInstance("PKCS12"); 
     ks.load(new FileInputStream(pkcs12CertificateFilePath), password.toCharArray());  
     return (PrivateKeyEntry)ks.getEntry(ks.aliases().nextElement(), new KeyStore.PasswordProtection(password.toCharArray()));   
    } 

    private static KeyInfo getKeyInfoWithX509Data(PrivateKeyEntry keyEntry, XMLSignatureFactory fac) { 
     X509Certificate cert = (X509Certificate) keyEntry.getCertificate(); 
     KeyInfoFactory kif = fac.getKeyInfoFactory(); 
     List x509Content = new ArrayList(); 
     x509Content.add(cert.getSubjectX500Principal().getName()); 
     x509Content.add(cert); 
     X509Data xd = kif.newX509Data(x509Content); 
     return kif.newKeyInfo(Collections.singletonList(xd)); 
    } 

    private static Document instantiateDocumentToBeSigned(String fileToBeSignedPath) throws Exception { 
     DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 
     dbf.setNamespaceAware(true); 
     return dbf.newDocumentBuilder().parse(new FileInputStream(fileToBeSignedPath)); 
    } 

    private static void signDocumentAndPlaceSignatureAsFirstChildElement(Document doc, PrivateKeyEntry keyEntry, XMLSignatureFactory fac, SignedInfo si, KeyInfo ki) throws Exception { 
     DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), doc.getDocumentElement(), doc.getDocumentElement().getFirstChild()); 
     XMLSignature signature = fac.newXMLSignature(si, ki); 
     signature.sign(dsc); 
    } 

    private static void writeResultingDocument(Document doc, String signedFileSavePath) throws Exception { 
     OutputStream os = new FileOutputStream(signedFileSavePath); 
     TransformerFactory tf = TransformerFactory.newInstance(); 
     Transformer trans = tf.newTransformer(); 
     trans.transform(new DOMSource(doc), new StreamResult(os)); 
    } 
} 
+0

嗨。我使用此代码,但System.Security.Cryptography.Xml.SignedXml.CheckSignature()返回false。你有什么想法,为什么? – gogagubi 2016-04-01 09:03:52

+0

我在同样的事情上挣扎着。在JDK将它们考虑在内的情况下,默认情况下,.NET中的签名检查默认会忽略空白和换行符。在将它提供给SignedXml构造函数之前,可以通过将XmlDocument的PreserveWhitespaces属性设置为true来很容易地解决这个问题。 – vlow 2017-11-15 23:13:15

-1
public static bool VerifyXml(string fileNFe) 
{ 
    //carregar o XML da NF-e 
    XmlDocument xml = new XmlDocument(); 

    xml.PreserveWhitespace = true; 
    xml.Load(fileNFe); 

    //pegar a Tag "NFe", à que contem assinatura dig. 
    XmlNodeList nodeNFe = xml.GetElementsByTagName("NFe"); 
    string NFe = nodeNFe[0].OuterXml.ToString(); 

    //carregar o XML da NFe 
    XmlDocument xmlNFe = new XmlDocument(); 
    xmlNFe.PreserveWhitespace = true; 

    xmlNFe.LoadXml(NFe); 

    //Carregar a assinatura 
    SignedXml signedXml = new SignedXml(xmlNFe); 
    XmlNodeList nodeList = xmlNFe.GetElementsByTagName("Signature"); 
    signedXml.LoadXml((XmlElement)nodeList[0]); 

    //buscar o KeyInfo da assinatura 
    IEnumerator keyInfoItems = signedXml.KeyInfo.GetEnumerator(); 
    keyInfoItems.MoveNext(); 
    KeyInfoX509Data keyInfoX509 = (KeyInfoX509Data)keyInfoItems.Current; 

    //buscar o certificado do KeyInfo 
    X509Certificate2 keyInfoCert = (X509Certificate2)keyInfoX509.Certificates[0]; 

    //Validar se assinatura é valida 
    bool isSigValid = signedXml.CheckSignature(keyInfoCert.PublicKey.Key); 

    if (isSigValid) 
    { 
     MessageBox.Show("Assinatura digital é válida."); 
     return true;     
    } 
    else 
    { 
     MessageBox.Show("Assinatura digital não é válida."); 
     return false; 
    } 
} 
+0

你能翻译英文评论吗? – cheesemacfly 2013-04-03 14:26:55

+0

@cheesemacfly,“Assinatura digitaléválida。” - >“数字签名有效”和“Assinatura digitalnãoéválida”。 - >“数字签名无效”;) – yugidroid 2013-04-10 09:24:11

+0

这个问题与验证签名无关,它与签名有关。 – lox 2013-06-04 07:25:12