验证签名链 SWI-Prolog [英] Verifying a signature chain SWI-Prolog

查看:83
本文介绍了验证签名链 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(网址,证书):-http_open(Url,Stream,[]),all_certs(Stream,Certs),forall(member(C,Certs),my_validate(C)),关闭(流).all_certs(Stream,[C1|Certs]):-捕获(负载证书(流,C1),_,失败),all_certs(Stream,Certs),!.all_certs(_Stream,[]).my_validate(C):-memberchk(to_be_signed(Signed),C),memberchk(key(Key),C),成员chk(签名(签名),C),memberchk(signature_algorithm(A),C),算法代码(A,代码),rsa_verify(Key,Signed,Signature,[type(Code)]).算法代码('RSA-SHA256',sha256).算法代码('RSA-SHA1',sha1).

这目前失败了.

解决方案

Preliminaries

使用 Prolog 验证数字签名和整个证书链非常容易.

但是,您需要对证书的签名方式有基本的了解.证书链是证书序列 C0、C1、...、CN.我使用 CN 来表示 root 证书.根据所使用的约定,您可以mutatis mutandis当然可以颠倒顺序.

重要的是,证书 Ck 是使用与 Ck公钥对应的私钥签名的+1.

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

一个不同的问题源于对什么正在签署的一些混淆.我们正在签署证书的待签署部分的哈希,而不是数据本身.因此,我们必须根据该哈希验证签名.

具体例子

为了使这个答案独立,我在此处发布了您的用例中的相关数据,即在撰写本文时文件包含的证书的相关属性.

数据

第一个证书

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

<预>签名( 7B86A6E7A86B192579380108B7EADA1C25E288AB46120117DC6A80635324C89713C70206EF3FE13CCA5EDFF43601972CB96658826ADCD68B0FB3AE7F5607D036D6AE8AF824CD96D7F1B4DB58E714343031F292B17E6EEF83872A0FD586CC0D1DA85A677E4AB4C6540E9132B5BC5644533E0388F830B1B6757B7DB88AB82846F08B3B6DCEC7F24319AB7EA56F86592DDDEC4522CAF331C8B81A4E543FBDBF4D661B534BAE546465DB88A525BC82A7B4127F0AFF4A55525927A66A09055743F109E30D90CA074D258166F0E472CB7CCDB0747ADE74F7040CFEEB9A78C3483864C5106D542556C874AF768005A6EC83ADEB2EE32F8E6F7182A362775C2BF40AFA20").to_be_signed(30820466A00302010202103F25CAAEE5AA838FA91C051A75FC719D300D06092A864886F70D01010B0500307E310B3009060355040613025553311D301B060355040A131453796D616E74656320436F72706F726174696F6E311F301D060355040B131653796D616E746563205472757374204E6574776F726B312F302D0603550403132653796D616E74656320436C61737320332053656375726520536572766572204341202D204734301E170D3136313030373030303030305A170D3137313033303233353935395A306D310B30090603550406130255533113301106035504080C0A57617368696E67746F6E3110300E06035504070C0753656174746C6531193017060355040A0C10416D617A6F6E2E636F6D2C20496E632E311C301A06035504030C136563686F2D6170692E616D617A6F6E2E636F6D30820122300D06092A864886F70D01010105000382010F003082010A02820101009CAFB306BB910354E76E0406C44F9BE178934D9C83906C09EB4EBC006B1742682DF655610BC0934C2E30751E4D5E8B1BA15EBFEB7C28AD008DA38D7672C0558D4CB71F5FD512CFB92AFF80880394B8AA017C453CCC0BC709CEC698E29480D89D703034312A71DD94CC48619B91F68B8A44739DEEA7159EA334E9E4A93B460FA4AB0886202CD02B49C6283F321C5C4CA91C5AE8827CEB47811ED1871E7C66724BCD58A9EBFF9658B4D5D02046FA6702DCBAF2B6139B190D8735121BA2086C51C8C5724A0044C090688A25C819A5F1B6D4E9390E4DF21AB11263F203E4E9F1BCCC625D29D7C21B7C243E9B775E6E8B4B4F0DAF390748E964B968A9065EDBAA11A30203010001A382020730820203301E0603551D110417301582136563686F2D6170692E616D617A6F6E2E636F6D30090603551D1304023000300E0603551D0F0101FF0404030205A0301D0603551D250416301406082B0601050507030106082B0601050507030230610603551D20045A30583056060667810C010202304C302306082B06010505070201161768747470733A2F2F642E73796D63622E636F6D2F637073302506082B0601050507020230190C1768747470733A2F2F642E73796D63622E636F6D2F727061301F0603551D230418301680145F60CF619055DF8443148A602AB2F57AF44318EF302B0603551D1F042430223020A01EA01C861A687474703A2F2F73732E73796D63622E636F6D2F73732E63726C305706082B06010505070101044B3049301F06082B060105050730018613687474703A2F2F73732E73796D63642E636F6D302606082B06010505073002861A687474703A2F2F73732E73796D63622E636F6D2F73732E637274300F06032B654D0408300602010102010130818B060A2B06010401D679020402047D047B0079007700A7CE4A4E6207E0ADDEE5FDAA4B1F86768767B5D002A55D47310E7E670A95EAB2000001579FAA404A0000040300483046022100CA6B7C069C140B774E8D64ED95D4A59E92349E04AA3C71B304F32C74BC857F81022100FA152CF8558BA0A87B515AB7522D1BE1725740E128042309DD274FEEA5EB3B62" ).

