OpenPGP with Bouncy Castle in Java: Complete Guide

Introduction to OpenPGP

OpenPGP (Pretty Good Privacy) is an encryption standard (RFC 4880) that provides cryptographic privacy and authentication for data communication. Bouncy Castle provides a comprehensive Java API for OpenPGP operations including key generation, encryption, decryption, signing, and verification.


System Architecture Overview

OpenPGP System Architecture
├── Key Management
│   ├── Key Generation (RSA/DSA/ECDSA/EdDSA)
│   ├── Key Rings (Public/Private)
│   ├── Key Servers (HKP/LDAP)
│   └── Key Certificates (User IDs, Signatures)
├── Encryption Operations
│   ├── Hybrid Encryption (AES + RSA/ElGamal)
│   ├── Compression (ZIP/ZLIB/BZip2)
│   ├── Radix-64 Encoding (ASCII Armor)
│   └── File/Stream Processing
└── Digital Signatures
├── Cleartext Signatures
├── Detached Signatures
├── Signature Verification
└── Timestamping

Core Implementation

1. Maven Dependencies

<properties>
<bouncycastle.version>1.78</bouncycastle.version>
<pgp.version>2.1.0</pgp.version>
</properties>
<dependencies>
<!-- Bouncy Castle Core -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<!-- Bouncy Castle OpenPGP Module -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<!-- Bouncy Castle PKIX/CMS/EAC/PKCS/OCSP/TSP/etc. -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<!-- Apache Commons IO for utilities -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.15.0</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.1</version>
<scope>test</scope>
</dependency>
</dependencies>

2. PGP Key Generation Service

