DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Trending

  • Navigating Double and Triple Extortion Tactics
  • Developers Beware: Slopsquatting and Vibe Coding Can Increase Risk of AI-Powered Attacks
  • What Is Plagiarism? How to Avoid It and Cite Sources
  • Operational Principles, Architecture, Benefits, and Limitations of Artificial Intelligence Large Language Models

Easy OpenPGP With PGPainless

OpenPGP has the reputation of being hard to use. This is especially true for the Java ecosystem. That's where PGPainless comes in.

By 
Paul Schaub user avatar
Paul Schaub
·
Updated Oct. 14, 22 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
3.3K Views

Join the DZone community and get the full member experience.

Join For Free

When it comes to encryption, Bouncy Castle is probably the most popular library in the Java ecosystem. It has great algorithm support and is actively maintained.

However, when trying to use it to add OpenPGP support to your application, you quickly find yourself digging through StackOverflow posts with walls of hard-to-read source code. To even create a simple encrypted, signed message, you are supposed to wrap at least three different OutputStreams obtained from different Factories, each initialized with tons of obscure parameters such as buffer sizes and Provider classes.

The OpenPGP protocol has a mechanism of signaling algorithm support through fields on the recipients' public key. Are you supposed to extract those yourself? How are you supposed to know which algorithms are safe?

This is exactly the situation I found myself in four years ago. The Bouncy Castle OpenPGP API is mighty and powerful, but unfortunately very low-level and requires you to do the heavy lifting yourself. This is okay-ish if you are familiar with the OpenPGP protocol, but you might not be an expert and merely want to get the job done. Leaving all the security-critical setup work to the consumer of the API is a bad idea.

That's why I decided to create PGPainless. Originally, I was looking for other alternatives and stumbled across a library called bouncy-gpg, butI quickly figured that it was not suiting my needs and decided to go my own way. Nevertheless, bouncy-gpg heavily influenced PGPainless' development, especially in the early phases.

PGPainless internally makes use of Bouncy Castle but hides away much of the complexity by making use of the builder pattern, but on a higher abstraction level than Bouncy Castle. It gets you the job done as quickly as possible, and, if you do not provide specifics such as encryption algorithms, it chooses safe and secure defaults for you.

Especially when it comes to signature verification, many posts on StackOverflow (and even Bouncy Castle's own examples!!) merely show you how to check the correctness of a signature. They do not discuss checking a signature for validity. A signature might be cryptographically correct while being invalid due to a revoked signing key, for example. There is a whole suite of checks that needs to be performed to check if a signature really is valid at a certain reference time. PGPainless performs those checks for you.

Now let's check out some examples, shall we?

Let's start with the first thing every user probably wants to do; Generate a fresh OpenPGP key:

Java
 
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
  .modernKeyRing("Romeo <romeo@montague.lit>", "p4ssw0rd");


The result is a password-protected OpenPGP key that makes use of modern EdDSA and XDH subkeys. The API abstracted away all the annoying stuff such as which algorithms to use for the keys, which algorithm preferences (hash-, symmetric- and compression algorithms) to set, and, of course, binding the keys and user-id together using binding signatures. Meanwhile, there is a more flexible API PGPainless.buildKeyRing() which allows the user to change all those parameters to their liking.

Now let's sign and encrypt a message:

Java
 
// We generated our key in the previous example
PGPSecretKeyRing secretKey = ...;
SecretKeyRingProtector protector = SecretKeyRingProtector
  .unlockAnyKeyWith(Passphrase.fromPassword("p4ssw0rd"));

// Extract our own public key certificate
PGPPublicKeyRing certificate = PGPainless.extractCertificate(secretKey);
// Read Juliets public key certificate from somewhere
PGPPublicKeyRing julietsCertificate = PGPainless.readKeyRing()
  .publicKeyRing(julietsCertInputStream);

// The message we want to encrypt and sign
InputStream plaintextIn = ...;
// The destination to where we want to write the ciphertext
OutputStream ciphertextOut = ...; // e.g. new ByteArrayOutputStream();
// Set up the stream
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign()
  .onOutputStream(ciphertextOut)
  .withOptions(ProducerOptions.signAndEncrypt(
     new EncryptionOptions()
       .addRecipient(certificate)
       .addRecipient(julietsCertificate),
     new SigningOptions()
       .addInlineSignature(protector, secretKey)
     )
  );
// Encrypt and sign
Streams.pipeAll(plaintextIn, encryptionStream);
encryptionStream.close();

// Information about the encryption (algorithms, detached signatures etc.)
EncryptionResult result = encryptionStream.getResult();


This is still not a one-liner, but it is a huge improvement compared to what you'd otherwise need to do when using Bouncy Castle directly.

Verification and decryption are done in a similar way. If you follow along at home, note that we need Juliet's secret key to decrypt the message and Romeo's certificate to verify the signature.

Java
 
// Set up the stream
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify()
  .onInputStream(encryptedInputStream)
  .withOptions(new ConsumerOptions()
     .addDecryptionKey(julietsSecretKey, secretKeyProtector)
     .addVerificationCert(romeosCertificate)
  );

// Decrypt and verify the data
Streams.pipeAll(decryptionStream, outputStream);
decryptionStream.close();

// Result contains information like signature status etc.
OpenPgpMetadata metadata = decryptionStream.getResult();


Internally, PGPainless parses the encrypted message, sets up all the various wrapped streams, checks used algorithms against a sane Policy, decrypts the data, does integrity checks, and verifies the signature correctness *and* validity by evaluating Romeo's certificate.

The API is capable of way more though, such as symmetric encryption using a passphrase, creating signatures on other users' certificates, modifying keys (adding user-ids and subkeys, changing passphrases, revoking, and expiring keys and user-ids...), and so on. There is an example package that demonstrates many of those use cases.

There is one more thing I want to touch on. The Stateless OpenPGP Protocol is a draft for a standardized OpenPGP command line interface. The draft defines common operations such as generating keys, encrypting/decrypting messages, creating and verifying signatures and so on.

PGPainless provides both a programmatic adaption of this API, as well as a CLI app. This means that if your app needs to do basic OpenPGP operations, you can simply depend on a module called sop-java which defines the programmatic SOP interface (here is a guide). This interface can be implemented by any OpenPGP library, but PGPainless provides its own implementation through the module pgpainless-sop. That way you can benefit from a super simple-to-use OpenPGP API while not being locked in to use PGPainless.

If you are developing an OpenPGP library for the Java ecosystem, please consider creating an implementation of sop-java with it, as this will not only allow your library to be plugged into projects that happen to use sop-java, but also allows your implementation to be plugged into Sequoia-PGP's OpenPGP Interoperability Test Suite, so you can benefit from a great number of test vectors to uncover bugs and interop issues.

Lastly, this is a free software project, meaning all the code is available under the Apache 2.0 license. Development is done both on GitHub and on Codeberg. If you find this project useful, please consider spreading the word and contributing.

Bouncy Castle (cryptography)

Opinions expressed by DZone contributors are their own.

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!