feat: Signature-InputとSignatureヘッダーのパースができるように

This commit is contained in:
usbharu 2024-09-23 22:30:28 +09:00
parent ebe864fd10
commit 87e40bead2
Signed by: usbharu
GPG Key ID: 95CBCF7046307B77
11 changed files with 139 additions and 10 deletions

View File

@ -11,6 +11,7 @@ repositories {
}
dependencies {
implementation("org.greenbytes.http:structured-fields:0.4")
testImplementation("org.junit.jupiter:junit-jupiter:5.8.1")
testImplementation("org.mockito:mockito-inline:5.2.0")
}

View File

@ -0,0 +1,58 @@
package dev.usbharu.httpsignature.v2
import org.greenbytes.http.sfv.*
class DefaultHttpSignatureHeaderParser() : HttpSignatureHeaderParser {
override fun parse(signatureInputs: List<String>, signatures: List<String>): Map<String, Signature> {
val signatureInputString = signatureInputs.joinToString(", ")
val signatureString = signatures.joinToString(", ")
val parseSignatureInputs = parseSignatureInputs(signatureInputString)
val parseSignatures = parseSignatures(signatureString)
require(parseSignatureInputs.size == parseSignatures.size)
return parseSignatureInputs.map {
val signatureInput = parseSignatureInputs.getValue(it.key)
it.key to Signature(
it.key,
signatureInput.first,
parseSignatures.getValue(it.key),
signatureInput.third,
signatureInput.second
)
}.toMap()
}
private fun parseSignatureInputs(signatureInput: String): Map<String, Triple<String, List<String>, List<SignatureParameter>>> {
val parser = Parser(signatureInput)
val map = parser.parseDictionary()
.get()
.mapValues {
val innerList = it.value as InnerList
Triple(
it.value.serialize(),
innerList.get().map { it.get().toString() },
innerList.params.map { param ->
when (val value = param.value) {
is IntegerItem -> LongSignatureParameter(param.key, value.asLong)
is NumberItem -> LongSignatureParameter(param.key, value.asLong)
is StringItem -> StringSignatureParameter(param.key, value.get())
else -> error("Unknown parameter: $param")
}
}
)
}
return map
}
private fun parseSignatures(signature: String): Map<String, String> {
val parser = Parser(signature)
val map = parser.parseDictionary().get()
return map.map { it.key to it.value.get().toString() }.toMap()
}
}

View File

@ -1,7 +1,7 @@
package dev.usbharu.httpsignature.v2
class HttpMessageSignatureSigner {
fun sign(material: Material, signatureParameters: List<SignatureParameter>, signer: SignatureSigner): Signatures {
fun sign(material: Material, signatureParameters: List<SignatureParameter>, signer: SignatureSigner): Signature {
val signatureBase = material.signatureBase.generateSignatureBase(signatureParameters)
val signatureInput =
@ -9,6 +9,12 @@ class HttpMessageSignatureSigner {
val signature = signer.sign(signatureBase.toByteArray(Charsets.UTF_8), material.privateKey)
return Signatures(signatureInput, signature)
return Signature(
material.label,
signatureInput,
signature,
signatureParameters,
material.signatureBase.coveredComponents()
)
}
}

View File

@ -0,0 +1,5 @@
package dev.usbharu.httpsignature.v2
interface HttpSignatureHeaderParser {
fun parse(signatureInputs: List<String>, signatures: List<String>): Map<String, Signature>
}

View File

@ -2,7 +2,7 @@ package dev.usbharu.httpsignature.v2
import java.time.Instant
class InstantSignatureParameter(private val instantName: String, val instant: Instant) : SignatureParameter {
data class InstantSignatureParameter(private val instantName: String, val instant: Instant) : SignatureParameter {
override val name: String
get() = instantName
override val value: String

View File

@ -0,0 +1,8 @@
package dev.usbharu.httpsignature.v2
data class LongSignatureParameter(val longName: String, val longValue: Long) : SignatureParameter {
override val name: String
get() = longName
override val value: String
get() = longValue.toString()
}

View File

@ -0,0 +1,9 @@
package dev.usbharu.httpsignature.v2
data class Signature(
val label: String,
val signatureInput: String,
val signature: String,
val signatureParameters: List<SignatureParameter>,
val coveredComponents: List<String>
)

View File

@ -32,4 +32,8 @@ class SignatureBase() {
)
) + signatureParameters.map { "${it.name}=${it.value}" }).joinToString(";")
}
fun coveredComponents(): List<String> {
return list.map { it.key }
}
}

View File

@ -1,6 +0,0 @@
package dev.usbharu.httpsignature.v2
data class Signatures(
val signatureInput: String,
val signature: String
)

View File

@ -1,6 +1,7 @@
package dev.usbharu.httpsignature.v2
class StringSignatureParameter(private val stringName: String, private val stringValue: String) : SignatureParameter {
data class StringSignatureParameter(private val stringName: String, private val stringValue: String) :
SignatureParameter {
override val name: String
get() = stringName
override val value: String

View File

@ -0,0 +1,43 @@
package dev.usbharu.httpsignature.v2
import org.junit.jupiter.api.Test
class DefaultHttpSignatureHeaderParserTest {
@Test
fun name() {
val defaultHttpSignatureHeaderParser = DefaultHttpSignatureHeaderParser()
defaultHttpSignatureHeaderParser.parse(
listOf(
"sig1=(\"@method\" \"@target-uri\" \"@authority\" " +
" \"content-digest\" \"cache-control\");" +
" created=1618884475;keyid=\"test-key-rsa-pss\""
), listOf()
)
}
@Test
fun test2() {
val parse = DefaultHttpSignatureHeaderParser().parse(
listOf(
"sig1=(\"@method\" \"@authority\" \"@path\" " +
"\"content-digest\" \"content-type\" \"content-length\")" +
";created=1618884475;keyid=\"test-key-ecc-p256\", " +
"proxy_sig=(\"@method\" \"@authority\" \"@path\" \"content-digest\" " +
"\"content-type\" \"content-length\" \"forwarded\")" +
";created=1618884480;keyid=\"test-key-rsa\";alg=\"rsa-v1_5-sha256\"" +
";expires=1618884540"
), listOf(
"sig1=:X5spyd6CFnAG5QnDyHfqoSNICd+BUP4LYMz2Q0JXlb//4Ijpzp" +
"+kve2w4NIyqeAuM7jTDX+sNalzA8ESSaHD3A==:, " +
"proxy_sig=:S6ZzPXSdAMOPjN/6KXfXWNO/f7V6cHm7BXYUh3YD/fRad4BCaRZxP+" +
"JH+8XY1I6+8Cy+CM5g92iHgxtRPz+MjniOaYmdkDcnL9cCpXJleXsOckpURl49G" +
"wiyUpZ10KHgOEe11sx3G2gxI8S0jnxQB+Pu68U9vVcasqOWAEObtNKKZd8tSFu7" +
"LB5YAv0RAGhB8tmpv7sFnIm9y+7X5kXQfi8NMaZaA8i2ZHwpBdg7a6CMfwnnrtf" +
"lzvZdXAsD3LH2TwevU+/PBPv0B6NMNk93wUs/vfJvye+YuI87HU38lZHowtznbL" +
"Vdp770I6VHR6WfgS9ddzirrswsE1w5o0LV/g==:"
)
)
println(parse)
}
}