package com.pgp.bouncycastle;
import org.bouncycastle.bcpg.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.bouncycastle.openpgp.operator.bc.*;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.springframework.stereotype.Service;
import java.io.*;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.time.Instant;
import java.util.*;
@Service
public class PGPKeyGenerationService {
static {
Security.addProvider(new BouncyCastleProvider());
}
private static final int RSA_KEY_SIZE = 4096;
private static final int DEFAULT_VALIDITY_DAYS = 730; // 2 years
/**
* Generate RSA key pair for PGP
*/
public PGPKeyPair generateRSAKeyPair(int keySize, long keyId) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", "BC");
keyGen.initialize(keySize);
KeyPair keyPair = keyGen.generateKeyPair();
return new JcaPGPKeyPair(
PublicKeyAlgorithmTags.RSA_GENERAL,
keyPair,
new Date()
);
}
/**
* Generate ECDSA key pair for PGP (NIST P-256)
*/
public PGPKeyPair generateECDSAKeyPair() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA", "BC");
keyGen.initialize(new ECGenParameterSpec("prime256v1"));
KeyPair keyPair = keyGen.generateKeyPair();
return new JcaPGPKeyPair(
PublicKeyAlgorithmTags.ECDSA,
keyPair,
new Date()
);
}
/**
* Generate EdDSA key pair (Ed25519)
*/
public PGPKeyPair generateEdDSAKeyPair() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("Ed25519", "BC");
KeyPair keyPair = keyGen.generateKeyPair();
return new JcaPGPKeyPair(
PublicKeyAlgorithmTags.EDDSA,
keyPair,
new Date()
);
}
/**
* Generate complete PGP key ring with subkeys
*/
public PGPKeyRingGenerator generateKeyRing(String identity,
char[] passphrase,
int keySize,
int subKeySize) throws Exception {
// Generate master key (for certification and signatures)
PGPKeyPair masterKey = generateRSAKeyPair(keySize, 0);
// Generate subkey (for encryption)
PGPKeyPair subKey = generateRSAKeyPair(subKeySize, 0);
// Create user ID
PGPSignatureSubpacketGenerator hashedPackets = new PGPSignatureSubpacketGenerator();
hashedPackets.setKeyFlags(false, KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA);
hashedPackets.setPreferredSymmetricAlgorithms(false, new int[]{
SymmetricKeyAlgorithmTags.AES_256,
SymmetricKeyAlgorithmTags.AES_192,
SymmetricKeyAlgorithmTags.AES_128
});
hashedPackets.setPreferredHashAlgorithms(false, new int[]{
HashAlgorithmTags.SHA512,
HashAlgorithmTags.SHA384,
HashAlgorithmTags.SHA256
});
hashedPackets.setPreferredCompressionAlgorithms(false, new int[]{
CompressionAlgorithmTags.ZLIB,
CompressionAlgorithmTags.BZIP2,
CompressionAlgorithmTags.ZIP
});
hashedPackets.setFeature(false, new byte[]{(byte)0x01}); // Modification detection
hashedPackets.setKeyExpirationTime(false, DEFAULT_VALIDITY_DAYS * 24 * 60 * 60);
// Create certification signature
PGPSignatureSubpacketGenerator unhashedPackets = new PGPSignatureSubpacketGenerator();
// Create key ring generator
PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(
PGPSignature.POSITIVE_CERTIFICATION,
masterKey,
identity,
BcPGPDigestCalculatorProviderBuilder.SHA256,
hashedPackets.generate(),
unhashedPackets.generate(),
new BcPGPContentSignerBuilder(masterKey.getPublicKey().getAlgorithm(), 
HashAlgorithmTags.SHA256),
new BcPBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256)
.build(passphrase)
);
// Add encryption subkey
PGPSignatureSubpacketGenerator subHashedPackets = new PGPSignatureSubpacketGenerator();
subHashedPackets.setKeyFlags(false, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE);
subHashedPackets.setKeyExpirationTime(false, DEFAULT_VALIDITY_DAYS * 24 * 60 * 60);
keyRingGen.addSubKey(subKey, subHashedPackets.generate(), null);
return keyRingGen;
}
/**
* Export public key ring to file
*/
public void exportPublicKeyRing(PGPKeyRingGenerator keyRingGen, 
String filename) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filename)) {
PGPKeyRing keyRing = keyRingGen.generatePublicKeyRing();
keyRing.encode(fos);
}
}
/**
* Export secret key ring to file (encrypted)
*/
public void exportSecretKeyRing(PGPKeyRingGenerator keyRingGen,
String filename) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filename)) {
PGPKeyRing keyRing = keyRingGen.generateSecretKeyRing();
keyRing.encode(fos);
}
}
/**
* Export ASCII-armored public key
*/
public void exportArmoredPublicKey(PGPKeyRingGenerator keyRingGen,
String filename) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filename);
ArmoredOutputStream aos = new ArmoredOutputStream(fos)) {
PGPKeyRing keyRing = keyRingGen.generatePublicKeyRing();
keyRing.encode(aos);
}
}
/**
* Load public key ring from file
*/
public PGPPublicKeyRing loadPublicKeyRing(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
return new PGPPublicKeyRing(fis);
}
}
/**
* Load secret key ring from file
*/
public PGPSecretKeyRing loadSecretKeyRing(String filename) throws IOException {
try (FileInputStream fis = new FileInputStream(filename)) {
return new PGPSecretKeyRing(fis);
}
}
/**
* Extract public key from secret key
*/
public PGPPublicKey extractPublicKey(PGPSecretKey secretKey) {
return secretKey.getPublicKey();
}
/**
* Find encryption key in key ring
*/
public PGPPublicKey findEncryptionKey(PGPPublicKeyRing keyRing) {
Iterator<PGPPublicKey> keys = keyRing.getPublicKeys();
while (keys.hasNext()) {
PGPPublicKey key = keys.next();
if (key.isEncryptionKey()) {
return key;
}
}
return null;
}
/**
* Find signing key in key ring
*/
public PGPPublicKey findSigningKey(PGPPublicKeyRing keyRing) {
Iterator<PGPPublicKey> keys = keyRing.getPublicKeys();
while (keys.hasNext()) {
PGPPublicKey key = keys.next();
if (key.isMasterKey()) {
return key;
}
}
return null;
}
/**
* Add user ID to existing key ring
*/
public PGPSecretKeyRing addUserID(PGPSecretKeyRing secretKeyRing,
String newUserID,
char[] passphrase) throws Exception {
PGPSecretKey masterKey = secretKeyRing.getSecretKey();
// Create certification signature
PGPSignatureGenerator sigGen = new PGPSignatureGenerator(
new BcPGPContentSignerBuilder(
masterKey.getPublicKey().getAlgorithm(),
HashAlgorithmTags.SHA512
)
);
sigGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterKey.extractPrivateKey(
new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProviderBuilder().build())
.build(passphrase)
));
List<PGPSignature> certifications = new ArrayList<>();
certifications.add(sigGen.generateCertification(newUserID, masterKey.getPublicKey()));
// Build new key ring
return PGPSecretKeyRingBuilder.replacePublicSubkeys(
secretKeyRing, masterKey.getPublicKey(), certifications, null
);
}
/**
* Revoke key
*/
public PGPPublicKeyRing revokeKey(PGPSecretKeyRing secretKeyRing,
char[] passphrase,
String reason) throws Exception {
PGPSecretKey masterKey = secretKeyRing.getSecretKey();
// Create revocation signature
PGPSignatureGenerator sigGen = new PGPSignatureGenerator(
new BcPGPContentSignerBuilder(
masterKey.getPublicKey().getAlgorithm(),
HashAlgorithmTags.SHA512
)
);
sigGen.init(PGPSignature.KEY_REVOCATION, masterKey.extractPrivateKey(
new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProviderBuilder().build())
.build(passphrase)
));
// Add revocation reason
PGPSignatureSubpacketGenerator subpackets = new PGPSignatureSubpacketGenerator();
subpackets.setRevocationReason(false, 
RevocationReasonTags.KEY_SUPERSEDED, reason);
sigGen.setHashedSubpackets(subpackets.generate());
PGPSignature revocation = sigGen.generateCertification(masterKey.getPublicKey());
// Create revoked key ring
List<PGPPublicKey> keys = new ArrayList<>();
Iterator<PGPPublicKey> pubKeys = secretKeyRing.getPublicKeys();
while (pubKeys.hasNext()) {
PGPPublicKey key = pubKeys.next();
if (key.isMasterKey()) {
keys.add(PGPPublicKey.addCertification(key, revocation));
} else {
keys.add(key);
}
}
return new PGPPublicKeyRing(keys);
}
/**
* Key information class
*/
public static class KeyInfo {
private String keyId;
private String fingerprint;
private int algorithm;
private int keySize;
private Date creationTime;
private Date expirationTime;
private boolean isMasterKey;
private boolean isEncryptionKey;
private List<String> userIds;
private List<String> subKeyIds;
// getters and setters
}
/**
* Get key information
*/
public KeyInfo getKeyInfo(PGPPublicKey key) {
KeyInfo info = new KeyInfo();
info.setKeyId(Long.toHexString(key.getKeyID()).toUpperCase());
info.setFingerprint(hexEncode(key.getFingerprint()));
info.setAlgorithm(key.getAlgorithm());
info.setKeySize(key.getBitStrength());
info.setCreationTime(key.getCreationTime());
info.setExpirationTime(key.getValidSeconds() > 0 ? 
new Date(key.getCreationTime().getTime() + key.getValidSeconds() * 1000L) : null);
info.setMasterKey(key.isMasterKey());
info.setEncryptionKey(key.isEncryptionKey());
Iterator<String> userIds = key.getUserIDs();
List<String> userIdList = new ArrayList<>();
while (userIds.hasNext()) {
userIdList.add(userIds.next());
}
info.setUserIds(userIdList);
return info;
}
private String hexEncode(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X", b & 0xFF));
}
return sb.toString();
}
}

3. PGP Encryption Service

