Use public key in RSA algorithm - c#

I have desktop C# code (console, wpf etc) which generate key from base64 string and encrypt by it.
string b64Key = "";
byte[] decoded = Convert.FromBase64String(b64Key);
int modLength = BitConverter.ToInt32(decoded.Take(4).Reverse().ToArray(), 0);
byte[] mod = decoded.Skip(4).Take(modLength).ToArray();
int expLength = BitConverter.ToInt32(decoded.Skip(modLength + 4).Take(4).Reverse().ToArray(), 0);
byte[] exponent = decoded.Skip(modLength + 8).Take(expLength).ToArray();
RSAParameters key = new RSAParameters();
key.Modulus = mod;
key.Exponent = exponent;
var provider = new RSACryptoServiceProvider();
provider.ImportParameters(key);
var encrypted = provider.Encrypt(Encoding.UTF8.GetBytes("string"), true);
I must reuse this part of code for UWP project.
I tried many ways but every time I catch an exception when I am trying to import public key:
// try to use DESKTOP key for understanding
byte[] mod = key.Modulus;
byte[] exponent = key.Exponent;
// this method concat arrays
var buf = this.Combine(mod, exponent);
// try to create key buffer from array
IBuffer keyBuffer = CryptographicBuffer.CreateFromByteArray(buf);
// try to create key buffer from base64 string
keyBuffer = CryptographicBuffer.DecodeFromBase64String("base64 string");
var provider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
var publicKey = provider.ImportPublicKey(keyBuffer, CryptographicPublicKeyBlobType.Pkcs1RsaPublicKey);
// I tried all values in 'CryptographicPublicKeyBlobType' enum
var encryptData = CryptographicEngine.Encrypt(publicKey, CryptographicBuffer.ConvertStringToBinary("string", BinaryStringEncoding.Utf8), null);
How can I import key correctly for the UWP project?
Thanks!

At the first - I have only correct modulus and all. My base64 string is not correct public key (key in this string but there are a lot of other items).
After that I found only one right way to generate public key from modulus and exponent -> combine them. But I was still had exception. After that I tried to generate default key and change modulus part in it -> and it worked!
var provider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaOaepSha1);
// create custom (random) key with size '1024'
var standardKeyPair = provider.CreateKeyPair(1024);
// export publick key to this default key
var standardBuffer = standardKeyPair.ExportPublicKey(CryptographicPublicKeyBlobType.Pkcs1RsaPublicKey);
// copy it to bytes array
byte[] standardKey;
CryptographicBuffer.CopyToByteArray(standardBuffer, out standardKey);
// change part of the key to our modules
// I DON'T KNOW WHY, but starndart key has 7 bytes in prefix and 5 in suffix (nail)
// we have 128 bytes in modulus and 140 in default key -> so we must make 140 bytes from our modulus
Array.Copy(modulus, 0, standardKey, 7, modulus.Length);

Related

RSA Cryptography with raw keys, c#

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.

RSA Extra Bit 129 instead off 128

I am using this function to change public key and encrypt data:
public byte[] EncryptData(byte[] data2Encrypt)
{
string key = "109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413";
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024);
BigInteger intk;
BigInteger.TryParse(key, out intk);
RSAParameters privateKey = new RSAParameters();
byte[] expont = { 1, 0, 1 };
byte[] modulus = intk.ToByteArray();
Logger.log(Log_Type.ERROR, "Pierwszy bit: " + modulus[0]);
privateKey.Exponent = expont;
privateKey.Modulus = intk.ToByteArray();
rsa.ImportParameters(privateKey);
return rsa.Encrypt(data2Encrypt, false);
}
But it return me array with 129 length instead od 128 (What should be max lenght using 1024 bits i think). What can be a reason?
If you use BigInteger an additional bit is always placed before the
number. If your key has 1024 bits you get 1025 bits, so skip the
first byte if it is 0x00 (meaning a positive value)
BigInteger produces signed little-endian numbers, while RSAParameters requires unsigned big-endian. You can still use BigInteger though, just convert its output to what RSAParameters is expecting.
byte[] modulus = intk.ToByteArray().Reverse().Skip(1).ToArray();
Reverse to make the number big-endian, and Skip(1) to skip the sign.
I am not sure, that it should be even converted into BitInteger. RSA key what I am trying to get is similar to this function in C++
void Crypt::rsaSetPublicKey(const std::string& n, const std::string& e)
{
BN_dec2bn(&m_rsa->n, n.c_str());
BN_dec2bn(&m_rsa->e, e.c_str());
// clear rsa cache
if(m_rsa->_method_mod_n) { BN_MONT_CTX_free(m_rsa->_method_mod_n); m_rsa->_method_mod_n = NULL; }
}
Where 'n' is this key srting and 'e' is : "65537"
If it should not be a BigInteger, then what?

