Bevezetés a Java-val történő BouncyCastle-ba

1. Áttekintés

A BouncyCastle egy Java könyvtár, amely kiegészíti az alapértelmezett Java kriptográfiai kiterjesztést (JCE).

Ebben a bevezető cikkben bemutatjuk, hogyan használhatjuk a BouncyCastle-t kriptográfiai műveletek, például titkosítás és aláírás végrehajtására.

2. Maven konfiguráció

Mielőtt elkezdenénk dolgozni a könyvtárral, hozzá kell adnunk a szükséges függőségeket a mi könyvtárunkhoz pom.xml fájl:

 org.bouncycastle bcpkix-jdk15on 1.58 

Ne feledje, hogy mindig a legfrissebb függőségi verziókat keressük meg a Maven központi adattárában.

3. Állítson be korlátlan erősségű joghatósági házirend-fájlokat

A normál Java telepítés korlátozott a kriptográfiai funkciók erőssége szempontjából, ennek oka az olyan házirendek betiltása, amelyek bizonyos értékeket meghaladó méretű kulcs használatát tiltják pl. 128 az AES esetében.

Ennek a korlátozásnak a leküzdéséhez meg kell tennünk konfigurálja a korlátlan erejű joghatósági házirend-fájlokat.

Ehhez először le kell töltenünk a csomagot erre a linkre kattintva. Ezt követően ki kell bontanunk a tömörített fájlt egy általunk kiválasztott könyvtárba - amely két jar fájlt tartalmaz:

  • local_policy.jar
  • US_export_policy.jar

Végül meg kell keresnünk a {JAVA_HOME} / lib / security mappát, és cserélje ki a meglévő házirend-fájlokat az itt kibontott fájlokra.

Vegye figyelembe, hogy a Java 9-ben már nem kell letöltenünk a házirend fájlok csomagot, beállítva a kriptográfia.politika tulajdonhoz korlátlan elég:

Security.setProperty ("kripto.politika", "korlátlan");

Miután elkészült, ellenőriznünk kell, hogy a konfiguráció megfelelően működik-e:

int maxKeySize = javax.crypto.Cipher.getMaxAllowedKeyLength ("AES"); System.out.println ("Maximális kulcsméret az AES számára:" + maxKeySize);

Ennek eredményeként:

Maximális kulcsméret AES esetén: 2147483647

A kulcs által megadott maximális kulcsméret alapján getMaxAllowedKeyLength () módszerrel nyugodtan kijelenthetjük, hogy a korlátlan erősségű házirendfájlok helyesen lettek telepítve.

Ha a visszaadott érték 128, akkor meg kell győződnünk arról, hogy a fájlokat a JVM-be telepítettük, ahol a kódot futtatjuk.

4. Titkos műveletek

4.1. Tanúsítvány és magánkulcs előkészítése

Mielőtt belekezdenénk a kriptográfiai funkciók megvalósításába, először létre kell hoznunk egy tanúsítványt és egy magánkulcsot.

Teszt célokra felhasználhatjuk ezeket az erőforrásokat:

  • Baeldung.cer
  • Baeldung.p12 (jelszó = “jelszó”)

Baeldung.cer egy digitális tanúsítvány, amely a nemzetközi X.509 nyilvános kulcsú infrastruktúra szabványt használja, míg az Baeldung.p12 egy jelszóval védett PKCS12 kulcstár, amely magánkulcsot tartalmaz.

Nézzük meg, hogyan lehet ezeket betölteni a Java-ba:

Security.addProvider (új BouncyCastleProvider ()); CertificateFactory certFactory = CertificateFactory .getInstance ("X.509", "BC"); X509Certificate tanúsítvány = (X509Certificate) certFactory .generateCertificate (új FileInputStream ("Baeldung.cer")); char [] keystorePassword = "jelszó" .toCharArray (); char [] keyPassword = "jelszó" .toCharArray (); KeyStore kulcstár = KeyStore.getInstance ("PKCS12"); keystore.load (új FileInputStream ("Baeldung.p12"), keystorePassword); PrivateKey kulcs = (PrivateKey) kulcstár.getKey ("baeldung", keyPassword);

Először hozzáadtuk a BouncyCastleProvider mint biztonsági szolgáltató dinamikusan használja a addProvider () módszer.

Ez statikusan is elvégezhető a {JAVA_HOME} /jre/lib/security/java.security fájlt, és hozzáadja ezt a sort:

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

A szolgáltató megfelelő telepítése után létrehoztuk a CertificateFactory objektum a getInstance () módszer.

A getInstance () a módszer két érvet vesz fel; az „X.509” tanúsítványtípus és a „BC” biztonsági szolgáltató.

A certFactory példányt ezt követően egy X509Tanúsítvány objektum, a geneCertificate () módszer.