package com.pgp.bouncycastle;
import org.bouncycastle.bcpg.*;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.bc.*;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.springframework.stereotype.Service;
import java.io.*;
import java.security.SecureRandom;
import java.util.*;
@Service
public class PGPEncryptionService {
private static final int BUFFER_SIZE = 1 << 16; // 64KB
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
/**
* Encrypt file for multiple recipients
*/
public void encryptFile(InputStream in, OutputStream out,
List<PGPPublicKey> encryptionKeys,
boolean armor,
boolean withIntegrityCheck) throws Exception {
if (armor) {
out = new ArmoredOutputStream(out);
}
try {
// Create encrypted data generator
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256)
.setWithIntegrityPacket(withIntegrityCheck)
.setSecureRandom(SECURE_RANDOM)
);
// Add recipients
for (PGPPublicKey key : encryptionKeys) {
encGen.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(key));
}
OutputStream encryptedOut = encGen.open(out, new byte[BUFFER_SIZE]);
// Compress data
PGPCompressedDataGenerator compressGen = new PGPCompressedDataGenerator(
CompressionAlgorithmTags.ZLIB
);
OutputStream compressOut = compressGen.open(encryptedOut, new byte[BUFFER_SIZE]);
// Create literal data
PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
OutputStream literalOut = literalGen.open(
compressOut,
PGPLiteralData.BINARY,
PGPLiteralData.CONSOLE,
new Date(),
new byte[BUFFER_SIZE]
);
// Copy data
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = in.read(buffer)) > 0) {
literalOut.write(buffer, 0, len);
}
// Close in reverse order
literalOut.close();
compressGen.close();
encryptedOut.close();
} finally {
if (armor) {
out.close();
}
}
}
/**
* Encrypt with compression and ASCII armor
*/
public void encryptWithOptions(InputStream in, OutputStream out,
PGPPublicKey encryptionKey,
int compressionAlgorithm,
int symmetricAlgorithm,
boolean armor) throws Exception {
encryptFile(in, out, Collections.singletonList(encryptionKey), 
armor, true);
}
/**
* Decrypt file
*/
public void decryptFile(InputStream in, OutputStream out,
PGPSecretKeyRing secretKeyRing,
char[] passphrase) throws Exception {
// Parse encrypted data
PGPObjectFactory pgpFactory = new PGPObjectFactory(
PGPUtil.getDecoderStream(in),
new BcKeyFingerprintCalculator()
);
Object obj = pgpFactory.nextObject();
// Handle encrypted data
PGPEncryptedDataList encList;
if (obj instanceof PGPEncryptedDataList) {
encList = (PGPEncryptedDataList) obj;
} else {
encList = (PGPEncryptedDataList) pgpFactory.nextObject();
}
// Find matching secret key
Iterator<PGPPublicKeyEncryptedData> it = encList.getEncryptedDataObjects();
PGPPrivateKey privateKey = null;
PGPPublicKeyEncryptedData encData = null;
while (it.hasNext()) {
encData = it.next();
PGPSecretKey secretKey = secretKeyRing.getSecretKey(encData.getKeyID());
if (secretKey != null) {
// Decrypt private key
privateKey = secretKey.extractPrivateKey(
new BcPBESecretKeyDecryptorBuilder(
new BcPGPDigestCalculatorProviderBuilder().build()
).build(passphrase)
);
break;
}
}
if (privateKey == null) {
throw new PGPException("No matching secret key found");
}
// Decrypt data
InputStream clearData = encData.getDataStream(
new BcPublicKeyDataDecryptorFactory(privateKey)
);
// Parse decrypted data
PGPObjectFactory clearFactory = new PGPObjectFactory(
clearData,
new BcKeyFingerprintCalculator()
);
obj = clearFactory.nextObject();
// Handle compression
if (obj instanceof PGPCompressedData) {
PGPCompressedData compData = (PGPCompressedData) obj;
InputStream compIn = compData.getDataStream();
clearFactory = new PGPObjectFactory(
compIn,
new BcKeyFingerprintCalculator()
);
obj = clearFactory.nextObject();
}
// Handle literal data
if (obj instanceof PGPLiteralData) {
PGPLiteralData literalData = (PGPLiteralData) obj;
InputStream literalIn = literalData.getDataStream();
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = literalIn.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
} else if (obj instanceof PGPOnePassSignatureList) {
throw new PGPException("Encrypted message contains signed data - use verification method");
} else {
throw new PGPException("Unknown PGP object type");
}
// Check integrity
if (encData.isIntegrityProtected() && !encData.verify()) {
throw new PGPException("Integrity check failed");
}
out.close();
}
/**
* Encrypt with password (symmetric)
*/
public void encryptWithPassword(InputStream in, OutputStream out,
char[] password,
boolean armor) throws Exception {
if (armor) {
out = new ArmoredOutputStream(out);
}
try {
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256)
.setWithIntegrityPacket(true)
.setSecureRandom(SECURE_RANDOM)
);
encGen.addMethod(new BcPGPBEEncryptedDataEncryptorBuilder(
password, SymmetricKeyAlgorithmTags.AES_256)
.setSecureRandom(SECURE_RANDOM)
.build()
);
OutputStream encryptedOut = encGen.open(out, new byte[BUFFER_SIZE]);
// Compress and write data
PGPCompressedDataGenerator compressGen = new PGPCompressedDataGenerator(
CompressionAlgorithmTags.ZLIB
);
OutputStream compressOut = compressGen.open(encryptedOut, new byte[BUFFER_SIZE]);
PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
OutputStream literalOut = literalGen.open(
compressOut,
PGPLiteralData.BINARY,
PGPLiteralData.CONSOLE,
new Date(),
new byte[BUFFER_SIZE]
);
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = in.read(buffer)) > 0) {
literalOut.write(buffer, 0, len);
}
literalOut.close();
compressGen.close();
encryptedOut.close();
} finally {
if (armor) {
out.close();
}
}
}
/**
* Decrypt with password (symmetric)
*/
public void decryptWithPassword(InputStream in, OutputStream out,
char[] password) throws Exception {
PGPObjectFactory pgpFactory = new PGPObjectFactory(
PGPUtil.getDecoderStream(in),
new BcKeyFingerprintCalculator()
);
Object obj = pgpFactory.nextObject();
PGPEncryptedDataList encList;
if (obj instanceof PGPEncryptedDataList) {
encList = (PGPEncryptedDataList) obj;
} else {
encList = (PGPEncryptedDataList) pgpFactory.nextObject();
}
Iterator<PGPEncryptedData> it = encList.getEncryptedDataObjects();
PGPBEEncryptedData encData = (PGPBEEncryptedData) it.next();
InputStream clearData = encData.getDataStream(
new BcPBEDataDecryptorFactory(
password,
new BcPGPDigestCalculatorProviderBuilder().build()
)
);
PGPObjectFactory clearFactory = new PGPObjectFactory(
clearData,
new BcKeyFingerprintCalculator()
);
obj = clearFactory.nextObject();
if (obj instanceof PGPCompressedData) {
PGPCompressedData compData = (PGPCompressedData) obj;
InputStream compIn = compData.getDataStream();
clearFactory = new PGPObjectFactory(
compIn,
new BcKeyFingerprintCalculator()
);
obj = clearFactory.nextObject();
}
if (obj instanceof PGPLiteralData) {
PGPLiteralData literalData = (PGPLiteralData) obj;
InputStream literalIn = literalData.getDataStream();
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = literalIn.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
}
if (encData.isIntegrityProtected() && !encData.verify()) {
throw new PGPException("Integrity check failed");
}
out.close();
}
}

