Skip to content

Commit 01d2383

Browse files
committed
[WebCrypto] Implement API to get public key from private key for RSA and EC
1 parent 38f8885 commit 01d2383

File tree

3 files changed

+43
-7
lines changed

3 files changed

+43
-7
lines changed

cryptography-providers/webcrypto/src/commonMain/kotlin/algorithms/WebCryptoEc.kt

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import dev.whyoleg.cryptography.serialization.asn1.*
1414
import dev.whyoleg.cryptography.serialization.asn1.modules.*
1515
import dev.whyoleg.cryptography.serialization.pem.*
1616

17-
internal sealed class WebCryptoEc<PublicK : EC.PublicKey, PrivateK : EC.PrivateKey, KP : EC.KeyPair<PublicK, PrivateK>>(
17+
internal sealed class WebCryptoEc<PublicK : EC.PublicKey, PrivateK : EC.PrivateKey<PublicK>, KP : EC.KeyPair<PublicK, PrivateK>>(
1818
protected val algorithmName: String,
1919
private val publicKeyWrapper: WebCryptoKeyWrapper<PublicK>,
2020
private val privateKeyWrapper: WebCryptoKeyWrapper<PrivateK>,
@@ -48,10 +48,20 @@ internal sealed class WebCryptoEc<PublicK : EC.PublicKey, PrivateK : EC.PrivateK
4848
keyProcessor = EcPublicKeyProcessor
4949
), EC.PublicKey
5050

51-
protected abstract class EcPrivateKey(val privateKey: CryptoKey) : WebCryptoEncodableKey<EC.PrivateKey.Format>(
51+
protected abstract inner class EcPrivateKey(val privateKey: CryptoKey) : WebCryptoEncodableKey<EC.PrivateKey.Format>(
5252
key = privateKey,
5353
keyProcessor = EcPrivateKeyProcessor
54-
), EC.PrivateKey
54+
), EC.PrivateKey<PublicK> {
55+
final override suspend fun getPublicKey(): PublicK = publicKeyWrapper.wrap(
56+
WebCrypto.reimportPrivateKeyAsPublicKey(
57+
privateKey = privateKey,
58+
extractable = true,
59+
keyUsages = publicKeyWrapper.usages,
60+
)
61+
)
62+
63+
final override fun getPublicKeyBlocking(): PublicK = nonBlocking()
64+
}
5565
}
5666

5767
private object EcPublicKeyProcessor : WebCryptoKeyProcessor<EC.PublicKey.Format>() {
@@ -77,7 +87,7 @@ private object EcPublicKeyProcessor : WebCryptoKeyProcessor<EC.PublicKey.Format>
7787
override fun afterEncoding(format: EC.PublicKey.Format, key: ByteArray): ByteArray = when (format) {
7888
EC.PublicKey.Format.JWK -> key
7989
EC.PublicKey.Format.RAW -> key
80-
EC.PublicKey.Format.RAW.Compressed
90+
EC.PublicKey.Format.RAW.Compressed,
8191
-> compressPublicKey(key)
8292
EC.PublicKey.Format.DER -> key
8393
EC.PublicKey.Format.PEM -> wrapPem(PemLabel.PublicKey, key)

cryptography-providers/webcrypto/src/commonMain/kotlin/algorithms/WebCryptoRsa.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import dev.whyoleg.cryptography.serialization.asn1.*
1515
import dev.whyoleg.cryptography.serialization.asn1.modules.*
1616
import dev.whyoleg.cryptography.serialization.pem.*
1717

18-
internal abstract class WebCryptoRsa<PublicK : RSA.PublicKey, PrivateK : RSA.PrivateKey, KP : RSA.KeyPair<PublicK, PrivateK>>(
18+
internal abstract class WebCryptoRsa<PublicK : RSA.PublicKey, PrivateK : RSA.PrivateKey<PublicK>, KP : RSA.KeyPair<PublicK, PrivateK>>(
1919
protected val algorithmName: String,
2020
private val publicKeyWrapper: WebCryptoKeyWrapper<PublicK>,
2121
private val privateKeyWrapper: WebCryptoKeyWrapper<PrivateK>,
@@ -62,10 +62,20 @@ internal abstract class WebCryptoRsa<PublicK : RSA.PublicKey, PrivateK : RSA.Pri
6262
keyProcessor = RsaPublicKeyProcessor
6363
), RSA.PublicKey
6464

65-
protected abstract class RsaPrivateKey(protected val privateKey: CryptoKey) : WebCryptoEncodableKey<RSA.PrivateKey.Format>(
65+
protected abstract inner class RsaPrivateKey(protected val privateKey: CryptoKey) : WebCryptoEncodableKey<RSA.PrivateKey.Format>(
6666
key = privateKey,
6767
keyProcessor = RsaPrivateKeyProcessor
68-
), RSA.PrivateKey
68+
), RSA.PrivateKey<PublicK> {
69+
final override suspend fun getPublicKey(): PublicK = publicKeyWrapper.wrap(
70+
WebCrypto.reimportPrivateKeyAsPublicKey(
71+
privateKey = privateKey,
72+
extractable = true,
73+
keyUsages = publicKeyWrapper.usages,
74+
)
75+
)
76+
77+
final override fun getPublicKeyBlocking(): PublicK = nonBlocking()
78+
}
6979
}
7080

7181
private object RsaPublicKeyProcessor : WebCryptoKeyProcessor<RSA.PublicKey.Format>() {

cryptography-providers/webcrypto/src/commonMain/kotlin/internal/WebCrypto.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,20 @@ internal object WebCrypto {
5858
}
5959
}
6060

61+
// for private -> public key conversion
62+
suspend fun reimportPrivateKeyAsPublicKey(
63+
privateKey: CryptoKey,
64+
extractable: Boolean,
65+
keyUsages: Array<String>,
66+
): CryptoKey {
67+
val jwkKey = subtle.exportKey("jwk", privateKey).await()
68+
// `d` contains secret value, by which, WebCrypto decides, if it's public or private key
69+
deleteObjectField(jwkKey, "d")
70+
// jwk `key_ops` = WebCrypto `keyUsages`
71+
setObjectField(jwkKey, "key_ops", keyUsages.toJsArray())
72+
return subtle.importKey("jwk", jwkKey, privateKey.algorithm, extractable, keyUsages.toJsArray()).await()
73+
}
74+
6175
suspend fun generateKey(algorithm: Algorithm, extractable: Boolean, keyUsages: Array<String>): CryptoKey {
6276
return subtle.generateKey(algorithm, extractable, keyUsages.toJsArray()).await()
6377
}
@@ -69,3 +83,5 @@ internal object WebCrypto {
6983

7084
private fun jsonParse(string: String): JsAny = js("JSON.parse(string)")
7185
private fun jsonStringify(any: JsAny): String = js("JSON.stringify(any)")
86+
private fun deleteObjectField(obj: JsAny, key: String): Unit = js("delete obj[key]")
87+
private fun setObjectField(obj: JsAny, key: String, value: JsArray<JsString>): Unit = js("obj[key] = value")

0 commit comments

Comments
 (0)