简体   繁体   中英

Convert .NET detached signature verification to bouncycastle

I'm trying to convert the following code (which uses .NET's built-in cryptography libraries) to BouncyCastle. Unfortunately, between the lack of documentation, the confusion with all the Java BC questions, and the unusualness of working with detached signatures, I can't figure out how to do this.

.NET code:

1) Code I need to convert:

var data = Encoding.ASCII.GetBytes("Hi!");
var signature = File.ReadAllText("Signature.p7s");
byte[] hashedDocument = HashAlgorithm.Create("SHA256").ComputeHash(data);
var cms = new SignedCms(new ContentInfo(hashedDocument), true);
cms.Decode(Convert.FromBase64String(signature));
cms.CheckSignature(true);

2) Code to generate the p7s:

var data = File.ReadAllBytes(fileToSign);
var hash = SHA256.Create().ComputeHash(data);
ContentInfo ci = new ContentInfo(hash);
SignedCms cms = new SignedCms(ci, true);
CmsSigner signer = new CmsSigner(new X509Certificate2(cert, pw));
signer.IncludeOption = X509IncludeOption.WholeChain;
signer.DigestAlgorithm = new Oid("sha256RSA");
signer.SignedAttributes.Add(new Pkcs9DocumentName(cert.SubjectName.Name));
signer.SignedAttributes.Add(new Pkcs9SigningTime());
cms.ComputeSignature(signer);
var enc = Convert.ToBase64String(cms.Encode());
File.WriteAllText(sigFile, enc);

I've tried BouncyCastle's RsaDigestSigner , CmsSignedData , CmsSignedDataParser , and SignerUtilities.GetSigner("SHA-256withRSA") (which just returns the RsaDigestSigner ), but none of them seem to have both a way to understand the p7s file and actually verify that it applies to the specified data.

So considering how you haven't provided an example on how you are generating the detached signature, I had to make some assumptions.

The code below was run in .net Core 3.0.

using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;

namespace Whatever
{
    class Program
    {
        static string cert = "MIIJmQIBA.... // This here is base64 encoded PFX for the purposes of the demo ONLY";
        static string pwd = "password";

        static void Main(string[] args)
        {
            var data = Encoding.ASCII.GetBytes("Hi!");

            var hash = HashAlgorithm.Create("SHA256").ComputeHash(data);

            var certificate = new X509Certificate2(Convert.FromBase64String(cert), pwd);

            // So since I've no idea how you got p7s, i improvised here:
            var cms = new SignedCms(new ContentInfo(hash), true); // true -> Detached
            var signer = new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, certificate);
            cms.ComputeSignature(signer);
            var data2 = cms.Encode();

            // assuming this was in p7s file
            var xx = Convert.ToBase64String(data2);

            // this passes, this is the .Net validation from OP
            var cms2 = new SignedCms(new ContentInfo(hash), true);
            cms2.Decode(Convert.FromBase64String(xx));
            cms2.CheckSignature(true);

            // Same in bouncy castle:
            BCUtil.Validate(certificate, hash, xx);
        }
    }
}

Here is our BCUtil class:

using System;
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.Security;
using System.IO;
using System.Linq;

namespace Whatever
{
    public class BCUtil
    {
        public static void Validate(System.Security.Cryptography.X509Certificates.X509Certificate2 cert, byte[] hash, string whatToValidate)
        {
            // My understanding is that you always need a cert to validate a signature with BC, but you only need a PUBLIC key
            var certificate = DotNetUtilities.FromX509Certificate(cert);

            // hash here
            var processable = new CmsProcessableByteArray(hash);
            // and signature here, for full .Net convert to old using() {} syntax
            using var str = new MemoryStream(Convert.FromBase64String(whatToValidate));
            var cms = new CmsSignedData(processable, str);
            var signers = cms.GetSignerInfos();
            var signersCollection = signers.GetSigners();

            foreach(var signer in signersCollection.Cast<SignerInformation>())
            {
                if (signer.Verify(certificate.GetPublicKey()))
                {
                    Console.WriteLine("yes banana"); // pass
                }
                else
                {
                    throw new Exception("no banana"); // fail
                }
            }
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM