验证签名链SWI-Prolog

use*_*815 3 ssl prolog pem swi-prolog

此问题与打开和检查SWI-Prolog中的Pem文件有关

下载并打开证书后,如何验证签名链?我有:

:-use_module(library(http/http_client)).

url('https://s3.amazonaws.com/echo.api/echo-api-cert-4.pem').

url_data1(Url,Certs):-
 http_open(Url,Stream,[]),
 all_certs(Stream,Certs),
 forall(member(C,Certs),my_validate(C)),
 close(Stream).

all_certs(Stream,[C1|Certs]):-
 catch(load_certificate(Stream,C1),_,fail),
 all_certs(Stream,Certs),!.
 all_certs(_Stream,[]).

my_validate(C):-
 memberchk(to_be_signed(Signed),C),
 memberchk(key(Key),C),
 memberchk(signature(Signature),C),
 memberchk(signature_algorithm(A),C),
 algo_code(A,Code),
 rsa_verify(Key,Signed,Signature,[type(Code)]).

algo_code('RSA-SHA256',sha256).
algo_code('RSA-SHA1',sha1).
Run Code Online (Sandbox Code Playgroud)

这目前失败了.

mat*_*mat 5

预赛

使用Prolog可以非常轻松地验证数字签名和整个证书链.

但是,您需要对证书的签名方式有基本的了解.甲证书链是证书序列c 0,C 1,...,C Ñ.我使用C N来表示证书.根据使用的惯例,您可以比照当然的顺序.

重要的是,产地证C ķ使用签名的私钥对应的公钥的C ķ +1.

因此,您的代码的一个问题是您错误地使用C的公钥来验证C的签名,即使证书是使用与不同证书对应的私钥签名的.

一个不同的问题约一些混乱茎什么被签署.我们签署哈希的的待签名的证书,而不是数据本身的一部分.因此,我们必须针对该哈希验证签名.

具体例子

为了使这个答案自成一体,我在这里发布你的用例中的相关数据,即在撰写本文时文件包含的证书的相关属性.

数据

第一证书

从链中的第一个证书,我们需要签名待签名部分,它们是:

signature("7B86A6E7A86B192579380108B7EADA1C25E288AB46120117DC6A80635324C89713C70206EF3FE13CCA5EDFF43601972CB96658826ADCD68B0FB3AE7F5607D036D6AE8AF824CD96D7F1B4DB58E714343031F292B17E6EEF83872A0FD586CC0D1DA85A677E4AB4C6540E9132B5BC5644533E0388F830B1B6757B7DB88AB82846F08B3B6DCEC7F24319AB7EA56F86592DDDEC4522CAF331C8B81A4E543FBDBF4D661B534BAE546465DB88A525BC82A7B4127F0AFF4A55525927A66A09055743F109E30D90CA074D258166F0E472CB7CCDB0747ADE74F7040CFEEB9A78C3483864C5106D542556C874AF768005A6EC83ADEB2EE32F8E6F7182A362775C2BF40AFA20").
to_be_signed("30820466A00302010202103F25CAAEE5AA838FA91C051A75FC719D300D06092A864886F70D01010B0500307E310B3009060355040613025553311D301B060355040A131453796D616E74656320436F72706F726174696F6E311F301D060355040B131653796D616E746563205472757374204E6574776F726B312F302D0603550403132653796D616E74656320436C61737320332053656375726520536572766572204341202D204734301E170D3136313030373030303030305A170D3137313033303233353935395A306D310B30090603550406130255533113301106035504080C0A57617368696E67746F6E3110300E06035504070C0753656174746C6531193017060355040A0C10416D617A6F6E2E636F6D2C20496E632E311C301A06035504030C136563686F2D6170692E616D617A6F6E2E636F6D30820122300D06092A864886F70D01010105000382010F003082010A02820101009CAFB306BB910354E76E0406C44F9BE178934D9C83906C09EB4EBC006B1742682DF655610BC0934C2E30751E4D5E8B1BA15EBFEB7C28AD008DA38D7672C0558D4CB71F5FD512CFB92AFF80880394B8AA017C453CCC0BC709CEC698E29480D89D703034312A71DD94CC48619B91F68B8A44739DEEA7159EA334E9E4A93B460FA4AB0886202CD02B49C6283F321C5C4CA91C5AE8827CEB47811ED1871E7C66724BCD58A9EBFF9658B4D5D02046FA6702DCBAF2B6139B190D8735121BA2086C51C8C5724A0044C090688A25C819A5F1B6D4E9390E4DF21AB11263F203E4E9F1BCCC625D29D7C21B7C243E9B775E6E8B4B4F0DAF390748E964B968A9065EDBAA11A30203010001A382020730820203301E0603551D110417301582136563686F2D6170692E616D617A6F6E2E636F6D30090603551D1304023000300E0603551D0F0101FF0404030205A0301D0603551D250416301406082B0601050507030106082B0601050507030230610603551D20045A30583056060667810C010202304C302306082B06010505070201161768747470733A2F2F642E73796D63622E636F6D2F637073302506082B0601050507020230190C1768747470733A2F2F642E73796D63622E636F6D2F727061301F0603551D230418301680145F60CF619055DF8443148A602AB2F57AF44318EF302B0603551D1F042430223020A01EA01C861A687474703A2F2F73732E73796D63622E636F6D2F73732E63726C305706082B06010505070101044B3049301F06082B060105050730018613687474703A2F2F73732E73796D63642E636F6D302606082B06010505073002861A687474703A2F2F73732E73796D63622E636F6D2F73732E637274300F06032B654D0408300602010102010130818B060A2B06010401D679020402047D047B0079007700A7CE4A4E6207E0ADDEE5FDAA4B1F86768767B5D002A55D47310E7E670A95EAB2000001579FAA404A0000040300483046022100CA6B7C069C140B774E8D64ED95D4A59E92349E04AA3C71B304F32C74BC857F81022100FA152CF8558BA0A87B515AB7522D1BE1725740E128042309DD274FEEA5EB3B62").

