如何在 C# 中使用证书文件验证签名值?

How to verify Signature Value using cert file in C#?

我是安全新手,对此深表歉意。我有一个 xml 文件,其中包括

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ID_VG_Response>
  <Result>
    <SubjectDN>CN="XX", SERIALNUMBER=XX/XX, C=IN</SubjectDN>
    <UserIDN>XX</UserIDN>
    <CardNumber>XX</CardNumber>
    <TransactionType>XX</TransactionType>
    <Status>Success</Status>
  </Result>
  <Validity>180</Validity>
  <SignatureTime>
    <date>20150726</date>
    <time>15:01:51:927</time>
  </SignatureTime>
  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
      <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" />
      <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
      <Reference URI="">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
        <DigestValue>XXX</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>XXXXXXXXXX</SignatureValue>
    <KeyInfo>
      <X509Data>
        <X509Certificate>XXXX</X509Certificate>
      </X509Data>
    </KeyInfo>
  </Signature>
</ID_VG_Response>

和一些cer文件。现在我需要使用 C# 中的 cer 文件验证 SignatureValue。谁能帮帮我吗?一位同事向我发送了一个 java 文件来验证这一点,但我需要在 C# 中执行此操作?这是java代码,

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;

import javax.security.cert.CertificateException;
import javax.security.cert.X509Certificate;
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.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.X509Data;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

import sun.misc.BASE64Decoder;
import sun.security.x509.X509CertImpl;

public class XMLSignatureValidator {
    private String userIDN;

    public static void main(String[] args) throws Exception {
        XMLSignatureValidator validator = new XMLSignatureValidator();
        //boolean verified = validator.verifyVGResponse(new FileInputStream(
        //      "C:/ID_VG_Response.xml"), "CA");
        FileInputStream fis = new FileInputStream("C:/ID_VG_Response.xml");
        byte[] data = new byte[fis.available()];
        fis.read(data);
        fis.close();

        boolean verified = validator.verifyVGResponse(new String(data),
                "E:/EIDA Toolkit/VG Related/ADSIC_Prod/VG Response Cert");
        System.out.println(verified);
    }

    public boolean verifyVGResponse(final String vgResponse,
            final String cAFolderPath) throws Exception {

        byte[] xml = new BASE64Decoder().decodeBuffer(vgResponse);
        return verifyVGResponse(new ByteArrayInputStream(xml), cAFolderPath);
    }

    public boolean verifyVGResponse(final InputStream vgResponse,
            final String cAFolderPath) throws Exception {

        X509Certificate[] certificates = getCertificates(cAFolderPath);
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

        dbf.setNamespaceAware(true);

        DocumentBuilder builder = dbf.newDocumentBuilder();
        Document doc = builder.parse(vgResponse);

        NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS,
                "Signature");

        if (nl.getLength() == 0) {
            throw new Exception("Cannot find Signature element");
        }
        final X509KeySelector keySelector = new X509KeySelector();
        DOMValidateContext valContext = new DOMValidateContext(keySelector,
                nl.item(0));

        XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM",
                new org.jcp.xml.dsig.internal.dom.XMLDSigRI());

        final XMLSignature signature = factory
                .unmarshalXMLSignature(valContext);

        // Verify signature.
        boolean coreValidity = signature.validate(valContext);

        if (coreValidity) {

            // Verify the complete certificate chain.
            final X509CertImpl signerCertImpl = (X509CertImpl) keySelector
                    .getSignerCertificate();
            final X509Certificate signerCert = javax.security.cert.X509Certificate
                    .getInstance(signerCertImpl.getEncoded());
            boolean verified = verifyCertificate(signerCert, certificates);
            if (verified) {

                NodeList nl2 = doc.getElementsByTagName("Status");

                if (!nl2.item(0).getFirstChild().getNodeValue()
                        .equalsIgnoreCase("Success")) {
                    throw new Exception("Status element not set to success");
                }

                long signerTime = new SimpleDateFormat("yyyyMMdd HH:mm:ss:SSS")
                        .parse(doc.getElementsByTagName("date").item(0)
                                .getFirstChild().getNodeValue()
                                + " "
                                + doc.getElementsByTagName("time").item(0)
                                        .getFirstChild().getNodeValue())
                        .getTime();

                int validitySec = Integer.parseInt(doc
                        .getElementsByTagName("Validity").item(0)
                        .getFirstChild().getNodeValue());

                System.out.println("signer time: " + signerTime);
                System.out.println("signer time date: " + new Date(signerTime));
                System.out.println("validitySec: " + validitySec);
                System.out.println("validitySec * 1000: " + validitySec * 1000);
                Date d = new Date();
                System.out.println("Date on the server is: " + d);
                System.out.println("Date on server in milliseconds: " + d.getTime());
                System.out.println(d.getTime() > signerTime + validitySec * 1000);

                if (d.getTime() > signerTime + validitySec * 1000) {
                    throw new Exception("VG response expired");
                }

                userIDN = doc.getElementsByTagName("UserIDN").item(0)
                        .getFirstChild().getNodeValue();

                return true;
            } else {
                throw new Exception("Certificate Not Valid");
            }
        }

        throw new Exception("Signature Not Valid");
    }
