C# Sign Data with SHA-256withECDSA using BouncyCastle produce different Signature everytime - c#

I am following this tutorial to sign data with SHA-256withECDSA.
Using the same private key and message, it generated different signature result every time. The only change I made is return Base64 encoded string instead of Base58
Is it supposed to be the same signature every time?
public static string GetSignature(string privateKey, string message)
{
var curve = SecNamedCurves.GetByName("secp256k1");
var domain = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);
var keyParameters = new
BC.Crypto.Parameters.ECPrivateKeyParameters(new BC.Math.BigInteger(privateKey),
domain);
ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA");
signer.Init(true, keyParameters);
signer.BlockUpdate(Encoding.ASCII.GetBytes(message), 0, message.Length);
var signature = signer.GenerateSignature();
return Convert.ToBase64String(signature);
}
Example result
MEQCIB0rqb8Dbrh+e2akoCVJaUS4tyJYqfRf8vdz/W2fUOomAiB3D2BaMYjwSgKRQyTd/W+YEn+wT0I4dq1hmgBfe/Sh7g==
MEUCIQDsWxG8Zr7MCemgGylAN+Y32qJYuDmqZMpaPwxTKosJ3AIgE3oSsBjcua/aCvfNXiMfcUM9U92p9aRlAIEopw/wvd0=
MEYCIQCjQ0EDHVFhASuUSPnCGjCb0O1sq3Op+aAl01afjIVviQIhAOnGyGN9cKswFn97de0o/Im9Hswo6AdnLhKIZSUcYbDY
MEYCIQCAqcHyhRcbLtuyimJ4XCHvJcz0p0Wd7FgJ1+07sOsC/gIhAKYwlhRv98C/3XeZE1TujkB9qMn2C99GaguJoWng9+2y
MEUCIQD7ObA0n0JpRNQDe+3udpeKGEk79KsrjHsjv/4Wlj2bigIgZERRSQBEN91HTJHqn+prlwSCKUT4AJx061Gi0tv8Xuw=

Related

Recreating Keys (ECPublicKeyParameters) in C# with BouncyCastle

I generate an AsymmetricCipherKeyPair as follows:
string curveName = "P-521";
X9ECParameters ecP = NistNamedCurves.GetByName(curveName);
ECDomainParameters ecSpec = new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed());
IAsymmetricCipherKeyPairGenerator g = GeneratorUtilities.GetKeyPairGenerator("ECDH");
g.Init(new ECKeyGenerationParameters(ecSpec, new SecureRandom()));
AsymmetricCipherKeyPair aKeyPair = g.GenerateKeyPair();
My intention was to extract the public and private keys and then rebuild the keys later. I first extracted the keys as follows:
byte[] privateKey = ((ECPrivateKeyParameters)aKeyPair.Private).D.ToByteArray();
byte[] publicKey = ((ECPublicKeyParameters)aKeyPair.Public).Q.GetEncoded();
How do I recreate the public and private key parameters so that I can use them? In this example, I do recreate the private key and then sign the data byte array.
public static byte[] SignData(byte[] data, byte[] 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(data, 0, data.Length);
return signer.GenerateSignature();
}
Although it feels like a real hack, it works just fine. How can I do this with the Public Key? I set the variable xxx to (ECPublicKeyParameters)aKeyPair.Public and I can use the code below to verify the signature. Note that I could use xxx directly, but the point is to serialize xxx out and then back in, so, this code actually does convert the xxx variable and creates a new one, which is stored in xx. I then use xx to verify (which shows that I can round trip the key).
var xx = PublicKeyFactory.CreateKey(Org.BouncyCastle.X509.SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(xxx).GetDerEncoded());
ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA");
signer.Init(false, xx);
signer.BlockUpdate(data, 0, data.Length);
return signer.VerifySignature(signature);
I had hoped that I could create the key (xx) from Q.GetEncoded() similar to how I did it for the private key.
Is there a better way to rebuild the private key? also using an ASN.1 encoding? If so, perhaps I should use that instead.
I can do this as follows:
string curveName = "P-521";
X9ECParameters ecP = NistNamedCurves.GetByName(curveName);
FpCurve c = (FpCurve)ecP.Curve;
ECFieldElement x = new FpFieldElement(c.Q, xxx.Q.X.ToBigInteger());
ECFieldElement y = new FpFieldElement(c.Q, xxx.Q.Y.ToBigInteger());
ECPoint q = new FpPoint(c, x, y);
ECPublicKeyParameters xxpk = new ECPublicKeyParameters("ECDH", q, SecObjectIdentifiers.SecP521r1);
Then, I can use xxpk to verify the signature.
Disclaimer: I do not claim that this is the best way to do this, just that it works!

