Java 11 Curve25519实现不';t表现为信号';s图书馆

Java 11 Curve25519实现不';t表现为信号';s图书馆,java,cryptography,java-11,java-16,curve-25519,Java,Cryptography,Java 11,Java 16,Curve 25519,在Java 11中,引入了curve25519内置实现。因为我不知道这个,而且是最近才发现的,所以我正在使用。这是我切换到Java 11实现之前的代码: private final Curve25519 CURVE_25519 = Curve25519.getInstance(Curve25519.JAVA); public Curve25519KeyPair calculateRandomKeyPair() { return CURVE_25519.generateKeyPair()

在Java 11中,引入了curve25519内置实现。因为我不知道这个,而且是最近才发现的,所以我正在使用。这是我切换到Java 11实现之前的代码:

private final Curve25519 CURVE_25519 = Curve25519.getInstance(Curve25519.JAVA);

public Curve25519KeyPair calculateRandomKeyPair() {
    return CURVE_25519.generateKeyPair();
}

public byte[] calculateSharedSecret(byte[] publicKey, byte[] privateKey) {
    return CURVE_25519.calculateAgreement(publicKey, privateKey);
}
这是我现在的代码:

private final String XDH = "XDH";
private final String CURVE = "X25519";

@SneakyThrows
public KeyPair calculateRandomKeyPair() {
    return KeyPairGenerator.getInstance(CURVE).generateKeyPair();
}

@SneakyThrows
public byte[] calculateSharedSecret(byte[] publicKeyBytes, XECPrivateKey privateKey) {
    var paramSpec = new NamedParameterSpec(CURVE);
    var keyFactory = KeyFactory.getInstance(XDH);

    var publicKeySpec = new XECPublicKeySpec(paramSpec, new BigInteger(publicKeyBytes));
    var publicKey = keyFactory.generatePublic(publicKeySpec);

    var keyAgreement = KeyAgreement.getInstance(XDH);
    keyAgreement.init(privateKey);
    keyAgreement.doPhase(publicKey, true);
    return keyAgreement.generateSecret();
}
显然,第二个实现不起作用,而第一个实现起作用。起初,我认为我做错了什么,所以我阅读了文档并检查了类似的答案,尽管如此,由于我没有发现任何有用的东西,我决定进一步挖掘,并尝试检查Signal的库和Java是否在给定私钥的情况下生成相同的公钥。为此,我编写了以下代码段:

import org.whispersystems.curve25519.Curve25519;
import sun.security.ec.XECOperations;
import sun.security.ec.XECParameters;

import java.security.InvalidAlgorithmParameterException;
import java.security.spec.NamedParameterSpec;
import java.util.Arrays;

private static boolean generateJava11KeyPair() throws InvalidAlgorithmParameterException {
    var signalKeyPair = Curve25519.getInstance(Curve25519.JAVA).generateKeyPair();
    
    var signalPublicKey = signalKeyPair.getPublicKey();
    var params = XECParameters.get(InvalidAlgorithmParameterException::new, NamedParameterSpec.X25519);
    var ops = new XECOperations(params);
    var javaPublicKey = ops.computePublic(signalKeyPair.getPrivateKey().clone()).toByteArray();
    
    return Arrays.equals(signalPublicKey, javaPublicKey);
}
(Java实现后用于计算公钥的代码摘自sun.security.ec.XDHKeyPairGenerator)


此方法打印false,这意味着两个实现的行为实际上并不相同。在这一点上,我想知道这是一个Java错误还是我遗漏了什么。提前感谢。

Bernstein等人为X25519(和X448)公钥和私钥定义的编码是无符号固定长度的小尾端,而由
BigInteger.toByteArray()
返回并被ctor
BigInteger(字节[])接受的表示形式是两个补变量长度的大尾端。由于255位用一个始终为零的备用位(对于XDH)将最多32个字节取整,因此可以忽略符号性差异,但其他差异很重要

JCA确实让inteface类
XECPrivateKey
返回,并且相应的
Spec
接受这些表单,但是对于
XECPublicKey[Spec]
它使用
biginger
。对于
Key.getEncoded()
返回并被
KeyFactory
接受的“X509”和“PKCS8”编码,它确实(分别)一致地使用Bernstein表单,但那些元数据只有XDH(或只有Bernstein的XDH和EdDSA)系统,如X3DH不使用

你的选择是什么

  • 需要时字节反转和(零)填充代码中的JCA公共值,或
  • 使用
    Key.getEncoded()
    并解析特定于算法的部分,或者反过来构建算法通用结构,以作为
    X509EncodedKeySpec
    传递到
    KeyFactory.getInstance(“Xblah”)

第二种方法在过去曾被问及其他算法:“传统”(X9样式)EC——特别是用于比特币和相关硬币的secp256k1,通常只使用raw-X9/SECG数据,不使用元数据——以及RSA,其中一些系统使用raw-PKCS1格式(这里通常用于私钥而不是公钥);如果您愿意,我可以找到一些近似的副本来说明该方法。

您好,非常感谢您提供清晰详细的答案!如果你能提供你所说的例子,我将不胜感激