package com.console.api.webhook.example ​ import com.nimbusds.jose.JOSEException import com.nimbusds.jose.JWSAlgorithm import com.nimbusds.jose.JWSHeader import com.nimbusds.jose.crypto.RSASSAVerifier import com.nimbusds.jose.util.Base64URL import org.junit.Assert.assertTrue import java.math.BigInteger import java.security.KeyFactory import java.security.interfaces.RSAPublicKey import java.security.spec.RSAPublicKeySpec import java.time.LocalDate import java.util.Base64 ​ class WebhookSignatureExample { ​ companion object { @JvmStatic fun main(args: Array) { ​ // This is retrieved from the 'GET /webhook/v1/jwks' API operation val jwk = WebhookJSONWebKey( kty = "RSA", kid = LocalDate.of(2020, 3, 18), alg = "RSA256", n = "ANV-aocctqt5xDRnqomCgsO9dm4hM0Qd75TqG7z2G5Z89JQ7SRy2ok-fIJRiSU5-JfjPc3uph3gSOXyqlNoEh4YGL2R4AP7jhxy9xv0gDVtj1tExB_mmk8EUbmj8" + "hTIrfAgEJrDeB4qMk7MkkKxhHkhLNEJEPZfgYHcHcuKjp2l_vtpiuR9Ouz0febB9K4gLozrp9KHW2K-m0z02-tSurxmmij5nnJ-CEgp0wXcCS4w4G0jve4hcLlL9" + "FU8HKxrb0d4rMQgM3VAal6yG5pwMdtrsch7xA-occwWFC_tHgpDJGNvOJNFtuk7Cit_aom-6U6ssGF13sUtdrog2ePWjVxc=", e = "AQAB" ) ​ // These come from the webhook HTTP request val signatureHeader = "1IJl6VyKU4pYfqMHUd55QBNq5Etbz5a7DOCkID2Nloay76y4f02w2iMXONlyL/Bx9SkrbivOHW1l1XadkUrd5pKUK1fhpcnItukLrsK5ADQOcu" + "EjSLBg9qJffZYooXfc7hOD/fV0sN33W2vBYJspbR3P766DwG/6IO/20f9t/DcSWa79EFZPMnsCicEArNS3iIYBtdZSX5ta5EETt7S8acHbpIlSDrTcYpo0vuz19LQ6SP" + "QqN2LGdR+U7ZOiUQWdfMXhUgE7w94pHQzcOq1IHfw3CylUEcRR/DhrGqs4mBaagO6JpWzeqE1uTAiN579kOtSSqjblTb2AXALTQ3+TtA==" ​ val requestBody = """{"eventId":"569886904","officeId":"132917981","eventType":"INTEGRATION_DEACTIVATED","event":{"integration":{"status":"INACTIVE","webhookId":"2bc47eed-08a0-4d18-a5c0-b7f18ab802e3","officeId":"132917981","createdDateTime":"2020-03-17T23:39:41.804Z","lastUpdatedDateTime":"2020-03-17T23:39:41.804Z"}},"createdDateTime":"2020-03-17T23:39:41.806Z"}""" ​ /* * Signature verification */ ​ val keySpec = RSAPublicKeySpec( BigInteger(Base64.getUrlDecoder().decode(jwk.n)), BigInteger(Base64.getUrlDecoder().decode(jwk.e)) ) val rsaPublicKey = KeyFactory.getInstance(jwk.kty).generatePublic(keySpec) as RSAPublicKey val rsaVerifier = RSASSAVerifier(rsaPublicKey) ​ try { assertTrue( "Invalid Signature", rsaVerifier.verify( JWSHeader(JWSAlgorithm.RS256), requestBody.toByteArray(), Base64URL(signatureHeader) ) ) println("Signature is valid") } catch (e: JOSEException) { println("Invalid signature: $e") } } } ​ private data class WebhookJSONWebKey( val kty: String? = null, val kid: LocalDate? = null, val alg: String? = null, val n: String? = null, val e: String? = null ) }