rsacryptoserviceprovider.VerifyData always returns false

Below is my C# Program that verifies a response from a php script which is using phpseclib
static void Main(string[] args)
{
var payment =
"VUQxMzE1MTg0OTk0MDM2MzIyMDJ8VDAwMDAxN0kxMFVEMTMxNTE4NDk5NDAzNjMyMjAyfDIwMTctMTAtMDd8MHxwYXltZW50IHN1Y2Nlc3NmdWx8MjAyNTQ=";
var signature =
"V0T9ZedZW8oB9uy4PazRIxWHvJ7rR+FVtnGjUy30mSKqgmEceZWE1aBvkQWeG4ERjAXHjsRge0D0MlHd9zvXjrLog+G5nWBHIu52O0srCd9d71JVztMQy8fV5oSnRPtlUpgdmn8QDnJ27XrbaHzNxnFyybTQhmbfxkT0oJ0MEOk=";
var sigByte = Convert.FromBase64String(signature);
var payBite = Convert.FromBase64String(payment);
Verify(payBite, sigByte);
}
public static bool Verify(byte[] payment, byte[] signature)
{
var key = Resources.PublicKey;
var cipher = Crypto.DecodeX509PublicKey(key);
var res = cipher.VerifyData(payment, "SHA256", signature);
return res;
}
the public key used is below:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDSiXzUuH9ePZgSLYrzZ0qhta25
HCb+WG48wIKUl+cQNC/Fl/KZG2cSwRXdo8KZLVWWO5qwzplfTWEylg4IqRA48rYY
f/b+Y7QhORKeAws4pttLZJBbh1mIbZ9HXfQ+zBjP+zfJZ1YjSFs2uZdwSt1itUcJ
/GQFct8GoUevNELG7wIDAQAB
-----END PUBLIC KEY-----
but the verify method seems to be returning false all the time. any idea why this happens.
the same content works in the php code which the vendor gave to me
<?php
//load RSA library
include 'Crypt/RSA.php';
//initialize RSA
$rsa = new Crypt_RSA();
//decode & get POST parameters
$payment = base64_decode("VUQxMzE1MTg0OTk0MDM2MzIyMDJ8VDAwMDAxN0kxMFVEMTMxNTE4NDk5NDAzNjMyMjAyfDIwMTctMTAtMDd8MHxwYXltZW50IHN1Y2Nlc3NmdWx8MjAyNTQ=");
$signature = base64_decode("V0T9ZedZW8oB9uy4PazRIxWHvJ7rR+FVtnGjUy30mSKqgmEceZWE1aBvkQWeG4ERjAXHjsRge0D0MlHd9zvXjrLog+G5nWBHIu52O0srCd9d71JVztMQy8fV5oSnRPtlUpgdmn8QDnJ27XrbaHzNxnFyybTQhmbfxkT0oJ0MEOk=");
//load public key for signature matching
$publickey = "-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDSiXzUuH9ePZgSLYrzZ0qhta25
HCb+WG48wIKUl+cQNC/Fl/KZG2cSwRXdo8KZLVWWO5qwzplfTWEylg4IqRA48rYY
f/b+Y7QhORKeAws4pttLZJBbh1mIbZ9HXfQ+zBjP+zfJZ1YjSFs2uZdwSt1itUcJ
/GQFct8GoUevNELG7wIDAQAB
-----END PUBLIC KEY-----";
$rsa->loadKey($publickey);
//verify signature
$signature_status = $rsa->verify($payment, $signature);
//get payment response in segments
//payment format: order_id|order_refference_number|date_time_transaction|payment_gateway_used|status_code|comment;
$responseVariables = explode('|', $payment);
//display values
echo $signature_status;
echo '<br/>';
var_dump($responseVariables);
?>
Any idea what i'm doing wrong here. i tried passing "SHA512", "MD5" all in the C# code and still returns false.
Well, seems like the vendor is NOT using PKCS1, he's using PSS. Verify it this way (requires Bouncy Castle!):
public static bool Verify(byte[] payment, byte[] signature)
{
var pub = #"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDSiXzUuH9ePZgSLYrzZ0qhta25HCb+WG48wIKUl+cQNC/Fl/KZG2cSwRXdo8KZLVWWO5qwzplfTWEylg4IqRA48rYYf/b+Y7QhORKeAws4pttLZJBbh1mIbZ9HXfQ+zBjP+zfJZ1YjSFs2uZdwSt1itUcJ/GQFct8GoUevNELG7wIDAQAB";
byte[] raw = Convert.FromBase64String(pub);
AsymmetricKeyParameter aKey = PublicKeyFactory.CreateKey(raw);
RsaKeyParameters rKey = (RsaKeyParameters)aKey;
PssSigner pss = new PssSigner(new RsaEngine(), new Sha1Digest(), 20);
pss.Init(false, rKey);
pss.BlockUpdate(payment, 0, payment.Length);
var res = pss.VerifySignature(signature);
return res;
}
PSS is supported in-the-box with .NET 4.6+, but requires using the RSACng class (CAPI, which RSACryptoServiceProvider is based on, doesn't offer it).
public static bool Verify(byte[] payment, byte[] signature)
{
var key = Resources.PublicKey;
// Change the function this calls to return RSACng instead of RSACryptoServiceProvider.
RSA cipher = Crypto.DecodeX509PublicKey(key);
// or, failing being able to change it:
RSA tmp = new RSACng();
tmp.ImportParameters(cipher.ExportParameters(false));
cipher = tmp;
return cipher.VerifyData(
payment,
signature,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pss);
}

SAML Redirect sign or verify failing to produce correct signature

I am using the following code to successfully produce a signature for my SAML. I have the POST sorted using XML sign, however REDIRECT is completely different. I am unable to produce the same signature as https://www.samltool.com/sign_logout_req.php and when I try and validate a genuine signature it is failing.
I took it back to basics and tried to see if I could sign it in the same manner and I cannot which suggests something is wrong with the way I am forming the data.
The following details (into samltool.com):
<saml:LogoutRequest ID="_02380F63816E0E92D6537758C37FE05F" Version="2.0" IssueInstant="2017-06-21T15:34:59.911Z" Destination="https://myteststs.net/appname/auth/" xmlns:saml="urn:oasis:names:tc:SAML:2.0:protocol"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://w.sp-app.com</saml:Issuer><saml:NameID xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">1869374313</saml:NameID></saml:LogoutRequest>
Private key (testcert)
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAL13oqZ+pNa/fYIc+GYBrENOQ9rKWMeQEL9iDJyj7DFrQA40jOCY1UiOT2uLYvIwfqTHMuPmmlOLSyqCumZbKYP6KIM0pe+vJcJO6Nxg81gmN3jx3GbnDsmhi54oAmj3JC/Z/WbliqUXjlIAUlzLmMll7/vy2V5ec/gxHBpuRWBjAgMBAAECgYAWiWn/+vV5k9qGhRKo8479jIw2tLX9uu27Dhso8LiyIitC5U5Skutfz4mz5acV3t3ZlNZBVJdL07hTrKwma7aSx1r6UwTtW002ZZzytEVn7G7ytOIXkT+q/TuooCR8aa88vwhUFPqCSOuZgOPH9ytqAkzDCaNgVKdhQgRgjxfOBQJBAOSu4t5AgFJUBOcYOEOm6+v8R1CqedfyOgya3g1gsA4VnuG+ms233ZxWSPkiMnoUpEh8gBnZyk6ZZSlk668rwBcCQQDUGYg7wVLqhPAyjfM74oaJgohyQfQK6rPnzlKoGbdDR0QRN545ATBsETi2GSIYAHgkgLDJw3/lw1wX1dXzFuWVAkABmR9IwlajPKcUHl02S9JWQdsVuztCwRSaxfJLUaOpVYlYtoZKbcCEuS2lYBHOPJqxTv1uMNFzHytP0L686KddAkART6gr4GKJG6KTLbzNhXafoJTMZo+pmHBomhFrAPZROm7WzOhQFMXD/D/ZtQFwXhFwQUSsoxU8Ro6sr1pQBe1lAkBlXndo3Bm6AITDDsJZYg10XiBMNj4743t0pV6jayf9UTRZHu2GI9AWoU3/FTQt34zbPz6TjlNuJnwMHwfCFk1F
x.509
MIICMTCCAZqgAwIBAgIQcuFBQn5d27JBvbkCO+utKjANBgkqhkiG9w0BAQUFADBXMVUwUwYDVQQDHkwAewAyAEYAOAA3ADkANQA4ADUALQA3AEMANQA0AC0ANAA1ADAARAAtADgAOABGAEIALQBBADMARgA3ADEAMwA2ADQANgBFAEMANgB9MB4XDTE2MDEwODExMTU0OFoXDTE3MDEwNzE3MTU0OFowVzFVMFMGA1UEAx5MAHsAMgBGADgANwA5ADUAOAA1AC0ANwBDADUANAAtADQANQAwAEQALQA4ADgARgBCAC0AQQAzAEYANwAxADMANgA0ADYARQBDADYAfTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvXeipn6k1r99ghz4ZgGsQ05D2spYx5AQv2IMnKPsMWtADjSM4JjVSI5Pa4ti8jB+pMcy4+aaU4tLKoK6Zlspg/oogzSl768lwk7o3GDzWCY3ePHcZucOyaGLnigCaPckL9n9ZuWKpReOUgBSXMuYyWXv+/LZXl5z+DEcGm5FYGMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBDXaccjXBrBhxp1fcEDm7MotKvgh8DxQAACk/Uxb4r2R6+LcePUxQTcxWVmyCQO0NR017FRf/fLFHmM9HZI3lwx5ka4xBnSOu8mejQ0KOYt4yf2VQG6pWGa046Ntip+KB/yDQKXQ3RHprsshe33MFlEWpDJyo6jyDpDUqLjPBvtg==
RelayState:
RELAYTEST
SigAlg:
#rsa-sha1
So... using the ssotool it produces the signature :
IG4VDmVwQRZWa75NmwjtqKlPVdCx6tm73gL7j3xvrqXsfirunUtr626SBmQJ4mke77bYzXg8D1hAy5EREOhz2QH23j47XexqbVSNTtAkZV7KP1/lO8K01tiQr8SGJqzdFor/FZZscIDFlw3cBLXhGSwWK9i0qO/e55qkgxJS9OA=
However.. using the code below (and many.. many.. variations) I cannot get it to produce that same signature. Please note the samlrequest is base64encoded with compression as recommended (but you can deflate it to the same output). I have followed this specification too (3.4.4.1):https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf
static byte[] Sign(string data, string certSubject)
{
// Access Personal (MY) certificate store of current user
X509Store my = new X509Store(StoreName.My, StoreLocation.LocalMachine);
my.Open(OpenFlags.ReadOnly);
// Find the certificate we’ll use to sign
RSACryptoServiceProvider csp = null;
foreach (X509Certificate2 cert in my.Certificates)
{
if (cert.Subject.Contains(certSubject))
{
// Get its associated CSP and private key
csp = (RSACryptoServiceProvider)cert.PrivateKey;
}
}
if (csp == null)
{
throw new Exception("No valid cert was found");
}
string certAlgorithm = csp.SignatureAlgorithm;
// Hash the data
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] dataRaw = encoding.GetBytes(data);
byte[] hash = sha1.ComputeHash(dataRaw);
// Sign the hash
return csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
}
static bool Verify(string text, byte[] signature, string certPublic)
{
// Load the certificate we’ll use to verify the signature from a file
X509Certificate2 appSigningX509Certificate = null;
var appSigningCertificateBytes = Convert.FromBase64String(certPublic);
appSigningX509Certificate = new X509Certificate2(appSigningCertificateBytes);
// Get its associated CSP and public key
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)appSigningX509Certificate.PublicKey.Key;
// Hash the data
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
// Verify the signature with the hash
return csp.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), signature);
}
static void Main(string[] args)
{
// Usage sample
try
{
string cert = "MIICMTCCAZqgAwIBAgIQcuFBQn5d27JBvbkCO+utKjANBgkqhkiG9w0BAQUFADBXMVUwUwYDVQQDHkwAewAyAEYAOAA3ADkANQA4ADUALQA3AEMANQA0AC0ANAA1ADAARAAtADgAOABGAEIALQBBADMARgA3ADEAMwA2ADQANgBFAEMANgB9MB4XDTE2MDEwODExMTU0OFoXDTE3MDEwNzE3MTU0OFowVzFVMFMGA1UEAx5MAHsAMgBGADgANwA5ADUAOAA1AC0ANwBDADUANAAtADQANQAwAEQALQA4ADgARgBCAC0AQQAzAEYANwAxADMANgA0ADYARQBDADYAfTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvXeipn6k1r99ghz4ZgGsQ05D2spYx5AQv2IMnKPsMWtADjSM4JjVSI5Pa4ti8jB+pMcy4+aaU4tLKoK6Zlspg/oogzSl768lwk7o3GDzWCY3ePHcZucOyaGLnigCaPckL9n9ZuWKpReOUgBSXMuYyWXv+/LZXl5z+DEcGm5FYGMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBDXaccjXBrBhxp1fcEDm7MotKvgh8DxQAACk/Uxb4r2R6+LcePUxQTcxWVmyCQO0NR017FRf/fLFHmM9HZI3lwx5ka4xBnSOu8mejQ0KOYt4yf2VQG6pWGa046Ntip+KB/yDQKXQ3RHprsshe33MFlEWpDJyo6jyDpDUqLjPBvtg==";
string samlRequestCompressed = "nZFPS8QwEMW/Ssl906TZ/gttQWwLhdWDyh68SKjBLbRJ7ExQv71traA38TiPee/9hilATaM82Rfr8U6/eg0YdHVJnlgkMtYmIuNJw5o8qpNYpGmcXYu0bVjckuCsZxisKUlEGQk6AK87A6gMLhLj6YElh4g/8FiKo4xzmnP+SIJ6aRiMws15QXQgwxC1Uab/MGrSdFBTP1r/TI3GUDm3iqE7KI+XkATv02hArtAl8bORVsEAct0Bib28v7o5yYVHutmi7e1IqmK7cMOb/2xXAHpeGUn1zfhGYaFwjvZ2KsIfoXvD7RLS1f9p4FmSi/QouNhzv6Kqffr1nOoT";
string relaystate = "RELAYTEST";
string algorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
string data = String.Empty;
if (String.IsNullOrEmpty(relaystate))
{
data = String.Format("SAMLRequest={0}&SigAlg={1}", HttpUtility.UrlEncode(samlRequestCompressed), HttpUtility.UrlEncode(algorithm));
}
else
{
data = String.Format("SAMLRequest={0}&RelayState={1}&SigAlg={2}", HttpUtility.UrlEncode(samlRequestCompressed,Encoding.UTF8), HttpUtility.UrlEncode(relaystate,Encoding.UTF8), HttpUtility.UrlEncode(algorithm,Encoding.UTF8));
}
// Sign text
byte[] signature = Sign(data, "{2F879585-7C54-450D-88FB-A3F713646EC6}");
string b64encodedSig = Convert.ToBase64String(signature);
string expectedSig = "IG4VDmVwQRZWa75NmwjtqKlPVdCx6tm73gL7j3xvrqXsfirunUtr626SBmQJ4mke77bYzXg8D1hAy5EREOhz2QH23j47XexqbVSNTtAkZV7KP1/lO8K01tiQr8SGJqzdFor/FZZscIDFlw3cBLXhGSwWK9i0qO/e55qkgxJS9OA=";
if (b64encodedSig != expectedSig)
{
Console.WriteLine("Not what i expected");
Environment.Exit(0);
}
// Verify signature. Testcert.cer corresponds to “cn=my cert subject”
if (Verify(data, signature, cert))
{
Console.WriteLine("Signature verified");
}
else
{
Console.WriteLine("ERROR: Signature not valid!");
}
}
catch (Exception ex)
{
Console.WriteLine("EXCEPTION: " +ex.Message);
}
Console.ReadKey();
}
I cannot seem to understand how to produce the sign data in the same way. I have also confirmed the locally installed certificate is exactly the same as the one above.
I have resolved this now.
The solution was that the HttpUtility.URLEncode is not encoding it to the same standard as the SAML standards (or OneLogin). I figured it out by looking at the compressed data and that matched but the URL Encoded one different.
The answer was to use Uri.EscapeString.