4. PGP Signing Service

package com.pgp.bouncycastle;
import org.bouncycastle.bcpg.*;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.operator.bc.*;
import org.springframework.stereotype.Service;
import java.io.*;
import java.security.SecureRandom;
import java.util.Date;
@Service
public class PGPSigningService {
private static final int BUFFER_SIZE = 1 << 16;
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
/**
* Create cleartext signature
*/
public void signCleartext(InputStream in, OutputStream out,
PGPSecretKey secretKey,
char[] passphrase) throws Exception {
PGPPrivateKey privateKey = secretKey.extractPrivateKey(
new BcPBESecretKeyDecryptorBuilder(
new BcPGPDigestCalculatorProviderBuilder().build()
).build(passphrase)
);
PGPSignatureGenerator sigGen = new PGPSignatureGenerator(
new BcPGPContentSignerBuilder(
secretKey.getPublicKey().getAlgorithm(),
HashAlgorithmTags.SHA512
)
);
sigGen.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, privateKey);
// Create cleartext signature stream
ArmoredOutputStream aos = new ArmoredOutputStream(out);
aos.beginClearText(HashAlgorithmTags.SHA512);
// Process data
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = in.read(buffer)) > 0) {
aos.write(buffer, 0, len);
sigGen.update(buffer, 0, len);
}
aos.endClearText();
// Generate and encode signature
PGPSignature signature = sigGen.generate();
signature.encode(aos);
aos.close();
}
/**
* Create detached signature
*/
public byte[] createDetachedSignature(InputStream in,
PGPSecretKey secretKey,
char[] passphrase) throws Exception {
PGPPrivateKey privateKey = secretKey.extractPrivateKey(
new BcPBESecretKeyDecryptorBuilder(
new BcPGPDigestCalculatorProviderBuilder().build()
).build(passphrase)
);
PGPSignatureGenerator sigGen = new PGPSignatureGenerator(
new BcPGPContentSignerBuilder(
secretKey.getPublicKey().getAlgorithm(),
HashAlgorithmTags.SHA512
)
);
sigGen.init(PGPSignature.BINARY_DOCUMENT, privateKey);
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = in.read(buffer)) > 0) {
sigGen.update(buffer, 0, len);
}
PGPSignature signature = sigGen.generate();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ArmoredOutputStream aos = new ArmoredOutputStream(baos);
signature.encode(aos);
aos.close();
return baos.toByteArray();
}
/**
* Create signature and encrypt combined
*/
public void signAndEncrypt(InputStream in, OutputStream out,
PGPSecretKey signingKey,
char[] signingPassphrase,
List<PGPPublicKey> encryptionKeys,
boolean armor) throws Exception {
if (armor) {
out = new ArmoredOutputStream(out);
}
// First sign, then encrypt
ByteArrayOutputStream signedData = new ByteArrayOutputStream();
// Create signature
PGPPrivateKey privateKey = signingKey.extractPrivateKey(
new BcPBESecretKeyDecryptorBuilder(
new BcPGPDigestCalculatorProviderBuilder().build()
).build(signingPassphrase)
);
PGPSignatureGenerator sigGen = new PGPSignatureGenerator(
new BcPGPContentSignerBuilder(
signingKey.getPublicKey().getAlgorithm(),
HashAlgorithmTags.SHA512
)
);
sigGen.init(PGPSignature.BINARY_DOCUMENT, privateKey);
// Create one-pass signature
PGPOnePassSignature onePass = new PGPOnePassSignature(sigGen.generate());
ByteArrayOutputStream literalOut = new ByteArrayOutputStream();
PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
OutputStream lOut = literalGen.open(
literalOut,
PGPLiteralData.BINARY,
PGPLiteralData.CONSOLE,
new Date(),
new byte[BUFFER_SIZE]
);
// Write data and update signature
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = in.read(buffer)) > 0) {
lOut.write(buffer, 0, len);
sigGen.update(buffer, 0, len);
}
lOut.close();
PGPSignature signature = sigGen.generate();
// Combine signed data
PGPObjectFactory factory = new PGPObjectFactory(
new ByteArrayInputStream(literalOut.toByteArray()),
new BcKeyFingerprintCalculator()
);
PGPLiteralData literalData = (PGPLiteralData) factory.nextObject();
InputStream dataIn = literalData.getDataStream();
ByteArrayOutputStream finalData = new ByteArrayOutputStream();
// Write one-pass signature
onePass.encode(finalData);
// Write literal data
buffer = new byte[BUFFER_SIZE];
while ((len = dataIn.read(buffer)) > 0) {
finalData.write(buffer, 0, len);
}
// Write signature
signature.encode(finalData);
// Now encrypt the signed data
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256)
.setWithIntegrityPacket(true)
.setSecureRandom(SECURE_RANDOM)
);
for (PGPPublicKey key : encryptionKeys) {
encGen.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(key));
}
OutputStream encryptedOut = encGen.open(out, new byte[BUFFER_SIZE]);
encryptedOut.write(finalData.toByteArray());
encryptedOut.close();
if (armor) {
out.close();
}
}
/**
* Verify detached signature
*/
public boolean verifyDetachedSignature(InputStream data,
InputStream signatureStream,
PGPPublicKeyRing publicKeyRing) throws Exception {
PGPObjectFactory pgpFactory = new PGPObjectFactory(
PGPUtil.getDecoderStream(signatureStream),
new BcKeyFingerprintCalculator()
);
Object obj = pgpFactory.nextObject();
if (!(obj instanceof PGPSignatureList)) {
throw new PGPException("Expected signature list");
}
PGPSignatureList sigList = (PGPSignatureList) obj;
PGPSignature signature = sigList.get(0);
// Find matching public key
PGPPublicKey publicKey = publicKeyRing.getPublicKey(signature.getKeyID());
if (publicKey == null) {
throw new PGPException("Public key not found for signature");
}
signature.init(new BcPGPContentVerifierBuilderProvider(), publicKey);
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = data.read(buffer)) > 0) {
signature.update(buffer, 0, len);
}
return signature.verify();
}
/**
* Verify cleartext signature
*/
public boolean verifyCleartextSignature(InputStream in,
PGPPublicKeyRing publicKeyRing) throws Exception {
// Parse cleartext signature
ArmoredInputStream aIn = new ArmoredInputStream(in);
ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
int ch;
// Read clear text
while ((ch = aIn.read()) >= 0 && aIn.isClearText()) {
lineOut.write(ch);
}
byte[] clearText = lineOut.toByteArray();
// Read signature
PGPObjectFactory pgpFactory = new PGPObjectFactory(aIn, new BcKeyFingerprintCalculator());
Object obj = pgpFactory.nextObject();
if (!(obj instanceof PGPSignatureList)) {
throw new PGPException("Expected signature list");
}
PGPSignatureList sigList = (PGPSignatureList) obj;
PGPSignature signature = sigList.get(0);
// Find matching public key
PGPPublicKey publicKey = publicKeyRing.getPublicKey(signature.getKeyID());
if (publicKey == null) {
throw new PGPException("Public key not found for signature");
}
signature.init(new BcPGPContentVerifierBuilderProvider(), publicKey);
// Update signature with clear text (canonicalized)
BufferedReader reader = new BufferedReader(new InputStreamReader(
new ByteArrayInputStream(clearText)
));
String line;
while ((line = reader.readLine()) != null) {
byte[] lineBytes = (line + "\r\n").getBytes();
signature.update(lineBytes, 0, lineBytes.length);
}
return signature.verify();
}
}

