简体   繁体   English

Java 11 Curve25519 实现不像 Signal 的库

[英]Java 11 Curve25519 Implementation doesn't behave as Signal's libary

In Java 11 a curve25519 built-in implementation was introduced.在 Java 11 中引入了 curve25519 内置实现。 As I had no idea of this, and only discovered it recently, I was using a library from Signal .由于我对此一无所知,并且最近才发现它,因此我正在使用Signal 的库 This was my code before I switched to Java 11's implementation:这是我在切换到 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);
}

And this is my code now:这是我现在的代码:

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();
}

Obviously, the second implementation doesn't work while the first one does.显然,第二个实现不起作用,而第一个实现。 Initially, I thought I was doing something wrong so I read the documentation and checked similar answers, though, as I didn't find anything helpful, I decided to dig further and tried to check if both Signal's library and Java generate the same public key given the private one.最初,我认为我做错了什么,所以我阅读了文档并检查了类似的答案,但是,由于我没有发现任何有用的信息,我决定进一步挖掘并尝试检查 Signal 的库和 Java 是否生成相同的公钥给私人的。 To do this I wrote this snippet:为此,我编写了以下代码段:

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);
}

(the code used to calculate the public key following Java's implementation was extracted from sun.security.ec.XDHKeyPairGenerator) (Java 实现之后用于计算公钥的代码是从 sun.security.ec.XDHKeyPairGenerator 中提取的)

This method prints false, which means that the two implementations actually don't behave in the same way.此方法打印 false,这意味着这两个实现实际上的行为方式不同。 At this point, I'm wondering if this is a Java bug or if I'm missing something.在这一点上,我想知道这是一个 Java 错误还是我遗漏了什么。 Thanks in advance.提前致谢。

The encoding defined by Bernstein et al for X25519 (and X448) keys both public and private is unsigned fixed-length little-endian, while the representation returned by BigInteger.toByteArray() and accepted by ctor BigInteger(byte[]) is twos-complement variable-length big-endian. Bernstein 等人为 X25519(和 X448)公钥和私钥定义的编码是无符号固定长度 little-endian,而BigInteger.toByteArray()返回并被 ctor BigInteger(byte[])接受的表示是双补变长大端。 Since 255 bits rounds up to 32 bytes with a spare bit that is always zero (for XDH) the signedness difference can be ignored there, but the others matter.由于 255 位四舍五入到 32 字节,而备用位始终为零(对于 XDH),因此可以忽略符号差异,但其他的很重要。

JCA did make the inteface class XECPrivateKey return, and the corresponding Spec accept, these forms, but for XECPublicKey[Spec] it uses BigInteger . JCA 确实使接口 class XECPrivateKey返回,并且相应的Spec接受,这些 forms,但对于XECPublicKey[Spec]它使用BigInteger It does use the Bernstein forms consistently for (both) the "X509" and "PKCS8" encodings (respectively) returned by Key.getEncoded() and accepted by a KeyFactory , but those have metadata that XDH-only (or Bernstein-only XDH-and-EdDSA) systems like X3DH don't use.它确实使用 Bernstein forms 一致地用于(分别)由Key.getEncoded() ) 返回并被KeyFactory接受的“X509”和“PKCS8”编码,但这些元数据具有仅 XDH(或仅 Bernstein XDH)的元数据-and-EdDSA) 系统,如 X3DH 不使用。

AFAICS your choices are AFAICS 你的选择是

  • byte-reverse and (zero-)pad when needed the JCA public values in your code, or需要时,字节反转和(零)填充代码中的 JCA 公共值,或者
  • use Key.getEncoded() and parse the algorithm-specific part or conversely build the algorithm-generic structure to pass as X509EncodedKeySpec to KeyFactory.getInstance("Xblah") .使用Key.getEncoded()并解析特定于算法的部分,或者反过来构建算法通用结构以作为X509EncodedKeySpec传递给KeyFactory.getInstance("Xblah")

The second approach has been asked about in the past for other algorithms: 'traditional' (X9-style) EC -- especially secp256k1 for bitcoin and related coins, which generally use only the raw-X9/SECG data with no metadata -- and RSA where a few systems use the raw-PKCS1 formats (here more commonly for privatekey than publickey);过去曾针对其他算法询问过第二种方法:“传统”(X9 风格)EC——尤其是比特币和相关硬币的 secp256k1,它通常只使用没有元数据的原始 X9/SECG 数据——和RSA,其中一些系统使用原始 PKCS1 格式(这里的私钥比公钥更常见); if you want I can find some near-duplicates to illustrate the approach.如果您愿意,我可以找到一些几乎重复的内容来说明该方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 无法在Android设备上运行Curve25519实现-能够在PC上运行 - Can't run Curve25519 implementation on Android device - able to on PC 使用ECC Curve25519在Java中加密/解密数据 - Using ECC Curve25519 to encrypt/decrypt data in Java java.lang.IllegalArgumentException:string curve25519 not a OID bouncycastle 1.52 - java.lang.IllegalArgumentException: string curve25519 not an OID bouncycastle 1.52 Curve25519 公钥是 309 字节,私钥是 587。不应该是 32 字节吗? - Curve25519 public key is 309 bytes and private key is 587. Shouldn't it be 32 bytes? 有没有办法在Android上使用Curve25519椭圆曲线加密法? - Is there a way to use Curve25519 Elliptic Curve encryption method on Android? 如何为diffie hellman算法生成curve25519密钥对? - How to generate curve25519 key pair for diffie hellman algorithm? Curve25519 密码仅接受 32 字节,但我在 Kotlin 中得到错误的公钥大小 - Curve25519 cipher accepts only 32 byte but I get wrong public key size in Kotlin Floyd算法的Java实现不适用于无向图 - Java implementation of Floyd's algorithm doesn't work for undirected graphs Z阶曲线java实现 - Z Order Curve java implementation Java HashSet remove(e)方法的行为与我的预期不符 - Java HashSet remove(e) method doesn't behave as I expected
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM