feat: hs2019の検証に対応
This commit is contained in:
parent
d023e46351
commit
94376f8eff
|
@ -11,11 +11,13 @@ class DefaultSignatureHeaderParser : SignatureHeaderParser {
|
||||||
.map { it.trim('"') }
|
.map { it.trim('"') }
|
||||||
.map { it.split("=\"") }
|
.map { it.split("=\"") }
|
||||||
.associate { it[0].split(" ").last() to it[1].trim('"') }
|
.associate { it[0].split(" ").last() to it[1].trim('"') }
|
||||||
|
.toMutableMap()
|
||||||
return Signature(
|
return Signature(
|
||||||
parameters.getValue("keyId"),
|
parameters.remove("keyId")!!,
|
||||||
parameters.getValue("algorithm"),
|
parameters.remove("algorithm")!!,
|
||||||
parameters.getValue("headers").split(" "),
|
parameters.remove("headers")?.split(" ")!!,
|
||||||
parameters.getValue("signature")
|
parameters.remove("signature")!!,
|
||||||
|
parameters
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
package dev.usbharu.httpsignature.verify
|
||||||
|
|
||||||
|
import dev.usbharu.httpsignature.common.HttpHeaders
|
||||||
|
import dev.usbharu.httpsignature.common.HttpMethod
|
||||||
|
import dev.usbharu.httpsignature.common.HttpRequest
|
||||||
|
import dev.usbharu.httpsignature.common.PublicKey
|
||||||
|
import java.net.URL
|
||||||
|
import java.security.spec.MGF1ParameterSpec
|
||||||
|
import java.security.spec.PSSParameterSpec
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class Hs2019HttpSignatureVerifier(
|
||||||
|
private val signatureHeaderParser: SignatureHeaderParser,
|
||||||
|
private val salt: Int = 64
|
||||||
|
) : HttpSignatureVerifier {
|
||||||
|
override fun verify(httpRequest: HttpRequest, key: PublicKey): VerificationResult {
|
||||||
|
val signature = signatureHeaderParser.parse(httpRequest.headers)
|
||||||
|
if (signature.algorithm.equals("hs2019", true).not()) {
|
||||||
|
return FailedVerification("Unsupported algorithm : ${signature.algorithm}")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signature.keyId != key.keyId) {
|
||||||
|
return FailedVerification("The keyId is different.")
|
||||||
|
}
|
||||||
|
|
||||||
|
val created = if (signature.headers.contains("(created)")) {
|
||||||
|
val created = signature.additionalData["created"]
|
||||||
|
?: return FailedVerification("(created) header is provided, but it does not exist.")
|
||||||
|
val l = created.toLongOrNull() ?: return FailedVerification("(created) is an unsupported format.")
|
||||||
|
if (Instant.ofEpochSecond(l) >= Instant.now()) {
|
||||||
|
return FailedVerification("(created) is the future.")
|
||||||
|
}
|
||||||
|
l
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
val expires = if (signature.headers.contains("(expires)")) {
|
||||||
|
val expires = (signature.additionalData["expires"]
|
||||||
|
?: return FailedVerification("(expires) header is provided, but it does not exist."))
|
||||||
|
val l = expires.toLongOrNull() ?: return FailedVerification("(expires) is an unsupported format.")
|
||||||
|
if (Instant.ofEpochSecond(l) <= Instant.now()) {
|
||||||
|
return FailedVerification("(expires) is expired.")
|
||||||
|
}
|
||||||
|
l
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val byteSignature = Base64.getDecoder().decode(signature.signature)
|
||||||
|
|
||||||
|
|
||||||
|
val buildSignString =
|
||||||
|
buildSignString(
|
||||||
|
url = httpRequest.url,
|
||||||
|
method = httpRequest.method,
|
||||||
|
headers = httpRequest.headers,
|
||||||
|
signHeaders = signature.headers,
|
||||||
|
created = created,
|
||||||
|
expires = expires
|
||||||
|
)
|
||||||
|
|
||||||
|
val verifier = java.security.Signature.getInstance("RSASSA-PSS")
|
||||||
|
verifier.setParameter(PSSParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, salt, 1))
|
||||||
|
verifier.initVerify(key.publicKey)
|
||||||
|
verifier.update(buildSignString.toByteArray())
|
||||||
|
val verify = verifier.verify(byteSignature)
|
||||||
|
|
||||||
|
if (verify) {
|
||||||
|
return SuccessfulVerification()
|
||||||
|
}
|
||||||
|
return FailedVerification("Signature verification failed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generalHeader(fieldName: String, value: List<String>): String = "$fieldName: ${value.first()}"
|
||||||
|
|
||||||
|
private fun buildSignString(
|
||||||
|
url: URL,
|
||||||
|
method: HttpMethod,
|
||||||
|
headers: HttpHeaders,
|
||||||
|
signHeaders: List<String>,
|
||||||
|
created: Long?,
|
||||||
|
expires: Long?
|
||||||
|
): String {
|
||||||
|
return signHeaders.joinToString("\n") {
|
||||||
|
when (it) {
|
||||||
|
"(request-target)" -> {
|
||||||
|
"(request-target): ${method.value.lowercase()} ${url.path}"
|
||||||
|
}
|
||||||
|
|
||||||
|
"(created)" -> {
|
||||||
|
"(created): ${created!!}"
|
||||||
|
}
|
||||||
|
|
||||||
|
"(expires)" -> {
|
||||||
|
"(expires): ${expires!!}"
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
generalHeader(it, headers.get(it)!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
package dev.usbharu.httpsignature.verify
|
package dev.usbharu.httpsignature.verify
|
||||||
|
|
||||||
import dev.usbharu.httpsignature.common.HttpRequest
|
import dev.usbharu.httpsignature.common.HttpRequest
|
||||||
import dev.usbharu.httpsignature.common.PrivateKey
|
|
||||||
import dev.usbharu.httpsignature.common.PublicKey
|
import dev.usbharu.httpsignature.common.PublicKey
|
||||||
|
|
||||||
interface HttpSignatureVerifier {
|
interface HttpSignatureVerifier {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import dev.usbharu.httpsignature.sign.HttpSignatureSigner
|
||||||
import java.security.Signature
|
import java.security.Signature
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class HttpSignatureVerifierImpl(
|
class RsaSha256HttpSignatureVerifier(
|
||||||
private val signatureHeaderParser: SignatureHeaderParser,
|
private val signatureHeaderParser: SignatureHeaderParser,
|
||||||
private val httpSignatureSigner: HttpSignatureSigner
|
private val httpSignatureSigner: HttpSignatureSigner
|
||||||
) : HttpSignatureVerifier {
|
) : HttpSignatureVerifier {
|
|
@ -1,8 +1,9 @@
|
||||||
package dev.usbharu.httpsignature.verify
|
package dev.usbharu.httpsignature.verify
|
||||||
|
|
||||||
data class Signature(
|
data class Signature(
|
||||||
val keyId:String,
|
val keyId: String,
|
||||||
val algorithm:String,
|
val algorithm: String,
|
||||||
val headers:List<String>,
|
val headers: List<String>,
|
||||||
val signature:String
|
val signature: String,
|
||||||
|
val additionalData: Map<String, String> = emptyMap()
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,417 @@
|
||||||
|
package dev.usbharu.httpsignature.verify
|
||||||
|
|
||||||
|
import dev.usbharu.httpsignature.common.*
|
||||||
|
import dev.usbharu.httpsignature.sign.Hs2019HttpSignatureSigner
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Assertions.assertInstanceOf
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.mockito.Mockito.CALLS_REAL_METHODS
|
||||||
|
import org.mockito.Mockito.mockStatic
|
||||||
|
import java.net.URL
|
||||||
|
import java.security.KeyFactory
|
||||||
|
import java.security.interfaces.RSAPrivateKey
|
||||||
|
import java.security.interfaces.RSAPublicKey
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec
|
||||||
|
import java.security.spec.X509EncodedKeySpec
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class Hs2019HttpSignatureVerifierTest {
|
||||||
|
@Test
|
||||||
|
fun hs2019の署名を検証できる() {
|
||||||
|
val privateKey = Base64.getDecoder().decode(
|
||||||
|
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDH+8IRQdB9vhej" +
|
||||||
|
"eNQqwxlIutt4Xxjg3eZ5XYm7KgR4jAT/q7+cfsz2/fVTNwDOBpG2H1YJZRLUsrVI" +
|
||||||
|
"3Pia8k7oFyemYKEUN6kaKioO5C9hB0TaiTGlxAKI7syitvedH1YtHqf4zvqgtI2y" +
|
||||||
|
"Lv0NAXja3nUoWZWgZg8ZUHWAGhtHd/BpLiyvlwXAydFHl/eN5u8uCZhQD0bKI5KJ" +
|
||||||
|
"qi8Vcs59Q13+ZNHFdgvNjeD+Hc2AdOmbB5HrW2wREgd1sygBcZ77sSLyleeel8+d" +
|
||||||
|
"VkDsq/l4MQV5/0PeOn+PI8pJkyQUpqnExqzku354XW9ZE6XjREgBb9M0AEdyBLk5" +
|
||||||
|
"+YbZwiqrAgMBAAECggEAIDgmKB7xDDvYFcpIbytHo47B/+ldBL2Q0vTdqn3hLTAX" +
|
||||||
|
"OL80URj3b25dsVknPrToPO4HhTP3jgp3Z+nR/oS+Gb5r8O5DMBKs9+jbJdMK9G2g" +
|
||||||
|
"tjoW+ZypcTj9VynLSFEy0nTMndVwTlFIkvCRwcqpl07yk9xQXas+ZixZrJiIKeyW" +
|
||||||
|
"rCmBDJAjUSknljHDnULxAXvk6K7Y5uqPCv9DQ1362ZopY56H0++9ZMaJwr5PYJMT" +
|
||||||
|
"QoKVeZCGLvfY29rUrhV0/CvC7cfxrPbSuQ7Tr/WrpxFpz9H/Dnc8uePoUEmMP2GM" +
|
||||||
|
"ozjXaJDQrzOVMOpn/2uGinmrcR5/8ETsYruGG96kAQKBgQDoS4PyiZ93zTv5Yvo/" +
|
||||||
|
"aWX5IvieMO/w3kRvsdq0IM27Gd+Ck+0C7WBUqljuU4ql10mp67MFkj7ZmECWCAKa" +
|
||||||
|
"OfE3NtXKqnRgixDhM4Q7nfolhkN08CxRrYP3dBh7HJMDtb2YzPDdwV+PSbL6AM8o" +
|
||||||
|
"oOvxjABJQk6CaGdmL8sQwnHwgQKBgQDcZCJbwDyLPJo04dXkhPBWoH5hGjbtsAv9" +
|
||||||
|
"whhjY9IWFN/0KvJfzfoTWtgCkpYT3wgMYBVp0aTextbg1euim7X+iVL6TRq4QaVk" +
|
||||||
|
"Jv6dRnNZPrY7MlnXxIXE81z6syVjChrJBWi81s14SDKtGOpvghLiKUR29wvGjfER" +
|
||||||
|
"jY/X1MxFKwKBgAUyOz9fqLuLUb4gYqysdOV/zMPtIFDpB+rftZ615SQ8Te2j1Xdt" +
|
||||||
|
"S+xY6yhZog5XpIQyi4yiWtmPOFKi1zwP879icKHZ8kR+l+ARwPF8dS4FtNiWzsb8" +
|
||||||
|
"9KjCZhHK79bzZ8xVOUYcn0CbS2+gOQIVp3F9yjvZSdxM7ZMxmn9Dej0BAoGBAJuf" +
|
||||||
|
"ZZeOLfJPz8AJvCSKLr+swrDEdwbtqfn8xYXhJacMBHwAm3dFFhH2stNWOP09HwzG" +
|
||||||
|
"CDjZnWbl1zOaOrJu61saEurF6Vk0mZoX4vChn6/kFX/FdSVkEuVYx04LlBnUN8e8" +
|
||||||
|
"txGpSBtoN8h88IXevoDOjRbIKZuB/Tjc0jagf8FTAoGAWW8uXsWS4c2nBGNdodqL" +
|
||||||
|
"xJHcNZVMenHPqdkm7rHEYdf1sdbM7r7Q+oj0cifhbZwaRG9HiRGUAgJerLGEqe+x" +
|
||||||
|
"vNeYuKRF3A5xBFUTw/t+XFhUZ1sSyvOordp0uNahQqkAx1UQFWUBCEkG2k/X81fY" +
|
||||||
|
"trEnKP2IjOJDzoXGvc4TG0w="
|
||||||
|
)
|
||||||
|
|
||||||
|
val pkcS8EncodedKeySpec = PKCS8EncodedKeySpec(privateKey)
|
||||||
|
val rsaPrivateKey = KeyFactory.getInstance("RSA").generatePrivate(pkcS8EncodedKeySpec) as RSAPrivateKey
|
||||||
|
|
||||||
|
val publicKey = Base64.getDecoder().decode(
|
||||||
|
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx/vCEUHQfb4Xo3jUKsMZ" +
|
||||||
|
"SLrbeF8Y4N3meV2JuyoEeIwE/6u/nH7M9v31UzcAzgaRth9WCWUS1LK1SNz4mvJO" +
|
||||||
|
"6BcnpmChFDepGioqDuQvYQdE2okxpcQCiO7Morb3nR9WLR6n+M76oLSNsi79DQF4" +
|
||||||
|
"2t51KFmVoGYPGVB1gBobR3fwaS4sr5cFwMnRR5f3jebvLgmYUA9GyiOSiaovFXLO" +
|
||||||
|
"fUNd/mTRxXYLzY3g/h3NgHTpmweR61tsERIHdbMoAXGe+7Ei8pXnnpfPnVZA7Kv5" +
|
||||||
|
"eDEFef9D3jp/jyPKSZMkFKapxMas5Lt+eF1vWROl40RIAW/TNABHcgS5OfmG2cIq" +
|
||||||
|
"qwIDAQAB"
|
||||||
|
)
|
||||||
|
|
||||||
|
val x509EncodedKeySpec = X509EncodedKeySpec(publicKey)
|
||||||
|
val rsaPublicKey = KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec) as RSAPublicKey
|
||||||
|
|
||||||
|
|
||||||
|
val hs2019HttpSignatureSigner = Hs2019HttpSignatureSigner(10, 64)
|
||||||
|
val headers = HttpHeaders(
|
||||||
|
mapOf(
|
||||||
|
"X-Request-Id" to listOf("00000000-0000-0000-0000-000000000004"),
|
||||||
|
"Tpp-Redirect-Uri" to listOf("https://www.sometpp.com/redirect/"),
|
||||||
|
"Digest" to listOf("SHA-256=TGGHcPGLechhcNo4gndoKUvCBhWaQOPgtoVDIpxc6J4="),
|
||||||
|
"Psu-Id" to listOf("1337")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val url = URL("https://example.com/")
|
||||||
|
val httpRequest = HttpRequest(url, headers, HttpMethod.GET)
|
||||||
|
val signature = hs2019HttpSignatureSigner.sign(
|
||||||
|
httpRequest, PrivateKey(rsaPrivateKey, "https://test-hideout.usbharu.dev/users/c#pubkey"),
|
||||||
|
listOf("x-request-id", "tpp-redirect-uri", "digest", "psu-id")
|
||||||
|
)
|
||||||
|
val hs2019HttpSignatureVerifier = Hs2019HttpSignatureVerifier(DefaultSignatureHeaderParser(), 64)
|
||||||
|
val verify = hs2019HttpSignatureVerifier.verify(
|
||||||
|
HttpRequest(
|
||||||
|
url,
|
||||||
|
headers.plus("Signature", listOf(signature.signatureHeader)),
|
||||||
|
HttpMethod.GET
|
||||||
|
), PublicKey(rsaPublicKey, "https://test-hideout.usbharu.dev/users/c#pubkey")
|
||||||
|
)
|
||||||
|
|
||||||
|
if (verify is FailedVerification) {
|
||||||
|
println(verify.reason)
|
||||||
|
}
|
||||||
|
assertInstanceOf(SuccessfulVerification::class.java, verify)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createdが指定されてても署名を検証できる() {
|
||||||
|
val privateKey = Base64.getDecoder().decode(
|
||||||
|
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDH+8IRQdB9vhej" +
|
||||||
|
"eNQqwxlIutt4Xxjg3eZ5XYm7KgR4jAT/q7+cfsz2/fVTNwDOBpG2H1YJZRLUsrVI" +
|
||||||
|
"3Pia8k7oFyemYKEUN6kaKioO5C9hB0TaiTGlxAKI7syitvedH1YtHqf4zvqgtI2y" +
|
||||||
|
"Lv0NAXja3nUoWZWgZg8ZUHWAGhtHd/BpLiyvlwXAydFHl/eN5u8uCZhQD0bKI5KJ" +
|
||||||
|
"qi8Vcs59Q13+ZNHFdgvNjeD+Hc2AdOmbB5HrW2wREgd1sygBcZ77sSLyleeel8+d" +
|
||||||
|
"VkDsq/l4MQV5/0PeOn+PI8pJkyQUpqnExqzku354XW9ZE6XjREgBb9M0AEdyBLk5" +
|
||||||
|
"+YbZwiqrAgMBAAECggEAIDgmKB7xDDvYFcpIbytHo47B/+ldBL2Q0vTdqn3hLTAX" +
|
||||||
|
"OL80URj3b25dsVknPrToPO4HhTP3jgp3Z+nR/oS+Gb5r8O5DMBKs9+jbJdMK9G2g" +
|
||||||
|
"tjoW+ZypcTj9VynLSFEy0nTMndVwTlFIkvCRwcqpl07yk9xQXas+ZixZrJiIKeyW" +
|
||||||
|
"rCmBDJAjUSknljHDnULxAXvk6K7Y5uqPCv9DQ1362ZopY56H0++9ZMaJwr5PYJMT" +
|
||||||
|
"QoKVeZCGLvfY29rUrhV0/CvC7cfxrPbSuQ7Tr/WrpxFpz9H/Dnc8uePoUEmMP2GM" +
|
||||||
|
"ozjXaJDQrzOVMOpn/2uGinmrcR5/8ETsYruGG96kAQKBgQDoS4PyiZ93zTv5Yvo/" +
|
||||||
|
"aWX5IvieMO/w3kRvsdq0IM27Gd+Ck+0C7WBUqljuU4ql10mp67MFkj7ZmECWCAKa" +
|
||||||
|
"OfE3NtXKqnRgixDhM4Q7nfolhkN08CxRrYP3dBh7HJMDtb2YzPDdwV+PSbL6AM8o" +
|
||||||
|
"oOvxjABJQk6CaGdmL8sQwnHwgQKBgQDcZCJbwDyLPJo04dXkhPBWoH5hGjbtsAv9" +
|
||||||
|
"whhjY9IWFN/0KvJfzfoTWtgCkpYT3wgMYBVp0aTextbg1euim7X+iVL6TRq4QaVk" +
|
||||||
|
"Jv6dRnNZPrY7MlnXxIXE81z6syVjChrJBWi81s14SDKtGOpvghLiKUR29wvGjfER" +
|
||||||
|
"jY/X1MxFKwKBgAUyOz9fqLuLUb4gYqysdOV/zMPtIFDpB+rftZ615SQ8Te2j1Xdt" +
|
||||||
|
"S+xY6yhZog5XpIQyi4yiWtmPOFKi1zwP879icKHZ8kR+l+ARwPF8dS4FtNiWzsb8" +
|
||||||
|
"9KjCZhHK79bzZ8xVOUYcn0CbS2+gOQIVp3F9yjvZSdxM7ZMxmn9Dej0BAoGBAJuf" +
|
||||||
|
"ZZeOLfJPz8AJvCSKLr+swrDEdwbtqfn8xYXhJacMBHwAm3dFFhH2stNWOP09HwzG" +
|
||||||
|
"CDjZnWbl1zOaOrJu61saEurF6Vk0mZoX4vChn6/kFX/FdSVkEuVYx04LlBnUN8e8" +
|
||||||
|
"txGpSBtoN8h88IXevoDOjRbIKZuB/Tjc0jagf8FTAoGAWW8uXsWS4c2nBGNdodqL" +
|
||||||
|
"xJHcNZVMenHPqdkm7rHEYdf1sdbM7r7Q+oj0cifhbZwaRG9HiRGUAgJerLGEqe+x" +
|
||||||
|
"vNeYuKRF3A5xBFUTw/t+XFhUZ1sSyvOordp0uNahQqkAx1UQFWUBCEkG2k/X81fY" +
|
||||||
|
"trEnKP2IjOJDzoXGvc4TG0w="
|
||||||
|
)
|
||||||
|
|
||||||
|
val pkcS8EncodedKeySpec = PKCS8EncodedKeySpec(privateKey)
|
||||||
|
val rsaPrivateKey = KeyFactory.getInstance("RSA").generatePrivate(pkcS8EncodedKeySpec) as RSAPrivateKey
|
||||||
|
|
||||||
|
val publicKey = Base64.getDecoder().decode(
|
||||||
|
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx/vCEUHQfb4Xo3jUKsMZ" +
|
||||||
|
"SLrbeF8Y4N3meV2JuyoEeIwE/6u/nH7M9v31UzcAzgaRth9WCWUS1LK1SNz4mvJO" +
|
||||||
|
"6BcnpmChFDepGioqDuQvYQdE2okxpcQCiO7Morb3nR9WLR6n+M76oLSNsi79DQF4" +
|
||||||
|
"2t51KFmVoGYPGVB1gBobR3fwaS4sr5cFwMnRR5f3jebvLgmYUA9GyiOSiaovFXLO" +
|
||||||
|
"fUNd/mTRxXYLzY3g/h3NgHTpmweR61tsERIHdbMoAXGe+7Ei8pXnnpfPnVZA7Kv5" +
|
||||||
|
"eDEFef9D3jp/jyPKSZMkFKapxMas5Lt+eF1vWROl40RIAW/TNABHcgS5OfmG2cIq" +
|
||||||
|
"qwIDAQAB"
|
||||||
|
)
|
||||||
|
|
||||||
|
val x509EncodedKeySpec = X509EncodedKeySpec(publicKey)
|
||||||
|
val rsaPublicKey = KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec) as RSAPublicKey
|
||||||
|
|
||||||
|
|
||||||
|
val hs2019HttpSignatureSigner = Hs2019HttpSignatureSigner(10, 64)
|
||||||
|
val headers = HttpHeaders(
|
||||||
|
mapOf(
|
||||||
|
"X-Request-Id" to listOf("00000000-0000-0000-0000-000000000004"),
|
||||||
|
"Tpp-Redirect-Uri" to listOf("https://www.sometpp.com/redirect/"),
|
||||||
|
"Digest" to listOf("SHA-256=TGGHcPGLechhcNo4gndoKUvCBhWaQOPgtoVDIpxc6J4="),
|
||||||
|
"Psu-Id" to listOf("1337")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val url = URL("https://example.com/")
|
||||||
|
val httpRequest = HttpRequest(url, headers, HttpMethod.GET)
|
||||||
|
val signature = hs2019HttpSignatureSigner.sign(
|
||||||
|
httpRequest, PrivateKey(rsaPrivateKey, "https://test-hideout.usbharu.dev/users/c#pubkey"),
|
||||||
|
listOf("(created)","x-request-id", "tpp-redirect-uri", "digest", "psu-id")
|
||||||
|
)
|
||||||
|
val hs2019HttpSignatureVerifier = Hs2019HttpSignatureVerifier(DefaultSignatureHeaderParser(), 64)
|
||||||
|
val verify = hs2019HttpSignatureVerifier.verify(
|
||||||
|
HttpRequest(
|
||||||
|
url,
|
||||||
|
headers.plus("Signature", listOf(signature.signatureHeader)),
|
||||||
|
HttpMethod.GET
|
||||||
|
), PublicKey(rsaPublicKey, "https://test-hideout.usbharu.dev/users/c#pubkey")
|
||||||
|
)
|
||||||
|
|
||||||
|
if (verify is FailedVerification) {
|
||||||
|
println(verify.reason)
|
||||||
|
}
|
||||||
|
assertInstanceOf(SuccessfulVerification::class.java, verify)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun expiresが指定されてても署名を検証できる() {
|
||||||
|
val privateKey = Base64.getDecoder().decode(
|
||||||
|
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDH+8IRQdB9vhej" +
|
||||||
|
"eNQqwxlIutt4Xxjg3eZ5XYm7KgR4jAT/q7+cfsz2/fVTNwDOBpG2H1YJZRLUsrVI" +
|
||||||
|
"3Pia8k7oFyemYKEUN6kaKioO5C9hB0TaiTGlxAKI7syitvedH1YtHqf4zvqgtI2y" +
|
||||||
|
"Lv0NAXja3nUoWZWgZg8ZUHWAGhtHd/BpLiyvlwXAydFHl/eN5u8uCZhQD0bKI5KJ" +
|
||||||
|
"qi8Vcs59Q13+ZNHFdgvNjeD+Hc2AdOmbB5HrW2wREgd1sygBcZ77sSLyleeel8+d" +
|
||||||
|
"VkDsq/l4MQV5/0PeOn+PI8pJkyQUpqnExqzku354XW9ZE6XjREgBb9M0AEdyBLk5" +
|
||||||
|
"+YbZwiqrAgMBAAECggEAIDgmKB7xDDvYFcpIbytHo47B/+ldBL2Q0vTdqn3hLTAX" +
|
||||||
|
"OL80URj3b25dsVknPrToPO4HhTP3jgp3Z+nR/oS+Gb5r8O5DMBKs9+jbJdMK9G2g" +
|
||||||
|
"tjoW+ZypcTj9VynLSFEy0nTMndVwTlFIkvCRwcqpl07yk9xQXas+ZixZrJiIKeyW" +
|
||||||
|
"rCmBDJAjUSknljHDnULxAXvk6K7Y5uqPCv9DQ1362ZopY56H0++9ZMaJwr5PYJMT" +
|
||||||
|
"QoKVeZCGLvfY29rUrhV0/CvC7cfxrPbSuQ7Tr/WrpxFpz9H/Dnc8uePoUEmMP2GM" +
|
||||||
|
"ozjXaJDQrzOVMOpn/2uGinmrcR5/8ETsYruGG96kAQKBgQDoS4PyiZ93zTv5Yvo/" +
|
||||||
|
"aWX5IvieMO/w3kRvsdq0IM27Gd+Ck+0C7WBUqljuU4ql10mp67MFkj7ZmECWCAKa" +
|
||||||
|
"OfE3NtXKqnRgixDhM4Q7nfolhkN08CxRrYP3dBh7HJMDtb2YzPDdwV+PSbL6AM8o" +
|
||||||
|
"oOvxjABJQk6CaGdmL8sQwnHwgQKBgQDcZCJbwDyLPJo04dXkhPBWoH5hGjbtsAv9" +
|
||||||
|
"whhjY9IWFN/0KvJfzfoTWtgCkpYT3wgMYBVp0aTextbg1euim7X+iVL6TRq4QaVk" +
|
||||||
|
"Jv6dRnNZPrY7MlnXxIXE81z6syVjChrJBWi81s14SDKtGOpvghLiKUR29wvGjfER" +
|
||||||
|
"jY/X1MxFKwKBgAUyOz9fqLuLUb4gYqysdOV/zMPtIFDpB+rftZ615SQ8Te2j1Xdt" +
|
||||||
|
"S+xY6yhZog5XpIQyi4yiWtmPOFKi1zwP879icKHZ8kR+l+ARwPF8dS4FtNiWzsb8" +
|
||||||
|
"9KjCZhHK79bzZ8xVOUYcn0CbS2+gOQIVp3F9yjvZSdxM7ZMxmn9Dej0BAoGBAJuf" +
|
||||||
|
"ZZeOLfJPz8AJvCSKLr+swrDEdwbtqfn8xYXhJacMBHwAm3dFFhH2stNWOP09HwzG" +
|
||||||
|
"CDjZnWbl1zOaOrJu61saEurF6Vk0mZoX4vChn6/kFX/FdSVkEuVYx04LlBnUN8e8" +
|
||||||
|
"txGpSBtoN8h88IXevoDOjRbIKZuB/Tjc0jagf8FTAoGAWW8uXsWS4c2nBGNdodqL" +
|
||||||
|
"xJHcNZVMenHPqdkm7rHEYdf1sdbM7r7Q+oj0cifhbZwaRG9HiRGUAgJerLGEqe+x" +
|
||||||
|
"vNeYuKRF3A5xBFUTw/t+XFhUZ1sSyvOordp0uNahQqkAx1UQFWUBCEkG2k/X81fY" +
|
||||||
|
"trEnKP2IjOJDzoXGvc4TG0w="
|
||||||
|
)
|
||||||
|
|
||||||
|
val pkcS8EncodedKeySpec = PKCS8EncodedKeySpec(privateKey)
|
||||||
|
val rsaPrivateKey = KeyFactory.getInstance("RSA").generatePrivate(pkcS8EncodedKeySpec) as RSAPrivateKey
|
||||||
|
|
||||||
|
val publicKey = Base64.getDecoder().decode(
|
||||||
|
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx/vCEUHQfb4Xo3jUKsMZ" +
|
||||||
|
"SLrbeF8Y4N3meV2JuyoEeIwE/6u/nH7M9v31UzcAzgaRth9WCWUS1LK1SNz4mvJO" +
|
||||||
|
"6BcnpmChFDepGioqDuQvYQdE2okxpcQCiO7Morb3nR9WLR6n+M76oLSNsi79DQF4" +
|
||||||
|
"2t51KFmVoGYPGVB1gBobR3fwaS4sr5cFwMnRR5f3jebvLgmYUA9GyiOSiaovFXLO" +
|
||||||
|
"fUNd/mTRxXYLzY3g/h3NgHTpmweR61tsERIHdbMoAXGe+7Ei8pXnnpfPnVZA7Kv5" +
|
||||||
|
"eDEFef9D3jp/jyPKSZMkFKapxMas5Lt+eF1vWROl40RIAW/TNABHcgS5OfmG2cIq" +
|
||||||
|
"qwIDAQAB"
|
||||||
|
)
|
||||||
|
|
||||||
|
val x509EncodedKeySpec = X509EncodedKeySpec(publicKey)
|
||||||
|
val rsaPublicKey = KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec) as RSAPublicKey
|
||||||
|
|
||||||
|
|
||||||
|
val hs2019HttpSignatureSigner = Hs2019HttpSignatureSigner(10, 64)
|
||||||
|
val headers = HttpHeaders(
|
||||||
|
mapOf(
|
||||||
|
"X-Request-Id" to listOf("00000000-0000-0000-0000-000000000004"),
|
||||||
|
"Tpp-Redirect-Uri" to listOf("https://www.sometpp.com/redirect/"),
|
||||||
|
"Digest" to listOf("SHA-256=TGGHcPGLechhcNo4gndoKUvCBhWaQOPgtoVDIpxc6J4="),
|
||||||
|
"Psu-Id" to listOf("1337")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val url = URL("https://example.com/")
|
||||||
|
val httpRequest = HttpRequest(url, headers, HttpMethod.GET)
|
||||||
|
val signature = hs2019HttpSignatureSigner.sign(
|
||||||
|
httpRequest, PrivateKey(rsaPrivateKey, "https://test-hideout.usbharu.dev/users/c#pubkey"),
|
||||||
|
listOf("(expires)","x-request-id", "tpp-redirect-uri", "digest", "psu-id")
|
||||||
|
)
|
||||||
|
val hs2019HttpSignatureVerifier = Hs2019HttpSignatureVerifier(DefaultSignatureHeaderParser(), 64)
|
||||||
|
val verify = hs2019HttpSignatureVerifier.verify(
|
||||||
|
HttpRequest(
|
||||||
|
url,
|
||||||
|
headers.plus("Signature", listOf(signature.signatureHeader)),
|
||||||
|
HttpMethod.GET
|
||||||
|
), PublicKey(rsaPublicKey, "https://test-hideout.usbharu.dev/users/c#pubkey")
|
||||||
|
)
|
||||||
|
|
||||||
|
if (verify is FailedVerification) {
|
||||||
|
println(verify.reason)
|
||||||
|
}
|
||||||
|
assertInstanceOf(SuccessfulVerification::class.java, verify)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createdが未来のとき署名に失敗する() {
|
||||||
|
val plusSeconds = Instant.now().plusSeconds(100000)
|
||||||
|
val mockStatic = mockStatic(Instant::class.java, CALLS_REAL_METHODS)
|
||||||
|
mockStatic.`when`<Instant>(Instant::now).thenReturn(plusSeconds)
|
||||||
|
|
||||||
|
val privateKey = Base64.getDecoder().decode(
|
||||||
|
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDH+8IRQdB9vhej" +
|
||||||
|
"eNQqwxlIutt4Xxjg3eZ5XYm7KgR4jAT/q7+cfsz2/fVTNwDOBpG2H1YJZRLUsrVI" +
|
||||||
|
"3Pia8k7oFyemYKEUN6kaKioO5C9hB0TaiTGlxAKI7syitvedH1YtHqf4zvqgtI2y" +
|
||||||
|
"Lv0NAXja3nUoWZWgZg8ZUHWAGhtHd/BpLiyvlwXAydFHl/eN5u8uCZhQD0bKI5KJ" +
|
||||||
|
"qi8Vcs59Q13+ZNHFdgvNjeD+Hc2AdOmbB5HrW2wREgd1sygBcZ77sSLyleeel8+d" +
|
||||||
|
"VkDsq/l4MQV5/0PeOn+PI8pJkyQUpqnExqzku354XW9ZE6XjREgBb9M0AEdyBLk5" +
|
||||||
|
"+YbZwiqrAgMBAAECggEAIDgmKB7xDDvYFcpIbytHo47B/+ldBL2Q0vTdqn3hLTAX" +
|
||||||
|
"OL80URj3b25dsVknPrToPO4HhTP3jgp3Z+nR/oS+Gb5r8O5DMBKs9+jbJdMK9G2g" +
|
||||||
|
"tjoW+ZypcTj9VynLSFEy0nTMndVwTlFIkvCRwcqpl07yk9xQXas+ZixZrJiIKeyW" +
|
||||||
|
"rCmBDJAjUSknljHDnULxAXvk6K7Y5uqPCv9DQ1362ZopY56H0++9ZMaJwr5PYJMT" +
|
||||||
|
"QoKVeZCGLvfY29rUrhV0/CvC7cfxrPbSuQ7Tr/WrpxFpz9H/Dnc8uePoUEmMP2GM" +
|
||||||
|
"ozjXaJDQrzOVMOpn/2uGinmrcR5/8ETsYruGG96kAQKBgQDoS4PyiZ93zTv5Yvo/" +
|
||||||
|
"aWX5IvieMO/w3kRvsdq0IM27Gd+Ck+0C7WBUqljuU4ql10mp67MFkj7ZmECWCAKa" +
|
||||||
|
"OfE3NtXKqnRgixDhM4Q7nfolhkN08CxRrYP3dBh7HJMDtb2YzPDdwV+PSbL6AM8o" +
|
||||||
|
"oOvxjABJQk6CaGdmL8sQwnHwgQKBgQDcZCJbwDyLPJo04dXkhPBWoH5hGjbtsAv9" +
|
||||||
|
"whhjY9IWFN/0KvJfzfoTWtgCkpYT3wgMYBVp0aTextbg1euim7X+iVL6TRq4QaVk" +
|
||||||
|
"Jv6dRnNZPrY7MlnXxIXE81z6syVjChrJBWi81s14SDKtGOpvghLiKUR29wvGjfER" +
|
||||||
|
"jY/X1MxFKwKBgAUyOz9fqLuLUb4gYqysdOV/zMPtIFDpB+rftZ615SQ8Te2j1Xdt" +
|
||||||
|
"S+xY6yhZog5XpIQyi4yiWtmPOFKi1zwP879icKHZ8kR+l+ARwPF8dS4FtNiWzsb8" +
|
||||||
|
"9KjCZhHK79bzZ8xVOUYcn0CbS2+gOQIVp3F9yjvZSdxM7ZMxmn9Dej0BAoGBAJuf" +
|
||||||
|
"ZZeOLfJPz8AJvCSKLr+swrDEdwbtqfn8xYXhJacMBHwAm3dFFhH2stNWOP09HwzG" +
|
||||||
|
"CDjZnWbl1zOaOrJu61saEurF6Vk0mZoX4vChn6/kFX/FdSVkEuVYx04LlBnUN8e8" +
|
||||||
|
"txGpSBtoN8h88IXevoDOjRbIKZuB/Tjc0jagf8FTAoGAWW8uXsWS4c2nBGNdodqL" +
|
||||||
|
"xJHcNZVMenHPqdkm7rHEYdf1sdbM7r7Q+oj0cifhbZwaRG9HiRGUAgJerLGEqe+x" +
|
||||||
|
"vNeYuKRF3A5xBFUTw/t+XFhUZ1sSyvOordp0uNahQqkAx1UQFWUBCEkG2k/X81fY" +
|
||||||
|
"trEnKP2IjOJDzoXGvc4TG0w="
|
||||||
|
)
|
||||||
|
|
||||||
|
val pkcS8EncodedKeySpec = PKCS8EncodedKeySpec(privateKey)
|
||||||
|
val rsaPrivateKey = KeyFactory.getInstance("RSA").generatePrivate(pkcS8EncodedKeySpec) as RSAPrivateKey
|
||||||
|
|
||||||
|
val publicKey = Base64.getDecoder().decode(
|
||||||
|
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx/vCEUHQfb4Xo3jUKsMZ" +
|
||||||
|
"SLrbeF8Y4N3meV2JuyoEeIwE/6u/nH7M9v31UzcAzgaRth9WCWUS1LK1SNz4mvJO" +
|
||||||
|
"6BcnpmChFDepGioqDuQvYQdE2okxpcQCiO7Morb3nR9WLR6n+M76oLSNsi79DQF4" +
|
||||||
|
"2t51KFmVoGYPGVB1gBobR3fwaS4sr5cFwMnRR5f3jebvLgmYUA9GyiOSiaovFXLO" +
|
||||||
|
"fUNd/mTRxXYLzY3g/h3NgHTpmweR61tsERIHdbMoAXGe+7Ei8pXnnpfPnVZA7Kv5" +
|
||||||
|
"eDEFef9D3jp/jyPKSZMkFKapxMas5Lt+eF1vWROl40RIAW/TNABHcgS5OfmG2cIq" +
|
||||||
|
"qwIDAQAB"
|
||||||
|
)
|
||||||
|
|
||||||
|
val x509EncodedKeySpec = X509EncodedKeySpec(publicKey)
|
||||||
|
val rsaPublicKey = KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec) as RSAPublicKey
|
||||||
|
|
||||||
|
|
||||||
|
val hs2019HttpSignatureSigner = Hs2019HttpSignatureSigner(10, 64)
|
||||||
|
val headers = HttpHeaders(
|
||||||
|
mapOf(
|
||||||
|
"X-Request-Id" to listOf("00000000-0000-0000-0000-000000000004"),
|
||||||
|
"Tpp-Redirect-Uri" to listOf("https://www.sometpp.com/redirect/"),
|
||||||
|
"Digest" to listOf("SHA-256=TGGHcPGLechhcNo4gndoKUvCBhWaQOPgtoVDIpxc6J4="),
|
||||||
|
"Psu-Id" to listOf("1337")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val url = URL("https://example.com/")
|
||||||
|
val httpRequest = HttpRequest(url, headers, HttpMethod.GET)
|
||||||
|
val signature = hs2019HttpSignatureSigner.sign(
|
||||||
|
httpRequest, PrivateKey(rsaPrivateKey, "https://test-hideout.usbharu.dev/users/c#pubkey"),
|
||||||
|
listOf("(created)","x-request-id", "tpp-redirect-uri", "digest", "psu-id")
|
||||||
|
)
|
||||||
|
|
||||||
|
mockStatic.close()
|
||||||
|
val hs2019HttpSignatureVerifier = Hs2019HttpSignatureVerifier(DefaultSignatureHeaderParser(), 64)
|
||||||
|
val verify = hs2019HttpSignatureVerifier.verify(
|
||||||
|
HttpRequest(
|
||||||
|
url,
|
||||||
|
headers.plus("Signature", listOf(signature.signatureHeader)),
|
||||||
|
HttpMethod.GET
|
||||||
|
), PublicKey(rsaPublicKey, "https://test-hideout.usbharu.dev/users/c#pubkey")
|
||||||
|
)
|
||||||
|
|
||||||
|
assertInstanceOf(FailedVerification::class.java, verify)
|
||||||
|
assertEquals("(created) is the future.",(verify as FailedVerification).reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun expiresが期限切れのとき失敗する() {
|
||||||
|
val plusSeconds = Instant.now().minusSeconds(100000)
|
||||||
|
val mockStatic = mockStatic(Instant::class.java, CALLS_REAL_METHODS)
|
||||||
|
mockStatic.`when`<Instant>(Instant::now).thenReturn(plusSeconds)
|
||||||
|
|
||||||
|
val privateKey = Base64.getDecoder().decode(
|
||||||
|
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDH+8IRQdB9vhej" +
|
||||||
|
"eNQqwxlIutt4Xxjg3eZ5XYm7KgR4jAT/q7+cfsz2/fVTNwDOBpG2H1YJZRLUsrVI" +
|
||||||
|
"3Pia8k7oFyemYKEUN6kaKioO5C9hB0TaiTGlxAKI7syitvedH1YtHqf4zvqgtI2y" +
|
||||||
|
"Lv0NAXja3nUoWZWgZg8ZUHWAGhtHd/BpLiyvlwXAydFHl/eN5u8uCZhQD0bKI5KJ" +
|
||||||
|
"qi8Vcs59Q13+ZNHFdgvNjeD+Hc2AdOmbB5HrW2wREgd1sygBcZ77sSLyleeel8+d" +
|
||||||
|
"VkDsq/l4MQV5/0PeOn+PI8pJkyQUpqnExqzku354XW9ZE6XjREgBb9M0AEdyBLk5" +
|
||||||
|
"+YbZwiqrAgMBAAECggEAIDgmKB7xDDvYFcpIbytHo47B/+ldBL2Q0vTdqn3hLTAX" +
|
||||||
|
"OL80URj3b25dsVknPrToPO4HhTP3jgp3Z+nR/oS+Gb5r8O5DMBKs9+jbJdMK9G2g" +
|
||||||
|
"tjoW+ZypcTj9VynLSFEy0nTMndVwTlFIkvCRwcqpl07yk9xQXas+ZixZrJiIKeyW" +
|
||||||
|
"rCmBDJAjUSknljHDnULxAXvk6K7Y5uqPCv9DQ1362ZopY56H0++9ZMaJwr5PYJMT" +
|
||||||
|
"QoKVeZCGLvfY29rUrhV0/CvC7cfxrPbSuQ7Tr/WrpxFpz9H/Dnc8uePoUEmMP2GM" +
|
||||||
|
"ozjXaJDQrzOVMOpn/2uGinmrcR5/8ETsYruGG96kAQKBgQDoS4PyiZ93zTv5Yvo/" +
|
||||||
|
"aWX5IvieMO/w3kRvsdq0IM27Gd+Ck+0C7WBUqljuU4ql10mp67MFkj7ZmECWCAKa" +
|
||||||
|
"OfE3NtXKqnRgixDhM4Q7nfolhkN08CxRrYP3dBh7HJMDtb2YzPDdwV+PSbL6AM8o" +
|
||||||
|
"oOvxjABJQk6CaGdmL8sQwnHwgQKBgQDcZCJbwDyLPJo04dXkhPBWoH5hGjbtsAv9" +
|
||||||
|
"whhjY9IWFN/0KvJfzfoTWtgCkpYT3wgMYBVp0aTextbg1euim7X+iVL6TRq4QaVk" +
|
||||||
|
"Jv6dRnNZPrY7MlnXxIXE81z6syVjChrJBWi81s14SDKtGOpvghLiKUR29wvGjfER" +
|
||||||
|
"jY/X1MxFKwKBgAUyOz9fqLuLUb4gYqysdOV/zMPtIFDpB+rftZ615SQ8Te2j1Xdt" +
|
||||||
|
"S+xY6yhZog5XpIQyi4yiWtmPOFKi1zwP879icKHZ8kR+l+ARwPF8dS4FtNiWzsb8" +
|
||||||
|
"9KjCZhHK79bzZ8xVOUYcn0CbS2+gOQIVp3F9yjvZSdxM7ZMxmn9Dej0BAoGBAJuf" +
|
||||||
|
"ZZeOLfJPz8AJvCSKLr+swrDEdwbtqfn8xYXhJacMBHwAm3dFFhH2stNWOP09HwzG" +
|
||||||
|
"CDjZnWbl1zOaOrJu61saEurF6Vk0mZoX4vChn6/kFX/FdSVkEuVYx04LlBnUN8e8" +
|
||||||
|
"txGpSBtoN8h88IXevoDOjRbIKZuB/Tjc0jagf8FTAoGAWW8uXsWS4c2nBGNdodqL" +
|
||||||
|
"xJHcNZVMenHPqdkm7rHEYdf1sdbM7r7Q+oj0cifhbZwaRG9HiRGUAgJerLGEqe+x" +
|
||||||
|
"vNeYuKRF3A5xBFUTw/t+XFhUZ1sSyvOordp0uNahQqkAx1UQFWUBCEkG2k/X81fY" +
|
||||||
|
"trEnKP2IjOJDzoXGvc4TG0w="
|
||||||
|
)
|
||||||
|
|
||||||
|
val pkcS8EncodedKeySpec = PKCS8EncodedKeySpec(privateKey)
|
||||||
|
val rsaPrivateKey = KeyFactory.getInstance("RSA").generatePrivate(pkcS8EncodedKeySpec) as RSAPrivateKey
|
||||||
|
|
||||||
|
val publicKey = Base64.getDecoder().decode(
|
||||||
|
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx/vCEUHQfb4Xo3jUKsMZ" +
|
||||||
|
"SLrbeF8Y4N3meV2JuyoEeIwE/6u/nH7M9v31UzcAzgaRth9WCWUS1LK1SNz4mvJO" +
|
||||||
|
"6BcnpmChFDepGioqDuQvYQdE2okxpcQCiO7Morb3nR9WLR6n+M76oLSNsi79DQF4" +
|
||||||
|
"2t51KFmVoGYPGVB1gBobR3fwaS4sr5cFwMnRR5f3jebvLgmYUA9GyiOSiaovFXLO" +
|
||||||
|
"fUNd/mTRxXYLzY3g/h3NgHTpmweR61tsERIHdbMoAXGe+7Ei8pXnnpfPnVZA7Kv5" +
|
||||||
|
"eDEFef9D3jp/jyPKSZMkFKapxMas5Lt+eF1vWROl40RIAW/TNABHcgS5OfmG2cIq" +
|
||||||
|
"qwIDAQAB"
|
||||||
|
)
|
||||||
|
|
||||||
|
val x509EncodedKeySpec = X509EncodedKeySpec(publicKey)
|
||||||
|
val rsaPublicKey = KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec) as RSAPublicKey
|
||||||
|
|
||||||
|
|
||||||
|
val hs2019HttpSignatureSigner = Hs2019HttpSignatureSigner(10, 64)
|
||||||
|
val headers = HttpHeaders(
|
||||||
|
mapOf(
|
||||||
|
"X-Request-Id" to listOf("00000000-0000-0000-0000-000000000004"),
|
||||||
|
"Tpp-Redirect-Uri" to listOf("https://www.sometpp.com/redirect/"),
|
||||||
|
"Digest" to listOf("SHA-256=TGGHcPGLechhcNo4gndoKUvCBhWaQOPgtoVDIpxc6J4="),
|
||||||
|
"Psu-Id" to listOf("1337")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val url = URL("https://example.com/")
|
||||||
|
val httpRequest = HttpRequest(url, headers, HttpMethod.GET)
|
||||||
|
val signature = hs2019HttpSignatureSigner.sign(
|
||||||
|
httpRequest, PrivateKey(rsaPrivateKey, "https://test-hideout.usbharu.dev/users/c#pubkey"),
|
||||||
|
listOf("(expires)","x-request-id", "tpp-redirect-uri", "digest", "psu-id")
|
||||||
|
)
|
||||||
|
|
||||||
|
mockStatic.close()
|
||||||
|
val hs2019HttpSignatureVerifier = Hs2019HttpSignatureVerifier(DefaultSignatureHeaderParser(), 64)
|
||||||
|
val verify = hs2019HttpSignatureVerifier.verify(
|
||||||
|
HttpRequest(
|
||||||
|
url,
|
||||||
|
headers.plus("Signature", listOf(signature.signatureHeader)),
|
||||||
|
HttpMethod.GET
|
||||||
|
), PublicKey(rsaPublicKey, "https://test-hideout.usbharu.dev/users/c#pubkey")
|
||||||
|
)
|
||||||
|
|
||||||
|
assertInstanceOf(FailedVerification::class.java, verify)
|
||||||
|
assertEquals("(expires) is expired.",(verify as FailedVerification).reason)
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ import java.security.spec.PKCS8EncodedKeySpec
|
||||||
import java.security.spec.X509EncodedKeySpec
|
import java.security.spec.X509EncodedKeySpec
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class HttpSignatureVerifierImplTest {
|
class RsaSha256HttpSignatureVerifierTest {
|
||||||
@Test
|
@Test
|
||||||
fun 署名の検証を行える() {
|
fun 署名の検証を行える() {
|
||||||
val privateKey = Base64.getDecoder().decode(
|
val privateKey = Base64.getDecoder().decode(
|
||||||
|
@ -76,9 +76,9 @@ class HttpSignatureVerifierImplTest {
|
||||||
listOf("x-request-id", "tpp-redirect-uri", "digest", "psu-id")
|
listOf("x-request-id", "tpp-redirect-uri", "digest", "psu-id")
|
||||||
)
|
)
|
||||||
|
|
||||||
val httpSignatureVerifierImpl =
|
val rsaSha256HttpSignatureVerifier =
|
||||||
HttpSignatureVerifierImpl(DefaultSignatureHeaderParser(), rsaSha256HttpSignatureSigner)
|
RsaSha256HttpSignatureVerifier(DefaultSignatureHeaderParser(), rsaSha256HttpSignatureSigner)
|
||||||
val verify = httpSignatureVerifierImpl.verify(
|
val verify = rsaSha256HttpSignatureVerifier.verify(
|
||||||
HttpRequest(url, headers.plus("Signature", listOf(signature.signatureHeader)), HttpMethod.GET),
|
HttpRequest(url, headers.plus("Signature", listOf(signature.signatureHeader)), HttpMethod.GET),
|
||||||
PublicKey(rsaPublicKey, "https://test-hideout.usbharu.dev/users/c#pubkey")
|
PublicKey(rsaPublicKey, "https://test-hideout.usbharu.dev/users/c#pubkey")
|
||||||
)
|
)
|
Loading…
Reference in New Issue