如何使用EdDSA / Ed448签名算法重新生成私钥?

问题描述

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

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

解决方法

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

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

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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...