当前位置: 首页 > 知识库问答 >
问题:

Bouncy Castle用C#签署并验证SHA256证书

李明贤
2023-03-14

也就是说,我需要创建一个可以通过WCF JSON调用公钥的客户机,以便稍后用于签名验证,但我似乎无法使其工作。首先,这里是我用来生成证书的代码:

    public System.Security.Cryptography.X509Certificates.X509Certificate2 LoadCertificate()
    {
        System.Security.Cryptography.X509Certificates.X509Certificate2 returnX509 = null;
        //X509Certificate returnCert = null;

        try
        {
            returnX509 = new System.Security.Cryptography.X509Certificates.X509Certificate2(CertificateName, CertificatePassword);
            //returnCert = DotNetUtilities.FromX509Certificate(returnX509);
        }
        catch
        {
            Console.WriteLine("Failed to obtain cert - trying to make one now.");

            try
            {
                Guid nameGuid = Guid.NewGuid();
                AsymmetricCipherKeyPair kp;
                var x509 = GenerateCertificate("CN=Server-CertA-" + nameGuid.ToString(), out kp);
                SaveCertificateToFile(x509, kp, CertificateName, CertificateAlias, CertificatePassword);
                returnX509 = new System.Security.Cryptography.X509Certificates.X509Certificate2(CertificateName, CertificatePassword);
                //returnCert = DotNetUtilities.FromX509Certificate(returnX509);
                Console.WriteLine("Successfully wrote and retrieved cert!");
            }
            catch (Exception exc)
            {
                Console.WriteLine("Failed to create cert - exception was: " + exc.ToString());
            }
        }

        return returnX509;
    }

完成后,我使用以下代码对消息进行签名:

    public string SignData(string Message, string PrivateKey)
    {
        try
        {
            byte[] orgBytes = UnicodeEncoding.ASCII.GetBytes(Message);
            byte[] privateKey = UnicodeEncoding.ASCII.GetBytes(PrivateKey);

            string curveName = "P-521";
            X9ECParameters ecP = NistNamedCurves.GetByName(curveName);
            ECDomainParameters ecSpec = new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed());

            ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA");
            BigInteger biPrivateKey = new BigInteger(privateKey);
            ECPrivateKeyParameters keyParameters = new ECPrivateKeyParameters(biPrivateKey, ecSpec);

            signer.Init(true, keyParameters);
            signer.BlockUpdate(orgBytes, 0, orgBytes.Length);

            byte[] data = signer.GenerateSignature();
            //Base64 Encode
            byte[] encodedBytes;
            using (MemoryStream encStream = new MemoryStream())
            {
                base64.Encode(orgBytes, 0, orgBytes.Length, encStream);
                encodedBytes = encStream.ToArray();
            }

            if (encodedBytes.Length > 0)
                return UnicodeEncoding.ASCII.GetString(encodedBytes);
            else
                return "";
        }
        catch (Exception exc)
        {
            Console.WriteLine("Signing Failed: " + exc.ToString());
            return "";
        }
    }

