【问题标题】:How to regenerate private key using EdDSA/Ed448 signature algorithm?如何使用 EdDSA/Ed448 签名算法重新生成私钥?
【发布时间】:2021-01-07 10:52:18
【问题描述】:

这是我生成密钥对的方式:

var keyPairGenerator = KeyPairGenerator.getInstance("Ed448");
keyPairGenerator.initialize(448, SecureRandom.getInstanceStrong());
var keyPair = keyPairGenerator.generateKeyPair();
var privateKey = keyPair.getPrivate();
var publicKey = keyPair.getPublic();
byte[] privateKeyBytes = privateKey.getEncoded();
byte[] publicKeyBytes = publicKey.getEncoded();

但是,当我再次尝试重新生成密钥时,出现异常:

var keyFactory = KeyFactory.getInstance("EdDSA");
var edECPoint = byteArrayToEdPoint(publicKeyBytes);
var edECPublicKeySpec = new EdECPublicKeySpec(new NamedParameterSpec("Ed448"), edECPoint);
var publicKey = keyFactory.generatePublic(edECPublicKeySpec);
var edECPrivateKeySpec = new EdECPrivateKeySpec(new NamedParameterSpec("Ed448"), privateKeyBytes);
var privateKey = keyFactory.generatePrivate(edECPrivateKeySpec); // generatePrivate() throws exception

异常堆栈跟踪:

Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: key length is 73, key length must be 57
    at jdk.crypto.ec/sun.security.ec.ed.EdDSAKeyFactory.engineGeneratePrivate(EdDSAKeyFactory.java:129)
    at java.base/java.security.KeyFactory.generatePrivate(KeyFactory.java:390)
    at io.fouad.Main.main(Main.java:48)
Caused by: java.security.InvalidKeyException: key length is 73, key length must be 57
    at jdk.crypto.ec/sun.security.ec.ed.EdDSAPrivateKeyImpl.checkLength(EdDSAPrivateKeyImpl.java:84)
    at jdk.crypto.ec/sun.security.ec.ed.EdDSAPrivateKeyImpl.<init>(EdDSAPrivateKeyImpl.java:61)
    at jdk.crypto.ec/sun.security.ec.ed.EdDSAKeyFactory.generatePrivateImpl(EdDSAKeyFactory.java:171)
    at jdk.crypto.ec/sun.security.ec.ed.EdDSAKeyFactory.engineGeneratePrivate(EdDSAKeyFactory.java:127)
    ... 2 more

这是我从OpenJDK repo复制的一些实用程序:

private static EdECPoint byteArrayToEdPoint(byte[] arr)
{
    byte msb = arr[arr.length - 1];
    boolean xOdd = (msb & 0x80) != 0;
    arr[arr.length - 1] &= (byte) 0x7F;
    reverse(arr);
    BigInteger y = new BigInteger(1, arr);
    return new EdECPoint(xOdd, y);
}

private static void reverse(byte [] arr)
{
    int i = 0;
    int j = arr.length - 1;
    
    while(i < j)
    {
        swap(arr, i, j);
        i++;
        j--;
    }
}

private static void swap(byte[] arr, int i, int j)
{
    byte tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}

【问题讨论】:

  • 你试过JEP-339票的例子吗?
  • @flaxel 该示例仅关于重新生成公钥,并且使用Ed25519 而不是Ed448
  • 使用PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);并将其传递给keyFactory.generatePrivate
  • @michalk PKCS8EncodedKeySpec 用于生成 RSA 私钥。我不确定这是否是正确的方法。
  • PKCS8 是描述私钥格式的标准。它不仅可以表示 RSA 密钥...

标签: java cryptography digital-signature java-15 eddsa


【解决方案1】:

由于privateKey.getEncoded(实例将是EdDSAPrivateKeyImpl 类型)将返回PKCS8 格式的密钥,您应该使用PKCS8EncodedKeySpec

var keyFactory = KeyFactory.getInstance("EdDSA");
var pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
var privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);

否则你必须自己解开序列并得到PrivateKey(这是OctetString),如RFC5958-section2中所述。然后你可以使用EdECPrivateKeySpec

【讨论】:

  • +1 感谢您的帮助。实际上,我是通过调用 ((EdECPrivateKey) keyPair.getPrivate()).getBytes().orElseThrow() 来实现的。
  • 如果您已经拥有 EdECPrivateKey 的实例,这将起作用,但如果您想从包含 PKCS8 格式密钥的文本文件中重建它 - 您将无法执行此操作.
  • ((EdECPrivateKey) keyPair.getPrivate()).getBytes().orElseThrow() 将返回一个字节数组,然后我可以像这样使用它:new EdECPrivateKeySpec(NamedParameterSpec.ED448, byteArray);
  • 但是,byteArrayToEdPoint() 好像不太好用,我得到了y value is too large 什么时候做验证:( 我会试着调查一下。
  • 我最终使用了keyFactory.generatePublic(new X509EncodedKeySpec(publicKey.getEncoded()))keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey.getEncoded()))。再次感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-26
  • 2015-08-25
  • 2021-06-22
  • 2017-03-07
  • 2012-08-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多