From 5fea9179c474e775ae94c27e99b179941827afa9 Mon Sep 17 00:00:00 2001 From: Moncef Abboud Date: Fri, 4 Dec 2020 11:33:56 +0100 Subject: [PATCH] NIFI-7783 Add CA Common Name as DNS Subject Alternative Name This closes #4709 Signed-off-by: David Handermann --- .../nifi/security/util/CertificateUtils.java | 23 +++++++++++++++++++ .../security/util/CertificateUtilsTest.groovy | 15 ++++++++++++ 2 files changed, 38 insertions(+) diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java index d3383ec030..0e4d387ec5 100644 --- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java @@ -56,6 +56,7 @@ import org.bouncycastle.asn1.x500.AttributeTypeAndValue; import org.bouncycastle.asn1.x500.RDN; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.style.BCStyle; +import org.bouncycastle.asn1.x500.style.IETFUtils; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.ExtendedKeyUsage; import org.bouncycastle.asn1.x509.Extension; @@ -63,6 +64,8 @@ import org.bouncycastle.asn1.x509.Extensions; import org.bouncycastle.asn1.x509.KeyPurposeId; import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.cert.CertIOException; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v3CertificateBuilder; @@ -469,6 +472,12 @@ public final class CertificateUtils { // (2) extendedKeyUsage extension certBuilder.addExtension(Extension.extendedKeyUsage, false, new ExtendedKeyUsage(new KeyPurposeId[]{KeyPurposeId.id_kp_clientAuth, KeyPurposeId.id_kp_serverAuth})); + // (3) subjectAlternativeName extension. Include CN as a SAN entry if it exists. + final String cn = getCommonName(dn); + if (StringUtils.isNotBlank(cn)) { + certBuilder.addExtension(Extension.subjectAlternativeName, false, new GeneralNames(new GeneralName(GeneralName.dNSName, cn))); + } + // Sign the certificate X509CertificateHolder certificateHolder = certBuilder.build(sigGen); return new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getCertificate(certificateHolder); @@ -625,6 +634,20 @@ public final class CertificateUtils { } } + /** + * Extracts the common name from the given DN. + * + * @param dn the distinguished name to evaluate + * @return the common name if it exists, null otherwise. + */ + public static String getCommonName(final String dn) { + RDN[] rdns = new X500Name(dn).getRDNs(BCStyle.CN); + if (rdns.length == 0) { + return null; + } + return IETFUtils.valueToString(rdns[0].getFirst().getValue()); + } + private CertificateUtils() { } } diff --git a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/CertificateUtilsTest.groovy b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/CertificateUtilsTest.groovy index f9fa704eaa..03ab118cc2 100644 --- a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/CertificateUtilsTest.groovy +++ b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/CertificateUtilsTest.groovy @@ -432,6 +432,15 @@ class CertificateUtilsTest extends GroovyTestCase { assert !dn1MatchesEmpty } + @Test + void testGetCommonName(){ + String dn1 = "CN=testDN,O=testOrg" + String dn2 = "O=testDN,O=testOrg" + + assertEquals("testDN", CertificateUtils.getCommonName(dn1)) + assertNull(CertificateUtils.getCommonName(dn2)) + } + @Test void testShouldGenerateSelfSignedCert() throws Exception { String dn = "CN=testDN,O=testOrg" @@ -451,6 +460,12 @@ class CertificateUtilsTest extends GroovyTestCase { assertEquals(SIGNATURE_ALGORITHM.toUpperCase(), x509Certificate.getSigAlgName().toUpperCase()) assertEquals("RSA", x509Certificate.getPublicKey().getAlgorithm()) + assertEquals(1, x509Certificate.getSubjectAlternativeNames().size()) + + GeneralName gn = x509Certificate.getSubjectAlternativeNames().iterator().next() + assertEquals(GeneralName.dNSName, gn.getTagNo()) + assertEquals("testDN", gn.getName().toString()) + x509Certificate.checkValidity() }