Ugyanígy létrehoztunk egy PKCS12 Keystore objektumot, amelyen a Betöltés() módszert nevezzük.

A getKey () metódus adja vissza az adott álnévhez társított titkos kulcsot.

Ne feledje, hogy a PKCS12 kulcstár tartalmaz egy sor magánkulcsot, mindegyik magánkulcsnak lehet saját jelszava, ezért globális jelszóra van szükségünk a kulcstár megnyitásához, és egy speciálisra a privát kulcs lekéréséhez.

A tanúsítványt és a magánkulcs-párt elsősorban aszimmetrikus kriptográfiai műveleteknél használják:

  • Titkosítás
  • Dekódolás
  • Aláírás
  • Igazolás

4.2 CMS / PKCS7 titkosítás és visszafejtés

Az aszimmetrikus titkosítási kriptográfiában minden kommunikációhoz nyilvános tanúsítvány és privát kulcs szükséges.

A címzett egy tanúsítványhoz van kötve, amelyet nyilvánosan megosztanak az összes feladó között.

Egyszerűen fogalmazva: a feladónak a címzett tanúsítványára van szüksége az üzenet titkosításához, míg a címzettnek a hozzárendelt magánkulcsra van szüksége a visszafejtéshez.

Vessünk egy pillantást a encryptData () funkció titkosítási tanúsítvánnyal:

public static byte [] encryptData (byte [] data, X509Certificate encryptionCertificate) dobja a CertificateEncodingException, CMSException, IOException {byte [] encryptedData = null; if (null! = data && null! = encryptionCertificate) {CMSEnvelopedDataGenerator cmsEnvelopedDataGenerator = új CMSEnvelopedDataGenerator (); JceKeyTransRecipientInfoGenerator jceKey = új JceKeyTransRecipientInfoGenerator (encryptionCertificate); cmsEnvelopedDataGenerator.addRecipientInfoGenerator (transKeyGen); CMSTypedData msg = new CMSProcessableByteArray (adatok); OutputEncryptor encryptor = új JceCMSContentEncryptorBuilder (CMSAlgorithm.AES128_CBC) .setProvider ("BC"). Build (); CMSEnvelopedData cmsEnvelopedData = cmsEnvelopedDataGenerator .generate (msg, titkosító); encryptedData = cmsEnvelopedData.getEncoded (); } return encryptedData; }

Hoztunk létre egy JceKeyTransRecipientInfoGenerator objektumot a címzett tanúsítványával.

Aztán létrehoztunk egy újat CMSEnvelopedDataGenerator objektumot, és hozzáadta a címzett információgenerátort.

Ezt követően felhasználtuk a JceCMSContentEncryptorBuilder osztály létrehozásához OutputEncrytor objektum, az AES CBC algoritmus használatával.

A titkosítót később használják a CMSEnvelopedData a titkosított üzenetet befogadó objektum.

Végül a boríték kódolt ábrázolása bájt tömbként kerül visszaadásra.

Most nézzük meg, mi a decryptData () A módszer így néz ki:

public static byte [] decryptData (byte [] encryptedData, PrivateKey decryptionKey) dobja a CMSException {byte [] decryptedData = null; if (null! = encryptedData && null! = decryptionKey) {CMSEnvelopedData wrapopedData = új CMSEnvelopedData (encryptedData); Gyűjtemény címzettjei = burkoltData.getRecipientInfos (). GetRecipients (); KeyTransRecipientInformation recipientInfo = (KeyTransRecipientInformation) recipients.iterator (). Next (); JceKeyTransRecipient recipiens = új JceKeyTransEnvelopedRecipient (decryptionKey); return recipientInfo.getContent (címzett); } return decryptedData; }

Először inicializáltuk a CMSEnvelopedData objektumot a titkosított adatbájt tömb felhasználásával, majd az üzenet összes címzettjét megkaptuk a getRecipients () módszer.

Miután elkészült, létrehoztunk egy újat JceKeyTransRecipient a címzett privát kulcsához társított objektum.

A recipientInfo példány tartalmazza a visszafejtett / beágyazott üzenetet, de csak akkor tudjuk letölteni, ha megvan a megfelelő címzett kulcsa.

Végül, a címzett kulcsát argumentumként megadva, a getContent () metódus adja vissza a EnvelopedData ez a címzett társítva van.

Írjunk egy egyszerű tesztet, hogy megbizonyosodhassunk arról, hogy minden pontosan úgy működik, ahogy kellene:

String secretMessage = "A jelszavam 123456Seven"; System.out.println ("Eredeti üzenet:" + secretMessage); bájt [] stringToEncrypt = secretMessage.getBytes (); byte [] encryptedData = encryptData (stringToEncrypt, tanúsítvány); System.out.println ("Titkosított üzenet:" + új karakterlánc (encryptedData)); bájt [] rawData = decryptData (encryptedData, privateKey); String decryptedMessage = új karakterlánc (rawData); System.out.println ("Decrypted Message:" + decryptedMessage);

