diff --git a/cpa-repo/src/main/kotlin/no/nav/emottak/cpa/App.kt b/cpa-repo/src/main/kotlin/no/nav/emottak/cpa/App.kt index a4a282fb..0955da2c 100644 --- a/cpa-repo/src/main/kotlin/no/nav/emottak/cpa/App.kt +++ b/cpa-repo/src/main/kotlin/no/nav/emottak/cpa/App.kt @@ -1,5 +1,6 @@ package no.nav.emottak.cpa +import io.ktor.http.* import io.ktor.server.application.Application import io.ktor.server.application.call import io.ktor.server.engine.embeddedServer @@ -13,6 +14,7 @@ import io.ktor.server.routing.get import no.nav.emottak.cpa.config.DatabaseConfig import no.nav.emottak.cpa.config.mapHikariConfig import no.nav.emottak.melding.model.Header +import no.nav.emottak.melding.model.ValidationResult fun main() { val database = Database(mapHikariConfig(DatabaseConfig())) @@ -33,6 +35,7 @@ fun Application.myApplicationModule() { post("cpa/validate") { val validateRequest = call.receive(Header::class) getCpa(validateRequest.cpaId)!!.validate(validateRequest) + call.respond(HttpStatusCode.OK,ValidationResult(true)) } get("/cpa/{$CPA_ID}/her/{$HER_ID}/encryption/certificate") { diff --git a/cpa-repo/src/main/kotlin/no/nav/emottak/cpa/CPAUtil.kt b/cpa-repo/src/main/kotlin/no/nav/emottak/cpa/CPAUtil.kt index 68592fbd..1b3f9ce7 100644 --- a/cpa-repo/src/main/kotlin/no/nav/emottak/cpa/CPAUtil.kt +++ b/cpa-repo/src/main/kotlin/no/nav/emottak/cpa/CPAUtil.kt @@ -2,7 +2,7 @@ package no.nav.emottak.cpa import io.ktor.server.plugins.BadRequestException import io.ktor.server.plugins.NotFoundException -import no.nav.emottak.melding.model.SignatureDetailsResponse +import no.nav.emottak.melding.model.SignatureDetails import org.oasis_open.committees.ebxml_cppa.schema.cpp_cpa_2_0.Certificate import org.oasis_open.committees.ebxml_cppa.schema.cpp_cpa_2_0.CollaborationProtocolAgreement import org.oasis_open.committees.ebxml_cppa.schema.cpp_cpa_2_0.DeliveryChannel @@ -35,9 +35,9 @@ fun PartyInfo.getCertificateForSignatureValidation( role: String, service: String, action: String -): SignatureDetailsResponse { +): SignatureDetails { val deliveryChannel = this.getSendDeliveryChannel(role, service, action) - return SignatureDetailsResponse( + return SignatureDetails( certificate = deliveryChannel.getSigningCertificate().getX509Certificate(), signatureAlgorithm = deliveryChannel.getSignatureAlgorithm(), hashFunction = deliveryChannel.getHashFunction() diff --git a/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/App.kt b/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/App.kt index 1c7e5b9c..7440653e 100644 --- a/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/App.kt +++ b/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/App.kt @@ -70,7 +70,7 @@ fun Application.myApplicationModule() { try { DokumentValidator().validate(ebMSDocument) }catch(ex:Exception) { - // parse fail + call.respond(HttpStatusCode.InternalServerError,"Validation feilet") } val message = ebMSDocument.buildEbmMessage() diff --git a/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/HttpClientUtil.kt b/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/HttpClientUtil.kt index 4c1297e7..744cc683 100644 --- a/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/HttpClientUtil.kt +++ b/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/HttpClientUtil.kt @@ -7,13 +7,14 @@ import io.ktor.client.request.post import io.ktor.client.request.request import io.ktor.client.request.setBody import io.ktor.client.statement.HttpResponse +import io.ktor.http.* import io.ktor.http.ContentType.Application.Json -import io.ktor.http.HttpMethod -import io.ktor.http.contentType import kotlinx.coroutines.runBlocking +import no.nav.emottak.melding.model.Header import no.nav.emottak.melding.model.PayloadRequest import no.nav.emottak.melding.model.PayloadResponse -import no.nav.emottak.melding.model.SignatureDetailsResponse +import no.nav.emottak.melding.model.SignatureDetails +import no.nav.emottak.melding.model.ValidationResult import no.nav.emottak.util.createX509Certificate import no.nav.emottak.util.decodeBase64 import org.apache.xml.security.algorithms.MessageDigestAlgorithm @@ -24,21 +25,23 @@ import java.security.cert.X509Certificate private val httpClientUtil = HttpClientUtil() private const val payloadProcessorEndpoint = "http://payload-processor/payload" +private const val validatorEndpoint = "https://cpa-repo.intern.dev.nav.no/validate" fun postPayloadRequest(payloadRequest: PayloadRequest): PayloadResponse = runBlocking { httpClientUtil.postPayloadRequest(payloadRequest) } -fun MessageHeader.getPublicSigningCertificate(): X509Certificate { - return createX509Certificate(this.getPublicSigningDetails().certificate) + +fun SignatureDetails.getPublicSigningCertificate(): X509Certificate { + return createX509Certificate(this.certificate) } -fun MessageHeader.getPublicSigningDetails(): SignatureDetailsResponse { +fun MessageHeader.getPublicSigningDetails(): SignatureDetails { //TODO hent og valider sertifikat val cert = decodeBase64("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".toByteArray()) //val timestamp = messageHeader.messageData.timestamp //SecurityUtils.validateCertificate(trustStore, certificate, timestamp) - return SignatureDetailsResponse( + return SignatureDetails( certificate = cert, signatureAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256, hashFunction = MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256 @@ -57,6 +60,14 @@ class HttpClientUtil { contentType(Json) }.body() } + suspend fun postValidate(header: Header) : ValidationResult { + return client.post(validatorEndpoint) { + this.url { + this.path("/cpa/validate") + } + setBody(header) + }.body() + } suspend fun makeHttpRequest(urlString: String): HttpResponse { val response: HttpResponse = client.request(urlString) { diff --git a/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/model/EbMSDocument.kt b/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/model/EbMSDocument.kt index 50f5a67b..664fe6ec 100644 --- a/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/model/EbMSDocument.kt +++ b/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/model/EbMSDocument.kt @@ -15,9 +15,9 @@ */ package no.nav.emottak.ebms.model -import no.nav.emottak.ebms.xml.unmarshal +import no.nav.emottak.ebms.processing.SignatursjekkProcessor import no.nav.emottak.ebms.xml.xmlMarshaller -import no.nav.emottak.util.getFirstChildElement +import no.nav.emottak.melding.model.SignatureDetails import no.nav.emottak.util.marker import org.oasis_open.committees.ebxml_msg.schema.msg_header_2_0.MessageHeader import org.slf4j.LoggerFactory @@ -27,20 +27,29 @@ import java.lang.RuntimeException import java.time.LocalDateTime val log = LoggerFactory.getLogger("no.nav.emottak.ebms.model") -data class EbMSDocument(val conversationId: String, val dokument: Document, val attachments: List){ - fun dokumentType(): DokumentType { - if (attachments.size>0) return DokumentType.PAYLOAD - if (dokument.getElementsByTagName("Acknowledgment").item(0)!=null) return DokumentType.ACKNOWLEDGMENT - if (dokument.getElementsByTagName("ErrorList").item(0)) return DokumentType.FAIL - throw RuntimeException("Unrecognized dokument type") +data class EbMSDocument(val messageId: String, val dokument: Document, val attachments: List) { + fun dokumentType(): DokumentType { + if (attachments.size > 0) return DokumentType.PAYLOAD + if (dokument.getElementsByTagName("Acknowledgment").item(0) != null) return DokumentType.ACKNOWLEDGMENT + if (dokument.getElementsByTagName("ErrorList").item(0)) return DokumentType.FAIL + throw RuntimeException("Unrecognized dokument type") + + } + fun messageHeader():MessageHeader { + val node =this.dokument.getElementsByTagName("MessageHeader").item(0) + return xmlMarshaller.unmarshal(node) + } - } } enum class DokumentType { PAYLOAD, ACKNOWLEDGMENT,FAIL,STATUS,PING } +fun EbMSDocument.sjekkSignature(signatureDetails: SignatureDetails) { + SignatursjekkProcessor().validate(signatureDetails, this.dokument, this.attachments) +} + fun EbMSDocument.buildEbmMessage(): EbMSBaseMessage { val envelope: Envelope = xmlMarshaller.unmarshal( this.dokument) @@ -57,6 +66,10 @@ fun EbMSDocument.buildEbmMessage(): EbMSBaseMessage { } } +fun EbMSDocument.checkSignature(signatureDetails: SignatureDetails, dokument: Document, attachments: List) { + SignatursjekkProcessor().validate(signatureDetails, dokument, attachments) +} + fun EbMSDocument.sendResponse(messageHeader: MessageHeader) { log.info(messageHeader.marker(), "TODO return response message") } diff --git a/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/processing/SignatursjekkProcessor.kt b/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/processing/SignatursjekkProcessor.kt index 26d486d6..f8dcdc88 100644 --- a/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/processing/SignatursjekkProcessor.kt +++ b/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/processing/SignatursjekkProcessor.kt @@ -7,6 +7,7 @@ import no.nav.emottak.ebms.model.EbMSDocument import no.nav.emottak.ebms.model.EbMSPayloadMessage import no.nav.emottak.ebms.validation.CID_PREFIX import no.nav.emottak.ebms.validation.EbMSAttachmentResolver +import no.nav.emottak.melding.model.SignatureDetails import no.nav.emottak.util.retrievePublicX509Certificate import no.nav.emottak.util.retrieveSignatureElement import no.nav.emottak.util.signatur.SignatureException @@ -30,22 +31,17 @@ import java.security.cert.X509Certificate * Mangler: 104 (sertifikatsjekk), 105 (sertifikatsjekk) //TODO * */ -class SignatursjekkProcessor(val dokument: Document, ebMSMessage: EbMSBaseMessage): Processor(ebMSMessage) { - - override fun process() { - val attachments = if (ebMSMessage is EbMSPayloadMessage) ebMSMessage.attachments else emptyList() - validate(ebMSMessage.messageHeader, dokument, attachments) - } +class SignatursjekkProcessor() { init { org.apache.xml.security.Init.init() } @Throws(SignatureException::class) - private fun validate(messageHeader: MessageHeader, dokument: Document, attachments: List) { + public fun validate(signatureDetails: SignatureDetails, dokument: Document, attachments: List) { //TODO Sjekk isNonRepudiation? val xmlSignature = retrieveSignatureElement(dokument) - val certificateFraCPA = messageHeader.getPublicSigningCertificate() + val certificateFraCPA = signatureDetails.getPublicSigningCertificate() val certificateFraSignatur = xmlSignature.retrievePublicX509Certificate() if (certificateFraSignatur != certificateFraCPA) throw SignatureException("Signert med annet sertifikat enn definert i CPA") try { diff --git a/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/validation/DokumentValidator.kt b/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/validation/DokumentValidator.kt index 305cbb4f..9b81fea9 100644 --- a/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/validation/DokumentValidator.kt +++ b/ebms-provider/src/main/kotlin/no/nav/emottak/ebms/validation/DokumentValidator.kt @@ -1,11 +1,38 @@ package no.nav.emottak.ebms.validation +import kotlinx.coroutines.runBlocking +import no.nav.emottak.ebms.HttpClientUtil +import no.nav.emottak.ebms.getPublicSigningDetails import no.nav.emottak.ebms.model.EbMSDocument +import no.nav.emottak.ebms.model.sjekkSignature +import no.nav.emottak.melding.model.Header +import no.nav.emottak.melding.model.Party +import no.nav.emottak.melding.model.SignatureDetails class DokumentValidator { + var httpClient = HttpClientUtil() + fun validate(dokument: EbMSDocument) { + val messageHeader = dokument.messageHeader() + val signaturedetails: SignatureDetails = messageHeader.getPublicSigningDetails() + val header = Header(messageHeader.messageData.messageId, + messageHeader.conversationId, + messageHeader.cpaId, + Party(messageHeader.to.partyId.first().value!!,messageHeader.to.role!!), + Party(messageHeader.from.partyId.first().value!!,messageHeader.from.role!!), + messageHeader.service.value!!, + messageHeader.action) + runBlocking { + httpClient.postValidate(header) + } + dokument.sjekkSignature(signaturedetails) + + + + + } } \ No newline at end of file diff --git a/felles/src/main/kotlin/no/nav/emottak/melding/model/Payload.kt b/felles/src/main/kotlin/no/nav/emottak/melding/model/Payload.kt index c4ff8b86..bb6f48ba 100644 --- a/felles/src/main/kotlin/no/nav/emottak/melding/model/Payload.kt +++ b/felles/src/main/kotlin/no/nav/emottak/melding/model/Payload.kt @@ -35,3 +35,8 @@ data class Party( val herID: String, val role: String ) + +@Serializable +data class ValidationResult( + val valid: Boolean +) diff --git a/felles/src/main/kotlin/no/nav/emottak/melding/model/SignatureDetailsResponse.kt b/felles/src/main/kotlin/no/nav/emottak/melding/model/SignatureDetails.kt similarity index 78% rename from felles/src/main/kotlin/no/nav/emottak/melding/model/SignatureDetailsResponse.kt rename to felles/src/main/kotlin/no/nav/emottak/melding/model/SignatureDetails.kt index 3f34cd66..9bb6850a 100644 --- a/felles/src/main/kotlin/no/nav/emottak/melding/model/SignatureDetailsResponse.kt +++ b/felles/src/main/kotlin/no/nav/emottak/melding/model/SignatureDetails.kt @@ -1,6 +1,6 @@ package no.nav.emottak.melding.model -data class SignatureDetailsResponse( +data class SignatureDetails( val certificate: ByteArray, val signatureAlgorithm: String, val hashFunction: String