Digitale signaturer i Java

Java Top

Jeg kunngjorde nettopp det nye Lær våren kurs, med fokus på det grunnleggende i vår 5 og vårstøvel 2:

>> KONTROLLER KURSET

1. Oversikt

I denne opplæringen skal vi lære om Digital signaturmekanisme og hvordan vi kan implementere den ved hjelp av Java Cryptography Architecture (JCA). Vi skal utforske KeyPair, MessageDigest, Cipher, KeyStore, Certificate, og Signatur JCA APIer.

Vi begynner med å forstå hva som er Digital signatur, hvordan man genererer et nøkkelpar og hvordan man sertifiserer den offentlige nøkkelen fra en sertifikatmyndighet (CA). Etter det får vi se hvordan du implementerer Digital Signature ved hjelp av JCA APIer på lavt nivå og høyt nivå.

2. Hva er digital signatur?

2.1. Definisjon av digital signatur

Digital signatur er en teknikk for å sikre:

  • Integritet: meldingen er ikke endret under transport
  • Autentisitet: forfatteren av meldingen er virkelig den de hevder å være
  • Ikke-avvisning: forfatteren av meldingen kan ikke senere benekte at de var kilden

2.2. Sende en melding med digital signatur

Teknisk sett, endigital signatur er den krypterte hash (fordøyelse, kontrollsum) av en melding. Det betyr at vi genererer en hash fra en melding og krypterer den med en privat nøkkel i henhold til en valgt algoritme.

Meldingen, den krypterte hashen, den tilhørende offentlige nøkkelen og algoritmen blir deretter sendt. Dette er klassifisert som en melding med sin digitale signatur.

2.3. Motta og sjekke en digital signatur

For å sjekke den digitale signaturen genererer meldingsmottakeren en ny hash fra den mottatte meldingen, dekrypterer den mottatte krypterte hasjen ved hjelp av den offentlige nøkkelen og sammenligner dem. Hvis de samsvarer, sies det at den digitale signaturen er bekreftet.

Vi bør merke oss at vi bare krypterer meldingshashen, og ikke selve meldingen. Med andre ord prøver ikke Digital signatur å holde meldingen hemmelig. Den digitale signaturen vår beviser bare at meldingen ikke ble endret under transport.

Når signaturen er bekreftet, er vi sikre på at bare eieren av den private nøkkelen kan være forfatter av meldingen.

3. Digital sertifikat og offentlig nøkkelidentitet

Et sertifikat er et dokument som knytter en identitet til en gitt offentlig nøkkel. Sertifikater er signert av en tredjepartsenhet kalt Certificate Certificate (CA).

Vi vet at hvis hashen vi dekrypterer med den publiserte offentlige nøkkelen samsvarer med den faktiske hasjen, så signeres meldingen. Men hvordan vet vi at den offentlige nøkkelen virkelig kom fra riktig enhet? Dette løses ved bruk av digitale sertifikater.

Et digitalt sertifikat inneholder en offentlig nøkkel og er selv signert av en annen enhet. Signaturen til den enheten kan selv verifiseres av en annen enhet og så videre. Vi ender med å ha det vi kaller en sertifikatkjede. Hver toppenhet sertifiserer den offentlige nøkkelen til neste enhet. Den høyeste enheten er selvsignert, noe som betyr at den offentlige nøkkelen hans er signert av sin egen private nøkkel.

X.509 er det mest brukte sertifikatformatet, og den sendes enten som binært format (DER) eller tekstformat (PEM). JCA gir allerede en implementering for dette via X509Sertifikat klasse.

4. KeyPair Management

Siden Digital Signature bruker en privat og offentlig nøkkel, bruker vi JCA-klassene PrivateKey og PublicKey for å signere og sjekke en melding, henholdsvis.

4.1. Få et nøkkelpar

For å lage et nøkkelpar av en privat og offentlig nøkkel, vi bruker Java nøkkelverktøy.

La oss generere et nøkkelpar ved hjelp av genkeypair kommando:

keytool -genkeypair -alias senderKeyPair -keyalg RSA -keysize 2048 \ -dname "CN = Baeldung" -validity 365 -storetype PKCS12 \ -keystore sender_keystore.p12 -storepass changeit

Dette skaper en privat nøkkel og dens tilhørende offentlige nøkkel for oss. Den offentlige nøkkelen pakkes inn i et X.509 selvsignert sertifikat som blir pakket inn i en enkeltelementsertifikatkjede. Vi lagrer sertifikatkjeden og den private nøkkelen i Keystore-filen sender_keystore.p12, som vi kan behandle ved hjelp av KeyStore API.

