EdDSA

EdDSA(Edwards-curve Digital Signature Algorithm)Edwards-Curve 곡선에 의한 전자 서명 알고리즘을 의미한다. 과거에 ED25519에 대해서 정리한 것과 같이 ECDSA와 혼동이 될 수 있다. RFC8410에서는 Internet X.509 Public Key Infrastructure 에서 Ed25519과 같은 Algorithm Identifiers에 대한 표준을 설명하고 있다.

Curve25519 and Curve448 Algorithm Identifiers

BC(Bouncy Castle)의 ECNamedCurveTable.getNames()에는 기본적인 EC 곡선이 포함되어있고 curve25519는 CustomNamedCurves에 포함되어 있어 Curve25519 곡선에 대한 X9ECParameters를 아래와 같이 ECParameterSpec으로 변경할 수 있다.

public static AlgorithmParameterSpec curve25519Spec() {
    // NOTE: Ed25519 not contains in ECNamedCurveTable.getNames()
    X9ECParameters ecP = CustomNamedCurves.getByName("curve25519");
    return new ECParameterSpec(ecP.getCurve(), ecP.getG(), ecP.getN(), ecP.getH(), ecP.getSeed());
}

Ed25519 키 페어 생성

Ed25519KeyPairGenerator와 함께 ECKeyGenerationParameters를 사용하면 Ed25519 키 페어를 생성할 수 있다. 그런데 이미 Ed25519KeyGenerationParameters를 제공하고 있으므로 굳이 ECParameterSpec를 사용하는 코드를 작성할 필요가 없다. 만약, 굳이 ECParameterSpec으로 ECKeyGenerationParameters를 만들어서 사용하고 싶은 사람들을 위해 예제 코드를 첨부한다.

@Test
void TestKeyPairUsingEd25519Generator() {
    Ed25519KeyPairGenerator keyPairGenerator = new Ed25519KeyPairGenerator();
    keyPairGenerator.init(new Ed25519KeyGenerationParameters(new SecureRandom()));

    AsymmetricCipherKeyPair keyPair = keyPairGenerator.generateKeyPair();
}

@Test
void TestKeyPairUsingECParams() {
    ECParameterSpec curve25519Spec = (ECParameterSpec) curve25519Spec();
    ECDomainParameters ecParams = new ECDomainParameters(curve25519Spec.getCurve(), curve25519Spec.getG(), curve25519Spec.getN(), curve25519Spec.getH());
    ECKeyGenerationParameters ecKeyGenerationParameters = new ECKeyGenerationParameters(ecParams, new SecureRandom());

    Ed25519KeyPairGenerator keyPairGenerator = new Ed25519KeyPairGenerator();
    keyPairGenerator.init(ecKeyGenerationParameters);

    AsymmetricCipherKeyPair keyPair = keyPairGenerator.generateKeyPair();
}

Ed25519PrivateKeyParameters를 PrivateKey로 변환

클라이언트의 X.509 인증서를 만드는 경우 서명을 위한 PrivateKey가 필요한데 직접적으로 변환할 수가 없으므로 KeyFactorySpi.Ed25519PrivateKeyInfoFactory를 사용해서 Ed25519PrivateKeyParameters로 되어있는 비밀키를 PrivateKey 클래스로 변경해야 한다.

// NOTE: AsymmetricCipherKeyPair keyPair
Ed25519PrivateKeyParameters privateKeyP = (Ed25519PrivateKeyParameters) keyPair.getPrivate();

KeyFactorySpi.Ed25519 keyFactory = new KeyFactorySpi.Ed25519();
PrivateKey privateKey = keyFactory.generatePrivate(PrivateKeyInfoFactory.createPrivateKeyInfo(privateKeyP));

공개키를 SubjectPublicKeyInfoFactory로 변환

Ed25519PublicKeyParameters로 되어있는 공개키를 SubjectPublicKeyInfo로 변환하는 것은 SubjectPublicKeyInfoFactory 클래스에서 제공하고 있다. 앞서 비밀키처럼 KeyFactorySpi.Ed25519를 사용해서 PublicKey로 변환할 필요는 없을 것 같다.

// NOTE: AsymmetricCipherKeyPair keyPair
Ed25519PublicKeyParameters publicKeyP = (Ed25519PublicKeyParameters) keyPair.getPublic();
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(publicKeyP);

EdDSA Signatures

Curve25519와 Curve448를 사용한 EdDSA는 각각 Ed25519Ed448 이라는 서명 알고리즘으로 표현한다. 앞서, PrivateKey와 SubjectPublickKeyInfo로 변환한 상태이므로 X.509 인증서를 만드는 것은 어렵지 않을 것이다.

X500Name issuer = new X500Name("CN=Mambo");
BigInteger serialNumber = new BigInteger(128, new SecureRandom());
ZonedDateTime now = ZonedDateTime.now();
Date notBefore = Date.from(now.toInstant());
Date notAfter = Date.from(now.plus(1, ChronoUnit.YEARS).toInstant());

ContentSigner contentSigner = new JcaContentSignerBuilder("Ed25519").setProvider(BouncyCastleProvider.PROVIDER_NAME).build(privateKey);
X509CertificateHolder certificateHolder = new JcaX509v3CertificateBuilder(issuer, serialNumber, notBefore, notAfter, issuer, publicKeyInfo).build(contentSigner);
X509Certificate certificate = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getCertificate(certificateHolder);

certificate.checkValidity(new Date());

KeyPairGeneratorSpi로 Ed25519 키 페어 생성

사실은 BC에서 KeyPairGeneratorSpi를 제공하고 있으므로 아래와 같이 BCEdDSAPrivateKeyBCEdDSAPublicKey를 가지는 키 페어를 생성할 수 있다.

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("Ed25519", BouncyCastleProvider.PROVIDER_NAME);
KeyPair keyPair = keyPairGenerator.generateKeyPair();

더 자세한 코드는 EdDSATest.java에서 확인할 수 있다.