I'm trying to create some classes that wrap and simply the use of the .NET cryptography library classes.
But if I have the caller supply only a password, and I generate the salt and IV automatically, it seems like I then need to store the salt and IV along with my encrypted data. (This is because I am told I should never use duplicate IVs and my code wouldn't be very simple if the caller had to generate and track different salts and IVs.)
Ideally, my encrypted data would be as compact as possible. But this adds quite a few bytes to my encrypted results, especially if I'm only encrypting a small amount of data such as a single byte.
My questions are:
Is there any way to reduce the size of my encrypted data without making my classes considerably more work to use?
Can anyone see any other flaws to my thinking here?
You could just use a random salt and generate a key and IV from it. The best way to do this is first to generate a key seed using a Password Based Key Derivation Function or PBKDF. In the case of C# PBKDF2, as implemented in Rfc2898DeriveBytes is probably the easiest choice. You'll need at least 8 bytes of salt and a high iteration count.
OK, so now you have a key seed, you need to generate a key from it. For this you need a Key Based Key Derivation Function or KBKDF. The most up to date one is HKDF (in which case you'd only need HKDF-extract to be precise). However KBKDF's are pretty uncommon in crypto libraries, so you it would take some time to implement it.
If you cannot find a good enough library then you may go for KDF1 as specified on this page. Just use the ASCII encoded string for "key" and "IV" as OtherInfo and set the counter to four bytes set to zero. The result is key = Hash(key seed | 00 00 00 00 | "key") and IV = Hash(key seed | 00 00 00 00 | "IV"). Here | means concatenation. If you have too many bytes, just use the leftmost ones from the result. SHA-1, SHA-256 and SHA-512 will do but I would recommend to use one that has enough output bits for your particular key - SHA-256 probably makes the most sense.
Once you have a key and IV you can use any kind of encryption method. For minimum overhead you can use CTR based encryption. Beware that if man-in-the-middle is possible (i.e. any transport protocol) then you need to have an authentication tag as well.
So there you have it: just 8 bytes of overhead, unless you need the authentication tag as well. What you should not do is to use a static key or IV. Neither a static key or IV is considered secure.
Related
This is a piece of my code that encrypts a private key:
string pemContent = File.ReadAllText(pemPath);
csp.ImportFromPem(pemContent);
string test = rsa.GetPrivateKey();
var data = Encoding.UTF8.GetBytes(test);
var cypher = csp.Encrypt(data, false);
Console.WriteLine(Convert.ToBase64String(cypher));
This is the GetPrivateKey() function:
public string GetPrivateKey()
{
return rsa.ToXmlString(true);
}
I get this error:
Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'Bad Length.'
I know by now that private key is to big to encrypt it with client public key and the question is how to make is possible?
I can't find anything similar to what I am doing, the only clue I have is that wannary used the same technique while its file encryption process.
I use RSACryptoServiceProvider to handle rsa encryption.
EDIT:
Maybe i should describe my case more in detail. I am building a simple ransomware (i (i am a cybersecurity enthusiast and i do it just to learn how this viruses works in depth, so it's for educational purposes only). More advanced ransomware uses hybrid encryption. The scheme i am trying to implement is well described in this video. I am stuck in the last step which is encrypting client private key with server public key.
I'm presuming RSA here, it's not directly in the question, but it can be concluded from the code. Also, because of the second parameter of Encrypt being false, I'll assume PKCS#1 v1.5 padding.
There are two ways to do this. One you have already mentioned, and it is the best option: use hybrid encryption. You first create a random encryption key, encrypt the RSA key, and then encrypt that key.
The second way is to simply use a larger RSA key pair for the server. PKCS#1 v1.5 padding has a minimum overhead of 11 bytes, 8 of which are non-zero random. It's better to use 16 bytes of random data though, so then you'd have 19 bytes / 152 bits of overhead. For more information on the overhead of RSA encryption see my answer here.
The encoded private key needs to be in the remaining bits. Now it is best to use the minimum amount of bits to encode the private key. The best way to do this is to encode the modulus and only the private exponent (i.e. without the CRT parameters). Each of these will take as many bits as the key size if you use a constant sized, unsigned big endian encoding. So the key pair needs to be klen * 2 + 152 bits.
Note that this is not necessarily the best option as there could be schemes that allow you to never generate the private key on the client in the first place, until the private key needs to be released for decryption that is.
I am new to hashing the data, I got a requirement to Hash data to the length of 128.
Tried hashing using SHA256 and SHA512, which produces 44 and 88 lengths of hashed data. Is there any way to generate hashed data at a specified length?
I am trying to achieve using the below code.
var value = "Test";
using var sha256 = SHA256.Create();
byte[] privatekeyBytes = Encoding.UTF8.GetBytes(value);
var text = Convert.ToBase64String(sha256.ComputeHash(privatekeyBytes));
I know it's a pretty basic question, any links to understand more on this will also help me.
It really depends on what you want to achieve. In some contexts, I keep insisting that return 1 is a valid hash function. By and large, there are three applications for hashes:
Quickly differentiate between data that is certainly different for better runtime performance. For that purpose, depending on your data something dumb like return input.Length can be perfectly sufficient.
Evenly distribute data in a HashSet or similar, i.e., you primarily care that all available hashes are used roughly equally often. For that, and again depending on the type of data you are processing, it might suffice to take the first 128 bytes, XOR them with the next 128 bytes, XOR with the next 128 bytes and so on until the end of the input data.
Cryptographically sign something. For that you should use one of the hashing algorithms designed for that purpose; they all produce a fixed number of bytes. If you find one that produces more than 128 bytes, it is perfectly fine to just truncate it to that length (at the loss of the additional security the extra bytes would have brought). As far as I can see, SHA512 is available in C# and should return a sufficiently long hash, that is, SHA512.Create().ComputeHash(privatekeyBytes).First(128).
Probably I have several misunderstandings.
AFAIK signing a byte array with RSA-SHA1 generates a byte array (signature) of the same lenght as the RSA key used. Is that right?
From another side signing, roughly means generate a hash using SHA1 (so it is 160 bites long) and then with or without a padding scheme encrypt it with the private key.Is that right?
Later on, in order to recover this hash (with or without padding schema on it) I would need to encrypt the signature with the public key. Is that right?
Something is broken in my logic because I'm not able to encrypt the signature with the public key.
Or my code is wrong. I'm using .net RSACryptoServiceProvider and it raises a bad data length error when trying to encrypt a signature... I assume encrypt means apply RSA using public key, right?
When trying to decrypt it raises a Key Not found exception. As expected because I only have the public key.
EDIT:
Given a byte array and RSACryptoServiceProvider I could Encrypt, Decrypt and SignData. I thought that SignData (without padding schema to simplify the question) is a shortcut of apply SHA, then Decrypt. For Encrypt I mean applying the RSA formula using public key as input, and for Decrypt I mean applying the RSA formula (the very same formula) using private key as input. Are this definitions ok?
EDIT2:
For exemple have a look at the next signed xml: http://www.facturae.gob.es/formato/Versiones/factura_ejemplo2_32v1.xml
And the next powershell script:
$signb64="oYR1T06OSaryEDv8VF9/JgWmwf0KSyOXKpBWY4uAD0YoMh7hedEj8GyRnKpVpaFanqycIAwGGCgl vtCNm+qeLvZXuI0cfl2RF421F8Ay+Q0ani/OtzUUE49wuvwTCClPaNdhv2vfUadR8ExR7e/gI/IL 51uc3mEJX+bQ8dxAQ2w=";
$certB64="MIIDtDCCAx2gAwIBAgICAIcwDQYJKoZIhvcNAQELBQAwcjELMAkGA1UEBhMCRVMxDzANBgNVBAgT Bk1hZHJpZDEPMA0GA1UEBxMGTWFkcmlkMQ4wDAYDVQQKEwVNSVR5QzEbMBkGA1UECxMSTUlUeUMg RE5JZSBQcnVlYmFzMRQwEgYDVQQDEwtDQSB1c3VhcmlvczAeFw0wOTEwMTUxNjA5MzRaFw0xMDEw MTUxNjA5MzRaMHExCzAJBgNVBAYTAkVTMQ8wDQYDVQQIEwZNYWRyaWQxDzANBgNVBAcTBk1hZHJp ZDEOMAwGA1UEChMFTUlUeUMxGzAZBgNVBAsTEk1JVHlDIEROSWUgUHJ1ZWJhczETMBEGA1UEAxMK VXN1YXJpbyA1NDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAroms65axKuQK18YDfD/x6DIn 0zKZ+6bv1K2hItJxel/JvU3JJ80/nY5o0Zbn+PrvlR2xF3poWYcPHLZpesgxhCMfnP7Jb5OUfceL g44m6T9P3PG1lSAZs3H6/TabyWGJy+cNRZMWs13KnB9fDAjJ5Jw0HVkwYNwmb1c7sHCuyxcCAwEA AaOCAVgwggFUMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMB0GA1UdDgQWBBTYhqU2tppJoHl+S1py BOH+dliYhzCBmAYDVR0jBIGQMIGNgBT1oWqod09bsQSMp35I8Q6fxXaPG6FypHAwbjEPMA0GA1UE CBMGTWFkcmlkMQ8wDQYDVQQHEwZNYWRyaWQxDjAMBgNVBAoTBU1JVHlDMRswGQYDVQQLExJNSVR5 QyBETkllIFBydWViYXMxEDAOBgNVBAMTB1Jvb3QgQ0ExCzAJBgNVBAYTAkVTggEDMAkGA1UdEQQC MAAwNgYDVR0SBC8wLYYraHR0cDovL21pbmlzdGVyLThqZ3h5OS5taXR5Yy5hZ2UvUEtJL0NBLmNy dDA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vbWluaXN0ZXItOGpneHk5Lm1pdHljLmFnZS9QS0kv Y3JsLmNybDANBgkqhkiG9w0BAQsFAAOBgQAhAN/KVouQrHOgd74gBJqGXyBXfVOeTVW+UTthhfCv DatXzTcrkYPQMfBAQMgGEa5KaQXcqKKhaoCUvrzFqE0HnAGX+ytX41oxZiM2fGNxRZcyUApLEX67 m8HOA/Cs2ZDlpU2W7wiOX5qr+ToTyfXsnRwPWvJ8VUmmXwyMEKcuzg==";
$signb=[System.Convert]::FromBase64String($signB64);
$certb=[System.Convert]::FromBase64String($certB64);
$cert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList #(,$certb)
$rsacsp = [System.Security.Cryptography.RSACryptoServiceProvider] $cert.PublicKey.Key;
$signb.Length*8;
$rsacsp;
$rsacsp.Encrypt($signb,0);
I tried:
$rsacsp.Encrypt($signb,[System.Security.Cryptography.RSAEncryptionPadding]::Pkcs1);
instead of
$rsacsp.Encrypt($signb,0);
But I always get a bad length error:
Exception calling "Encrypt" with "2" argument(s): "Bad Length.
EDIT 3:
After reading, I can see my main issue was "From another side signing, roughly means generate a hash using SHA1 (so it is 160 bites long) and then with or without a padding scheme encrypt it with the private key.Is that right?".
RSA sign (with a n bits key length) could be viewed as an operation that takes an arbitraty byte array and outputs n bits. In order to do that, it uses a hash function like SHA1 that takes an arbitrary byte array and produces a fixed output (160 bits for SHA1). Now in theory I could "encrypt" with the private key but then the output would be 160 bits long too it is not the way RSA is implemented. RSA Signing needs to apply padding function after the hash in order to produces an n bits text before "encrypting" it.
Another source of confusion is the meaning of the Encrypt method of .NET RSACryptoProvider. It turns out that this method has two parameters: a byte array and a flag indicating the padding function. It takes the byte array, applies the padding and then "encrypts" with the public key. It is of no use for a signature scenario. The operations decrypt and encrypt in RSACryptoProvider are not simmetrical. You can "decrypt" whatever has been "encrypt", but not the other way around.
At the end the confusion lies in that "atomic" functions used when encrypting/decrypting and the ones used when signin are the same, but they are used in incompatible ways.
AFAIK signing a byte array with RSA-SHA1 generates a byte array (signature) of the same lenght as the RSA key used. Is that right?
Usually yes, although the size will of course be encoded as octet stream (aka byte array) it is possible that the size of the signature is actually up to 7 bits larger. The key size is normally a multiple of 8 (bits) so this doesn't come up much.
From another side signing, roughly means generate a hash using SHA1 (so it is 160 bites long) and then with or without a padding scheme encrypt it with the private key.Is that right?
No, you should never perform modular exponentiation in RSA without padding; a padding scheme is required for security. Note that you should not talk about encryption here. Encryption is used to provide confidentiality. That RSA signature generation and encryption both uses modular exponentiation - although with different keys - doesn't mean one equates the other.
It is important to note that the padding scheme for PKCS#1 v1.5 encryption is different from the one used for signature generation. Furthermore there are also the newer OAEP padding scheme for encryption and the PSS padding scheme for signature generation which are rather distinct.
Later on, in order to recover this hash (with or without padding schema on it) I would need to encrypt the signature with the public key. Is that right?
Perform modular exponentiation and then verify the result, yes. But as the verification requires verifying the padding in a secure way you should really let an API handle this.
Something is broken in my logic because I'm not able to encrypt the signature with the public key.
Try something written for verification instead, like the method VerifyHash as seen in this example.
You can try and find a raw RSA implementation to find out what is within the RSA signature. You should only do this to analyze the signature.
So if you "encrypt" the data with the public key (i.e. just perform modular exponentiation) you would get:
0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003021300906052b0e03021a05000414a2304127e2fe3b8a8203b219feafdd9b58558310
as result. This is clearly PCKS#1 v1.5 padding for signature generation. It includes an encoded hash value:
SEQUENCE(2 elem)
SEQUENCE(2 elem)
OBJECT IDENTIFIER1.3.14.3.2.26
NULL
OCTET STRING(20 byte) A2304127E2FE3B8A8203B219FEAFDD9B58558310
I need to make strong key for AES-256 in a) Unicode characters, b) key in bytes.
a) I have to generate 50 random Unicode characters and then convert them to bytes. Is this possible to use Unicode characters as AES256 key?
For e.g. I want to use this page to create password.
is there any way to import all characters from Windows characters table to program in Windows Form App?
b) I'm using this code:
System.Security.Cryptography.AesCryptoServiceProvider key = new System.Security.Cryptography.AesCryptoServiceProvider();
key.KeySize = 256;
key.GenerateKey();
byte[] AESkey = key.Key;
It's enough or I should change something?
Also I have one more question. Making an AES key longer then 43 ASCII characters will be more secure or it will be anyway hashed to 256bit? And there is difference between ASCII key of 43 characters and 100?
a) I have to generate 50 random Unicode characters and then convert them to bytes. Is this possible to use Unicode characters as AES256 key?
Yes, this is possible. Since you have plenty of space for characters you can just encode it. ceil(32 / 3) * 4 = 44, so you'd have enough characters for this. You would not be using the additional space provided by Unicode encoding though. Obviously you would need to convert it back to binary before using it.
b) is aes.GenerateKey "enough"?
Yes, aes.GenerateKey is enough to generate a binary AES key.
c) Making an AES key longer then 43 ASCII characters will be more secure or it will be anyway hashed to 256bit? And there is difference between ASCII key of 43 characters and 100?
An AES key is not hashed at all. It's just 128, 192 or 256 bits (i.e. 16, 24 or 32 bytes) of data that should be indistinguishable from random (to somebody that doesn't know the value, of course). If you want to hash something you'd have to do it yourself - but please read on.
The important thing to understand is that a password is not a key, and that keys for modern ciphers are almost always encoded as binary. For AES there is no such thing as an ASCII key. If you need to encode the key, use base 64.
If you want to use a password then you need to use a key derivation function or KDF. Furthermore, if you want to protect against dictionary and rainbow table attacks you will want to use a password based key derivation function or PBKDF. Such a KDF is also called a "password hash". In case of .NET your best bet is Rfc2898DeriveBytes which implements PBKDF2. PBKDF2 is defined in the RFC
2898 titled: PKCS #5: Password-Based Cryptography Specification Version 2.0 which you may want to read.
What is the difference between using Rfc2898DeriveBytes and just using Encoding.ASCII.GetBytes(string object);?
I have had relative success with either approach, the former is a more long winded approach where as the latter is simple and to the point. Both seem to allow you to do the same thing eventually but I am struggling to the see the point in using the former over the latter.
The basic concept I have been able to grasp is that you can convert string passwords into
byte arrays to be used for e.g a symmetric encryption class, AesManaged. Via the RFC class but you get to use salt values and password when creating your rfc object. I assume its more secure but still thats an uneducated guess at best! Also that it allows you to return byte arrays of a certain size, well something like that.
Here are a few examples to show you where I am coming from:
byte[] myPassinBytes = Encoding.ASCII.GetBytes("some password");
or
string password = "P#%5w0r]>";
byte[] saltArray = Encoding.ASCII.GetBytes("this is my salt");
Rfc2898DeriveBytes rfcKey = new Rfc2898DeriveBytes(password, saltArray);
The 'rfcKey' object can now be used towards setting up the the .Key or .IV properties
on a symmetric encryption algorithm class.
ie.
RijndaelManaged rj = new RijndaelManaged ();
rj.Key = rfcKey.Getbytes(rj.KeySize / 8);
rj.IV = rfcKey.Getbytes(rj.Blocksize / 8);
'rj' should be ready to go !
The confusing part ... so rather than using the 'rfcKey' object can I not just use my
'myPassInBytes' array to help set-up my 'rj' object?
I have tried doing this in VS2008 and the immediate answer is NO. But have you guys got a better educated answer as to why the RFC class is used over the other alternative I have mentioned above?
You really, really do not want to use a user password directly as a crypto key, especially with AES.
Rfc2898DeriveBytes is an implementation of PBKDF2. What it does is repeatedly hash the user password along with the salt. This has multiple benefits:
Firstly, you can use arbitrarily sized passwords - AES only supports specific key sizes.
Secondly, the addition of the salt means that you can use the same passphrase to generate multiple different keys (assuming the salt is not a constant, as it is in your example). This is important for key separation; reusing keys in different contexts is one of the most common ways cryptographic systems are broken.
The multiple iterations (1000 by default) slow down password guessing attacks. Consider someone who is trying to guess your AES key. If you just used the password, this would be straightforward - just try each possible password as the key. On the other hand, with PBKDF2, the attacker first has to perform 1000 hash iterations for each password guess. So while it slows down a user only slightly, it has a disproportionate effect on an attacker. (In fact it's quite common to use much higher iteration counts; 10000 is commonly recommended).
It also means the final output key is uniformly distributed. If you used the password, for instance, typically 16 out of 128 bits of the key would be 0 (the high ASCII bit). That right there immediately makes keysearch 65536 times easier than it should be, even ignoring the password guessing.
Finally, AES has specific vulnerabilities with related key attacks. Related key attacks are possible when an attacker knows some data encrypted with several keys, and there is some known (or guessed) relation between them. For instance, if you encrypted data with both a password-key of "My AES key sucks" (16 bytes, for AES-128) and with "MY AES KEY SUCKS", a related key attack might be possible. The currently best known attacks do not actually allow breaking the full AES in this way, but they have been getting progressively better over time - just last week a new attack was published that breaks 13 rounds (out of 14 total) of AES-256 using a related key attack. It would be profoundly unwise to rely on such attacks not getting better over time.