I am learning TLS protocol 1.0/1.1/1.2 recently. I notice AESCBC128/256 can be used for TLS 1.0 although it is not mentioned in the initial TLS1.0 RFC. I am wonderring how the AESCBC IV/salt is exchanged between client and server? Is it the same as TLS 1.2 that always exchange IV within the application data (the first 16 bytes of the data)? If any official materials describe this would be grateful.
TLS_RSA_WITH_AES_128_CBC_SHA is mandatory to implement in TLS 1.2 (see RFC 5246).
Section §6.2.3.2 explains how CBC works:
For block ciphers (such as 3DES or AES), the encryption and MAC
functions convert TLSCompressed.fragment structures to and from block
TLSCiphertext.fragment structures.
struct {
opaque IV[SecurityParameters.record_iv_length];
block-ciphered struct {
opaque content[TLSCompressed.length];
opaque MAC[SecurityParameters.mac_length];
uint8 padding[GenericBlockCipher.padding_length];
uint8 padding_length;
};
} GenericBlockCipher;
The MAC is generated as described in Section 6.2.3.1.
AES ciphersuites were introduced by RFC 3268 in June 2002, hence for TLS prior to 1.2. Note in particular this:
Cipher Usage
The new ciphersuites proposed here are very similar to the following,
defined in [TLS]:
TLS_RSA_WITH_3DES_EDE_CBC_SHA
TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA
TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA
TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
TLS_DH_anon_WITH_3DES_EDE_CBC_SHA
All the ciphersuites described here use the AES in cipher block
chaining (CBC) mode. Furthermore, they use SHA-1 [SHA-1] in an HMAC
construction as described in section 5 of [TLS]. (Although the TLS
ciphersuite names include the text "SHA", this actually refers to the
modified SHA-1 version of the algorithm.)
If you now look at section §6.2.3.2 of RFC 2246 (TLS 1.0 and hence also 1.1) you can read this:
Note: With block ciphers in CBC mode (Cipher Block Chaining) the
initialization vector (IV) for the first record is generated with
the other keys and secrets when the security parameters are set.
The IV for subsequent records is the last ciphertext block from
the previous record.
The glossary also says:
cipher block chaining (CBC)
CBC is a mode in which every plaintext block encrypted with a
block cipher is first exclusive-ORed with the previous ciphertext
block (or, in the case of the first block, with the
initialization vector). For decryption, every block is first
decrypted, then exclusive-ORed with the previous ciphertext block
(or IV).
Related
I am using the following code to query current TLS connection:
SecPkgContext_ConnectionInfo data;
QueryContextAttributes(&myHandle, SECPKG_ATTR_CONNECTION_INFO, &data);
It returns correct structure with all the fields:
typedef struct _SecPkgContext_ConnectionInfo {
DWORD dwProtocol;
ALG_ID aiCipher;
DWORD dwCipherStrength;
ALG_ID aiHash;
DWORD dwHashStrength;
ALG_ID aiExch;
DWORD dwExchStrength;
} SecPkgContext_ConnectionInfo, *PSecPkgContext_ConnectionInfo;
as per MSDN documentation: https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-secpkgcontext_connectioninfo
However, the field aiExch has value of 0xAE06 which I guess (from wincrypt.h file) is defined as CALG_ECDH_EPHEM, but documentation only mention two possible values:
CALG_RSA_KEYX 0xA400 // RSA key exchange
CALG_DH_EPHEM 0xAA02 // Diffie-Hellman key exchange.
Now the questions:
What is the meaning of 0xAE06 / CALG_ECDH_EPHEM?
What other values the field aiExch can have?
From ALG_ID:
CALG_ECDH_EPHEM | 0x0000ae06 | Ephemeral elliptic curve Diffie-Hellman key exchange algorithm.
[!Note]
This algorithm is supported only through
Cryptography API: Next Generation
Windows Server 2003 and Windows XP: This algorithm is not supported.
I can't tell you what else might show up.
I'm wondering which changes from SSL 3.0 to TLS 1.0 exactly fixed the POODLE Attack. The Base for this Attack is the Messageblocks M1||MAC||PAD, so a whole Block is used for MAC and Padding.
I have the Idea, that it doesn't work anymore (without downgrading) cause in TLS 1.0 if the last Block is Padding it is 0x101010... (With block size of 16) and not 0xXX...XX10 (XX=Random), so it's a lot more Heavy to guess 16 Bytes directly instead of only the last Byte.
But are there any other security parameters that fixed this problem or did I mentioned it right? Like is the end of the Messages not ||MAC||PAD anymore? Or is the PAD maybe signed or something like that?
Regards
Julian
SSL 3.0 and TLS 1.0 differ in how they treat the padding.
See https://www.openssl.org/~bodo/ssl-poodle.pdf and this section:
The most severe problem of CBC encryption in SSL 3.0 is that its block
cipher padding is not deterministic, and not covered by the MAC
(Message Authentication Code): thus, the integrity of padding cannot
be fully verified when decrypting. Padding by 1 to L bytes (where L is
the block size in bytes) is used to obtain an integral number of
blocks before performing blockwise CBC (cipherblock chaining)
encryption. The weakness is the easiest to exploit if there’s an
entire block of padding, which (before encryption) consists of L-1
arbitrary bytes followed by a single byte of value L-1.
The messages in TLS1.0 are still structured the same, see this structure from RFC 2246:
block-ciphered struct {
opaque content[TLSCompressed.length];
opaque MAC[CipherSpec.hash_size];
uint8 padding[GenericBlockCipher.padding_length];
uint8 padding_length;
} GenericBlockCipher;
The padding is defined as such:
Each uint8 in the padding data vector must be filled with the padding length value.
This is the crucial difference between SSL 3.0 and TLS 1.0 in that regard, which makes the receiver able to check that the padding is right, and not being in fact leftover from valid application data blocks.
(compare https://www.rfc-editor.org/rfc/rfc6101#section-5.2.3.2 for SSL 3.0 with https://www.rfc-editor.org/rfc/rfc2246.html#section-6.2.3.2 for TLS 1.0)
This is also explained on https://www.imperialviolet.org/2014/10/14/poodle.html like that:
Consider the following plaintext HTTP request, which I've broken into
8-byte blocks (as in 3DES), but the same idea works for 16-byte blocks
(as in AES) just as well:
[GET / HT][TP/1.1\r\n][Cookie: ][abcdefgh][\r\n\r\nxxxx][MAC DATA][•••••••7]
The last block contains seven bytes of padding (represented as •) and
the final byte is the length of the padding.
[..]
An attacker can't see the plaintext contents like we can in the
diagram, above. They only see the CBC-encrypted ciphertext blocks. But
what happens if the attacker duplicates the block containing the
cookie data and overwrites the last block with it? When the receiver
decrypts the last block it XORs in the contents of the previous
ciphertext (which the attacker knows) and checks the authenticity of
the data. Critically, since SSLv3 doesn't specify the contents of the
padding (•) bytes, the receiver cannot check them. Thus the record
will be accepted if, and only if, the last byte ends up as a seven.
And later:
The critical part of this attack is that SSLv3 doesn't specify the
contents of padding bytes (the •s). TLS does and so this attack
doesn't work because the attacker only has a 2-64 or 2-128 chance of a
duplicated block being a valid padding block.
I use RSACryptoServiceProvider to encrypt some small blocks of data. For the solution I'm working on, it's important that if the same piece of source data is encrypted twice with the same public key, the result (the encrypted block of data) is not the same.
I have checked this with an example and it worked like I hoped. My question is now, if this behaviour is by design and guaranteed or if I have to add some random part to the source data for guaranteeing that data blocks with the same data can not be matched anymore after encryption.
Here is the example:
byte[] data=new byte[]{1,7,8,3,4,5};
RSACryptoServiceProvider encrypter = cert.PublicKey.Key as RSACryptoServiceProvider;
byte[] encryptedData = encrypter.Encrypt(data,true);
// encryptedData has always other values in, although the source data is always
// 1,7,8,3,4,5 and the certificate is always the same (loaded from disk)
The concrete question is for .net but maybe the answer can be given in general for all RSA-implementations if it is by design?
The text-book RSA encryption algorithm is deterministic:
ciphertext = plaintext ^ encryption-exponent mod modulus
(Here ^ is integer exponentiation, mod the remainder operation.)
But as you remarked, this does not provide a good security guarantee, as an attacker which can guess the plaintext can simply verify this guess by encrypting it himself and comparing the results.
For this reason, the official RSA specifications (and also all implementations used in practice) include some (partly random) padding, so we don't actually encrypt plaintext, but pad(plaintext):
ciphertext = pad(plaintext) ^ encryption-exponent mod modulus
Decryption:
plaintext = unpad( ciphertext ^ decryption-exponent mod modulus )
Only with this padding RSA is actually a secure encryption scheme.
A similar padding is also used for RSA signatures, to avoid easy forging of signatures.
I want to know if RSA signatures are unique for a data.
Suppose I have a "hello" string. The method of computing the RSA signature is firstly to get the sha1 digest(these are , I know, unqiue for data), then add a header with OID and padding scheme mentioned and do some mathematical jiggle to give the signature.
Now assuming padding is same, will the signature generating by openSSL or Bouncy Castle be same?
If yes, my only fear is, won't it be easy to get back the "text"/data??
I actaully tried to do an RSA signature of some data and the signatures from OpenSSL and BC was different. I repeated it but got same signature again and again for each of them. I realized that the two signatures of the methods were different because of the difference in padding. However I am still not sure why the signatures of each of the libs are same all the time I repeat them. Can somebody please give an easy explanation?
The "usual" padding scheme, described in PKCS#1 as the "old-style, v1.5" padding, is deterministic. It works like this:
The data to sign is hashed (e.g. with SHA-1).
A fixed header is added; that header is actually an ASN.1 structure which identifies the hash function which was just used to process the data.
Padding bytes are added (on the left): 0x00, then 0x01, then some 0xFF bytes, then 0x00. The number of 0xFF bytes is adjusted so that the resulting total length is exactly the byte length of the modulus (i.e. 128 bytes for a 1024-bit RSA key).
The padded value is converted to an integer (which is less than the modulus), which goes through the modular exponentiation which is at the core of RSA. The result is converted back to a sequence of bytes, and that's the signature.
All these operations are deterministic, there is no random, hence it is normal and expected that signing the same data with the same key and the same hash function will yield the same signature ever and ever.
However there is a slight underspecification in the ASN.1-based fixed header. This is a structure which identifies the hash function, along with "parameters" for that hash function. Usual hash functions take no parameters, hence the parameters shall be represented with either a special "NULL" value (which takes a few bytes), or be omitted altogether: both representations are acceptable (although the former is supposedly preferred). So, the raw effect is that there are two versions of the "fixed header", for a given hash function. OpenSSL and Bouncycastle do not use the same header. However, signature verifiers are supposed to accept both.
PKCS#1 also describes a newer padding scheme, called PSS, which is more complex but with a stronger security proof. PSS includes a bunch of random bytes, so you will get a distinct signature every time.
Signatures are not a privacy mechanism; it's not considered a problem if you can get the plaintext back out. If your message must be kept secret, then encrypt as well as sign.
Nevertheless, remember that RSA signatures are created using a signer's private key. Given such a signature, you can use the signer's public key to "undo" the RSA transform (raise the message's signature to e, mod n) and get out the SHA1 or other hash value that was provided as its input. You still can't undo the hash function to get the input plaintext corresponding to a signature that has become detached from its message.
RSA for encryption is a different matter. Padding methods for encryption here do include random data in order to defeat traffic analysis.
This is why you add a salt/initialisation vector on top of your key. That way it shouldn't be possible to tell which records came from the same plaintext.
I'm trying to encrypt some date using a public key derived form the exchange key pair made with the CALG_RSA_KEYX key type. I determined the block size was 512 bits using cryptgetkeyparam KP_BLOCKLEN. It seems the maximum number of bytes I can feed cryptencrypt in 53 (424 bits) for which I get an encrypted length of 64 back. How can I determine how many bytes I can feed into cryptencrypt? If I feed in more than 53 bytes, the call fails.
RSA using the usual PKCS#1 v.1.5 mode can encrypt a message that is at most k-11 bytes, where k is the length of the modulus in bytes. So a 512 bit key can encrypt up to 53 bytes and a 1024 bit key can encrypt up to 117 bytes.
RSA using OAEP can encrypt a message up to k-2*hLen-2, where k is the modulus byte-length and hLen is the length of the output of the underlying hash-function. So using SHA-1, a 512 bit key can encrypt up to 22 bytes and a 1024 bit key can encrypt up to 86 bytes.
You should not normally use a RSA key to encrypt your message directly. Instead you should generate a random symmetric key (f.x. an AES key), encrypt your message with the symmetric key, encrypt the key with the RSA key and transmit both encryptions to the recipient. This is usually called hybrid encryption.
EDIT: Although this response is marked as accepted by the OP, please see Rasmus Faber response instead, as this is a much better response. Posted 24 hours later, Rasmus's response corrects factual errors,in particular a mis-characterization of OAEP as a block cipher; OAEP is in fact a scheme used atop PKCS-1's Encoding Primitive for the purpose of key-encryption. OAEP is more secure and puts an even bigger limit on the maximum message length, this limit is also bound to a hash algorithm and its key length.
Another shortcoming of the following reply is its failure to stress that CALG_RSA_KEYX should be used exclusively for the key exchange, after which transmission of messages of any length can take place with whatever symmetric key encryption algorithm desired. The OP was aware of this, he was merely trying to "play" with the PK, and I did cover that much, albeit deep in the the long remarks thread.
Fore the time being, I'm leaving this response here, for the record, and also as Mike D may want to refer to it, but do remark-me-in, if you think that it would be better to remove it altogether; I don't mind doing so for sake of clarity!
-mjv- Sept 29, 2009
Original reply:
Have you check the error code from GetLastError(), following cryptencrypt()'s false return?
I suspect it might be NTE_BAD_LEN, unless there's be some other issue.
Maybe you can post the code that surrounds your calling criptencryt().
Bingo, upon seeing the CryptEncrypt() call.
You do not seem to be using the RSAES w/ OAEP scheme, since you do not have the CRYPT_OAEP flag on. This OAEP scheme is a block cipher based upon RSAES. This latter encryption algorihtm, however, can only encrypt messages slightly less than its key size (expressed in bytes). This is due to the minimum padding size defined in PKCS#1; such padding helps protect the algorithm from some key attacks, I think the ones based on known cleartext).
Therefore you have three options:
use the CRYPT_OAEP in the Flag parameter to CryptEncrypt()
extend the key size to say 1024 (if you have control over it, beware that longer keys will increase the time to encode/decode...)
Limit yourself to clear-text messages shorter than 54 bytes.
For documentation purposes, I'd like to make note of a few online resources.
- The [RSA Labs][1] web site which is very useful in all things crypto.
- Wikipedia articles on the subject are also quite informative, easier to read
and yet quite factual (I think).
When in doubt, however, do consult a real crypto specialist, not someone like me :-)