Skip to content

Commit

Permalink
Merge pull request #19 from OSGP/feature/FDP-2421
Browse files Browse the repository at this point in the history
FDP-2421: Signature verification functions return booleans
  • Loading branch information
jasperkamerling authored Jul 5, 2024
2 parents 4bfa025 + 3cc60ea commit e4ac1f3
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 152 deletions.
31 changes: 19 additions & 12 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,43 @@ name: Gradle Pipeline

on:
push:
branches: [ "main" , "develop" ]
branches: [ "main" ]
tags: [ "v**" ]
pull_request:
branches: [ "main", "develop" ]
branches: [ "main" ]

jobs:
build:
name: Gradle build and publish
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 17
uses: actions/setup-java@v3
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '17'
java-version: '21'
distribution: 'temurin'
- name: Build and publish with Gradle
uses: gradle/gradle-build-action@v2.8.0
- name: Setup Gradle to generate and submit dependency graphs
uses: gradle/actions/setup-gradle@v3
with:
arguments: build
dependency-graph: generate-and-submit
- name: Build with Gradle
run: ./gradlew build integrationTest
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run sonar analysis with Gradle
run: ./gradlew testCodeCoverageReport integrationTestCodeCoverageReport sonar
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Publish Maven artifacts
if: github.ref == 'refs/heads/main' || github.ref_type == 'tag'
uses: gradle/gradle-build-action@v2.8.0
with:
arguments: publish
run: ./gradlew publish
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
42 changes: 27 additions & 15 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import io.spring.gradle.dependencymanagement.internal.dsl.StandardDependencyManagementExtension
import org.jetbrains.kotlin.com.github.gundy.semver4j.SemVer
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
import java.net.URI

plugins {
id("io.spring.dependency-management") version "1.1.4" apply false
kotlin("jvm") version "1.9.22" apply false
kotlin("plugin.spring") version "1.9.22" apply false
kotlin("jvm") version "2.0.0" apply false
kotlin("plugin.spring") version "2.0.0" apply false
id("org.sonarqube") version "5.0.0.4638"
}

sonar {
properties {
property("sonar.host.url", "https://sonarcloud.io")
property("sonar.projectKey", "OSGP_gxf-java-utilities")
property("sonar.organization", "gxf")
}
}
group = "com.gxf.utilities"
version = System.getenv("GITHUB_REF_NAME")
?.replace("/", "-")
Expand All @@ -22,6 +29,8 @@ subprojects {
apply(plugin = "org.jetbrains.kotlin.plugin.spring")
apply(plugin = "io.spring.dependency-management")
apply(plugin = "org.gradle.maven-publish")
apply(plugin = "jacoco")
apply(plugin = "jacoco-report-aggregation")

group = rootProject.group
version = rootProject.version
Expand All @@ -30,24 +39,22 @@ subprojects {
mavenCentral()
}

extensions.configure<StandardDependencyManagementExtension> {
imports {
mavenBom("org.springframework.boot:spring-boot-dependencies:3.3.1")
}
}

extensions.configure<JavaPluginExtension> {
withJavadocJar()
withSourcesJar()

toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}

extensions.configure<StandardDependencyManagementExtension> {
imports {
mavenBom("org.springframework.boot:spring-boot-dependencies:3.2.2")
extensions.configure<KotlinJvmProjectExtension> {
jvmToolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}

tasks.withType<KotlinCompile> {
compilerOptions {
jvmTarget = JvmTarget.JVM_21
freeCompilerArgs = listOf("-Xjsr305=strict")
}
}
Expand All @@ -68,5 +75,10 @@ subprojects {
}
}
}
publications {
create<MavenPublication>("java") {
from(components.getByName("java"))
}
}
}
}
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
10 changes: 2 additions & 8 deletions kafka-avro/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
dependencies {
implementation("org.apache.kafka:kafka-clients")
implementation("org.apache.avro:avro:1.11.1")
implementation(libs.avro)

implementation("org.slf4j:slf4j-api")

testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("org.junit.jupiter:junit-jupiter-engine")

testImplementation("org.assertj:assertj-core")
}

