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?
Related
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);
I consider feasibility and performance under .NET (using C#) of the verification part of a standard RSA signature scheme that is not directly supported. Towards that goals, I need the raw RSA public-key encryption function x → (x65537) mod N (where x is a byte array as wide as the public modulus N is, like 256 bytes).
On other platforms, the standard technique is to implement that function using RSA encryption with no padding (Java's Cipher with "RSA/ECB/NoPadding"). But I can't find how to perform this under .NET. What are my options?
.NET doesn't provide this functionality inbox. If you're just doing public key operations then you can use the BigInteger class without having security liabilities. (Don't use it for private key operations, because a) it'll have your private key fairly obviously in memory and b) it doesn't have a Montgomery Ladder based ModPow, so it would leak the Hamming Weight of your private key)
RSA existingKey = HoweverYouWereGettingAKey();
RSAParameters rsaParams = existingKey.ExportParameters(false);
BigInteger n = PrepareBigInteger(rsaParams.Modulus);
BigInteger e = PrepareBigInteger(rsaParams.Exponent);
BigInteger sig = PrepareBigInteger(signature);
BigInteger paddedMsgVal = BigInteger.ModPow(sig, e, n);
byte[] paddedMsg = paddedMsgVal.ToArray();
if (paddedMsg[paddedMsg.Length - 1] == 0)
{
Array.Resize(ref paddedMsg, paddedMsg.Length - 1);
}
Array.Reverse(paddedMsg);
// paddedMsg is now ready.
private static BigInteger PrepareBigInteger(byte[] unsignedBigEndian)
{
// Leave an extra 0x00 byte so that the sign bit is clear
byte[] tmp = new byte[unsignedBigEndian.Length + 1];
Buffer.BlockCopy(unsignedBigEndian, 0, tmp, 1, unsignedBigInteger.Length);
Array.Reverse(tmp);
return new BigInteger(tmp);
}
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);
}
How can I take a maximum 19-digits long BigInteger and encrypt it with the following rules:
The result must be based on digits and lower-case English letters only.
All outputs must have the same length to any input. The length must be between 11 to 16 characters, depending on your method, but should be consistent for all possible inputs.
No easy patterns. For example, if you encrypt 000...1 and 000...2 the results should look completely different.
No collisions at all
Should be able to decrypt back to the original BigInteger.
Things that I have tried
Take the original number, XOR it by some key, multiply it by a factor and convert it to a base 36 string. The purpose of the factor is to expand the range so there won't be too much 0 padding. The factor must be between 1 to 36^16/10^19. The problem with this method is that a) it's not 'secure' enough, and b) close numbers have very similar results.
This answer. However, the result was often too short or too long, and the factor method used before didn't work here.
19 digits is slightly less than 64 bits, so you can simply use a 8 byte block cipher like TDEA in ECB mode to encrypt the BigInteger values. First retrieve a default 64 bit encoding of the BigInteger, then encrypt with the secret key, and finally base 36 encode it. The result will be a few characters less than 16 characters, but you can always pad with any value.
Note that if you encrypt the same value twice that you will get the same result, so in that respect the ciphertext does leak some information about the plain text.
The technique you want is format perserving encryption. This will allow you to encrypt a 19 digit number as another 19 digit number.
Unfortunately, the efficient version of this technique is somewhat difficult to implement and in fact can be done very insecurely if you pick the wrong paramaters. There are libraries for it.
This one is open source. It is in C++ unfortunately and its not clear if it runs on windows. Voltage has a library as well, though it presumably costs money and I'm not sure what languages they support.
Here is a piece of code that seems to do it, provided you can convert the BigInteger into an ulong (9999999999999999999 is in fact an ulong). The result is always a fixed 16 characters string (hexadecimal).
byte[] key = // put your 16-bytes private key here
byte[] iv = Guid.NewGuid().ToByteArray(); // or anything that varies and you can carry
string s = EncryptUInt64(ul, key, iv); // encode
ulong dul = DecryptUInt64(s, key, iv).Value; // decode if possible
public static string EncryptUInt64(ulong ul, byte[] key, byte[] iv)
{
using (MemoryStream output = new MemoryStream())
using (var algo = TripleDES.Create())
{
algo.Padding = PaddingMode.None;
using (CryptoStream stream = new CryptoStream(output, algo.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
byte[] ulb = BitConverter.GetBytes(ul);
stream.Write(ulb, 0, ulb.Length);
}
return BitConverter.ToUInt64(output.ToArray(), 0).ToString("x16");
}
}
public static ulong? DecryptUInt64(string text, byte[] key, byte[] iv)
{
if (text == null)
return null;
ulong ul;
if (!ulong.TryParse(text, NumberStyles.HexNumber, null, out ul))
return null;
using (MemoryStream input = new MemoryStream(BitConverter.GetBytes(ul)))
using (var algo = TripleDES.Create())
{
algo.Padding = PaddingMode.None;
using (CryptoStream stream = new CryptoStream(input, algo.CreateDecryptor(key, iv), CryptoStreamMode.Read))
{
byte[] olb = new byte[8];
try
{
stream.Read(olb, 0, olb.Length);
}
catch
{
return null;
}
return BitConverter.ToUInt64(olb, 0);
}
}
}
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);