X.509-verificatie in Spring Security

1. Overzicht

In dit artikel zullen we ons concentreren op de belangrijkste gebruiksscenario's voor X.509-certificaatverificatie - het verifiëren van de identiteit van een communicatie-peer bij gebruik van het HTTPS-protocol (HTTP over SSL).

Simpel gezegd - terwijl een beveiligde verbinding tot stand wordt gebracht, verifieert de client de server aan de hand van zijn certificaat (uitgegeven door een vertrouwde certificeringsinstantie).

Maar verder kan X.509 in Spring Security worden gebruikt verifieer de identiteit van een klant door de server tijdens het verbinden. Dit heet "Wederzijdse authenticatie", en we zullen hier ook kijken hoe dat wordt gedaan.

Ten slotte gaan we verder wanneer het zinvol is om dit soort authenticatie te gebruiken.

Om serververificatie te demonstreren, maken we een eenvoudige webtoepassing en installeren we een aangepaste certificeringsinstantie in een browser.

Bovendien, voor wederzijdse authenticatie, maken we een clientcertificaat aan en passen we onze server aan om alleen geverifieerde clients toe te staan.

Het wordt ten zeerste aanbevolen om de tutorial stap voor stap te volgen en de certificaten, evenals de keystore en de truststore, zelf aan te maken volgens de instructies in de volgende secties. Alle kant-en-klare bestanden zijn echter te vinden in onze GitHub-repository.

2. Zelfondertekende basis-CA

Om onze server-side en client-side certificaten te kunnen ondertekenen, moeten we eerst ons eigen zelfondertekende root CA-certificaat aanmaken. Op deze manier wij treden op als onze eigen certificeringsinstantie.

Voor dit doel gebruiken we de openssl-bibliotheek, dus we moeten deze hebben geïnstalleerd voordat we de volgende stap volgen.

Laten we nu het CA-certificaat maken:

openssl req -x509 -sha256 -days 3650 -newkey rsa: 4096 -keyout rootCA.key -out rootCA.crt

Wanneer we de bovenstaande opdracht uitvoeren, moeten we het wachtwoord voor onze privésleutel opgeven. Voor deze tutorial gebruiken we verander het als een wachtwoordzin.

Bovendien, we moeten informatie invoeren die een zogenaamde DN-naam vormt. Hier verstrekken we alleen de CN (Common Name) - Baeldung.com - en laten we andere delen leeg.

3. Sleutelarchief

Facultatieve vereiste: Om cryptografisch sterke sleutels te gebruiken in combinatie met coderings- en decoderingsfuncties hebben we de "Java Cryptography Extension (JCE) Onbeperkte machtsbeleidsbestanden voor jurisdictie”Geïnstalleerd in onze JVM.

Deze kunnen bijvoorbeeld worden gedownload van Oracle (volg de installatie-instructies die bij de download zijn meegeleverd). Sommige Linux-distributies bieden ook een installeerbaar pakket via hun pakketbeheerders.

Een keystore is een opslagplaats die onze Spring Boot-applicatie zal gebruiken om de privésleutel en het certificaat van onze server te bewaren. Met andere woorden, onze applicatie zal de keystore gebruiken om het certificaat aan de klanten te serveren tijdens de SSL-handshake.

In deze tutorial gebruiken we de Java Key-Store (JKS) -indeling en een keytool-opdrachtregelprogramma.

3.1. Certificaat aan serverzijde

Om de server-side X.509-authenticatie in onze Spring Boot-applicatie te implementeren, hebben we moet eerst een server-side certificaat aanmaken.

Laten we beginnen met het maken van een zogenaamd certificaatondertekeningsverzoek (CSR):

openssl req -new -newkey rsa: 4096 -keyout localhost.key –out localhost.csr

Evenzo moeten we, net als voor het CA-certificaat, het wachtwoord voor de privésleutel opgeven. Laten we bovendien localhost als een algemene naam (CN).

Voordat we verder gaan, moeten we een configuratiebestand maken - localhost.ext. Het slaat enkele aanvullende parameters op die nodig zijn tijdens het ondertekenen van het certificaat.

AuthorityKeyIdentifier = keyid, uitgever basicConstraints = CA: FALSE subjectAltName = @alt_names [alt_names] DNS.1 = localhost

Een kant-en-klaar bestand is hier ook beschikbaar.

Nu is het tijd om teken het verzoek met onze rootCA.crt certificaat en de bijbehorende privésleutel:

openssl x509 -req -CA rootCA.crt -CAkey rootCA.key -in localhost.csr -out localhost.crt -days 365 -CAcreateserial -extfile localhost.ext

Houd er rekening mee dat we hetzelfde wachtwoord moeten opgeven dat we hebben gebruikt bij het maken van ons CA-certificaat.

