diff --git a/src/main/java/de/mlessmann/certassist/models/Certificate.java b/src/main/java/de/mlessmann/certassist/models/Certificate.java index fdf3d16..6150139 100644 --- a/src/main/java/de/mlessmann/certassist/models/Certificate.java +++ b/src/main/java/de/mlessmann/certassist/models/Certificate.java @@ -3,12 +3,13 @@ package de.mlessmann.certassist.models; import jakarta.persistence.*; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; +import lombok.*; +import org.hibernate.proxy.HibernateProxy; + import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import lombok.*; -import org.hibernate.proxy.HibernateProxy; @Entity @Table(uniqueConstraints = { @UniqueConstraint(columnNames = { "fingerprint" }) }) @@ -29,14 +30,7 @@ public class Certificate { private String trustingAuthority; - /** - * - */ - @Min(-1) + @Min(1) private int requestedKeyLength; private OffsetDateTime notBefore; @@ -75,12 +69,8 @@ public class Certificate { public final boolean equals(Object o) { if (this == o) return true; if (o == null) return false; - Class oEffectiveClass = o instanceof HibernateProxy - ? ((HibernateProxy) o).getHibernateLazyInitializer().getPersistentClass() - : o.getClass(); - Class thisEffectiveClass = this instanceof HibernateProxy - ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass() - : this.getClass(); + Class oEffectiveClass = o instanceof HibernateProxy ? ((HibernateProxy) o).getHibernateLazyInitializer().getPersistentClass() : o.getClass(); + Class thisEffectiveClass = this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass() : this.getClass(); if (thisEffectiveClass != oEffectiveClass) return false; Certificate that = (Certificate) o; return getId() != null && Objects.equals(getId(), that.getId()); @@ -88,8 +78,6 @@ public class Certificate { @Override public final int hashCode() { - return this instanceof HibernateProxy - ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass().hashCode() - : getClass().hashCode(); + return this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass().hashCode() : getClass().hashCode(); } } diff --git a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLService.java b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLService.java index 85ae4b1..9355796 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/OpenSSLService.java +++ b/src/main/java/de/mlessmann/certassist/openssl/OpenSSLService.java @@ -23,9 +23,11 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.security.cert.X509Certificate; import java.time.OffsetDateTime; +import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; +import java.time.temporal.IsoFields; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; @@ -70,20 +72,20 @@ public class OpenSSLService { "^(?[0-9a-zA-Z]+) (?i)Fingerprint(?-i)=(?[a-z:A-Z0-9]+)" ); private final DateTimeFormatter OSSL_DATE_TIME = new DateTimeFormatterBuilder() - .parseCaseInsensitive() - .appendValue(ChronoField.YEAR, 4) - .appendLiteral('-') - .appendValue(ChronoField.MONTH_OF_YEAR, 2) - .appendLiteral('-') - .appendValue(ChronoField.DAY_OF_MONTH, 2) - .appendLiteral(' ') - .appendValue(ChronoField.HOUR_OF_DAY, 2) - .appendLiteral(':') - .appendValue(ChronoField.MINUTE_OF_HOUR, 2) - .appendLiteral(':') - .appendValue(ChronoField.SECOND_OF_MINUTE, 2) - .appendOffset("+HH:MM:ss", "Z") - .toFormatter(); + .parseCaseInsensitive() + .appendValue(ChronoField.YEAR, 4) + .appendLiteral('-') + .appendValue(ChronoField.MONTH_OF_YEAR, 2) + .appendLiteral('-') + .appendValue(ChronoField.DAY_OF_MONTH, 2) + .appendLiteral(' ') + .appendValue(ChronoField.HOUR_OF_DAY, 2) + .appendLiteral(':') + .appendValue(ChronoField.MINUTE_OF_HOUR, 2) + .appendLiteral(':') + .appendValue(ChronoField.SECOND_OF_MINUTE, 2) + .appendOffset("+HH:MM:ss","Z") + .toFormatter(); private static final String OSSL_ENV_KEY_PW = "KEY_PASS"; private static final String OSSL_ARG_KEY_PW = "env:" + OSSL_ENV_KEY_PW; private final AtomicBoolean versionLogged = new AtomicBoolean(false); @@ -762,7 +764,7 @@ public class OpenSSLService { "sep_multiline", "-nameopt", "lname", - "-modulus" + "-modulus" ) ); command.addAll(Arrays.asList(additArgs)); diff --git a/src/main/java/de/mlessmann/certassist/openssl/X509CertificateInfo.java b/src/main/java/de/mlessmann/certassist/openssl/X509CertificateInfo.java index d99d506..3f81078 100644 --- a/src/main/java/de/mlessmann/certassist/openssl/X509CertificateInfo.java +++ b/src/main/java/de/mlessmann/certassist/openssl/X509CertificateInfo.java @@ -2,21 +2,23 @@ package de.mlessmann.certassist.openssl; import de.mlessmann.certassist.models.CertificateInfoExtension; import de.mlessmann.certassist.models.CertificateInfoSubject; -import java.time.OffsetDateTime; -import java.util.Collections; -import java.util.List; -import java.util.Objects; import lombok.Builder; import org.springframework.lang.Nullable; +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + @Builder public record X509CertificateInfo( - CertificateInfoSubject subject, - @Nullable CertificateInfoSubject issuer, - String serial, - OffsetDateTime notBefore, - OffsetDateTime notAfter, - List extensions + CertificateInfoSubject subject, + @Nullable CertificateInfoSubject issuer, + String serial, + OffsetDateTime notBefore, + OffsetDateTime notAfter, + List extensions ) { public X509CertificateInfo { Objects.requireNonNull(subject); @@ -32,8 +34,4 @@ public record X509CertificateInfo( } return Collections.unmodifiableList(extensions); } - - public boolean hasExtensions() { - return extensions != null && !extensions.isEmpty(); - } } diff --git a/src/main/java/de/mlessmann/certassist/service/CertificateCreationService.java b/src/main/java/de/mlessmann/certassist/service/CertificateCreationService.java index 349558a..1383116 100644 --- a/src/main/java/de/mlessmann/certassist/service/CertificateCreationService.java +++ b/src/main/java/de/mlessmann/certassist/service/CertificateCreationService.java @@ -79,17 +79,9 @@ public class CertificateCreationService { private Certificate createEntityFromInfo(X509CertificateInfo info) { final Certificate certificate = new Certificate(); - certificate.setType( - mapCertificateRequestType( - info.issuer() != null - ? CertificateInfo.RequestType.NORMAL_CERTIFICATE - : CertificateInfo.RequestType.STANDALONE_CERTIFICATE - ) - ); + certificate.setType(mapCertificateRequestType(info.issuer() != null ? CertificateInfo.RequestType.NORMAL_CERTIFICATE : CertificateInfo.RequestType.STANDALONE_CERTIFICATE)); certificate.setSubjectCommonName(info.subject().getCommonName()); - if (info.issuer() != null) { - certificate.setTrustingAuthority(info.issuer().getCommonName()); - } + certificate.setTrustingAuthority(info.issuer().getCommonName()); certificate.setRequestedKeyLength(-1); certificate.setNotBefore(info.notBefore()); certificate.setNotAfter(info.notAfter()); @@ -102,14 +94,12 @@ public class CertificateCreationService { certificate.setSubjectState(subjectInfo.getState()); certificate.setSubjectLocality(subjectInfo.getLocality()); - if (info.hasExtensions()) { - final CertificateInfoExtension extension = info.extensions().getFirst(); - if (extension != null) { - final CertificateExtension certificateExtension = new CertificateExtension(); - certificateExtension.setIdentifier("alternativeNames"); - certificateExtension.setValue(String.join(",", extension.getAlternativeDnsNames())); - certificate.setCertificateExtension(List.of(certificateExtension)); - } + final CertificateInfoExtension extension = info.extensions().getFirst(); + if (extension != null) { + final CertificateExtension certificateExtension = new CertificateExtension(); + certificateExtension.setIdentifier("alternativeNames"); + certificateExtension.setValue(String.join(",", extension.getAlternativeDnsNames())); + certificate.setCertificateExtension(List.of(certificateExtension)); } return certificate; } @@ -123,7 +113,6 @@ public class CertificateCreationService { try { String fingerprint = openSSLService.getCertificateFingerprint(certificate); Certificate entity = createEntityFromInfo(openSSLService.getCertificateInfo(certificate)); - entity.setRequestedKeyLength(-1); entity.setFingerprint(fingerprint); entity.setCert(Files.readAllBytes(certificate)); if (keyFile != null) { diff --git a/src/test/java/de/mlessmann/certassist/TestOpenSSLService.java b/src/test/java/de/mlessmann/certassist/TestOpenSSLService.java index cc6c4ea..c25780f 100644 --- a/src/test/java/de/mlessmann/certassist/TestOpenSSLService.java +++ b/src/test/java/de/mlessmann/certassist/TestOpenSSLService.java @@ -1,9 +1,5 @@ package de.mlessmann.certassist; -import static java.util.Objects.requireNonNull; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; - import de.mlessmann.certassist.models.CertificateInfo; import de.mlessmann.certassist.models.CertificateInfo.RequestType; import de.mlessmann.certassist.models.CertificateInfoExtension; @@ -12,19 +8,24 @@ import de.mlessmann.certassist.openssl.CertificatePasswordProvider; import de.mlessmann.certassist.openssl.CertificateProvider; import de.mlessmann.certassist.openssl.OpenSSLService; import de.mlessmann.certassist.service.ExecutableResolver; -import java.nio.file.Path; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; +import java.nio.file.Path; + +import static java.util.Objects.requireNonNull; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + @SpringBootTest class TestOpenSSLService { public static final String TEST_CERT_PASSPHRASE = "ABC-123"; public static final Path TEST_CERT_PATH = Path.of("src/test/resources/openssl"); public static final String TEST_CERT_FINGERPRINT = - "SHA1;4E:D6:0A:47:F0:63:AD:96:26:83:16:28:32:F5:E8:36:5A:62:91:95"; + "SHA1;4E:D6:0A:47:F0:63:AD:96:26:83:16:28:32:F5:E8:36:5A:62:91:95"; private static final String ERR_NOT_ENCRYPTED = "Private key not encrypted"; private static final String ERR_VERIFY_FAILED = "Certificate verification failed"; @@ -44,55 +45,57 @@ class TestOpenSSLService { var certificateCreator = new OpenSSLService(executableResolver, passwordProvider, certificateProvider); CertificateInfo certRequest = CertificateInfo - .builder() - .type(RequestType.STANDALONE_CERTIFICATE) - .subject( - CertificateInfoSubject - .builder() - .commonName("test.home") - .country("DE") - .state("SH") - .locality("HH") - .organization("Crazy-Cats") - ) - .extension(CertificateInfoExtension.builder().alternativeDnsNames("test2.home", "test3.home")) - .build(); + .builder() + .type(RequestType.STANDALONE_CERTIFICATE) + .subject( + CertificateInfoSubject + .builder() + .commonName("test.home") + .country("DE") + .state("SH") + .locality("HH") + .organization("Crazy-Cats") + ) + .extension(CertificateInfoExtension.builder().alternativeDnsNames("test2.home", "test3.home")) + .build(); try (var cert = certificateCreator.createCertificate(certRequest)) { assertThat(certificateCreator.verifyCertificate(cert.certificatePath(), cert.certificatePath())) - .withFailMessage(ERR_VERIFY_FAILED) - .isTrue(); + .withFailMessage(ERR_VERIFY_FAILED) + .isTrue(); assertThat(certificateCreator.isKeyEncrypted(requireNonNull(cert.certificateKeyPath()))) - .withFailMessage(ERR_NOT_ENCRYPTED) - .isTrue(); + .withFailMessage(ERR_NOT_ENCRYPTED) + .isTrue(); CertificateInfo childRequest = CertificateInfo - .builder() - .type(RequestType.NORMAL_CERTIFICATE) - .trustingAuthority(cert.fingerprint()) - .subject( - CertificateInfoSubject - .builder() - .commonName("test.local") - .country("DE") - .state("SH") - .locality("HH") - .organization("Crazy-Cats") - ) - .extension(CertificateInfoExtension.builder().alternativeDnsNames("test2.local", "test3.local")) - .build(); + .builder() + .type(RequestType.NORMAL_CERTIFICATE) + .trustingAuthority(cert.fingerprint()) + .subject( + CertificateInfoSubject + .builder() + .commonName("test.local") + .country("DE") + .state("SH") + .locality("HH") + .organization("Crazy-Cats") + ) + .extension(CertificateInfoExtension.builder().alternativeDnsNames("test2.local", "test3.local")) + .build(); var spiedCert = spy(cert); doNothing().when(spiedCert).close(); when(certificateProvider.requestCertificateUsage(cert.fingerprint())).thenReturn(spiedCert); try (var childCert = certificateCreator.createCertificate(childRequest)) { Path fullchain = childCert.fullchainPath(); - assertThat(certificateCreator.verifyCertificate(requireNonNull(fullchain), cert.certificatePath())) - .withFailMessage(ERR_VERIFY_FAILED) - .isTrue(); + assertThat( + certificateCreator.verifyCertificate(requireNonNull(fullchain), cert.certificatePath()) + ) + .withFailMessage(ERR_VERIFY_FAILED) + .isTrue(); assertThat(certificateCreator.isKeyEncrypted(requireNonNull(childCert.certificateKeyPath()))) - .withFailMessage(ERR_NOT_ENCRYPTED) - .isTrue(); + .withFailMessage(ERR_NOT_ENCRYPTED) + .isTrue(); } } } @@ -121,7 +124,6 @@ class TestOpenSSLService { assertThat(request.subject().getState()).isEqualTo("SH"); assertThat(request.subject().getLocality()).isEqualTo("HH"); assertThat(request.subject().getOrganization()).isEqualTo("Crazy-Cats"); - assertThat(request.extensions().getFirst().getAlternativeDnsNames()) - .containsExactly("test2.local", "test3.local"); + assertThat(request.extensions().getFirst().getAlternativeDnsNames()).containsExactly("test2.local", "test3.local"); } } diff --git a/src/test/java/de/mlessmann/certassist/repositories/CertificateRepositoryTest.java b/src/test/java/de/mlessmann/certassist/repositories/CertificateRepositoryTest.java index 1b53594..e142fa4 100644 --- a/src/test/java/de/mlessmann/certassist/repositories/CertificateRepositoryTest.java +++ b/src/test/java/de/mlessmann/certassist/repositories/CertificateRepositoryTest.java @@ -6,6 +6,7 @@ import de.mlessmann.certassist.models.Certificate; import de.mlessmann.certassist.models.CertificateExtension; import de.mlessmann.certassist.models.CertificateType; import jakarta.transaction.Transactional; + import java.time.OffsetDateTime; import java.util.List; import java.util.stream.StreamSupport;