Digital Sign With Sha256 With C#

How to do signature using SHA256withRSA algorithm in C#

Do not use RSACryptoServiceProvider unless you are doing interop with CAPI, like opening a named key.

To do RSA signing with SHA-(2-)256:

byte[] signature = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

You can get the RSA object from a cert:

using (RSA rsa = cert.GetRSAPrivateKey())
{
...
}

Or you can make one up from existing RSA parameters:

using (RSA rsa = RSA.Create(rsaParameters))
{
...
}

Or you can make up a new key from nothing:

using (RSA rsa = RSA.Create(2048))
{
// you’ll need to save the parameters somewhere to have a stable key
rsaParameters = rsa.ExportParameters(true));
...
}

How to sign and verify an ECDSA with SHA256 signature in C#

Mr Polk is correct, you need to use verifier.blockUpdate with the data, and then use the signature bytes in VerifySignature.

Signatures cannot be verified on their own. The algorithm requires the data as input to calculate the hash value before entering the validation state. Of course having the signature verified without the data only provides limited value: you can just prove that the private key was used.

With ECDSA there is also no real option to verify any part of the signature without the hash


RSA / PKCS#1 in theory would allow you to verify that the contents of the signature are at least created by the private key, even though the contents are unknown. As you can retrieve the full hash value, it is possible to guess the data input without having to go through the signature verification phase for each possible input value. Most API's are of course not geared towards such use of signatures as this is specific to certain schemes.

How can I sign a file using RSA and SHA256 with .NET?

RSA + SHA256 can and will work...

Your later example may not work all the time, it should use the hash algorithm's OID, rather than it's name. As per your first example, this is obtained from a call to CryptoConfig.MapNameToOID(AlgorithmName) where AlgorithmName is what you are providing (i.e. "SHA256").

First you are going to need is the certificate with the private key. I normally read mine from the LocalMachine or CurrentUser store by using a public key file (.cer) to identify the private key, and then enumerate the certificates and match on the hash...

X509Certificate2 publicCert = new X509Certificate2(@"C:\mycertificate.cer");

//Fetch private key from the local machine store
X509Certificate2 privateCert = null;
X509Store store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
foreach( X509Certificate2 cert in store.Certificates)
{
if (cert.GetCertHashString() == publicCert.GetCertHashString())
privateCert = cert;
}

However you get there, once you've obtained a certificate with a private key we need to reconstruct it. This may be required due to the way the certificate creates it's private key, but I'm not really sure why. Anyway, we do this by first exporting the key and then re-importing it using whatever intermediate format you like, the easiest is xml:

//Round-trip the key to XML and back, there might be a better way but this works
RSACryptoServiceProvider key = new RSACryptoServiceProvider();
key.FromXmlString(privateCert.PrivateKey.ToXmlString(true));

Once that is done we can now sign a piece of data as follows:

//Create some data to sign
byte[] data = new byte[1024];

//Sign the data
byte[] sig = key.SignData(data, CryptoConfig.MapNameToOID("SHA256"));

Lastly, the verification can be done directly with the certificate's public key without need for the reconstruction as we did with the private key:

key = (RSACryptoServiceProvider)publicCert.PublicKey.Key;
if (!key.VerifyData(data, CryptoConfig.MapNameToOID("SHA256"), sig))
throw new CryptographicException();

Bouncy Castle Sign and Verify SHA256 Certificate With C#

Bouncy Castle doesn't support XML formats at all. Unless your use-case strictly requires it, you'll find it much easier going just to use Base64 encodings, with certificates (X.509) and private keys (PKCS#8) stored in PEM format. These are all string formats, so should be usable with JSON directly.

There are other problems in the code samples: signing should use the private key, signatures shouldn't be treated as ASCII strings, possibly your messages are actually UTF8. I would expect the inner sign/verify routines to perhaps look like this:

    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;
}
}

A further issue is that I think .NET didn't get ECDSA support until .NET 3.5, in any case there's no ECDsa class in .NET 1.1 (which is BC's target for the upcoming 1.8 release - we will be "modernising" after that), so DotNetUtilities doesn't have support for ECDSA. However, we can export to PKCS#12 and import to BC. An example program:

    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!");
}

