From e85e5d10f9f44f3a0c99510bcd7a11570ddaeb1f Mon Sep 17 00:00:00 2001
From: usbharu <i@usbharu.dev>
Date: Mon, 23 Sep 2024 17:52:06 +0900
Subject: [PATCH] =?UTF-8?q?feat:=20RSASSA-PSS=20Using=20SHA-512=E3=81=A7?=
 =?UTF-8?q?=E7=BD=B2=E5=90=8D=E3=81=AE=E7=94=9F=E6=88=90=E3=81=8C=E3=81=A7?=
 =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../httpsignature/v2/SignatureAlgorithm.kt    |   6 -
 .../usbharu/httpsignature/v2/SignatureBase.kt |   6 +-
 .../httpsignature/v2/SignatureParameter.kt    |   3 +-
 .../httpsignature/v2/GenerateSignatureTest.kt | 104 ++++++++++++++++++
 4 files changed, 109 insertions(+), 10 deletions(-)
 delete mode 100644 src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureAlgorithm.kt

diff --git a/src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureAlgorithm.kt b/src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureAlgorithm.kt
deleted file mode 100644
index e88cb82..0000000
--- a/src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureAlgorithm.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package dev.usbharu.httpsignature.v2
-
-enum class SignatureAlgorithm(val value: String) {
-    ;
-
-}
\ No newline at end of file
diff --git a/src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureBase.kt b/src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureBase.kt
index b0da170..8d9b313 100644
--- a/src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureBase.kt
+++ b/src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureBase.kt
@@ -23,10 +23,10 @@ class SignatureBase() {
     fun generateSignatureParameterString(signatureParameter: SignatureParameter): String {
         return listOfNotNull(
             list.keys.joinToString(" ", "(", ")"),
-            signatureParameter.algorithm?.let { algorithm -> "alg=\"${algorithm.value}\"" },
+            signatureParameter.algorithm?.let { algorithm -> "alg=\"${algorithm}\"" },
             signatureParameter.keyId?.let { keyId -> "keyid=\"$keyId\"" },
-            signatureParameter.created?.let { created -> "created=$created" },
-            signatureParameter.expires?.let { expires -> "expires=$expires" },
+            signatureParameter.created?.let { created -> "created=${created.epochSecond}" },
+            signatureParameter.expires?.let { expires -> "expires=${expires.epochSecond}" },
             signatureParameter.nonce?.let { nonce -> "nonce=\"$nonce\"" },
             signatureParameter.tag?.let { tag -> "tag=\"$tag\"" },
         ).joinToString(";")
diff --git a/src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureParameter.kt b/src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureParameter.kt
index 1597056..43ec442 100644
--- a/src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureParameter.kt
+++ b/src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureParameter.kt
@@ -3,10 +3,11 @@ package dev.usbharu.httpsignature.v2
 import java.time.Instant
 
 data class SignatureParameter(
-    val algorithm: SignatureAlgorithm? = null,
+    val algorithm: String? = null,
     val keyId: String? = null,
     val created: Instant? = null,
     val expires: Instant? = null,
     val nonce: String? = null,
     val tag: String? = null,
 )
+
diff --git a/src/test/kotlin/dev/usbharu/httpsignature/v2/GenerateSignatureTest.kt b/src/test/kotlin/dev/usbharu/httpsignature/v2/GenerateSignatureTest.kt
index 3b44b9f..5934a7b 100644
--- a/src/test/kotlin/dev/usbharu/httpsignature/v2/GenerateSignatureTest.kt
+++ b/src/test/kotlin/dev/usbharu/httpsignature/v2/GenerateSignatureTest.kt
@@ -1,11 +1,18 @@
 package dev.usbharu.httpsignature.v2
 
 import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertTrue
 import org.junit.jupiter.api.Test
 import java.security.KeyFactory
+import java.security.Signature
 import java.security.interfaces.RSAPrivateKey
+import java.security.spec.MGF1ParameterSpec
 import java.security.spec.PKCS8EncodedKeySpec
+import java.security.spec.PSSParameterSpec
+import java.security.spec.X509EncodedKeySpec
+import java.time.Instant
 import java.util.*
+import kotlin.math.sign
 
 class GenerateSignatureTest {
 
@@ -75,4 +82,101 @@ class GenerateSignatureTest {
         println(sign.signature)
         println(sign.signatureInput)
     }
+
+    @Test
+    fun test2() {
+
+        val key = KeyFactory.getInstance("RSASSA-PSS").generatePrivate(
+            PKCS8EncodedKeySpec(
+                Base64.getDecoder().decode(
+                    "MIIEvgIBADALBgkqhkiG9w0BAQoEggSqMIIEpgIBAAKCAQEAr4tmm3r20Wd/Pbqv" +
+                            "P1s2+QEtvpuRaV8Yq40gjUR8y2Rjxa6dpG2GXHbPfvMs8ct+Lh1GH45x28Rw3Ry5" +
+                            "3mm+oAXjyQ86OnDkZ5N8lYbggD4O3w6M6pAvLkhk95AndTrifbIFPNU8PPMO7Oyr" +
+                            "FAHqgDsznjPFmTOtCEcN2Z1FpWgchwuYLPL+Wokqltd11nqqzi+bJ9cvSKADYdUA" +
+                            "AN5WUtzdpiy6LbTgSxP7ociU4Tn0g5I6aDZJ7A8Lzo0KSyZYoA485mqcO0GVAdVw" +
+                            "9lq4aOT9v6d+nb4bnNkQVklLQ3fVAvJm+xdDOp9LCNCN48V2pnDOkFV6+U9nV5oy" +
+                            "c6XI2wIDAQABAoIBAQCUB8ip+kJiiZVKF8AqfB/aUP0jTAqOQewK1kKJ/iQCXBCq" +
+                            "pbo360gvdt05H5VZ/RDVkEgO2k73VSsbulqezKs8RFs2tEmU+JgTI9MeQJPWcP6X" +
+                            "aKy6LIYs0E2cWgp8GADgoBs8llBq0UhX0KffglIeek3n7Z6Gt4YFge2TAcW2WbN4" +
+                            "XfK7lupFyo6HHyWRiYHMMARQXLJeOSdTn5aMBP0PO4bQyk5ORxTUSeOciPJUFktQ" +
+                            "HkvGbym7KryEfwH8Tks0L7WhzyP60PL3xS9FNOJi9m+zztwYIXGDQuKM2GDsITeD" +
+                            "2mI2oHoPMyAD0wdI7BwSVW18p1h+jgfc4dlexKYRAoGBAOVfuiEiOchGghV5vn5N" +
+                            "RDNscAFnpHj1QgMr6/UG05RTgmcLfVsI1I4bSkbrIuVKviGGf7atlkROALOG/xRx" +
+                            "DLadgBEeNyHL5lz6ihQaFJLVQ0u3U4SB67J0YtVO3R6lXcIjBDHuY8SjYJ7Ci6Z6" +
+                            "vuDcoaEujnlrtUhaMxvSfcUJAoGBAMPsCHXte1uWNAqYad2WdLjPDlKtQJK1diCm" +
+                            "rqmB2g8QE99hDOHItjDBEdpyFBKOIP+NpVtM2KLhRajjcL9Ph8jrID6XUqikQuVi" +
+                            "4J9FV2m42jXMuioTT13idAILanYg8D3idvy/3isDVkON0X3UAVKrgMEne0hJpkPL" +
+                            "FYqgetvDAoGBAKLQ6JZMbSe0pPIJkSamQhsehgL5Rs51iX4m1z7+sYFAJfhvN3Q/" +
+                            "OGIHDRp6HjMUcxHpHw7U+S1TETxePwKLnLKj6hw8jnX2/nZRgWHzgVcY+sPsReRx" +
+                            "NJVf+Cfh6yOtznfX00p+JWOXdSY8glSSHJwRAMog+hFGW1AYdt7w80XBAoGBAImR" +
+                            "NUugqapgaEA8TrFxkJmngXYaAqpA0iYRA7kv3S4QavPBUGtFJHBNULzitydkNtVZ" +
+                            "3w6hgce0h9YThTo/nKc+OZDZbgfN9s7cQ75x0PQCAO4fx2P91Q+mDzDUVTeG30mE" +
+                            "t2m3S0dGe47JiJxifV9P3wNBNrZGSIF3mrORBVNDAoGBAI0QKn2Iv7Sgo4T/XjND" +
+                            "dl2kZTXqGAk8dOhpUiw/HdM3OGWbhHj2NdCzBliOmPyQtAr770GITWvbAI+IRYyF" +
+                            "S7Fnk6ZVVVHsxjtaHy1uJGFlaZzKR4AGNaUTOJMs6NadzCmGPAxNQQOCqoUjn4XR" +
+                            "rOjr9w349JooGXhOxbu8nOxX"
+                )
+            )
+        )
+
+        val signer = HttpMessageSignatureSigner()
+        val signatureBase = SignatureBaseBuilder()
+            .header("Host", "example.com")
+            .build()
+        val material = Material(
+            signatureBase,
+            key,
+            "sig"
+        )
+
+        val signatureParameter = SignatureParameter(
+            algorithm = "rsa-pss-sha512",
+            keyId = "a",
+            created = Instant.ofEpochSecond(1727076643),
+//            expires = Instant.ofEpochSecond(1727076943),
+            nonce = "a",
+            tag = "a"
+        )
+        val sign = signer.sign(
+            material,
+            signatureParameter,
+            RsaPssSha512SignatureSigner()
+        )
+
+        val expectedSignatureBase ="\"host\": example.com\n" +
+                "\"@signature-params\": (\"host\");alg=\"rsa-pss-sha512\";keyid=\"a\";created=1727076643;nonce=\"a\";tag=\"a\""
+
+        val expectedSignatureInput = "sig=(\"host\");alg=\"rsa-pss-sha512\";keyid=\"a\";created=1727076643;nonce=\"a\";tag=\"a\""
+
+        assertEquals(expectedSignatureBase, signatureBase.generateSignatureBase(signatureParameter))
+        assertEquals(expectedSignatureInput, sign.signatureInput)
+
+
+        println(sign.signature)
+        println(signatureBase.generateSignatureBase(signatureParameter))
+
+
+
+        val publicKey = KeyFactory.getInstance("RSA").generatePublic(
+            X509EncodedKeySpec(
+                Base64.getDecoder().decode(
+                    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr4tmm3r20Wd/PbqvP1s2" +
+                            "+QEtvpuRaV8Yq40gjUR8y2Rjxa6dpG2GXHbPfvMs8ct+Lh1GH45x28Rw3Ry53mm+" +
+                            "oAXjyQ86OnDkZ5N8lYbggD4O3w6M6pAvLkhk95AndTrifbIFPNU8PPMO7OyrFAHq" +
+                            "gDsznjPFmTOtCEcN2Z1FpWgchwuYLPL+Wokqltd11nqqzi+bJ9cvSKADYdUAAN5W" +
+                            "Utzdpiy6LbTgSxP7ociU4Tn0g5I6aDZJ7A8Lzo0KSyZYoA485mqcO0GVAdVw9lq4" +
+                            "aOT9v6d+nb4bnNkQVklLQ3fVAvJm+xdDOp9LCNCN48V2pnDOkFV6+U9nV5oyc6XI" +
+                            "2wIDAQAB"
+                )
+            )
+        )
+
+        val signature = Signature.getInstance("RSASSA-PSS")
+        signature.setParameter(PSSParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 64, PSSParameterSpec.TRAILER_FIELD_BC))
+        signature.initVerify(publicKey)
+        signature.update(signatureBase.generateSignatureBase(signatureParameter).toByteArray())
+        val verify = signature.verify(Base64.getDecoder().decode(sign.signature))
+
+        assertTrue(verify)
+    }
 }
\ No newline at end of file