feat: RSASSA-PKCS1-v1_5 Using SHA-256で作成された署名を検証できるように
This commit is contained in:
parent
a33bdd7fe9
commit
bf3fa2c014
|
@ -0,0 +1,19 @@
|
||||||
|
package dev.usbharu.httpsignature.v2
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class HttpMessageSignatureVerifier {
|
||||||
|
fun verify(verifyMaterial: VerifyMaterial, signature: Signature, signatureVerifier: SignatureVerifier): Boolean {
|
||||||
|
val coveredComponents = verifyMaterial.signatureBase.coveredComponents()
|
||||||
|
signature.coveredComponents.all { coveredComponents.contains(it) }
|
||||||
|
|
||||||
|
val signatureBase = verifyMaterial.signatureBase.generateSignatureBase(signature.signatureParameters)
|
||||||
|
|
||||||
|
return signatureVerifier.verify(
|
||||||
|
signatureBase.toByteArray(Charsets.UTF_8),
|
||||||
|
Base64.getDecoder().decode(signature.signature),
|
||||||
|
verifyMaterial.publicKey
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package dev.usbharu.httpsignature.v2
|
||||||
|
|
||||||
|
import java.security.PublicKey
|
||||||
|
import java.security.Signature
|
||||||
|
|
||||||
|
class RsaV1_5Sha256SignatureVerifier : SignatureVerifier {
|
||||||
|
override fun verify(byteArray: ByteArray, signature: ByteArray, publicKey: PublicKey): Boolean {
|
||||||
|
val instance = Signature.getInstance("SHA256withRSA")
|
||||||
|
instance.initVerify(publicKey)
|
||||||
|
instance.update(byteArray)
|
||||||
|
return instance.verify(signature)
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,18 +2,18 @@ package dev.usbharu.httpsignature.v2
|
||||||
|
|
||||||
class SignatureBase() {
|
class SignatureBase() {
|
||||||
|
|
||||||
private val list = mutableMapOf<String, Component>()
|
private val list = mutableListOf<Component>()
|
||||||
|
|
||||||
fun addComponent(component: Component) {
|
fun addComponent(component: Component) {
|
||||||
if (list[component.componentIdentifier] != null) {
|
if (list.indexOf(component) != -1) {
|
||||||
throw IllegalArgumentException("Component with identifier ${component.componentIdentifier} already exists.")
|
throw IllegalArgumentException("Component with identifier ${component.componentIdentifier} already exists.")
|
||||||
}
|
}
|
||||||
list[component.componentIdentifier] = component
|
list.add(component)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateSignatureBase(signatureParameters: List<SignatureParameter>): String {
|
fun generateSignatureBase(signatureParameters: List<SignatureParameter>): String {
|
||||||
val signatureBase =
|
val signatureBase =
|
||||||
list.values.joinToString(
|
list.joinToString(
|
||||||
separator = "",
|
separator = "",
|
||||||
postfix = "\n"
|
postfix = "\n"
|
||||||
) { component -> "${component.componentIdentifier}: ${component.componentValue}" }
|
) { component -> "${component.componentIdentifier}: ${component.componentValue}" }
|
||||||
|
@ -25,15 +25,15 @@ class SignatureBase() {
|
||||||
|
|
||||||
fun generateSignatureParameterString(signatureParameters: List<SignatureParameter>): String {
|
fun generateSignatureParameterString(signatureParameters: List<SignatureParameter>): String {
|
||||||
return (listOf(
|
return (listOf(
|
||||||
list.keys.joinToString(
|
list.joinToString(
|
||||||
" ",
|
" ",
|
||||||
"(",
|
"(",
|
||||||
")"
|
")"
|
||||||
)
|
) { it.componentIdentifier }
|
||||||
) + signatureParameters.map { "${it.name}=${it.value}" }).joinToString(";")
|
) + signatureParameters.map { "${it.name}=${it.value}" }).joinToString(";")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun coveredComponents(): List<String> {
|
fun coveredComponents(): List<String> {
|
||||||
return list.map { it.key }
|
return list.map { it.componentIdentifier }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package dev.usbharu.httpsignature.v2
|
||||||
|
|
||||||
|
import java.security.PublicKey
|
||||||
|
|
||||||
|
interface SignatureVerifier {
|
||||||
|
fun verify(byteArray: ByteArray, signature: ByteArray, publicKey: PublicKey): Boolean
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package dev.usbharu.httpsignature.v2
|
||||||
|
|
||||||
|
import java.security.PublicKey
|
||||||
|
|
||||||
|
data class VerifyMaterial(
|
||||||
|
val signatureBase: SignatureBase,
|
||||||
|
val publicKey: PublicKey,
|
||||||
|
val label: String
|
||||||
|
)
|
|
@ -0,0 +1,86 @@
|
||||||
|
package dev.usbharu.httpsignature.v2
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
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.util.*
|
||||||
|
|
||||||
|
class HttpMessageSignatureVerifierTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun verify() {
|
||||||
|
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 signatureBase = SignatureBaseBuilder()
|
||||||
|
.header("Host", "example.com")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val material = Material(
|
||||||
|
signatureBase,
|
||||||
|
rsaPrivateKey,
|
||||||
|
"label"
|
||||||
|
)
|
||||||
|
|
||||||
|
val signer = HttpMessageSignatureSigner()
|
||||||
|
|
||||||
|
val sign = signer.sign(material, SignatureParameters().toParameterList(), RsaV1_5Sha256SignatureSigner())
|
||||||
|
|
||||||
|
val httpMessageSignatureVerifier = HttpMessageSignatureVerifier()
|
||||||
|
|
||||||
|
val actual = httpMessageSignatureVerifier.verify(
|
||||||
|
VerifyMaterial(
|
||||||
|
signatureBase, rsaPublicKey, "label"
|
||||||
|
),
|
||||||
|
sign, RsaV1_5Sha256SignatureVerifier()
|
||||||
|
)
|
||||||
|
|
||||||
|
assertTrue(actual)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue