Getting BouncyCastle to decrypt a GPG-encrypted message - java

How can I get BouncyCastle to decrypt a GPG-encrypted message?
I have created a GPG key pair at the CentOS 7 command line using gpg --gen-key. I chose RSA RSA as the encryption types, and I exported the keys using gpg --export-secret-key -a "User Name" > /home/username/username_private.key and gpg --armor --export 66677FC6 > /home/username/username_pubkey.asc
I am able to import username_pubkey.asc into a remote Thunderbird client of another email account and successfully send an encrypted email to username#mydomain.com. But when my Java/BouncyCastle code running at mydomain.com tries to decrypt the GPG-encoded data, it gives the following error:
org.bouncycastle.openpgp.PGPException:
Encrypted message contains a signed message - not literal data.
If you look at the code below, you will see this corresponds with the line in PGPUtils.decryptFile() which states else if (message instanceof PGPOnePassSignatureList) {throw new PGPException("Encrypted message contains a signed message - not literal data.");
The original code for this came from the blog entry at this link, though I made minor changes to get it to compile in Eclipse Luna with Java 7. A user of the linked blog reported the same error, and the blog author replied by saying that it does not work with GPG. So how do I fix this to make it work with GPG?
The Java decryption code starts when the GPG-encoded-file and the GPG-secret-key are passed into Tester.testDecrypt() as follows:
Tester.java contains:
public InputStream testDecrypt(String input, String output, String passphrase, String skeyfile) throws Exception {
PGPFileProcessor p = new PGPFileProcessor();
p.setInputFileName(input);//this is GPG-encoded data sent from another email address using Thunderbird
p.setOutputFileName(output);
p.setPassphrase(passphrase);
p.setSecretKeyFileName(skeyfile);//this is the GPG-generated key
return p.decrypt();//this line throws the error
}
PGPFileProcessor.java includes:
public InputStream decrypt() throws Exception {
FileInputStream in = new FileInputStream(inputFileName);
FileInputStream keyIn = new FileInputStream(secretKeyFileName);
FileOutputStream out = new FileOutputStream(outputFileName);
PGPUtils.decryptFile(in, out, keyIn, passphrase.toCharArray());//error thrown here
in.close();
out.close();
keyIn.close();
InputStream result = new FileInputStream(outputFileName);//I changed return type from boolean on 1/27/15
Files.deleteIfExists(Paths.get(outputFileName));//I also added this to accommodate change of return type on 1/27/15
return result;
}
PGPUtils.java includes:
/**
* decrypt the passed in message stream
*/
#SuppressWarnings("unchecked")
public static void decryptFile(InputStream in, OutputStream out, InputStream keyIn, char[] passwd)
throws Exception
{
Security.addProvider(new BouncyCastleProvider());
in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);
//1/26/15 added Jca prefix to avoid eclipse warning, also used https://www.bouncycastle.org/docs/pgdocs1.5on/index.html
PGPObjectFactory 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<PGPPublicKeyEncryptedData> it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
while (sKey == null && it.hasNext()) {
pbe = it.next();
sKey = findPrivateKey(keyIn, pbe.getKeyID(), passwd);
}
if (sKey == null) {throw new IllegalArgumentException("Secret key for message not found.");}
InputStream clear = pbe.getDataStream(new BcPublicKeyDataDecryptorFactory(sKey));
//1/26/15 added Jca prefix to avoid eclipse warning, also used https://www.bouncycastle.org/docs/pgdocs1.5on/index.html
PGPObjectFactory plainFact = new JcaPGPObjectFactory(clear);
Object message = plainFact.nextObject();
if (message instanceof PGPCompressedData) {
PGPCompressedData cData = (PGPCompressedData) message;
//1/26/15 added Jca prefix to avoid eclipse warning, also used https://www.bouncycastle.org/docs/pgdocs1.5on/index.html
PGPObjectFactory pgpFact = new JcaPGPObjectFactory(cData.getDataStream());
message = pgpFact.nextObject();
}
if (message instanceof PGPLiteralData) {
PGPLiteralData ld = (PGPLiteralData) message;
InputStream unc = ld.getInputStream();
int ch;
while ((ch = unc.read()) >= 0) {out.write(ch);}
} 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()) {throw new PGPException("Message failed integrity check");}
}
}
/**
* Load a secret key ring collection from keyIn and find the private key corresponding to
* keyID if it exists.
*
* #param keyIn input stream representing a key ring collection.
* #param keyID keyID we want.
* #param pass passphrase to decrypt secret key with.
* #return
* #throws IOException
* #throws PGPException
* #throws NoSuchProviderException
*/
public static PGPPrivateKey findPrivateKey(InputStream keyIn, long keyID, char[] pass)
throws IOException, PGPException, NoSuchProviderException
{
//1/26/15 added Jca prefix to avoid eclipse warning, also used https://www.bouncycastle.org/docs/pgdocs1.5on/index.html
PGPSecretKeyRingCollection pgpSec = new JcaPGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyIn));
return findPrivateKey(pgpSec.getSecretKey(keyID), pass);
}
/**
* Load a secret key and find the private key in it
* #param pgpSecKey The secret key
* #param pass passphrase to decrypt secret key with
* #return
* #throws PGPException
*/
public static PGPPrivateKey findPrivateKey(PGPSecretKey pgpSecKey, char[] pass)
throws PGPException
{
if (pgpSecKey == null) return null;
PBESecretKeyDecryptor decryptor = new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass);
return pgpSecKey.extractPrivateKey(decryptor);
}
The complete code of all three Java files can be found on a file sharing site by clicking on this link.
The complete stack trace for the error can be found by clicking on this link.
For reference, the GUI instructions for encryption by the remote Thunderbird sender are summarized in the following screen shot:
I have read many postings and links about this. In particular, this other SO posting looks similar, but is different. My Keys use RSA RSA, but the other posting does not.
EDIT#1
As per #DavidHook's suggestion, I have read SignedFileProcessor, and I am starting to read the much longer RFC 4880. However, I need actual working code to study in order to understand this. Most people who find this via google searches will also need working code to illustrate the examples.
For reference, the SignedFileProcessor.verifyFile() method recommended by #DavidHook is as follows. How should this be customized to fix the problems in the code above?
private static void verifyFile(InputStream in, InputStream keyIn) throws Exception {
in = PGPUtil.getDecoderStream(in);
PGPObjectFactory pgpFact = new PGPObjectFactory(in);
PGPCompressedData c1 = (PGPCompressedData)pgpFact.nextObject();
pgpFact = new PGPObjectFactory(c1.getDataStream());
PGPOnePassSignatureList p1 = (PGPOnePassSignatureList)pgpFact.nextObject();
PGPOnePassSignature ops = p1.get(0);
PGPLiteralData p2 = (PGPLiteralData)pgpFact.nextObject();
InputStream dIn = p2.getInputStream();
int ch;
PGPPublicKeyRingCollection pgpRing = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(keyIn));
PGPPublicKey key = pgpRing.getPublicKey(ops.getKeyID());
FileOutputStream out = new FileOutputStream(p2.getFileName());
ops.initVerify(key, "BC");
while ((ch = dIn.read()) >= 0){
ops.update((byte)ch);
out.write(ch);
}
out.close();
PGPSignatureList p3 = (PGPSignatureList)pgpFact.nextObject();
if (ops.verify(p3.get(0))){System.out.println("signature verified.");}
else{System.out.println("signature verification failed.");}
}
EDIT#2
The SignedFileProcessor.verifyFile() method recommended by #DavidHook is almost identical to the PGPUtils.verifyFile() method in my code above, except that PGPUtils.verifyFile() makes a copy of extractContentFile and calls PGPOnePassSignature.init() instead of PGPOnePassSignature.initVerify(). This may be due to a version difference. Also, PGPUtils.verifyFile() returns a boolean, while SignedFileProcessor.verifyFile() gives SYSO for the two boolean values and returns void after the SYSO.
If I interpret #JRichardSnape's comments correctly, this means that the verifyFile() method might best be called upstream to confirm the signature of the incoming file using the sender's public key, and then, if the signature on the file is verified, using another method to decrypt the file using the recipient's private key. Is this correct? If so, how do I restructure the code to accomplish this?

