mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 18:46:46 +00:00
feature/keyId.CN is mandatory and SN is checked in dec and hex systems
This commit is contained in:
parent
667b14a0b7
commit
ac17aa5de7
@ -117,22 +117,32 @@ object BerlinGroupCheck extends MdcLoggable {
|
||||
maybeSignature.flatMap { header =>
|
||||
BerlinGroupSignatureHeaderParser.parseSignatureHeader(header) match {
|
||||
case Right(parsed) =>
|
||||
// Log parsed values
|
||||
logger.debug(s"Parsed Signature Header:")
|
||||
logger.debug(s" SN: ${parsed.keyId.sn}")
|
||||
logger.debug(s" CA: ${parsed.keyId.ca}")
|
||||
logger.debug(s" CN: ${parsed.keyId.cn}")
|
||||
logger.debug(s" O: ${parsed.keyId.o}")
|
||||
logger.debug(s" Headers: ${parsed.headers.mkString(", ")}")
|
||||
logger.debug(s" Signature: ${parsed.signature}")
|
||||
|
||||
val certificate = getCertificateFromTppSignatureCertificate(reqHeaders)
|
||||
val serialNumber = certificate.getSerialNumber.toString
|
||||
if(parsed.keyId.sn != serialNumber) {
|
||||
logger.debug(s"Serial number from certificate: $serialNumber")
|
||||
logger.debug(s"keyId.SN:${parsed.keyId.sn}")
|
||||
val certSerialNumber = certificate.getSerialNumber
|
||||
|
||||
logger.debug(s"Certificate serial number (decimal): ${certSerialNumber.toString}")
|
||||
logger.debug(s"Certificate serial number (hex): ${certSerialNumber.toString(16).toUpperCase}")
|
||||
|
||||
val snMatches = BerlinGroupSignatureHeaderParser.doesSerialNumberMatch(parsed.keyId.sn, certSerialNumber)
|
||||
|
||||
if (!snMatches) {
|
||||
logger.debug(s"Serial number mismatch. Parsed SN: ${parsed.keyId.sn}, Certificate decimal: ${certSerialNumber.toString}, Certificate hex: ${certSerialNumber.toString(16).toUpperCase}")
|
||||
Some(
|
||||
(
|
||||
fullBoxOrException(
|
||||
Empty ~> APIFailureNewStyle(s"${ErrorMessages.InvalidSignatureHeader}keyId.SN does not match the serial number from certificate", 400, forwardResult._2.map(_.toLight))
|
||||
Empty ~> APIFailureNewStyle(
|
||||
s"${ErrorMessages.InvalidSignatureHeader} keyId.SN does not match the serial number from certificate",
|
||||
400,
|
||||
forwardResult._2.map(_.toLight)
|
||||
)
|
||||
),
|
||||
forwardResult._2
|
||||
)
|
||||
@ -140,11 +150,16 @@ object BerlinGroupCheck extends MdcLoggable {
|
||||
} else {
|
||||
None // All good
|
||||
}
|
||||
|
||||
case Left(error) =>
|
||||
Some(
|
||||
(
|
||||
fullBoxOrException(
|
||||
Empty ~> APIFailureNewStyle(s"${ErrorMessages.InvalidSignatureHeader}$error", 400, forwardResult._2.map(_.toLight))
|
||||
Empty ~> APIFailureNewStyle(
|
||||
s"${ErrorMessages.InvalidSignatureHeader} $error",
|
||||
400,
|
||||
forwardResult._2.map(_.toLight)
|
||||
)
|
||||
),
|
||||
forwardResult._2
|
||||
)
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
package code.api.util
|
||||
|
||||
object BerlinGroupSignatureHeaderParser {
|
||||
import code.util.Helper.MdcLoggable
|
||||
|
||||
case class ParsedKeyId(sn: String, ca: String, o: String)
|
||||
object BerlinGroupSignatureHeaderParser extends MdcLoggable {
|
||||
|
||||
case class ParsedKeyId(sn: String, ca: String, cn: String, o: String)
|
||||
|
||||
case class ParsedSignature(keyId: ParsedKeyId, headers: List[String], signature: String)
|
||||
|
||||
@ -18,13 +20,32 @@ object BerlinGroupSignatureHeaderParser {
|
||||
}
|
||||
}.toMap
|
||||
|
||||
val caValue = kvMap.get("CA").map(_.stripPrefix("CN="))
|
||||
// mandatory fields
|
||||
val snOpt = kvMap.get("SN")
|
||||
val oOpt = kvMap.get("O")
|
||||
|
||||
val caOpt = kvMap.get("CA")
|
||||
val cnOpt = kvMap.get("CN")
|
||||
|
||||
val (caFinal, cnFinal): (String, String) = (caOpt, cnOpt) match {
|
||||
case (Some(caRaw), Some(cnRaw)) =>
|
||||
// Both CA and CN are present: use them as-is
|
||||
(caRaw, cnRaw)
|
||||
|
||||
case (Some(caRaw), None) if caRaw.startsWith("CN=") =>
|
||||
// Special case: CA=CN=... → set both CA and CN to value after CN=
|
||||
val value = caRaw.stripPrefix("CN=")
|
||||
(value, value)
|
||||
|
||||
(kvMap.get("SN"), caValue, kvMap.get("O")) match {
|
||||
case (Some(sn), Some(ca), Some(o)) =>
|
||||
Right(ParsedKeyId(sn, ca, o))
|
||||
case _ =>
|
||||
Left(s"Invalid or missing fields in keyId: found keys ${kvMap.keys.mkString(", ")}")
|
||||
return Left(s"Missing mandatory 'CN' field or invalid CA format: found keys ${kvMap.keys.mkString(", ")}")
|
||||
}
|
||||
|
||||
(snOpt, oOpt) match {
|
||||
case (Some(sn), Some(o)) =>
|
||||
Right(ParsedKeyId(sn, caFinal, cnFinal, o))
|
||||
case _ =>
|
||||
Left(s"Missing mandatory 'SN' or 'O' field: found keys ${kvMap.keys.mkString(", ")}")
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,21 +67,57 @@ object BerlinGroupSignatureHeaderParser {
|
||||
} yield ParsedSignature(keyId, headers, sig)
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect and match incoming SN as decimal or hex against certificate serial number.
|
||||
*/
|
||||
def doesSerialNumberMatch(incomingSn: String, certSerial: java.math.BigInteger): Boolean = {
|
||||
try {
|
||||
val incomingAsDecimal = new java.math.BigInteger(incomingSn, 10)
|
||||
if (incomingAsDecimal == certSerial) {
|
||||
logger.debug(s"SN matched in decimal")
|
||||
return true
|
||||
}
|
||||
} catch {
|
||||
case _: NumberFormatException =>
|
||||
logger.debug(s"Incoming SN is not valid decimal: $incomingSn")
|
||||
}
|
||||
|
||||
try {
|
||||
val incomingAsHex = new java.math.BigInteger(incomingSn, 16)
|
||||
if (incomingAsHex == certSerial) {
|
||||
logger.debug(s"SN matched in hex")
|
||||
return true
|
||||
}
|
||||
} catch {
|
||||
case _: NumberFormatException =>
|
||||
logger.debug(s"Incoming SN is not valid hex: $incomingSn")
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
// Example usage
|
||||
def main(args: Array[String]): Unit = {
|
||||
val header =
|
||||
"""keyId="CA=CN=MAIB Prisacaru Sergiu (Test), SN=43A, O=MAIB", headers="digest date x-request-id", signature="abc123+==" """
|
||||
val testHeaders = List(
|
||||
"""keyId="CA=CN=MAIB Prisacaru Sergiu (Test), SN=43A, O=MAIB", headers="digest date x-request-id", signature="abc123+=="""",
|
||||
"""keyId="CA=SomeCAValue, CN=SomeCNValue, SN=43A, O=MAIB", headers="digest date x-request-id", signature="def456+=="""",
|
||||
"""keyId="CA=MissingCN, SN=43A, O=MAIB", headers="digest date x-request-id", signature="should_fail""""
|
||||
)
|
||||
|
||||
parseSignatureHeader(header) match {
|
||||
case Right(parsed) =>
|
||||
println("Parsed Signature Header:")
|
||||
println(s"SN: ${parsed.keyId.sn}")
|
||||
println(s"CA: ${parsed.keyId.ca}")
|
||||
println(s"O: ${parsed.keyId.o}")
|
||||
println(s"Headers: ${parsed.headers.mkString(", ")}")
|
||||
println(s"Signature: ${parsed.signature}")
|
||||
case Left(error) =>
|
||||
println(s"Error: $error")
|
||||
testHeaders.foreach { header =>
|
||||
println(s"\nParsing header:\n$header")
|
||||
parseSignatureHeader(header) match {
|
||||
case Right(parsed) =>
|
||||
println("Parsed Signature Header:")
|
||||
println(s" SN: ${parsed.keyId.sn}")
|
||||
println(s" CA: ${parsed.keyId.ca}")
|
||||
println(s" CN: ${parsed.keyId.cn}")
|
||||
println(s" O: ${parsed.keyId.o}")
|
||||
println(s" Headers: ${parsed.headers.mkString(", ")}")
|
||||
println(s" Signature: ${parsed.signature}")
|
||||
case Left(error) =>
|
||||
println(s"Error: $error")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user