Add new writer for certificate chains

pull/30/head
Richard Körber 2016-11-18 21:24:46 +01:00
parent ef42e04793
commit 7c033095e6
5 changed files with 87 additions and 20 deletions

View File

@ -50,6 +50,7 @@ public class ClientTest {
private static final File DOMAIN_KEY_FILE = new File("domain.key");
private static final File DOMAIN_CERT_FILE = new File("domain.crt");
private static final File CERT_CHAIN_FILE = new File("chain.crt");
private static final File DOMAIN_CHAIN_FILE = new File("domain-chain.crt");
private static final File DOMAIN_CSR_FILE = new File("domain.csr");
private static final int KEY_SIZE = 2048;
@ -181,14 +182,21 @@ public class ClientTest {
// Download the certificate
X509Certificate cert = certificate.download();
X509Certificate[] chain = certificate.downloadChain();
// Write certificate only (e.g. for Apache's SSLCertificateFile)
try (FileWriter fw = new FileWriter(DOMAIN_CERT_FILE)) {
CertificateUtils.writeX509Certificate(cert, fw);
}
// Download the certificate chain
X509Certificate[] chain = certificate.downloadChain();
// Write chain only (e.g. for Apache's SSLCertificateChainFile)
try (FileWriter fw = new FileWriter(CERT_CHAIN_FILE)) {
CertificateUtils.writeX509CertificateChain(chain, fw);
CertificateUtils.writeX509CertificateChain(fw, null, chain);
}
// Write combined certificate and chain (e.g. for nginx)
try (FileWriter fw = new FileWriter(DOMAIN_CHAIN_FILE)) {
CertificateUtils.writeX509CertificateChain(fw, cert, chain);
}
// Revoke the certificate (uncomment if needed...)

View File

@ -44,7 +44,7 @@ import org.bouncycastle.util.io.pem.PemWriter;
/**
* Generator for a CSR (Certificate Signing Request) suitable for ACME servers.
* <p>
* Requires {@code Bouncy Castle}.
* Requires {@code Bouncy Castle}. This class is part of the {@code acme4j-utils} module.
*/
public class CSRBuilder {
private static final String SIGNATURE_ALG = "SHA256withRSA";
@ -199,7 +199,8 @@ public class CSRBuilder {
* Writes the signed certificate request to a {@link Writer}.
*
* @param w
* {@link Writer} to write the PEM file to
* {@link Writer} to write the PEM file to. The {@link Writer} is closed
* after use.
*/
public void write(Writer w) throws IOException {
if (csr == null) {
@ -215,7 +216,8 @@ public class CSRBuilder {
* Writes the signed certificate request to an {@link OutputStream}.
*
* @param out
* {@link OutputStream} to write the PEM file to
* {@link OutputStream} to write the PEM file to. The {@link OutputStream}
* is closed after use.
*/
public void write(OutputStream out) throws IOException {
write(new OutputStreamWriter(out, "utf-8"));

View File

@ -42,7 +42,7 @@ import org.shredzone.acme4j.challenge.TlsSni02Challenge;
/**
* Utility class offering convenience methods for certificates.
* <p>
* Requires {@code Bouncy Castle}.
* Requires {@code Bouncy Castle}. This class is part of the {@code acme4j-utils} module.
*/
public final class CertificateUtils {
@ -54,7 +54,8 @@ public final class CertificateUtils {
* Reads an {@link X509Certificate} PEM file from an {@link InputStream}.
*
* @param in
* {@link InputStream} to read the certificate from.
* {@link InputStream} to read the certificate from. The
* {@link InputStream} is closed after use.
* @return {@link X509Certificate} that was read
*/
public static X509Certificate readX509Certificate(InputStream in) throws IOException {
@ -72,7 +73,8 @@ public final class CertificateUtils {
* @param cert
* {@link X509Certificate} to write
* @param out
* {@link OutputStream} to write the PEM file to
* {@link OutputStream} to write the PEM file to. The {@link OutputStream}
* is closed after use.
*/
public static void writeX509Certificate(X509Certificate cert, OutputStream out) throws IOException {
writeX509Certificate(cert, new OutputStreamWriter(out, "utf-8"));
@ -84,7 +86,8 @@ public final class CertificateUtils {
* @param cert
* {@link X509Certificate} to write
* @param w
* {@link Writer} to write the PEM file to
* {@link Writer} to write the PEM file to. The {@link Writer} is closed
* after use.
*/
public static void writeX509Certificate(X509Certificate cert, Writer w) throws IOException {
try (JcaPEMWriter jw = new JcaPEMWriter(w)) {
@ -92,27 +95,56 @@ public final class CertificateUtils {
}
}
/**
* Writes a X.509 certificate chain to a PEM file.
*
* @param w
* {@link Writer} to write the certificate chain to. The {@link Writer} is
* closed after use.
* @param cert
* {@link X509Certificate} to write, {@code null} to skip this certificate
* @param chain
* {@link X509Certificate} chain to add to the certificate. {@code null}
* values are ignored, array may be empty.
*/
public static void writeX509CertificateChain(Writer w, X509Certificate cert, X509Certificate... chain)
throws IOException {
try (JcaPEMWriter jw = new JcaPEMWriter(w)) {
if (cert != null) {
jw.writeObject(cert);
}
if (chain != null) {
for (X509Certificate c : chain) {
if (c != null) {
jw.writeObject(c);
}
}
}
}
}
/**
* Writes an X.509 certificate chain PEM file.
*
* @param chain
* {@link X509Certificate[]} to write
* @param w
* {@link Writer} to write the PEM file to
* {@link Writer} to write the PEM file to. The {@link Writer} is closed
* after use.
* @deprecated Use
* {@link #writeX509CertificateChain(Writer, X509Certificate, X509Certificate...)}
*/
@Deprecated
public static void writeX509CertificateChain(X509Certificate[] chain, Writer w) throws IOException {
try (JcaPEMWriter jw = new JcaPEMWriter(w)) {
for (X509Certificate cert : chain) {
jw.writeObject(cert);
}
}
writeX509CertificateChain(w, null, chain);
}
/**
* Reads a CSR PEM file.
*
* @param in
* {@link InputStream} to read the CSR from.
* {@link InputStream} to read the CSR from. The {@link InputStream} is
* closed after use.
* @return CSR that was read
*/
public static PKCS10CertificationRequest readCSR(InputStream in) throws IOException {

View File

@ -34,7 +34,7 @@ import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
/**
* Utility class offering convenience methods for {@link KeyPair}.
* <p>
* Requires {@code Bouncy Castle}.
* Requires {@code Bouncy Castle}. This class is part of the {@code acme4j-utils} module.
*/
public class KeyPairUtils {
@ -83,7 +83,8 @@ public class KeyPairUtils {
* Reads a {@link KeyPair} from a PEM file.
*
* @param r
* {@link Reader} to read the PEM file from
* {@link Reader} to read the PEM file from. The {@link Reader} is closed
* after use.
* @return {@link KeyPair} read
*/
public static KeyPair readKeyPair(Reader r) throws IOException {
@ -101,7 +102,8 @@ public class KeyPairUtils {
* @param keypair
* {@link KeyPair} to write
* @param w
* {@link Writer} to write the PEM file to
* {@link Writer} to write the PEM file to. The {@link Writer} is closed
* after use.
*/
public static void writeKeyPair(KeyPair keypair, Writer w) throws IOException {
try (JcaPEMWriter jw = new JcaPEMWriter(w)) {

View File

@ -54,6 +54,29 @@ URI locationUri = ... // location URI from cert.getLocation()
Certificate cert = Certificate.bind(session, locationUri);
```
### Saving Certificates
Most web servers, like _Apache_, _nginx_, but also other servers like _postfix_ or _dovecot_, need a combined certificate file that contains the leaf certificate itself, and the certificate chain up to the root certificate. `acme4j-utils` offers a method that helps to write the necessary file:
```java
try (FileWriter fw = new FileWriter("cert-chain.crt")) {
CertificateUtils.writeX509CertificateChain(fw, cert, chain);
}
```
Some older servers may need the leaf certificate and the certificate chain in different files. Use this snippet to write both files:
```java
try (FileWriter fw = new FileWriter("cert.pem")) {
CertificateUtils.writeX509Certificate(cert, fw);
}
try (FileWriter fw = new FileWriter("chain.pem")) {
CertificateUtils.writeX509CertificateChain(fw, null, chain);
}
```
These utility methods should be sufficient for most use cases. If you need the certificate written in a different format, see the [source code of `CertificateUtils`](https://github.com/shred/acme4j/blob/master/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java) to find out how certificates are written using _Bouncy Castle_.
### Multiple Domains
The example above generates a certificate per domain. However, you would usually prefer to use a single certificate for multiple domains (for example, the domain itself and the `www.` subdomain).