if anyone is interested to know how to encrypt and decrypt gpg files using bouncy castle openPGP library, check the below java code:
The below are the 4 methods you going to need:
The below method will read and import your secret key from .asc file:
public static PGPSecretKey readSecretKeyFromCol(InputStream in, long keyId) throws IOException, PGPException {
in = PGPUtil.getDecoderStream(in);
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(in, new BcKeyFingerprintCalculator());
PGPSecretKey key = pgpSec.getSecretKey(keyId);
if (key == null) {
throw new IllegalArgumentException("Can't find encryption key in key ring.");
}
return key;
}
The below method will read and import your public key from .asc file:
#SuppressWarnings("rawtypes")
public static PGPPublicKey readPublicKeyFromCol(InputStream in) throws IOException, PGPException {
in = PGPUtil.getDecoderStream(in);
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in, new BcKeyFingerprintCalculator());
PGPPublicKey key = null;
Iterator rIt = pgpPub.getKeyRings();
while (key == null && rIt.hasNext()) {
PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next();
Iterator kIt = kRing.getPublicKeys();
while (key == null && kIt.hasNext()) {
PGPPublicKey k = (PGPPublicKey) kIt.next();
if (k.isEncryptionKey()) {
key = k;
}
}
}
if (key == null) {
throw new IllegalArgumentException("Can't find encryption key in key ring.");
}
return key;
}
The below 2 methods to decrypt and encrypt gpg files:
public void decryptFile(InputStream in, InputStream secKeyIn, InputStream pubKeyIn, char[] pass) throws IOException, PGPException, InvalidCipherTextException {
Security.addProvider(new BouncyCastleProvider());
PGPPublicKey pubKey = readPublicKeyFromCol(pubKeyIn);
PGPSecretKey secKey = readSecretKeyFromCol(secKeyIn, pubKey.getKeyID());
in = PGPUtil.getDecoderStream(in);
JcaPGPObjectFactory pgpFact;
PGPObjectFactory pgpF = new PGPObjectFactory(in, new BcKeyFingerprintCalculator());
Object o = pgpF.nextObject();
PGPEncryptedDataList encList;
if (o instanceof PGPEncryptedDataList) {
encList = (PGPEncryptedDataList) o;
} else {
encList = (PGPEncryptedDataList) pgpF.nextObject();
}
Iterator<PGPPublicKeyEncryptedData> itt = encList.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData encP = null;
while (sKey == null && itt.hasNext()) {
encP = itt.next();
secKey = readSecretKeyFromCol(new FileInputStream("PrivateKey.asc"), encP.getKeyID());
sKey = secKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass));
}
if (sKey == null) {
throw new IllegalArgumentException("Secret key for message not found.");
}
InputStream clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(sKey));
pgpFact = new JcaPGPObjectFactory(clear);
PGPCompressedData c1 = (PGPCompressedData) pgpFact.nextObject();
pgpFact = new JcaPGPObjectFactory(c1.getDataStream());
PGPLiteralData ld = (PGPLiteralData) pgpFact.nextObject();
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
InputStream inLd = ld.getDataStream();
int ch;
while ((ch = inLd.read()) >= 0) {
bOut.write(ch);
}
//System.out.println(bOut.toString());
bOut.writeTo(new FileOutputStream(ld.getFileName()));
//return bOut;
}
public static void encryptFile(OutputStream out, String fileName, PGPPublicKey encKey) throws IOException, NoSuchProviderException, PGPException {
Security.addProvider(new BouncyCastleProvider());
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
PGPUtil.writeFileToLiteralData(comData.open(bOut), PGPLiteralData.BINARY, new File(fileName));
comData.close();
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.TRIPLE_DES).setSecureRandom(new SecureRandom()));
cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(encKey));
byte[] bytes = bOut.toByteArray();
OutputStream cOut = cPk.open(out, bytes.length);
cOut.write(bytes);
cOut.close();
out.close();
}
Now here is how to invoke/run the above:
try {
decryptFile(new FileInputStream("encryptedFile.gpg"), new FileInputStream("PrivateKey.asc"), new FileInputStream("PublicKey.asc"), "yourKeyPassword".toCharArray());
PGPPublicKey pubKey = readPublicKeyFromCol(new FileInputStream("PublicKey.asc"));
encryptFile(new FileOutputStream("encryptedFileOutput.gpg"), "fileToEncrypt.txt", pubKey);
} catch (PGPException e) {
fail("exception: " + e.getMessage(), e.getUnderlyingException());
}

It just means that content has been signed and then encrypted, the routine provided does not know how to deal with it, but at least tells you that. PGP protocol presents as a series of packets some of which can be wrapped in other ones (for example compressed data can also wrap signed data or simply literal data, these can be used to generate encrypted data as well, actual content always appears in literal data).
If you look at the verifyFile method in the SignedFileProcessor in the Bouncy Castle OpenPGP examples package you will see how to handle the signature data and get to the literal data containing the actual content.
I would also recommend looking at RFC 4880 so you have some idea of how the protocol works. The protocol is very loose and both GPG, BC, and the variety of products out there reflect this - that said the looseness does mean that if you try and cut and paste your way to a solution you'll end up with a disaster. It's not complicated, but understanding is required here as well.

Related

Bouncy Castle PGP sign and encrypt in one pass?

I'm looking for an implementation of Bouncy Castle PGP 'sign and encrypt'. Ideally in one operation, if that makes any difference.
I've taken the encrypt example and the signing example and tried to turn it into a 'one pass' encrypt and sign operation.
I see this relatively dated implementation Boncode. It seems to show that the two operations are just linked together.
I'm not getting the consumer to decrypt the code. The signature seems to be able to be verified. This is true whether I use the merged operations or separate encrypt then sign.
Is there a better Bouncy Castle PGP implementation?
Here is my current implementation of a one pass, Bouncy Castle PGP encrypt+sign. The signature seems to verify, but the payload is not decrypted.
public class SinglePassSignedEncryptedFileProcessor {
private static final Logger logger = LoggerFactory.getLogger(SinglePassSignedEncryptedFileProcessor.class);
/*
* This is the primary function that will create encrypt a file and sign it
* with a one pass signature. This leans on an C# example by John Opincar
* #author Bilal Soylu
* #param targetFileName
* -- file name on drive systems that will contain encrypted content
* #param embeddedFileName
* -- the original file name before encryption
* #param secretKeyRingInputStream
* -- Private Key Ring File
* #param targetFileStream
* -- The stream for the encrypted target file
* #param secretKeyPassphrase
* -- The private key password for the key retrieved from
* collection used for signing
* #param signPublicKeyInputStream
* -- the public key of the target recipient to be used to
* encrypt the file
* #throws Exception
*/
public void encryptOnePassSign(
String fileName,
InputStream keyIn,
OutputStream out,
char[] pass,
PGPPublicKey encryptionKey,
boolean armor,
boolean withIntegrityCheck,
String providerName)
throws IOException, NoSuchAlgorithmException, NoSuchProviderException, PGPException, SignatureException {
if (armor) {
out = new ArmoredOutputStream(out);
}
// Compress
byte[] bytes = PGPEncryptUtil.compressFile(fileName, CompressionAlgorithmTags.ZIP);
// Encryption process.
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(withIntegrityCheck).setSecureRandom(new SecureRandom()).setProvider("BC"));
encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encryptionKey).setProvider("BC"));
ByteArrayOutputStream encryptedOutputStream = new ByteArrayOutputStream();
OutputStream encryptedOut = encGen.open(encryptedOutputStream, bytes);
encryptedOut.write(bytes);
encryptedOut.close();
byte[] bytesEncrypted = encryptedOutputStream.toByteArray();
encryptedOutputStream.close();
// Signing process.
PGPSecretKey pgpSec = PGPEncryptUtil.readSecretKey(keyIn);
PGPPrivateKey pgpPrivKey = pgpSec.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("BC").build(pass));
PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(pgpSec.getPublicKey().getAlgorithm(), PGPUtil.SHA1).setProvider("BC"));
sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey);
Iterator it = pgpSec.getPublicKey().getUserIDs();
if (it.hasNext()) {
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
spGen.setSignerUserID(false, (String) it.next());
sGen.setHashedSubpackets(spGen.generate());
}
PGPCompressedDataGenerator cGen = new PGPCompressedDataGenerator(
PGPCompressedData.UNCOMPRESSED);
// Write to the output stream.
BCPGOutputStream bOut = new BCPGOutputStream(cGen.open(out));
sGen.generateOnePassVersion(false).encode(bOut);
File file = new File(fileName);
PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator();
// file is encoding name.
Date lastModified = new Date(file.lastModified());
OutputStream lOut = lGen.open(bOut, PGPLiteralData.BINARY, fileName, lastModified, bytesEncrypted);
//FileInputStream fIn = new FileInputStream(file);
//int ch;
//while ((ch = fIn.read()) >= 0) {
lOut.write(bytesEncrypted);
sGen.update(bytesEncrypted);
// }
// ?
lGen.close();
sGen.generate().encode(bOut);
cGen.close();
if (armor) {
out.close();
}
// close everything down we are done
/*
literalOut.close();
literalDataGenerator.close();
signatureGenerator.generate().encode(compressedOut);
compressedOut.close();
compressedDataGenerator.close();
encryptedOut.close();
encryptedDataGenerator.close();
*/
// if (armor) targetFileStream.close();
}
}
Latest answer is to use BouncyGPG
Works as per the test cases. Kotlin
val encryptionStream = BouncyGPG
.encryptToStream()
.withConfig(keyringConfig)
.withStrongAlgorithms()
.toRecipient("recipient#example.com")
.andSignWith("sender#example.com")
.armorAsciiOutput()
.andWriteTo(cipherText)
encryptionStream.write(expectedPlaintext)
encryptionStream.close()
cipherText.close()