二级证书

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

<预>键(PUBLIC_KEY(RSA( B2D805CA1C742DB5175639C54A520996E84BD80CF1689F9A422862C3A530537E5511825B037A0D2FE17904C9B496771981019459F9BCF77A9927822DB783DD5A277FB2037A9C5325E9481F464FC89D29F8BE7956F6F7FDD93A68DA8B4B82334112C3C83CCCD6967A84211A22040327178B1C6861930F0E5180331DB4B5CEEB7ED062ACEEB37B0174EF6935EBCAD53DA9EE9798CA8DAA440E25994A1596A4CE6D02541F2A6A26E2063A6348ACB44CD1759350FF132FD6DAE1C618F59FC9255DF3003ADE264DB42909CD0F3D236F164A8116FBF28310C3B8D6D855323DF1BD0FBD8C52954A16977A522163752F16F9C466BEF5B509D8FF2700CD447C6F4B3FB0F7", 010001", - , - , - , - , - , - ))).

验证

计算哈希

正如我所说,哈希是实际签署的内容.重要的是,我指的不是整个证书的哈希值,而是 to-be-signed 部分的哈希值.这个区别很重要,因为整个证书的哈希值也包含签名,当然签名时还没有签名.

在 SWI-Prolog 中,我们可以使用 library(crypto):

<预>?- to_be_signed(TBS),hex_bytes(TBS,字节),crypto_data_hash(字节,哈希,[算法(sha256),编码(八位字节)]).TBS = "3082...EB3B62",字节数 = [48, 130, 4, 102, 160, 3, 2, 1, 2|...],哈希 = '651bdcdd90251f71a47a5d1bbc6f28486c94d2dc3739dcd58ecb09b3f224ee05'.

我使用 sha256 因为第一个证书在其 signature_algorithm/1 中指示 (RSA 和) SHA256领域.

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

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

<预>? - X#= 0x7B86A6E7A86B192579380108B7EADA1C25E288AB46120117DC6A80635324C89713C70206EF3FE13CCA5EDFF43601972CB96658826ADCD68B0FB3AE7F5607D036D6AE8AF824CD96D7F1B4DB58E714343031F292B17E6EEF83872A0FD586CC0D1DA85A677E4AB4C6540E9132B5BC5644533E0388F830B1B6757B7DB88AB82846F08B3B6DCEC7F24319AB7EA56F86592DDDEC4522CAF331C8B81A4E543FBDBF4D661B534BAE546465DB88A525BC82A7B4127F0AFF4A55525927A66A09055743F109E30D90CA074D258166F0E472CB7CCDB0747ADE74F7040CFEEB9A78C3483864C5106D542556C874AF768005A6EC83ADEB2EE32F8E6F7182A362775C2BF40AFA20 ^ 0x010001mod0xB2D805CA1C742DB5175639C54A520996E84BD80CF1689F9A422862C3A530537E5511825B037A0D2FE17904C9B496771981019459F9BCF77A9927822DB783DD5A277FB2037A9C5325E9481F464FC89D29F8BE7956F6F7FDD93A68DA8B4B82334112C3C83CCCD6967A84211A22040327178B1C6861930F0E5180331DB4B5CEEB7ED062ACEEB37B0174EF6935EBCAD53DA9EE9798CA8DAA440E25994A1596A4CE6D02541F2A6A26E2063A6348ACB44CD1759350FF132FD6DAE1C618F59FC9255DF3003ADE264DB42909CD0F3D236F164A8116FBF28310C3B8D6D855323DF1BD0FBD8C52954A16977A522163752F16F9C466BEF5B509D8FF2700CD447C6F4B3FB0F7.