Different values on encryption using the same container

I have 2 Projects in the same solution, in one project I encrypt a message using SignData and in the other project I am recreating the message that was encrypted in the first project and I compare the results to see if they are the same. But the encryption from the first project doesn't return the same value like the encryption from the second project. How can I make so that if i give the same byte array in both the projects I get the same value returned. I think that RSA generates 2 different keys but I use the same container
param.KeyContainerName = "SignatureContainer101";
Is there any way that I can do this without passing any Key? I need it to run locally on the same machine. Or any other suggestions in what I can do to get the same result?
This is the code I am using:
public static byte[] SignData(byte[] message)
{
//convert data to be signed in a byte array
byte[] byteSignature2;
CspParameters param = new CspParameters();
//Giving container a name, so we can use the same keys for verifying data.
param.KeyContainerName = "SignatureContainer101";
//Initializing RSACryptoServiceProvider
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(param))
{
//We want RSACryptoServiceProvider to save keys in keystore
//because we want to keep signature generation routine seperate
//from verifying data against signature.
rsa.PersistKeyInCsp = true;
byte[] byteSignature = rsa.SignData(message, "SHA256");
byteSignature2 = new byte[byteSignature.Length];
byteSignature2 = byteSignature;
}
return byteSignature2;
}
public static bool VerifyData(byte[] originalMessage, byte[] signedMessage)
{
bool isSuccess = false;
CspParameters param = new CspParameters();
//Utilizing the same key container which was created during
//signature generation process i.e. in GetDataSignature(string data) function.
//If we don't utilize the same keycontainer, VerifyData will always return false
//because RSACryptoSeriveProvider initialization will generate new keys.
param.KeyContainerName = "SignatureContainer101";
//Initializing RSACryptoServiceProvider
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(param))
{
//verifying data against signature
isSuccess = rsa.VerifyData(originalMessage, "SHA256", signedMessage);
}
return isSuccess;
}
}
I am not sure what is the problem, exactly.
How can I make so that if i give the same byte array in both the projects I get the same value returned.
Do you expect SignData to return exactly the same bytes when called twice on an identical message with an identical key? It won’t and it shouldn’t. Every time an “identical” asymmetric (public-key) encryption/signing is done, it should have a different result. That is achieved using a random piece of padding used in the process. It is needed because in public-key cryptography, one of the keys is public, and if no randomness was used, it would make the cryptographic properties weaker. (For more information, see e.g. this answer on Security SE.)
So, you should not compare the result byte-by-byte, you should use the VerifyData method to check whether the signature is correct, not whether it is identical.
I found no problem in you code. It works! This is what I've done:
public partial class Program
{
protected override void Execute()
{
var message1 = Encoding.UTF8.GetBytes("Hello there,");
var message2 = Encoding.UTF8.GetBytes("Hello there");
var signed = SignData(message1);
var notOK = VerifyData(message2, signed);
var oK = VerifyData(message1, signed);
}
public static byte[] SignData(byte[] message)
{
byte[] byteSignature2;
var param = new CspParameters
{
KeyContainerName = "SignatureContainer101"
};
using (var rsa = new RSACryptoServiceProvider(param))
{
rsa.PersistKeyInCsp = true;
var byteSignature = rsa.SignData(message, "SHA256");
byteSignature2 = byteSignature;
}
return byteSignature2;
}
public static bool VerifyData(byte[] originalMessage, byte[] signedMessage)
{
bool isSuccess;
var param = new CspParameters
{
KeyContainerName = "SignatureContainer101"
};
using (var rsa = new RSACryptoServiceProvider(param))
isSuccess = rsa.VerifyData(originalMessage, "SHA256", signedMessage);
return isSuccess;
}
}