Generating PBKDF2 keys in C# and NodeJS

I'm trying to encrypt a byte array in C# using AES192 and a PBKDF2 password/salt based key and decrypt the same data in NodeJS. However my key generation produces different results in both NodeJS and C#.
The C# code is as follows:
private void getKeyAndIVFromPasswordAndSalt(string password, byte[] salt, SymmetricAlgorithm symmetricAlgorithm, ref byte[] key, ref byte[] iv)
{
Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt);
key = rfc2898DeriveBytes.GetBytes(symmetricAlgorithm.KeySize / 8);
iv = rfc2898DeriveBytes.GetBytes(symmetricAlgorithm.BlockSize / 8);
}
private byte[] encrypt(byte[] unencryptedBytes, string password, int keySize)
{
RijndaelManaged aesEncryption = new RijndaelManaged();
aesEncryption.KeySize = keySize;
aesEncryption.BlockSize = 128;
byte[] key = new byte[keySize];
byte[] iv = new byte[128];
getKeyAndIVFromPasswordAndSalt(password, Encoding.ASCII.GetBytes("$391Ge3%£2gfR"), aesEncryption, ref key, ref iv);
aesEncryption.Key = key;
aesEncryption.IV = iv;
Console.WriteLine("iv: {0}", Convert.ToBase64String(aesEncryption.IV));
Console.WriteLine("key: {0}", Convert.ToBase64String(aesEncryption.Key));
ICryptoTransform crypto = aesEncryption.CreateEncryptor();
// The result of the encryption and decryption
return crypto.TransformFinalBlock(unencryptedBytes, 0, unencryptedBytes.Length);
}
The NodeJS code reads like this:
crypto.pbkdf2("Test", "$391Ge3%£2gfR", 1000, 192/8, (err, key) => {
var binkey = new Buffer(key, 'ascii');
var biniv = new Buffer("R6taODpFa1/A7WhTZVszvA==", 'base64');
var decipher = crypto.createDecipheriv('aes192', binkey, biniv);
console.log("KEY: " + binkey.toString("base64"));
var decodedLIL = decipher.update(decryptedBuffer);
console.log(decodedLIL);
return;
});
The IV is hardcoded as I can't figure out how to calculate that using pbkdf2. I've looked through the nodeJS docs for more help but I'm at a loss as to what's going on here.
Any assistance would be greatly appreciated.
One of the issues I see is the encoding of the pound sign (£). crypto.pbkdf2 encodes the password and salt to a binary array by default, where each character is truncated to the lowest 8 bits (meaning the pound sign becomes the byte 0xA3).
However, your C# code converts the salt to ASCII, where each character is truncated to the lowest 7 bits (meaning the pound sign becomes the byte 0x23). Also it uses the Rfc2898DeriveBytes constructor that takes a String for the password. Unfortunately, the documentation doesn't say what encoding is used to convert the string to bytes. Fortunately, Rfc2898DeriveBytes does have another constructor that takes a byte array for the password and also takes an iteration count parameter, here 1000.
Accordingly, you should convert the password and salt strings to byte arrays by truncating each character to 8 bits, just like Node.js does by default. Here is an example:
var bytes=new byte[password.Length];
for(var i=0;i<bytes.Length;i++){
bytes[i]=(byte)(password[i]&0xFF);
}

RSA Public exponent defaults to 65537. What should this value be? What are the impacts of my choices?

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)

Why RSA encryption can return different results with C# and Java?

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

Categories