公鑰加密,或者非對稱加密,它需要兩個密鑰,一個是公開密鑰,另一個是私有密鑰;一個用作加密,另一個則用作解密。使用其中一個密鑰把明文加密後所得的密文,只能用相對應的另一個密鑰才能解密得到原本的明文。雖然兩個密鑰在數學上相關,但如果知道了其中一個,並不能憑此計算出另外一個。這些密鑰的生成依賴於基於數學問題的密碼算法來生成單向函數,如果要確保安全,那麼只需要保密私鑰,公鑰可以在不影響安全性的情況下公開分發。
在這樣的系統中,任何人都可以使用接收者的公鑰加密消息,但是加密的消息只能用接收者的私鑰解密。基於公開密鑰加密的特性,它還提供數字簽名的功能。本文主要介紹公鑰加密的功能,而數字簽名會在後面的文章介紹。
RSA
RSA
加密是RSA Data Security
開發的公鑰加密技術。RSA
算法基於分解非常大的數字的難度。基於此原理,RSA
加密算法使用素數分解作爲加密的陷阱門,換言之,對一極大整數做因數分解愈困難,RSA
算法愈可靠。因此,推導RSA
密鑰需要大量的時間和處理能力。RSA
是重要數據的標準加密方法,尤其是通過Internet
傳輸的數據。RSA
代表該技術的創造者,Rivest
,Shamir
和Adelman
。
加密和解密:OpenSSL
命令行
由於RSA
算法的特性,因此一般不加密大型文件。RSA
加密的最有效使用是使用公鑰加密隨機生成的對稱密碼,然後使用對稱加密密碼加密文件。然後我們將加密文件和加密密鑰發送給另一方,然後可以用他們的私鑰解密密鑰,使用該密鑰解密大文件。
使用RSA
密鑰時,以下命令是相關的:
openssl genrsa
:生成RSA
私鑰。openssl rsa
:管理RSA
私鑰(包括從中生成公鑰)。openssl rsautl
:使用RSA
密鑰加密和解密文件。
生成RSA
私鑰:
openssl genrsa -out privatekey.pem
根據私鑰生成證書籤名請求:
openssl req -new -key privatekey.pem -out csr.pem
如果是自己做測試,那麼證書的申請機構和頒發機構都是自己。就可以用下面這個命令來生成證書certificate.pem
:
openssl req -new -x509 -key privatekey.pem -out certificate.pem -days 1095
把certificate.pem
發送給對方,對方使用如下命令獲取公鑰:
openssl rsa -in certificate.pem -out publickey.pem -outform PEM -pubout
生成隨機密鑰:
openssl rand -hex 64 -out key.bin
使用隨機密鑰加密大文件:
openssl enc -aes-256-cbc -salt -in largefile.pdf -out largefile.pdf.enc -pass file:./bin.key
使用公鑰文件加密隨機密鑰:
openssl rsautl -encrypt -inkey publickey.pem -pubin -in key.bin -out key.bin.enc
對方可以安全地發送key.bin.enc
和largefile.pdf.enc
給你。你需要用以下命令和私鑰來解密隨機密鑰:
openssl rsautl -decrypt -inkey privatekey.pem -in key.bin.enc -out key.bin
獲得隨機密鑰後,可以使用解密密鑰解密加密文件:
openssl enc -d -aes-256-cbc -in largefile.pdf.enc -out largefile.pdf -pass file:./bin.key
使用OpenSSL API
加密和解密
使用非對稱密鑰的加密和解密在計算上是非常“昂貴”的。通常,消息不是使用這樣的密鑰直接加密,而是使用對稱的“會話”密鑰加密。然後使用公鑰對該密鑰進行加密。在OpenSSL
中,這種組合稱爲信封(envelope
)。
加密信封
使用EVP_Seal*
函數集密封信封,操作包括以下步驟:
- 初始化上下文
- 初始化密封操作,提供將要使用的對稱密碼,以及用於加密會話密鑰的公鑰集。
- 提供要加密的消息。
- 完成加密操作
下面是示例代碼:
int envelope_seal(EVP_PKEY **pub_key, unsigned char *plaintext, int plaintext_len,
unsigned char **encrypted_key, int *encrypted_key_len, unsigned char *iv,
unsigned char *ciphertext)
{
EVP_CIPHER_CTX *ctx;
int ciphertext_len;
int len;
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
/* Initialise the envelope seal operation. This operation generates
* a key for the provided cipher, and then encrypts that key a number
* of times (one for each public key provided in the pub_key array). In
* this example the array size is just one. This operation also
* generates an IV and places it in iv. */
if(1 != EVP_SealInit(ctx, EVP_aes_256_cbc(), encrypted_key,
encrypted_key_len, iv, pub_key, 1))
handleErrors();
/* Provide the message to be encrypted, and obtain the encrypted output.
* EVP_SealUpdate can be called multiple times if necessary
*/
if(1 != EVP_SealUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
handleErrors();
ciphertext_len = len;
/* Finalise the encryption. Further ciphertext bytes may be written at
* this stage.
*/
if(1 != EVP_SealFinal(ctx, ciphertext + len, &len)) handleErrors();
ciphertext_len += len;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return ciphertext_len;
}
解密信封
使用以下步驟中的EVP_Open*
函數集打開信封
- 初始化上下文
- 初始化打開操作,提供已使用的對稱密碼,以及用於解密會話密鑰的私鑰。
- 使用會話密鑰提供要解密和解密的消息
- 完成解密操作
下面是示例代碼:
int envelope_open(EVP_PKEY *priv_key, unsigned char *ciphertext, int ciphertext_len,
unsigned char *encrypted_key, int encrypted_key_len, unsigned char *iv,
unsigned char *plaintext)
{
EVP_CIPHER_CTX *ctx;
int len;
int plaintext_len;
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
/* Initialise the decryption operation. The asymmetric private key is
* provided and priv_key, whilst the encrypted session key is held in
* encrypted_key */
if(1 != EVP_OpenInit(ctx, EVP_aes_256_cbc(), encrypted_key,
encrypted_key_len, iv, priv_key))
handleErrors();
/* Provide the message to be decrypted, and obtain the plaintext output.
* EVP_OpenUpdate can be called multiple times if necessary
*/
if(1 != EVP_OpenUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
handleErrors();
plaintext_len = len;
/* Finalise the decryption. Further plaintext bytes may be written at
* this stage.
*/
if(1 != EVP_OpenFinal(ctx, plaintext + len, &len)) handleErrors();
plaintext_len += len;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return plaintext_len;
}