密碼學(三):公鑰加密和RSA

公鑰加密,或者非對稱加密,它需要兩個密鑰,一個是公開密鑰,另一個是私有密鑰;一個用作加密,另一個則用作解密。使用其中一個密鑰把明文加密後所得的密文,只能用相對應的另一個密鑰才能解密得到原本的明文。雖然兩個密鑰在數學上相關,但如果知道了其中一個,並不能憑此計算出另外一個。這些密鑰的生成依賴於基於數學問題的密碼算法來生成單向函數,如果要確保安全,那麼只需要保密私鑰,公鑰可以在不影響安全性的情況下公開分發。

在這樣的系統中,任何人都可以使用接收者的公鑰加密消息,但是加密的消息只能用接收者的私鑰解密。基於公開密鑰加密的特性,它還提供數字簽名的功能。本文主要介紹公鑰加密的功能,而數字簽名會在後面的文章介紹。

RSA

RSA加密是RSA Data Security開發的公鑰加密技術。RSA算法基於分解非常大的數字的難度。基於此原理,RSA加密算法使用素數分解作爲加密的陷阱門,換言之,對一極大整數做因數分解愈困難,RSA算法愈可靠。因此,推導RSA密鑰需要大量的時間和處理能力。RSA是重要數據的標準加密方法,尤其是通過Internet傳輸的數據。RSA代表該技術的創造者,RivestShamirAdelman

加密和解密: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.enclargefile.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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章