Bouncycastle PGP encryption error Illegal Key Size

I'm currently writing an encrypted messaging service in java, and I'm using the bouncycastle PGP library. I have written a test program that generates a key pair, and encrypts/decrypts a message. This was working for a while, but it recently stopped in the decrypt stage, giving me an InvalidKeyException.
I've done some research and downloaded the JCE .jar files and imported them into my project (through Eclipse project -> properties -> add external JARs). I saw that for windows users, they should be put into a specific folder in the java library, but i couldn't find a similar one on my Mac. I tried looking through the usr/library folder but couldn't find anything of use.
Has anyone solved this issue on Mac?
EDIT: here's some code from my main test function
// decrypt
byte[] decrypted = PGPEncryptDecrypt.decrypt(encFromFile, secKey, pass.toCharArray());
Here's my decrypt method( this was not written by me, but I made a PGPEncryptDecrypt class to hold related static methods, and it worked for me)
public static byte[] decrypt(byte[] encrypted, InputStream keyIn, char[] password)
throws IOException, PGPException, NoSuchProviderException {
InputStream in = new ByteArrayInputStream(encrypted);
in = PGPUtil.getDecoderStream(in);
PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc = null;
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 it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
PGPUtil.getDecoderStream(keyIn));
while (sKey == null && it.hasNext()) {
pbe = (PGPPublicKeyEncryptedData) it.next();
sKey = findSecretKey(pgpSec, pbe.getKeyID(), password);
}
if (sKey == null) {
throw new IllegalArgumentException(
"secret key for message not found.");
}
InputStream clear = pbe.getDataStream(sKey, "BC");
PGPObjectFactory pgpFact = new PGPObjectFactory(clear);
PGPCompressedData cData = (PGPCompressedData) pgpFact.nextObject();
pgpFact = new PGPObjectFactory(cData.getDataStream());
PGPLiteralData ld = (PGPLiteralData) pgpFact.nextObject();
InputStream unc = ld.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
int ch;
while ((ch = unc.read()) >= 0) {
out.write(ch);
}
byte[] returnBytes = out.toByteArray();
out.close();
return returnBytes;
}
The error points to the findSecretKey (in PGPEncryptDecrypt class) method, which is as follows
public static PGPPrivateKey findSecretKey(
PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass)
throws PGPException, NoSuchProviderException {
PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
if (pgpSecKey == null) {
return null;
}
return pgpSecKey.extractPrivateKey(pass, "BC");
}
These functions all worked perfectly when i first implemented them, but they stopped working.
For anyone else looking, i found the answer to this after digging around a little.
what i did was open terminal, enter root library (as sudo), found the appropriate java library, and did a manual copy from my downloads folder into the appropriate java security folder
path was Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/jre/lib/security
then in there i did two cp filename commands to copy the appropriate file

