📜 ⬆️ ⬇️

Work with PGP digital signatures using the Bouncy Castle Cryptography Library in Java

In this tutorial, we will look at working with OpenPGP in Java using the Bouncy Castle Cryptography Library using web-based development.

image

( link to the source of the scheme )

PGP / OpenPGP


OpenPGP is described in RFC 4880 , and is the most common standard public key cryptographic system for working with digital signatures and message encryption. Supported by many programs and libraries, including free with open source. In particular, it should be noted GnuPG , Kleopatra , a set of utilities for Windows Gpg4win (it includes GnuPG, Kleopatra, etc.), an extension for Thunderbird Enigmail .
For Windows, also pay attention to the commercial package Symantec Endpoint Encryption, also known as PGP Desktop (a trial version is available, which can be used for non-commercial purposes after the expiration date), and other products from Symantec Encryption Family .
')
For details, see: Introduction to Public Key Encryption and PGP , PGP Operation Mechanism .

Bouncy Castle Cryptography Library


The Bouncy Castle Cryptography Library is currently the most widely used cryptographic library for Java, including Android.
This is an open source project (GitHub: github.com/bcgit/bc-java ). Also for our topic of interest is the open source program Portable PGP v. 1.x, written in Java using Bouncy Castle: sourceforge.net/p/ppgp/code

To work with OpenPGP in Bouncy Castle there is a separate org.bouncycastle.openpgp package, with which we will work.

Read public key


Solved problem: read an ASCII-armored Public PGP Key Block (public key) in the form of a String and create an org.bouncycastle.openpgp.PGPPublicKey object representing the public key in BC.

The ASCII-armored format represents the key in a text format that is convenient to use for publishing on the site, sending by e-mail, etc., looks like this:
-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v2.0.22 (GNU/Linux) mQINBFZpf0wBEADo7NZ4Oymw2pVJMfrNEhGwYOT4CGMOTZHQo3rYFrN3yxCsd/xX r8kWLvkKvEFSD0XfQlq6slA9fnOtsRZl4JlmabKC33ZkB1zhV2c78AhhVMrfFi2a 114xHhOHkR4LOv8mAyyRKd5mpuyYpPKcF2670jXxANeqNQCoKKM/dPS1uxGapE9Z GG0GFKvIUqKWJUAv7JqOAxtXbAS7JFMwiH16jL/TeuvKy+0JKvlBM4qxB9S18hi4 GYg1SEATqmeu9E+6alz0L25REYYZZOMja1quF8HywsRY0fSZqCbD+9diP0keAg6z PSDSlgDF4WBUi+c8PoXcRRZq7+XYgky4l8wfGoGnOZKA4GH2SMlNAX8jCIrC7Cpf KPCu6NMY533X735Md6fnWOeuyxz4owPRb6OTt4rYiA3/9vCu/waZ1zZx/wATYORS 1wmOx8ZjogeAPOz6a2PW8hrK0tWbT3ILucC7dxjYfdKHZX2+v3eMGvXFRKss8J5i E5rlJiDnAMoERmIJunh/EJHz4te+RdMy2jWeDQWGzaFyZC92SUo9tAUJk7xez0KX 5sYzhXjFJX/17DwFBTXIOYpFjWUyPaQaP7ByWOjZzhCmQxYRBUJSoxOty1ngkZ+B 7zAZSTXh2mukHukRERG1//2FsiBZeapRhAaFWdWnPDN95bg7L7/qQ5szjQARAQAB tDdWaWt0b3IgQWdleWV2IDxhZ2V5ZXZAaW50ZXJuYXRpb25hbC1hcmJpdHJhdGlv bi5vcmcudWs+iQI/BBMBAgApBQJWaX9MAhsvBQkB4lLUBwsJCAcDAgEGFQgCCQoL BBYCAwECHgECF4AACgkQzjaf2edxc+UywQ/+MfYX5BRzl7iSndhr/vVOEycxZntu 9efMhwQYO87EXT/cNpRMX/u2Wzhx70brzFIenvaYTPpVkYKinKv3iEAauJ1wr5/a PrUxRcfVnBrV8ecWTO6SbNRDePqayst8bBq9rdqnKnpDEv911zk2hjJtCeFDlUkO eiTLgYCVOQ7n0fbN4pjrBGAqDGs2SFjJXAKYJpZqY77P0crHxXCMyukrdj2WxnB7 JyMdmBqViJSo/MCi7SFXqVQFTvJZVBhTQEUO5KNJA6oAYztYKXBE3AybE528m571 p+HhJ2mOnlNaRu5y12RRQJ7qmKvN9uzHz0+mt8rRk3oYWCYImTMwxDpc1mT39QKs 0WS/zp5NTIaa+Hvt7V8GH5hmq67YL+JcxEINZGZ+MZmiSKuvGVuqC8ZJpswumVw5 PHtMEL+0zBxZXN41YyfTudssXbvMrNiGIdlQA7CW/7yMviVR4nyQtFQJwYSL4mva uTQSnihm4Bj6FeYpgGpk1TyIzgtIyaQNSvnlDYFezmN/JsAs+25EP/oxyFLaK+ij WdjVmnsDGS4l5BWH/a8BYPbSnm7YrlYkYCXjTjK1/EeWA/tggApISFptc8YQw0Wp Qw0IwhJVPWDVW8nDrv/1IPhTJgYfIfdmxr32RLhNePEZGZ02zfCP9BrlNKut9/Hn x1yKWjVaWZO8VtU= =XyhJ -----END PGP PUBLIC KEY BLOCK----- 

