diff --git a/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java b/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java index ec842ba7..8f6d194c 100644 --- a/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java +++ b/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java @@ -16,6 +16,7 @@ package org.shredzone.acme4j.util; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; @@ -31,9 +32,11 @@ import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.openssl.jcajce.JcaPEMWriter; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.shredzone.acme4j.challenge.TlsSni01Challenge; /** @@ -91,6 +94,23 @@ public final class CertificateUtils { } } + /** + * Reads a CSR PEM file. + * + * @param in + * {@link InputStream} to read the CSR from. + * @return CSR that was read + */ + public static PKCS10CertificationRequest readCSR(InputStream in) throws IOException { + try (PEMParser pemParser = new PEMParser(new InputStreamReader(in))) { + Object parsedObj = pemParser.readObject(); + if (!(parsedObj instanceof PKCS10CertificationRequest)) { + throw new IOException("Not a PKCS10 CSR"); + } + return (PKCS10CertificationRequest) parsedObj; + } + } + /** * Creates a self-signed {@link X509Certificate} that can be used for * {@link TlsSni01Challenge}. The certificate is valid for 7 days. diff --git a/acme4j-utils/src/test/java/org/shredzone/acme4j/util/CertificateUtilsTest.java b/acme4j-utils/src/test/java/org/shredzone/acme4j/util/CertificateUtilsTest.java index 13502b8f..d2acfb63 100644 --- a/acme4j-utils/src/test/java/org/shredzone/acme4j/util/CertificateUtilsTest.java +++ b/acme4j-utils/src/test/java/org/shredzone/acme4j/util/CertificateUtilsTest.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.Set; import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.junit.Before; import org.junit.Test; @@ -107,6 +108,30 @@ public class CertificateUtilsTest { assertThat(getSANs(cert), containsInAnyOrder(subject)); } + /** + * Test if {@link CertificateUtils#readCSR(InputStream)} reads an identical CSR. + */ + @Test + public void testReadCSR() throws IOException { + KeyPair keypair = KeyPairUtils.createKeyPair(2048); + + CSRBuilder builder = new CSRBuilder(); + builder.addDomains("example.com", "example.org"); + builder.sign(keypair); + + PKCS10CertificationRequest original = builder.getCSR(); + byte[] pemFile; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + builder.write(baos); + pemFile = baos.toByteArray(); + } + + try (ByteArrayInputStream bais = new ByteArrayInputStream(pemFile)) { + PKCS10CertificationRequest read = CertificateUtils.readCSR(bais); + assertThat(original.getEncoded(), is(equalTo(read.getEncoded()))); + } + } + /** * Extracts all DNSName SANs from a certificate. *