mirror of https://github.com/usbharu/Hideout.git
feat: HTTP Signatureの署名器を実装
This commit is contained in:
parent
16d0b555f7
commit
8e611aaf4f
|
@ -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
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
)
|
|
@ -0,0 +1,6 @@
|
||||||
|
package dev.usbharu.hideout.service.signature
|
||||||
|
|
||||||
|
data class Sign(
|
||||||
|
val signature: String,
|
||||||
|
val signatureHeader: String
|
||||||
|
)
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue