Как восстановить закрытый ключ с помощью алгоритма подписи 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
Исключение stacktrace:
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:
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;
}
2 ответа
Решение
Поскольку
privateKey.getEncoded
(экземпляр будет
EdDSAPrivateKeyImpl
type) вернет ключ в формате PKCS8, вы должны использовать
PKCS8EncodedKeySpec
:
var keyFactory = KeyFactory.getInstance("EdDSA");
var pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
var privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
В противном случае вам пришлось бы самостоятельно развернуть последовательность и получить
PrivateKey
(это OctetString), как описано в RFC5958-section2. Тогда вы могли бы использовать
EdECPrivateKeySpec
.
Способ №1:
var keyPairGenerator = KeyPairGenerator.getInstance("Ed25519");
var keyPair = keyPairGenerator.generateKeyPair();
var privateKey = (EdECPrivateKey) keyPair.getPrivate();
var publicKey = (EdECPublicKey) keyPair.getPublic();
byte[] privateKeyBytes = privateKey.getBytes().orElseThrow();
var edecPoint = publicKey.getPoint();
BigInteger y = edecPoint.getY();
boolean xOdd = edecPoint.isXOdd();
// reconstruct the keys using (privateKeyBytes), (y) and (xOdd)
var keyFactory = KeyFactory.getInstance("Ed25519");
var edECPublicKeySpec = new EdECPublicKeySpec(NamedParameterSpec.ED25519, new EdECPoint(xOdd, y));
var publicKey2 = keyFactory.generatePublic(edECPublicKeySpec);
var edECPrivateKeySpec = new EdECPrivateKeySpec(NamedParameterSpec.ED25519, privateKeyBytes);
var privateKey2 = keyFactory.generatePrivate(edECPrivateKeySpec);
Способ №2:
var keyPairGenerator = KeyPairGenerator.getInstance("Ed25519");
var keyPair = keyPairGenerator.generateKeyPair();
var privateKey = keyPair.getPrivate();
var publicKey = keyPair.getPublic();
byte[] privateKeyBytes = privateKey.getEncoded();
byte[] publicKeyBytes = publicKey.getEncoded();
// reconstruct the keys using (privateKeyBytes) and (publicKeyBytes)
var keyFactory = KeyFactory.getInstance("Ed25519");
var publicKey2 = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
var privateKey2 = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));