你使用的
openssl
命令执行了三个操作:
- 使用 SHA256 创建数据的哈希
- 如果使用 RSA,将消息填充到特定长度,使用 PKCS#1 1.5
- 使用提供的私钥签署(填充的)哈希。算法取决于密钥类型。
hmac
模块不具有相同的功能。
您需要安装类似
cryptography
这样的加密包来复制
openssl dgst -sign
的功能。
cryptography
将 OpenSSL 用作后端,因此它将生成相同的输出。
然后,您可以:
您必须在不同的类型之间切换以正确生成签名:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa, utils
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
_signing_configs = (
(dsa.DSAPrivateKey, lambda h: {
'algorithm': h}),
(ec.EllipticCurvePrivateKey, lambda h: {
'signature_algorithm': ec.ECDSA(h)}),
(rsa.RSAPrivateKey, lambda h: {
'padding': padding.PKCS1v15(),
'algorithm': h
}),
)
def _key_singing_config(key, hashing_algorithm):
try:
factory = next(
config
for type_, config in _signing_configs
if isinstance(key, type_)
)
except StopIteration:
raise ValueError('Unsupported key type {!r}'.format(type(key)))
return factory(hashing_algorithm)
def sign(private_key, data, algorithm=hashes.SHA256()):
with open(private_key, 'rb') as private_key:
key = serialization.load_pem_private_key(
private_key.read(), None, default_backend())
return key.sign(data, **_key_singing_config(key, algorithm))
如果你需要对大量数据进行哈希,可以先将数据分块自行哈希,然后只传递摘要和特殊的util.Prehashed()
对象:
def sign_streaming(private_key, data_iterable, algorithm=hashes.SHA256()):
with open(private_key, 'rb') as private_key:
key = serialization.load_pem_private_key(
private_key.read(), None, default_backend())
hasher = hashes.Hash(algorithm, default_backend())
for chunk in data_iterable:
hasher.update(chunk)
digest = hasher.finalize()
prehashed = utils.Prehashed(algorithm)
return key.sign(digest, **_key_singing_config(key, prehashed))
with open(large_file, 'rb') as large_file:
signature = sign_streaming(private_key_file, iter(lambda: large_file.read(2 ** 16), b''))
这里使用iter()
函数以64千字节的块来读取二进制文件中的数据。
演示代码:我正在使用我在 /tmp/test_rsa.pem 中生成的 RSA 密钥。使用命令行为 Hello world! 生成签名摘要:
$ echo -n 'Hello world!' | openssl dgst -sign /tmp/test_rsa.pem -sha256 | openssl base64
R1bRhzEr+ODNThyYiHbiUackZpx+TCviYR6qPlmiRGd28wpQJZGnOFg9tta0IwkT
HetvITcdggXeiqUqepzzT9rDkIw6CU7mlnDRcRu2g76TA4Uyq+0UzW8Ati8nYCSx
Wyu09YWaKazOQgIQW3no1e1Z4HKdN2LtZfRTvATk7JB9/nReKlXgRjVdwRdE3zl5
x3XSPlaMwnSsCVEhZ8N7Gf1xJf3huV21RKaXZw5zMypHGBIXG5ngyfX0+aznYEve
x1uBrtZQwUGuS7/RuHw67WDIN36aXAK1sRP5Q5CzgeMicD8d9wr8St1w7WtYLXzY
HwzvHWcVy7kPtfIzR4R0vQ==
或者使用Python代码:
>>> signature = sign(keyfile, b'Hello world!')
>>> import base64
>>> print(base64.encodebytes(signature).decode())
R1bRhzEr+ODNThyYiHbiUackZpx+TCviYR6qPlmiRGd28wpQJZGnOFg9tta0IwkTHetvITcdggXe
iqUqepzzT9rDkIw6CU7mlnDRcRu2g76TA4Uyq+0UzW8Ati8nYCSxWyu09YWaKazOQgIQW3no1e1Z
4HKdN2LtZfRTvATk7JB9/nReKlXgRjVdwRdE3zl5x3XSPlaMwnSsCVEhZ8N7Gf1xJf3huV21RKaX
Zw5zMypHGBIXG5ngyfX0+aznYEvex1uBrtZQwUGuS7/RuHw67WDIN36aXAK1sRP5Q5CzgeMicD8d
9wr8St1w7WtYLXzYHwzvHWcVy7kPtfIzR4R0vQ==
尽管行长度不同,但两个输出的base64数据明显相同。
或者,使用包含随机二进制数据的生成文件,大小为32kb:
$ dd if=/dev/urandom of=/tmp/random_data.bin bs=16k count=2
2+0 records in
2+0 records out
32768 bytes transferred in 0.002227 secs (14713516 bytes/sec)
$ cat /tmp/random_data.bin | openssl dgst -sign /tmp/test_rsa.pem -sha256 | openssl base64
b9sYFdRzpBtJTan7Pnfod0QRon+YfdaQlyhW0aWabia28oTFYKKiC2ksiJq+IhrF
tIMb0Ti60TtBhbdmR3eF5tfRqOfBNHGAzZxSaRMau6BuPf5AWqCIyh8GvqNKpweF
yyzWNaTBYATTt0RF0fkVioE6Q2LdfrOP1q+6zzRvLv4BHC0oW4qg6F6CMPSQqpBy
dU/3P8drJ8XCWiJV/oLhVehPtFeihatMzcZB3IIIDFP6rN0lY1KpFfdBPlXqZlJw
PJQondRBygk3fh+Sd/pGYzjltv7/4mC6CXTKlDQnYUWV+Rqpn6+ojTElGJZXCnn7
Sn0Oh3FidCxIeO/VIhgiuQ==
使用Python处理相同文件:
>>> with open('/tmp/random_data.bin', 'rb') as random_data:
... signature = sign_streaming('/tmp/test_rsa.pem', iter(lambda: random_data.read(2 ** 16), b''))
...
>>> print(base64.encodebytes(signature).decode())
b9sYFdRzpBtJTan7Pnfod0QRon+YfdaQlyhW0aWabia28oTFYKKiC2ksiJq+IhrFtIMb0Ti60TtB
hbdmR3eF5tfRqOfBNHGAzZxSaRMau6BuPf5AWqCIyh8GvqNKpweFyyzWNaTBYATTt0RF0fkVioE6
Q2LdfrOP1q+6zzRvLv4BHC0oW4qg6F6CMPSQqpBydU/3P8drJ8XCWiJV/oLhVehPtFeihatMzcZB
3IIIDFP6rN0lY1KpFfdBPlXqZlJwPJQondRBygk3fh+Sd/pGYzjltv7/4mC6CXTKlDQnYUWV+Rqp
n6+ojTElGJZXCnn7Sn0Oh3FidCxIeO/VIhgiuQ==
openssl
进行两个操作,但都没有使用HMAC签名。你正在生成一个普通的SHA256摘要,然后还对该摘要进行签名。 - Martijn Pieterscryptography
包,并根据您拥有的私钥类型(DSA、RSA等)选择其中一种算法。然后阅读Signing部分。例如,对于RSA,有如何签署消息的文档。 - Martijn Pieters