I have a python library that uses pycryptodome
library to sign data using Ed25519 algorithm using an openssh format ED25519 private key. The signature then needs to be verified in a Java application using sshtools
library with corresponding public key. However the signature verification is failing.
Constraint: It’s important to read the private/public keys from files. I cannot change the Python code and/or the keys used.
To debug, I wrote a simple implementation to sign the data in Java as well and compared the signature with that generated with Python. However both are coming different.
My Python implementation to sign the data is as follows:
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
My Java implementation to generate and verify the signature.
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();
}
}
}
Running Python code for message json.dumps({"key": "value"})
gives 5Sdt3bIKFbLBhbZ2JLzQP+8MNX6/uzFtxHTkBa/UIpBbjtwKfNu+wfcMHmxksQkmzI5OMhEpY46hVlkM0P5nAA==
Running Java Code gives xHgYq8/nUYOkpbGzCsUkei9Vw0O1/XKoYZlLAbsUPpQF3cTMQ96ROL/ZHSH+cUUNJlmTI2Qb2thAU3kEqvdHBQ==
and also verification fails.
The private
key looks like -----BEGIN OPENSSH PRIVATE KEY-----<suff>-----END OPENSSH PRIVATE KEY-----
and the public key looks like ssh-ed25519 <stuff>
Why signature is not matching? I have also tried bouncycastle
and still signature is not matching.