phpseclib sign and verify in C#

I need to sign the string with private Key using RSA phpseclib and then verify it in C# . I have seen many examples of how to encrypt in C# and decrypt in php, but none of how to sign string in php and verify in .NET.
here is php code:
include('Crypt/RSA.php');
$info = "Something";
$PrivateKey= "<RSAKeyValue><Modulus>3C5QWo4H+............"; //long string
$unsignedString = base64_encode($info);
$signedString = HashAndSignBytes($info, $PrivateKey);
file_put_contents('file.txt', $unsignedString."\n".$signedString);
function HashAndSignBytes($stringToSign, $Key) {
$rsa = new Crypt_RSA();
$rsa->loadKey($Key); // private key
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$signature = $rsa->sign($stringToSign);
return base64_encode($signature);
}
and here is my attempt to read the file and verify it in C#:
const string publicKey = #"<RSAKeyValue><Modulus>3C5QWo4H.....";
TextReader reader = new StreamReader(path, Encoding.ASCII);
var unsignedString = reader.ReadLine();
var signedString = reader.ReadLine();
reader.Close();
if (VerifySignedHash(unsignedString,signedString, publicKey)) {
//some code
}
private bool VerifySignedHash(string stringToVerify, string signedString, string publicKey)
{
var byteConverter = new ASCIIEncoding();
var dataToVerify = Convert.FromBase64String(stringToVerify);
var signedData = Convert.FromBase64String(signedString);
try
{
// Create a new instance of RSACryptoServiceProvider using the
// key from RSAParameters.
var rsaAlg = new RSACryptoServiceProvider();
rsaAlg.FromXmlString(publicKey);
// Verify the data using the signature. Pass a new instance of SHA1CryptoServiceProvider
// to specify the use of SHA1 for hashing.
return rsaAlg.VerifyData(dataToVerify, new SHA1CryptoServiceProvider(), signedData);
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return false;
}
}
verfication fails...
In your "signing" code, you base64encode the original string, then write that string to the output file. However, on the C# side, you read that value into unsignedString, but never reverse the base64 encoding.
The end result is that you're trying to verify the bytes of the base64Encoded string, not the data itself, so the VerifyData step fails.
Think that's your problem.
Modifying the following line might solve the problem:
var dataToVerify = Convert.FromBase64String(stringToVerify);

Resources