第二证书

从第二个证书中,我们只需要公钥来验证为以前的证书颁发的签名:

key(public_key(rsa("B2D805CA1C742DB5175639C54A520996E84BD80CF1689F9A422862C3A530537E5511825B037A0D2FE17904C9B496771981019459F9BCF77A9927822DB783DD5A277FB2037A9C5325E9481F464FC89D29F8BE7956F6F7FDD93A68DA8B4B82334112C3C83CCCD6967A84211A22040327178B1C6861930F0E5180331DB4B5CEEB7ED062ACEEB37B0174EF6935EBCAD53DA9EE9798CA8DAA440E25994A1596A4CE6D02541F2A6A26E2063A6348ACB44CD1759350FF132FD6DAE1C618F59FC9255DF3003ADE264DB42909CD0F3D236F164A8116FBF28310C3B8D6D855323DF1BD0FBD8C52954A16977A522163752F16F9C466BEF5B509D8FF2700CD447C6F4B3FB0F7", "010001", -, -, -, -, -, -))).

验证

计算哈希值

正如我所说,哈希是实际签名的.关键的是,我不是指整个证书的哈希值,而是指待签名部分的哈希值.这种差异很重要,因为整个证书的哈希值也包含签名,当然,在签署证书时,该签名当然不可用.

在SWI-Prolog中,我们可以使用以下方法获取待签名部分的哈希值library(crypto):

?- to_be_signed(TBS),
   hex_bytes(TBS, Bytes),
   crypto_data_hash(Bytes, Hash, [algorithm(sha256), encoding(octet)]).
TBS = "3082...EB3B62",
Bytes = [48, 130, 4, 102, 160, 3, 2, 1, 2|...],
Hash = '651bdcdd90251f71a47a5d1bbc6f28486c94d2dc3739dcd58ecb09b3f224ee05'.

我正在使用,sha256因为第一个证书在其字段中指示(RSA和).SHA256signature_algorithm/1

使用CLP(FD)约束验证签名