Ennek eredményeként:

Eredeti üzenet: A jelszavam 123456Seven titkosított üzenet: 0  *  H   ... Dekódolt üzenet: A jelszavam 123456Seven

4.3 CMS / PKCS7 aláírás és ellenőrzés

Az aláírás és az ellenőrzés rejtjelezési műveletek, amelyek ellenőrzik az adatok hitelességét.

Nézzük meg, hogyan írhatunk alá titkos üzenetet digitális tanúsítvánnyal:

public static byte [] signData (byte [] data, X509Certificate signingCertificate, PrivateKey signingKey) dobja a {byte [] Signed Message = null kivételt; List certList = new ArrayList (); CMSTypedData cmsData = új CMSProcessableByteArray (adatok); certList.add (signingCertificate); Store tanúsítványok = új JcaCertStore (certList); CMSSignedDataGenerator cmsGenerator = új CMSSignedDataGenerator (); ContentSigner contentSigner = new JcaContentSignerBuilder ("SHA256withRSA"). Build (signingKey); cmsGenerator.addSignerInfoGenerator (új JcaSignerInfoGeneratorBuilder (új JcaDigestCalculatorProviderBuilder (). setProvider ("BC") .build ()). build (contentSigner, signingCertificate)); cmsGenerator.addCertificates (tanúsítványok); CMSSignedData cms = cmsGenerator.generate (cmsData, true); signMessage = cms.getEncoded (); visszaküldés aláírvaÜzenet; } 

Először beágyazottuk a bemenetet a CMSTypedData, akkor létrehoztunk egy újat CMSSignedDataGenerator tárgy.

Használtuk SHA256RSA-val mint aláírási algoritmust, és az aláíró kulcsunkat egy új létrehozásához ContentSigner tárgy.

A contentSigner példányt használják azután, az aláíró tanúsítvánnyal együtt a SigningInfoGenerator tárgy.

Miután hozzáadta a SignerInfoGenerator és az aláíró tanúsítvány a CMSSignedDataGenerator például végre használjuk a generál() módszer CMS aláírt adatobjektum létrehozására, amely CMS aláírást is hordoz.

Most, hogy láttuk, hogyan kell aláírni az adatokat, nézzük meg, hogyan ellenőrizheti az aláírt adatokat:

public static boolean verifSignedData (byte [] SignData) dobja a Kivételt {X509Certificate signCert = null; ByteArrayInputStream inputStream = új ByteArrayInputStream (SignedData); ASN1InputStream asnInputStream = új ASN1InputStream (inputStream); CMSSignedData cmsSignedData = új CMSSignedData (ContentInfo.getInstance (asnInputStream.readObject ())); SignerInformationStore signers = cmsSignedData.getCertificates (). GetSignerInfos (); SignerInformation signer = signers.getSigners (). Iterator (). Next (); Gyűjtemény certCollection = certs.getMatches (signer.getSID ()); X509CertificateHolder certHolder = certCollection.iterator (). Next (); visszatérő aláíró .verify (új JcaSimpleSignerInfoVerifierBuilder () .build (certHolder)); }

Ismét létrehoztunk egy CMSSignedData objektumot az aláírt adatbájt tömbünk alapján, akkor az aláírásokhoz társított összes aláírót a getSignerInfos () módszer.

Ebben a példában csak egy aláírót ellenőriztünk, de általános használat esetén kötelező az ismétlés az aláírók gyűjteménye felett, amelyet a getSigners () módszert, és mindegyiket külön-külön ellenőrizze.

Végül létrehoztuk a SignerInformationVerifier objektum a épít() módszert, és átadta a igazolni () módszer.

Az Verify () metódus visszatér igaz ha az adott objektum sikeresen ellenőrizheti az aláírást az aláíró objektumon.

Íme egy egyszerű példa:

bájt [] SignedData = signData (rawData, tanúsítvány, privateKey); Logikai ellenőrzés = verifSignData (SignedData); System.out.println (ellenőrzés);

Ennek eredményeként:

igaz

5. Következtetés

Ebben a cikkben rájöttünk, hogyan lehet a BouncyCastle könyvtárat használni az alapvető kriptográfiai műveletek, például a titkosítás és az aláírás elvégzésére.

Valós helyzetben gyakran szeretnénk aláírni, majd titkosítani az adatainkat, így csak a címzett képes visszafejteni a privát kulcs segítségével, és a digitális aláírás alapján ellenőrizheti hitelességét.

A kódrészletek, mint mindig, a GitHubon találhatók.