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)
}
Last moify: 2022-12-04 15:11:33
Build time:2025-07-18 09:41:42
Powered By asphinx