Introduksjon til BouncyCastle med Java

1. Oversikt

BouncyCastle er et Java-bibliotek som utfyller standard Java Cryptographic Extension (JCE).

I denne innledende artikkelen skal vi vise hvordan du bruker BouncyCastle til å utføre kryptografiske operasjoner, for eksempel kryptering og signatur.

2. Maven-konfigurasjon

Før vi begynner å jobbe med biblioteket, må vi legge til de nødvendige avhengighetene til vårt pom.xml fil:

 org.bouncycastle bcpkix-jdk15on 1.58 

Merk at vi alltid kan slå opp de nyeste avhengighetsversjonene i Maven Central Repository.

3. Konfigurer ubegrensede styringsrettsregler

Standard Java-installasjon er begrenset når det gjelder styrke for kryptografiske funksjoner, dette skyldes retningslinjer som forbyr bruk av en nøkkel med en størrelse som overstiger visse verdier, f.eks. 128 for AES.

For å overvinne denne begrensningen, må vi konfigurere de ubegrensede retningslinjene for styrke jurisdiksjon.

For å gjøre det, må vi først laste ned pakken ved å følge denne lenken. Etterpå må vi trekke ut den zip-filen i en katalog du ønsker - som inneholder to jar-filer:

  • local_policy.jar
  • US_export_policy.jar

Til slutt må vi se etter {JAVA_HOME} / lib / sikkerhet mappen og erstatt de eksisterende policyfilene med de som vi har hentet ut her.

Noter det i Java 9 trenger vi ikke lenger å laste ned policy-filpakken, sette inn crypto.policy eiendom til ubegrenset er nok:

Security.setProperty ("crypto.policy", "ubegrenset");

Når det er gjort, må vi sjekke at konfigurasjonen fungerer som den skal:

int maxKeySize = javax.crypto.Cipher.getMaxAllowedKeyLength ("AES"); System.out.println ("Maks nøkkelstørrelse for AES:" + maxKeySize);

Som et resultat:

Maks nøkkelstørrelse for AES: 2147483647

Basert på den maksimale nøkkelstørrelsen som returneres av getMaxAllowedKeyLength () metoden, kan vi trygt si at de ubegrensede styringspolicyfilene er installert riktig.

Hvis den returnerte verdien er lik 128, må vi sørge for at vi har installert filene i JVM der vi kjører koden.

4. Kryptografiske operasjoner

4.1. Forbereder sertifikat og privat nøkkel

Før vi hopper inn i implementeringen av kryptografiske funksjoner, må vi først lage et sertifikat og en privat nøkkel.

For testformål kan vi bruke disse ressursene:

  • Baeldung.cer
  • Baeldung.p12 (passord = “passord”)

Baeldung.cer er et digitalt sertifikat som bruker den internasjonale X.509 offentlige nøkkelinfrastrukturstandarden, mens Baeldung.p12 er en passordbeskyttet PKCS12 Keystore som inneholder en privat nøkkel.

La oss se hvordan disse kan lastes inn i Java:

Security.addProvider (ny BouncyCastleProvider ()); CertificateFactory certFactory = CertificateFactory .getInstance ("X.509", "BC"); X509Certificate certificate = (X509Certificate) certFactory .generateCertificate (new FileInputStream ("Baeldung.cer")); char [] keystorePassword = "passord" .toCharArray (); char [] keyPassword = "passord" .toCharArray (); KeyStore keystore = KeyStore.getInstance ("PKCS12"); keystore.load (ny FileInputStream ("Baeldung.p12"), keystorePassword); PrivateKey key = (PrivateKey) keystore.getKey ("baeldung", keyPassword);

Først har vi lagt til BouncyCastleProvider som sikkerhetsleverandør dynamisk ved hjelp av addProvider () metode.

Dette kan også gjøres statisk ved å redigere {JAVA_HOME} /jre/lib/security/java.security fil, og legger til denne linjen:

security.provider.N = org.bouncycastle.jce.provider.BouncyCastleProvider

Når leverandøren er riktig installert, har vi opprettet en CertificateFactory objektet ved hjelp av getInstance () metode.

De getInstance () metoden tar to argumenter; sertifikattypen “X.509”, og sikkerhetsleverandøren “BC”.

De certFactory forekomst blir deretter brukt til å generere en X509Sertifikat objekt, via generere sertifikat () metode.

På samme måte har vi opprettet et PKCS12 Keystore-objekt som laste() metoden kalles.

De getKey () metoden returnerer den private nøkkelen tilknyttet et gitt alias.

