我花了大约 2 天的时间尝试完成这项工作,并在 Google 上搜索了有关密码学、PyNaCl 和 paramiko 等软件包的信息。
问问题
710 次
1 回答
1
最后,感谢 GitHub https://github.com/R-VdP的Ramses,我在https://gist.github.com/R-VdP/b7ac0106a4fd395ee1c37bfe6f552a36找到了一些示例代码seal.py。
您可以阅读使用 ssh-keygen 生成的 id_ed25519 和 id_ed25519.pub 文件,将密钥作为文本剥离,然后使用seal.py 中的extract_curve_private_key和extract_curve_public_key。这些键可用于从 PyNaCl 构造 Box 类。因此,您可以使用 Ramses 的代码和 PyNaCl 完成对称、非对称和签名操作。
希望这可以帮助一些人比我花更少的时间在谷歌上搜索。
编辑:根据 Kenster 的建议添加了包装类和客户端代码。data/ 中的文件是通过ssh-keygen -t ed25519 生成的,没有密码。请注意,这是一个围绕 Ramses 代码的薄类包装器,值得称赞。
from nacl.public import Box
from nacl.utils import random as nacl_random
from nacl.secret import SecretBox
from nacl.exceptions import BadSignatureError
from base64 import b64decode
from nacl.encoding import RawEncoder
from nacl.signing import SigningKey, VerifyKey
class C25519:
# Adapted from https://gist.github.com/R-VdP/b7ac0106a4fd395ee1c37bfe6f552a36 sealing.py
# Author: Ramses https://github.com/R-VdP
__key_length = 32
__private_key_signature = b'\x00\x00\x00\x40'
__public_key_signature = b'\x00\x00\x00\x20'
@classmethod
def __bytes_after(cls, signature, length, bytestr):
start = bytestr.find(signature) + len(signature)
return bytestr[start:start+length]
@classmethod
def __extract_signing_key(cls, private_data):
openssh_bytes = b64decode(private_data)
private_bytes = cls.__bytes_after(
cls.__private_key_signature,
cls.__key_length,
openssh_bytes
)
signing_key = SigningKey(seed=private_bytes, encoder=RawEncoder)
return signing_key
@classmethod
def __extract_verify_key(cls, public_data):
openssh_bytes = b64decode(public_data)
public_bytes = cls.__bytes_after(
cls.__public_key_signature,
cls.__key_length,
openssh_bytes
)
verify_key = VerifyKey(key=public_bytes, encoder=RawEncoder)
return verify_key
@classmethod
def __private_data_from_file(cls, file_name):
with open(file_name, 'r') as file:
contents = file.read()
contents = contents.split('\n')
private_data = ''
for line in contents:
if 'PRIVATE KEY' in line:
continue
if not line:
continue
private_data += line
return private_data
@classmethod
def __public_data_from_file(cls, file_name):
with open(file_name, 'r') as file:
contents = file.read()
contents = contents.split(' ')
# assert contents[0] == 'ssh-ed25519'
public_data = contents[1].strip(' ')
return public_data
@classmethod
def signingKey(cls, private_ed25519_file):
private_data = cls.__private_data_from_file(private_ed25519_file)
signing_key = cls.__extract_signing_key(private_data)
return signing_key
@classmethod
def verifyKey(cls, public_ed25519_file):
public_data = cls.__public_data_from_file(public_ed25519_file)
verify_key = cls.__extract_verify_key(public_data)
return verify_key
@classmethod
def privateKey(cls, private_ed25519_file):
signing_key = cls.signingKey(private_ed25519_file)
return signing_key.to_curve25519_private_key()
@classmethod
def publicKey(cls, public_ed25519_file):
verify_key = cls.verifyKey(public_ed25519_file)
return verify_key.to_curve25519_public_key()
def asymmetric():
# Alice and Bob exchange public keys
public_key_alice = C25519.publicKey('data/id_ed25519_alice.pub')
public_key_bob = C25519.publicKey('data/id_ed25519_bob.pub')
private_key_alice = C25519.privateKey('data/id_ed25519_alice')
private_key_bob = C25519.privateKey('data/id_ed25519_bob')
bob_box = Box(private_key_bob, public_key_alice)
message = 'Asymmetric secret message from Bob to Alice'
message_bytes = message.encode('utf-8')
encrypted = bob_box.encrypt(message_bytes)
alice_box = Box(private_key_alice, public_key_bob)
plaintext_bytes = alice_box.decrypt(encrypted)
plaintext = plaintext_bytes.decode('utf-8')
assert message == plaintext
def symmetric():
# Alice and Bob exchange public keys
public_key_alice = C25519.publicKey('data/id_ed25519_alice.pub')
public_key_bob = C25519.publicKey('data/id_ed25519_bob.pub')
private_key_alice = C25519.privateKey('data/id_ed25519_alice')
private_key_bob = C25519.privateKey('data/id_ed25519_bob')
symmetric_key_bytes = nacl_random(SecretBox.KEY_SIZE)
bob_box_assymetric = Box(private_key_bob, public_key_alice)
encrypted = bob_box_assymetric.encrypt(symmetric_key_bytes)
alice_box_assymetric = Box(private_key_alice, public_key_bob)
plaintext_bytes = alice_box_assymetric.decrypt(encrypted)
assert symmetric_key_bytes == plaintext_bytes
bob_box_symmetric = SecretBox(symmetric_key_bytes)
message = 'Symmetric secret message from Bob to Alice'
message_bytes = message.encode('utf-8')
encrypted_message = bob_box_symmetric.encrypt(message_bytes)
alice_box_symmetric = SecretBox(symmetric_key_bytes)
plaintext_bytes = alice_box_symmetric.decrypt(encrypted_message)
plaintext = plaintext_bytes.decode('utf-8')
assert plaintext == message
def signing():
signingKey = C25519.signingKey('data/id_ed25519_bob')
message = 'Document signed with private key'
message_bytes = message.encode('utf-8')
signed = signingKey.sign(message_bytes)
signed_message = signed.message
signed_signature = signed.signature
verifyKey = C25519.verifyKey('data/id_ed25519_bob.pub')
verifyKey.verify(signed_message, signed_signature)
forged = signed[:-1] + bytes([int(signed[-1]) ^ 1])
try:
verifyKey.verify(forged)
except BadSignatureError as exc:
print(exc.args)
if __name__ == "__main__":
asymmetric()
symmetric()
signing()
于 2021-01-12T13:10:32.243 回答