How do I store and read PGP public keys as strings using Bouncycastle Java?

I have been trying to create, encode, store, retrieve, and decode a Bouncy Castle PGP public key. I get what appears to be the wrong output and an EOFException when I try to read the key back in. The key will be stored in a database as a string.
The original RSA encryption public key is extracted from the key ring as follows:
#SuppressWarnings("unchecked")
public PGPPublicKey getPublicKey() {
PGPPublicKey pk = null;
Iterator<PGPPublicKey> it = publicKeyRing.getPublicKeys();
while (pk == null && it.hasNext()) {
PGPPublicKey key = it.next();
if (key.isEncryptionKey()) {
pk = key;
}
}
return pk;
}
It is encoded, ASCII armored, and stored as a string as follows:
PGPPublicKey contactPK = realContact.getPublicKey();
ByteArrayOutputStream out = new ByteArrayOutputStream();
ArmoredOutputStream armored = new ArmoredOutputStream(out);
contactPK.encode(armored);
armored.close();
publicKey = new String(out.toByteArray(), Charset.forName("US-ASCII"));
This gets me a PGP message block where I would expect a PGP public key block:
-----BEGIN PGP MESSAGE-----\nVersion: BCPG v1.50\n\nuQINBFO8StkCEACQ4vrDnBTDjEvQkGwrAHuJSBZL8tNLxhZ9B74afhObhLVzW6ZB\nT3pk/5XcSPOTvcWd9k1yOKJUabCuF5ixFmMz+niFqUVQTtnl7aqOZ+GrDEzmoYmG\nNQROP0EiA1TWtm2+Ja0FqiJauXytt1sIF/Pr5L47FCjtmZKVoXTP8RVFfGLPB0kT\ndjOz53PaEE3GSValh85w24XIH2/gczURUnjphCX1bRwTFr14SfA9X/rFWqv9SqWQ\nV8OiIWrSiwNd5RLJ9q0B+viDzoxrjmnMJZikxhKiuNVKJCu2ccBdMrbW42iBM2w3\n
... (for brevity)
\n-----END PGP MESSAGE-----
When I try to read the string back in to recreate the public key, I get an EOFException:
// Import the public key.
ByteArrayInputStream in =
new ByteArrayInputStream(stored.publicKey.getBytes(
Charset.forName("US-ASCII")));
// Needed to read ASCII armored keys
InputStream decoded = PGPUtil.getDecoderStream(in);
BCPGInputStream bcpgIn = new BCPGInputStream(decoded);
RSAPublicBCPGKey bcpgKey = new RSAPublicBCPGKey(bcpgIn);
PublicKeyPacket pkPacket = new PublicKeyPacket(PublicKeyAlgorithmTags.RSA_ENCRYPT,
new Date(), bcpgKey);
publicKey = new PGPPublicKey(pkPacket, new BcKeyFingerprintCalculator());
The exception occurs when I create the RSAPublicBCPGKey.
I am doing something very wrong, but I can't figure out what it is. Can anyone help?
Say you already have your pgp public key (ascii armored) in a String str:
InputStream in=new ByteArrayInputStream(str.getBytes());
in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);
JcaPGPPublicKeyRingCollection pgpPub = new JcaPGPPublicKeyRingCollection(in);
in.close();
PGPPublicKey key = null;
Iterator<PGPPublicKeyRing> rIt = pgpPub.getKeyRings();
while (key == null && rIt.hasNext())
{
PGPPublicKeyRing kRing = rIt.next();
Iterator<PGPPublicKey> kIt = kRing.getPublicKeys();
while (key == null && kIt.hasNext())
{
PGPPublicKey k = kIt.next();
if (k.isEncryptionKey())
{
key = k;
}
}
}
return key;
Now, your variable key will have your PGPPublicKey.
I ran into this issue, and it turned out that the Public Key Ring was actually the public key itself. When I was running the iterator I was actually getting subkeys (which turns into that message block instead of the public key block). If I'm not explaining that well, check out this explanation.
Assuming realContact is a public key ring that only contains subkeys, you don't actually need to run it through getPublicKey().

Difference result by using sign and verify for signature

