EVP 认证加密
EVP 认证加密 支持经过身份验证的数据加密和解密、将未加密数据附加到消息中两种功能。这种带有相关数据的认证加密(AEAD, Authenticated-Encryption with Associated-Data) 方案通过加密提供数据的机密性,然后通过在加密数据上附带 MAC(Message Authentication Code) 的方式的方式确保数据未被篡改。
AEAD 模式有很多种,和普通的加密基本相同,需要了解以下信息:
算法:目前只支持 AES。
模式:目前只支持 GCM 和 CCM。
密钥
初始向量 IV
除此之外还可以提供一些未加密的额外认证数据(AAD, Additional Authenticated Data) 。AAD 会随着密文以明文的形式传递给接受者。
加密操作的输出是一个密文和一个标签,标签会在之后的解密操作中使用。
GMAC 是 EVP GCM 的一个特殊模式,GMAC 不提供数据加密,只提供数据认证,因此提供的明文为空,而实际数据以 AAD 的形式附加到数据中。 |
Details
GCM 加密操作:
int gcm_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *aad, int aad_len, unsigned char *key, unsigned char *iv, int iv_len, unsigned char *ciphertext, unsigned char *tag) {
EVP_CIPHER_CTX *ctx;
int len;
int ciphertext_len;
/* Create and initialise the context */
if (!(ctx = EVP_CIPHER_CTX_new()))
handleErrors();
/* Initialise the encryption operation. */
if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
handleErrors();
/*
* Set IV length if default 12 bytes (96 bits) is not appropriate
*/
if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv_len, NULL))
handleErrors();
/* Initialise key and IV */
if (1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv))
handleErrors();
/*
* Provide any AAD data. This can be called zero or more times as
* required
*/
if (1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len))
handleErrors();
/*
* Provide the message to be encrypted, and obtain the encrypted output.
* EVP_EncryptUpdate can be called multiple times if necessary
*/
if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
handleErrors();
ciphertext_len = len;
/*
* Finalise the encryption. Normally ciphertext bytes may be written at
* this stage, but this does not occur in GCM mode
*/
if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len))
handleErrors();
ciphertext_len += len;
/* Get the tag */
if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag))
handleErrors();
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return ciphertext_len;
}
对于 rust 而言,此操作变为:
/// `cipertext`: if cipertext is None, then is algo become aes_gmac.
pub fn aes_gcm_encode(
plaintext: &[u8],
key: &[u8],
iv: &[u8],
tag: &mut [u8], // 16B
cipertext: Option<&mut [u8]>,
) -> Result<usize, ()> {
let mut ctx = CipherCtx::new().unwrap();
ctx.encrypt_init(Some(Cipher::aes_256_gcm()), None, None)
.unwrap();
ctx.set_iv_length(iv.len());
ctx.encrypt_init(None, Some(key), Some(iv)).unwrap();
let len = ctx.cipher_update(plaintext, cipertext).unwrap();
let mut buf = [0u8; 0];
ctx.cipher_final(&mut buf).unwrap();
ctx.tag(tag).unwrap();
Ok(len)
}
类似的,对于解密操作变为:
/// `plaintext`: if plaintext is None, then this algo become aes_gmac.
pub fn aes_gcm_decode(
cipertext: &[u8],
key: &[u8],
iv: &[u8],
tag: &[u8],
plaintext: Option<&mut [u8]>,
) -> Result<usize, ()> {
let mut ctx = CipherCtx::new().unwrap();
ctx.decrypt_init(Some(Cipher::aes_256_gcm()), None, None)
.unwrap();
ctx.set_iv_length(iv.len());
ctx.decrypt_init(None, Some(key), Some(iv));
let len = ctx.cipher_update(cipertext, plaintext).unwrap();
ctx.set_tag(tag).unwrap();
let mut buf = [0u8; 0];
ctx.cipher_final(&mut buf).unwrap();
Ok(len)
}