Verify a SHA-256 signature with public key from modulus and exponent - c#

I have been wrapping my head around this problem for some time now. I am to implement a signature check, which is already implemented using c# but we need to implement it on iOS also.
The c# code looks like this.
var signedData = version + "#" + string.Join("#", deltas.Select(s => s.Key + "=" + s.Value));
var signature = Convert.FromBase64String(vertification);
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{ //TODO: Configuration
rsa.FromXmlString("<RSAKeyValue><Modulus>a long text here</Modulus><Exponent>a small text here</Exponent></RSAKeyValue>");
var valid = rsa.VerifyData(Encoding.UTF8.GetBytes(signedData), new SHA256Managed(), signature);
return valid;
}
A little catch is that we can't use any cocoa pods or external libraries.

Related

C# ECDSA signature running in an online compiler fails

I'm running this code successfully on my Windows machine (Win 10 x64, running dotnet 4.7.2). It generates an EC keypair ("P-256"), hashes the plaintext with SHA-256, signs the hash with the ec private key and verifies the signature against the hashed plaintext with the ec public key.
I'm getting this output so everything works fine:
EC signature curve secp256r1 / P-256 string
dataToSign: The quick brown fox jumps over the lazy dog
* * * sign the plaintext with the EC private key * * *
EC keysize: 256
signature (Base64): cwLBRSt1vtO33tHWcTdx1OTu9lBFXHEJgvdRyDUynLLE5eMakUZUAKLwaJvYoS7NBylx2Zz0+G6dvgJ6xv5qNA==
* * *verify the signature against hash of plaintext with the EC public key * * *
signature verified: True
Now I'm trying to find any online compiler that is been able to run the code. My favorite compiler
(https://repl.it/, Mono C# compiler version 6.8.0.123, full code: https://repl.it/#javacrypto/EcSignatureFull#main.cs) is running into this error:
Unhandled Exception:
System.NotImplementedException: The method or operation is not implemented.
at EcSignatureString.Main () [0x00036] in <13e2ad358a924efc874a89efad35ffe7>:0
[ERROR] FATAL UNHANDLED EXCEPTION: System.NotImplementedException: The method or operation is not implemented.
at EcSignatureString.Main () [0x00036] in <13e2ad358a924efc874a89efad35ffe7>:0
exit status 1
Using another platform (https://dotnetfiddle.net/, Compiler .net 5, full code: https://dotnetfiddle.net/lSPpjz) is giving this similar error:
Unhandled exception. System.PlatformNotSupportedException: Windows Cryptography Next Generation (CNG) is not supported on this platform.
at System.Security.Cryptography.ECDsaCng..ctor(Int32 keySize)
at EcSignatureString.Main()
Command terminated by signal 6
So my question: is there any online compiler available that is been able to run the code?
I assume my question might be a slice off-topic for SO - in this case - is there any other stackexchange-site that would be a better place for my question?
Warning: the following code has no exception handling and is for educational purpose only:
using System;
using System.Security.Cryptography;
class EcSignatureString {
static void Main() {
Console.WriteLine("EC signature curve secp256r1 / P-256 string");
string dataToSignString = "The quick brown fox jumps over the lazy dog";
byte[] dataToSign = System.Text.Encoding.UTF8.GetBytes(dataToSignString);
Console.WriteLine("dataToSign: " + dataToSignString);
try {
Console.WriteLine("\n* * * sign the plaintext with the EC private key * * *");
ECDsaCng ecDsaKeypair = new ECDsaCng(256);
Console.WriteLine("EC keysize: " + ecDsaKeypair.KeySize);
byte[] hashedData = null;
byte[] signature = null;
// create new instance of SHA256 hash algorithm to compute hash
HashAlgorithm hashAlgo = new SHA256Managed();
hashedData = hashAlgo.ComputeHash(dataToSign);
// sign Data using private key
signature = ecDsaKeypair.SignHash(hashedData);
string signatureBase64 = Convert.ToBase64String(signature);
Console.WriteLine("signature (Base64): " + signatureBase64);
// get public key from private key
string ecDsaPublicKeyParametersXml = ecDsaKeypair.ToXmlString(ECKeyXmlFormat.Rfc4050);
// verify
Console.WriteLine("\n* * *verify the signature against hash of plaintext with the EC public key * * *");
ECDsaCng ecDsaVerify = new ECDsaCng();
bool signatureVerified = false;
ecDsaVerify.FromXmlString(ecDsaPublicKeyParametersXml, ECKeyXmlFormat.Rfc4050);
signatureVerified = ecDsaVerify.VerifyHash(hashedData, signature);
Console.WriteLine("signature verified: " + signatureVerified);
}
catch(ArgumentNullException) {
Console.WriteLine("The data was not signed or verified");
}
}
}
Microsoft has decided that encryption and hashing must be fully delegated to the OS (in .NET Framework it was half and half), so now .NET 5 (and .NET Core) has multiple backends for encryption (for example for ECDsa it has ECDsaCng that uses Windows services and ECDsaOpenSsl for Linux/MacOs that uses OpenSsl (see MSDN)
Now... the solution for your problem is to use the ECDsa class and let it select its backend. There are some problems with it. You can't easily export the keys to xml format, nor you can easily export them to PEM format. You can easily export them to a byte[], and you can easily import them from PEM format. This isn't really a big problem, because rarely you'll need to generate keys, and normally your program receives its keys from an external source, or if it generates them itself, it can save them to binary format to reuse them later.
var dataToSignString = "Hello world!";
var dataToSign = Encoding.UTF8.GetBytes(dataToSignString);
Console.WriteLine("dataToSign: " + dataToSignString);
try
{
Console.WriteLine("\n* * * sign the plaintext with the EC private key * * *");
var ecDsaKeypair = ECDsa.Create(ECCurve.NamedCurves.nistP256);
// Normally here:
//ecDsaKeypair.ImportFromPem()
Console.WriteLine("EC keysize: " + ecDsaKeypair.KeySize);
byte[] hashedData = null;
byte[] signature = null;
// create new instance of SHA256 hash algorithm to compute hash
HashAlgorithm hashAlgo = new SHA256Managed();
hashedData = hashAlgo.ComputeHash(dataToSign);
// sign Data using private key
signature = ecDsaKeypair.SignHash(hashedData);
string signatureBase64 = Convert.ToBase64String(signature);
Console.WriteLine("signature (Base64): " + signatureBase64);
// get public key from private key
string ecDsaPublicKeyParameters = Convert.ToBase64String(ecDsaKeypair.ExportSubjectPublicKeyInfo());
// verify
Console.WriteLine("\n* * *verify the signature against hash of plaintext with the EC public key * * *");
var ecDsaVerify = ECDsa.Create(ECCurve.NamedCurves.nistP256);
bool signatureVerified = false;
// Normally here:
//ecDsaKeypair.ImportFromPem()
var publicKey = Convert.FromBase64String(ecDsaPublicKeyParameters);
ecDsaVerify.ImportSubjectPublicKeyInfo(publicKey, out _);
signatureVerified = ecDsaVerify.VerifyHash(hashedData, signature);
Console.WriteLine("signature verified: " + signatureVerified);
}
catch (ArgumentNullException)
{
Console.WriteLine("The data was not signed or verified");
}
About the From/ToXmlFormat, the current comment on them on the github of .NET Core is:
// There is currently not a standard XML format for ECC keys, so we will not implement the default
// To/FromXmlString so that we're not tied to one format when a standard one does exist. Instead we'll
// use an overload which allows the user to specify the format they'd like to serialize into.
Mmmh from some tests done, exporting in PEM format seems to be quite easy:
public static IEnumerable<string> Split(string str, int chunkSize)
{
for (int i = 0; i < str.Length; i += chunkSize)
{
yield return str.Substring(i, Math.Min(chunkSize, str.Length - i));
}
}
and then
string b64privateKey = Convert.ToBase64String(ecDsaKeypair.ExportPkcs8PrivateKey());
b64privateKey = string.Join("\r\n", Split(b64privateKey, 64));
string pemPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" + b64privateKey + "\r\n-----END PRIVATE KEY-----";
or
string b64publicKey = Convert.ToBase64String(ecDsaKeypair.ExportSubjectPublicKeyInfo());
b64publicKey = string.Join("\r\n", Split(b64publicKey, 64));
string pemPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" + b64publicKey + "\r\n-----END PUBLIC KEY-----";
(note that I had to split the string manually in blocks of 64 characters, that is the exact number given in the rfc7468, because Convert.ToBase64String() supports only the 76 line length)

C# sign data ECDSA from smartcard

I am trying to sign some data using the private key from the smart card. The key algorithm is ECDSA. when I try to get the private key object it occurs system not supported exception.
Then after some research, I get to know that X509Certificate2 is not supporting EC Keys.
sysSec.X509Certificate2 cert = CertHelper.GetSignCertificate(serialNumber); //Get Certificate from Store var
key = cert.PrivateKey;
Then i try to use Bouncy Castle library. But in here i couldn't get ECPrivateKeyParameters after parsing X509Certificate2 . There is a code :
byte[] pkcs12Bytes = cert.Export(sysSec.X509ContentType.Pkcs12,"test");
Pkcs12Store pkcs12 = new Pkcs12StoreBuilder().Build();
pkcs12.Load(new MemoryStream(pkcs12Bytes, false), "test".ToCharArray());
ECPrivateKeyParameters privKey = null;
foreach (string alias in pkcs12.Aliases)
{
if (pkcs12.IsKeyEntry(alias))
{
privKey = (ECPrivateKeyParameters)pkcs12.GetKey(alias).Key;
break;
}
}
It also not works. But strange things happen when I create CMS file. It works.
public byte[] Sign(byte[] data , X509Certificate2 certificate ,bool detached )
{
if (data == null)
throw new ArgumentNullException("data");
if (certificate == null)
throw new ArgumentNullException("certificate");
// setup the data to sign
// ContentInfo content = new ContentInfo( new Oid("1.3.14.3.2.26"), data);
ContentInfo content = new ContentInfo( data);
SignedCms signedCms = new SignedCms(content, detached);
CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, certificate);
signer.SignedAttributes.Add(new Pkcs9DocumentName("testname"));
signer.SignedAttributes.Add(new Pkcs9SigningTime());
//signer.;
// CmsRecipientCollection recipest =new CmsRecipientCollection ()
// create the signature
signedCms.ComputeSignature(signer);
// signedCms.ComputeSignature()
byte[] res = signedCms.Encode();
foreach (SignerInfo info in signedCms.SignerInfos)
{
foreach (var item in info.SignedAttributes)
{
string frname = item.Oid.FriendlyName ?? "null";
Console.WriteLine(string.Format(" OID {0} : Value {1}", frname, item.Oid.Value.ToString()));
}
foreach (var item in info.UnsignedAttributes)
{
string frname = item.Oid.FriendlyName ?? "null";
Console.WriteLine(string.Format(" OID {0} : Value {1}", frname, item.Oid.Value.ToString()));
}
}
Console.WriteLine("Signed !");
return res;
}
So do anyone knows how to handle it?
Also how to sign from smartCard using Bouncy Castle?
According to my understanding BouncyCastle is a cryptographic library. It can sign something, if you provide the key. Smart cards however don't typically export private keys (so I have some doubts, whether your certificate contains the one from the smart card) but expect commands to sign something, e. g. by receiving the respective hash value and returning the signature (after ensuring appropriate user authentication).
This is typically accomplished using a PKCS#11 interface (assumed you have a driver for it matching the command set of your card) or by sending the appropriate command APDUs directly to the card (quite complicated) from your application. I found nothing on the bouncy castle website, suggesting that there is some support for addressing smart cards. (It may be hidden in the OpenPGP functionality, if your card is compliant to that standard.)
So without being acquainted with BouncyCastle it seems to me, that it won't match your expectations.

How to verify xml digital signature in c# without using SignedXml?

How to verify xml signature (used in SOAP requests) without usage of SignedXml (which is not available in dotnet core)?
I am trying like this, but it gives me false all the time:
public static void CheckSignature(XElement responseXml, MyResponseType response)
{
string originalDigestValue = Convert.ToBase64String(response.Signature.SignedInfo.Reference.FirstOrDefault().DigestValue);
var originalSignatureValue = response.Signature.SignatureValue.Value;
X509DataType certificateData = (X509DataType)response.Signature.KeyInfo.Items[0];
X509Certificate2 certificate = new X509Certificate2((byte[])certificateData.Items[0]);
//for calculating digest value
//responseXml.Descendants(nm + "Signature").SingleOrDefault().Remove();
//var digestValue = Convert.ToBase64String(SHA1.Create().ComputeHash(System.Text.Encoding.UTF8.GetBytes(responseXml.Document.ToString())));
XNamespace nm = #"http://www.w3.org/2000/09/xmldsig#";
var signedInfoNode = responseXml.Descendants(nm + "SignedInfo").SingleOrDefault();
var signedInfo = signedInfoNode.ToString().Trim();
byte[] signedInfoBytes = Encoding.UTF8.GetBytes(signedInfo);
var hash = SHA1.Create().ComputeHash(signedInfoBytes);
RSA rsa = certificate.GetRSAPublicKey();
try
{
Console.WriteLine("Signed Info: \n" + signedInfo);
Console.WriteLine("Verification: \n" + rsa.VerifyData(signedInfoBytes, originalSignatureValue, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1));
Console.WriteLine("Verification hash: \n" + rsa.VerifyData(hash, originalSignatureValue, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1));
}
catch (Exception exc)
{
//
}
}
xmldsig is a very large, very complicated spec. You can try to implement it if you like, the complicated bits are turning the XML document into bytes for doing signing and verification (the canonicalization (or c14n) spec is separate, and large).
SignedXml should be available now with .NET Core 2.0 Preview 1, and upgrading is definitely your easiest bet.

AES encrypt in .NET and decrypt with Node.js crypto?

I'm trying to encrypt some data in Mono C#, send it to a NodeJS server and decrypt it there. I'm trying to figure out what algorithms to use to match the two.
I send the encrypted string encoded with base64. So I do something like this in Javascript, where I know the key which was used to encrypt the data in my C# application:
var decipher = crypto.createDecipher('aes192',binkey, biniv);
var dec = decipher.update(crypted,'base64','utf8');
dec += decipher.final('utf8');
console.log("dec", dec);
In Mono I create my Cypher with:
using System.Security.Cryptography;
using (Aes aesAlg = Aes.Create("aes192"))
I need to pass the correct string to Aes.Create() in order to have it use the same algorithm, but I can't find what it should be. "aes192" is not correct it seems.
I don't need aes192 this was just a tryout. Suggest a different encryption flavor if it makes sense. Security is not much of an issue.
Here are links to .NET and Nodejs docs:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.aes.aspx
http://nodejs.org/api/crypto.html
This code works for my Node.js side, but please replace the static iv, otherwhise aes encryption would be useless.
var crypto = require('crypto');
function encrypt(data, key) {
key = key || new Buffer(Core.config.crypto.cryptokey, 'binary'),
cipher = crypto.createCipheriv('aes-256-cbc', key.toString('binary'), str_repeat('\0', 16));
cipher.update(data.toString(), 'utf8', 'base64');
return cipher.final('base64');
}
function decipher(data, key) {
key = key || new Buffer(Core.config.crypto.cryptokey, 'binary'),
decipher = crypto.createDecipheriv('aes-256-cbc', key.toString('binary'), str_repeat('\0', 16));
decipher.update(data, 'base64', 'utf8');
return decipher.final('utf8');
}
function str_repeat(input, multiplier) {
var y = '';
while (true) {
if (multiplier & 1) {
y += input;
}
multiplier >>= 1;
if (multiplier) {
input += input;
} else {
break;
}
}
return y;
}
I hope this helps You.
NOTE: You need to deliver an 265bit aka 32 character key for this algorithm to work.
POSSIBLE .NET SOLUTION: This may help you Example
You should simply write new AesManaged().
You don't need to call Create().
You then need to set Key and IV, then call CreateDecryptor() and put it in a CryptoStream.
It turned out to be a stupid mistake. I thought the create function in Node.js could take a variable argument count. Turns out you need to call the createDecipheriv() instead.
Just for the record, you can easily check the padding and mode by looking at those properties in the Aes object. The defaults are CBC and PKCS7. That padding is also used in nodejs crypto. So a for a 128 key size my code to decrypt a base64 encoded string would be:
var crypto = require('crypto');
var binkey = new Buffer(key, 'base64');
var biniv = new Buffer(iv, 'base64');
var decipher = crypto.createDecipheriv('aes-128-cbc', binkey, biniv);
var decrypted = decipher.update(crypted,'base64','utf8');
decrypted += decipher.final('utf8');
console.log("decrypted", decrypted);

C# HMAC to Java

I'm making the equivalent java code for the code below. But I can make something that returns the same result for encodedString. What Java class can I use for achieve the same result?
//Set the Hash method to SHA1
HMAC hash;
switch (validation)
{
case MachineKeyValidation.MD5:
hash = new HMACMD5();
break;
case MachineKeyValidation.SHA1:
default:
hash = new HMACSHA1();
break;
}
//Get the hash validation key as an array of bytes
hash.Key = HexToByte(validationKey);
//Encode the password based on the hash key and
//converts the encrypted value into a string
encodedString = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
Thanks in advance!
:)
I found a solution for the translation code.
There was two main problem. When a request a HMACSHA1 I'm not talking about a SHA1 algorithm, but a HmacSHA1. And there is a difference between the encoding from Java and C#. I was using the correct key, and the correct algorithm, but the encoding was differente.
SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
// The big problem is difference between C# and Java encoding
byte[] rawHmac = mac.doFinal(data.getBytes("UTF-16LE"));
result = new String(Base64.encode(rawHmac));
See this question about computing hash functions in Java.
And look at the javadoc for java.security.MessageDigest.getInstance(String algorithm).
Edited to add:
Try running the following app to see what providers you have registered.
import java.security.Provider;
import java.security.Security;
public class SecurityTest {
public static void main(String[] args) {
Provider[] providers = Security.getProviders();
for (Provider p : providers) {
System.out.println(p.toString());
}
}
}
You should have at least a few Sun providers listed. If not, you may need to download some security libraries.

Categories

Resources