diff --git a/changelog/unreleased/issue-20738.toml b/changelog/unreleased/issue-20738.toml new file mode 100644 index 000000000000..06049f45e786 --- /dev/null +++ b/changelog/unreleased/issue-20738.toml @@ -0,0 +1,5 @@ +type = "fixed" +message = "Fix encoding of CName in generated CA certificates" + +issues = ["20738"] +pulls = ["20745"] diff --git a/graylog2-server/src/main/java/org/graylog/security/certutil/CertificateGenerator.java b/graylog2-server/src/main/java/org/graylog/security/certutil/CertificateGenerator.java index f20e7a9e9529..70c04fec2690 100644 --- a/graylog2-server/src/main/java/org/graylog/security/certutil/CertificateGenerator.java +++ b/graylog2-server/src/main/java/org/graylog/security/certutil/CertificateGenerator.java @@ -17,6 +17,8 @@ package org.graylog.security.certutil; import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x500.X500NameBuilder; +import org.bouncycastle.asn1.x500.style.BCStyle; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.GeneralName; @@ -41,7 +43,8 @@ public class CertificateGenerator { public static KeyPair generate(CertRequest request) throws Exception { KeyPairGenerator keyGen = KeyPairGenerator.getInstance(KEY_GENERATION_ALGORITHM); java.security.KeyPair certKeyPair = keyGen.generateKeyPair(); - X500Name name = new X500Name("CN=" + request.cnName()); + + final X500Name name = getX500Name(request.cnName()); // TODO: cert serial number? BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis()); @@ -84,4 +87,10 @@ public static KeyPair generate(CertRequest request) throws Exception { X509Certificate cert = new JcaX509CertificateConverter().getCertificate(certHolder); return new KeyPair(certKeyPair.getPrivate(), certKeyPair.getPublic(), cert); } + + private static X500Name getX500Name(String cname) { + X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE); + builder.addRDN(BCStyle.CN, cname); + return builder.build(); + } } diff --git a/graylog2-server/src/test/java/org/graylog/security/certutil/CertificateGeneratorTest.java b/graylog2-server/src/test/java/org/graylog/security/certutil/CertificateGeneratorTest.java new file mode 100644 index 000000000000..4685b4d4d748 --- /dev/null +++ b/graylog2-server/src/test/java/org/graylog/security/certutil/CertificateGeneratorTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ +package org.graylog.security.certutil; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.security.cert.X509Certificate; +import java.time.Duration; + +class CertificateGeneratorTest { + + @Test + void testDomainName() throws Exception { + final KeyPair pair = selfSigned("www.graylog.org"); + final X509Certificate certificate = pair.certificate(); + final String cn = certificate.getSubjectX500Principal().getName(); + Assertions.assertThat(cn).isEqualTo("CN=www.graylog.org"); + } + + @Test + void testEscaping() throws Exception { + final KeyPair pair = selfSigned("Graylog, Inc."); + final X509Certificate certificate = pair.certificate(); + final String cn = certificate.getSubjectX500Principal().getName(); + Assertions.assertThat(cn).isEqualTo("CN=Graylog\\, Inc."); + } + + private static KeyPair selfSigned(String cname) throws Exception { + final CertRequest req = CertRequest.selfSigned(cname).validity(Duration.ofDays(1)); + return CertificateGenerator.generate(req); + } +}