Skip to content

Commit

Permalink
Add download link for signing certificate
Browse files Browse the repository at this point in the history
To further analyze the signing certificate it can be useful to
be able to download the raw .crt file.
  • Loading branch information
dnl50 committed Jun 16, 2022
1 parent b65eb7e commit a6ffbc6
Show file tree
Hide file tree
Showing 11 changed files with 41 additions and 4 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Binary file modified docs/images/validation-page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

}
2 changes: 1 addition & 1 deletion signing/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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);
}
}

}
2 changes: 2 additions & 0 deletions signing/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

requires org.bouncycastle.provider;

requires org.apache.commons.codec;

requires org.apache.commons.io;

requires org.apache.commons.lang3;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions web/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

requires org.apache.commons.codec;

requires org.apache.commons.io;

requires org.apache.commons.lang3;

requires com.fasterxml.jackson.databind;
Expand Down
1 change: 1 addition & 0 deletions web/src/main/resources/messages_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions web/src/main/resources/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
11 changes: 9 additions & 2 deletions web/src/main/resources/templates/validation-result.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,19 @@ <h2 th:text="#{validate.result.signing.title}">Signing</h2>

<tr>
<th scope="row" class="w-25">
<span th:text="#{validate.result.signing-cert-included}">Signed by this TSA?</span>
<span th:text="#{validate.result.signing-cert-included}">Signing cert included</span>
<i class="bi bi-question-circle" data-bs-toggle="tooltip" data-bs-placement="right"
th:title="#{validate.result.signing-cert-included.description}"></i>
</th>
<td>
<i class="bi" th:classappend="${validationResult.signingCertificateInformation}? 'bi-check-circle' : 'bi-x-circle'"></i>
<i class="bi"
th:classappend="${validationResult.signingCertificateInformation}? 'bi-check-circle' : 'bi-x-circle'"></i>
<a th:href="'data:application/pkix-cert;base64,' + ${validationResult.signingCertificateInformation.base64EncodedCertificate}"
target="_blank" download="signing-cert.crt" class="px-3"
th:title="#{validate.result.download-cert}"
th:if="${validationResult.signingCertificateInformation}">
<i class="bi bi-download"></i>
</a>
</td>
</tr>

Expand Down

0 comments on commit a6ffbc6

Please sign in to comment.