簡體   English   中英

Java 11 Curve25519 實現不像 Signal 的庫

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

在 Java 11 中引入了 curve25519 內置實現。 由於我對此一無所知,並且最近才發現它,因此我正在使用Signal 的庫 這是我在切換到 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)公鑰和私鑰定義的編碼是無符號固定長度 little-endian,而BigInteger.toByteArray()返回並被 ctor BigInteger(byte[])接受的表示是雙補變長大端。 由於 255 位四舍五入到 32 字節,而備用位始終為零(對於 XDH),因此可以忽略符號差異,但其他的很重要。

JCA 確實使接口 class XECPrivateKey返回,並且相應的Spec接受,這些 forms,但對於XECPublicKey[Spec]它使用BigInteger 它確實使用 Bernstein forms 一致地用於(分別)由Key.getEncoded() ) 返回並被KeyFactory接受的“X509”和“PKCS8”編碼,但這些元數據具有僅 XDH(或僅 Bernstein XDH)的元數據-and-EdDSA) 系統,如 X3DH 不使用。

AFAICS 你的選擇是

  • 需要時,字節反轉和(零)填充代碼中的 JCA 公共值,或者
  • 使用Key.getEncoded()並解析特定於算法的部分,或者反過來構建算法通用結構以作為X509EncodedKeySpec傳遞給KeyFactory.getInstance("Xblah")

過去曾針對其他算法詢問過第二種方法:“傳統”(X9 風格)EC——尤其是比特幣和相關硬幣的 secp256k1,它通常只使用沒有元數據的原始 X9/SECG 數據——和RSA,其中一些系統使用原始 PKCS1 格式(這里的私鑰比公鑰更常見); 如果您願意,我可以找到一些幾乎重復的內容來說明該方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM