6 新旧 API
本章介绍加密和解密的新 API。
6.1 背景
CRYPTO 应用程序在其生命周期中不断发展。由于 OpenSSL cryptolib 的 API 也多次更改,CRYPTO 应用程序中的一些部分在内部使用的是非常旧的 API,而另一些部分则使用的是最新的 API。例如,密码名称的内部定义很难维护。
事实证明,以新的方式使用旧 API(稍后将详细介绍),同时保持向后兼容是不可能的。特别是,由于需要在错误消息中提供更多精度,因此无法将其与旧标准结合使用。
因此,旧 API(见下一节)目前保留,但内部使用新的原语实现。
6.2 旧 API
旧函数(从 23.0 开始弃用,从 OTP 24.0 开始删除)用于密码
- block_encrypt/3
- block_encrypt/4
- block_decrypt/3
- block_decrypt/4
- stream_init/2
- stream_init/3
- stream_encrypt/2
- stream_decrypt/2
- next_iv/2
- next_iv/3
用于支持的算法列表
- supports/0
以及用于 MAC(消息认证码)
- cmac/3
- cmac/4
- hmac/3
- hmac/4
- hmac_init/2
- hmac_update/2
- hmac_final/1
- hmac_final_n/2
- poly1305/2
6.3 新 API
加密和解密
用于加密或解密单个二进制文件的新的函数是
在这些函数中,内部加密状态首先创建并使用密码类型、密钥以及可能的其他数据进行初始化。然后加密或解密单个二进制文件,释放加密状态,并返回加密操作的结果。
crypto_one_time_aead 函数用于模式为 ccm 或 gcm 的密码,以及密码 chacha20-poly1305。
对于重复加密或解密文本(文本被分成多个部分),内部加密状态仅初始化一次,然后使用相同的状态加密或解密多个二进制文件,函数是
crypto_init 初始化内部密码状态,一个或多个 crypto_update 调用执行实际的加密或解密。请注意,由于 AEAD 密码的性质,无法以这种方式处理它们。
对于重复加密或解密文本(文本被分成多个部分),使用相同的密码和密钥,但每个部分都应应用一个新的初始化向量(nonce),函数是
这些函数的应用场景示例是处理 TLS 协议。
如果未启用填充,则可以省略对 crypto_final/1 的调用。
有关可用算法的信息,请使用
next_iv/2 和 next_iv/3 不需要,因为 crypto_init 和 crypto_update 包含此功能。
MAC(消息认证码)
用于计算单个文本段的 MAC 的新函数是
对于计算文本(文本被分成多个部分)的 MAC,请使用
6.4 新 API 的示例
crypto_init/4 和 crypto_update/2 的示例
函数 crypto_init/4 和 crypto_update/2 用于加密或解密一系列块。首先, crypto_init/4 的一个调用初始化加密上下文。一个或多个 crypto_update/2 调用对每个块执行实际的加密或解密。
此示例首先显示了对两个块的加密,然后是对密文的解密,但将其分成三个块,只是为了说明对于某些密码,可以对明文和密文进行不同的划分。
1> crypto:start(). ok 2> Key = <<1:128>>. <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1>> 3> IV = <<0:128>>. <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>> 4> StateEnc = crypto:crypto_init(aes_128_ctr, Key, IV, true). % encrypt -> true #Ref<0.3768901617.1128660993.124047> 5> crypto:crypto_update(StateEnc, <<"First bytes">>). <<67,44,216,166,25,130,203,5,66,6,162>> 6> crypto:crypto_update(StateEnc, <<"Second bytes">>). <<16,79,94,115,234,197,94,253,16,144,151,41>> 7> 7> StateDec = crypto:crypto_init(aes_128_ctr, Key, IV, false). % decrypt -> false #Ref<0.3768901617.1128660994.124255> 8> crypto:crypto_update(StateDec, <<67,44,216,166,25,130,203>>). <<"First b">> 9> crypto:crypto_update(StateDec, <<5,66,6,162,16,79,94,115,234,197, 94,253,16,144,151>>). <<"ytesSecond byte">> 10> crypto:crypto_update(StateDec, <<41>>). <<"s">> 11>
请注意, StateEnc 和 StateDec 引用的内部数据会受到对 crypto_update/2 的调用的破坏性更新。这样做是为了在调用与 cryptolib 交互的 nif 时节省时间。在循环中,如果状态保存在循环状态中,则每次加密操作也会节省一次循环状态更新。
例如,一个简单的服务器接收要加密的文本部分,并将结果发送回发送者(Requester)
encode(Crypto, Key, IV) -> crypto_loop(crypto:crypto_init(Crypto, Key, IV, true)). crypto_loop(State) -> receive {Text, Requester} -> Requester ! crypto:crypto_update(State, Text), loop(State) end.
crypto_one_time/5 的示例
与 上一节 相同的示例,但现在使用 crypto_one_time/5 调用一次。
1> Key = <<1:128>>. <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1>> 2> IV = <<0:128>>. <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>> 3> Txt = [<<"First bytes">>,<<"Second bytes">>]. [<<"First bytes">>,<<"Second bytes">>] 4> crypto:crypto_one_time(aes_128_ctr, Key, IV, Txt, true). <<67,44,216,166,25,130,203,5,66,6,162,16,79,94,115,234, 197,94,253,16,144,151,41>> 5>
[<<"First bytes">>,<<"Second bytes">>] 当然可以是一个二进制文件: <<"First bytesSecond bytes">>。
crypto_one_time_aead/6 的示例
与 上一节 相同的示例,但现在使用 crypto_one_time_aead/6 调用一次。
1> Key = <<1:128>>. <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1>> 2> IV = <<0:128>>. <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>> 3> Txt = [<<"First bytes">>,<<"Second bytes">>]. [<<"First bytes">>,<<"Second bytes">>] 4> AAD = <<"Some bytes">>. <<"Some bytes">> 5> crypto:crypto_one_time_aead(aes_128_gcm, Key, IV, Txt, AAD, true). {<<240,130,38,96,130,241,189,52,3,190,179,213,132,1,72, 192,103,176,90,104,15,71,158>>, <<131,47,45,91,142,85,9,244,21,141,214,71,31,135,2,155>>} 6>
[<<"First bytes">>,<<"Second bytes">>] 当然可以是一个二进制文件: <<"First bytesSecond bytes">>。
mac_init mac_update 和 mac_final 的示例
1> Key = <<1:128>>. <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1>> 2> StateMac = crypto:mac_init(cmac, aes_128_cbc, Key). #Ref<0.2424664121.2781478916.232610> 3> crypto:mac_update(StateMac, <<"First bytes">>). #Ref<0.2424664121.2781478916.232610> 4> crypto:mac_update(StateMac, " "). #Ref<0.2424664121.2781478916.232610> 5> crypto:mac_update(StateMac, <<"last bytes">>). #Ref<0.2424664121.2781478916.232610> 6> crypto:mac_final(StateMac). <<68,191,219,128,84,77,11,193,197,238,107,6,214,141,160, 249>> 7>
并比较结果与仅针对此示例的单个计算。
7> crypto:mac(cmac, aes_128_cbc, Key, "First bytes last bytes"). <<68,191,219,128,84,77,11,193,197,238,107,6,214,141,160, 249>> 8> v(7) == v(6). true 9>
6.5 已弃用的密码名称
此表列出了第一列中已弃用的密码名称,并在第二列中建议用它们替换的名称。
新的名称遵循 OpenSSL libcrypto 名称。格式为 ALGORITM_KEYSIZE_MODE。
算法示例包括 aes、chacha20 和 des。密钥大小是比特数,模式示例包括 cbc、ctr 和 gcm。模式后可能跟着一个数字,具体取决于模式。例如,ccm 模式有一个称为 ccm8 的变体,其中所谓的标签长度为 8 位。
旧名称随着时间的推移失去了任何常见的命名约定,而新名称现在引入了这种约定。新名称包含密钥长度,这提高了密码应用程序较低级别的错误检查。
不要使用 | 使用 |
aes_cbc128 | aes_128_cbc |
aes_cbc256 | aes_256_cbc |
aes_cbc | aes_128_cbc, aes_192_cbc, aes_256_cbc |
aes_ccm | aes_128_ccm, aes_192_ccm, aes_256_ccm |
aes_cfb128 | aes_128_cfb128, aes_192_cfb128, aes_256_cfb128 |
aes_cfb8 | aes_128_cfb8, aes_192_cfb8, aes_256_cfb8 |
aes_ctr | aes_128_ctr, aes_192_ctr, aes_256_ctr |
aes_gcm | aes_128_gcm, aes_192_gcm, aes_256_gcm |
des3_cbc | des_ede3_cbc |
des3_cbf | des_ede3_cfb |
des3_cfb | des_ede3_cfb |
des_ede3 | des_ede3_cbc |
des_ede3_cbf | des_ede3_cfb |