提问者:小点点

使用Python中RSA公钥验证签名的许可证密钥


免责声明:我不擅长密码学,对RSA或密码验证知之甚少。

我需要在Python验证签名许可证。我使用了“RSAPKCS1 v1.5填充,带有SHA256摘要”。背景信息是我正在尝试使用https://keygen.sh分发软件。

基本上,二进制软件安装在服务器上,并验证存储在服务器上的许可证文件是真实的。要做到这一点,软件可以访问我的RSA公钥。我有一段代码在Node. js中工作,来自官方keygen留档,我试图将其移植到Python,但没有成功。我选择使用密码库,但我对其他选项持开放态度。

这是Node. js中的工作代码:

const crypto = require('crypto')

const public_key = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtmwlw+mDo2ZVBlRXa7Em\ncj7cVrlwnwrIPC+Ij5KpltadJfwvRFvCr37USJvkc+FIND2dKk2mmbY32cvtxl3F\nYLpjRwwlFuajbP8ZEdJl1YJyJDnLlKHWEfTSvTzZhpT939yjuBKoZ9A+wiIQ9tzY\nF/ytb9zwPkOF7/XmPAaukah5xRgwsb3fo7E0CsBQuHZxFX83+nfdZ/60MWpSCWL6\nAjNWDEmoLFEHVRm69+lwXTW51wojfurZy/wUw42sciHLV5A8mz7gJJGO5y+sGzzD\nM5VxtmLz51Fl1Rl3fMzUAjPK77i9UDWo11EuNPrzMAgjmuuMLfpIDMlMR3n/ZsW7\nXwIDAQAB\n-----END PUBLIC KEY-----\n"
const key = 'somerandomkey'
const encodedSignature = "oMTrvIz3IX4kre5UTzvkzCn712wulPvl9knSYBduYcGsX2W703zWMC9ZVepDytxLdpUIiCUtx6wx5OzmLx3rTzgaKqptrbf2wYHrCIPBgrhcHdJ3fLJRh8ASC_NdLK6i1jC_bEAq84d7QNLlTPC20aCmNLdxEJFy-DValGG0iFdxx6n6-Vp5oL8jSyWubAvBSqEQ4ubptcYirxpbDdC4DRpNzBuA48DGxWg6Pxq5HdGZWKS05iohNlrFkW-K8NJYHuLKszT0FN5UWcghx1oklagCm72aDvXm3CzKL2id7yL78X_V69JYsExx3fjRsU0pUe-f5lzKLB_HLTAdc0e1gQ=="

const verifier = crypto.createVerify('sha256')
verifier.write(key)
verifier.end()

const ok = verifier.verify(public_key, encodedSignature, 'base64')
if (ok) {
  console.log('License key is valid!')
} else {
  console.log('License key is invalid!')
}

运行这段代码(nodejs verfy. js)会打印许可证密钥有效!

这是失败的代码,Python:

import base64

# pip install pycryptodome
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
import Crypto.Signature.pkcs1_15
import Crypto.Util.Padding

public_key = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtmwlw+mDo2ZVBlRXa7Em\ncj7cVrlwnwrIPC+Ij5KpltadJfwvRFvCr37USJvkc+FIND2dKk2mmbY32cvtxl3F\nYLpjRwwlFuajbP8ZEdJl1YJyJDnLlKHWEfTSvTzZhpT939yjuBKoZ9A+wiIQ9tzY\nF/ytb9zwPkOF7/XmPAaukah5xRgwsb3fo7E0CsBQuHZxFX83+nfdZ/60MWpSCWL6\nAjNWDEmoLFEHVRm69+lwXTW51wojfurZy/wUw42sciHLV5A8mz7gJJGO5y+sGzzD\nM5VxtmLz51Fl1Rl3fMzUAjPK77i9UDWo11EuNPrzMAgjmuuMLfpIDMlMR3n/ZsW7\nXwIDAQAB\n-----END PUBLIC KEY-----\n"
license_key = b'somerandomkey'

encoded_license_signature = """oMTrvIz3IX4kre5UTzvkzCn712wulPvl9knSYBduYcGsX2W703zWMC9ZVepDytxLdpUIiCUtx6wx5OzmLx3rTzgaKqptrbf2wYHrCIPBgrhcHdJ3fLJRh8ASC_NdLK6i1jC_bEAq84d7QNLlTPC20aCmNLdxEJFy-DValGG0iFdxx6n6-Vp5oL8jSyWubAvBSqEQ4ubptcYirxpbDdC4DRpNzBuA48DGxWg6Pxq5HdGZWKS05iohNlrFkW-K8NJYHuLKszT0FN5UWcghx1oklagCm72aDvXm3CzKL2id7yL78X_V69JYsExx3fjRsU0pUe-f5lzKLB_HLTAdc0e1gQ=="""
license_signature = base64.b64decode(encoded_license_signature)
# Padding: none of these solutions work
# license_signature = Crypto.Util.Padding.pad(license_signature, 8, style='pkcs7')
# license_signature = Crypto.Util.Padding.pad(license_signature, 8, style='iso7816')
# license_signature = Crypto.Util.Padding.pad(license_signature, 8, style='x923')
# Custom zero-padding (doesn't work either)
#license_signature = (8 - len(license_signature) % 8)*bytes([0]) + license_signature
#license_signature = license_signature + (8 - len(license_signature) % 8)*bytes([0])