In dit stadium we hebben eindelijk een klaar voor gebruik localhost.crt certificaat ondertekend door onze eigen certificeringsinstantie.

Om de details van ons certificaat af te drukken in een voor mensen leesbare vorm, kunnen we het volgende commando gebruiken:

openssl x509 -in localhost.crt -text

3.2. Importeer naar de Keystore

In dit gedeelte zullen we zien hoe u dat doet importeer het ondertekende certificaat en de bijbehorende privésleutel naar het keystore.jks het dossier.

We gebruiken het PKCS 12-archief om de privésleutel van onze server samen met het ondertekende certificaat te verpakken. Vervolgens importeren we het naar het nieuw gemaakte keystore.jks.

We kunnen de volgende opdracht gebruiken om een .p12 het dossier:

openssl pkcs12 -export -out localhost.p12 -naam "localhost" -inkey localhost.key -in localhost.crt

Dus we hebben nu de localhost.key en de localhost.crt gebundeld in de single localhost.p12 het dossier.

Laten we nu keytool gebruiken om Maak een keystore.jks repository en importeer het localhost.p12 bestand met een enkele opdracht:

keytool -importkeystore -srckeystore localhost.p12 -srcstoretype PKCS12 -destkeystore keystore.jks -deststoretype JKS

In dit stadium hebben we alles voor het serverauthenticatiegedeelte. Laten we doorgaan met de configuratie van onze Spring Boot-applicatie.

4. Voorbeeldtoepassing

Ons SSL-beveiligde serverproject bestaat uit een @SpringBootApplication geannoteerde toepassingsklasse (wat een soort @Configuratie), een application.properties configuratiebestand en een zeer eenvoudige front-end in MVC-stijl.

Het enige wat de applicatie hoeft te doen, is een HTML-pagina presenteren met een "Hallo {User}!" bericht. Op deze manier kunnen we het servercertificaat in een browser inspecteren om er zeker van te zijn dat de verbinding is geverifieerd en beveiligd.

4.1. Afhankelijkheden van Maven

Eerst maken we een nieuw Maven-project met drie Spring Boot Starter-bundels inbegrepen:

 org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-thymeleaf 

Als referentie: we kunnen de bundels vinden op Maven Central (beveiliging, web, thymeleaf).

4.2. Spring Boot-applicatie

Als volgende stap maken we de hoofdtoepassingsklasse en de gebruikerscontroller:

@SpringBootApplication openbare klasse X509AuthenticationServer {openbare statische leegte hoofd (String [] args) {SpringApplication.run (X509AuthenticationServer.class, args); }} @Controller public class UserController {@RequestMapping (value = "/ user") public String gebruiker (Model model, Principal principal) {UserDetails currentUser = (UserDetails) ((Authentication) principal) .getPrincipal (); model.addAttribute ("gebruikersnaam", currentUser.getUsername ()); retourneer "gebruiker"; }}

Nu vertellen we de applicatie waar ze onze keystore.jks en hoe u er toegang toe krijgt. We stellen SSL in op de status "ingeschakeld" en wijzigen de standaard luisterpoort in duiden op een beveiligde verbinding.

Bovendien configureren we enkele gebruikersdetails voor toegang tot onze server via basisverificatie:

server.ssl.key-store = .. / store / keystore.jks server.ssl.key-store-password = $ {PASSWORD} server.ssl.key-alias = localhost server.ssl.key-wachtwoord = $ {PASSWORD } server.ssl.enabled = true server.port = 8443 spring.security.user.name = Beheerder spring.security.user.password = admin

Dit wordt de HTML-sjabloon, die zich bevindt in het bronnen / sjablonen map:

   Demo voor X.509-authenticatie 

Hallo !

4.3. Root CA-installatie

Voordat we deze sectie afronden en naar de site kijken, we moeten onze gegenereerde rootcertificeringsinstantie installeren als een vertrouwd certificaat in een browser.

Een voorbeeldige installatie van onze certificeringsinstantie voor Mozilla Firefox zou er als volgt uitzien:

  1. Type about: voorkeuren in de adresbalk
  2. Open Geavanceerd -> Certificaten -> Bekijk certificaten -> Autoriteiten
  3. Klik op Importeren
  4. Zoek het Baeldung-tutorials map en zijn submap spring-security-x509 / keystore
  5. Selecteer de rootCA.crt bestand en klik OK
  6. Kiezen "Vertrouw op deze CA om websites te identificeren " en klik OK

Opmerking: Als u onze certificeringsinstantie naar de lijst van vertrouwde autoriteiten, heb je later de mogelijkheid om een uitzondering en de website stoer laten zien, zelfs als deze als onveilig wordt genoemd. Maar dan zie je een 'geel uitroepteken'-symbool in de adresbalk, wat de onveilige verbinding aangeeft!