Merk at en PKCS12 Keystore inneholder et sett med private nøkler, hver private nøkkel kan ha et spesifikt passord, det er derfor vi trenger et globalt passord for å åpne Keystore, og et spesifikt for å hente den private nøkkelen.

Sertifikatet og det private nøkkelparet brukes hovedsakelig i asymmetriske kryptografiske operasjoner:

  • Kryptering
  • Dekryptering
  • Signatur
  • Bekreftelse

4.2 CMS / PKCS7-kryptering og dekryptering

I asymmetrisk krypteringskryptering krever hver kommunikasjon et offentlig sertifikat og en privat nøkkel.

Mottakeren er bundet til et sertifikat som deles offentlig mellom alle avsendere.

Enkelt sagt trenger avsenderen mottakerens sertifikat for å kryptere en melding, mens mottakeren trenger den tilhørende private nøkkelen for å kunne dekryptere den.

La oss ta en titt på hvordan du implementerer en encryptData () funksjon, ved hjelp av et krypteringssertifikat:

offentlig statisk byte [] encryptData (byte [] data, X509Certificate encryptionCertificate) kaster CertificateEncodingException, CMSException, IOException {byte [] encryptedData = null; if (null! = data && null! = encryptionCertificate) {CMSEnvelopedDataGenerator cmsEnvelopedDataGenerator = ny CMSEnvelopedDataGenerator (); JceKeyTransRecipientInfoGenerator jceKey = ny JceKeyTransRecipientInfoGenerator (encryptionCertificate); cmsEnvelopedDataGenerator.addRecipientInfoGenerator (transKeyGen); CMSTypedData msg = ny CMSProcessableByteArray (data); OutputEncryptor encryptor = ny JceCMSContentEncryptorBuilder (CMSAlgorithm.AES128_CBC) .setProvider ("BC"). Build (); CMSEnvelopedData cmsEnvelopedData = cmsEnvelopedDataGenerator .generate (msg, encryptor); encryptedData = cmsEnvelopedData.getEncoded (); } returner encryptedData; }

Vi har opprettet en JceKeyTransRecipientInfoGenerator objekt ved hjelp av mottakerens sertifikat.

Så har vi opprettet en ny CMSEnvelopedDataGenerator objekt og la mottakerinformasjonsgeneratoren inn i den.

Etter det har vi brukt JceCMSContentEncryptorBuilder klasse for å lage en OutputEncrytor objekt, ved hjelp av AES CBC-algoritmen.

Krypteren brukes senere til å generere en CMSEnvelopedData objekt som innkapsler den krypterte meldingen.

Til slutt returneres den kodede representasjonen av konvolutten som en byte-array.

La oss nå se hva implementeringen av decryptData () metoden ser ut som:

offentlig statisk byte [] decryptData (byte [] encryptedData, PrivateKey decryptionKey) kaster CMSException {byte [] decryptedData = null; if (null! = encryptedData && null! = decryptionKey) {CMSEnvelopedData envelopedData = new CMSEnvelopedData (encryptedData); Samlingsmottakere = envelopedData.getRecipientInfos (). GetRecipients (); KeyTransRecipientInformation recipientInfo = (KeyTransRecipientInformation) mottakere.iterator (). Neste (); JceKeyTransRecipient mottaker = ny JceKeyTransEnvelopedRecipient (dekrypteringKey); returner recipientInfo.getContent (mottaker); } returner dekryptert data; }

Først har vi initialisert a CMSEnvelopedData objekt ved hjelp av den krypterte databyte-matrisen, og så har vi hentet alle de tiltenkte mottakerne av meldingen ved hjelp av getRecipients () metode.

Når det er gjort, har vi opprettet en ny JceKeyTransMottaker objekt tilknyttet mottakerens private nøkkel.

De mottakerInfo forekomst inneholder den dekrypterte / innkapslede meldingen, men vi kan ikke hente den med mindre vi har den tilsvarende mottakerens nøkkel.

Til slutt, gitt mottakernøkkelen som argument, getContent () metoden returnerer den rå byte-matrisen ekstrahert fra EnvelopedData denne mottakeren er assosiert med.

La oss skrive en enkel test for å sikre at alt fungerer akkurat som det skal:

String secretMessage = "Mitt passord er 123456Seven"; System.out.println ("Original Message:" + secretMessage); byte [] stringToEncrypt = secretMessage.getBytes (); byte [] encryptedData = encryptData (stringToEncrypt, sertifikat); System.out.println ("Kryptert melding:" + ny streng (encryptedData)); byte [] rawData = decryptData (encryptedData, privateKey); String decryptedMessage = ny streng (rawData); System.out.println ("Dekryptert melding:" + decryptedMessage);

