I am trying to implement PGP encryption in Java using Bouncy Castle, also using some of the examples provided by them.
But if I try and decrypt the message I just encrypted, it does not work. The keys and the decryption method seem to be ok, because I can encrypt using an external tool (link) and then successfully decrypt it here.
JavaScript
x
public static void main(String[] args) throws PGPException, IOException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
Security.addProvider(new BouncyCastleProvider());
byte[] msg = ("Test blabla BLA").getBytes();
byte[] encData = encrypt(msg, publicKey);
System.out.println(new String(encData));
System.err.println("DECRYPTED");
System.out.println(decrypt(IOUtils.toInputStream(new String(encData), StandardCharsets.UTF_8), privateKey, passphrase));
System.err.println("DECRYPTED external");
String testEncryptedMsg = "-----BEGIN PGP MESSAGE-----n" +
"Version: BCPG C# v1.6.1.0n" +
"n" +
"hQIMA4p9BgtBOg+BARAAiLdmGZBupsSkrji1qdC/y6KACuaWytcjVg3LWudWRlH8n" +
"Ye1U0bz+HYc6mLt21RivAZ3p+lPXPzh/h0GVLW/aI2ngJracL+iEA7eam5/H3NQVn" +
"2o0CR1mjw4xOf0XwDxaLrnivdeuWniXiZyFN7Ud7lmNG+EmQ0d5ZfR7KEnTJNjSen" +
"FpzwRffBG5lKB4CxF49jBp+Iupdv4A5JzHuDVBkypPMNhS/UEbuPOt5cfmddYVvWn" +
"gtDM3rX+ePBhg7d/mVkiDT2KM0Cr7GSrdZ8q83fF4sat3Xp8OHMq61GWriNK4gwbn" +
"ViT/vmEsY0n55TVgN5VagMZJJlEThkqWE2wpEjq4HM8pNxR4PvP5inqP5oipn8Osn" +
"Nq2a5f77BS7GB9NQ+hnaLSBsywJkZVKqygN81MeHBnNrVXFQ/Hqg24BLwk49bOuHn" +
"jTUzorudCnjj/p8/WiyR+PuESkQDLKpo1z9KGEE/rKjTqAwH/fBKJ73XpmAWwd4Un" +
"1hv/EdF+XDZv7XwxeVjSIK2inAjdNz+way1WNY3qouo7G+gjKf1gbL14q2HVPnd/n" +
"7mfiW3uWCQ7GhXlHtd1E0WeK2296TdBuSGAvLlVxI4xepsiuVw+zC4BxvAkTXB/an" +
"HyJZhKPksHMRCt8ZxitMfhdvMNGjc4MDhv1MLqKijpagKhgrmDTiJRMb3ng6QLvJn" +
"L2GICyuS4aIsz6XiCQvZG8ebfb0J4uogbb5HxS2fGFT+Y/VgpCnwhwj6LnLBy1Q3n" +
"=D/1dn" +
"-----END PGP MESSAGE-----n";
System.out.println(decrypt(IOUtils.toInputStream(testEncryptedMsg, StandardCharsets.UTF_8), privateKey, passphrase));
}
I think the problem is on the encryption side, but I cannot figure out what exactly it is. Does anyone have any ideas?
JavaScript
public static byte[] encrypt(byte[] bytes, String publicKey) throws IOException {
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
try {
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(PGPEncryptedData.AES_128)
.setSecureRandom(new SecureRandom())
.setWithIntegrityPacket(true)
.setProvider("BC"));
PGPPublicKey encryptionKey = readPublicKey(publicKey);
encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encryptionKey).setProvider("BC"));
ArmoredOutputStream aOut = new ArmoredOutputStream(encOut);
OutputStream cOut = encGen.open(aOut, bytes.length);
// write out the literal data
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
OutputStream pOut = lData.open(aOut, PGPLiteralData.UTF8, PGPLiteralData.CONSOLE, bytes.length, new Date());
pOut.write(bytes);
pOut.close();
// finish the encryption
cOut.close();
aOut.close();
} catch (PGPException e) {
log.error("Exception encountered while encoding data.", e);
}
return encOut.toByteArray();
}
public static String decrypt(InputStream in, String secretKey, String passphrase) throws IOException {
String content = null;
try {
in = PGPUtil.getDecoderStream(in);
JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in);
PGPEncryptedDataList enc;
Object o = pgpF.nextObject();
// the first object might be a PGP marker packet.
if (o instanceof PGPEncryptedDataList) {
enc = (PGPEncryptedDataList) o;
} else {
enc = (PGPEncryptedDataList) pgpF.nextObject();
}
// find the secret key
Iterator<PGPEncryptedData> it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(new ByteArrayInputStream(secretKey.getBytes())), new JcaKeyFingerprintCalculator());
while (sKey == null && it.hasNext()) {
pbe = (PGPPublicKeyEncryptedData) it.next();
sKey = findSecretKey(pgpSec, pbe.getKeyID(), passphrase.toCharArray());
}
if (sKey == null) {
log.error("Secret key for message not found.");
throw new IllegalArgumentException("secret key for message not found.");
}
InputStream clear = pbe.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("BC").build(sKey));
JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear);
Object message = plainFact.nextObject();
if (message instanceof PGPCompressedData) {
PGPCompressedData cData = (PGPCompressedData) message;
JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(cData.getDataStream());
message = pgpFact.nextObject();
}
if (message instanceof PGPLiteralData) {
PGPLiteralData ld = (PGPLiteralData) message;
content = new String(ld.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
} else if (message instanceof PGPOnePassSignatureList) {
throw new PGPException("encrypted message contains a signed message - not literal data.");
} else {
throw new PGPException("message is not a simple encrypted file - type unknown.");
}
if (pbe.isIntegrityProtected()) {
if (!pbe.verify()) {
log.error("message failed integrity check");
} else {
log.info("message integrity check passed");
}
} else {
log.warn("no message integrity check");
}
} catch (PGPException e) {
log.error("Exception encountered while decrypting file.", e);
}
return content;
}
private static PGPPrivateKey findSecretKey(PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass) throws PGPException {
PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
if (pgpSecKey == null) {
return null;
}
return pgpSecKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("BC").build(pass));
}
private static PGPPublicKey readPublicKey(String publicKey) throws IOException, PGPException {
InputStream input = IOUtils.toInputStream(publicKey, StandardCharsets.UTF_8);
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator());
Iterator<PGPPublicKeyRing> keyRingIter = pgpPub.getKeyRings();
while (keyRingIter.hasNext()) {
PGPPublicKeyRing keyRing = keyRingIter.next();
Iterator<PGPPublicKey> keyIter = keyRing.getPublicKeys();
while (keyIter.hasNext()) {
PGPPublicKey key = keyIter.next();
if (key.isEncryptionKey()) {
return key;
}
}
}
throw new IllegalArgumentException("Can't find encryption key in key ring.");
}
Advertisement
Answer
Here is the encryption method that eventually worked for me. It helped debugging together with the examples provided by the BCFipsIn100 book.
JavaScript
public byte[] encrypt(byte[] bytes, String publicKey) throws IOException {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
byte[] compressedData = compress(bytes);
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5)
.setWithIntegrityPacket(true)
.setSecureRandom(new SecureRandom())
.setProvider(PROVIDER));
PGPPublicKey encryptionKey = readPublicKey(publicKey);
encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encryptionKey).setProvider(PROVIDER));
ArmoredOutputStream aOut = new ArmoredOutputStream(byteArrayOutputStream);
OutputStream cOut = encGen.open(aOut, compressedData.length);
cOut.write(compressedData);
cOut.close();
aOut.close();
return byteArrayOutputStream.toByteArray();
} catch (PGPException e) {
log.error("Exception encountered while encoding data.", e);
}
return new byte[0];
}
private static byte[] compress(byte[] clearData) throws IOException {
try (ByteArrayOutputStream bOut = new ByteArrayOutputStream()) {
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
OutputStream cos = comData.open(bOut);
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
OutputStream pOut = lData.open(cos,
PGPLiteralData.BINARY,
PGPLiteralData.CONSOLE,
clearData.length,
new Date());
pOut.write(clearData);
pOut.close();
comData.close();
return bOut.toByteArray();
}
}