5. PGP Key Server Operations

package com.pgp.bouncycastle;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import org.springframework.stereotype.Service;
import java.io.*;
import java.net.*;
import java.util.*;
@Service
public class PGPKeyServerService {
private static final String DEFAULT_KEYSERVER = "hkp://keyserver.ubuntu.com";
private static final int HKP_PORT = 11371;
/**
* Upload public key to keyserver (HKP)
*/
public void uploadKey(PGPPublicKeyRing publicKeyRing, String keyserverUrl) 
throws IOException, PGPException {
URL url = new URL(keyserverUrl + ":11371/pks/add");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
// Export key to armored format
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ArmoredOutputStream aos = new ArmoredOutputStream(baos);
publicKeyRing.encode(aos);
aos.close();
String keyText = baos.toString("UTF-8");
String encodedKey = URLEncoder.encode(keyText, "UTF-8");
try (OutputStream os = conn.getOutputStream()) {
os.write(("keytext=" + encodedKey).getBytes());
os.flush();
}
int responseCode = conn.getResponseCode();
if (responseCode != 200) {
throw new IOException("Failed to upload key: " + responseCode);
}
}
/**
* Download public key from keyserver by key ID
*/
public PGPPublicKeyRing downloadKey(String keyId, String keyserverUrl) 
throws IOException, PGPException {
String searchUrl = keyserverUrl + "/pks/lookup?op=get&search=0x" + keyId;
URL url = new URL(searchUrl);
try (InputStream in = url.openStream()) {
return new PGPPublicKeyRing(PGPUtil.getDecoderStream(in), 
new BcKeyFingerprintCalculator());
}
}
/**
* Search keyserver for keys
*/
public List<KeySearchResult> searchKeys(String query, String keyserverUrl) 
throws IOException {
String searchUrl = keyserverUrl + "/pks/lookup?op=index&search=" 
+ URLEncoder.encode(query, "UTF-8");
URL url = new URL(searchUrl);
List<KeySearchResult> results = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(url.openStream()))) {
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("pub:")) {
results.add(parseKeyLine(line));
}
}
}
return results;
}
/**
* Verify key through keyserver network
*/
public boolean verifyKeyWithNetwork(String keyId) throws Exception {
// Try multiple keyservers
String[] keyservers = {
"hkp://keyserver.ubuntu.com",
"hkp://pgp.mit.edu",
"hkp://keys.gnupg.net"
};
for (String server : keyservers) {
try {
PGPPublicKeyRing keyRing = downloadKey(keyId, server);
if (keyRing != null) {
return true;
}
} catch (Exception e) {
// Try next server
}
}
return false;
}
/**
* Get key signatures
*/
public List<SignatureInfo> getKeySignatures(PGPPublicKey key) {
List<SignatureInfo> signatures = new ArrayList<>();
Iterator<PGPSignature> sigs = key.getSignatures();
while (sigs.hasNext()) {
PGPSignature sig = sigs.next();
SignatureInfo info = new SignatureInfo();
info.setKeyId(Long.toHexString(sig.getKeyID()).toUpperCase());
info.setCreationTime(sig.getCreationTime());
info.setSignatureType(sig.getSignatureType());
info.setHashAlgorithm(sig.getHashAlgorithm());
signatures.add(info);
}
return signatures;
}
private KeySearchResult parseKeyLine(String line) {
String[] parts = line.split(":");
KeySearchResult result = new KeySearchResult();
if (parts.length > 4) {
result.setKeyId(parts[1]);
result.setAlgorithm(parts[2]);
result.setKeyLength(Integer.parseInt(parts[3]));
result.setCreationDate(new Date(Long.parseLong(parts[4]) * 1000));
if (parts.length > 5) {
result.setUserId(parts[5]);
}
}
return result;
}
/**
* Key search result class
*/
public static class KeySearchResult {
private String keyId;
private String algorithm;
private int keyLength;
private Date creationDate;
private String userId;
// getters and setters
}
/**
* Signature information class
*/
public static class SignatureInfo {
private String keyId;
private Date creationTime;
private int signatureType;
private int hashAlgorithm;
// getters and setters
}
}

6. PGP Utility Service

