任务描述
本关任务:对给定的身份以及消息进行数字签名。
相关知识
为了完成本关任务,你需要掌握:1.数字签名的概念和性质,2.公钥即身份。
数字签名的概念和性质
数字签名是密码学中的第二个重要部分。数字签名被认为是对纸上手写签名的数字模拟。我们对数字签名有两个特性要求,使其与我们对手写签名的预期一致。
- 第一,只有你可以制作你自己的签名,但任何看到它的人都可以验证其有效性;
- 第二,我们希望签名只与某一特定文件发生联系,因此该签名不能用于表明你同意或支持另一份不同的文件。
对于手写签名来说,第二条就如同确保别人不能将你的签名从一份文件上剪下来,贴到另一份文件的末尾那样。 那我们如何通过密码学来构建这些性质呢?首先,让我们把之前的直观讨论说得更具体一些,以便今后可以更好地论证数字签名方案,并讨论其安全特性。 数字签名方案
(1)数字签名方案由以下三个算法构成: ● (sk,pk):=generateKeys(keysize)``generateKeys
方法把keysize
作为输入,来产生一对公钥和私钥。私钥sk
被安全保存,并用来签名一段消息;公钥pk
是人人都可以找到的,拿到它,就可以用来验证你的签名。 ● sig:=sign(sk,message)
签名过程是把一段消息和私钥作为一个输入,对于消息输出是签名。 ●isValid:=verify(pk,message,sig)
验证过程是通过把一段消息和签名消息与公钥作为输入,如果返回的结果是真,证明签名属实;如果返回的结果为假,证明签名消息为假。 (2)我们要求以下两个性质有效: ● 有效签名可以通过验证,即: verify(pk,message,sign(sk,message))==true
● 签名不可伪造。
公钥即身份
让我们来看一下与数字签名并行的一个有用技巧,基本想法是从数字签名模式中拿出一个公共验证密钥,并将其与一个人或一个系统参与者的身份对等。如果你见到一条消息的签名被公钥pk
正确验证,那么你可以认为pk
就是在表达这条消息。你真的可以将公钥认为是参与者或者系统的一方,他可以通过签署声明而发布声明。从这个角度来说,公钥就是身份,让某人能为pk
身份发声,他必须知道相应的密钥sk
。 将公钥视为身份的一个结果是,你可以随时制定新的身份——你可以简单通过数字签名方案中的generateKeys
程序,生成新的密钥对sk
和pk
。pk
是你可以使用的新的公共身份,sk
是相应的密钥,只有你自己知道并可以让你代表身份为pk
发声。在实践中,你可能会使用pk
的哈希作为你的身份,这是因为公钥很大。如果是这样的话,为了验证消息来自你的身份,人们会需要验证:
- (1)你的身份确实是
pk
的哈希; - (2)信息能经过公钥
pk
验证。
此外,在默认情况下,你的公钥pk
基本上看起来是随机的,也并没有人能够通过检查pk
发现你的现实身份(当然,一旦你开始使用这个身份发表声明,这些声明可能泄露信息,而让别人将你的真实身份与pk
联系起来。我们很快会更详细地讨论这个问题)。你可以生成一个看起来随机的新身份,看起来像人群中的一张脸,但这些都只有你能够控制。
编程要求
根据提示,在右侧编辑器补充代码,根据公钥即身份,我们可以模拟一次消息签名,假如你的身份是e
,有哈希函数H(x)=ax+b
,那么我们可以把pk=H(e)
作为公钥,sk=H(e)−1modq
作为私钥,我们用私钥对消息m进行加密,即en(m)=sk∗m
,那么我们解密就可以这样计算de(en(m))=en(m)∗pk
。这里q
保证是素数。 输入
e a b q m
输出
pk
sk
en(m)
输入
3 4 5 11 6
输出
17
2
12
提示: 费马定理求逆元,可以查询一下。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int e,a,b,q,m;
//在下面Begin和End之间补全代码
/*********** Begin ***********/
int quick(int x,int y)
{
int ans=1;
while(y)
{
if(y&1)
ans=ans*x%q;
x=x*x%q;
y/=2;
}
return ans%q;
}
int main()
{
cin>>e>>a>>b>>q>>m;
int pk=a*e+b;
int sk=quick(pk,q-2);
int en=sk*m;
cout<<pk<<endl<<sk<<endl<<en<<endl;
return 0;
}
/*********** End ***********/