I haven't really tested any of the above code, but it should give you something to go on. Note that BC has key and certificate generators too, so you could choose to use BC for everything (except XML!), and export/import to/from .NET land only where necessary.

C# signature verification using ECDSA with SHA256 certificate

The lack of PEM/OpenSSL-compatible manipulation tools in .NET proved to be extremely frustrating. I ended up using Bouncy Castle to load the certificate or public key and then use that to verify my ASN signature. Here's a full working code sample demonstrating how to perform the signature verification using both the certificate and the PEM public key and working with an ASN-encoded signature.

using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;

namespace security_sandbox
{
class Program
{
static void Main(string[] args)
{

var certificateString = @"-----BEGIN CERTIFICATE-----
MIIB4TCCAYegAwIBAgIUKt0WdaKI2eRXBO2nVk+OF6AZqHMwCgYIKoZIzj0EAwIw
RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMDA1MDcxMzM2MTNaGA8yMTIwMDQx
MzEzMzYxM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAf
BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABO39ccEC+Zv1HDeMy/s7JculdYN/8DXC+U3Qn1rdF351H4NMfkbC
H9bnqwZ4a5oR9HDfaNMX14OQd4VERZV1bhGjUzBRMB0GA1UdDgQWBBRGuUmsyB2h
JCXMRTVMRTcdoWZQaDAfBgNVHSMEGDAWgBRGuUmsyB2hJCXMRTVMRTcdoWZQaDAP
BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIGG8tQZlh7aJaI34Y7jq
44SmSc/ule9MgjIX+Gg+i5vwAiEA9Jb/304KO4t9OMqFMQeWZXIHdzhDFBwx7FWz
78+UsnY=
-----END CERTIFICATE-----";
var pemreader = new PemReader(new StringReader(certificateString));
var cert = (X509Certificate)pemreader.ReadObject();

// Alternatively, load the public key directly
var pubkeyString =
@"-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7f1xwQL5m/UcN4zL+zsly6V1g3/w
NcL5TdCfWt0XfnUfg0x+RsIf1uerBnhrmhH0cN9o0xfXg5B3hURFlXVuEQ==
-----END PUBLIC KEY-----";
pemreader = new PemReader(new StringReader(pubkeyString));
var pubkey = (AsymmetricKeyParameter)pemreader.ReadObject();

var data = "Hello";
var signature = Convert.FromBase64String("MEUCIQD5593C/NBhHA1DILT72gjhGj/lKjom9vYP+JbuypBrxQIgNAjYT1LihEpPbUhe1n9ccUHQvw676bGqOTEU/25qcRQ=");

// Verify using the public key
var signer = SignerUtilities.GetSigner("SHA-256withECDSA");
signer.Init(false, pubkey);
signer.BlockUpdate(Encoding.ASCII.GetBytes(data), 0, data.Length);
var success = signer.VerifySignature(signature);

if (success) {
Console.WriteLine("Signature verified successfully using public key");
} else {
Console.WriteLine("Failed to verify signature using public key");
}

// Verify using the certificate - the certificate's public key is extracted using the GetPublicKey method.
signer.Init(false, cert.GetPublicKey());
signer.BlockUpdate(Encoding.ASCII.GetBytes(data), 0, data.Length);
success = signer.VerifySignature(signature);

if (success) {
Console.WriteLine("Signature verified successfully using certificate");
} else {
Console.WriteLine("Failed to verify signature using certificate");
}
}
}
}

How to sign XML with SHA256 & X.509 correctly?

While you successfully called signedXml.ComputeSignature() that just does the computation, it doesn't insert a signature element into the document (the XmlDocument you pass in the constructor is just what document GetXml()'s node will parent to).

You need to call signedXml.GetXml() (after ComputeSignature) to get the computed ds:Signature element, then insert that into your document.

The fact that your document has a signature node already in it is confusing. My first thought was that test.xml already had that signature in it, but your comment says that it was just some oops code from elsewhere. Lesson learned, I guess :).



Related Topics



Leave a reply



Submit