diff --git a/pom.xml b/pom.xml
index 9be9e1ef25..d797240169 100644
--- a/pom.xml
+++ b/pom.xml
@@ -99,6 +99,7 @@
spring-security-rest-custom
spring-security-rest-digest-auth
spring-security-rest-full
+ spring-security-x509
spring-thymeleaf
spring-zuul
jsf
diff --git a/spring-security-x509/basic-secured-server/pom.xml b/spring-security-x509/basic-secured-server/pom.xml
new file mode 100644
index 0000000000..87fdd64727
--- /dev/null
+++ b/spring-security-x509/basic-secured-server/pom.xml
@@ -0,0 +1,27 @@
+
+
+ 4.0.0
+
+ basic-secured-server
+ 0.0.1-SNAPSHOT
+ jar
+
+ basic-secured-server
+ Spring x.509 Authentication Demo
+
+
+ com.baeldung
+ spring-security-x509
+ 0.0.1-SNAPSHOT
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/spring-security-x509/basic-secured-server/src/main/java/com/baeldung/spring/security/x509/UserController.java b/spring-security-x509/basic-secured-server/src/main/java/com/baeldung/spring/security/x509/UserController.java
new file mode 100644
index 0000000000..af1c103739
--- /dev/null
+++ b/spring-security-x509/basic-secured-server/src/main/java/com/baeldung/spring/security/x509/UserController.java
@@ -0,0 +1,21 @@
+package com.baeldung.spring.security.x509;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.security.Principal;
+
+@Controller
+public class UserController {
+ @PreAuthorize("hasAuthority('ROLE_USER')")
+ @RequestMapping(value = "/user")
+ public String user(Model model, Principal principal) {
+ UserDetails currentUser = (UserDetails) ((Authentication) principal).getPrincipal();
+ model.addAttribute("username", currentUser.getUsername());
+ return "user";
+ }
+}
diff --git a/spring-security-x509/basic-secured-server/src/main/java/com/baeldung/spring/security/x509/X509AuthenticationServer.java b/spring-security-x509/basic-secured-server/src/main/java/com/baeldung/spring/security/x509/X509AuthenticationServer.java
new file mode 100644
index 0000000000..edcacfda15
--- /dev/null
+++ b/spring-security-x509/basic-secured-server/src/main/java/com/baeldung/spring/security/x509/X509AuthenticationServer.java
@@ -0,0 +1,22 @@
+package com.baeldung.spring.security.x509;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+@SpringBootApplication
+@EnableWebSecurity
+public class X509AuthenticationServer extends WebSecurityConfigurerAdapter {
+ public static void main(String[] args) {
+ SpringApplication.run(X509AuthenticationServer.class, args);
+ }
+}
diff --git a/spring-security-x509/basic-secured-server/src/main/resources/application.properties b/spring-security-x509/basic-secured-server/src/main/resources/application.properties
new file mode 100644
index 0000000000..f293d6712d
--- /dev/null
+++ b/spring-security-x509/basic-secured-server/src/main/resources/application.properties
@@ -0,0 +1,8 @@
+server.ssl.key-store=../keystore/keystore.jks
+server.ssl.key-store-password=changeit
+server.ssl.key-alias=localhost
+server.ssl.key-password=changeit
+server.ssl.enabled=true
+server.port=8443
+security.user.name=Admin
+security.user.password=admin
\ No newline at end of file
diff --git a/spring-security-x509/basic-secured-server/src/main/resources/templates/user.html b/spring-security-x509/basic-secured-server/src/main/resources/templates/user.html
new file mode 100644
index 0000000000..3e36d7b644
--- /dev/null
+++ b/spring-security-x509/basic-secured-server/src/main/resources/templates/user.html
@@ -0,0 +1,9 @@
+
+
+
+ X.509 Authentication Demo
+
+
+ Hello !
+
+
diff --git a/spring-security-x509/basic-secured-server/src/test/java/com/baeldung/spring/security/x509/X509AuthenticationServerTests.java b/spring-security-x509/basic-secured-server/src/test/java/com/baeldung/spring/security/x509/X509AuthenticationServerTests.java
new file mode 100644
index 0000000000..0b9a11552a
--- /dev/null
+++ b/spring-security-x509/basic-secured-server/src/test/java/com/baeldung/spring/security/x509/X509AuthenticationServerTests.java
@@ -0,0 +1,14 @@
+package com.baeldung.spring.security.x509;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class X509AuthenticationServerTests {
+ @Test
+ public void contextLoads() {
+ }
+}
diff --git a/spring-security-x509/client-auth-server/pom.xml b/spring-security-x509/client-auth-server/pom.xml
new file mode 100644
index 0000000000..56cef8ea07
--- /dev/null
+++ b/spring-security-x509/client-auth-server/pom.xml
@@ -0,0 +1,27 @@
+
+
+ 4.0.0
+
+ client-auth-server
+ 0.0.1-SNAPSHOT
+ jar
+
+ client-auth-server
+ Spring x.509 Client Authentication Demo
+
+
+ com.baeldung
+ spring-security-x509
+ 0.0.1-SNAPSHOT
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/spring-security-x509/client-auth-server/src/main/java/com/baeldung/spring/security/x509/UserController.java b/spring-security-x509/client-auth-server/src/main/java/com/baeldung/spring/security/x509/UserController.java
new file mode 100644
index 0000000000..af1c103739
--- /dev/null
+++ b/spring-security-x509/client-auth-server/src/main/java/com/baeldung/spring/security/x509/UserController.java
@@ -0,0 +1,21 @@
+package com.baeldung.spring.security.x509;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.security.Principal;
+
+@Controller
+public class UserController {
+ @PreAuthorize("hasAuthority('ROLE_USER')")
+ @RequestMapping(value = "/user")
+ public String user(Model model, Principal principal) {
+ UserDetails currentUser = (UserDetails) ((Authentication) principal).getPrincipal();
+ model.addAttribute("username", currentUser.getUsername());
+ return "user";
+ }
+}
diff --git a/spring-security-x509/client-auth-server/src/main/java/com/baeldung/spring/security/x509/X509AuthenticationServer.java b/spring-security-x509/client-auth-server/src/main/java/com/baeldung/spring/security/x509/X509AuthenticationServer.java
new file mode 100644
index 0000000000..7d8413589e
--- /dev/null
+++ b/spring-security-x509/client-auth-server/src/main/java/com/baeldung/spring/security/x509/X509AuthenticationServer.java
@@ -0,0 +1,43 @@
+package com.baeldung.spring.security.x509;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+@SpringBootApplication
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class X509AuthenticationServer extends WebSecurityConfigurerAdapter {
+ public static void main(String[] args) {
+ SpringApplication.run(X509AuthenticationServer.class, args);
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.authorizeRequests().anyRequest().authenticated()
+ .and()
+ .x509().subjectPrincipalRegex("CN=(.*?)(?:,|$)").userDetailsService(userDetailsService());
+ }
+
+ @Bean
+ public UserDetailsService userDetailsService() {
+ return new UserDetailsService() {
+ @Override
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ if (username.equals("cid")) {
+ return new User(username, "", AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
+ }
+ throw new UsernameNotFoundException("User not found!");
+ }
+ };
+ }
+}
diff --git a/spring-security-x509/client-auth-server/src/main/resources/application.properties b/spring-security-x509/client-auth-server/src/main/resources/application.properties
new file mode 100644
index 0000000000..174eba9f98
--- /dev/null
+++ b/spring-security-x509/client-auth-server/src/main/resources/application.properties
@@ -0,0 +1,11 @@
+server.ssl.key-store=../keystore/keystore.jks
+server.ssl.key-store-password=changeit
+server.ssl.key-alias=localhost
+server.ssl.key-password=changeit
+server.ssl.enabled=true
+server.port=8443
+security.user.name=Admin
+security.user.password=admin
+server.ssl.trust-store=../keystore/truststore.jks
+server.ssl.trust-store-password=changeit
+server.ssl.client-auth=need
\ No newline at end of file
diff --git a/spring-security-x509/client-auth-server/src/main/resources/keystore.jks b/spring-security-x509/client-auth-server/src/main/resources/keystore.jks
new file mode 100644
index 0000000000..044a820c39
Binary files /dev/null and b/spring-security-x509/client-auth-server/src/main/resources/keystore.jks differ
diff --git a/spring-security-x509/client-auth-server/src/main/resources/templates/user.html b/spring-security-x509/client-auth-server/src/main/resources/templates/user.html
new file mode 100644
index 0000000000..a35b18bacc
--- /dev/null
+++ b/spring-security-x509/client-auth-server/src/main/resources/templates/user.html
@@ -0,0 +1,9 @@
+
+
+
+ X.509 Authentication Demo
+
+
+ Hello !
+
+
diff --git a/spring-security-x509/client-auth-server/src/test/java/com/baeldung/spring/security/x509/X509AuthenticationServerTests.java b/spring-security-x509/client-auth-server/src/test/java/com/baeldung/spring/security/x509/X509AuthenticationServerTests.java
new file mode 100644
index 0000000000..0b9a11552a
--- /dev/null
+++ b/spring-security-x509/client-auth-server/src/test/java/com/baeldung/spring/security/x509/X509AuthenticationServerTests.java
@@ -0,0 +1,14 @@
+package com.baeldung.spring.security.x509;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class X509AuthenticationServerTests {
+ @Test
+ public void contextLoads() {
+ }
+}
diff --git a/spring-security-x509/keystore/Makefile b/spring-security-x509/keystore/Makefile
new file mode 100644
index 0000000000..5321be9de3
--- /dev/null
+++ b/spring-security-x509/keystore/Makefile
@@ -0,0 +1,88 @@
+PASSWORD=changeit
+KEYSTORE=keystore.jks
+HOSTNAME=localhost
+CLIENTNAME=cid
+
+# CN = Common Name
+# OU = Organization Unit
+# O = Organization Name
+# L = Locality Name
+# ST = State Name
+# C = Country (2-letter Country Code)
+# E = Email
+DNAME_CA='CN=Baeldung CA,OU=baeldung.com,O=Baeldung,L=SomeCity,ST=SomeState,C=CC'
+# For server certificates, the Common Name (CN) must be the hostname
+DNAME_HOST='CN=$(HOSTNAME),OU=baeldung.com,O=Baeldung,L=SomeCity,ST=SomeState,C=CC'
+DNAME_CLIENT='CN=$(CLIENTNAME),OU=baeldung.com,O=Baeldung,L=SomeCity,ST=SomeState,C=CC'
+TRUSTSTORE=truststore.jks
+
+all: clean create-keystore add-host create-truststore add-client
+
+create-keystore:
+ # Generate a certificate authority (CA)
+ keytool -genkey -alias ca -ext BC=ca:true \
+ -keyalg RSA -keysize 4096 -sigalg SHA512withRSA -keypass $(PASSWORD) \
+ -validity 3650 -dname $(DNAME_CA) \
+ -keystore $(KEYSTORE) -storepass $(PASSWORD)
+
+add-host:
+ # Generate a host certificate
+ keytool -genkey -alias $(HOSTNAME) \
+ -keyalg RSA -keysize 4096 -sigalg SHA512withRSA -keypass $(PASSWORD) \
+ -validity 3650 -dname $(DNAME_HOST) \
+ -keystore $(KEYSTORE) -storepass $(PASSWORD)
+ # Generate a host certificate signing request
+ keytool -certreq -alias $(HOSTNAME) -ext BC=ca:true \
+ -keyalg RSA -keysize 4096 -sigalg SHA512withRSA \
+ -validity 3650 -file "$(HOSTNAME).csr" \
+ -keystore $(KEYSTORE) -storepass $(PASSWORD)
+ # Generate signed certificate with the certificate authority
+ keytool -gencert -alias ca \
+ -validity 3650 -sigalg SHA512withRSA \
+ -infile "$(HOSTNAME).csr" -outfile "$(HOSTNAME).crt" -rfc \
+ -keystore $(KEYSTORE) -storepass $(PASSWORD)
+ # Import signed certificate into the keystore
+ keytool -import -trustcacerts -alias $(HOSTNAME) \
+ -file "$(HOSTNAME).crt" \
+ -keystore $(KEYSTORE) -storepass $(PASSWORD)
+
+export-authority:
+ # Export certificate authority
+ keytool -export -alias ca -file ca.crt -rfc \
+ -keystore $(KEYSTORE) -storepass $(PASSWORD)
+
+
+create-truststore: export-authority
+ # Import certificate authority into a new truststore
+ keytool -import -trustcacerts -noprompt -alias ca -file ca.crt \
+ -keystore $(TRUSTSTORE) -storepass $(PASSWORD)
+
+add-client:
+ # Generate client certificate
+ keytool -genkey -alias $(CLIENTNAME) \
+ -keyalg RSA -keysize 4096 -sigalg SHA512withRSA -keypass $(PASSWORD) \
+ -validity 3650 -dname $(DNAME_CLIENT) \
+ -keystore $(TRUSTSTORE) -storepass $(PASSWORD)
+ # Generate a host certificate signing request
+ keytool -certreq -alias $(CLIENTNAME) -ext BC=ca:true \
+ -keyalg RSA -keysize 4096 -sigalg SHA512withRSA \
+ -validity 3650 -file "$(CLIENTNAME).csr" \
+ -keystore $(TRUSTSTORE) -storepass $(PASSWORD)
+ # Generate signed certificate with the certificate authority
+ keytool -gencert -alias ca \
+ -validity 3650 -sigalg SHA512withRSA \
+ -infile "$(CLIENTNAME).csr" -outfile "$(CLIENTNAME).crt" -rfc \
+ -keystore $(KEYSTORE) -storepass $(PASSWORD)
+ # Import signed certificate into the truststore
+ keytool -import -trustcacerts -alias $(CLIENTNAME) \
+ -file "$(CLIENTNAME).crt" \
+ -keystore $(TRUSTSTORE) -storepass $(PASSWORD)
+ # Export private certificate for importing into a browser
+ keytool -importkeystore -srcalias $(CLIENTNAME) \
+ -srckeystore $(TRUSTSTORE) -srcstorepass $(PASSWORD) \
+ -destkeystore "$(CLIENTNAME).p12" -deststorepass $(PASSWORD) \
+ -deststoretype PKCS12
+
+clean:
+ # Remove generated artifacts
+ find . ! -name Makefile -type f -exec rm -f {} \;
diff --git a/spring-security-x509/keystore/keystore.jks b/spring-security-x509/keystore/keystore.jks
new file mode 100644
index 0000000000..044a820c39
Binary files /dev/null and b/spring-security-x509/keystore/keystore.jks differ
diff --git a/spring-security-x509/pom.xml b/spring-security-x509/pom.xml
new file mode 100644
index 0000000000..8a9fd200a8
--- /dev/null
+++ b/spring-security-x509/pom.xml
@@ -0,0 +1,49 @@
+
+
+ 4.0.0
+
+ com.baeldung
+ spring-security-x509
+ 0.0.1-SNAPSHOT
+ pom
+
+
+ basic-secured-server
+ client-auth-server
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 1.4.0.RELEASE
+
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+