How do I generate random RSA public and private keys (RSAParameters) using RSACryptoServiceProvider class?
Each time I create a new instance of RSACryptoServiceProvider, I end up exporting the same keys.
Thanks
I did some test on the following code, and the exported parameters are always different:
var rsaAlgo1 = new RSACryptoServiceProvider();
var rsaAlgo2 = new RSACryptoServiceProvider();
var xml1 = rsaAlgo1.ToXmlString(true);
var xml2 = rsaAlgo2.ToXmlString(true);
if (xml1 != xml2)
{
// it always goes here...
}
Using the following code you should never get all the same keys out
var rsa = new RSACryptoServiceProvider();
var rsaParams = rsa.ExportParameters(true);
However you should note that the Exponent key can be the same and if often is 65537(0x010001)
"Choose an integer e such that 1 < e < φ(n) and gcd(e, φ(n)) = 1; i.e., e and φ(n) are coprime.
e is released as the public key exponent.
e having a short bit-length and small Hamming weight results in more efficient encryption – most commonly 216 + 1 = 65,537. However, much smaller values of e (such as 3) have been shown to be less secure in some settings."
RSA wiki
Related
I am trying to implement a simple RSA signature algorithm in C#, by only using BigInteger, and no built-in crypto tools. To sign a message, I encrypt it with the private key, and to verify it, I decrypt it with the public key. In the code below, I have noticed a very strange behavior. For some input strings, (e.g. "Test"), it successfully validates the signature, but for other strings (e.g. "Test1"), it fails.
What I've noticed is, that it only fails, when the m variable (the hash of the message) is a negative number, and this causes the decrypted ver variable to become an extremely large number and the code prints 'Wrong Signature'.
// Generating RSA keys
var random = new Random();
var p = new BigInteger(256, 100, random);
var q = new BigInteger(256, 100, random);
var n = p.Multiply(q);
var phi = p.Subtract(BigInteger.One).Multiply(q.Subtract(BigInteger.One));
var e = new BigInteger("65537");
var d = e.ModInverse(phi);
// Preparing the message
var messageHash = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes("Test"));
var m = new BigInteger(messageHash);
// Signing the message
var signature = m.ModPow(d, n);
// Verifying the message
var ver = signature.ModPow(e, n);
Console.Out.WriteLine(m);
Console.Out.WriteLine(ver);
if(ver.Equals(m))
Console.Out.WriteLine("Valid Signature");
else
Console.Out.WriteLine("Wrong Signature");
I'm using BouncyCastle for the BigInteger type.
Example output for the message "Test":
16928957987407306249184430693954511195
16928957987407306249184430693954511195
Valid Signature
Example output for the message "Test1":
-40249184872970767997805928798048797124
10061048024793920770364189111057222210443355984675629704584018449091407182950576678391694061743052292040531884029654111183675927763342349593675935744009443
Wrong Signature
I believe that the error is somewhere in the signing process, and not in the verification. Maybe I shouldn't try to encrypt negative numbers? What should I do if the hash is negative? How can I fix this code so it works reliably on any input string?
#Topaco helped me solve my problem.
The RSA algorithm requires 0 <= m < n for a message m and modulus n, here . To prevent negative numbers, var m = new BigInteger(1, messageHash); could be used, which creates the corresponding unsigned value instead of the signed value
I want to implement an ElGamal encryption from this link https://csharp.hotexamples.com/examples/Portable.Licensing.Security.Cryptography.ElGamal/ElGamalKeyStruct/-/php-elgamalkeystruct-class-examples.html
This is what I have tried so far and have no luck
string lic = "abcdefghijklmnopqrstuvwxyz";
byte[] licenseKey = Encoding.UTF8.GetBytes(lic);
ElGamalKeyStruct key = new ElGamalKeyStruct();
key.P = new BigInteger(123567890);
key.G = new BigInteger(1234567890);
key.Y = new BigInteger(1234567890);
key.X = new BigInteger(0); //zero
byte[] signature = CreateSignature(licenseKey, key);
bool verified = VerifySignature(licenseKey, signature, key);
The VerifySignature always returns false.
P must be prime. See here (that is the ebook from which the code you use was taken):
Create a random prime number, p. This number is the ElGamal "modulus." The number of bits required to represent p is the size of the public key, so that if p is represented using 1024 bits, then the key created by following the protocol is a 1024-bit key. We will select 607 as our example value.
If you look in the text then there is code to generate El Gamal keys (the CreateKeyPair() method)
I have been given a 256 byte modulus ('n'), a 256 byte private exponent ('d') and a 3 byte {1,0,1} public exponent ('e'). I am trying to sign 32 bytes of data.
I have tried to create a new RSAParameters.
byte[] n = new byte[256]; //populated in my code
byte[] d = new byte[256]; //populated in my code
byte[] e = new byte[]{1,0,1};
byte[] junkData = new byte[32]; //populated in my code
RSAParameters rsaParam = new RSAParameters();
rsaParam.Modulus = n;
rsaParam.Exponent = e;
rsaParam.D = d;
I then create a RSACrytoServiceProvider, import the parameters into it, and try to sign data.
var csp = new RSACryptoServiceProvider(2048);
csp.ImportParameters(rsaParam);
csp.SignData(junkData, new SHA1CryptoServiceProvider());
The problem is that the RSACryptoServiceProvider appears to be public only, and when I try to sign I receive a 'Keyset does not exist' Cryptography Exception.
Do I also need the P and Q elements to properly sign the data, or am I doing something that is obviously wrong? Thanks for the help!
I'm surprised that the ImportParameters didn't throw (are you using Mono?).
RSACryptoServiceProvider (and all of the RSA implementations in .NET Framework and .NET Core) require a fully populated RSAParameters structure for private keys.
https://stackoverflow.com/a/42117655/6535399 gives the answer in a bit more detail.
I'm creating unit tests for software that may encounter different exponent sizes. (see section 3.3.1 of this RFC)
How can I use Bouncy Castle, or any other C# library to generate a RSA key pair that doesn't have a key size of 65537.
If the answer is that I can directly modify this, as long as I update the private key as well, what specific changes (or re-computation) should I make for the public and private key?
Here is the sample code that I'm using to create the key with the exponent of 65537:
// Create key
RsaKeyPairGenerator generator = new RsaKeyPairGenerator();
var param = new KeyGenerationParameters(new SecureRandom(), 1024);
generator.Init(param);
AsymmetricCipherKeyPair keyPair= generator.GenerateKeyPair();
// Save to export format
SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
byte[] ret = info.GetEncoded();
string ovalue1 = Convert.ToBase64String(ret);
// Read from export format
byte[] publicKeyBytes = Convert.FromBase64String(ovalue1);
AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(publicKeyBytes);
RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter;
RSAParameters rsaParameters = new RSAParameters();
rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArray();
rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArray();
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
Thanks to #BrettHale I was able to solve the issue.
This is how to create a key pair in Bouncy Castle
// 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);
Additional links that relate to his recommendation to use RSAKeyGenerationParameters include:
Why is exponent value 65537 used, what are the alternatives and impacts?
What is certainty, and what is the correct value for this (hint: it depends on key length)
I using:
c#: RSACryptoServiceProvider
JAVA: KeyFactory.getInstance("RSA")+Cipher
I sending public key (exponent + modulus) as byte array from java to c#. It's ok, there is the same bytes. But when i try to encrypt some data with one key in Java and c# - there is different results.
Java Key Generation:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize( Config.CRYPTO_KEY_NUM_BITS );
m_KeyPair = keyGen.genKeyPair();
m_PublicKey = KeyFactory.getInstance("RSA").generatePublic(
newX509EncodedKeySpec(m_KeyPair.getPublic().getEncoded()));
byte[] exponent = m_PublicKey.getPublicExponent().toByteArray();
byte[] modulus = m_PublicKey.getModulus().toByteArray(); // then sending...
C# Key Recieve:
// Recieved...
m_ExternKey = new RSAParameters();
m_ExternKey.Exponent = exponent;
m_ExternKey.Modulus = modulus;
m_RsaExtern = new RSACryptoServiceProvider();
m_RsaExtern.ImportParameters(m_ExternKey);
byte[] test = m_RsaExtern.Encrypt(bytesToEncrypt, true);
and problem is that encrypted bytes is different.
Thank you.
RSA encryption is randomized. For a given public key and a given message, each attempt at encryption yields a distinct sequence of bytes. This is normal and expected; random bytes are injected as part of the padding phase, and not injecting random bytes would result in a weak encryption system. During decryption, the padding bytes are located and removed, and the original message is recovered unscathed.
Hence it is expected that you will get distinct encrypted messages with Java and C#, but also if you run your Java or C# code twice.
RSA Encription mustn't return diffferent values with simular keys - its standardized algorithm. Check your keys.
RSA Parameters contains more parameters than modulus and exponent if i remember correctly. You need fully initialized rsa parameters to get the encryption correct (in .net).
Moreover, your private and private key is not even set in .net
i hope this is helpful , in C# lough code
byte[] rsaExp = rsaParameters.Exponent.ToByteArray();
byte[] Modulus = rsaParameters.Modulus.ToByteArray();
// Microsoft RSAParameters modulo wants leading zero's removed so create new array with leading zero's removed
int Pos = 0;
for (int i = 0; i < Modulus.Length; i++)
{
if (Modulus[i] == 0)
{
Pos++;
}
else
{
break;
}
}
byte[] rsaMod = new byte[Modulus.Length - Pos];
Array.Copy(Modulus, Pos, rsaMod, 0, Modulus.Length - Pos);