publishing {
publications {
create<MavenPublication>("java") {
from(components.findByName("java"))
}
}
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
10 changes: 2 additions & 8 deletions kafka-azure-oauth/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
dependencies {
implementation("org.apache.kafka:kafka-clients")
implementation("com.microsoft.azure:msal4j:1.13.10")
implementation(libs.msal)

implementation("org.slf4j:slf4j-api")

testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("org.junit.jupiter:junit-jupiter-engine")

testImplementation("org.assertj:assertj-core")
}

publishing {
publications {
create<MavenPublication>("java") {
from(components.findByName("java"))
}
}
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
12 changes: 3 additions & 9 deletions kafka-message-signing/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,11 @@ dependencies {
testImplementation("org.springframework:spring-test")
testImplementation("org.springframework.boot:spring-boot-test")
testImplementation("org.springframework.boot:spring-boot-starter")
testImplementation(libs.mockitoKotlin)
testImplementation(testLibs.mockitoKotlin)

testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

tasks.test {
useJUnitPlatform()
}

publishing {
publications {
create<MavenPublication>("java") {
from(components.findByName("java"))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import com.gxf.utilities.kafka.message.wrapper.SignableMessageWrapper
import org.apache.avro.specific.SpecificRecordBase
import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.clients.producer.ProducerRecord
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.ssl.pem.PemContent
import org.springframework.core.io.Resource
Expand Down Expand Up @@ -163,28 +165,26 @@ class MessageSigner(properties: MessageSigningProperties) {
* @param message the message to be verified
* @return `true` if the signature of the given `message` was verified; `false`
* if not.
* @throws IllegalStateException if this message signer has a private key needed for signing
* messages, but does not have the public key for signature verification.
* @throws UncheckedIOException if determining the bytes for the message throws an IOException.
* @throws UncheckedSecurityException if the signature verification process throws a
* SignatureException.
*/
fun <T> verifyUsingField(message: SignableMessageWrapper<T>): T {
check(this.canVerifyMessageSignatures()) { "This MessageSigner is not configured for verification, it can only be used for signing" }
fun <T> verifyUsingField(message: SignableMessageWrapper<T>): Boolean {
if (!this.canVerifyMessageSignatures()) {
logger.error("This MessageSigner is not configured for verification, it can only be used for signing")
return false
}

val messageSignature = message.getSignature() ?: throw IllegalStateException(
"This message does not contain a signature"
)
val messageSignature = message.getSignature()

if (messageSignature == null || messageSignature.isEmpty()) {
logger.error("This message does not contain a signature")
return false
}

try {
message.setSignature(null)
if(this.verifySignatureBytes(messageSignature, this.toByteArray(message))) {
return message.message
} else {
throw VerificationException("Verification of message signing failed")
}
} catch (e: SignatureException) {
throw UncheckedSecurityException("Unable to verify message signature", e)
return this.verifySignatureBytes(messageSignature, this.toByteArray(message))
} catch (e: Exception) {
logger.error("Unable to verify message signature", e)
return false
} finally {
message.setSignature(messageSignature)
}
Expand All @@ -196,32 +196,32 @@ class MessageSigner(properties: MessageSigningProperties) {
* @param consumerRecord the record to be verified
* @return `true` if the signature of the given `consumerRecord` was verified; `false`
* if not.
* @throws IllegalStateException if this message signer has a private key needed for signing
* messages, but does not have the public key for signature verification.
* @throws UncheckedIOException if determining the bytes throws an IOException.
* @throws UncheckedSecurityException if the signature verification process throws a
* SignatureException.
*/
fun verifyUsingHeader(consumerRecord: ConsumerRecord<String, out SpecificRecordBase>): ConsumerRecord<String, out SpecificRecordBase> {
check(this.canVerifyMessageSignatures()) { "This MessageSigner is not configured for verification, it can only be used for signing" }
fun verifyUsingHeader(consumerRecord: ConsumerRecord<String, out SpecificRecordBase>): Boolean {
if (!this.canVerifyMessageSignatures()) {
logger.error("This MessageSigner is not configured for verification, it can only be used for signing")
return false
}

val header = consumerRecord.headers().lastHeader(RECORD_HEADER_KEY_SIGNATURE)
?: throw IllegalStateException(
"This ProducerRecord does not contain a signature header"
)
if (header == null) {
logger.error("This ProducerRecord does not contain a signature header")
return false
}

val signatureBytes = header.value()
check(!(signatureBytes == null || signatureBytes.isEmpty())) { "Signature header is empty" }
if (signatureBytes == null || signatureBytes.isEmpty()) {
logger.error("Signature header is empty")
return false
}

try {
consumerRecord.headers().remove(RECORD_HEADER_KEY_SIGNATURE)
val specificRecordBase: SpecificRecordBase = consumerRecord.value()
if(this.verifySignatureBytes(signatureBytes, this.toByteArray(specificRecordBase))) {
return consumerRecord
} else {
throw VerificationException("Verification of record signing failed")
}
} catch (e: SignatureException) {
throw UncheckedSecurityException("Unable to verify message signature", e)
return this.verifySignatureBytes(signatureBytes, this.toByteArray(specificRecordBase))
} catch (e: Exception) {
logger.error("Unable to verify message signature", e)
return false
}
}

Expand Down Expand Up @@ -289,6 +289,8 @@ class MessageSigner(properties: MessageSigningProperties) {

private val PEM_REMOVAL_PATTERN: Pattern = Pattern.compile("-----(?:BEGIN|END) .*?-----|\\r|\\n")

val logger: Logger = LoggerFactory.getLogger(this::class.java)

@JvmStatic
private fun signatureInstance(
signatureAlgorithm: String,
Expand Down

This file was deleted.

Loading

0 comments on commit e4ac1f3

Please sign in to comment.