Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.ownd_project.tw2023_wallet_android.oid

import arrow.core.Either
import com.auth0.jwt.exceptions.JWTVerificationException
import com.ownd_project.tw2023_wallet_android.signature.ECPublicJwk
import com.ownd_project.tw2023_wallet_android.signature.ES256K.createJws
import com.ownd_project.tw2023_wallet_android.signature.JWT
import com.ownd_project.tw2023_wallet_android.utils.EnumDeserializer
Expand Down Expand Up @@ -137,7 +135,7 @@ class OpenIdProvider(val uri: String, val option: SigningOption = SigningOption(
val clientScheme = payload.clientIdScheme ?: authorizationRequestPayload.clientIdScheme

if (clientScheme == "x509_san_dns") {
val verifyResult = JWT.verifyJwtByX5C(requestObjectJwt)
val verifyResult = JWT.verifyJwtByX509(requestObjectJwt)
if (!verifyResult.isSuccess) {
return Result.failure(Exception("Invalid request"))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import com.ownd_project.tw2023_wallet_android.utils.KeyPairUtil
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.ownd_project.tw2023_wallet_android.signature.SignatureUtil.convertPemToX509Certificates
import org.jose4j.jwk.HttpsJwks
import org.jose4j.jwk.Use
import org.jose4j.jws.AlgorithmIdentifiers
import java.io.IOException
import java.nio.charset.StandardCharsets
Expand Down Expand Up @@ -119,13 +118,20 @@ class JWT {
}
}

fun verifyJwtByX5C(jwt: String): Result<Pair<DecodedJWT, Array<X509Certificate>>> {
fun verifyJwtByX509(jwt: String): Result<Pair<DecodedJWT, Array<X509Certificate>>> {
// https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.6
// https://www.rfc-editor.org/rfc/rfc7515.html#appendix-B
val decodedJwt = JWT.decode(jwt)
val certs = decodedJwt.getHeaderClaim("x5c").asList(String::class.java)
val certsUrl = decodedJwt.getHeaderClaim("x5u").asString()
try {
val certificates = convertPemToX509Certificates(certs)
val certificates = if (!certs.isNullOrEmpty()) {
convertPemToX509Certificates(certs)
} else if (certsUrl != null) {
SignatureUtil.getX509CertificatesFromUrl(certsUrl)
} else {
null
}

if (certificates.isNullOrEmpty()) {
return Result.failure(Exception("証明書リストが取得できませんでした"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.ownd_project.tw2023_wallet_android.utils.generateEcKeyPair
import com.github.tomakehurst.wiremock.WireMockServer
import com.github.tomakehurst.wiremock.client.WireMock
import com.ownd_project.tw2023_wallet_android.oid.hasSubjectAlternativeName
import com.ownd_project.tw2023_wallet_android.signature.JWT.Companion.verifyJwtByX5C
import com.ownd_project.tw2023_wallet_android.signature.JWT.Companion.verifyJwtByX509
import com.ownd_project.tw2023_wallet_android.signature.JWT.Companion.verifyJwtByX5U
import org.junit.After
import org.junit.Assert
Expand Down Expand Up @@ -128,8 +128,23 @@ class CredentialVerifierTest {
}
)
}
private val testW3CVcData = mapOf(
"@context" to listOf(
"https://www.w3.org/ns/credentials/v2",
"https://www.w3.org/ns/credentials/examples/v2"
),
"id" to "http://university.example/credentials/3732",
"type" to listOf("VerifiableCredential", "ExampleDegreeCredential"),
"issuer" to "https://university.example/issuers/565049",
"validFrom" to "2010-01-01T00:00:00Z",
"credentialSubject" to mapOf(
"id" to "did:example:ebfeb1f712ebc6f1c276e12ec21",
"name" to "Sample Event ABC",
"date" to "2024-01-24T00:00:00Z",
)
)
@Test
fun testVerifyJwtByX5C() {
fun testVerifyJwtByX509ByX5c() {
val cert0 = SignatureUtil.generateCertificate(keyPairTestIssuer, keyPairTestCA, false, listOf("alt1.verifier.com"))
val encodedCert0 = Base64.getEncoder().encodeToString(cert0.encoded)
val cert1 =
Expand All @@ -146,27 +161,59 @@ class CredentialVerifierTest {
.withIssuer("https://university.example/issuers/565049")
.withKeyId("http://university.example/credentials/3732")
.withSubject("did:example:ebfeb1f712ebc6f1c276e12ec21")
.withClaim(
"vc", mapOf(
"@context" to listOf(
"https://www.w3.org/ns/credentials/v2",
"https://www.w3.org/ns/credentials/examples/v2"
),
"id" to "http://university.example/credentials/3732",
"type" to listOf("VerifiableCredential", "ExampleDegreeCredential"),
"issuer" to "https://university.example/issuers/565049",
"validFrom" to "2010-01-01T00:00:00Z",
"credentialSubject" to mapOf(
"id" to "did:example:ebfeb1f712ebc6f1c276e12ec21",
"name" to "Sample Event ABC",
"date" to "2024-01-24T00:00:00Z",
)
.withClaim("vc", testW3CVcData)
.withIssuedAt(Date())
.withHeader(mapOf("x5c" to certs))
.sign(algorithm)
val result = verifyJwtByX509(token)
Assert.assertTrue(result.isSuccess)
val (decodedJwt, certificates) = result.getOrThrow()
if (!certificates[0].hasSubjectAlternativeName("alt1.verifier.com")) {
Assert.fail()
}
val vc = decodedJwt.getClaim("vc")
Assert.assertNotNull(vc)
}

@Test
fun testVerifyJwtByX509ByX5u() {
val cert0 = SignatureUtil.generateCertificate(
keyPairTestIssuer,
keyPairTestCA,
false,
listOf("alt1.verifier.com")
)
val cert1 =
SignatureUtil.generateCertificate(keyPairTestCA, keyPairTestCA, true) // 認証局は自己証明

val pem0 = SignatureUtil.certificateToPem(cert0)
val pem1 = SignatureUtil.certificateToPem(cert1)
val pemChain = "$pem0\n$pem1"
wireMockServer.stubFor(
WireMock.get(WireMock.urlEqualTo("/test-certificate"))
.willReturn(
WireMock.aResponse()
.withStatus(200)
.withBody(pemChain)
.withHeader("Content-Type", "application/x-pem-file")
)
)
val x5uUrl = "http://localhost:${wireMockServer.port()}/test-certificate"

val algorithm =
Algorithm.ECDSA256(
keyPairTestIssuer.public as ECPublicKey,
keyPairTestIssuer.private as ECPrivateKey?
)
val token = JWT.create()
.withIssuer("https://university.example/issuers/565049")
.withKeyId("http://university.example/credentials/3732")
.withSubject("did:example:ebfeb1f712ebc6f1c276e12ec21")
.withClaim("vc", testW3CVcData)
.withIssuedAt(Date())
.withHeader(mapOf("x5c" to certs))
.withHeader(mapOf("x5u" to x5uUrl))
.sign(algorithm)
val result = verifyJwtByX5C(token)
val result = verifyJwtByX509(token)
Assert.assertTrue(result.isSuccess)
val (decodedJwt, certificates) = result.getOrThrow()
if (!certificates[0].hasSubjectAlternativeName("alt1.verifier.com")) {
Expand Down