feat: HTTP Signatureの署名器を実装

This commit is contained in:
usbharu 2023-10-13 15:57:01 +09:00
parent cddaad0433
commit 10d50815b8
5 changed files with 133 additions and 0 deletions

View File

@ -0,0 +1,16 @@
package dev.usbharu.hideout.service.signature
import io.ktor.http.*
interface HttpSignatureSigner {
suspend fun sign(
url: String,
method: HttpMethod,
headers: Headers,
requestBody: String,
keyPair: Key,
signHeaders: List<String>
): SignedRequest
suspend fun signRaw(signString: String, keyPair: Key, signHeaders: List<String>): Sign
}

View File

@ -0,0 +1,78 @@
package dev.usbharu.hideout.service.signature
import dev.usbharu.hideout.util.Base64Util
import io.ktor.http.*
import io.ktor.util.*
import org.springframework.stereotype.Component
import java.net.URL
import java.security.Signature
@Component
class HttpSignatureSignerImpl : HttpSignatureSigner {
override suspend fun sign(
url: String,
method: HttpMethod,
headers: Headers,
requestBody: String,
keyPair: Key,
signHeaders: List<String>
): SignedRequest {
val sign = signRaw(
signString = buildSignString(
url = URL(url),
method = method,
headers = headers,
signHeaders = signHeaders
),
keyPair = keyPair,
signHeaders = signHeaders
)
return SignedRequest(
url = url,
method = method,
headers = headers,
requestBody = requestBody,
sign = sign
)
}
override suspend fun signRaw(signString: String, keyPair: Key, signHeaders: List<String>): Sign {
val signer = Signature.getInstance("SHA256withRSA")
signer.initSign(keyPair.privateKey)
signer.update(signString.toByteArray())
val sign = signer.sign()
val signature = Base64Util.encode(sign)
return Sign(
signature,
"""keyId="${keyPair.keyId}",algorithm="${signHeaders.joinToString(" ")}",signature="$signature""""
)
}
private fun buildSignString(
url: URL,
method: HttpMethod,
headers: Headers,
signHeaders: List<String>
): String {
headers.toMap().map { it.key.lowercase() to it.value }.toMap()
val result = signHeaders.map {
if (it.startsWith("(")) {
specialHeader(it, url, method)
} else {
generalHeader(it, headers.get(it)!!)
}
}.joinToString("\n")
return result
}
private fun specialHeader(fieldName: String, url: URL, method: HttpMethod): String {
if (fieldName != "(request-target)") {
throw IllegalArgumentException(fieldName + "is unsupported type")
}
return "(request-target): ${method.value.lowercase()} ${url.path}"
}
private fun generalHeader(fieldName: String, value: String): String {
return "$fieldName: $value"
}
}

View File

@ -0,0 +1,10 @@
package dev.usbharu.hideout.service.signature
import java.security.PrivateKey
import java.security.PublicKey
data class Key(
val keyId: String,
val privateKey: PrivateKey,
val publicKey: PublicKey
)

View File

@ -0,0 +1,6 @@
package dev.usbharu.hideout.service.signature
data class Sign(
val signature: String,
val signatureHeader: String
)

View File

@ -0,0 +1,23 @@
package dev.usbharu.hideout.service.signature
import io.ktor.client.request.*
import io.ktor.http.*
data class SignedRequest(
val url: String,
val method: HttpMethod,
val headers: Headers,
val requestBody: String,
val sign: Sign
) {
fun toRequestBuilder(): HttpRequestBuilder {
val httpRequestBuilder = HttpRequestBuilder()
httpRequestBuilder.url(this.url)
httpRequestBuilder.method = this.method
httpRequestBuilder.headers {
this.appendAll(headers)
}
httpRequestBuilder.setBody(requestBody)
return httpRequestBuilder
}
}