diff --git a/obp-api/src/main/resources/props/sample.props.template b/obp-api/src/main/resources/props/sample.props.template index a3ecc27fd1..9847912b09 100644 --- a/obp-api/src/main/resources/props/sample.props.template +++ b/obp-api/src/main/resources/props/sample.props.template @@ -162,6 +162,9 @@ jwt.use.ssl=false # truststore.path.tpp_signature = path/to/ca.p12 # truststore.password.tpp_signature = truststore-password +# Bypass TPP signature validation +# bypass_tpp_signature_validation = false + ## Enable writing API metrics (which APIs are called) to RDBMS write_metrics=true diff --git a/obp-api/src/main/scala/code/api/util/BerlinGroupSigning.scala b/obp-api/src/main/scala/code/api/util/BerlinGroupSigning.scala index ac89f1181f..fa22c49233 100644 --- a/obp-api/src/main/scala/code/api/util/BerlinGroupSigning.scala +++ b/obp-api/src/main/scala/code/api/util/BerlinGroupSigning.scala @@ -1,12 +1,11 @@ package code.api.util -import code.api.{CertificateConstants, RequestHeader} +import code.api.RequestHeader import code.util.Helper.MdcLoggable import com.openbankproject.commons.model.User import net.liftweb.common.{Box, Failure, Full} import net.liftweb.http.provider.HTTPParam -import java.util.Base64 import java.nio.charset.StandardCharsets import java.nio.file.{Files, Paths} import java.security._ @@ -113,7 +112,7 @@ object BerlinGroupSigning extends MdcLoggable { * @param forwardResult Propagated result of calling function * @return Propagated result of calling function or signing request error */ - def verifySignedRequest(body: Box[String], verb: String, url: String, reqHeaders: List[HTTPParam], forwardResult: (Box[User], Option[CallContext])) = { + def verifySignedRequest(body: Box[String], verb: String, url: String, reqHeaders: List[HTTPParam], forwardResult: (Box[User], Option[CallContext])): (Box[User], Option[CallContext]) = { def checkRequestIsSigned(requestHeaders: List[HTTPParam]): Boolean = { requestHeaders.exists(_.name == RequestHeader.`TPP-Signature-Certificate`) && requestHeaders.exists(_.name == RequestHeader.Signature) && @@ -132,8 +131,8 @@ object BerlinGroupSigning extends MdcLoggable { val signatureHeaderValue = getHeaderValue(RequestHeader.Signature, requestHeaders) val signature = parseSignatureHeader(signatureHeaderValue).getOrElse("signature", "NONE") - val headersss = parseSignatureHeader(signatureHeaderValue).getOrElse("headers", "").split(" ").toList - val headers = headersss.map(h => + val headersToSign = parseSignatureHeader(signatureHeaderValue).getOrElse("headers", "").split(" ").toList + val headers = headersToSign.map(h => if(h.toLowerCase() == RequestHeader.Digest.toLowerCase()) { s"$h: SHA-256=$digest" } else { @@ -143,8 +142,10 @@ object BerlinGroupSigning extends MdcLoggable { val signingString = headers.mkString("\n") val isVerified = verifySignature(signingString, signature, certificatePem) val isValidated = CertificateVerifier.validateCertificate(certificatePem) + val bypassValidation = APIUtil.getPropsAsBoolValue("bypass_tpp_signature_validation", defaultValue = false) (isVerified, isValidated) match { case (true, true) => forwardResult + case (true, false) if bypassValidation => forwardResult case (true, false) => (Failure(ErrorMessages.X509PublicKeyCannotBeValidated), forwardResult._2) case (false, _) => (Failure(ErrorMessages.X509PublicKeyCannotVerify), forwardResult._2) } @@ -157,7 +158,7 @@ object BerlinGroupSigning extends MdcLoggable { def getHeaderValue(name: String, requestHeaders: List[HTTPParam]): String = { requestHeaders.find(_.name.toLowerCase() == name.toLowerCase()).map(_.values.mkString).getOrElse("None") } - def getPem(requestHeaders: List[HTTPParam]): String = { + private def getPem(requestHeaders: List[HTTPParam]): String = { val certificate = getHeaderValue(RequestHeader.`TPP-Signature-Certificate`, requestHeaders) // Decode the Base64 string val decodedBytes = Base64.getDecoder.decode(certificate) @@ -184,6 +185,13 @@ object BerlinGroupSigning extends MdcLoggable { } } + def getTppSignatureCertificate(requestHeaders: List[HTTPParam]): Option[String] = { + getPem(requestHeaders) match { + case value if value.isEmpty => None + case value => Some(value) + } + } + def parseSignatureHeader(signatureHeader: String): Map[String, String] = { val regex = new Regex("""(\w+)\s*=\s*"([^"]*)"""", "key", "value") regex.findAllMatchIn(signatureHeader).map(m => m.group("key") -> m.group("value")).toMap diff --git a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala index 6ecfb46063..82d44e42eb 100644 --- a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala +++ b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala @@ -131,7 +131,7 @@ object Consent extends MdcLoggable { */ def getCurrentConsumerViaMtls(callContext: CallContext): Box[Consumer] = { val clientCert: String = APIUtil.`getPSD2-CERT`(callContext.requestHeaders) // MTLS certificate QWAC (Qualified Website Authentication Certificate) - .orElse(APIUtil.getTppSignatureCertificate(callContext.requestHeaders)) // Signature certificate QSealC (Qualified Electronic Seal Certificate) + .orElse(BerlinGroupSigning.getTppSignatureCertificate(callContext.requestHeaders)) // Signature certificate QSealC (Qualified Electronic Seal Certificate) .getOrElse(SecureRandomUtil.csprng.nextLong().toString) // Force to fail { // 1st search is via the original value