rsa_public_key = RSA.import_key(public_key)
signature = Crypto.Signature.pkcs1_15.new(rsa_public_key)

license_hash = SHA256.new(data=license_key)
print(signature.verify(license_hash, license_signature))

运行这段代码(python3verify.py)会引发错误:

Traceback (most recent call last):
  File "verify.py", line 30, in <module>
    print(signature.verify(license_hash, license_signature))
  File "/home/user/venvs/tutor/lib/python3.6/site-packages/Crypto/Signature/pkcs1_15.py", line 111, in verify
    raise ValueError("Invalid signature")
ValueError: Invalid signature

这对应于签名长度错误:

# Step 1
if len(signature) != k:
    raise ValueError("Invalid signature")

我假设这是由不正确的填充引起的,所以我在签名填充方面做了不同的尝试,你可以在我的代码中看到,没有一个有效。但至少,它们允许我超越签名验证的第一步。我现在被困在第四步也是最后一步:

Traceback (most recent call last):
  File "verify.py", line 30, in <module>
    print(signature.verify(license_hash, license_signature))
  File "/home/user/venvs/tutor/lib/python3.6/site-packages/Crypto/Signature/pkcs1_15.py", line 137, in verify
    raise ValueError("Invalid signature")
ValueError: Invalid signature

知道如何解决这个问题吗?如果有必要,我准备使用与密码穹顶不同的东西。


共2个答案

匿名用户

你的签名是base64url而不是标准base64。我认为后者是罪魁祸首。所以你需要用替换-,用/替换_。或者当然使用base64url解码器。

匿名用户

我是Keygen的创始人。这是我这边缺乏适当的留档,我会纠正的。就像Maarten提到的,许可证密钥的签名内容(包含两个部分:密钥有效负载及其签名)是使用RFC4648进行base64url编码的,RFC4648是base64的URL安全版本,大多数编程语言都支持(但并非总是如此)。这种base64url编码方案与普通的base64编码略有不同,如Maarten的回答所述。

大多数编程语言会有一个单独的函数来解码URLbase64编码值,但如果没有,就像Maarten在他的回答中概述的那样,你可以简单地将所有“-”base64字符替换为“”,将所有“_”字符替换为“/”。

以下是使用PKCS1 v1.5填充验证使用RSA-SHA256签名的许可证密钥的完整示例:

from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
import base64

# This should be your Keygen account's public key
PUBLIC_KEY = \
"""-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtmwlw+mDo2ZVBlRXa7Em
cj7cVrlwnwrIPC+Ij5KpltadJfwvRFvCr37USJvkc+FIND2dKk2mmbY32cvtxl3F
YLpjRwwlFuajbP8ZEdJl1YJyJDnLlKHWEfTSvTzZhpT939yjuBKoZ9A+wiIQ9tzY
F/ytb9zwPkOF7/XmPAaukah5xRgwsb3fo7E0CsBQuHZxFX83+nfdZ/60MWpSCWL6
AjNWDEmoLFEHVRm69+lwXTW51wojfurZy/wUw42sciHLV5A8mz7gJJGO5y+sGzzD
M5VxtmLz51Fl1Rl3fMzUAjPK77i9UDWo11EuNPrzMAgjmuuMLfpIDMlMR3n/ZsW7
XwIDAQAB
-----END PUBLIC KEY-----"""

# This should be the license key that you're cryptographically verifying
LICENSE_KEY = \
"""c29tZXJhbmRvbWtleQ==.oMTrvIz3IX4kre5UTzvkzCn712wulPvl9knSYBduYcGsX2W703zWMC9ZVepDytxLdpUIiCUtx6wx5OzmLx3rTzgaKqptrbf2wYHrCIPBgrhcHdJ3fLJRh8ASC_NdLK6i1jC_bEAq84d7QNLlTPC20aCmNLdxEJFy-DValGG0iFdxx6n6-Vp5oL8jSyWubAvBSqEQ4ubptcYirxpbDdC4DRpNzBuA48DGxWg6Pxq5HdGZWKS05iohNlrFkW-K8NJYHuLKszT0FN5UWcghx1oklagCm72aDvXm3CzKL2id7yL78X_V69JYsExx3fjRsU0pUe-f5lzKLB_HLTAdc0e1gQ=="""

# Split license key to obtain key and signature, then decode urlbase64 encoded values
enc_key, enc_sig = LICENSE_KEY.split(".")
key = base64.urlsafe_b64decode(enc_key)
sig = base64.urlsafe_b64decode(enc_sig)

# Verify the key's signature
pub_key = RSA.importKey(PUBLIC_KEY)
verifier = PKCS1_v1_5.new(pub_key)
digest = SHA256.new(data=key)

print(
  verifier.verify(digest, sig)
)

我更新了凯基的留档,以澄清所有这些事情,以及提供关于如何加密验证许可证的更好的例子。