如何确保某人没有使用公钥生成签名

问题描述

我正在遵循这些说明 https://www.onebigfluke.com/2013/11/public-key-crypto-math-explained.html。我的问题是 - 我怎么知道某个恶意代理是否没有编写恶意消息,然后使用我的公钥生成签名?例如:

  1. Bob 有他的私钥。
  2. Eve 向 Alice 发送一条消息,以及使用 Bob 的公钥计算出的“确认”。
  3. Alice 收到消息和 Bob 的公钥,计算确认 == 签名 => 消息是 Bob 发送的 (?)
  4. 这与 Bob 发送消息并使用他的私钥计算的签名有什么不同?

编辑

privatekey = (95,49) # Bob's private key
publickey = (95,25) #Bob's public key
message = 122 #Eve writes a mallicIoUs message
hash = message**65537 % 2**8 #Eve computes the hash
sig = hash**publickey[1]%publickey[0] #Eve computes the signature using bob's public key
Letter = (message,sig) #Eve sends her message and the signature to Alice
Alice_hash = Letter[0]**65537 % 2**8 #Alice computes the hash on her own
Alice_confirmation = Alice_hash**publickey[1]%publickey[0] #Alice computes the confirmation
if Alice_confirmation == Letter[1]:
    print("The message was sent by Bob")

它有效。我哪里出错了? (唯一用私钥计算的是签名,但也可以使用公钥计算[除非我遵循的教程(上面的链接)是错误的])

解决方法

这个问题一点也不傻。非对称加密有点令人困惑。我总是需要刷新我对这个主题的记忆,因为我不是每天都在处理它。

非对称加密的核心概念是您拥有公钥和私钥。您可以使用其中之一将消息转换为密码,但您只能使用另一个将密码转换回消息。

因此,如果 Alice 想加密给 Bob 的消息,她会应用 Bob 的公钥,即使 Alice 自己也无法解密该消息,因为她需要应用 Bob 的私钥,而只有 Bob 拥有。两次应用公钥将不起作用。

就像一把锁有两把钥匙,一把用来锁,一把用来开锁。

如果爱丽丝想签署一条消息,则相反。她使用她的私钥,只有她拥有。 Bob 或任何有权访问她的公钥的人都能够验证 Alice 的身份,前提是他可以安全地验证公钥实际上是 Alice 的。如果 Eve 可以让 Bob 相信公钥属于 Alice,而实际上它是 Eve 的密钥并且她用她的私钥签署了消息,那么这将是一次成功的攻击。

与加密示例一样,使用公钥签名和使用相同公钥检查是行不通的,它必须是一个用于签名,另一个用于检查。这就是为什么它被称为非对称。

您描述的攻击失败了,因为 Eve 尝试使用 Bob 的公钥对消息进行签名。如果 Alice 再次使用 Bob 的公钥验证此消息,验证将失败。

代码审查

在您提供代码示例和源链接后,我能够更深入地研究它。

在我们开始之前:如果一切都正确完成,那么应用公钥两次不会得到与先应用私钥然后应用公钥的结果相同的结果。这就是您建议的攻击失败的原因。让我们来看看为什么攻击似乎在您的代码中起作用:

问题 1:当您从示例中转移代码时,您的代码中存在错误。这一行

hash = message**65537 % 2**8  # incorrect

应该使用乘法而不是幂函数

hash = message*65537 % 2**8  # correct

这个错误是在你身上,但从现在开始,没有什么是你的错,因为你链接的源本身就有问题。因此,让我们继续一点一点地修复您的代码。

我将切换到使用私钥签名并使用公钥检查以确保算法有效的常规情况,然后我们将再次运行您的攻击。

问题 2: Alice_confirmation 计算不正确。这个想法是 Bob 计算散列,然后加密散列以获得签名。现在 Alice 解密签名并将其与她也计算的哈希值进行比较。最后一步在示例中进行了切换。

所以这个

Alice_confirmation = Alice_hash**publickey[1] % publickey[0]
if Alice_confirmation == sig:
    print("The message was sent by Bob")

实际上应该变成这样:

Alice_confirmation = sig**publickey[1] % publickey[0]
if Alice_confirmation == Alice_hash:
    print("The message was sent by Bob")

这是一个至关重要的区别,也是您的攻击似乎有效的原因。如果我没记错的话,副作用是正确签名的消息将无法通过测试(不过,由于问题 1,这在您的原始代码中没有发生)。

问题 3: 我无法重建您是如何获得私钥和公钥的,因此我将使用示例网站提供的那些。这里的问题是,作为一项规则,散列必须小于 n。在您的情况下,散列可以增长到 255(由于 % 2**8),这大于 n = 91。这在网站上更糟,因为他们在散列函数中使用 % 2**32甚至更多。

所以让我们改用维基百科页面 https://en.wikipedia.org/wiki/RSA_(cryptosystem) 提供的值。我们鼓励您尝试自己动手,只要确保它们对于您的哈希函数范围来说足够大。

公钥:n = 3233,e = 17

私钥:n = 3233,d = 413

这里您的散列函数有效,因为应用 % 2**8 保证结果小于 256,因此小于 n = 3233。一般来说,使用更强的散列可能是个好主意,例如在您的示例链接 (message * 2654435761 mod 2**32) 中提供,但当然您必须充分选择您的 n,使其超过 2**32

因此,应用修复程序并稍微清理一下代码会给我们带来:

private_key = (3233,17) # Bob's private key
public_key = (3233,413) # Bob's public key

bob_message = 122

bob_hash = bob_message * 65537 % 2**8
bob_sig = bob_hash**private_key[1] % private_key[0]

# Alice receives Bob's message and his signature

alice_hash = bob_message * 65537 % 2**8  # same as bob_hash
alice_confirmation_hash = bob_sig**public_key[1] % public_key[0]

if alice_hash == alice_confirmation_hash:
    print("The message was sent by Bob")
else:
    print("The message was not sent by Bob")

print()
print(f"message: {bob_message},signature: {bob_sig}")
print(f"hash: {alice_hash},confirmation: {alice_confirmation_hash}")

如果我们运行这段代码,我们会得到以下输出:

The message was sent by Bob

message: 122,signature: 1830
hash: 122,confirmation: 122

如果您想知道为什么消息与哈希相同,这是因为您的消息小于 2**8。大于该值的消息将提供与消息不同的哈希值。

现在让我们运行您提出的攻击:Eve 在计算签名时使用 Bob 的公钥。这导致 Alice 在尝试验证签名时第二次使用公钥,结果如下:

The message was not sent by Bob

message: 122,signature: 1159
hash: 122,confirmation: 1891

所以这里的散列和确认散列不匹配。夏娃的攻击失败了。