diff --git a/README.md b/README.md index ea0f856..9bc1796 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ signed and who signed it: ![Validation Page](https://raw.githubusercontent.com/dnl50/tsa-server/develop/docs/images/validation-page.png "Validation Page") +You can also download the signing certificate in case it is included in the response. + ## Configuration All Parameters mentioned below can be configured in variety of ways. Please refer to the diff --git a/docs/images/validation-page.png b/docs/images/validation-page.png index 77edf60..8fcff3c 100644 Binary files a/docs/images/validation-page.png and b/docs/images/validation-page.png differ diff --git a/domain/src/main/java/dev/mieser/tsa/domain/SigningCertificateInformation.java b/domain/src/main/java/dev/mieser/tsa/domain/SigningCertificateInformation.java index 508b9d6..be5a271 100644 --- a/domain/src/main/java/dev/mieser/tsa/domain/SigningCertificateInformation.java +++ b/domain/src/main/java/dev/mieser/tsa/domain/SigningCertificateInformation.java @@ -28,4 +28,9 @@ public class SigningCertificateInformation { */ private final ZonedDateTime expirationDate; + /** + * The Base64 encoding of the ASN.1 DER X.509 encoded certificate which was used to sign the request. + */ + private final String base64EncodedCertificate; + } diff --git a/signing/build.gradle.kts b/signing/build.gradle.kts index 3096c3d..ba64842 100644 --- a/signing/build.gradle.kts +++ b/signing/build.gradle.kts @@ -13,10 +13,10 @@ dependencies { implementation("jakarta.validation:jakarta.validation-api") implementation("commons-io:commons-io:${libs.versions.commonsIo.get()}") implementation("org.apache.commons:commons-lang3") + implementation("commons-codec:commons-codec") testImplementation(project(":test-util")) testImplementation("org.apache.commons:commons-lang3") - testImplementation("commons-codec:commons-codec") integrationTestImplementation("org.springframework.boot:spring-boot-starter-test") integrationTestImplementation("org.hibernate.validator:hibernate-validator") diff --git a/signing/src/main/java/dev/mieser/tsa/signing/mapper/TimeStampValidationResultMapper.java b/signing/src/main/java/dev/mieser/tsa/signing/mapper/TimeStampValidationResultMapper.java index d86fede..4d86cd2 100644 --- a/signing/src/main/java/dev/mieser/tsa/signing/mapper/TimeStampValidationResultMapper.java +++ b/signing/src/main/java/dev/mieser/tsa/signing/mapper/TimeStampValidationResultMapper.java @@ -1,5 +1,9 @@ package dev.mieser.tsa.signing.mapper; +import static org.apache.commons.codec.binary.Base64.encodeBase64String; + +import java.io.IOException; + import lombok.RequiredArgsConstructor; import org.bouncycastle.cert.X509CertificateHolder; @@ -67,7 +71,16 @@ private SigningCertificateInformation mapCertificateInformation(SigningCertifica .serialNumber(signingCertificate.getSerialNumber()) .expirationDate(dateConverter.toZonedDateTime(signingCertificate.getNotAfter())) .issuer(signingCertificate.getIssuer().toString()) + .base64EncodedCertificate(encodeAsBase64String(signingCertificate)) .build(); } + private String encodeAsBase64String(X509CertificateHolder signingCertificate) { + try { + return encodeBase64String(signingCertificate.getEncoded()); + } catch (IOException e) { + throw new IllegalStateException("Failed to retrieve encoded signing certificate.", e); + } + } + } diff --git a/signing/src/main/java/module-info.java b/signing/src/main/java/module-info.java index 7a479c0..3133d13 100644 --- a/signing/src/main/java/module-info.java +++ b/signing/src/main/java/module-info.java @@ -13,6 +13,8 @@ requires org.bouncycastle.provider; + requires org.apache.commons.codec; + requires org.apache.commons.io; requires org.apache.commons.lang3; diff --git a/signing/src/test/java/dev/mieser/tsa/signing/mapper/TimeStampValidationResultMapperTest.java b/signing/src/test/java/dev/mieser/tsa/signing/mapper/TimeStampValidationResultMapperTest.java index 34a6f5a..c7d6015 100644 --- a/signing/src/test/java/dev/mieser/tsa/signing/mapper/TimeStampValidationResultMapperTest.java +++ b/signing/src/test/java/dev/mieser/tsa/signing/mapper/TimeStampValidationResultMapperTest.java @@ -17,6 +17,7 @@ import java.time.ZonedDateTime; import java.util.Date; +import org.apache.commons.codec.binary.Base64; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.tsp.TimeStampResponse; @@ -127,7 +128,7 @@ void mapsTimeStampTokenInformation() { } @Test - void mapsSigningCertificateIdentifier() throws Exception { + void mapsSigningCertificateIdentifier() { // given byte[] signingCertificateHash = "hash".getBytes(UTF_8); SigningCertificateHolder signingCertificateHolder = new SigningCertificateHolder(new AlgorithmIdentifier(id_SHA1), @@ -179,10 +180,13 @@ void mapsSigningCertificateInformation() throws Exception { TimeStampValidationResult mappedValidationResult = testSubject.map(timeStampResponse, signingCertificateHolder, true); // then + String expectedBase64EncodedCertificate = Base64.encodeBase64String(certificate.getEncoded()); + SigningCertificateInformation expectedCertificateInformation = SigningCertificateInformation.builder() .expirationDate(mappedExpirationDate) .serialNumber(certificate.getSerialNumber()) .issuer(certificateHolder.getIssuer().toString()) + .base64EncodedCertificate(expectedBase64EncodedCertificate) .build(); assertThat(mappedValidationResult.getSigningCertificateInformation()).isEqualTo(expectedCertificateInformation); diff --git a/web/src/main/java/module-info.java b/web/src/main/java/module-info.java index 1edace4..33731db 100644 --- a/web/src/main/java/module-info.java +++ b/web/src/main/java/module-info.java @@ -23,6 +23,8 @@ requires org.apache.commons.codec; + requires org.apache.commons.io; + requires org.apache.commons.lang3; requires com.fasterxml.jackson.databind; diff --git a/web/src/main/resources/messages_de.properties b/web/src/main/resources/messages_de.properties index c56dfbb..615c4b7 100644 --- a/web/src/main/resources/messages_de.properties +++ b/web/src/main/resources/messages_de.properties @@ -37,5 +37,6 @@ validate.result.cert-hash.description=Der Base64 kodierte Hashwert des Signaturz validate.result.certificate-issuer=Aussteller validate.result.certificate-serial=Seriennummer validate.result.certificate-expiration-date=Ablaufdatum +validate.result.download-cert=Zertifikat herunterladen validate.error.invalid-response=Bei der Eingabe handelt es sich nicht um eine valide Time Stamp response nach RFC 3161/RFC 5816. error.title-prefix=Fehler diff --git a/web/src/main/resources/messages_en.properties b/web/src/main/resources/messages_en.properties index 62f7f90..f3ce330 100644 --- a/web/src/main/resources/messages_en.properties +++ b/web/src/main/resources/messages_en.properties @@ -37,5 +37,6 @@ validate.result.cert-hash.description=The Base64 encoded hash value of the signi validate.result.certificate-issuer=Issuer validate.result.certificate-serial=Serialnumber validate.result.certificate-expiration-date=Expiration Date +validate.result.download-cert=Download Certificate validate.error.invalid-response=The input is not a valid time stamp response according to RFC 3161/RFC 5816. error.title-prefix=Error diff --git a/web/src/main/resources/templates/validation-result.html b/web/src/main/resources/templates/validation-result.html index 7a70809..320f26f 100644 --- a/web/src/main/resources/templates/validation-result.html +++ b/web/src/main/resources/templates/validation-result.html @@ -86,12 +86,19 @@