我有一个 python 库,它使用
pycryptodome
library 使用 openssh 格式的 ED25519 私钥使用 Ed25519 算法对数据进行签名。然后需要使用
sshtools
库和相应的公钥在 Java 应用程序中验证签名。但是签名验证失败。
约束 :从文件中读取私钥/公钥很重要。我无法更改 Python 代码和/或使用的密钥。
为了调试,我编写了一个实现来用 Java 生成签名,并验证 python 生成的签名。然而两者都不同。
我用于签名数据的 Python 实现如下:
from Crypto.Hash import SHA512
from Crypto.PublicKey import ECC
from Crypto.Signature import eddsa
import base64
import json
def generate_signature_v1(message):
message = message.replace(" ", "")
h = SHA512.new(message.encode("utf-8"))
with open("private", "r") as f:
key = ECC.import_key(f.read())
signer = eddsa.new(key, "rfc8032")
signature = signer.sign(h)
str_signature = base64.standard_b64encode(signature).decode("utf-8")
return str_signature
我用于生成和验证签名的 Java 实现。
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.io.File;
import java.io.IOException;
import com.google.gson.Gson;
import com.sshtools.common.publickey.InvalidPassphraseException;
import com.sshtools.common.publickey.SshKeyUtils;
import com.sshtools.common.ssh.components.SshKeyPair;
import com.sshtools.common.ssh.components.SshPrivateKey;
import com.sshtools.common.ssh.components.SshPublicKey;
public class StackOverflow {
private static final Gson gson = new Gson();
public static boolean verifyV1Signature(String message, String signature) {
try {
byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
MessageDigest digest = MessageDigest.getInstance("SHA-512");
byte[] hash = digest.digest(messageBytes);
// read public key
SshPublicKey readPublicKey = SshKeyUtils.getPublicKey(new File("public.pub"));
// verify signature
Base64.Decoder decoder = Base64.getDecoder();
byte[] signatureDecoded = decoder.decode(signature);
boolean isVerified = readPublicKey.verifySignature(signatureDecoded, hash);
System.out.println("signature is valid: " + isVerified);
return isVerified;
} catch (Exception e) {
return false;
}
}
public static String generateV1Signature(String message)
throws NoSuchAlgorithmException, IOException, InvalidPassphraseException {
byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
MessageDigest digest = MessageDigest.getInstance("SHA-512");
byte[] hash = digest.digest(messageBytes);
// create signature
SshKeyPair readKeyPair = SshKeyUtils.getPrivateKey(new File("private"));
SshPrivateKey readPrivateKey = readKeyPair.getPrivateKey();
byte[] signature = readPrivateKey.sign(hash);
Base64.Encoder encoder = Base64.getEncoder();
return encoder.encodeToString(signature);
}
public static void main(String[] args) {
Map<String, String> data = new HashMap<>();
data.put("key", "value");
String message = gson.toJson(data);
String pythonSignature = "5Sdt3bIKFbLBhbZ2JLzQP+8MNX6/uzFtxHTkBa/UIpBbjtwKfNu+wfcMHmxksQkmzI5OMhEpY46hVlkM0P5nAA==";
verifyV1Signature(message, pythonSignature);
try {
String javaSignature = generateV1Signature(message);
System.out.println(javaSignature);
} catch (NoSuchAlgorithmException | IOException | InvalidPassphraseException e) {
e.printStackTrace();
}
}
}
运行消息的 Python 代码
json.dumps({"key": "value"})
给出
5Sdt3bIKFbLBhbZ2JLzQP+8MNX6/uzFtxHTkBa/UIpBbjtwKfNu+wfcMHmxksQkmzI5OMhEpY46hVlkM0P5nAA==
运行 Java代码给出
xHgYq8/nUYOkpbGzCsUkei9Vw0O1/XKoYZlLAbsUPpQF3cTMQ96ROL/ZHSH+cUUNJlmTI2Qb2thAU3kEqvdHBQ==
并且验证失败。
private
密钥看起来像
-----BEGIN OPENSSH PRIVATE KEY-----<suff>-----END OPENSSH PRIVATE KEY-----
而公钥看起来像
ssh-ed25519 <stuff>
为什么签名不匹配?我也尝试过
bouncycastle
但签名仍然不匹配。
签名不匹配的原因是没有对要签名的消息使用相同的哈希。
- 的 Python 代码使用原始消息创建一个 SHA-512 哈希。
- 的 Java 代码在 sshtools 库处理消息时会创建一个“ssh-ed25519”格式的预哈希。
为了使签名匹配,需要在 Python 代码中创建与 Java 代码相同的预哈希消息。可以使用 sshtools 库,或按照“ssh-ed25519”格式的 RFC 8032 中的说明手动执行此操作:
from Crypto.Hash import SHA512
from Crypto.PublicKey import ECC
from Crypto.Signature import eddsa
import base64
import json
import struct
def generate_signature_v1(message):
message = message.replace(" ", "")
message = message.encode('utf-8')
# 创建“ssh-ed25519”预哈希消息
prehashed_message = b"ssh-ed25519" + struct.pack(">I", len(message)) + message
h = SHA512.new(prehashed_message)
with open("private", "r") as f:
key = ECC.import_key(f.read())
signer = eddsa.new(key, "rfc8032")
signature = signer.sign(h.digest())
str_signature = base64.standard_b64encode(signature).decode("utf-8")
return str_signature
通过对要签名的消息应用相同的预哈希,的 Python 和 Java 代码现在应该会生成匹配的签名。
标签:python,java,cryptography,pycryptodome,eddsa From: 78818365