Adding utility methods for generating x509 certificates for testing purposes.
Adding jacoco test coverage report plugin. Adding and improving tests for X509SvidValidator and CertificateUtils. Signed-off-by: Max Lambrecht <maxlambrecht@gmail.com>
This commit is contained in:
		
							parent
							
								
									ef4dbf86c5
								
							
						
					
					
						commit
						ef2cdafab9
					
				
							
								
								
									
										23
									
								
								build.gradle
								
								
								
								
							
							
						
						
									
										23
									
								
								build.gradle
								
								
								
								
							|  | @ -3,6 +3,7 @@ subprojects { | |||
|     version '0.6.0' | ||||
| 
 | ||||
|     apply plugin: 'java-library' | ||||
|     apply plugin: 'jacoco' | ||||
| 
 | ||||
|     sourceCompatibility = JavaVersion.VERSION_1_8 | ||||
|     targetCompatibility = JavaVersion.VERSION_1_8 | ||||
|  | @ -13,6 +14,28 @@ subprojects { | |||
| 
 | ||||
|     test { | ||||
|         useJUnitPlatform() | ||||
|         finalizedBy jacocoTestReport | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     jacocoTestReport { | ||||
|         dependsOn test // tests are required to run before generating the report | ||||
|         reports { | ||||
|             xml.enabled false | ||||
|             csv.enabled false | ||||
|             html.destination file("${buildDir}/jacocoHtml") | ||||
|         } | ||||
| 
 | ||||
|         afterEvaluate { | ||||
|             classDirectories.setFrom(files(classDirectories.files.collect { | ||||
|                 fileTree(dir: it, exclude: ['**/internal/**', '**/exception/**']) | ||||
|             })) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     jacoco { | ||||
|         toolVersion = "0.8.5" | ||||
|         reportsDir = file("$buildDir/customJacocoReportDir") | ||||
|     } | ||||
| 
 | ||||
|     dependencies { | ||||
|  |  | |||
|  | @ -56,5 +56,8 @@ dependencies { | |||
| 
 | ||||
|     // library for processing JWT tokens and JOSE JWK bundles | ||||
|     implementation group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: '5.7' | ||||
| 
 | ||||
|     // using bouncy castle for generating x509 certs for testing purposes | ||||
|     testImplementation group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.65' | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ import java.util.List; | |||
| import java.util.concurrent.ThreadLocalRandom; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import static java.util.Collections.EMPTY_LIST; | ||||
| import static org.apache.commons.lang3.StringUtils.startsWith; | ||||
| 
 | ||||
| /** | ||||
|  | @ -209,6 +210,9 @@ public class CertificateUtils { | |||
|     } | ||||
| 
 | ||||
|     private static List<String> getSpiffeIds(X509Certificate certificate) throws CertificateParsingException { | ||||
|         if (certificate.getSubjectAlternativeNames() == null) { | ||||
|             return EMPTY_LIST; | ||||
|         } | ||||
|         return certificate.getSubjectAlternativeNames() | ||||
|                 .stream() | ||||
|                 .map(san -> (String) san.get(SAN_VALUE_INDEX)) | ||||
|  |  | |||
|  | @ -3,19 +3,26 @@ package spiffe.internal; | |||
| import lombok.val; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import spiffe.spiffeid.SpiffeId; | ||||
| import spiffe.spiffeid.TrustDomain; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.net.URI; | ||||
| import java.net.URISyntaxException; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Paths; | ||||
| import java.security.InvalidKeyException; | ||||
| import java.security.NoSuchAlgorithmException; | ||||
| import java.security.PrivateKey; | ||||
| import java.security.cert.CertPathValidatorException; | ||||
| import java.security.cert.CertificateException; | ||||
| import java.security.cert.X509Certificate; | ||||
| import java.security.spec.InvalidKeySpecException; | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.junit.jupiter.api.Assertions.fail; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static spiffe.utils.X509CertificateTestUtils.createCertificate; | ||||
| import static spiffe.utils.X509CertificateTestUtils.createRootCA; | ||||
| 
 | ||||
| public class CertificateUtilsTest { | ||||
| 
 | ||||
|  | @ -24,7 +31,7 @@ public class CertificateUtilsTest { | |||
|         val path = Paths.get(toUri("testdata/internal/cert.pem")); | ||||
|         val certBytes = Files.readAllBytes(path); | ||||
| 
 | ||||
|         List<X509Certificate> x509CertificateList = null; | ||||
|         List<X509Certificate> x509CertificateList; | ||||
|         SpiffeId spiffeId = null; | ||||
|         try { | ||||
|             x509CertificateList = CertificateUtils.generateCertificates(certBytes); | ||||
|  | @ -55,7 +62,59 @@ public class CertificateUtilsTest { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void testGeneratePrivateKey() throws URISyntaxException, IOException { | ||||
|         val keyPath = Paths.get(toUri("testdata/internal/privateKeyRsa.pem")); | ||||
|         val keyBytes = Files.readAllBytes(keyPath); | ||||
| 
 | ||||
|         try { | ||||
|             PrivateKey privateKey = CertificateUtils.generatePrivateKey(keyBytes); | ||||
|             assertNotNull(privateKey); | ||||
|             assertEquals("RSA", privateKey.getAlgorithm()); | ||||
|         } catch (InvalidKeySpecException | InvalidKeyException | NoSuchAlgorithmException e) { | ||||
|             fail("Should have generated key", e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void testGetSpiffeId() throws Exception { | ||||
|         val rootCa = createRootCA("C = US, O = SPIFFE", "spiffe://domain.test" ); | ||||
|         val leaf = createCertificate("C = US, O = SPIRE", "C = US, O = SPIRE",  "spiffe://domain.test/workload", rootCa, false); | ||||
|         SpiffeId spiffeId = CertificateUtils.getSpiffeId(leaf.getCertificate()); | ||||
|         assertEquals(SpiffeId.parse("spiffe://domain.test/workload"), spiffeId); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void testGetSpiffeId_certNotContainSpiffeId_throwsCertificateException() throws Exception { | ||||
|         val rootCa = createRootCA("C = US, O = SPIFFE", "spiffe://domain.test" ); | ||||
|         val leaf = createCertificate("C = US, O = SPIRE", "C = US, O = SPIRE",  "", rootCa, false); | ||||
|         try { | ||||
|             CertificateUtils.getSpiffeId(leaf.getCertificate()); | ||||
|             fail("exception is expected"); | ||||
|         } catch (CertificateException e) { | ||||
|             assertEquals("Certificate does not contain SPIFFE ID in the URI SAN", e.getMessage()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void testGetTrustDomain() throws Exception { | ||||
|         val rootCa = createRootCA("C = US, O = SPIFFE", "spiffe://domain.test" ); | ||||
|         val intermediate = createCertificate("C = US, O = SPIRE", "C = US, O = SPIRE",  "spiffe://domain.test/host", rootCa, true); | ||||
|         val leaf = createCertificate("C = US, O = SPIRE", "C = US, O = SPIRE",  "spiffe://domain.test/workload", intermediate, false); | ||||
| 
 | ||||
|         val chain = Arrays.asList(leaf.getCertificate(), intermediate.getCertificate()); | ||||
| 
 | ||||
|         try { | ||||
|             TrustDomain trustDomain = CertificateUtils.getTrustDomain(chain); | ||||
|             assertNotNull(trustDomain); | ||||
|             assertEquals(TrustDomain.of("domain.test"), trustDomain); | ||||
|         } catch (CertificateException e) { | ||||
|             fail(e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private URI toUri(String path) throws URISyntaxException { | ||||
|         return getClass().getClassLoader().getResource(path).toURI(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -3,88 +3,79 @@ package spiffe.svid.x509svid; | |||
| import lombok.val; | ||||
| import org.junit.jupiter.api.BeforeEach; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.mockito.Mock; | ||||
| import org.mockito.MockitoAnnotations; | ||||
| import spiffe.bundle.x509bundle.X509Bundle; | ||||
| import spiffe.bundle.x509bundle.X509BundleSource; | ||||
| import spiffe.exception.BundleNotFoundException; | ||||
| import spiffe.internal.CertificateUtils; | ||||
| import spiffe.spiffeid.SpiffeId; | ||||
| import spiffe.spiffeid.TrustDomain; | ||||
| import spiffe.utils.X509CertificateTestUtils.CertAndKeyPair; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.net.URI; | ||||
| import java.net.URISyntaxException; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Paths; | ||||
| import java.security.cert.CertificateException; | ||||
| import java.security.cert.X509Certificate; | ||||
| import java.util.Arrays; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import static java.util.Collections.EMPTY_LIST; | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.junit.jupiter.api.Assertions.fail; | ||||
| import static org.mockito.Mockito.when; | ||||
| import static spiffe.utils.X509CertificateTestUtils.createCertificate; | ||||
| import static spiffe.utils.X509CertificateTestUtils.createRootCA; | ||||
| 
 | ||||
| public class X509SvidValidatorTest { | ||||
| 
 | ||||
|     @Mock | ||||
|     X509BundleSource bundleSourceMock; | ||||
|     List<X509Certificate> chain; | ||||
|     CertAndKeyPair rootCa; | ||||
|     CertAndKeyPair otherRootCa; | ||||
|     CertAndKeyPair leaf; | ||||
| 
 | ||||
|     @BeforeEach | ||||
|     void setup() { | ||||
|         MockitoAnnotations.initMocks(this); | ||||
|     void setUp() throws Exception { | ||||
|         rootCa = createRootCA("C = US, O = SPIFFE", "spiffe://example.org" ); | ||||
|         val intermediate1 = createCertificate("C = US, O = SPIRE", "C = US, O = SPIFFE",  "spiffe://example.org/host", rootCa, true); | ||||
|         val intermediate2 = createCertificate("C = US, O = SPIRE", "C = US, O = SPIRE",  "spiffe://example.org/host2", intermediate1, true); | ||||
|         leaf = createCertificate("C = US, O = SPIRE", "C = US, O = SPIRE",  "spiffe://example.org/test", intermediate2, false); | ||||
|         chain = Arrays.asList(leaf.getCertificate(), intermediate2.getCertificate(), intermediate1.getCertificate()); | ||||
|         otherRootCa = createRootCA("C = US, O = SPIFFE", "spiffe://example.org" ); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void verifyChain_certificateExpired_throwsCertificateException() throws IOException, CertificateException, BundleNotFoundException, URISyntaxException { | ||||
|         val certPath = Paths.get(toUri("testdata/x509svid/cert.pem")); | ||||
|         val certBytes = Files.readAllBytes(certPath); | ||||
|         val chain = CertificateUtils.generateCertificates(certBytes); | ||||
|     void testVerifyChain_chainCanBeVerifiedWithAuthorityInBundle() throws Exception { | ||||
|         HashSet<X509Certificate> x509Authorities = new HashSet<>(); | ||||
|         x509Authorities.add(rootCa.getCertificate()); | ||||
|         x509Authorities.add(otherRootCa.getCertificate()); | ||||
| 
 | ||||
|         val bundlePath = Paths.get(toUri("testdata/x509svid/bundle.pem")); | ||||
|         X509Bundle x509Bundle= | ||||
|                 X509Bundle.load( | ||||
|                         TrustDomain.of("example.org"), | ||||
|                         bundlePath | ||||
|                 ); | ||||
|         val x509Bundle = new X509Bundle(TrustDomain.of("example.org"), x509Authorities); | ||||
|         X509SvidValidator.verifyChain(chain, x509Bundle); | ||||
|     } | ||||
| 
 | ||||
|         when(bundleSourceMock | ||||
|                 .getX509BundleForTrustDomain( | ||||
|                         TrustDomain.of("example.org"))) | ||||
|                 .thenReturn(x509Bundle); | ||||
|     @Test | ||||
|     void testVerifyChain_chainCannotBeVerifiedWithAuthorityInBundle_throwsCertificateException() throws Exception { | ||||
|         HashSet<X509Certificate> x509Authorities = new HashSet<>(); | ||||
|         x509Authorities.add(otherRootCa.getCertificate()); | ||||
| 
 | ||||
|         val x509Bundle = new X509Bundle(TrustDomain.of("example.org"), x509Authorities); | ||||
|         try { | ||||
|             X509SvidValidator.verifyChain(chain, bundleSourceMock); | ||||
|             fail("Verify chain should have thrown validation exception"); | ||||
|             X509SvidValidator.verifyChain(chain, x509Bundle); | ||||
|             fail("exception is expected"); | ||||
|         } catch (CertificateException e) { | ||||
|             assertEquals("Cert chain cannot be verified", e.getMessage()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     void verifyChain_noBundleForTrustDomain_throwsBundleNotFoundException() throws IOException, CertificateException, BundleNotFoundException, URISyntaxException { | ||||
|         val certPath = Paths.get(toUri("testdata/x509svid/cert.pem")); | ||||
|         val certBytes = Files.readAllBytes(certPath); | ||||
|         val chain = CertificateUtils.generateCertificates(certBytes); | ||||
|     void verifyChain_noBundleForTrustDomain_throwsBundleNotFoundException() throws Exception { | ||||
|         HashSet<X509Certificate> x509Authorities = new HashSet<>(); | ||||
|         x509Authorities.add(otherRootCa.getCertificate()); | ||||
| 
 | ||||
|         val bundlePath = Paths.get(toUri("testdata/x509svid/bundle.pem")); | ||||
|         X509Bundle x509Bundle= | ||||
|                 X509Bundle.load( | ||||
|                         TrustDomain.of("example.org"), | ||||
|                         bundlePath | ||||
|                 ); | ||||
| 
 | ||||
|         when(bundleSourceMock | ||||
|                 .getX509BundleForTrustDomain( | ||||
|                         TrustDomain.of("example.org"))) | ||||
|                 .thenThrow(new BundleNotFoundException("No bundle found")); | ||||
|         val x509Bundle = new X509Bundle(TrustDomain.of("other.org"), x509Authorities); | ||||
| 
 | ||||
|         try { | ||||
|             X509SvidValidator.verifyChain(chain, bundleSourceMock); | ||||
|             X509SvidValidator.verifyChain(chain, x509Bundle); | ||||
|             fail("Verify chain should have thrown validation exception"); | ||||
|         } catch (BundleNotFoundException e) { | ||||
|             assertEquals("No bundle found", e.getMessage()); | ||||
|             assertEquals("No X509 bundle found for trust domain example.org", e.getMessage()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -93,13 +84,9 @@ public class X509SvidValidatorTest { | |||
|         val spiffeId1 = SpiffeId.parse("spiffe://example.org/test"); | ||||
|         val spiffeId2 = SpiffeId.parse("spiffe://example.org/test2"); | ||||
| 
 | ||||
|         val certPath = Paths.get(toUri("testdata/x509svid/cert.pem")); | ||||
|         val certBytes = Files.readAllBytes(certPath); | ||||
|         val x509Certificate = CertificateUtils.generateCertificates(certBytes); | ||||
| 
 | ||||
|         val spiffeIdList = Arrays.asList(spiffeId1, spiffeId2); | ||||
| 
 | ||||
|         X509SvidValidator.verifySpiffeId(x509Certificate.get(0), () -> spiffeIdList); | ||||
|         X509SvidValidator.verifySpiffeId(leaf.getCertificate(), () -> spiffeIdList); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|  | @ -108,12 +95,8 @@ public class X509SvidValidatorTest { | |||
|         val spiffeId2 = SpiffeId.parse("spiffe://example.org/other2"); | ||||
|         List<SpiffeId> spiffeIdList = Arrays.asList(spiffeId1, spiffeId2); | ||||
| 
 | ||||
|         val certPath = Paths.get(toUri("testdata/x509svid/cert.pem")); | ||||
|         val certBytes = Files.readAllBytes(certPath); | ||||
|         val x509Certificate = CertificateUtils.generateCertificates(certBytes); | ||||
| 
 | ||||
|         try { | ||||
|             X509SvidValidator.verifySpiffeId(x509Certificate.get(0), () -> spiffeIdList); | ||||
|             X509SvidValidator.verifySpiffeId(leaf.getCertificate(), () -> spiffeIdList); | ||||
|             fail("Should have thrown CertificateException"); | ||||
|         } catch (CertificateException e) { | ||||
|             assertEquals("SPIFFE ID spiffe://example.org/test in X.509 certificate is not accepted", e.getMessage()); | ||||
|  | @ -133,10 +116,7 @@ public class X509SvidValidatorTest { | |||
|     @Test | ||||
|     void checkSpiffeId_nullAcceptedSpiffeIdsSuppplier_throwsNullPointerException() throws CertificateException, URISyntaxException, IOException { | ||||
|         try { | ||||
|             val certPath = Paths.get(toUri("testdata/x509svid/cert.pem")); | ||||
|             val certBytes = Files.readAllBytes(certPath); | ||||
|             val x509Certificate = CertificateUtils.generateCertificates(certBytes); | ||||
|             X509SvidValidator.verifySpiffeId(x509Certificate.get(0), null); | ||||
|             X509SvidValidator.verifySpiffeId(leaf.getCertificate(), null); | ||||
|             fail("should have thrown an exception"); | ||||
|         } catch (NullPointerException e) { | ||||
|             assertEquals("acceptedSpiffedIdsSupplier is marked non-null but is null", e.getMessage()); | ||||
|  | @ -146,7 +126,7 @@ public class X509SvidValidatorTest { | |||
|     @Test | ||||
|     void verifyChain_nullChain_throwsNullPointerException() throws CertificateException, BundleNotFoundException { | ||||
|         try { | ||||
|             X509SvidValidator.verifyChain(null, bundleSourceMock); | ||||
|             X509SvidValidator.verifyChain(null, new X509Bundle(TrustDomain.of("example.org"))); | ||||
|             fail("should have thrown an exception"); | ||||
|         } catch (NullPointerException e) { | ||||
|             assertEquals("chain is marked non-null but is null", e.getMessage()); | ||||
|  | @ -156,17 +136,10 @@ public class X509SvidValidatorTest { | |||
|     @Test | ||||
|     void verifyChain_nullBundleSource_throwsNullPointerException() throws CertificateException, BundleNotFoundException, URISyntaxException, IOException { | ||||
|         try { | ||||
|             val certPath = Paths.get(toUri("testdata/x509svid/cert.pem")); | ||||
|             val certBytes = Files.readAllBytes(certPath); | ||||
|             val chain = CertificateUtils.generateCertificates(certBytes); | ||||
|             X509SvidValidator.verifyChain(chain, null); | ||||
|             fail("should have thrown an exception"); | ||||
|         } catch (NullPointerException e) { | ||||
|             assertEquals("x509BundleSource is marked non-null but is null", e.getMessage()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private URI toUri(String path) throws URISyntaxException { | ||||
|         return getClass().getClassLoader().getResource(path).toURI(); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,125 @@ | |||
| package spiffe.utils; | ||||
| 
 | ||||
| import lombok.Value; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.bouncycastle.asn1.ASN1EncodableVector; | ||||
| import org.bouncycastle.asn1.DERSequence; | ||||
| import org.bouncycastle.asn1.x500.X500Name; | ||||
| import org.bouncycastle.asn1.x509.*; | ||||
| import org.bouncycastle.cert.CertIOException; | ||||
| import org.bouncycastle.cert.X509CertificateHolder; | ||||
| import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; | ||||
| import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; | ||||
| import org.bouncycastle.operator.ContentSigner; | ||||
| import org.bouncycastle.operator.OperatorCreationException; | ||||
| import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; | ||||
| 
 | ||||
| import java.math.BigInteger; | ||||
| import java.security.KeyPair; | ||||
| import java.security.KeyPairGenerator; | ||||
| import java.security.NoSuchAlgorithmException; | ||||
| import java.security.PrivateKey; | ||||
| import java.security.cert.CertificateException; | ||||
| import java.security.cert.X509Certificate; | ||||
| import java.time.Instant; | ||||
| import java.time.temporal.ChronoUnit; | ||||
| import java.util.Date; | ||||
| 
 | ||||
| public class X509CertificateTestUtils { | ||||
| 
 | ||||
|     /** | ||||
|      * Creates a self-signed Root CA certificate | ||||
|      */ | ||||
|     public static CertAndKeyPair createRootCA(String subject, String spiffeId) throws Exception { | ||||
|         KeyPair certKeyPair = generateKeyPair(); | ||||
|         JcaX509v3CertificateBuilder builder = getCertificateBuilder(certKeyPair, subject, subject); | ||||
|         addCAExtensions(builder, certKeyPair, spiffeId); | ||||
| 
 | ||||
|         // self signed | ||||
|         X509Certificate cert = getSignedX509Certificate(certKeyPair.getPrivate(), builder); | ||||
|         return new CertAndKeyPair(cert, certKeyPair); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Creates a certificate signed with the private key of the issuer. The generated cert can be an intermediate CA or | ||||
|      * a leaf certificate. | ||||
|      */ | ||||
|     public static CertAndKeyPair createCertificate(String subject, String issuerSubject, String spiffeId, CertAndKeyPair issuer, boolean isCa) throws Exception { | ||||
|         KeyPair certKeyPair = generateKeyPair(); | ||||
|         PrivateKey issuerKey = issuer.keyPair.getPrivate(); | ||||
|         JcaX509v3CertificateBuilder builder = getCertificateBuilder(certKeyPair, subject, issuerSubject); | ||||
|         addCertExtensions(builder, spiffeId, isCa); | ||||
|         X509Certificate cert = getSignedX509Certificate(issuerKey, builder); | ||||
|         return new CertAndKeyPair(cert, certKeyPair); | ||||
|     } | ||||
| 
 | ||||
|     @Value | ||||
|     public final static class CertAndKeyPair { | ||||
|         private final KeyPair keyPair; | ||||
|         private final X509Certificate certificate; | ||||
| 
 | ||||
|         public CertAndKeyPair(X509Certificate certificate, KeyPair keyPair) { | ||||
|             this.keyPair = keyPair; | ||||
|             this.certificate = certificate; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static KeyPair generateKeyPair() throws NoSuchAlgorithmException { | ||||
|         KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); | ||||
|         return keyGen.generateKeyPair(); | ||||
|     } | ||||
| 
 | ||||
|     private static void addCertExtensions(JcaX509v3CertificateBuilder builder, String spiffeId, boolean isCa) throws CertIOException { | ||||
|         builder.addExtension(Extension.basicConstraints, true, new BasicConstraints(isCa)); | ||||
| 
 | ||||
|         if (isCa) { | ||||
|             KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.digitalSignature | KeyUsage.cRLSign); | ||||
|             builder.addExtension(Extension.keyUsage, true, usage); | ||||
|         } else { | ||||
|             KeyUsage usage = new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.keyAgreement); | ||||
|             builder.addExtension(Extension.keyUsage, true, usage); | ||||
| 
 | ||||
|             ASN1EncodableVector purposes = new ASN1EncodableVector(); | ||||
|             purposes.add(KeyPurposeId.id_kp_serverAuth); | ||||
|             purposes.add(KeyPurposeId.id_kp_clientAuth); | ||||
|             builder.addExtension(Extension.extendedKeyUsage, false, new DERSequence(purposes)); | ||||
|         } | ||||
| 
 | ||||
|         if (StringUtils.isNotBlank(spiffeId)) { | ||||
|             builder.addExtension(Extension.subjectAlternativeName, false, | ||||
|                     new GeneralNames(new GeneralName(GeneralName.uniformResourceIdentifier, spiffeId ))); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static void addCAExtensions(JcaX509v3CertificateBuilder builder, KeyPair certKeyPair, String spiffeId) throws CertIOException { | ||||
|         builder.addExtension(Extension.basicConstraints, true, new BasicConstraints(true)); | ||||
|         KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.digitalSignature | KeyUsage.cRLSign); | ||||
|         builder.addExtension(Extension.keyUsage, true, usage); | ||||
| 
 | ||||
|         builder.addExtension(Extension.subjectAlternativeName, true, | ||||
|                 new GeneralNames(new GeneralName(GeneralName.uniformResourceIdentifier, spiffeId))); | ||||
| 
 | ||||
|         builder.addExtension(Extension.subjectKeyIdentifier, false, new SubjectKeyIdentifier(certKeyPair.getPublic().getEncoded())); | ||||
|     } | ||||
| 
 | ||||
|     private static JcaX509v3CertificateBuilder getCertificateBuilder(KeyPair certKeyPair, String subject, String issuerSubject) { | ||||
|         BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis()); | ||||
|         Instant validFrom = Instant.now().minus(5, ChronoUnit.DAYS); | ||||
|         Instant validUntil = validFrom.plus(30 , ChronoUnit.DAYS); | ||||
|         X500Name name = new X500Name(subject); | ||||
|         X500Name issuerName = new X500Name(issuerSubject); | ||||
|         return new JcaX509v3CertificateBuilder( | ||||
|                 issuerName, | ||||
|                 serialNumber, | ||||
|                 Date.from(validFrom), Date.from(validUntil), | ||||
|                 name, certKeyPair.getPublic()); | ||||
|     } | ||||
| 
 | ||||
|     private static X509Certificate getSignedX509Certificate(PrivateKey issuerKey, JcaX509v3CertificateBuilder builder) throws OperatorCreationException, CertificateException { | ||||
|         ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSA").build(issuerKey); | ||||
|         X509CertificateHolder certHolder = builder.build(signer); | ||||
|         return new JcaX509CertificateConverter().getCertificate(certHolder); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -0,0 +1,13 @@ | |||
| -----BEGIN PRIVATE KEY----- | ||||
| MIIB5QIBADANBgkqhkiG9w0BAQEFAASCAc8wggHLAgEAAmEA0EdYWa7LZ/ZA2sKk | ||||
| JsREA2TkXDIq01JIRsVetPFRqRW5GEmkwwk0qThebp/ofZt8oLkMwp06BLaQC3tH | ||||
| QGkK31jh5Djd1NHA8CpU4ByObgGeY75dhwFEzI4YwXM+e0n/AgMBAAECYFD4S4qh | ||||
| /4WtIE1refFwP5iqMnT9M9TvmhWZSVZCsqJvRYQBrUH9ZDGdLmkHVZTvSvKKmkoZ | ||||
| VvXDlpmW4Eaed8xXqsLYplMrVo6WkvdtvlvfIwP69PGFmWwKgFBe2aLHsQIxAOoT | ||||
| dwmlr/dNNu2MjyjcvTK0lCn6vexp6k8MaXTEsTvG0kBmVDZGuSXcKzbBoAZLxQIx | ||||
| AOPJU65HnDpcOM+qLH3jahTnbrg4C0BO0mj1OusLcSUnA6bFP2NkZ9LyWfMerbvG | ||||
| 8wIxAI7Iyt8mo50+C5iCGj250OtiPdMRsdLJlPUdRCLHbLljAZPpF8t3/q66i929 | ||||
| 5MiSZQIwE3wXQmMxw/Q7j9f4slQPsPYTDIMOw1N6wCup/I0gApORxmQ9Bd2C3BKL | ||||
| CzbmmZdtAjEA2v1fSN4DPcQW2bgmoE0GoNEMYGfSza7jBGOiKkqm4p2hAjaur174 | ||||
| U2t9BPJHk+Xh | ||||
| -----END PRIVATE KEY----- | ||||
|  | @ -1,12 +0,0 @@ | |||
| -----BEGIN CERTIFICATE----- | ||||
| MIIBzDCCAVGgAwIBAgIBADAKBggqhkjOPQQDAzAeMQswCQYDVQQGEwJVUzEPMA0G | ||||
| A1UEChMGU1BJRkZFMB4XDTIwMDMxNjE4MDQ1N1oXDTIwMDMyMzE4MDUwN1owHjEL | ||||
| MAkGA1UEBhMCVVMxDzANBgNVBAoTBlNQSUZGRTB2MBAGByqGSM49AgEGBSuBBAAi | ||||
| A2IABOStlk5qbbCuuRqKitTqTzQf/UnlljO2bHAekgz+0na4oJhcsOYhRLbKQVcr | ||||
| U5jf7BpcT7nYHyLMv79Cy+Xa1snzMCGhx1obgt8gAXb4b4GwyAiNzaq7ytX4SAZB | ||||
| CUXp36NjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O | ||||
| BBYEFCMzt4WGhen9N2N+MwzUkwEtG6YLMB8GA1UdEQQYMBaGFHNwaWZmZTovL2V4 | ||||
| YW1wbGUub3JnMAoGCCqGSM49BAMDA2kAMGYCMQDJSIl+8jQek7tHRSfU2vYLQ6sy | ||||
| 2t4NJcc5zeSjNxFIajUogl7L8T1QNzrKpXm/ZbQCMQDU54VnA1Awq0Qq2pf4zQ1C | ||||
| KOpj4PguMpUmulRVrzD2KNQz+FUKsHgL/mIZSfN5g1M= | ||||
| -----END CERTIFICATE----- | ||||
|  | @ -1,13 +0,0 @@ | |||
| -----BEGIN CERTIFICATE----- | ||||
| MIICADCCAYagAwIBAgIQJqqLnXOR3tjUV43PpGW4YDAKBggqhkjOPQQDAzAeMQsw | ||||
| CQYDVQQGEwJVUzEPMA0GA1UEChMGU1BJRkZFMB4XDTIwMDMxODE5MDc1MloXDTIw | ||||
| MDMyMzE4MDUwN1owHTELMAkGA1UEBhMCVVMxDjAMBgNVBAoTBVNQSVJFMFkwEwYH | ||||
| KoZIzj0CAQYIKoZIzj0DAQcDQgAE4/NA2umeK9j1U5+egPqfzomjnpnLz68jNvUN | ||||
| tdA0Lg6E7/nsmvqoNbVVbaD84Jplfg6/6HWSnoO7K7A+oZ1g+qOBpjCBozAOBgNV | ||||
| HQ8BAf8EBAMCA6gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1Ud | ||||
| EwEB/wQCMAAwHQYDVR0OBBYEFPLf59AkFCva6ehKx8L4i+pjCU1CMB8GA1UdIwQY | ||||
| MBaAFCMzt4WGhen9N2N+MwzUkwEtG6YLMCQGA1UdEQQdMBuGGXNwaWZmZTovL2V4 | ||||
| YW1wbGUub3JnL3Rlc3QwCgYIKoZIzj0EAwMDaAAwZQIwRzrN6Rh8X28UYJEuql/1 | ||||
| GZEeto7zzj0UtjZFwQy2ODl48nFFRGKUnq8mc4cIMI/kAjEAlixJBUHqb4ty8Ff+ | ||||
| d0XHzA5duE1hzFxd2feRppjqiOHKJu7Rh2dzZd3rZhMZWrrd | ||||
| -----END CERTIFICATE----- | ||||
|  | @ -0,0 +1 @@ | |||
| lombok.addLombokGeneratedAnnotation = true | ||||
		Loading…
	
		Reference in New Issue