From 21f1af2ec217c6e64af2095186c558b81a00e212 Mon Sep 17 00:00:00 2001 From: emboss Date: Sat, 9 Jun 2012 18:16:18 +0000 Subject: [PATCH] * ext/openssl/ossl.c: Fix error in example. Patch by David Albert. Add/extend existing documentation. Examples now also cover RSA signatures and PBKDF2. [ruby-core: 45154][ruby-trunk - Bug #6475] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@35996 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 9 ++++ ext/openssl/ossl.c | 119 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 114 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index f721db74d6..bd3f5cf3e0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Sun Jun 10 03:09:41 2012 Martin Bosslet + + * ext/openssl/ossl.c: Fix error in example. Patch by David Albert. + + Add/extend existing documentation. Examples now also cover RSA + signatures and PBKDF2. + [ruby-core: 45154][ruby-trunk - Bug #6475] + + Sun Jun 10 01:41:45 2012 Martin Bosslet * ext/openssl/ossl_ssl.c: Introduce SSLContext#renegotiation_cb and diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index 7c8507050c..da7d08ad49 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -455,7 +455,7 @@ ossl_debug_set(VALUE self, VALUE val) * ahold of the key may use it unless it is encrypted. In order to securely * export a key you may export it with a pass phrase. * - * cipher = OpenSSL::Cipher::Cipher.new 'AES-128-CBC' + * cipher = OpenSSL::Cipher.new 'AES-128-CBC' * pass_phrase = 'my secure pass phrase goes here' * * key_secure = key.export cipher, pass_phrase @@ -489,35 +489,126 @@ ossl_debug_set(VALUE self, VALUE val) * * == RSA Encryption * - * RSA provides ecryption and decryption using the public and private keys. + * RSA provides encryption and decryption using the public and private keys. * You can use a variety of padding methods depending upon the intended use of * encrypted data. * + * === Encryption & Decryption + * + * Asymmetric public/private key encryption is slow and victim to attack in + * cases where it is used without padding or directly to encrypt larger chunks + * of data. Typical use cases for RSA encryption involve "wrapping" a symmetric + * key with the public key of the recipient who would "unwrap" that symmetric + * key again using their private key. + * The following illustrates a simplified example of such a key transport + * scheme. It shouldn't be used in practice, though, standardized protocols + * should always be preferred. + * + * wrapped_key = key.public_encrypt key + * + * A symmetric key encrypted with the public key can only be decrypted with + * the corresponding private key of the recipient. + * + * original_key = key.private_decrypt wrapped_key + * + * By default PKCS#1 padding will be used, but it is also possible to use + * other forms of padding, see PKey::RSA for further details. + * + * === Signatures + * + * Using "private_encrypt" to encrypt some data with the private key is + * equivalent to applying a digital signature to the data. A verifying + * party may validate the signature by comparing the result of decrypting + * the signature with "public_decrypt" to the original data. However, + * OpenSSL::PKey already has methods "sign" and "verify" that handle + * digital signatures in a standardized way - "private_encrypt" and + * "public_decrypt" shouldn't be used in practice. + * + * To sign a document, a cryptographically secure hash of the document is + * computed first, which is then signed using the private key. + * + * digest = OpenSSL::Digest::SHA256.new + * signature = key.sign digest, document + * + * To validate the signature, again a hash of the document is computed and + * the signature is decrypted using the public key. The result is then + * compared to the hash just computed, if they are equal the signature was + * valid. + * + * digest = OpenSSL::Digest::SHA256.new + * if key.verify digest, signature, document + * puts 'Valid' + * else + * puts 'Invalid' + * end + * + * == PBKDF2 Password-based Encryption + * + * If supported by the underlying OpenSSL version used, Password-based + * Encryption should use the features of PKCS5. If not supported or if + * required by legacy applications, the older, less secure methods specified + * in RFC 2898 are also supported (see below). + * + * PKCS5 supports PBKDF2 as it was specified in PKCS#5 + * v2.0[http://www.rsa.com/rsalabs/node.asp?id=2127]. It still uses a + * password, a salt, and additionally a number of iterations that will + * slow the key derivation process down. The slower this is, the more work + * it requires being able to brute-force the resulting key. + * * === Encryption + * + * The strategy is to first instantiate a Cipher for encryption, and + * then to generate a random IV plus a key derived from the password + * using PBKDF2. PKCS #5 v2.0 recommends at least 8 bytes for the salt, + * the number of iterations largely depends on the hardware being used. * - * Documents encrypted with the public key can only be decrypted with the - * private key. + * cipher = OpenSSL::Cipher.new 'AES-128-CBC' + * cipher.encrypt + * iv = cipher.random_iv * - * public_encrypted = key.public_encrypt 'top secret document' + * pwd = 'some hopefully not to easily guessable password' + * salt = OpenSSL::Random.random_bytes 16 + * iter = 20000 + * key_len = cipher.key_len + * digest = OpenSSL::Digest::SHA256.new * - * Documents encrypted with the private key can only be decrypted with the - * public key. + * key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest) + * cipher.key = key * - * private_encrypted = key.private_encrypt 'public release document' + * Now encrypt the data: + * + * encrypted = cipher.update document + * encrypted << cipher.final * * === Decryption * - * Use the opposite key type do decrypt the document + * Use the same steps as before to derive the symmetric AES key, this time + * setting the Cipher up for decryption. * - * top_secret = key.public_decrypt public_encrypted + * cipher = OpenSSL::Cipher.new 'AES-128-CBC' + * cipher.decrypt + * cipher.iv = iv # the one generated with #random_iv * - * public_release = key.private_decrypt private_encrypted + * pwd = 'some hopefully not to easily guessable password' + * salt = ... # the one generated above + * iter = 20000 + * key_len = cipher.key_len + * digest = OpenSSL::Digest::SHA256.new + * + * key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest) + * cipher.key = key + * + * Now decrypt the data: + * + * decrypted = cipher.update encrypted + * decrypted << cipher.final * * == PKCS #5 Password-based Encryption * * PKCS #5 is a password-based encryption standard documented at * RFC2898[http://www.ietf.org/rfc/rfc2898.txt]. It allows a short password or - * passphrase to be used to create a secure encryption key. + * passphrase to be used to create a secure encryption key. If possible, PBKDF2 + * as described above should be used if the circumstances allow it. * * PKCS #5 uses a Cipher, a pass phrase and a salt to generate an encryption * key. @@ -529,7 +620,7 @@ ossl_debug_set(VALUE self, VALUE val) * * First set up the cipher for encryption * - * encrypter = OpenSSL::Cipher::Cipher.new 'AES-128-CBC' + * encrypter = OpenSSL::Cipher.new 'AES-128-CBC' * encrypter.encrypt * encrypter.pkcs5_keyivgen pass_phrase, salt * @@ -542,7 +633,7 @@ ossl_debug_set(VALUE self, VALUE val) * * Use a new Cipher instance set up for decryption * - * decrypter = OpenSSL::Cipher::Cipher.new 'AES-128-CBC' + * decrypter = OpenSSL::Cipher.new 'AES-128-CBC' * decrypter.decrypt * decrypter.pkcs5_keyivgen pass_phrase, salt *