Daarna navigeren we naar het spring-security-x509-basic-auth module en voer uit:

mvn spring-boot: run

Eindelijk raakten we // localhost: 8443 / gebruiker, voer onze gebruikersgegevens in vanuit het application.properties en zou een "Hallo beheerder!" bericht. Nu kunnen we de verbindingsstatus controleren door op het "groene slot" -symbool in de adresbalk te klikken. Het zou een beveiligde verbinding moeten zijn.

5. Wederzijdse authenticatie

In de vorige sectie hebben we laten zien hoe het meest voorkomende SSL-authenticatieschema geïmplementeerd kan worden: server-side authenticatie. Dit betekent dat alleen een server zichzelf heeft geverifieerd bij clients.

In deze sectie we zullen beschrijven hoe u het andere deel van de authenticatie toevoegt - client-side authenticatie. Op deze manier hebben alleen klanten met geldige certificaten ondertekend door de autoriteit die onze server vertrouwt toegang tot onze beveiligde website.

Maar laten we, voordat we verder gaan, eens kijken wat de voor- en nadelen zijn van het gebruik van wederzijdse SSL-authenticatie.

Voordelen:

  • De privésleutel van een X.509 clientcertificaat is sterker dan elk door de gebruiker gedefinieerd wachtwoord. Maar het moet geheim worden gehouden!
  • Met een certificaat is de de identiteit van een klant is bekend en gemakkelijk te verifiëren.
  • Geen vergeten wachtwoorden meer!

Nadelen:

  • Voor elke nieuwe klant moeten we een certificaat aanmaken.
  • Het certificaat van de klant moet in een clienttoepassing worden geïnstalleerd. In feite: X.509-clientverificatie is apparaatafhankelijk, waardoor het niet mogelijk is om deze vorm van authenticatie in openbare ruimtes te gebruiken, bijvoorbeeld in een internetcafé.
  • Er moet een mechanisme zijn om gecompromitteerde clientcertificaten in te trekken.
  • We moeten de certificaten van de klanten behouden. Dit kan gemakkelijk kostbaar worden.

5.1. Truststore

Een trustsore is op de een of andere manier het tegenovergestelde van een keystore. Het bevat de certificaten van de externe entiteiten die we vertrouwen.

In ons geval is het voldoende om het root-CA-certificaat in de truststore te bewaren.

Laten we eens kijken hoe we een truststore.jks bestand en importeer het rootCA.crt keytool gebruiken:

keytool -import -trustcacerts -noprompt -alias ca -ext san = dns: localhost, ip: 127.0.0.1 -file rootCA.crt -keystore truststore.jks

Let op, we moeten het wachtwoord opgeven voor het nieuw aangemaakte trusstore.jks. Hier hebben we opnieuw de verander het wachtwoordzin.

Dat is alles, we hebben ons eigen CA-certificaat geïmporteerd en de truststore is klaar voor gebruik.

5.2. Spring-beveiligingsconfiguratie

Om door te gaan, zijn we onze X509AuthenticationServer om uit te breiden WebSecurityConfigurerAdapter en overschrijf een van de aangeboden configuratiemethoden. Hier configureren we het x.509-mechanisme om het Algemene naam (CN) veld van een certificaat voor het extraheren van gebruikersnamen.

Met deze geëxtraheerde gebruikersnamen zoekt Spring Security op in een opgegeven UserDetailsService voor het matchen van gebruikers. Daarom implementeren we ook deze service-interface met één demo-gebruiker.

Tip: In productieomgevingen is dit UserDetailsService kan zijn gebruikers bijvoorbeeld laden vanuit een JDBC Datasource.

U moet opmerken dat we onze klas annoteren met @EnableWebSecurity en @EnableGlobalMethodSecurity met ingeschakelde pre- / post-autorisatie.

Met de laatste kunnen we onze bronnen annoteren met @PreAuthorize en @PostAuthorize voor fijnmazige toegangscontrole:

@SpringBootApplication @EnableWebSecurity @EnableGlobalMethodSecurity (prePostEnabled = true) openbare klasse X509AuthenticationServer breidt WebSecurityConfigurerAdapter uit {... @Override beschermde ongeldige configuratie (HttpSecurity @ http) gooit Uitzondering $) Details UserDetails UserDetails UserDetails UserDetails UserDetails UserDetails (userDetails) UserDetails {retourneer nieuwe UserDetailsService () {@Override openbare UserDetails loadUserByUsername (String gebruikersnaam) {if (gebruikersnaam.equals ("Bob")) {retourneer nieuwe gebruiker (gebruikersnaam, "", AuthorityUtils .commaSeparatedStringToAuthorityList ("ROLE_USER"));} new UsernameNotFoundException ("Gebruiker niet gevonden!");}};}}

