首页 > 编程问答 >Java sshtools 生成的 EDDSA 签名与 Python 的 pycryptome 生成的签名不匹配

Java sshtools 生成的 EDDSA 签名与 Python 的 pycryptome 生成的签名不匹配

时间:2024-08-01 05:35:50浏览次数:10  
标签:python java cryptography pycryptodome eddsa

我有一个 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

相关文章

  • Elastic python请求超时错误:池达到最大大小,不允许更多连接
    我正在使用Elasticsearchpython模块。我正在尝试像这样建立到服务器的连接es=Elasticsearch([config.endpoint],api_key=config.key,request_timeout=config.request_timeout)服务器连接,然后我尝试执行丰富策略。es.enr......
  • 使用 python 将 JSON 数据空值导入数据库。收到此错误 - 数据需要字符串或类似字节的
    我正在尝试使用python将JSON数据集导入到我的PostgreSQL数据库,但在尝试导入null值时会抛出错误。表的名称是Loan_info。我在python中尝试过此操作:-forfieldinloan_info:ifloan_info[field]in['Null','null',None]:......
  • Python 将脚本转换为 exe 并给出 PermissionError
    我有一个Python(Windows10)脚本,其功能之一是创建备份。这是函数:defcreate_backups(self,file:str,counter:int=None)->None:counter=counteror1res=self.re_obj.match(file)ifresisNoneorlen(res.groups())==0:back_file=......
  • conda update python 不会更新,但 conda update --all 会更新
    我正在尝试更新我的venv。这就是我看到的(base_test)>condaupdatepythonCollectingpackagemetadata(current_repodata.json):doneSolvingenvironment:done==>WARNING:Anewerversionofcondaexists.<==currentversion:4.10.3latestversion:24......
  • 如何使用 Selenium (python) 访问另一个影子根中影子根中的元素?
    我有以下代码和HTML结构(我不是这方面的专家)。我正在尝试抓取HTML代码末尾的96.00C元素,其路径是:Xpath://*[@id="_grid"]/set-class2/div2/text-binding//text()完整Xpath:/html/body/main/div/div3/div3/......
  • 在Python中,如何在一段时间内接受输入
    我正在尝试用Python制作一个蛇游戏,但不知道如何制作它,以便蛇在没有玩家输入的情况下继续移动,所以如果有人知道一个简单的方法来做到这一点,我需要在2秒后取消输入将不胜感激代码如下:importrandomimportsysplayerY=(1)playerX=(0)appleY=random.randint(1,10)appl......
  • 在Python中单步执行代码时是否可以引发异常
    当我在IDE(例如PyCharm)中单步执行代码时,我想转储函数的参数(以供以后使用,例如复制它)。计划的场景是在某处设置断点,然后引发异常(这不在我运行的代码中,这就是重点),并捕获它。代码应该如下所示:defexception_cathcher_decorator(func):try:returnfunc(*f_args,**f_k......
  • 解决python自动化操作异常处理的问题
    在python自动化领域,往往要用到pyautogui,pywin32等模块实现自动化操作。然而,这种自动化操作,本身具有一定的局限性,其中最主要的一个问题就是,一旦执行结果不按照脚本预设的来执行,往往会抛出异常,导致程序中断。解决这个问题,主要有这么几种思路:第一,每一次操作后分情况讨论。这种方......
  • Python爬虫入门03:用Urllib假装我们是浏览器
    文章目录引言Urllib库简介Request模块详解Error模块与异常处理Parse模块与URL解析Robotparser模块模拟浏览器请求使用Request方法添加请求头信息代码示例1.设置请求URL和请求头2.定义请求参数并转换为适当的格式3.使用Request方法封装请求4.发送请求并获取响应常用......
  • JAVA里的反射(详解)
    1.反射1.1反射的概述:专业的解释(了解一下):是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意属性和方法;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。通俗的理解:(掌握)利用反射创建的对象可以无......