它产生:

<预>X = 986236757547332986472011617696226561292849812918563355472727826767720188564083584387121625107510786855734801053524719833194566624465665316622563244215340671405971599343902468620306327831715457360719532421388780770165778156818229863337344187575566725786793391480600129482653072861971002459947277805295727097226389568776499707662505334062639449916265137796823793276300221537201727072401742985542559596685092673521228140822200236743113743661549252453726123450722876929538747702356573783116197523966334991563351853851212597377279504828784716104866621888265058037501385433453379649364782998949981722124880992983641605.

<小时>

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

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

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

现在我们有了查询:

<预>? - 时间(signature_pow(0x7B86A6E7A86B192579380108B7EADA1C25E288AB46120117DC6A80635324C89713C70206EF3FE13CCA5EDFF43601972CB96658826ADCD68B0FB3AE7F5607D036D6AE8AF824CD96D7F1B4DB58E714343031F292B17E6EEF83872A0FD586CC0D1DA85A677E4AB4C6540E9132B5BC5644533E0388F830B1B6757B7DB88AB82846F08B3B6DCEC7F24319AB7EA56F86592DDDEC4522CAF331C8B81A4E543FBDBF4D661B534BAE546465DB88A525BC82A7B4127F0AFF4A55525927A66A09055743F109E30D90CA074D258166F0E472CB7CCDB0747ADE74F7040CFEEB9A78C3483864C5106D542556C874AF768005A6EC83ADEB2EE32F8E6F7182A362775C2BF40AFA20,0x010001,0xB2D805CA1C742DB5175639C54A520996E84BD80CF1689F9A422862C3A530537E5511825B037A0D2FE17904C9B496771981019459F9BCF77A9927822DB783DD5A277FB2037A9C5325E9481F464FC89D29F8BE7956F6F7FDD93A68DA8B4B82334112C3C83CCCD6967A84211A22040327178B1C6861930F0E5180331DB4B5CEEB7ED062ACEEB37B0174EF6935EBCAD53DA9EE9798CA8DAA440E25994A1596A4CE6D02541F2A6A26E2063A6348ACB44CD1759350FF132FD6DAE1C618F59FC9255DF3003ADE264DB42909CD0F3D236F164A8116FBF28310C3B8D6D855323DF1BD0FBD8C52954A16977A522163752F16F9C466BEF5B509D8FF2700CD447C6F4B3FB0F7,战俘)).

时间:

<预>% 16 推理,0.000 秒内 0.000 CPU(99% CPU,130624 唇)

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

<预>% 3 推理,1.852 秒内 1.847 CPU(100% CPU,2 唇)

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

建议:在 Prolog 中使用 CLP(FD) 约束对整数进行推理.它们通常使您的谓词更通用,有时大大更有效. 

<小时>

现在,X 怎么样?要了解它是什么,请考虑它的十六进制编码:

<预>?- 格式(~16r",[$X]).1ffffffff...fff003031300d060960864801650304020105000420651bdcdd90251f71a47a5d1bbc6f28486c94d2dc3739ddeeb5035ec

这听起来很熟悉:最后,您会看到证书的待签名部分的哈希出现.这意味着签名已签出!

使用 rsa_verify/4 验证签名

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

这是完整的查询:

<预>?- to_be_signed(TBS),hex_bytes(TBS,字节),crypto_data_hash(Bytes, Hash, [algorithm(sha256), encoding(octet)]),签名(Sig),键(键),rsa_verify(Key, Hash, Sig, [type(sha256)]).

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

结束语

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

SWI-Prolog SSL 基础设施自动验证证书链,从而在您每次使用 http_open/3 和相关谓词通过连接建立连接时验证所有签名.TLS.但是自己进行这些计算很有趣.有时甚至有必要,如在此示例中,您正在对存储在某处的证书进行推理.

补充一点:请在您的代码中使用 setup_call_cleanup/3.否则,如果在 close/1 之前出现任何问题,您就有泄漏文件描述符的风险,实际上在您的示例中也是如此.

This question is related to Opening and checking a Pem file in SWI-Prolog

Once I have downloaded and opened the certificates how do I verify the signature chain? I have:

:-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).