Her har vi brukt formatet PKCS12 nøkkelbutikk, da det er standard og anbefales over det Java-proprietære JKS-formatet. Vi bør også huske passordet og aliaset, ettersom vi bruker dem i neste underavsnitt når vi laster inn Keystore-filen.

4.2. Laster inn den private nøkkelen for signering

For å signere en melding trenger vi en forekomst av PrivateKey.

Bruker KeyStore API og den forrige Keystore-filen, sender_keystore.p12, vi kan få en PrivateKey gjenstand:

KeyStore keyStore = KeyStore.getInstance ("PKCS12"); keyStore.load (ny FileInputStream ("sender_keystore.p12"), "changeit"); PrivateKey privateKey = (PrivateKey) keyStore.getKey ("senderKeyPair", "changeit");

4.3. Publisering av den offentlige nøkkelen

Før vi kan publisere den offentlige nøkkelen, må vi først bestemme om vi skal bruke et selvsignert sertifikat eller et CA-signert sertifikat.

Når du bruker et selvsignert sertifikat, trenger vi bare å eksportere det fra Keystore-filen. Vi kan gjøre dette med eksportcert kommando:

keytool -exportcert -alias senderKeyPair -storetype PKCS12 \ -keystore sender_keystore.p12 -fil \ sender_certificate.cer -rfc -storepass endring

Ellers, hvis vi skal jobbe med et CA-signert sertifikat, må vi opprette en sertifikatsigneringsforespørsel (CSR). Vi gjør dette med certreq kommando:

nøkkelverktøy -certreq -alias avsenderKeyPair -storetype PKCS12 \ -keystore sender_keystore.p12 -fil -rfc \ -storpasspass endring> sender_certificate.csr

CSR-filen, sender_certificate.csr, blir deretter sendt til en sertifikatmyndighet for signering. Når dette er gjort, mottar vi en signert offentlig nøkkel pakket i et X.509-sertifikat, enten i binært (DER) eller tekst (PEM) -format. Her har vi brukt rfc alternativ for et PEM-format.

Den offentlige nøkkelen vi mottok fra CA, sender_certificate.cer, er nå signert av en CA og kan gjøres tilgjengelig for klienter.

4.4. Laster inn en offentlig nøkkel for bekreftelse

Etter å ha tilgang til den offentlige nøkkelen, kan en mottaker laste den inn i Keystore ved hjelp av importcert kommando:

keytool -importcert -alias mottakerKeyPair -storetype PKCS12 \ -keystore receiver_keystore.p12 -fil \ sender_certificate.cer -rfc -storepass endring

Og bruke KeyStore API som før, kan vi få en PublicKey forekomst:

KeyStore keyStore = KeyStore.getInstance ("PKCS12"); keyStore.load (ny FileInputStream ("receiver_keytore.p12"), "changeit"); Sertifikatsertifikat = keyStore.getCertificate ("receiverKeyPair"); PublicKey publicKey = certificate.getPublicKey ();

Nå som vi har en PrivateKey forekomst på avsendersiden, og en forekomst av PublicKey på mottakersiden kan vi starte prosessen med signering og verifisering.

5. Digital signatur med MessageDigest og Kryptering Klasser

Som vi har sett, er den digitale signaturen basert på hashing og kryptering.

Vanligvis bruker vi MessageDigest klasse med SHA eller MD5 for hashing og Kryptering klasse for kryptering.

La oss nå begynne å implementere de digitale signaturmekanismene.

5.1. Generere en melding Hash

En melding kan være en streng, en fil eller andre data. Så la oss ta innholdet i en enkel fil:

byte [] messageBytes = Files.readAllBytes (Paths.get ("message.txt"));

Nå, bruker MessageDigest, la oss bruke fordøye metode for å generere en hash:

MessageDigest md = MessageDigest.getInstance ("SHA-256"); byte [] messageHash = md.digest (messageBytes);

Her har vi brukt SHA-256-algoritmen, som er den mest brukte. Andre alternativer er MD5, SHA-384 og SHA-512.

5.2. Kryptering av den genererte hasjen

For å kryptere en melding trenger vi en algoritme og en privat nøkkel. Her bruker vi RSA-algoritmen. DSA-algoritmen er et annet alternativ.

La oss lage en Kryptering forekomst og initialiser den for kryptering. Så kaller vi doFinal () metode for å kryptere den tidligere hashede meldingen:

Cipher cipher = Cipher.getInstance ("RSA"); cipher.init (Cipher.ENCRYPT_MODE, privateKey); byte [] digitalSignature = cipher.doFinal (messageHash);

