diff --git a/core-java-modules/core-java-security-4/README.md b/core-java-modules/core-java-security-4/README.md
new file mode 100644
index 0000000000..e850841d2b
--- /dev/null
+++ b/core-java-modules/core-java-security-4/README.md
@@ -0,0 +1,7 @@
+## Core Java Security
+
+This module contains articles about core Java Security
+
+### Relevant Articles:
+
+- More articles: [[<-- prev]](/core-java-modules/core-java-security-3)
diff --git a/core-java-modules/core-java-security-4/pom.xml b/core-java-modules/core-java-security-4/pom.xml
new file mode 100644
index 0000000000..cca86d804a
--- /dev/null
+++ b/core-java-modules/core-java-security-4/pom.xml
@@ -0,0 +1,16 @@
+
+
+ 4.0.0
+ core-java-security-4
+ core-java-security-4
+ jar
+
+
+ com.baeldung.core-java-modules
+ core-java-modules
+ 0.0.1-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/core-java-modules/core-java-security-4/src/main/java/com/baeldung/certificate/RootCertificateUtil.java b/core-java-modules/core-java-security-4/src/main/java/com/baeldung/certificate/RootCertificateUtil.java
new file mode 100644
index 0000000000..3e8445971f
--- /dev/null
+++ b/core-java-modules/core-java-security-4/src/main/java/com/baeldung/certificate/RootCertificateUtil.java
@@ -0,0 +1,51 @@
+package com.baeldung.certificate;
+
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
+
+public class RootCertificateUtil {
+
+ private RootCertificateUtil() {
+ }
+
+ public static X509Certificate getRootCertificate(X509Certificate endEntityCertificate, KeyStore trustStore)
+ throws Exception {
+ X509Certificate issuerCertificate = findIssuerCertificate(endEntityCertificate, trustStore);
+ if (issuerCertificate != null) {
+ if (isRoot(issuerCertificate)) {
+ return issuerCertificate;
+ } else {
+ return getRootCertificate(issuerCertificate, trustStore);
+ }
+ }
+ return null;
+ }
+
+ private static X509Certificate findIssuerCertificate(X509Certificate certificate, KeyStore trustStore)
+ throws KeyStoreException {
+ Enumeration aliases = trustStore.aliases();
+ while (aliases.hasMoreElements()) {
+ String alias = aliases.nextElement();
+ Certificate cert = trustStore.getCertificate(alias);
+ if (cert instanceof X509Certificate) {
+ X509Certificate x509Cert = (X509Certificate) cert;
+ if (x509Cert.getSubjectX500Principal().equals(certificate.getIssuerX500Principal())) {
+ return x509Cert;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static boolean isRoot(X509Certificate certificate) {
+ try {
+ certificate.verify(certificate.getPublicKey());
+ return certificate.getKeyUsage() != null && certificate.getKeyUsage()[5];
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
diff --git a/core-java-modules/core-java-security-4/src/main/resources/keystore.jks b/core-java-modules/core-java-security-4/src/main/resources/keystore.jks
new file mode 100644
index 0000000000..656c6c17d3
Binary files /dev/null and b/core-java-modules/core-java-security-4/src/main/resources/keystore.jks differ
diff --git a/core-java-modules/core-java-security-4/src/main/resources/truststore.jks b/core-java-modules/core-java-security-4/src/main/resources/truststore.jks
new file mode 100644
index 0000000000..5e1a9b4ce1
Binary files /dev/null and b/core-java-modules/core-java-security-4/src/main/resources/truststore.jks differ
diff --git a/core-java-modules/core-java-security-4/src/test/java/com/baeldung/certificate/SignedCertificateUnitTest.java b/core-java-modules/core-java-security-4/src/test/java/com/baeldung/certificate/SignedCertificateUnitTest.java
new file mode 100644
index 0000000000..f5ea3c1b87
--- /dev/null
+++ b/core-java-modules/core-java-security-4/src/test/java/com/baeldung/certificate/SignedCertificateUnitTest.java
@@ -0,0 +1,76 @@
+package com.baeldung.certificate;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.security.KeyStore;
+import java.security.SignatureException;
+import java.security.cert.X509Certificate;
+
+import static com.baeldung.certificate.RootCertificateUtil.getRootCertificate;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class SignedCertificateUnitTest {
+
+ private KeyStore keyStore;
+
+ private KeyStore trustStore;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ char[] passwd = "changeit".toCharArray();
+ keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(this.getClass().getClassLoader().getResourceAsStream("keystore.jks"), passwd);
+ trustStore = KeyStore.getInstance("JKS");
+ trustStore.load(this.getClass().getClassLoader().getResourceAsStream("truststore.jks"), passwd);
+ }
+
+ @Test
+ void whenCertificateIsSelfSigned_thenSubjectIsEqualToIssuer() throws Exception {
+ X509Certificate certificate = (X509Certificate) keyStore.getCertificate("selfsigned");
+ assertEquals(certificate.getSubjectDN(), certificate.getIssuerDN());
+ }
+
+ @Test
+ void whenCertificateIsSelfSigned_thenItCanBeVerifiedWithItsOwnPublicKey() throws Exception {
+ X509Certificate certificate = (X509Certificate) keyStore.getCertificate("selfsigned");
+ assertDoesNotThrow(() -> certificate.verify(certificate.getPublicKey()));
+ }
+
+ @Test
+ void whenCertificateIsCASigned_thenItCantBeVerifiedWithItsOwnPublicKey() throws Exception {
+ X509Certificate certificate = (X509Certificate) keyStore.getCertificate("baeldung");
+ assertThrows(SignatureException.class, () -> certificate.verify(certificate.getPublicKey()));
+ }
+
+ @Test
+ void whenCertificateIsCASigned_thenRootCanBeFoundInTruststore() throws Exception {
+ X509Certificate endEntityCertificate = (X509Certificate) keyStore.getCertificate("baeldung");
+ X509Certificate rootCertificate = getRootCertificate(endEntityCertificate, trustStore);
+ assertNotNull(rootCertificate);
+ }
+
+ @Test
+ void whenCertificateIsCA_thenItCanBeUsedToSignOtherCertificates() throws Exception {
+ X509Certificate certificate = (X509Certificate) keyStore.getCertificate("cloudflare");
+ assertTrue(certificate.getKeyUsage()[5]);
+ }
+
+ @Test
+ void whenCertificateIsCA_thenBasicConstrainsReturnsZeroOrGreaterThanZero() throws Exception {
+ X509Certificate certificate = (X509Certificate) keyStore.getCertificate("cloudflare");
+ assertNotEquals(-1, certificate.getBasicConstraints());
+ }
+
+ @Test
+ void whenCertificateIsSelfSigned_thenItCantBeUsedToSignOtherCertificates() throws Exception {
+ X509Certificate certificate = (X509Certificate) keyStore.getCertificate("selfsigned");
+ assertNull(certificate.getKeyUsage());
+ }
+}
\ No newline at end of file
diff --git a/core-java-modules/pom.xml b/core-java-modules/pom.xml
index e9bb86e500..4f21da40f5 100644
--- a/core-java-modules/pom.xml
+++ b/core-java-modules/pom.xml
@@ -127,6 +127,7 @@
core-java-scanner
core-java-security-2
core-java-security-3
+ core-java-security-4
core-java-security-algorithms
core-java-streams
core-java-streams-3