验证RSA签名的最简单方法之一是使用CLP(FD)约束.我们只需要计算Sig Exp  mod  p.我们插入我们的具体数字,(#=)/2用于评估整数的算术表达式:

?- X #= 0x7B86A6E7A86B192579380108B7EADA1C25E288AB46120117DC6A80635324C89713C70206EF3FE13CCA5EDFF43601972CB96658826ADCD68B0FB3AE7F5607D036D6AE8AF824CD96D7F1B4DB58E714343031F292B17E6EEF83872A0FD586CC0D1DA85A677E4AB4C6540E9132B5BC5644533E0388F830B1B6757B7DB88AB82846F08B3B6DCEC7F24319AB7EA56F86592DDDEC4522CAF331C8B81A4E543FBDBF4D661B534BAE546465DB88A525BC82A7B4127F0AFF4A55525927A66A09055743F109E30D90CA074D258166F0E472CB7CCDB0747ADE74F7040CFEEB9A78C3483864C5106D542556C874AF768005A6EC83ADEB2EE32F8E6F7182A362775C2BF40AFA20^0x010001
        mod 0xB2D805CA1C742DB5175639C54A520996E84BD80CF1689F9A422862C3A530537E5511825B037A0D2FE17904C9B496771981019459F9BCF77A9927822DB783DD5A277FB2037A9C5325E9481F464FC89D29F8BE7956F6F7FDD93A68DA8B4B82334112C3C83CCCD6967A84211A22040327178B1C6861930F0E5180331DB4B5CEEB7ED062ACEEB37B0174EF6935EBCAD53DA9EE9798CA8DAA440E25994A1596A4CE6D02541F2A6A26E2063A6348ACB44CD1759350FF132FD6DAE1C618F59FC9255DF3003ADE264DB42909CD0F3D236F164A8116FBF28310C3B8D6D855323DF1BD0FBD8C52954A16977A522163752F16F9C466BEF5B509D8FF2700CD447C6F4B3FB0F7.

它产生:

X = 986236757547332986472011617696226561292849812918563355472727826767720188564083584387121625107510786855734801053524719833194566624465665316622563244215340671405971599343902468620306327831715457360719532421388780770165778156818229863337344187575566725786793391480600129482653072861971002459947277805295727097226389568776499707662505334062639449916265137796823793276300221537201727072401742985542559596685092673521228140822200236743113743661549252453726123450722876929538747702356573783116197523966334991563351853851212597377279504828784716104866621888265058037501385433453379649364782998949981722124880992983641605.

游览:关于CLP(FD)的效率和使用.

你现在可以说:"好吧,我真的不需要(#=)/2,我可以随时使用(is)/2我几十年前学过的东西." 但是,如果您(is)/2在这样的示例中使用,您很容易得到效率低几千倍的代码.作为一个简单的基准,请考虑谓词:

signature_pow(Sig, Exp, P, Pow) :-
        Pow #= Sig^Exp mod P.

现在我们有了查询:

?- time(signature_pow(0x7B86A6E7A86B192579380108B7EADA1C25E288AB46120117DC6A80635324C89713C70206EF3FE13CCA5EDFF43601972CB96658826ADCD68B0FB3AE7F5607D036D6AE8AF824CD96D7F1B4DB58E714343031F292B17E6EEF83872A0FD586CC0D1DA85A677E4AB4C6540E9132B5BC5644533E0388F830B1B6757B7DB88AB82846F08B3B6DCEC7F24319AB7EA56F86592DDDEC4522CAF331C8B81A4E543FBDBF4D661B534BAE546465DB88A525BC82A7B4127F0AFF4A55525927A66A09055743F109E30D90CA074D258166F0E472CB7CCDB0747ADE74F7040CFEEB9A78C3483864C5106D542556C874AF768005A6EC83ADEB2EE32F8E6F7182A362775C2BF40AFA20, 0x010001, 0xB2D805CA1C742DB5175639C54A520996E84BD80CF1689F9A422862C3A530537E5511825B037A0D2FE17904C9B496771981019459F9BCF77A9927822DB783DD5A277FB2037A9C5325E9481F464FC89D29F8BE7956F6F7FDD93A68DA8B4B82334112C3C83CCCD6967A84211A22040327178B1C6861930F0E5180331DB4B5CEEB7ED062ACEEB37B0174EF6935EBCAD53DA9EE9798CA8DAA440E25994A1596A4CE6D02541F2A6A26E2063A6348ACB44CD1759350FF132FD6DAE1C618F59FC9255DF3003ADE264DB42909CD0F3D236F164A8116FBF28310C3B8D6D855323DF1BD0FBD8C52954A16977A522163752F16F9C466BEF5B509D8FF2700CD447C6F4B3FB0F7, Pow)).

时机:

% 16 inferences, 0.000 CPU in 0.000 seconds (99% CPU, 130624 Lips)

相反,如果我们在Prolog语言开发中回归并替换(#=)/2(is)/2,我们得到:

% 3 inferences, 1.847 CPU in 1.852 seconds (100% CPU, 2 Lips)

原因:在SWI-Prolog中,某些目标涉及(#=)/2 自动使用专门的算术谓词.您不需要学习这些谓词来使用它们.CLP(FD)为您服务.

建议:使用CLP(FD)约束来推理Prolog中的整数.他们通常让您的谓词更普遍,有时大大更有效率. 


现在,怎么样X?要查看它是什么,请考虑其十六进制编码:

?- format("~16r", [$X]).
 1fffffff...fff003031300d060960864801650304020105000420651bdcdd90251f71a47a5d1bbc6f28486c94d2dc3739dcd58ecb09b3f224ee05

这听起来很熟悉:最后,你看到的哈希的的待签名出现在证书的一部分.这意味着签名结账!

用.验证签名 rsa_verify/4

或者,我们可以使用rsa_verify/4from library(crypto)来验证签名.

这是完整的查询:

?- to_be_signed(TBS),
   hex_bytes(TBS, Bytes),
   crypto_data_hash(Bytes, Hash, [algorithm(sha256), encoding(octet)]),
   signature(Sig),
   key(Key),
   rsa_verify(Key, Hash, Sig, [type(sha256)]).

由于这成功,我们知道对应  的私钥Key用于生成签名.

闭幕致辞

我有一个重要的评论:通常,这当然是完全没必要的!

SWI-Prolog SSL基础结构会自动验证证书链,从而每次使用时都会签署所有签名,http_open/3并通过TLS建立相关谓词.但是自己进行这些计算很有意思.有时甚至是必要的,如果在这个例子中,你是在推理你存储在某个地方的证书.

一句小话:请setup_call_cleanup/3在您的代码中使用.否则,如果之前出现任何问题,您可能会泄漏文件描述符close/1,实际上甚至在您的示例中也是如此.