Skip to content

Commit

Permalink
separate KeyUtil from KeyPairUtil not to load android store code when…
Browse files Browse the repository at this point in the history
… unit test
  • Loading branch information
ryosuke-wakaba committed Jun 4, 2024
1 parent 66d8363 commit 12b6374
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.module.kotlin.convertValue
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.ownd_project.tw2023_wallet_android.utils.SigningOption
import com.ownd_project.tw2023_wallet_android.utils.KeyPairUtil
import com.ownd_project.tw2023_wallet_android.utils.KeyUtil
import okhttp3.FormBody
import okhttp3.OkHttpClient
import okhttp3.Request
Expand Down Expand Up @@ -205,7 +205,7 @@ class OpenIdProvider(val uri: String, val option: SigningOption = SigningOption(
val nonce = authRequest.nonce
val SEC_IN_MS = 1000

val subJwk = KeyPairUtil.keyPairToPublicJwk(keyPair, option)
val subJwk = KeyUtil.keyPairToPublicJwk(keyPair, option)
// todo: support rsa key
val jwk = object : ECPublicJwk {
override val kty = subJwk["kty"]!!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.ownd_project.tw2023_wallet_android.oid.JwtVpJsonPayloadOptions
import com.ownd_project.tw2023_wallet_android.signature.JWT
import com.ownd_project.tw2023_wallet_android.utils.SigningOption
import com.ownd_project.tw2023_wallet_android.utils.KeyPairUtil
import com.ownd_project.tw2023_wallet_android.utils.KeyUtil
import java.security.PublicKey

class JwtVpJsonGeneratorImpl(private val keyAlias: String = Constants.KEY_PAIR_ALIAS_FOR_KEY_JWT_VP_JSON) :
Expand Down Expand Up @@ -48,7 +49,7 @@ class JwtVpJsonGeneratorImpl(private val keyAlias: String = Constants.KEY_PAIR_A
}
val publicKey: PublicKey = KeyPairUtil.getPublicKey(keyAlias)
?: throw IllegalStateException("Public key not found for alias: $keyAlias")
val jwk = KeyPairUtil.publicKeyToJwk(publicKey, SigningOption())
val jwk = KeyUtil.publicKeyToJwk(publicKey, SigningOption())
return jwk
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,65 +138,6 @@ object KeyPairUtil {

return keyFactory.generatePublic(keySpec)
}
fun correctBytes(value: BigInteger): ByteArray {
/*
BigInteger の toByteArray() メソッドは、数値をバイト配列に変換しますが、
この数値が正の場合、最上位バイトが符号ビットとして解釈されることを避けるために、追加のゼロバイトが先頭に挿入されることがあります。
これは、数値が正で、最上位バイトが 0x80 以上の場合(つまり、最上位ビットが 1 の場合)に起こります。
その結果、期待していた 32 バイトではなく 33 バイトの配列が得られることがあります。
期待する 32 バイトの配列を得るには、返されたバイト配列から余分なゼロバイトを取り除くか、
または正確なバイト長を指定して配列を生成する必要があります。
*/
val bytes = value.toByteArray()
return if (bytes.size == 33 && bytes[0] == 0.toByte()) bytes.copyOfRange(
1,
bytes.size
) else bytes
}

private fun publicKeyToJwk(
ecPublicKey: ECPublicKey,
option: SigningOption
): Map<String, String> {
val ecPoint: ECPoint = ecPublicKey.w
val x = correctBytes(ecPoint.affineX).toBase64Url()
val y = correctBytes(ecPoint.affineY).toBase64Url()

// return """{"kty":"EC","crv":"P-256","x":"$x","y":"$y"}""" // crvは適宜変更してください
return mapOf(
"kty" to "EC",
"crv" to option.signingCurve,
"x" to x,
"y" to y
)
}

private fun publicKeyToJwk(rsaPublicKey: RSAPublicKey): Map<String, String> {
val n = Base64.getUrlEncoder().encodeToString(rsaPublicKey.modulus.toByteArray())
val e = Base64.getUrlEncoder().encodeToString(rsaPublicKey.publicExponent.toByteArray())

// return """{"kty":"RSA","n":"$n","e":"$e"}"""
return mapOf(
"kty" to "RSA",
"n" to n,
"e" to e
)
}

fun keyPairToPublicJwk(keyPair: KeyPair, option: SigningOption): Map<String, String> {
val publicKey: PublicKey = keyPair.public
return publicKeyToJwk(publicKey, option)
}

fun publicKeyToJwk(publicKey: PublicKey, option: SigningOption): Map<String, String> {
return when (publicKey) {
is RSAPublicKey -> publicKeyToJwk(publicKey)
is ECPublicKey -> publicKeyToJwk(publicKey, option)
else -> throw IllegalArgumentException("Unsupported Key Type: ${publicKey::class.java.name}")
}
}

// todo move to anywhere else
fun verifyJwt(jwkJson: Map<String, String>, jwt: String): Boolean {
val publicKey = createPublicKey(jwkJson)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.ownd_project.tw2023_wallet_android.utils

import com.ownd_project.tw2023_wallet_android.signature.toBase64Url
import java.math.BigInteger
import java.security.KeyPair
import java.security.PublicKey
import java.security.interfaces.ECPublicKey
import java.security.interfaces.RSAPublicKey
import java.security.spec.ECPoint
import java.util.Base64

object KeyUtil {

fun correctBytes(value: BigInteger): ByteArray {
/*
BigInteger の toByteArray() メソッドは、数値をバイト配列に変換しますが、
この数値が正の場合、最上位バイトが符号ビットとして解釈されることを避けるために、追加のゼロバイトが先頭に挿入されることがあります。
これは、数値が正で、最上位バイトが 0x80 以上の場合(つまり、最上位ビットが 1 の場合)に起こります。
その結果、期待していた 32 バイトではなく 33 バイトの配列が得られることがあります。
期待する 32 バイトの配列を得るには、返されたバイト配列から余分なゼロバイトを取り除くか、
または正確なバイト長を指定して配列を生成する必要があります。
*/
val bytes = value.toByteArray()
return if (bytes.size == 33 && bytes[0] == 0.toByte()) bytes.copyOfRange(
1,
bytes.size
) else bytes
}

private fun publicKeyToJwk(
ecPublicKey: ECPublicKey,
option: SigningOption
): Map<String, String> {
val ecPoint: ECPoint = ecPublicKey.w
val x = correctBytes(ecPoint.affineX).toBase64Url()
val y = correctBytes(ecPoint.affineY).toBase64Url()

// return """{"kty":"EC","crv":"P-256","x":"$x","y":"$y"}""" // crvは適宜変更してください
return mapOf(
"kty" to "EC",
"crv" to option.signingCurve,
"x" to x,
"y" to y
)
}

private fun publicKeyToJwk(rsaPublicKey: RSAPublicKey): Map<String, String> {
val n = Base64.getUrlEncoder().encodeToString(rsaPublicKey.modulus.toByteArray())
val e = Base64.getUrlEncoder().encodeToString(rsaPublicKey.publicExponent.toByteArray())

// return """{"kty":"RSA","n":"$n","e":"$e"}"""
return mapOf(
"kty" to "RSA",
"n" to n,
"e" to e
)
}

fun keyPairToPublicJwk(keyPair: KeyPair, option: SigningOption): Map<String, String> {
val publicKey: PublicKey = keyPair.public
return publicKeyToJwk(publicKey, option)
}

fun publicKeyToJwk(publicKey: PublicKey, option: SigningOption): Map<String, String> {
return when (publicKey) {
is RSAPublicKey -> publicKeyToJwk(publicKey)
is ECPublicKey -> publicKeyToJwk(publicKey, option)
else -> throw IllegalArgumentException("Unsupported Key Type: ${publicKey::class.java.name}")
}
}

}

0 comments on commit 12b6374

Please sign in to comment.