最后,我试图验证:

    public bool VerifySignature(string PublicKey, string Signature, string Message)
    {
        try
        {
            AsymmetricKeyParameter pubKey = new AsymmetricKeyParameter(false);

            var publicKey = PublicKeyFactory.CreateKey(Convert.FromBase64String(PublicKey));
            ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA");
            byte[] orgBytes = UnicodeEncoding.ASCII.GetBytes(Message);

            signer.Init(false, publicKey);
            signer.BlockUpdate(orgBytes, 0, orgBytes.Length);

            //Base64 Decode
            byte[] encodeBytes = UnicodeEncoding.ASCII.GetBytes(Signature);
            byte[] decodeBytes;
            using (MemoryStream decStream = new MemoryStream())
            {
                base64.Decode(encodeBytes, 0, encodeBytes.Length, decStream);
                decodeBytes = decStream.ToArray();
            }

            return signer.VerifySignature(decodeBytes);
        }
        catch (Exception exc)
        {
            Console.WriteLine("Verification failed with the error: " + exc.ToString());
            return false;
        } 
    }
        Console.WriteLine("Attempting to load cert...");
        System.Security.Cryptography.X509Certificates.X509Certificate2 thisCert = LoadCertificate();
        if (thisCert != null)
        {
            Console.WriteLine(thisCert.IssuerName.Name);
            Console.WriteLine("Signing the text - Mary had a nuclear bomb");
            string signature = SignData("Mary had a nuclear bomb", thisCert.PublicKey.Key.ToXmlString(true));

            Console.WriteLine("Signature: " + signature);

            Console.WriteLine("Verifying Signature");

            if (VerifySignature(thisCert.PublicKey.Key.ToXmlString(false), signature, "Mary had a nuclear bomb."))
                Console.WriteLine("Valid Signature!");
            else
                Console.WriteLine("Signature NOT valid!");

        }
    string signature = SignData("Mary had a nuclear bomb", thisCert.PublicKey.Key.ToXmlString(true));
    public string SignDataAsXml(string Message, X509Certificate2 ThisCert)
    {
        XmlDocument doc = new XmlDocument();
        doc.PreserveWhitespace = false;
        doc.LoadXml("<core>" + Message + "</core>");

        // Create a SignedXml object.
        SignedXml signedXml = new SignedXml(doc);

        // Add the key to the SignedXml document. 
        signedXml.SigningKey = ThisCert.PrivateKey;

        // Create a reference to be signed.
        Reference reference = new Reference();
        reference.Uri = "";

        // Add an enveloped transformation to the reference.
        XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
        reference.AddTransform(env);

        // Add the reference to the SignedXml object.
        signedXml.AddReference(reference);

        // Create a new KeyInfo object.
        KeyInfo keyInfo = new KeyInfo();

        // Load the certificate into a KeyInfoX509Data object 
        // and add it to the KeyInfo object.
        keyInfo.AddClause(new KeyInfoX509Data(ThisCert));

        // Add the KeyInfo object to the SignedXml object.
        signedXml.KeyInfo = keyInfo;

        // Compute the signature.
        signedXml.ComputeSignature();

        // Get the XML representation of the signature and save 
        // it to an XmlElement object.
        XmlElement xmlDigitalSignature = signedXml.GetXml();

        // Append the element to the XML document.
        doc.DocumentElement.AppendChild(doc.ImportNode(xmlDigitalSignature, true));


        if (doc.FirstChild is XmlDeclaration)
        {
            doc.RemoveChild(doc.FirstChild);
        }

        using (var stringWriter = new StringWriter())
        {
            using (var xmlTextWriter = XmlWriter.Create(stringWriter))
            {
                doc.WriteTo(xmlTextWriter);
                xmlTextWriter.Flush();
                return stringWriter.GetStringBuilder().ToString();
            }
        }
    }

    public bool VerifyXmlSignature(string XmlMessage, X509Certificate2 ThisCert)
    {
        // Create a new XML document.
        XmlDocument xmlDocument = new XmlDocument();

        // Load the passed XML file into the document. 
        xmlDocument.LoadXml(XmlMessage);

        // Create a new SignedXml object and pass it the XML document class.
        SignedXml signedXml = new SignedXml(xmlDocument);

        // Find the "Signature" node and create a new XmlNodeList object.
        XmlNodeList nodeList = xmlDocument.GetElementsByTagName("Signature");

        // Load the signature node.
        signedXml.LoadXml((XmlElement)nodeList[0]);

        // Check the signature and return the result. 
        return signedXml.CheckSignature(ThisCert, true);
    }

共有1个答案

公冶和豫
2023-03-14