Zoals eerder gezegd, kunnen we nu gebruiken Expressie-gebaseerde toegangscontrole in onze controller. Meer specifiek worden onze autorisatie-annotaties gerespecteerd vanwege de @EnableGlobalMethodSecurity annotatie in onze @Configuratie:

@Controller public class UserController {@PreAuthorize ("hasAuthority ('ROLE_USER')") @RequestMapping (value = "/ user") public String-gebruiker (Modelmodel, Principal-principal) {...}}

Een overzicht van alle mogelijke autorisatiemogelijkheden vindt u in het officiële documentatie.

Als laatste modificatiestap moeten we de applicatie vertellen waar onze truststore is gelegen en dat SSL-clientverificatie is noodzakelijk (server.ssl.client-auth = nodig).

Dus we hebben het volgende in onze application.properties:

server.ssl.trust-store = store / truststore.jks server.ssl.trust-store-password = $ {PASSWORD} server.ssl.client-auth = nodig

Als we nu de applicatie uitvoeren en onze browser naar // localhost: 8443 / gebruiker, vernemen we dat de peer niet kan worden geverifieerd en hij weigert onze website te openen.

5.3. Certificaat aan clientzijde

Nu is het tijd om het client-side certificaat te maken. De stappen die we moeten nemen, zijn vrijwel hetzelfde als voor het server-side certificaat dat we al hebben gemaakt.

Eerst moeten we een certificaatondertekeningsverzoek maken:

openssl req -new -newkey rsa: 4096 -nodes -keyout clientBob.key –out clientBob.csr

We zullen informatie moeten verstrekken die in het certificaat wordt opgenomen. Voor deze oefening laten we alleen de algemene naam (CN) invoeren - Bob. Het is belangrijk omdat we deze invoer gebruiken tijdens de autorisatie en alleen Bob wordt herkend door onze voorbeeldtoepassing.

Vervolgens moeten we het verzoek ondertekenen met onze CA:

openssl x509 -req -CA rootCA.crt -CAkey rootCA.key -in clientBob.csr -out clientBob.crt -days 365 -CAcreateserial

De laatste stap die we moeten nemen, is het ondertekende certificaat en de privésleutel verpakken in het PKCS-bestand:

openssl pkcs12 -export -out clientBob.p12 -naam "clientBob" -inkey clientBob.key -in clientBob.crt

Tenslotte, we zijn klaar om het clientcertificaat in de browser te installeren.

Nogmaals, we zullen Firefox gebruiken:

  1. Type about: voorkeuren in de adresbalk
  2. Open Geavanceerd -> Bekijk certificaten -> Uw certificaten
  3. Klik op Importeren
  4. Zoek het Baeldung-tutorials map en zijn submap spring-security-x509 / store
  5. Selecteer de clientBob.p12 bestand en klik OK
  6. Voer het wachtwoord voor uw certificaat in en klik op OK

Wanneer we onze website vernieuwen, wordt ons gevraagd om het clientcertificaat te selecteren dat we willen gebruiken:

Als we een welkomstbericht zien zoals "Hallo Bob!", dat betekent dat alles werkt zoals verwacht!

6. Wederzijdse authenticatie met XML

X.509-clientverificatie toevoegen aan een http beveiligingsconfiguratie in XML is ook mogelijk:

 ...         ... 

Om een ​​onderliggende Tomcat te configureren, moeten we onze keystore en onze truststore in zijn conf map en bewerk het server.xml:

Tip: Met clientAuth ingesteld op "willen", SSL is nog steeds ingeschakeld, zelfs als de client geen geldig certificaat verstrekt. Maar in dit geval moeten we een tweede authenticatiemechanisme gebruiken, bijvoorbeeld een inlogformulier, om toegang te krijgen tot de beveiligde bronnen.

7. Conclusie

Samenvattend hebben we geleerd hoe u een zelfondertekend CA-certificaat maakt en hoe u dit gebruikt om andere certificaten te ondertekenen.

Daarnaast hebben we zowel server-side als client-side certificaten gemaakt. Vervolgens hebben we uitgelegd hoe u ze kunt importeren in een keystore en een dienovereenkomstig truststore.

Bovendien zou je dat nu moeten kunnen verpak een certificaat samen met de bijbehorende privésleutel in het PKCS12-formaat.

We hebben ook besproken wanneer het zinvol is om Spring Security X.509-clientverificatie te gebruiken, dus het is aan jou om te beslissen of je het in je webtoepassing wilt implementeren of niet.

En om af te ronden, zoek de broncode van dit artikel op Github.