I tried to use private key to sign the message digest to generate the signature, and then use public key to verify the signature to get message digest. I want to compare with this two message digests, but i got a error about "java.security.SignatureException: Signature length not correct: got 250 but was expecting 128".
When i tried to print out Arrays.toString(data2), its all 0.
I also tried to follow the link: Verify the Signature
public void firstDigitalSignature() throws IOException, NoSuchAlgorithmException, Throwable
{
//*Generate Message Digest1*
byte[] buffer=null;
buffer = new byte[(int) inputFile1.length()];
FileInputStream fis = new FileInputStream(inputFile1);
fis.read(buffer);
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(buffer);
digestBytes = messageDigest.digest();
//convert the byte to hex format method 2
StringBuffer hexString = new StringBuffer();
for(int i=0;i<digestBytes.length;i++){
hexString.append(Integer.toHexString(0xFF & digestBytes[i]));
}
System.out.println("Message Digest-1: "+hexString.toString());
//*Using private key to encrypt the image-Digital signauture1 *
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(digestBytes);
encryptmd= signature.sign();
StringBuffer hexString2 = new StringBuffer();
for(int i=0;i<encryptmd.length;i++){
hexString2.append(Integer.toHexString(0xFF & encryptmd[i]));
x=hexString2.toString();
File file = new File("c://Directroy111");
if (!file.exists()) {
if (file.mkdir()) {
System.out.println("Doctor is created!");
} else {
System.out.println("Failed to create Doctor!");
}
}
BufferedWriter out = new BufferedWriter(
new FileWriter("C:\\Directroy111\\Digital Signature Doctor.txt"));
out.write(x);
out.close();
this.copyImageFiles(sourceFile, destinationDir);
}
System.out.println("Message Digest Encrypted-1: "+hexString2.toString()+"\n");
}
public void firstVerify() throws IOException, NoSuchAlgorithmException, Throwable
{
//Generate Message Digest1 - Decrypt
String verifyfile= "c:\\Directroy111\\2.jpg";
File decryptfile= new File(verifyfile);
byte[] buffer2=null;
buffer2 = new byte[(int) decryptfile.length()]; //array type is integer, thats why we use int here
FileInputStream fis2 = new FileInputStream(decryptfile);
fis2.read(buffer2);
MessageDigest messageDigest2 = MessageDigest.getInstance("SHA-256");
messageDigest2.update(buffer2);
byte[] digestBytes2 = messageDigest2.digest();
StringBuffer hexString22 = new StringBuffer();
for(int i=0;i<digestBytes2.length;i++){
hexString22.append(Integer.toHexString(0xFF & digestBytes2[i]));
}
System.out.println("Message Digest(Hash)-1(Decryption): "+hexString22.toString()); //System.out.println(hexString);
//*******Decypt*************//
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
//FileReader read= new FileReader("C:\\TEMP1\\Doctor\\Digital Signature Doctor.txt");
FileInputStream br2 = new FileInputStream("C:\\Directroy111\\Digital Signature Doctor.txt");
//BufferedInputStream bis=new BufferedInputStream(br2);
//BufferedReader br = new BufferedReader(new FileReader(br2));
byte[] data2=new byte[br2.available()];
System.out.println(Arrays.toString(data2));
br2.read(data2);
br2.close();
FileInputStream datafis=new FileInputStream("C:\\Directroy111\\Digital Signature Doctor.txt");
BufferedInputStream bufin=new BufferedInputStream(datafis);
byte[] buffer=new byte[1024];
int len;
while(bufin.available()!=0){
len=bufin.read(buffer);
signature.update(buffer,0,len);
};
bufin.close();
System.out.println("111111");
boolean decryptmd2= signature.verify(data2);
System.out.println("signature verifies: " + decryptmd2);
if(decryptmd2==false){
str = String.valueOf(decryptmd2);
System.out.println("Message Digest-1(Decryption): "+str);
}else{
System.out.println("1111");
}
//**Verify*
if(str.equals(hexString22.toString())){
System.out.println("Digital Signature-1 was not modified"+"\n");
}else{
System.out.println("ERROR!!! Digital Signature-1 was modified"+"\n");
}
}
I see a couple issues here. First, the actual signing code should be included with your question as well. Without being able to see the signing code, it's more difficult to debug the problem. Check if your signing algorithm is the same as your verification algorithm (SHA256withRSA).
Next, if you're using the SHA256withRSA algorithm, you don't need to calculate a message digest first. The algorithm will calculate it for you and sign the digest. You just pass in the entire file to be signed.
In your verification step, you are updating the Signature object with the same file that you are saying is the signature. Both files are Digital Signature Doctor.txt. You should update the Signature object with the file to be signed, which I believe from your example is 2.jpg. Then when you call the verify() method, pass in the signature bytes.
Finally, you are misinterpreting the return value of verify(). It returns a boolean. It does not return the message digest or the original message. When your code does String.valueOf(decryptmd2), the value will either be "false" or "true".
I'm not sure why you are trying to compare the message digests. Let's say you are sent a message with a signature signed by a private key corresponding to a public key that you trust. IF the verify() method returns true, you can be sure that:
the message was signed by that person
the message you received is identical to the message that was signed
That should be sufficient for what you need.
Echoing gtrig's comments, here is a complete example showing signature creation and verification:
import java.io.*;
import java.security.*;
public class RSASignatureExample {
private static int BUFSIZE = 8192;
private PrivateKey privateKey;
private PublicKey publicKey;
public RSASignatureExample() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair kp = kpg.generateKeyPair();
privateKey = kp.getPrivate();
publicKey = kp.getPublic();
}
private static void processData(File f, Signature s) throws Exception {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f), BUFSIZE);
byte[] buf = new byte[BUFSIZE];
int numRead;
while ((numRead = bis.read(buf)) > 0) {
s.update(buf, 0, numRead);
}
bis.close();
}
public byte[] sign(File fileToSign) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
processData(fileToSign, signature);
return signature.sign();
}
public boolean verify(File fileToVerify, byte[] signatureBytes) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
processData(fileToVerify, signature);
return signature.verify(signatureBytes);
}
public static void main(String[] args) throws Exception {
RSASignatureExample example = new RSASignatureExample();
File tempFile = File.createTempFile("rsa", null);
FileOutputStream fos = new FileOutputStream(tempFile);
fos.write("Hello World".getBytes("UTF-8"));
fos.close();
// Sign the file
byte [] signatureBytes = example.sign(tempFile);
// Verify the signature
boolean isVerified = example.verify(tempFile, signatureBytes);
System.out.printf("Signature verified ?: %b", isVerified);
}
}