//  CHECKSTYLE_IGNORE_START
    private final boolean verifyCertificate(final X509Certificate signerCert,
            final X509Certificate[] certificates) throws InvalidKeyException,
            NoSuchAlgorithmException, NoSuchProviderException,
            SignatureException, CertificateException {
//      CHECKSTYLE_IGNORE_END
        signerCert.checkValidity();

        X509Certificate issuerCert = null;
        String issuerDN = signerCert.getIssuerDN().getName();
        for (int i = 0; i < certificates.length; i++) {
            if (certificates[i].getSubjectDN().getName().equals(issuerDN)) {
                issuerCert = certificates[i];
                break;
            }
        }
        if (issuerCert == null) {
            return false;
        }
        signerCert.verify(issuerCert.getPublicKey());
        for (int i = 0; i < certificates.length; i++) {
            if (signerCert.getSerialNumber().equals(
                    certificates[i].getSerialNumber())) {
                return true;
            }
        }
        return false;
    }
//  CHECKSTYLE_IGNORE_START
    private class SimpleKeySelectorResult implements KeySelectorResult {

        Key pk = null;

        SimpleKeySelectorResult(Key _pk) {
            pk = _pk;
        }

        // @Override
        public Key getKey() {
            return pk;
        }
    }
//  CHECKSTYLE_IGNORE_END
    public class X509KeySelector extends KeySelector {

        private X509CertImpl cert;

        @SuppressWarnings("rawtypes")
        public KeySelectorResult select(KeyInfo keyInfo,
                KeySelector.Purpose purpose, AlgorithmMethod method,
                XMLCryptoContext context) throws KeySelectorException {
            Iterator ki = keyInfo.getContent().iterator();
            while (ki.hasNext()) {
                XMLStructure info = (XMLStructure) ki.next();
                if (!(info instanceof X509Data)) {
                    continue;
                }

                X509Data x509Data = (X509Data) info;
                Iterator xi = x509Data.getContent().iterator();

                while (xi.hasNext()) {
                    Object o = xi.next();

                    if (o instanceof X509Certificate) {

                        // Currently not used, the object returned is
                        // X509CertImpl

                        PublicKey key = ((X509Certificate) o).getPublicKey();
                        // Make sure the algorithm is compatible
                        // with the method.
                        if (algEquals(method.getAlgorithm(), key.getAlgorithm())) {
                            return new SimpleKeySelectorResult(key);
                        }
                    }

                    if (o instanceof X509CertImpl) {

                        cert = ((X509CertImpl) o);
                        PublicKey key = ((X509CertImpl) o).getPublicKey();
                        // Make sure the algorithm is compatible
                        // with the method.
                        if (algEquals(method.getAlgorithm(), key.getAlgorithm())) {
                            return new SimpleKeySelectorResult(key);
                        }
                    }
                }
            }

            throw new KeySelectorException("No key found!");
        }

        public X509CertImpl getSignerCertificate() {
            return cert;
        }
//      CHECKSTYLE_IGNORE_START
        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 if (algName.equalsIgnoreCase("RSA")
                    && algURI
                            .equalsIgnoreCase("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256")) {
                return true;
            } else {
                return false;
            }
        }
//      CHECKSTYLE_IGNORE_END
    }

    private static X509Certificate[] getCertificates(String cAFolderPath)
            throws FileNotFoundException, CertificateException {

        ArrayList<X509Certificate> certificates = new ArrayList<X509Certificate>();
        File f = new File(cAFolderPath);
        if (f.isDirectory()) {
            File[] files = f.listFiles();

            for (int i = 0; i < files.length; i++) {
                X509Certificate cert = X509Certificate
                        .getInstance(new FileInputStream(files[i]));
                certificates.add(cert);
            }

            X509Certificate[] certs = new X509Certificate[certificates.size()];
            if (certificates.size() != 0) {
                for (int i = 0; i < certs.length; i++) {
                    certs[i] = certificates.get(i);
                }
                return certs;
            }
        }
        return null;
    }

    public String getUserIDN() {
        return userIDN;
    }

    public void setUserIDN(String userIDN) {
        this.userIDN = userIDN;
    }
}

如果您有 public 键(类似于):

<RSAKeyValue>
    <Modulus>tt5QV .... kJqsMZ2yuxZfoyQ==</Modulus>
    <Exponent>AQAB</Exponent>
</RSAKeyValue>

您应该能够验证已签名的 xml 文档,例如:

private bool TryGetValidDocument(string publicKey, XmlDocument doc)
{
    var rsa = new RSACryptoServiceProvider();
    rsa.FromXmlString(publicKey);

    var nsMgr = new XmlNamespaceManager(doc.NameTable);
    nsMgr.AddNamespace("sig", "http://www.w3.org/2000/09/xmldsig#");

    var signedXml = new SignedXml(doc);
    var sig = (XmlElement) doc.SelectSingleNode("//sig:Signature", nsMgr);
    if (sig == null)
    {
        Console.WriteLine("Could not find the signature node");
        return false;
    }
    signedXml.LoadXml(sig);

    return signedXml.CheckSignature(rsa);
}

有关更多信息,请关注此 link

对于证书非常相似。代码应如下所示:

private bool TryGetValidDocument(X509Certificate2 cert, XmlDocument doc)
{
    var nsMgr = new XmlNamespaceManager(doc.NameTable);
    nsMgr.AddNamespace("sig", "http://www.w3.org/2000/09/xmldsig#");

    var signedXml = new SignedXml(doc);
    var sig = (XmlElement) doc.SelectSingleNode("//sig:Signature", nsMgr);
    if (sig == null)
    {
        Logger.Warn("Could not find the signature node");
        return false;
    }
    signedXml.LoadXml(sig);

    return signedXml.CheckSignature(cert, true);
}

可以找到完整的示例here