I have been tearing what is left of my hair out trying to get a trivial example of RSA data signing and verification with C# and BouncyCastle working.
RSACryptoServiceProvider.VerifyHash() always returns false on an example that works for me with Python and M2Crypto.
I have verified that the hash signatures are identical between the working example and the C# example and it is there I am stuck. I feel I am missing some vital detail.
The working Python code and non working C# code follow.
The key was generated with
openssl genrsa -out testkey.pem 1024
openssl rsa -in testkey.pem -pubout > testkey.pub
Python code (working):
private = """-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCxSHbp1ID/XHEdzVzgqYR1F/79PeMbqzuKNZChvt1gFObBhKyB
pgaHDHw3UZrO8s/hBEE/Mpe2Lh90ZAFdPIXq+HyVoznXMoJYBv0uLDApvSQbJNOd
f7T5VwmpBbkfj1NAlm39Eun9uBSokEOnB24g+bczPQPGOASrQ2Ly9afOZQIDAQAB
AoGBAIEzQIZ1OnXgVwfTLMcGg+QaUtkYizUU+9Vj6D4YrZliYjHSkS4DY2p0rOpb
7Ki5yMpCoZJ/OpWo03+tilj6zNUU6X3aHrPPSv8jcsE0sDi9zYJr/Ztk3EG23sad
bM28Bb4fV/Z0/E6FZJrmuvI2dZP/57oQSHGOhtkHFO21cW5BAkEA3l/i5Rc34YXc
WHYEsOYe0kAxS4dCmhbLWaWvsghW/TomjdxzePsO70GZZqRMdzkfA1iS1OrK+pP4
4suL2rSLrQJBAMwXFnBp4Jmek0CTSxoYf6q91eFm/IRkGLnzE1fEZ76vQOBTas8T
/mpjNQHSEywo/QB62h9A8hy7XNrfZJAMJJkCQA5TYwybKFBxDTbts3Op/4ZP+F0D
Q7klisglsmHnw6Lgoic1coLyuY2UTkucfgiYN3VBuYPZ9GWcLsZ9km7ufqkCQQCz
NVa70Qyqd+cfXfcla/u2pskHCtKTQf3AUmRavhjHBMa39CemvAy7yG9EMP4q2bcH
U9jydqnidtdbTavVHQSJAkA0zJtLzHGPtQqQaI7K6kBDXYPqloGnQxHxad0HPx2e
Vj2qv1tEsqeG6HC7aL/afXOtxcfjq4oMHbGUjDv+dGfP
-----END RSA PRIVATE KEY-----"""
public = """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxSHbp1ID/XHEdzVzgqYR1F/79
PeMbqzuKNZChvt1gFObBhKyBpgaHDHw3UZrO8s/hBEE/Mpe2Lh90ZAFdPIXq+HyV
oznXMoJYBv0uLDApvSQbJNOdf7T5VwmpBbkfj1NAlm39Eun9uBSokEOnB24g+bcz
PQPGOASrQ2Ly9afOZQIDAQAB
-----END PUBLIC KEY-----"""
message = "test input string"
import base64
# Use EVP api to sign message
from M2Crypto import EVP
key = EVP.load_key_string(private)
key.reset_context(md='sha1')
key.sign_init()
key.sign_update(message)
signature = key.sign_final()
encoded = base64.b64encode(signature)
print encoded
with open("python_sig2.txt","w") as f:
f.write(encoded)
# Use EVP api to verify signature
from M2Crypto import BIO, RSA, EVP
bio = BIO.MemoryBuffer(public)
rsa = RSA.load_pub_key_bio(bio)
pubkey = EVP.PKey()
pubkey.assign_rsa(rsa)
pubkey.reset_context(md="sha1")
pubkey.verify_init()
pubkey.verify_update(message)
decoded = base64.b64decode(encoded)
print pubkey.verify_final(decoded) == 1
C# code (verifyhash() returns false):
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
namespace RsaSignTest
{
class Program
{
private const string privateKey =
@"-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCxSHbp1ID/XHEdzVzgqYR1F/79PeMbqzuKNZChvt1gFObBhKyB
pgaHDHw3UZrO8s/hBEE/Mpe2Lh90ZAFdPIXq+HyVoznXMoJYBv0uLDApvSQbJNOd
f7T5VwmpBbkfj1NAlm39Eun9uBSokEOnB24g+bczPQPGOASrQ2Ly9afOZQIDAQAB
AoGBAIEzQIZ1OnXgVwfTLMcGg+QaUtkYizUU+9Vj6D4YrZliYjHSkS4DY2p0rOpb
7Ki5yMpCoZJ/OpWo03+tilj6zNUU6X3aHrPPSv8jcsE0sDi9zYJr/Ztk3EG23sad
bM28Bb4fV/Z0/E6FZJrmuvI2dZP/57oQSHGOhtkHFO21cW5BAkEA3l/i5Rc34YXc
WHYEsOYe0kAxS4dCmhbLWaWvsghW/TomjdxzePsO70GZZqRMdzkfA1iS1OrK+pP4
4suL2rSLrQJBAMwXFnBp4Jmek0CTSxoYf6q91eFm/IRkGLnzE1fEZ76vQOBTas8T
/mpjNQHSEywo/QB62h9A8hy7XNrfZJAMJJkCQA5TYwybKFBxDTbts3Op/4ZP+F0D
Q7klisglsmHnw6Lgoic1coLyuY2UTkucfgiYN3VBuYPZ9GWcLsZ9km7ufqkCQQCz
NVa70Qyqd+cfXfcla/u2pskHCtKTQf3AUmRavhjHBMa39CemvAy7yG9EMP4q2bcH
U9jydqnidtdbTavVHQSJAkA0zJtLzHGPtQqQaI7K6kBDXYPqloGnQxHxad0HPx2e
Vj2qv1tEsqeG6HC7aL/afXOtxcfjq4oMHbGUjDv+dGfP
-----END RSA PRIVATE KEY-----";
private const string publicKey =
@"-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxSHbp1ID/XHEdzVzgqYR1F/79
PeMbqzuKNZChvt1gFObBhKyBpgaHDHw3UZrO8s/hBEE/Mpe2Lh90ZAFdPIXq+HyV
oznXMoJYBv0uLDApvSQbJNOdf7T5VwmpBbkfj1NAlm39Eun9uBSokEOnB24g+bcz
PQPGOASrQ2Ly9afOZQIDAQAB
-----END PUBLIC KEY-----";
static void Main(string[] args)
{
var data = "test input string";
var sig = SignWithPrivateKey(data);
var valid = VerifyWithPublicKey(data,sig);
}
private static byte[] SignWithPrivateKey(string data)
{
RSACryptoServiceProvider rsa;
using (var keyreader = new StringReader(privateKey))
{
var pemreader = new PemReader(keyreader);
var y = (AsymmetricCipherKeyPair) pemreader.ReadObject();
var rsaPrivKey = (RsaPrivateCrtKeyParameters)y.Private;
rsa = (RSACryptoServiceProvider)RSACryptoServiceProvider.Create();
var rsaParameters = DotNetUtilities.ToRSAParameters(rsaPrivKey);
rsa.ImportParameters(rsaParameters);
}
// compute sha1 hash of the data
var sha = new SHA1CryptoServiceProvider();
byte[] hash = sha.ComputeHash(Encoding.ASCII.GetBytes(data));
// actually compute the signature of the SHA1 hash of the data
var sig = rsa.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
// base64 encode the signature and write to compare to the python version
String b64signature = Convert.ToBase64String(sig);
using (var sigwriter = new StreamWriter(@"C:\scratch\csharp_sig2.txt"))
{
sigwriter.Write(b64signature);
}
return sig;
}
private static bool VerifyWithPublicKey(string data,byte[] sig)
{
RSACryptoServiceProvider rsa;
using (var keyreader = new StringReader(publicKey))
{
var pemReader = new PemReader(keyreader);
var y = (RsaKeyParameters) pemReader.ReadObject();
rsa = (RSACryptoServiceProvider) RSACryptoServiceProvider.Create();
var rsaParameters = new RSAParameters();
rsaParameters.Modulus = y.Modulus.ToByteArray();
rsaParameters.Exponent = y.Exponent.ToByteArray();
rsa.ImportParameters(rsaParameters);
}
// compute sha1 hash of the data
var sha = new SHA1CryptoServiceProvider();
byte[] hash = sha.ComputeHash(Encoding.ASCII.GetBytes(data));
// This always returns false
return rsa.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"),sig);
}
}
}
At this point I don't know how to proceed and any help would be greatly appreciated.
RSA Digital Signatures To sign a message m, just apply the RSA function with the private key to produce a signature s; to verify, apply the RSA function with the public key to the signature, and check that the result equals the expected message.
RSA : It is the most popular asymmetric cryptographic algorithm. It is primarily used for encrypting message s but can also be used for performing digital signature over a message.
RSA SecurID, sometimes referred to as SecurID, is a two-factor, public-key encryption authentication technology that is used to protect network resources. Developed by RSA Data Security, SecureID is built around the difficulty of factoring very large numbers.
Something is wrong with how you're re-constructing the private/public key. Obviously in python you're note required to do that.
I created new keys that verify (in a different format) using this code:
private static void GenerateKeys(out string forPubKey, out string forPrivKey)
{
GenerateKeys(out forPubKey, out forPrivKey, 2048, 65537, 80);
}
/// <summary>
///
/// </summary>
/// <param name="forPubKey"></param>
/// <param name="forPrivKey"></param>
/// <param name="keyStrength">1024, 2048,4096</param>
/// <param name="exponent">Typically a fermat number 3, 5, 17, 257, 65537, 4294967297, 18446744073709551617,</param>
/// <param name="certaninty">Should be 80 or higher depending on Key strength number (exponent)</param>
private static void GenerateKeys(out string forPubKey, out string forPrivKey, int keyStrength, int exponent, int certaninty)
{
// Create key
RsaKeyPairGenerator generator = new RsaKeyPairGenerator();
/*
* This value should be a Fermat number. 0x10001 (F4) is current recommended value. 3 (F1) is known to be safe also.
* 3, 5, 17, 257, 65537, 4294967297, 18446744073709551617,
*
* Practically speaking, Windows does not tolerate public exponents which do not fit in a 32-bit unsigned integer. Using e=3 or e=65537 works "everywhere".
*/
BigInteger exponentBigInt = new BigInteger(exponent.ToString());
var param = new RsaKeyGenerationParameters(
exponentBigInt, // new BigInteger("10001", 16) publicExponent
new SecureRandom(), // SecureRandom.getInstance("SHA1PRNG"),//prng
keyStrength, //strength
certaninty);//certainty
generator.Init(param);
AsymmetricCipherKeyPair keyPair = generator.GenerateKeyPair();
// Save to export format
SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
byte[] ret = info.GetEncoded();
forPubKey = Convert.ToBase64String(ret);
// EncryptedPrivateKeyInfo asdf = EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo(
// DerObjectIdentifier.Ber,,,keyPair.Private);
//// demonstration: how to serialise option 1
//TextWriter textWriter = new StringWriter();
//PemWriter pemWriter = new PemWriter(textWriter);
//pemWriter.WriteObject(keyPair);
//pemWriter.Writer.Flush();
//string ret2 = textWriter.ToString();
//// demonstration: how to serialise option 1
//TextReader tr = new StringReader(ret2);
//PemReader read = new PemReader(tr);
//AsymmetricCipherKeyPair something = (AsymmetricCipherKeyPair)read.ReadObject();
//// demonstration: how to serialise option 2 (don't know how to deserailize)
//PrivateKeyInfo pKinfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
//byte[] privRet = pKinfo.GetEncoded();
//string forPrivKey2Test = Convert.ToBase64String(privRet);
PrivateKeyInfo pKinfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
byte[] privRet = pKinfo.GetEncoded();
string forPrivKey2Test = Convert.ToBase64String(privRet);
forPrivKey = forPrivKey2Test;
}
and then turned them back into RSA objects like this:
// Private key
RsaPrivateCrtKeyParameters kparam = ConvertToRSAPrivateKey(privateKey);
RSAParameters p1 = DotNetUtilities.ToRSAParameters(kparam);
rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(p1);
// Public key
RsaKeyParameters kparam = ConvertToRSAPublicKey(publicKey);
RSAParameters p1 = DotNetUtilities.ToRSAParameters(kparam);
rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(p1);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With