Som et resultat:

Original melding: Passordet mitt er 123456 Seven Kryptert melding: 0  *  H   ... Dekryptert melding: Passordet mitt er 123456Seven

4.3 CMS / PKCS7 signatur og bekreftelse

Signatur og verifisering er kryptografiske operasjoner som validerer ektheten til dataene.

La oss se hvordan du signerer en hemmelig melding ved hjelp av et digitalt sertifikat:

offentlig statisk byte [] signData (byte [] data, X509Certificate SigningCertificate, PrivateKey SigningKey) kaster Unntak {byte [] signertMessage = null; Liste certList = ny ArrayList (); CMSTypedData cmsData = ny CMSProcessableByteArray (data); certList.add (signeringSertifikat); Store certs = nye JcaCertStore (certList); CMSSignedDataGenerator cmsGenerator = ny CMSSignedDataGenerator (); ContentSigner contentSigner = ny JcaContentSignerBuilder ("SHA256withRSA"). Build (signeringKey); cmsGenerator.addSignerInfoGenerator (ny JcaSignerInfoGeneratorBuilder (ny JcaDigestCalculatorProviderBuilder (). setProvider ("BC") .build ()). build (contentSigner, signering Certifikat)); cmsGenerator.addCertificates (certs); CMSSignedData cms = cmsGenerator.generate (cmsData, true); signertMessage = cms.getEncoded (); retur signertMelding; } 

Først har vi innebygd inngangen i en CMSTypedData, så har vi opprettet en ny CMSSignedDataGenerator gjenstand.

Vi har brukt SHA256medRSA som en signaturalgoritme, og vår signeringsnøkkel for å lage en ny ContentSigner gjenstand.

De contentSigner forekomst brukes etterpå, sammen med signeringssertifikatet for å opprette en SigningInfoGenerator gjenstand.

Etter å ha lagt til SignerInfoGenerator og signeringssertifikatet til CMSSignedDataGenerator For eksempel bruker vi endelig generere() metode for å opprette et CMS-signert dataobjekt, som også bærer en CMS-signatur.

Nå som vi har sett hvordan vi skal signere data, la oss se hvordan vi bekrefter signerte data:

offentlig statisk boolsk verifSignedData (byte [] signertData) kaster Unntak {X509Certificate signCert = null; ByteArrayInputStream inputStream = ny ByteArrayInputStream (signertData); ASN1InputStream asnInputStream = ny ASN1InputStream (inputStream); CMSSignedData cmsSignedData = ny CMSSignedData (ContentInfo.getInstance (asnInputStream.readObject ())); SignerInformationStore signers = cmsSignedData.getCertificates (). GetSignerInfos (); SignerInformation signer = signers.getSigners (). Iterator (). Neste (); Samling certCollection = certs.getMatches (signer.getSID ()); X509CertificateHolder certHolder = certCollection.iterator (). Neste (); return signer .verify (new JcaSimpleSignerInfoVerifierBuilder () .build (certHolder)); }

Igjen har vi opprettet en CMSSignedData objekt basert på den signerte databyte-arrayen, så har vi hentet alle underskrivere tilknyttet signaturene ved hjelp av getSignerInfos () metode.

I dette eksemplet har vi bare bekreftet en undertegner, men for generisk bruk er det obligatorisk å gjenta over samlingen av underskrivere som returneres av getSigners () metode og sjekk hver for seg.

Endelig har vi opprettet en SignerInformationVerifier objektet ved hjelp av bygge() metoden og sendte den til bekrefte() metode.

Verifiser () -metoden returnerer ekte hvis det gitte objektet kan bekrefte signaturen på signeringsobjektet.

Her er et enkelt eksempel:

byte [] signertData = signData (rawData, sertifikat, privateKey); Boolsk sjekk = verifSignData (signertData); System.out.println (sjekk);

Som et resultat:

ekte

5. Konklusjon

I denne artikkelen har vi oppdaget hvordan vi bruker BouncyCastle-biblioteket til å utføre grunnleggende kryptografiske operasjoner, for eksempel kryptering og signatur.

I en reell situasjon ønsker vi ofte å signere og kryptere dataene våre. På den måten er det bare mottakeren som er i stand til å dekryptere den ved hjelp av den private nøkkelen, og kontrollere dens autentisitet basert på den digitale signaturen.

Kodebitene kan du finne som alltid på GitHub.


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