This currently fails.

解决方案

Preliminaries

Verifying digital signatures and entire certificate chains is extremely easy with Prolog.

However, you need to have a basic understanding of how certificates are signed. A certificate chain is a sequence of certificates C0, C1, ..., CN. I am using CN to denote the root certificate. Depending on the used convention, you may mutatis mutandis reverse the order of course.

Importantly, certificate Ck is signed using the private key corresponding to the public key of Ck+1.

Thus, one of the issues with your code is that you are mistakenly using the public key of C to verify the signature of C even though the certificate was signed with the private key corresponding to a different certificate.

A different issue stems from some confusion about what is being signed. We are signing the hash of the to-be-signed part of a certificate, not the data itself. Thus, we must verify the signature against that hash.

Concrete example

To make this answer self-contained, I post here the relevant data from your use case, i.e., the relevant attributes of the certificates that the file contains at the time of this writing.

Data

First certificate

From the first certificate in the chain, we need the signature and the to-be-signed portion, which are:

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

Second certificate

From the second certificate, we only need the public key to verify the signature that was issued for the previous certificate:

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

Verification

Computing the hash

As I said, a hash is what was actually signed. Critically, I don't mean the hash of the whole certificate, but the hash of the to-be-signed portion. This difference is important, because the hash of the whole certificate also comprises the signature, and that is of course not yet available when the certificate is being signed.

In SWI-Prolog, we can obtain the hash of the to-be-signed portion using 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'.

I am using sha256 because the first certificate indicates (RSA and) SHA256 in its signature_algorithm/1 field.

Verifying the signature using CLP(FD) constraints

One of the easiest ways to verify an RSA signature is to use CLP(FD) constraints. We only need to compute SigExp mod p. We plug in our concrete numbers, using (#=)/2 to evaluate the arithmetic expression over integers:

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

It yields:

X = 986236757547332986472011617696226561292849812918563355472727826767720188564083584387121625107510786855734801053524719833194566624465665316622563244215340671405971599343902468620306327831715457360719532421388780770165778156818229863337344187575566725786793391480600129482653072861971002459947277805295727097226389568776499707662505334062639449916265137796823793276300221537201727072401742985542559596685092673521228140822200236743113743661549252453726123450722876929538747702356573783116197523966334991563351853851212597377279504828784716104866621888265058037501385433453379649364782998949981722124880992983641605.


Excursion: On efficiency and use of CLP(FD).

You may now say: "Well, I don't really need (#=)/2, I can always use (is)/2 which I learned decades ago." But, if you are using (is)/2 in such examples, you easily end up with code that is thousands of times less efficient. As a simple benchmark, consider the predicate:

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

Now we have, for the query:

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

the timing:

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

In contrast, if we regress in Prolog language development and replace (#=)/2 by (is)/2, we get:

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

Reason: In SWI-Prolog, certain goals involving (#=)/2 automatically use specialized arithmetic predicates. You do not need to learn these predicates to use them. CLP(FD) does it for you.

Recommendation: Use CLP(FD) constraints for reasoning over integers in Prolog. They typically make your predicates more general, and sometimes vastly more efficient. 


Now, what about X? To see what it is, consider its hexadecimal encoding:

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

This sounds familiar: At the end, you see that the hash of the to-be-signed portion of the certificate appears. This means that the signature checks out!

Verifying the signature with rsa_verify/4

Alternatively, we can use rsa_verify/4 from library(crypto) to verify the signature.

Here is the full query:

?- 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)]).

Since this succeeds, we know that the private key corresponding to Key was used to produce the signature.

Closing remarks

I have one important remark: Normally, this is all of course completely unnecessary!

The SWI-Prolog SSL infrastructure automatically verifies the certificate chain and thus all signatures every single time you use http_open/3 and related predicates to make a connection via TLS. But it is interesting to make these calculations yourself. Sometimes it is even necessary, if, as in this example, you are reasoning over certificates you have stored somewhere.

One small additional remark: Please use setup_call_cleanup/3 in your code. Otherwise, you risk leaking file descriptors if anything goes wrong before close/1, which is in fact even the case in your example.

这篇关于验证签名链 SWI-Prolog的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