查看源代码 新旧 API

本章介绍加密和解密的新 API。

背景

CRYPTO 应用在其生命周期中不断发展。由于 OpenSSL 加密库也多次更改了 API,CRYPTO 应用的部分内部使用非常旧的 API,而其他部分则使用最新的 API。例如,密码名称的内部定义有点难以维护。

事实证明,以新的方式使用旧的 API(稍后会详细介绍),并仍然保持向后兼容是不可能的。特别是因为需要更精确的错误消息,所以它不能与旧标准相结合。

因此,旧的 API(见下一节)暂时保留,但内部使用新的原语实现。

旧的 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

新的 API

加密和解密

用于加密或解密单个二进制文件的新函数是

在这些函数中,首先创建内部加密状态,并使用密码类型、密钥以及可能的其他数据进行初始化。然后,对单个二进制文件进行加密或解密,释放加密状态,并返回加密操作的结果。

crypto_one_time_aead 函数用于模式为 ccmgcm 的密码,以及用于密码 chacha20-poly1305

对于重复加密或解密分成多个部分的文本,其中内部加密状态初始化一次,然后使用相同的状态加密或解密多个二进制文件,则使用以下函数

crypto_init 初始化内部密码状态,并且一次或多次调用 crypto_update 执行实际的加密或解密。请注意,由于其性质,AEAD 密码无法以这种方式处理。

一个需要这些函数的示例是处理 TLS 协议时。

如果未启用填充,则可以省略对 crypto_final/1 的调用。

有关可用算法的信息,请使用

由于 crypto_initcrypto_update 包括此功能,因此不需要 next_iv/2next_iv/3

MAC(消息身份验证码)

用于计算单个文本的 MAC 的新函数是

对于计算分成多个部分的文本的 MAC,请使用

新 API 的示例

crypto_init/4 和 crypto_update/2 的示例

函数 crypto_init/4crypto_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>

请注意,StateEncStateDec 引用的内部数据会被调用 crypto_update/2 破坏性地更新。这是为了在与加密库接口的 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 additional auth data">>.
	<<"Some additional auth data">>
	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>

已弃用的密码名称

此表列出了第一列中已弃用的密码名称,并建议在第二列中使用名称替换它们。

新名称遵循 OpenSSL libcrypto 名称。格式为 ALGORITM_KEYSIZE_MODE。

算法示例包括 aes、chacha20 和 des。密钥大小是位数,模式示例包括 cbc、ctr 和 gcm。模式后面可能会跟一个数字,具体取决于模式。一个示例是 ccm 模式,它有一个名为 ccm8 的变体,其中所谓的标签的长度为 8 位。

旧的名称随着时间的推移失去了任何通用的命名约定,而新名称现在引入了这种约定。新名称包括密钥长度,这提高了加密应用程序较低级别的错误检查。

而不是使用
aes_cbc128aes_128_cbc
aes_cbc256aes_256_cbc
aes_cbcaes_128_cbc, aes_192_cbc, aes_256_cbc
aes_ccmaes_128_ccm, aes_192_ccm, aes_256_ccm
aes_cfb128aes_128_cfb128, aes_192_cfb128, aes_256_cfb128
aes_cfb8aes_128_cfb8, aes_192_cfb8, aes_256_cfb8
aes_ctraes_128_ctr, aes_192_ctr, aes_256_ctr
aes_gcmaes_128_gcm, aes_192_gcm, aes_256_gcm
des3_cbcdes_ede3_cbc
des3_cbfdes_ede3_cfb
des3_cfbdes_ede3_cfb
des_ede3des_ede3_cbc
des_ede3_cbfdes_ede3_cfb