Getting GPG Decryption To Work In Java (Bouncy Castle)

let me start by saying I'm extremely new to all of this. What I am trying to do is to use gpg from within Java in order to decrypt an encrypted file.
What I've done successfully:
Had a colleague encrypt a file using my public key and his private key and successfully decrypted it.
Went the other way
Had another colleague try to decrypt a file that wasn't for him: fail (as expected)
My key was generated like this...
(gpg --version tells me I'm using 1.4.5 and I'm using Bouncy Castle 1.47)
gpg --gen-ley
Select option "DSA and Elgamal (default)"
Fill in the other fields and generate a key.
The file was encrypted using my public key and another's secret key. I want to decrypt it. I've written the following Java code to accomplish this. I'm using several deprecated methods, but I can't figure out how to properly implement the factory methods required to use the non-deprecated versions, so if anyone has an idea on implementations of those that I should be using that would be a nice bonus.
Security.addProvider(new BouncyCastleProvider());
PGPSecretKeyRingCollection secretKeyRing = new PGPSecretKeyRingCollection(new FileInputStream(new File("test-files/secring.gpg")));
PGPSecretKeyRing pgpSecretKeyRing = (PGPSecretKeyRing) secretKeyRing.getKeyRings().next();
PGPSecretKey secretKey = pgpSecretKeyRing.getSecretKey();
PGPPrivateKey privateKey = secretKey.extractPrivateKey("mypassword".toCharArray(), "BC");
System.out.println(privateKey.getKey().getAlgorithm());
System.out.println(privateKey.getKey().getFormat());
PGPObjectFactory pgpF = new PGPObjectFactory(
new FileInputStream(new File("test-files/test-file.txt.gpg")));
Object pgpObj = pgpF.nextObject();
PGPEncryptedDataList encryptedDataList = (PGPEncryptedDataList) pgpObj;
Iterator objectsIterator = encryptedDataList.getEncryptedDataObjects();
PGPPublicKeyEncryptedData publicKeyEncryptedData = (PGPPublicKeyEncryptedData) objectsIterator.next();
InputStream inputStream = publicKeyEncryptedData.getDataStream(privateKey, "BC");
So when I run this code I learn that my algorithm and format are as follows for my secret key:
Algorithm: DSA
Format: PKCS#8
And then it breaks on the last line:
Exception in thread "main" org.bouncycastle.openpgp.PGPException: error setting asymmetric cipher
at org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder.decryptSessionData(Unknown Source)
at org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder.access$000(Unknown Source)
at org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder$2.recoverSessionData(Unknown Source)
at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
at TestBouncyCastle.main(TestBouncyCastle.java:74)
Caused by: java.security.InvalidKeyException: unknown key type passed to ElGamal
at org.bouncycastle.jcajce.provider.asymmetric.elgamal.CipherSpi.engineInit(Unknown Source)
at org.bouncycastle.jcajce.provider.asymmetric.elgamal.CipherSpi.engineInit(Unknown Source)
at javax.crypto.Cipher.init(DashoA13*..)
at javax.crypto.Cipher.init(DashoA13*..)
... 8 more
I'm open to a lot of suggestions here, from "don't use gpg, use x instead" to "don't use bouncy castle, use x instead" to anything in between. Thanks!
If anyone is interested to know how to encrypt and decrypt gpg files using bouncy castle openPGP library, check the below java code:
The below are the 4 methods you going to need:
The below method will read and import your secret key from .asc file:
public static PGPSecretKey readSecretKeyFromCol(InputStream in, long keyId) throws IOException, PGPException {
in = PGPUtil.getDecoderStream(in);
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(in, new BcKeyFingerprintCalculator());
PGPSecretKey key = pgpSec.getSecretKey(keyId);
if (key == null) {
throw new IllegalArgumentException("Can't find encryption key in key ring.");
}
return key;
}
The below method will read and import your public key from .asc file:
#SuppressWarnings("rawtypes")
public static PGPPublicKey readPublicKeyFromCol(InputStream in) throws IOException, PGPException {
in = PGPUtil.getDecoderStream(in);
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in, new BcKeyFingerprintCalculator());
PGPPublicKey key = null;
Iterator rIt = pgpPub.getKeyRings();
while (key == null && rIt.hasNext()) {
PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next();
Iterator kIt = kRing.getPublicKeys();
while (key == null && kIt.hasNext()) {
PGPPublicKey k = (PGPPublicKey) kIt.next();
if (k.isEncryptionKey()) {
key = k;
}
}
}
if (key == null) {
throw new IllegalArgumentException("Can't find encryption key in key ring.");
}
return key;
}
The below 2 methods to decrypt and encrypt gpg files:
public void decryptFile(InputStream in, InputStream secKeyIn, InputStream pubKeyIn, char[] pass) throws IOException, PGPException, InvalidCipherTextException {
Security.addProvider(new BouncyCastleProvider());
PGPPublicKey pubKey = readPublicKeyFromCol(pubKeyIn);
PGPSecretKey secKey = readSecretKeyFromCol(secKeyIn, pubKey.getKeyID());
in = PGPUtil.getDecoderStream(in);
JcaPGPObjectFactory pgpFact;
PGPObjectFactory pgpF = new PGPObjectFactory(in, new BcKeyFingerprintCalculator());
Object o = pgpF.nextObject();
PGPEncryptedDataList encList;
if (o instanceof PGPEncryptedDataList) {
encList = (PGPEncryptedDataList) o;
} else {
encList = (PGPEncryptedDataList) pgpF.nextObject();
}
Iterator<PGPPublicKeyEncryptedData> itt = encList.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData encP = null;
while (sKey == null && itt.hasNext()) {
encP = itt.next();
secKey = readSecretKeyFromCol(new FileInputStream("PrivateKey.asc"), encP.getKeyID());
sKey = secKey.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass));
}
if (sKey == null) {
throw new IllegalArgumentException("Secret key for message not found.");
}
InputStream clear = encP.getDataStream(new BcPublicKeyDataDecryptorFactory(sKey));
pgpFact = new JcaPGPObjectFactory(clear);
PGPCompressedData c1 = (PGPCompressedData) pgpFact.nextObject();
pgpFact = new JcaPGPObjectFactory(c1.getDataStream());
PGPLiteralData ld = (PGPLiteralData) pgpFact.nextObject();
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
InputStream inLd = ld.getDataStream();
int ch;
while ((ch = inLd.read()) >= 0) {
bOut.write(ch);
}
//System.out.println(bOut.toString());
bOut.writeTo(new FileOutputStream(ld.getFileName()));
//return bOut;
}
public static void encryptFile(OutputStream out, String fileName, PGPPublicKey encKey) throws IOException, NoSuchProviderException, PGPException {
Security.addProvider(new BouncyCastleProvider());
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
PGPUtil.writeFileToLiteralData(comData.open(bOut), PGPLiteralData.BINARY, new File(fileName));
comData.close();
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.TRIPLE_DES).setSecureRandom(new SecureRandom()));
cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(encKey));
byte[] bytes = bOut.toByteArray();
OutputStream cOut = cPk.open(out, bytes.length);
cOut.write(bytes);
cOut.close();
out.close();
}
Now here is how to invoke/run the above:
try {
decryptFile(new FileInputStream("encryptedFile.gpg"), new FileInputStream("PrivateKey.asc"), new FileInputStream("PublicKey.asc"), "yourKeyPassword".toCharArray());
PGPPublicKey pubKey = readPublicKeyFromCol(new FileInputStream("PublicKey.asc"));
encryptFile(new FileOutputStream("encryptedFileOutput.gpg"), "fileToEncrypt.txt", pubKey);
} catch (PGPException e) {
fail("exception: " + e.getMessage(), e.getUnderlyingException());
}
To any one looking for an alternative solution, see https://stackoverflow.com/a/42176529/7550201
final InputStream plaintextStream = BouncyGPG
.decryptAndVerifyStream()
.withConfig(keyringConfig)
.andRequireSignatureFromAllKeys("sender#example.com")
.fromEncryptedInputStream(cipherTextStream)
Long story short: Bouncycastle is programming is often a lot of cargo cult programming and I wrote a library to change that.
I've decided to go with a much different approach, which is to forego the use of bouncy castle altogether and simply use a runtime process instead. For me this solution is working and completely removes the complexity surrounding bouncy castle:
String[] gpgCommands = new String[] {
"gpg",
"--passphrase",
"password",
"--decrypt",
"test-files/accounts.txt.gpg"
};
Process gpgProcess = Runtime.getRuntime().exec(gpgCommands);
BufferedReader gpgOutput = new BufferedReader(new InputStreamReader(gpgProcess.getInputStream()));
BufferedReader gpgError = new BufferedReader(new InputStreamReader(gpgProcess.getErrorStream()));
After doing that you need to remember to drain your input stream as your process is execing or your program will probably hang depending on how much you're outputing. See my answer in this thread (and also that of Cameron Skinner and Matthew Wilson who got me on the proper path) for a bit more context: Calling GnuPG in Java via a Runtime Process to encrypt and decrypt files - Decrypt always hangs
The first Google result is this. It looks like you are trying to decrypt ElGamal data, but you are not passing in an ElGamal key.
There are two easy possibilities:
Your keyring collection has multiple keyrings.
Your keyring has subkeys.
You've picked DSA with ElGamal encryption, so I suspect at least the latter: Subkeys are signed by the master key; ElGamal is not a signing algorithm (I don't know if DSA and ElGamal can use the same key, but it's generally seen as a good idea to use different keys for different purposes).
I think you want something like this (also, secretKeyRing should probably be renamed to secretKeyRingCollection):
PGPSecretKey secretKey = secretKeyRing.getSecretKey(publicKeyEncryptedData.getKeyID());
The error message is difficult because it's not completely accurate. Besides the illegal key size or default parameters the exception doesn't says it could be failing because of crypto permission check fails. That means you haven't setup the JCE permissions properly. You'll need to install the JCE Unlimited Strength Policy.
You can see the debug messages by setting the system property on the jvm
java -Djava.security.debug=access ....

Categories