Skip to content

Commit 1320236

Browse files
committed
jdk: implement EdDSA/XDH; RAW key wrap/unwrap helper (PKCS#8 only); wire provider; use base unwrapSubjectPublicKeyInfo(Set) + ObjectIdentifier OIDs
1 parent 1d0f98f commit 1320236

File tree

4 files changed

+247
-0
lines changed

4 files changed

+247
-0
lines changed

cryptography-providers/jdk/src/jvmMain/kotlin/JdkCryptographyProvider.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,9 @@ internal class JdkCryptographyProvider(provider: Provider?) : CryptographyProvid
131131
RSA.PKCS1 -> JdkRsaPkcs1(state)
132132
RSA.RAW -> JdkRsaRaw(state)
133133
ECDSA -> JdkEcdsa(state)
134+
EdDSA -> JdkEdDSA(state)
134135
ECDH -> JdkEcdh(state)
136+
XDH -> JdkXDH(state)
135137
PBKDF2 -> JdkPbkdf2(state)
136138
HKDF -> JdkHkdf(state, this)
137139
else -> null
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package dev.whyoleg.cryptography.providers.jdk.algorithms
2+
3+
import dev.whyoleg.cryptography.algorithms.*
4+
import dev.whyoleg.cryptography.materials.key.*
5+
import dev.whyoleg.cryptography.operations.*
6+
import dev.whyoleg.cryptography.providers.jdk.*
7+
import dev.whyoleg.cryptography.providers.jdk.materials.*
8+
import dev.whyoleg.cryptography.providers.base.materials.*
9+
import dev.whyoleg.cryptography.providers.jdk.operations.*
10+
import dev.whyoleg.cryptography.serialization.pem.*
11+
import dev.whyoleg.cryptography.serialization.asn1.*
12+
import dev.whyoleg.cryptography.serialization.asn1.modules.*
13+
14+
internal class JdkEdDSA(private val state: JdkCryptographyState) : EdDSA {
15+
private fun curveName(curve: EdDSA.Curve): String = when (curve) {
16+
EdDSA.Curve.Ed25519 -> "Ed25519"
17+
EdDSA.Curve.Ed448 -> "Ed448"
18+
}
19+
private fun oid(curve: EdDSA.Curve): ObjectIdentifier = when (curve) {
20+
EdDSA.Curve.Ed25519 -> ObjectIdentifier.Ed25519
21+
EdDSA.Curve.Ed448 -> ObjectIdentifier.Ed448
22+
}
23+
24+
override fun publicKeyDecoder(curve: EdDSA.Curve): KeyDecoder<EdDSA.PublicKey.Format, EdDSA.PublicKey> =
25+
object : JdkPublicKeyDecoder<EdDSA.PublicKey.Format, EdDSA.PublicKey>(state, curveName(curve)) {
26+
override fun JPublicKey.convert(): EdDSA.PublicKey = EdDsaPublicKey(state, this)
27+
28+
override fun decodeFromByteArrayBlocking(format: EdDSA.PublicKey.Format, bytes: ByteArray): EdDSA.PublicKey = when (format) {
29+
EdDSA.PublicKey.Format.JWK -> error("JWK is not supported")
30+
EdDSA.PublicKey.Format.RAW -> decodeFromDer(
31+
wrapSubjectPublicKeyInfo(UnknownKeyAlgorithmIdentifier(oid(curve)), bytes)
32+
)
33+
EdDSA.PublicKey.Format.DER -> decodeFromDer(bytes)
34+
EdDSA.PublicKey.Format.PEM -> decodeFromDer(unwrapPem(PemLabel.PublicKey, bytes))
35+
}
36+
}
37+
38+
override fun privateKeyDecoder(curve: EdDSA.Curve): KeyDecoder<EdDSA.PrivateKey.Format, EdDSA.PrivateKey> =
39+
object : JdkPrivateKeyDecoder<EdDSA.PrivateKey.Format, EdDSA.PrivateKey>(state, curveName(curve)) {
40+
override fun JPrivateKey.convert(): EdDSA.PrivateKey = EdDsaPrivateKey(state, this)
41+
42+
override fun decodeFromByteArrayBlocking(format: EdDSA.PrivateKey.Format, bytes: ByteArray): EdDSA.PrivateKey = when (format) {
43+
EdDSA.PrivateKey.Format.JWK -> error("JWK is not supported")
44+
EdDSA.PrivateKey.Format.RAW -> decodeFromDer(
45+
wrapPrivateKeyInfo(
46+
0,
47+
UnknownKeyAlgorithmIdentifier(oid(curve)),
48+
bytes
49+
)
50+
)
51+
EdDSA.PrivateKey.Format.DER -> decodeFromDer(bytes)
52+
EdDSA.PrivateKey.Format.PEM -> decodeFromDer(unwrapPem(PemLabel.PrivateKey, bytes))
53+
}
54+
}
55+
56+
override fun keyPairGenerator(curve: EdDSA.Curve): KeyGenerator<EdDSA.KeyPair> = object : JdkKeyPairGenerator<EdDSA.KeyPair>(state, curveName(curve)) {
57+
override fun JKeyPairGenerator.init() {
58+
// no additional init required
59+
}
60+
61+
override fun JKeyPair.convert(): EdDSA.KeyPair = EdDsaKeyPair(
62+
EdDsaPublicKey(state, public),
63+
EdDsaPrivateKey(state, private),
64+
)
65+
}
66+
67+
private class EdDsaKeyPair(
68+
override val publicKey: EdDSA.PublicKey,
69+
override val privateKey: EdDSA.PrivateKey,
70+
) : EdDSA.KeyPair
71+
72+
private class EdDsaPublicKey(
73+
private val state: JdkCryptographyState,
74+
private val key: JPublicKey,
75+
) : EdDSA.PublicKey, JdkEncodableKey<EdDSA.PublicKey.Format>(key) {
76+
override fun signatureVerifier(): SignatureVerifier {
77+
return JdkSignatureVerifier(state, key, "EdDSA", null)
78+
}
79+
80+
override fun encodeToByteArrayBlocking(format: EdDSA.PublicKey.Format): ByteArray = when (format) {
81+
EdDSA.PublicKey.Format.JWK -> error("JWK is not supported")
82+
EdDSA.PublicKey.Format.RAW -> {
83+
val der = encodeToDer()
84+
unwrapSubjectPublicKeyInfo(setOf(ObjectIdentifier.Ed25519, ObjectIdentifier.Ed448), der)
85+
}
86+
EdDSA.PublicKey.Format.DER -> encodeToDer()
87+
EdDSA.PublicKey.Format.PEM -> wrapPem(PemLabel.PublicKey, encodeToDer())
88+
}
89+
}
90+
91+
private class EdDsaPrivateKey(
92+
private val state: JdkCryptographyState,
93+
private val key: JPrivateKey,
94+
) : EdDSA.PrivateKey, JdkEncodableKey<EdDSA.PrivateKey.Format>(key) {
95+
override fun signatureGenerator(): SignatureGenerator {
96+
return JdkSignatureGenerator(state, key, "EdDSA", null)
97+
}
98+
99+
override fun encodeToByteArrayBlocking(format: EdDSA.PrivateKey.Format): ByteArray = when (format) {
100+
EdDSA.PrivateKey.Format.JWK -> error("JWK is not supported")
101+
EdDSA.PrivateKey.Format.RAW -> {
102+
val der = encodeToDer()
103+
KeyInfoUnwrap.unwrapPkcs8ForOids(der, listOf(ObjectIdentifier.Ed25519, ObjectIdentifier.Ed448))
104+
}
105+
EdDSA.PrivateKey.Format.DER -> encodeToDer()
106+
EdDSA.PrivateKey.Format.PEM -> wrapPem(PemLabel.PrivateKey, encodeToDer())
107+
}
108+
}
109+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package dev.whyoleg.cryptography.providers.jdk.algorithms
2+
3+
import dev.whyoleg.cryptography.algorithms.*
4+
import dev.whyoleg.cryptography.materials.key.*
5+
import dev.whyoleg.cryptography.operations.*
6+
import dev.whyoleg.cryptography.providers.jdk.*
7+
import dev.whyoleg.cryptography.providers.jdk.materials.*
8+
import dev.whyoleg.cryptography.providers.base.materials.*
9+
import dev.whyoleg.cryptography.providers.jdk.operations.*
10+
import dev.whyoleg.cryptography.serialization.pem.*
11+
import dev.whyoleg.cryptography.serialization.asn1.*
12+
import dev.whyoleg.cryptography.serialization.asn1.modules.*
13+
14+
internal class JdkXDH(private val state: JdkCryptographyState) : XDH {
15+
private fun curveName(curve: XDH.Curve): String = when (curve) {
16+
XDH.Curve.X25519 -> "X25519"
17+
XDH.Curve.X448 -> "X448"
18+
}
19+
private fun oid(curve: XDH.Curve): ObjectIdentifier = when (curve) {
20+
XDH.Curve.X25519 -> ObjectIdentifier.X25519
21+
XDH.Curve.X448 -> ObjectIdentifier.X448
22+
}
23+
24+
override fun publicKeyDecoder(curve: XDH.Curve): KeyDecoder<XDH.PublicKey.Format, XDH.PublicKey> =
25+
object : JdkPublicKeyDecoder<XDH.PublicKey.Format, XDH.PublicKey>(state, curveName(curve)) {
26+
override fun JPublicKey.convert(): XDH.PublicKey = XdhPublicKey(state, this)
27+
28+
override fun decodeFromByteArrayBlocking(format: XDH.PublicKey.Format, bytes: ByteArray): XDH.PublicKey = when (format) {
29+
XDH.PublicKey.Format.JWK -> error("JWK is not supported")
30+
XDH.PublicKey.Format.RAW -> decodeFromDer(
31+
wrapSubjectPublicKeyInfo(UnknownKeyAlgorithmIdentifier(oid(curve)), bytes)
32+
)
33+
XDH.PublicKey.Format.DER -> decodeFromDer(bytes)
34+
XDH.PublicKey.Format.PEM -> decodeFromDer(unwrapPem(PemLabel.PublicKey, bytes))
35+
}
36+
}
37+
38+
override fun privateKeyDecoder(curve: XDH.Curve): KeyDecoder<XDH.PrivateKey.Format, XDH.PrivateKey> =
39+
object : JdkPrivateKeyDecoder<XDH.PrivateKey.Format, XDH.PrivateKey>(state, curveName(curve)) {
40+
override fun JPrivateKey.convert(): XDH.PrivateKey = XdhPrivateKey(state, this)
41+
42+
override fun decodeFromByteArrayBlocking(format: XDH.PrivateKey.Format, bytes: ByteArray): XDH.PrivateKey = when (format) {
43+
XDH.PrivateKey.Format.JWK -> error("JWK is not supported")
44+
XDH.PrivateKey.Format.RAW -> decodeFromDer(
45+
wrapPrivateKeyInfo(
46+
0,
47+
UnknownKeyAlgorithmIdentifier(oid(curve)),
48+
bytes
49+
)
50+
)
51+
XDH.PrivateKey.Format.DER -> decodeFromDer(bytes)
52+
XDH.PrivateKey.Format.PEM -> decodeFromDer(unwrapPem(PemLabel.PrivateKey, bytes))
53+
}
54+
}
55+
56+
override fun keyPairGenerator(curve: XDH.Curve): KeyGenerator<XDH.KeyPair> = object : JdkKeyPairGenerator<XDH.KeyPair>(state, curveName(curve)) {
57+
override fun JKeyPairGenerator.init() {
58+
// no additional init required
59+
}
60+
61+
override fun JKeyPair.convert(): XDH.KeyPair = XdhKeyPair(
62+
XdhPublicKey(state, public),
63+
XdhPrivateKey(state, private),
64+
)
65+
}
66+
67+
private class XdhKeyPair(
68+
override val publicKey: XDH.PublicKey,
69+
override val privateKey: XDH.PrivateKey,
70+
) : XDH.KeyPair
71+
72+
private class XdhPublicKey(
73+
private val state: JdkCryptographyState,
74+
val key: JPublicKey,
75+
) : XDH.PublicKey, JdkEncodableKey<XDH.PublicKey.Format>(key), SharedSecretGenerator<XDH.PrivateKey> {
76+
private val keyAgreement = state.keyAgreement("XDH")
77+
override fun sharedSecretGenerator(): SharedSecretGenerator<XDH.PrivateKey> = this
78+
override fun generateSharedSecretToByteArrayBlocking(other: XDH.PrivateKey): ByteArray {
79+
check(other is XdhPrivateKey) { "Only key produced by JDK provider is supported" }
80+
return keyAgreement.doAgreement(state, other.key, key)
81+
}
82+
83+
override fun encodeToByteArrayBlocking(format: XDH.PublicKey.Format): ByteArray = when (format) {
84+
XDH.PublicKey.Format.JWK -> error("JWK is not supported")
85+
XDH.PublicKey.Format.RAW -> {
86+
val der = encodeToDer()
87+
unwrapSubjectPublicKeyInfo(setOf(ObjectIdentifier.X25519, ObjectIdentifier.X448), der)
88+
}
89+
XDH.PublicKey.Format.DER -> encodeToDer()
90+
XDH.PublicKey.Format.PEM -> wrapPem(PemLabel.PublicKey, encodeToDer())
91+
}
92+
}
93+
94+
private class XdhPrivateKey(
95+
private val state: JdkCryptographyState,
96+
val key: JPrivateKey,
97+
) : XDH.PrivateKey, JdkEncodableKey<XDH.PrivateKey.Format>(key), SharedSecretGenerator<XDH.PublicKey> {
98+
private val keyAgreement = state.keyAgreement("XDH")
99+
override fun sharedSecretGenerator(): SharedSecretGenerator<XDH.PublicKey> = this
100+
override fun generateSharedSecretToByteArrayBlocking(other: XDH.PublicKey): ByteArray {
101+
check(other is XdhPublicKey) { "Only key produced by JDK provider is supported" }
102+
return keyAgreement.doAgreement(state, key, other.key)
103+
}
104+
105+
override fun encodeToByteArrayBlocking(format: XDH.PrivateKey.Format): ByteArray = when (format) {
106+
XDH.PrivateKey.Format.JWK -> error("JWK is not supported")
107+
XDH.PrivateKey.Format.RAW -> {
108+
val der = encodeToDer()
109+
KeyInfoUnwrap.unwrapPkcs8ForOids(der, listOf(ObjectIdentifier.X25519, ObjectIdentifier.X448))
110+
}
111+
XDH.PrivateKey.Format.DER -> encodeToDer()
112+
XDH.PrivateKey.Format.PEM -> wrapPem(PemLabel.PrivateKey, encodeToDer())
113+
}
114+
}
115+
}
116+
117+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package dev.whyoleg.cryptography.providers.jdk.algorithms
2+
3+
import dev.whyoleg.cryptography.serialization.asn1.*
4+
import dev.whyoleg.cryptography.serialization.asn1.modules.*
5+
import dev.whyoleg.cryptography.providers.base.materials.*
6+
7+
internal object KeyInfoUnwrap {
8+
fun unwrapPkcs8ForOids(der: ByteArray, oids: List<ObjectIdentifier>): ByteArray {
9+
var lastError: Throwable? = null
10+
for (oid in oids) {
11+
try {
12+
return unwrapPrivateKeyInfo(oid, der)
13+
} catch (t: Throwable) {
14+
lastError = t
15+
}
16+
}
17+
throw lastError ?: IllegalArgumentException("No OID matched for PKCS#8 unwrap")
18+
}
19+
}

0 commit comments

Comments
 (0)