Bouncy Castle根本不支持XML格式。除非您的用例严格要求,否则您会发现使用Base64编码更容易,证书(X.509)和私钥(PKCS#8)存储在PEM格式中。这些都是字符串格式,因此可以直接与JSON一起使用。

代码示例中还有其他问题:签名应该使用私钥,签名不应该被视为ASCII字符串,可能您的消息实际上是UTF8。我希望内部的sign/verify例程可能如下所示:

    public string SignData(string msg, ECPrivateKeyParameters privKey)
    {
        try
        {
            byte[] msgBytes = Encoding.UTF8.GetBytes(msg);

            ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA");
            signer.Init(true, privKey);
            signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
            byte[] sigBytes = signer.GenerateSignature();

            return Convert.ToBase64String(sigBytes);
        }
        catch (Exception exc)
        {
            Console.WriteLine("Signing Failed: " + exc.ToString());
            return null;
        }
    }

    public bool VerifySignature(ECPublicKeyParameters pubKey, string signature, string msg)
    {
        try
        {
            byte[] msgBytes = Encoding.UTF8.GetBytes(msg);
            byte[] sigBytes = Convert.FromBase64String(signature);

            ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA");
            signer.Init(false, pubKey);
            signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
            return signer.VerifySignature(sigBytes);
        }
        catch (Exception exc)
        {
            Console.WriteLine("Verification failed with the error: " + exc.ToString());
            return false;
        }
    }

另一个问题是,我认为.NET直到.NET 3.5才获得ECDSA支持,无论如何.NET 1.1中没有ECDSA类(这是BC即将发布的1.8版本的目标--在那之后我们将“现代化”),所以DotNetUtilities不支持ECDSA。但是,我们可以导出到PKCS#12,并导入到BC。一个示例程序:

    public void Program()
    {
        Console.WriteLine("Attempting to load cert...");
        System.Security.Cryptography.X509Certificates.X509Certificate2 thisCert = LoadCertificate();

        Console.WriteLine(thisCert.IssuerName.Name);
        Console.WriteLine("Signing the text - Mary had a nuclear bomb");

        byte[] pkcs12Bytes = thisCert.Export(X509ContentType.Pkcs12, "dummy");
        Pkcs12Store pkcs12 = new Pkcs12StoreBuilder().Build();
        pkcs12.Load(new MemoryStream(pkcs12Bytes, false), "dummy".ToCharArray());

        ECPrivateKeyParameters privKey = null;
        foreach (string alias in pkcs12.Aliases)
        {
            if (pkcs12.IsKeyEntry(alias))
            {
                privKey = (ECPrivateKeyParameters)pkcs12.GetKey(alias).Key;
                break;
            }
        }

        string signature = SignData("Mary had a nuclear bomb", privKey);

        Console.WriteLine("Signature: " + signature);

        Console.WriteLine("Verifying Signature");

        var bcCert = DotNetUtilities.FromX509Certificate(thisCert);
        if (VerifySignature((ECPublicKeyParameters)bcCert.GetPublicKey(), signature, "Mary had a nuclear bomb."))
            Console.WriteLine("Valid Signature!");
        else
            Console.WriteLine("Signature NOT valid!");
    }
 类似资料:
  • 下面是我的场景--我从条形码中读取数据,并将其转换为纯文本。本文是条码数据+数字签名的结合。数字签名附加在末尾,使我能够分离出实际数据和数字签名数据。数字签名数据使用sha256哈希-用户将公钥作为windows证书文件()发送给我。 所需实现:-需要从证书中提取公钥,并根据提供的条形码数据和数字签名验证公钥。 问题:

  • 我应该如何更改A方法?任何想法都是好的。提前谢谢。

  • 下面是Java代码: 然后使用C代码来验证签名 我在想我需要将我的签名编码为hexa,但它并没有解决我的问题。我已经使用crypto编写了一个c版本的符号方法,并且已经验证过了。所以为什么当我使用java代码时,签名没有得到验证。谢谢

  • 我正在尝试使用C#和内置的加密库来验证使用EC密钥SHA256创建的签名。 我使用openssl创建了一个私钥和相应的证书: 以下是我正在使用的密钥(别担心,它们并不重要): 然后,我有一个包含字符串“Hello”的简单数据文件。然后,我使用openssl对该文件进行签名,如下所示: 然后,我可以通过首先从证书中提取公钥,然后使用公钥来验证签名: 然后,我尝试使用 C#(.NET Core 3.1

  • 但是,为了签署请求,openSSL提供了两个工具:ca和X509。然而,这些都不允许使用SHA256。根据官方文档,ca只支持md5、sha1和MDC2。x509仅支持md2、md5、sha1、MDC2。 谢谢你。

  • 代码在这里,使用BouncyCastle实现: 我希望能够读取此签名并使用公钥验证它。如何将其转换回BouncyCastle对象?