package com.pgp.bouncycastle;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.operator.bc.*;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.*;
@Component
public class PGPUtilityService {
/**
* Convert between armored and binary formats
*/
public byte[] dearmor(byte[] armoredData) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(armoredData);
ArmoredInputStream ais = new ArmoredInputStream(bais);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int len;
while ((len = ais.read(buffer)) > 0) {
baos.write(buffer, 0, len);
}
return baos.toByteArray();
}
/**
* Convert binary to armored
*/
public byte[] armor(byte[] binaryData, String type) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ArmoredOutputStream aos = new ArmoredOutputStream(baos);
if (type != null) {
aos.setHeader("Comment", "Generated by Java PGP " + Instant.now().toString());
}
aos.write(binaryData);
aos.close();
return baos.toByteArray();
}
/**
* Extract key IDs from encrypted message
*/
public List<String> getRecipientKeyIDs(byte[] encryptedData) throws IOException {
List<String> keyIds = new ArrayList<>();
PGPObjectFactory factory = new PGPObjectFactory(
PGPUtil.getDecoderStream(new ByteArrayInputStream(encryptedData)),
new BcKeyFingerprintCalculator()
);
Object obj = factory.nextObject();
if (obj instanceof PGPEncryptedDataList) {
PGPEncryptedDataList encList = (PGPEncryptedDataList) obj;
Iterator<PGPPublicKeyEncryptedData> it = encList.getEncryptedDataObjects();
while (it.hasNext()) {
PGPPublicKeyEncryptedData encData = it.next();
keyIds.add(Long.toHexString(encData.getKeyID()).toUpperCase());
}
}
return keyIds;
}
/**
* Get key information as formatted string
*/
public String formatKeyInfo(PGPPublicKey key) {
StringBuilder sb = new StringBuilder();
sb.append("Key ID: ").append(Long.toHexString(key.getKeyID()).toUpperCase()).append("\n");
sb.append("Algorithm: ").append(getAlgorithmName(key.getAlgorithm())).append("\n");
sb.append("Key Size: ").append(key.getBitStrength()).append(" bits\n");
sb.append("Creation: ").append(key.getCreationTime()).append("\n");
if (key.getValidSeconds() > 0) {
Date expiry = new Date(key.getCreationTime().getTime() + key.getValidSeconds() * 1000L);
sb.append("Expires: ").append(expiry).append("\n");
}
sb.append("Fingerprint: ").append(formatFingerprint(key.getFingerprint())).append("\n");
Iterator<String> userIds = key.getUserIDs();
if (userIds.hasNext()) {
sb.append("User IDs:\n");
while (userIds.hasNext()) {
sb.append("  ").append(userIds.next()).append("\n");
}
}
return sb.toString();
}
/**
* Verify file integrity (checks if it's valid PGP data)
*/
public boolean isPGPData(byte[] data) {
try {
PGPUtil.getDecoderStream(new ByteArrayInputStream(data));
return true;
} catch (Exception e) {
return false;
}
}
/**
* Get algorithm name from algorithm ID
*/
private String getAlgorithmName(int algorithmId) {
switch (algorithmId) {
case PublicKeyAlgorithmTags.RSA_GENERAL:
case PublicKeyAlgorithmTags.RSA_ENCRYPT:
case PublicKeyAlgorithmTags.RSA_SIGN:
return "RSA";
case PublicKeyAlgorithmTags.DSA:
return "DSA";
case PublicKeyAlgorithmTags.ECDSA:
return "ECDSA";
case PublicKeyAlgorithmTags.EDDSA:
return "EdDSA";
case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT:
case PublicKeyAlgorithmTags.ELGAMAL_GENERAL:
return "ElGamal";
case PublicKeyAlgorithmTags.DIFFIE_HELLMAN:
return "Diffie-Hellman";
default:
return "Unknown";
}
}
/**
* Format fingerprint with spaces
*/
private String formatFingerprint(byte[] fingerprint) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < fingerprint.length; i++) {
if (i > 0 && i % 2 == 0) {
sb.append(' ');
}
sb.append(String.format("%02X", fingerprint[i] & 0xFF));
}
return sb.toString();
}
/**
* Process uploaded file
*/
public PGPFileInfo processUploadedFile(MultipartFile file) throws IOException {
PGPFileInfo info = new PGPFileInfo();
info.setFilename(file.getOriginalFilename());
info.setSize(file.getSize());
info.setContentType(file.getContentType());
info.setUploadTime(Instant.now());
// Detect if it's PGP data
byte[] content = file.getBytes();
info.setPgpData(isPGPData(content));
if (info.isPgpData()) {
try {
info.setRecipientKeyIds(getRecipientKeyIDs(content));
} catch (Exception e) {
// Not an encrypted message
}
}
return info;
}
/**
* PGP file information class
*/
public static class PGPFileInfo {
private String filename;
private long size;
private String contentType;
private Instant uploadTime;
private boolean isPgpData;
private List<String> recipientKeyIds;
// getters and setters
}
}

7. REST API Controller