Signaturen kan lagres i en fil for å sende den senere:

Files.write (Paths.get ("digital_signature_1"), digitalSignature);

På dette tidspunktet sendes meldingen, den digitale signaturen, den offentlige nøkkelen og algoritmen, og mottakeren kan bruke disse informasjonene for å verifisere integriteten til meldingen.

5.3. Verifiserer signatur

Når vi mottar en melding, må vi bekrefte signaturen. For å gjøre det dekrypterer vi den mottatte krypterte hasjen og sammenligner den med en hash vi lager av den mottatte meldingen.

La oss lese den mottatte digitale signaturen:

byte [] encryptedMessageHash = Files.readAllBytes (Paths.get ("digital_signature_1"));

For dekryptering lager vi en Kryptering forekomst. Så kaller vi doFinal metode:

Cipher cipher = Cipher.getInstance ("RSA"); cipher.init (Cipher.DECRYPT_MODE, publicKey); byte [] decryptedMessageHash = cipher.doFinal (encryptedMessageHash);

Deretter genererer vi en ny meldingshash fra den mottatte meldingen:

byte [] messageBytes = Files.readAllBytes (Paths.get ("message.txt")); MessageDigest md = MessageDigest.getInstance ("SHA-256"); byte [] newMessageHash = md.digest (messageBytes);

Og til slutt sjekker vi om den nylig genererte meldingshashen samsvarer med den dekrypterte:

boolsk isCorrect = Arrays.equals (decryptedMessageHash, newMessageHash);

I dette eksemplet har vi brukt tekstfilen message.txt for å simulere en melding vi vil sende, eller plasseringen av kroppen til en melding vi har mottatt. Normalt forventer vi å motta meldingen vår ved siden av signaturen.

6. Digital signatur ved hjelp av Signatur Klasse

Så langt har vi brukt API-er på lavt nivå for å bygge vår egen prosess for bekreftelse av digital signatur. Dette hjelper oss å forstå hvordan det fungerer og lar oss tilpasse det.

Imidlertid tilbyr JCA allerede en dedikert API i form av Signatur klasse.

6.1. Signere en melding

For å starte prosessen med signering, oppretter vi først en forekomst av Signatur klasse. For å gjøre det trenger vi en signeringsalgoritme. Vi initialiserer deretter Signatur med vår private nøkkel:

Signatur signatur = Signature.getInstance ("SHA256withRSA"); signatur.initSign (privateKey);

Signeringsalgoritmen vi valgte, SHA256medRSA i dette eksemplet, er en kombinasjon av en hashingalgoritme og en krypteringsalgoritme. Andre alternativer inkluderer SHA1medRSA, SHA1medDSA, og MD5medRSA, blant andre.

Deretter fortsetter vi å signere byte-arrayet til meldingen:

byte [] messageBytes = Files.readAllBytes (Paths.get ("message.txt")); signatur.update (messageBytes); byte [] digitalSignature = signatur.sign ();

Vi kan lagre signaturen i en fil for senere overføring:

Files.write (Paths.get ("digital_signature_2"), digitalSignature);

6.2. Bekrefte signaturen

For å bekrefte mottatt signatur oppretter vi igjen en Signatur forekomst:

Signatur signatur = Signature.getInstance ("SHA256withRSA");

Deretter initialiserer vi Signatur objekt for bekreftelse ved å ringe initVerify metode, som tar en offentlig nøkkel:

signatur.initVerify (publicKey);

Deretter må vi legge til mottatte meldingsbyte i signaturobjektet ved å påkalle Oppdater metode:

byte [] messageBytes = Files.readAllBytes (Paths.get ("message.txt")); signatur.update (messageBytes);

Og til slutt kan vi sjekke signaturen ved å ringe bekrefte metode:

boolsk isCorrect = signatur.verify (mottattSignatur);

7. Konklusjon

I denne artikkelen så vi først på hvordan digital signatur fungerer og hvordan vi kan skape tillit til et digitalt sertifikat. Så implementerte vi en digital signatur ved hjelp av MessageDigest,Kryptering, og Signatur klasser fra Java Cryptography Architecture.

Vi så i detalj hvordan man signerer data ved hjelp av den private nøkkelen og hvordan man bekrefter signaturen ved hjelp av en offentlig nøkkel.

Som alltid er koden fra denne artikkelen tilgjengelig på GitHub.

Java bunn

Jeg kunngjorde nettopp det nye Lær våren kurs, med fokus på det grunnleggende i vår 5 og vårstøvel 2:

>> KONTROLLER KURSET

$config[zx-auto] not found$config[zx-overlay] not found