I use an Openssl library to provide RSA encryption-decryption for my app. And this is what I found:
int RSA_private_decrypt(int flen, unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
RSA_private_decrypt() decrypts the flen bytes at from using the private key rsa and stores the plaintext in to. (From the docs, https://www.openssl.org/docs/man1.1.1/man3/RSA_private_decrypt.html)
To setup RSA structure I use the following method:
int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d);
The n, e and d parameter values can be set by calling RSA_set0_key() and passing the new values for n, e and d as parameters to the function. The values n and e must be non-NULL the first time this function is called on a given RSA object. The value d may be NULL. (From the docs, https://www.openssl.org/docs/man1.1.1/man3/RSA_set0_key.html)
So, when I use the stuff above for decryption as described, everything works fine. I set RSA struct with n, e and d, then pass RSA struct to private_decrypt() with other parameters and get correct result.
But if I do not set e to RSA struct (or set it with random value to avoid not-null constraint), private_decrypt() returns wrong decryption result (all zeroes typically).
So, why does e required for private decryption? As far as RSA algorithm works, it should be enough to have n, d and cipher_text to provide correct result, isn't it?
Related
I'm working on a project to decrypt an AES-128 encrypted string in ColdFusion that is passed as a URL parameter.
The vendor takes a pass phrase and converts it to a valid AES-128 key "using an algorithm equivalent to Microsoft's CryptDeriveKey using the SHA-1 hash function." I need to replicate this generatedKey in ColdFusion so I can use the value in my decrypt() call.
When using CryptDeriveKey you pass the encryption type, the Hash type, the block length and a 0 iv array and it returns the Hash. Source: Generating a Key from a Password
// generate an RC2 key
byte[] iv = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
byte[] key = cdk.CryptDeriveKey(“RC2”, “SHA1”, 128, iv);
In the vendor's test tool, the pass phrase "test1234" results in a Hash of:
A6455C7A24BC5E869B0DDF647238F5DA
I found the genAESKeyFromPW() UDF, which seems to be the closest, but requires a salt which CryptDeriveKey does not use. I have also tried the code below. However, it is not working as the Hash() is not creating a valid AES-128 key:
<cfset generatedKey = Hash('test1234', 'SHA-1')>
<cfset decrypted=decrypt(encryptedString, generatedKey, 'AES/CBC/PKCS7Padding', 'Base64', '0')>
What steps do I need to replicate CryptDeriveKey function?
Update:
The vendor provided this C# example of the decryption:
public static byte[] AesDecryptBytes(byte[] cipherText, byte[] key)
{
byte[] IV = new byte[16];
AesManaged aes = new AesManaged();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
ICryptoTransform decryptor = aes.CreateDecryptor(key, IV);
byte[] plainBytes;
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(cipherText, 0, cipherText.Length);
}
plainBytes = memoryStream.ToArray();
}
return plainBytes;
}
From what I have read in this documentation, it sounds like the function essentially hashes the password binary then does an XOR with two arrays. I am not 100%, but I do not think the method they described is the same as PBKDF1 or PBKDF2.
Let n be the required derived key length, in bytes. The derived key is
the first n bytes of the hash value after the hash computation has
been completed by CryptDeriveKey. If the hash is not a member of the
SHA-2 family and the required key is for either 3DES or AES, the key
is derived as follows:
Form a 64-byte buffer by repeating the constant 0x36 64 times. Let k be the length of the hash value that is represented by the input
parameter hBaseData. Set the first k bytes of the buffer to the result
of an XOR operation of the first k bytes of the buffer with the hash
value that is represented by the input parameter hBaseData.
Form a 64-byte buffer by repeating the constant 0x5C 64 times. Set the first k bytes of the buffer to the result of an XOR operation of
the first k bytes of the buffer with the hash value that is
represented by the input parameter hBaseData.
Hash the result of step 1 by using the same hash algorithm as that used to compute the hash value that is represented by the hBaseData
parameter.
Hash the result of step 2 by using the same hash algorithm as that used to compute the hash value that is represented by the hBaseData
parameter.
Concatenate the result of step 3 with the result of step 4.
Use the first n bytes of the result of step 5 as the derived key.
CF Key Generation
Start by hashing the password and converting it into binary with binaryDecode:
hBaseData = binaryDecode(hash("test1234", "SHA1"), "hex");
Build and populate the two buffers with the specified constants:
// 0x36 (i.e. 54 decimal)
buff1 = listToArray(repeatString("54,", 64));
// 0x5C (i.e. 92 decimal)
buff2 = listToArray(repeatString("92,", 64));
Then do a bitwise XOR, storing the results in the buffer:
for (k = 1; k <= arrayLen(hBaseData); k++) {
buff1[k] = BitXOR( buff1[k], hBaseData[k]);
buff2[k] = BitXOR( buff2[k], hBaseData[k]);
}
Next hash() both buffers and concatenate the results:
hash1 = hash( javacast("byte[]", buff1), "SHA1");
hash2 = hash( javacast("byte[]", buff2), "SHA1");
combined = hash1 & hash2;
Finally, extract the first n bytes (16 == 128 bits / 8) as the new key. Since CF's hash() function returns hexadecimal (always two characters per byte) string functions can be used here.
keySize = 128 / 8;
newKey = left(combined, keySize *2);
Result: A6455C7A24BC5E869B0DDF647238F5DA
Decrypting in CF
Before you can decrypt, a few important notes:
CF's encrypt/decrypt functions expect keys to be encoded as base64. The generated key above is in hexadecimal format. So it must be converted first:
"PKCS7Padding" is not valid for CF/Java. Instead use PKCS5Padding.
"CBC" mode always requires an IV. The IV is a binary array whose length is the same as the algorithm's block size (AES block size = 16 bytes). It must be "...the same value [used to encrypt] to successfully decrypt the data." Based on the descriptions in your API, your IV should be all zeros. (This is not good in practice, but is okay for a test case).
For more details, see Strong encryption in ColdFusion
Example:
encrypted = "1lqcm0Jiy4Rs29tz2jpuoQ==";
newKeyHex = "A6455C7A24BC5E869B0DDF647238F5DA";
keyBase64 = binaryEncode(binaryDecode(newKeyHex, "hex"), "base64");
iv = javacast("byte[]", [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
decrypted = decrypt(encrypted, keyBase64, "AES/CBC/PKCS5Padding", "Base64", iv);
writeOutput("<br>"& decrypted);
Result: recordID=000001
I'm using .NET's implementation of RSA, and two things looked odd to me. I'd like to confirm that it's operating properly.
Background
Using System.Security.Cryptography.RSACryptoServiceProvider with 2048-bit keyword size to perform asymmetric encryption/decrpytion, initially following the example in this question, "AES 256 Encryption: public and private key how can I generate and use it .net".
As a first implementation, this seems to work:
public const int CSPPARAMETERS_FLAG = 1; // Specifies RSA: https://msdn.microsoft.com/en-us/library/ms148034(v=vs.110).aspx
public const bool USE_OAEP_PADDING = false;
public const int KEYWORD_SIZE = 2048;
public static byte[] Encrypt(byte[] publicKey, byte[] dataToEncrypt)
{
var cspParameters = new System.Security.Cryptography.CspParameters(CSPPARAMETERS_FLAG);
byte[] encryptedData = null;
using (var rsaProvider = new System.Security.Cryptography.RSACryptoServiceProvider(cspParameters))
{
try
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.ImportCspBlob(publicKey);
encryptedData = rsaProvider.Encrypt(dataToEncrypt, USE_OAEP_PADDING);
}
finally
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.Clear();
}
}
return encryptedData;
}
public static byte[] Decrypt(byte[] privateKey, byte[] dataToDecrypt)
{
var cspParameters = new System.Security.Cryptography.CspParameters(CSPPARAMETERS_FLAG);
byte[] encryptedData = null;
using (var rsaProvider = new System.Security.Cryptography.RSACryptoServiceProvider(cspParameters))
{
try
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.ImportCspBlob(privateKey);
encryptedData = rsaProvider.Decrypt(dataToDecrypt, USE_OAEP_PADDING);
}
finally
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.Clear();
}
}
return encryptedData;
}
After looking into these methods a bit more, it seems that the public key that I've been generating as from the example seemed to have a lot of very predictable data at its start, and it was 276-bytes long.
Apparently rsaProvider.ExportCspBlob(bool includePrivateParameters) is a functional alternative to rsaProvider.ExportParameters(bool includePrivateParameters); the main difference is that the blob is already serialized as a byte[] while the other emits the object version, RSAParameters.
Two observations about the methods:
The .Exponent is always 0x010001$=65537$.
The exported blobs contain 17 extra bytes versus the serialized typed versions.
rsaProvider.ExportCspBlob():
Public key is 276 bytes.
Private key is 1172 bytes.
RSAParameters:
Public key is 259 bytes.
.Exponent.Length = 3
.Modulus .Length = 256
Private key is 1155 bytes.
.D .Length = 256
.DP .Length = 128
.DQ .Length = 128
.Exponent.Length = 3
.InverseQ.Length = 128
.Modulus .Length = 256
.P .Length = 128
.Q .Length = 128
The extra 17 bytes appear to be at the header of the binary blob.
Concerns
From this, two concerns:
Is it okay for the exponent to not be random?
If the exponent is defined as a constant, then it'd seem like that's another 3 bytes I could shave off the serialization?
Another question, Should RSA public exponent be only in {3, 5, 17, 257 or 65537} due to security considerations?, seems to suggest that $\left{3, 5, 17, 257, 65537\right}$ are all common values for the exponent, so 0x101$=65537$ seems reasonable if it's true that there's no harm in always using the same constant exponent.
Are the 17 extra bytes an information leak?
Do they represent the option parameters like key length and method?
Is it a good idea to be transmitting option parameter information when I already know that both the sender and receiver are using the same, hard-coded method?
Question
Is RSACryptoServiceProvider's behavior a cause for concern, or are these things normal?
Update 1
In Should RSA public exponent be only in {3, 5, 17, 257 or 65537} due to security considerations?, the accepted answer starts off by noting:
There is no known weakness for any short or long public exponent for RSA, as long as the public exponent is "correct" (i.e. relatively prime to p-1 for all primes p which divide the modulus).
If this is so, then I'd guess that the apparently-constant exponent of 0x010001$=65537$ is sufficient as long as it's relatively prime to $p-1$. So, presumably the .NET implementation of RSA checks for this condition.
But then what does RSACryptoServiceProvider do if that condition isn't satisfied? If it selects a different exponent, then that'd seem to leak information about $p$ whenever the exponent isn't 0x010001. Or, if a different key is selected, then it'd seem like we can just assume that the exponent is always 0x010001 and omit it from the serialization.
Everything reported is normal, and non-alarming.
It is perfectly OK for the public exponent e to be short and non-random. e = 216+1 = 65537 = 0x010001 is common and safe. Some authorities mandate it (or some range including it). Using it (or/and something significantly larger than the bit size of the public modulus) gives some protection against some of the worst RSA paddings.
No, the 17 extra bytes in the public key are unlikely to be an information leak; they more likely are a header part of the data format chosen for an RSA public key by the software you use. My guess is that you are encountering the MS-specific format detailed in this answer (perhaps, within endianness), which also uses precisely 276 bytes for an RSA public key with a 2048-bit public modulus. In that case, you should find that the extra bytes are always the same (thus they demonstrably leak nothing). And there are countless more subtle ways to leak information about the private key, like in the public modulus itself.
Many RSA key generators used in practice, including I guess RSACryptoServiceProvider, first choose e, then somewhat avoid generating primes p such that gcd(e, p-1) ≠ 1. Since e = 65537 is prime, it is enough that ( p % e ) ≠ 1, and this is easily checked, or otherwise insured by the process generating p.
For public key encryption and diffie-hellman in libsodium, I typically make private keys simply by generating 32 random bytes with randombytes_buf and then derive the public key (when needed) using crypto_scalarmult_base.
Is there any benefit to using crypto_box_keypair to generate a keypair (other than syntax)? Or does this function basically do exactly that?
This is exactly what the crypto_box_keypair() function does.
The benefits of this function are clarity, and guarantee that the secret key is properly generated.
https://download.libsodium.org/doc/public-key_cryptography/public-key_signatures.html
for example:
unsigned char pk[crypto_sign_PUBLICKEYBYTES]; //Variable declarations
unsigned char sk[crypto_sign_SECRETKEYBYTES]; Variable declarations
crypto_sign_keypair(pk, sk);
NSData *privateKeyData = [NSData dataWithBytes:sk length:crypto_box_SECRETKEYBYTES];
NSData *publicKeyData = [NSData dataWithBytes:pk length:crypto_box_PUBLICKEYBYTES];
NSLog(#"%#",privateKeyData); // target publick key data and secret key data
NSLog(#"%#",publicKeyData);
//Other
NSLog(#"%s\n\n=====\n\n\n%s",pk,sk); //(nullable const void *)bytes
Byte *byte = (Byte *)[publicKeyData bytes];
NSLog(#"%s",byte);
I'm working on rsa private and public key / encryption / decryption / etc using openssl module. But a question is still unanswered : why private key are often (are always, i don't know) longer than public key ?
Is there several answer to this ?
This is my public and private key generated.
why private key are often (are always, i don't know) longer than public key ?
The answer is detailed in PKCS 1 (and friends like RFC 2437).
The public key is the pair {e, n}, where e is the public exponent and n is the modulus.
One of the private key representations is the triplet {e, d, n}, where e is the public exponent, d is the private exponent and n is the modulus.
The other private key representations the n-tuple{e, d, n, p, q, dp, dq, qi}, where e is the public exponent; d is the private exponent; n is the modulus; and p and q are the factors of n.
And the remaining are for the Chinese Remainder theorem, which allows a speedup in signatures (I believe). dp is p's exponent, a positive integer such that e(dP) ≅ 1 (mod(p-1)); dq is q's exponent, a positive integer such that e(dq) ≅ 1 (mod(q-1)); and qi is CRT coefficient, a positive integer less than p such that q(qInv) ≅ 1 (mod p).
The RSA private key includes the values that are in the RSA public key, and some more values that are private. Futhermore, the values themselves are larger.
The public key contains the modulus and the public exponent e which is short (mostly 3 or 65537). On the other hand the private key contains the private exponent d ≡ e−1 (mod φ(n)). Depending on φ(n), d can be vastly larger than e.
There are other public key algorithms where this is different. Some algorithms based on elliptic curves have a single big integer as the private key (and group description) and the public key has a curve point (and group description) which is larger than the big integer.
I need to implement but I am not sure how can I as I am completely new into this. A function called get_values that has the prototype:
void get_values(unsigned int value, unsigned int *p_lsb, unsigned int *p_msb,
unsigned int *p_combined)
The function computes the least significant byte and the most significant byte of the value
parameter. In addition, both values are combined. For this problem:
a. You may not use any loop constructs.
b. You may not use the multiplication operator (* or *=).
c. Your code must work for unsigned integers of any size (4 bytes, 8 bytes, etc.).
d. To combine the values, append the least significant byte to the most significant one.
e. Your implementation should be efficient.
The following driver (and associated output) provides an example of using the function you are
expected to write. Notice that in this example an unsigned int is 4 bytes, but your function
needs to work with an unsigned int of any size.
Driver
int main() {
unsigned int value = 0xabcdfaec, lsb, msb, combined;
get_values(value, &lsb, &msb, &combined);
printf("Value: %x, lsb: %x, msb: %x, combined: %x\n", value, lsb, msb, combined);
return 0;
}
Output
Value: abcdfaec, lsb: ec, msb: ab, combined: abec
I think you want to look into bitwise and and bit shifting operators. The last piece of the puzzle might be the sizeof() operator if the question is asking that the code should work with platforms with different sized int types.