package com.pgp.bouncycastle.rest;
import com.pgp.bouncycastle.*;
import org.bouncycastle.openpgp.*;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.*;
@RestController
@RequestMapping("/api/pgp")
public class PGPController {
private final PGPKeyGenerationService keyGenService;
private final PGPEncryptionService encryptionService;
private final PGPSigningService signingService;
private final PGPKeyServerService keyServerService;
private final PGPUtilityService utilityService;
public PGPController(PGPKeyGenerationService keyGenService,
PGPEncryptionService encryptionService,
PGPSigningService signingService,
PGPKeyServerService keyServerService,
PGPUtilityService utilityService) {
this.keyGenService = keyGenService;
this.encryptionService = encryptionService;
this.signingService = signingService;
this.keyServerService = keyServerService;
this.utilityService = utilityService;
}
@PostMapping("/keys/generate")
public ResponseEntity<KeyGenerationResponse> generateKeyPair(
@RequestBody KeyGenerationRequest request) {
try {
PGPKeyRingGenerator keyRing = keyGenService.generateKeyRing(
request.getIdentity(),
request.getPassphrase().toCharArray(),
request.getKeySize(),
request.getSubKeySize()
);
// Export keys
ByteArrayOutputStream publicKey = new ByteArrayOutputStream();
ByteArrayOutputStream privateKey = new ByteArrayOutputStream();
keyGenService.exportArmoredPublicKey(keyRing, publicKey);
keyGenService.exportSecretKeyRing(keyRing, privateKey);
return ResponseEntity.ok(new KeyGenerationResponse(
publicKey.toString(),
privateKey.toString(),
keyGenService.getKeyInfo(keyRing.generatePublicKeyRing().getPublicKey())
));
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(new KeyGenerationResponse("Key generation failed: " + e.getMessage()));
}
}
@PostMapping("/encrypt")
public ResponseEntity<EncryptResponse> encrypt(
@RequestParam("file") MultipartFile file,
@RequestParam("publicKey") String publicKeyArmored) {
try {
// Parse public key
PGPPublicKeyRing publicKeyRing = new PGPPublicKeyRing(
PGPUtil.getDecoderStream(new ByteArrayInputStream(publicKeyArmored.getBytes())),
new BcKeyFingerprintCalculator()
);
PGPPublicKey encryptionKey = keyGenService.findEncryptionKey(publicKeyRing);
if (encryptionKey == null) {
return ResponseEntity.badRequest()
.body(new EncryptResponse("No encryption key found"));
}
// Encrypt file
ByteArrayOutputStream encryptedOut = new ByteArrayOutputStream();
encryptionService.encryptFile(
file.getInputStream(),
encryptedOut,
Collections.singletonList(encryptionKey),
true,
true
);
return ResponseEntity.ok(new EncryptResponse(
Base64.getEncoder().encodeToString(encryptedOut.toByteArray()),
file.getSize()
));
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(new EncryptResponse("Encryption failed: " + e.getMessage()));
}
}
@PostMapping("/decrypt")
public ResponseEntity<DecryptResponse> decrypt(
@RequestParam("file") MultipartFile file,
@RequestParam("privateKey") String privateKeyArmored,
@RequestParam("passphrase") String passphrase) {
try {
// Parse private key
PGPSecretKeyRing secretKeyRing = new PGPSecretKeyRing(
PGPUtil.getDecoderStream(new ByteArrayInputStream(privateKeyArmored.getBytes())),
new BcKeyFingerprintCalculator()
);
// Decrypt file
ByteArrayOutputStream decryptedOut = new ByteArrayOutputStream();
encryptionService.decryptFile(
file.getInputStream(),
decryptedOut,
secretKeyRing,
passphrase.toCharArray()
);
return ResponseEntity.ok(new DecryptResponse(
decryptedOut.toByteArray(),
file.getOriginalFilename().replace(".pgp", "")
));
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(new DecryptResponse("Decryption failed: " + e.getMessage()));
}
}
@PostMapping("/sign")
public ResponseEntity<SignResponse> sign(
@RequestParam("file") MultipartFile file,
@RequestParam("privateKey") String privateKeyArmored,
@RequestParam("passphrase") String passphrase,
@RequestParam(defaultValue = "false") boolean detached) {
try {
// Parse signing key
PGPSecretKeyRing secretKeyRing = new PGPSecretKeyRing(
PGPUtil.getDecoderStream(new ByteArrayInputStream(privateKeyArmored.getBytes())),
new BcKeyFingerprintCalculator()
);
PGPSecretKey signingKey = secretKeyRing.getSecretKey();
byte[] signature;
if (detached) {
signature = signingService.createDetachedSignature(
file.getInputStream(),
signingKey,
passphrase.toCharArray()
);
} else {
ByteArrayOutputStream signedOut = new ByteArrayOutputStream();
signingService.signCleartext(
file.getInputStream(),
signedOut,
signingKey,
passphrase.toCharArray()
);
signature = signedOut.toByteArray();
}
return ResponseEntity.ok(new SignResponse(
Base64.getEncoder().encodeToString(signature),
detached ? "detached" : "cleartext"
));
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(new SignResponse("Signing failed: " + e.getMessage()));
}
}
@PostMapping("/verify")
public ResponseEntity<VerifyResponse> verify(
@RequestParam("file") MultipartFile file,
@RequestParam("signature") String signatureData,
@RequestParam("publicKey") String publicKeyArmored,
@RequestParam(defaultValue = "false") boolean detached) {
try {
// Parse public key
PGPPublicKeyRing publicKeyRing = new PGPPublicKeyRing(
PGPUtil.getDecoderStream(new ByteArrayInputStream(publicKeyArmored.getBytes())),
new BcKeyFingerprintCalculator()
);
boolean verified;
if (detached) {
verified = signingService.verifyDetachedSignature(
file.getInputStream(),
new ByteArrayInputStream(signatureData.getBytes()),
publicKeyRing
);
} else {
verified = signingService.verifyCleartextSignature(
new ByteArrayInputStream(signatureData.getBytes()),
publicKeyRing
);
}
return ResponseEntity.ok(new VerifyResponse(
verified,
verified ? "Signature valid" : "Signature invalid"
));
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(new VerifyResponse(false, "Verification failed: " + e.getMessage()));
}
}
@PostMapping("/keys/upload")
public ResponseEntity<KeyUploadResponse> uploadKey(
@RequestParam("publicKey") String publicKeyArmored,
@RequestParam(defaultValue = "hkp://keyserver.ubuntu.com") String keyserver) {
try {
PGPPublicKeyRing publicKeyRing = new PGPPublicKeyRing(
PGPUtil.getDecoderStream(new ByteArrayInputStream(publicKeyArmored.getBytes())),
new BcKeyFingerprintCalculator()
);
keyServerService.uploadKey(publicKeyRing, keyserver);
return ResponseEntity.ok(new KeyUploadResponse(
true,
"Key uploaded successfully"
));
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(new KeyUploadResponse(false, "Upload failed: " + e.getMessage()));
}
}
@GetMapping("/keys/search")
public ResponseEntity<List<PGPKeyServerService.KeySearchResult>> searchKeys(
@RequestParam String query,
@RequestParam(defaultValue = "hkp://keyserver.ubuntu.com") String keyserver) {
try {
List<PGPKeyServerService.KeySearchResult> results = 
keyServerService.searchKeys(query, keyserver);
return ResponseEntity.ok(results);
} catch (Exception e) {
return ResponseEntity.badRequest().body(Collections.emptyList());
}
}
@PostMapping("/file/info")
public ResponseEntity<PGPUtilityService.PGPFileInfo> getFileInfo(
@RequestParam("file") MultipartFile file) {
try {
PGPUtilityService.PGPFileInfo info = 
utilityService.processUploadedFile(file);
return ResponseEntity.ok(info);
} catch (IOException e) {
return ResponseEntity.badRequest().build();
}
}
// Request/Response classes
public static class KeyGenerationRequest {
private String identity;
private String passphrase;
private int keySize = 4096;
private int subKeySize = 4096;
// getters and setters
}
public static class KeyGenerationResponse {
private String publicKey;
private String privateKey;
private PGPKeyGenerationService.KeyInfo keyInfo;
private String error;
public KeyGenerationResponse(String publicKey, String privateKey, 
PGPKeyGenerationService.KeyInfo keyInfo) {
this.publicKey = publicKey;
this.privateKey = privateKey;
this.keyInfo = keyInfo;
}
public KeyGenerationResponse(String error) {
this.error = error;
}
// getters
}
public static class EncryptResponse {
private String encryptedData;
private long originalSize;
private String error;
public EncryptResponse(String encryptedData, long originalSize) {
this.encryptedData = encryptedData;
this.originalSize = originalSize;
}
public EncryptResponse(String error) {
this.error = error;
}
// getters
}
public static class DecryptResponse {
private byte[] decryptedData;
private String filename;
private String error;
public DecryptResponse(byte[] decryptedData, String filename) {
this.decryptedData = decryptedData;
this.filename = filename;
}
public DecryptResponse(String error) {
this.error = error;
}
// getters
}
public static class SignResponse {
private String signature;
private String type;
private String error;
public SignResponse(String signature, String type) {
this.signature = signature;
this.type = type;
}
public SignResponse(String error) {
this.error = error;
}
// getters
}
public static class VerifyResponse {
private boolean verified;
private String message;
public VerifyResponse(boolean verified, String message) {
this.verified = verified;
this.message = message;
}
// getters
}
public static class KeyUploadResponse {
private boolean success;
private String message;
public KeyUploadResponse(boolean success, String message) {
this.success = success;
this.message = message;
}
// getters
}
}

