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

How to ensure someone did not generate the signature using public key

我正在按照这些说明进行操作 https://www.onebigfluke.com/2013/11/public-key-crypto-math-explained.html。我的问题是 - 我如何知道是否某些恶意代理没有编写恶意消息,然后使用我的 public 密钥生成签名?例如:

  1. Bob 有他的私钥。
  2. Eve 向 Alice 发送一条消息,连同使用 Bob 的 public 密钥计算的“确认”。
  3. Alice 收到消息和 Bob 的 public 密钥,计算确认 == 签名 => 消息是由 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")

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

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

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

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

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

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

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

您描述的攻击失败了,因为 Eve 试图使用 Bob 的 public 密钥签署消息。如果 Alice 再次使用 Bob 的 public 密钥验证此消息,验证将失败。

代码审查

在你提供了你的代码示例和 link 到你的来源之后,我能够进一步研究它。

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

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

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

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

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

这个 bug 出在你身上,但从现在开始,一切都不是你的错,因为你 linked 的来源本身就有问题。因此,让我们继续一点一点地修复您的代码。

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

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

所以这个

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

因此,让我们取用维基百科页面 https://en.wikipedia.org/wiki/RSA_(cryptosystem) 提供的值。我们鼓励您尝试自己推出,只要确保它们足够大以适应您的哈希函数范围即可。

public key: n = 3233, e = 17

private key: n = 3233, d = 413

这里你的散列函数有效,因为应用 % 2**8 保证结果小于 256,因此小于 n = 3233。一般来说,使用更强的散列可能是个好主意,比如您的示例中提供的那个 link (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 的 public 密钥。这导致 Alice 在尝试验证签名时第二次使用 public 密钥,结果如下:

The message was not sent by Bob

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

所以这里的哈希和确认哈希不匹配。伊芙的攻击失败了。