In this form, the public key can be transferred to a web form. We will write code that can retrieve data from the key on the server, and then, say, return it to the user (public key recognition service) and / or save it in the database. In BC there is a class PGPPublicKey , our task is to create a new object of this class using the input String, and then using the methods provided by this class to get data about the key.

In the org.bouncycastle.openpgp.examples package there is a PGPExampleUtil class, we cannot import it as it is non-public, but we can use its code to create our own similar class. The PGPExampleUtil method we can use as a base:
 /** * A simple routine that opens a key ring file and loads the first available key * suitable for encryption. * * @param input data stream containing the public key data * @return the first public key found. * @throws IOException * @throws PGPException */ static PGPPublicKey readPublicKey(InputStream input) throws IOException, PGPException { PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection( PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator()); // // we just loop through the collection till we find a key suitable for encryption, in the real // world you would probably want to be a bit smarter about this. // Iterator keyRingIter = pgpPub.getKeyRings(); while (keyRingIter.hasNext()) { PGPPublicKeyRing keyRing = (PGPPublicKeyRing)keyRingIter.next(); Iterator keyIter = keyRing.getPublicKeys(); while (keyIter.hasNext()) { PGPPublicKey key = (PGPPublicKey)keyIter.next(); if (key.isEncryptionKey()) { return key; } } } throw new IllegalArgumentException("Can't find encryption key in key ring."); } 

In portablepgp.core.KeyRing, a similar problem is solved as follows:
  public static PGPPublicKeyRing importPublicKey(InputStream iKeyStream) throws IOException { PGPPublicKeyRing newKey = new PGPPublicKeyRing(new ArmoredInputStream(iKeyStream)); pubring = PGPPublicKeyRingCollection.addPublicKeyRing(pubring, newKey); savePubring(); return newKey; } 

Assuming that we need to remove only one (first) PGPPublicKey from PGPPublicKeyRing, we simply use .getPublicKey () and do not use an iterator (at the input of String, the method returns org.bouncycastle.openpgp.PGPPublicKey):
  public static PGPPublicKey importPublicKey (String armoredPublicPGPkeyBlock) throws IOException { InputStream in = new ByteArrayInputStream(armoredPublicPGPkeyBlock.getBytes()); PGPPublicKeyRing pgpPublicKeyRing = new PGPPublicKeyRing(new ArmoredInputStream(in), new JcaKeyFingerprintCalculator()); in.close(); PGPPublicKey pgpPublicKey = pgpPublicKeyRing.getPublicKey(); return pgpPublicKey; } 

Or we can completely copy the readPublicKey method from org.bouncycastle.openpgp.examples.PGPExampleUtil into our class, and add the class with the following method:
  public static PGPPublicKey readPublicKeyFromString (String armoredPublicPGPkeyBlock) throws IOException, PGPException { InputStream in = new ByteArrayInputStream(armoredPublicPGPkeyBlock.getBytes()); PGPPublicKey pgpPublicKey = readPublicKey(in); in.close(); return pgpPublicKey; } 

Key data display


Based on the object's org.bouncycastle.openpgp.PGPPublicKey object, we create a representation of key data that can be read by a person or stored in a database.

For the simplest version of presenting public key data in a readable form, you can use the portablepgp.core.PrintablePGPPublicKey class with the toString () method:
 package portablepgp.core; import java.util.Iterator; import org.bouncycastle.openpgp.PGPPublicKey; /** * * @author Primiano Tucci - http://www.primianotucci.com/ */ public class PrintablePGPPublicKey { PGPPublicKey base; public PrintablePGPPublicKey(PGPPublicKey iBase){ base = iBase; } public PGPPublicKey getPublicKey(){ return base; } @Override public String toString() { StringBuilder outStr = new StringBuilder(); Iterator iter = base.getUserIDs(); outStr.append("[0x"); outStr.append(Integer.toHexString((int)base.getKeyID()).toUpperCase()); outStr.append("] "); while(iter.hasNext()){ outStr.append(iter.next().toString()); outStr.append("; "); } return outStr.toString(); } } 

To store more complete information about the key, create a class PGPPublicKeyData with a constructor that retrieves data from org.bouncycastle.openpgp.PGPPublicKey :
 import com.google.common.base.Splitter; import com.google.gson.Gson; import org.bouncycastle.openpgp.PGPPublicKey; import javax.xml.bind.DatatypeConverter; import java.util.Date; import java.util.Iterator; import java.util.List; public class PGPPublicKeyData { String keyID; String userID; String firstName; String lastName; String userEmail; Date created; Date exp; int bitStrength; String asciiArmored; String fingerprint; public PGPPublicKeyData(PGPPublicKey pgpPublicKey) { // keyID StringBuilder keyIDstrBuilder = new StringBuilder(); keyIDstrBuilder.append("[0x"); keyIDstrBuilder.append(Integer.toHexString((int) pgpPublicKey.getKeyID()).toUpperCase()); keyIDstrBuilder.append("] "); this.keyID = keyIDstrBuilder.toString(); // userID StringBuilder userIDstrBuilder = new StringBuilder(); Iterator userIDsIterator = pgpPublicKey.getUserIDs(); while (userIDsIterator.hasNext()) { userIDstrBuilder.append(userIDsIterator.next().toString()); userIDstrBuilder.append("; "); } this.userID = userIDstrBuilder.toString(); // user's first and last name and email List<String> userIdList = Splitter.on(' ').trimResults().omitEmptyStrings().splitToList(userID); this.firstName = userIdList.get(0); this.lastName = userIdList.get(1); String userEmailDirty = userIdList.get(userIdList.size() - 1); this.userEmail = userEmailDirty.substring( 1, userEmailDirty.length() - 2 ); // fingerprint // [0xE77173E5] this.fingerprint = DatatypeConverter.printHexBinary( pgpPublicKey.getFingerprint() ); // creation date this.created = pgpPublicKey.getCreationTime(); // exp date this.exp = org.apache.commons.lang3.time.DateUtils.addSeconds(created, (int) pgpPublicKey.getValidSeconds()); // Bit strength this.bitStrength = pgpPublicKey.getBitStrength(); } public String toJSON() { Gson gson = new Gson(); return gson.toJson(this); } // ---------- Getters and Setters: } 


To be continued

Source: https://habr.com/ru/post/274779/


All Articles