Security Best Practices

1. Key Management

// Always protect private keys with strong passphrases
public void storePrivateKey(PGPSecretKeyRing secretKeyRing, char[] passphrase) {
// Use strong passphrase (minimum 20 characters)
if (passphrase.length < 20) {
throw new SecurityException("Passphrase too short");
}
// Store encrypted
keyStore.setKeyEntry("pgp-key", 
secretKeyRing.getEncoded(),
passphrase);
}

2. Key Expiration

// Always set expiration on keys
public void setKeyExpiration(PGPKeyRingGenerator keyRingGen, int days) {
PGPSignatureSubpacketGenerator hashedPackets = new PGPSignatureSubpacketGenerator();
hashedPackets.setKeyExpirationTime(false, days * 24 * 60 * 60);
// Apply expiration
}

3. Algorithm Selection

// Use strong algorithms
public static final int[] PREFERRED_ALGORITHMS = {
SymmetricKeyAlgorithmTags.AES_256,
HashAlgorithmTags.SHA512,
CompressionAlgorithmTags.ZLIB,
PublicKeyAlgorithmTags.RSA_GENERAL
};

PGP Message Format

PGP Message Structure
├── PGP Encrypted Data
│   ├── Public Key Encrypted Session Key (PKESK)
│   ├── Symmetrically Encrypted Data Packet
│   └── Integrity Protection (MDC)
├── PGP Signed Data
│   ├── One-Pass Signature Packet
│   ├── Literal Data Packet
│   └── Signature Packet
└── PGP Compressed Data
├── ZIP/ZLIB/BZip2 Compressed Data
└── Original Packet

Conclusion

This comprehensive OpenPGP implementation provides:

  • Complete key management with generation, export, and revocation
  • Hybrid encryption using RSA/ECC + AES
  • Digital signatures with detached and cleartext options
  • Key server integration for key distribution
  • ASCII armor for text-based transport
  • Compression for efficiency
  • Multiple algorithms for flexibility

Key features:

  • RSA (2048-4096 bits) for encryption/signing
  • ECC/EdDSA for modern elliptic curve crypto
  • AES-256 for symmetric encryption
  • SHA-512 for hashing
  • ZLIB/BZip2 for compression
  • RFC 4880 compliant implementation

This implementation provides production-ready PGP functionality suitable for secure file encryption, email encryption, and digital signatures.

Java Programming Intermediate Topics – Modifiers, Loops, Math, Methods & Projects (Related to Java Programming)


Access Modifiers in Java:
Access modifiers control how classes, variables, and methods are accessed from different parts of a program. Java provides four main access levels—public, private, protected, and default—which help protect data and control visibility in object-oriented programming.
Read more: https://macronepal.com/blog/access-modifiers-in-java-a-complete-guide/


Static Variables in Java:
Static variables belong to the class rather than individual objects. They are shared among all instances of the class and are useful for storing values that remain common across multiple objects.
Read more: https://macronepal.com/blog/static-variables-in-java-a-complete-guide/


Method Parameters in Java:
Method parameters allow values to be passed into methods so that operations can be performed using supplied data. They help make methods flexible and reusable in different parts of a program.
Read more: https://macronepal.com/blog/method-parameters-in-java-a-complete-guide/


Random Numbers in Java:
This topic explains how to generate random numbers in Java for tasks such as simulations, games, and random selections. Random numbers help create unpredictable results in programs.
Read more: https://macronepal.com/blog/random-numbers-in-java-a-complete-guide/


Math Class in Java:
The Math class provides built-in methods for performing mathematical calculations such as powers, square roots, rounding, and other advanced calculations used in Java programs.
Read more: https://macronepal.com/blog/math-class-in-java-a-complete-guide/


Boolean Operations in Java:
Boolean operations use true and false values to perform logical comparisons. They are commonly used in conditions and decision-making statements to control program flow.
Read more: https://macronepal.com/blog/boolean-operations-in-java-a-complete-guide/


Nested Loops in Java:
Nested loops are loops placed inside other loops to perform repeated operations within repeated tasks. They are useful for pattern printing, tables, and working with multi-level data.
Read more: https://macronepal.com/blog/nested-loops-in-java-a-complete-guide/


Do-While Loop in Java:
The do-while loop allows a block of code to run at least once before checking the condition. It is useful when the program must execute a task before verifying whether it should continue.
Read more: https://macronepal.com/blog/do-while-loop-in-java-a-complete-guide/


Simple Calculator Project in Java:
This project demonstrates how to create a basic calculator program using Java. It combines input handling, arithmetic operations, and conditional logic to perform simple mathematical calculations.
Read more: https://macronepal.com/blog/simple-calculator-project-in-java/

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper