Caine Lai
2011-07-08 00:21:07 UTC
I have been banging my head against the wall now for a couple of weeks
trying to figure out why our bank cannot decrypt a message that has been
single-pass signed and encrypted using BouncyCastle PGP. The bank is using
McAfee E-Business Server 8.6 for decryption.
The data is encrypted with the bank's public key, and is signed with our
private key.
Using our own public key for encryption, I am successfully able to decrypt
and verify the signature on a file generated with the code below. Gnupg can
decrypt and verify the file just fine.
However, the bank is unable to decrypt the file. I have tried first turning
compression off, and then turning ASCII armoring off. Neither option seems
to work, and they always get the same error message no matter what options I
try:
event 1: initial
event 13: BeginLex
event 8: Analyze
File is encrypted. event 9: Recipients
Secret key is required to read it.
Key for user ID "XXXXXXXXX <***@XXXX>"
event 6: Passphrase
event 23: Decryption
symmetric cipher used: CAST5
event 3: error -11391
event 2: final
Error decrypting file '/somepath/FILENAME'.
Corrupt data.
Bad packet
exitcode = 32
Here is the code I am using to do the single pass sign and encrypt:
public class PGPService {
private static final Logger log = Logger.getLogger(PGPService.class);
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* A simple routine that opens a key ring file and loads the first
available key
* suitable for signature generation.
*
* @param input stream to read the secret key ring collection from.
* @return a secret key.
* @throws IOException on a problem with using the input stream.
* @throws PGPException if there is an issue parsing the input stream.
*/
@SuppressWarnings("rawtypes")
private static PGPSecretKey readSecretKey(InputStream input) throws
IOException, PGPException {
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
PGPUtil.getDecoderStream(input));
// We just loop through the collection till we find a key suitable
for encryption, in the real
// world you would probably want to be a bit smarter about this.
Iterator keyRingIter = pgpSec.getKeyRings();
while (keyRingIter.hasNext()) {
PGPSecretKeyRing keyRing = (PGPSecretKeyRing)keyRingIter.next();
Iterator keyIter = keyRing.getSecretKeys();
while (keyIter.hasNext()) {
PGPSecretKey key = (PGPSecretKey)keyIter.next();
if (key.isSigningKey()) {
return key;
}
}
}
throw new IllegalArgumentException("Can't find signing key in key
ring.");
}
/**
* Single pass signs and encrypts the given file to the given output
file using the provided keys.
*
* @param fileNameIn - The name and location of the source input file.
* @param fileNameOut - The name and location of the destination output
file.
* @param privateKeyIn - The input stream for the private key file.
* @param privateKeyPassword - The password for the private key file.
* @param publicEncryptionKey - The public encryption key.
* @param armoredOutput - Whether or not to ASCII armor the output.
*/
@SuppressWarnings("rawtypes")
public static void signAndEncrypt(String fileNameIn, String fileNameOut,
InputStream privateKeyIn, String privateKeyPassword, PGPPublicKey
publicEncryptionKey, boolean armoredOutput, boolean compress) {
int bufferSize = 1<<16;
InputStream input = null;
OutputStream finalOut = null;
OutputStream encOut = null;
OutputStream compressedOut = null;
OutputStream literalOut = null;
PGPEncryptedDataGenerator encryptedDataGenerator = null;
PGPCompressedDataGenerator compressedDataGenerator = null;
PGPSignatureGenerator signatureGenerator = null;
PGPLiteralDataGenerator literalDataGenerator = null;
try {
File output = new File(fileNameOut);
OutputStream out = new FileOutputStream(output);
if (armoredOutput) out = new ArmoredOutputStream(out);
// ? Use BCPGOutputStreams ?
// Init encrypted data generator
encryptedDataGenerator = new
PGPEncryptedDataGenerator(PGPEncryptedDataGenerator.CAST5, true, new
SecureRandom(), "BC");
encryptedDataGenerator.addMethod(publicEncryptionKey);
finalOut = new BufferedOutputStream(out, bufferSize);
encOut = encryptedDataGenerator.open(finalOut, new
byte[bufferSize]);
// Init compression
if (compress) {
compressedDataGenerator = new
PGPCompressedDataGenerator(PGPCompressedData.ZLIB);
compressedOut = new
BufferedOutputStream(compressedDataGenerator.open(encOut));
}
// Init signature
PGPSecretKey pgpSec = readSecretKey(privateKeyIn);
PGPPrivateKey pgpPrivKey =
pgpSec.extractPrivateKey(privateKeyPassword.toCharArray(), "BC");
signatureGenerator = new
PGPSignatureGenerator(pgpSec.getPublicKey().getAlgorithm(), PGPUtil.SHA1,
"BC");
signatureGenerator.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT,
pgpPrivKey);
Iterator it = pgpSec.getPublicKey().getUserIDs();
if (it.hasNext()) {
PGPSignatureSubpacketGenerator spGen = new
PGPSignatureSubpacketGenerator();
spGen.setSignerUserID(false, (String)it.next());
signatureGenerator.setHashedSubpackets(spGen.generate());
}
PGPOnePassSignature onePassSignature =
signatureGenerator.generateOnePassVersion(false);
if (compress) onePassSignature.encode(compressedOut);
else onePassSignature.encode(encOut);
// Create the Literal Data generator Output stream which writes
to the compression stream
literalDataGenerator = new PGPLiteralDataGenerator(true);
if (compress) literalOut =
literalDataGenerator.open(compressedOut, PGPLiteralData.BINARY,
output.getName(), new Date(), new byte[bufferSize]);
else literalOut = literalDataGenerator.open(encOut,
PGPLiteralData.TEXT, fileNameIn, new Date(), new byte[bufferSize]);
// Update sign and encrypt
byte[] buffer = new byte[bufferSize];
int bytesRead = 0;
input = new FileInputStream(fileNameIn);
while((bytesRead = input.read(buffer)) != -1) {
literalOut.write(buffer,0,bytesRead);
signatureGenerator.update(buffer,0,bytesRead);
literalOut.flush();
}
// Close Literal data stream and add signature
literalOut.close();
literalDataGenerator.close();
if (compress)
signatureGenerator.generate().encode(compressedOut);
else signatureGenerator.generate().encode(encOut);
} catch (Exception e) {
log.error(e);
throw new RuntimeException(e);
} finally {
// Close all streams
if (literalOut != null) try { literalOut.close(); } catch
(IOException e) {}
if (literalDataGenerator != null) try {
literalDataGenerator.close(); } catch (IOException e) {}
if (compressedOut != null) try { compressedOut.close(); } catch
(IOException e) {}
if (compressedDataGenerator != null) try {
compressedDataGenerator.close(); } catch (IOException e) {}
if (encOut != null) try { encOut.close(); } catch (IOException
e) {}
if (encryptedDataGenerator != null) try {
encryptedDataGenerator.close(); } catch (IOException e) {}
if (finalOut != null) try { finalOut.close(); } catch
(IOException e) {}
if (input != null) try { input.close(); } catch (IOException e)
{}
}
}
@SuppressWarnings("rawtypes")
private static PGPPublicKey readPublicKeyFromCol(InputStream in) throws
Exception {
PGPPublicKeyRing pkRing = null;
PGPPublicKeyRingCollection pkCol = new
PGPPublicKeyRingCollection(in);
log.info("Key ring size = " + pkCol.size());
Iterator it = pkCol.getKeyRings();
while (it.hasNext()) {
pkRing = (PGPPublicKeyRing) it.next();
Iterator pkIt = pkRing.getPublicKeys();
while (pkIt.hasNext()) {
PGPPublicKey key = (PGPPublicKey) pkIt.next();
log.info("Encryption key = " + key.isEncryptionKey() + ",
Master key = " +
key.isMasterKey());
if (key.isEncryptionKey()) {
// Find out a little about the keys in the public key
ring.
log.info("Key Strength = " + key.getBitStrength());
log.info("Algorithm = " + key.getAlgorithm());
log.info("Bit strength = " + key.getBitStrength());
log.info("Version = " + key.getVersion());
return key;
}
}
}
return null;
}
private static PGPPrivateKey findSecretKey(InputStream keyIn, long
keyID, char[] pass)
throws IOException, PGPException, NoSuchProviderException {
PGPSecretKeyRingCollection pgpSec = new
PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyIn));
PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
if (pgpSecKey == null) {
return null;
}
return pgpSecKey.extractPrivateKey(pass, "BC");
}
}
Any idea what could be causing this? I've found numerous reports using
Google, but no resolutions or follow-ups.
trying to figure out why our bank cannot decrypt a message that has been
single-pass signed and encrypted using BouncyCastle PGP. The bank is using
McAfee E-Business Server 8.6 for decryption.
The data is encrypted with the bank's public key, and is signed with our
private key.
Using our own public key for encryption, I am successfully able to decrypt
and verify the signature on a file generated with the code below. Gnupg can
decrypt and verify the file just fine.
However, the bank is unable to decrypt the file. I have tried first turning
compression off, and then turning ASCII armoring off. Neither option seems
to work, and they always get the same error message no matter what options I
try:
event 1: initial
event 13: BeginLex
event 8: Analyze
File is encrypted. event 9: Recipients
Secret key is required to read it.
Key for user ID "XXXXXXXXX <***@XXXX>"
event 6: Passphrase
event 23: Decryption
symmetric cipher used: CAST5
event 3: error -11391
event 2: final
Error decrypting file '/somepath/FILENAME'.
Corrupt data.
Bad packet
exitcode = 32
Here is the code I am using to do the single pass sign and encrypt:
public class PGPService {
private static final Logger log = Logger.getLogger(PGPService.class);
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* A simple routine that opens a key ring file and loads the first
available key
* suitable for signature generation.
*
* @param input stream to read the secret key ring collection from.
* @return a secret key.
* @throws IOException on a problem with using the input stream.
* @throws PGPException if there is an issue parsing the input stream.
*/
@SuppressWarnings("rawtypes")
private static PGPSecretKey readSecretKey(InputStream input) throws
IOException, PGPException {
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
PGPUtil.getDecoderStream(input));
// We just loop through the collection till we find a key suitable
for encryption, in the real
// world you would probably want to be a bit smarter about this.
Iterator keyRingIter = pgpSec.getKeyRings();
while (keyRingIter.hasNext()) {
PGPSecretKeyRing keyRing = (PGPSecretKeyRing)keyRingIter.next();
Iterator keyIter = keyRing.getSecretKeys();
while (keyIter.hasNext()) {
PGPSecretKey key = (PGPSecretKey)keyIter.next();
if (key.isSigningKey()) {
return key;
}
}
}
throw new IllegalArgumentException("Can't find signing key in key
ring.");
}
/**
* Single pass signs and encrypts the given file to the given output
file using the provided keys.
*
* @param fileNameIn - The name and location of the source input file.
* @param fileNameOut - The name and location of the destination output
file.
* @param privateKeyIn - The input stream for the private key file.
* @param privateKeyPassword - The password for the private key file.
* @param publicEncryptionKey - The public encryption key.
* @param armoredOutput - Whether or not to ASCII armor the output.
*/
@SuppressWarnings("rawtypes")
public static void signAndEncrypt(String fileNameIn, String fileNameOut,
InputStream privateKeyIn, String privateKeyPassword, PGPPublicKey
publicEncryptionKey, boolean armoredOutput, boolean compress) {
int bufferSize = 1<<16;
InputStream input = null;
OutputStream finalOut = null;
OutputStream encOut = null;
OutputStream compressedOut = null;
OutputStream literalOut = null;
PGPEncryptedDataGenerator encryptedDataGenerator = null;
PGPCompressedDataGenerator compressedDataGenerator = null;
PGPSignatureGenerator signatureGenerator = null;
PGPLiteralDataGenerator literalDataGenerator = null;
try {
File output = new File(fileNameOut);
OutputStream out = new FileOutputStream(output);
if (armoredOutput) out = new ArmoredOutputStream(out);
// ? Use BCPGOutputStreams ?
// Init encrypted data generator
encryptedDataGenerator = new
PGPEncryptedDataGenerator(PGPEncryptedDataGenerator.CAST5, true, new
SecureRandom(), "BC");
encryptedDataGenerator.addMethod(publicEncryptionKey);
finalOut = new BufferedOutputStream(out, bufferSize);
encOut = encryptedDataGenerator.open(finalOut, new
byte[bufferSize]);
// Init compression
if (compress) {
compressedDataGenerator = new
PGPCompressedDataGenerator(PGPCompressedData.ZLIB);
compressedOut = new
BufferedOutputStream(compressedDataGenerator.open(encOut));
}
// Init signature
PGPSecretKey pgpSec = readSecretKey(privateKeyIn);
PGPPrivateKey pgpPrivKey =
pgpSec.extractPrivateKey(privateKeyPassword.toCharArray(), "BC");
signatureGenerator = new
PGPSignatureGenerator(pgpSec.getPublicKey().getAlgorithm(), PGPUtil.SHA1,
"BC");
signatureGenerator.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT,
pgpPrivKey);
Iterator it = pgpSec.getPublicKey().getUserIDs();
if (it.hasNext()) {
PGPSignatureSubpacketGenerator spGen = new
PGPSignatureSubpacketGenerator();
spGen.setSignerUserID(false, (String)it.next());
signatureGenerator.setHashedSubpackets(spGen.generate());
}
PGPOnePassSignature onePassSignature =
signatureGenerator.generateOnePassVersion(false);
if (compress) onePassSignature.encode(compressedOut);
else onePassSignature.encode(encOut);
// Create the Literal Data generator Output stream which writes
to the compression stream
literalDataGenerator = new PGPLiteralDataGenerator(true);
if (compress) literalOut =
literalDataGenerator.open(compressedOut, PGPLiteralData.BINARY,
output.getName(), new Date(), new byte[bufferSize]);
else literalOut = literalDataGenerator.open(encOut,
PGPLiteralData.TEXT, fileNameIn, new Date(), new byte[bufferSize]);
// Update sign and encrypt
byte[] buffer = new byte[bufferSize];
int bytesRead = 0;
input = new FileInputStream(fileNameIn);
while((bytesRead = input.read(buffer)) != -1) {
literalOut.write(buffer,0,bytesRead);
signatureGenerator.update(buffer,0,bytesRead);
literalOut.flush();
}
// Close Literal data stream and add signature
literalOut.close();
literalDataGenerator.close();
if (compress)
signatureGenerator.generate().encode(compressedOut);
else signatureGenerator.generate().encode(encOut);
} catch (Exception e) {
log.error(e);
throw new RuntimeException(e);
} finally {
// Close all streams
if (literalOut != null) try { literalOut.close(); } catch
(IOException e) {}
if (literalDataGenerator != null) try {
literalDataGenerator.close(); } catch (IOException e) {}
if (compressedOut != null) try { compressedOut.close(); } catch
(IOException e) {}
if (compressedDataGenerator != null) try {
compressedDataGenerator.close(); } catch (IOException e) {}
if (encOut != null) try { encOut.close(); } catch (IOException
e) {}
if (encryptedDataGenerator != null) try {
encryptedDataGenerator.close(); } catch (IOException e) {}
if (finalOut != null) try { finalOut.close(); } catch
(IOException e) {}
if (input != null) try { input.close(); } catch (IOException e)
{}
}
}
@SuppressWarnings("rawtypes")
private static PGPPublicKey readPublicKeyFromCol(InputStream in) throws
Exception {
PGPPublicKeyRing pkRing = null;
PGPPublicKeyRingCollection pkCol = new
PGPPublicKeyRingCollection(in);
log.info("Key ring size = " + pkCol.size());
Iterator it = pkCol.getKeyRings();
while (it.hasNext()) {
pkRing = (PGPPublicKeyRing) it.next();
Iterator pkIt = pkRing.getPublicKeys();
while (pkIt.hasNext()) {
PGPPublicKey key = (PGPPublicKey) pkIt.next();
log.info("Encryption key = " + key.isEncryptionKey() + ",
Master key = " +
key.isMasterKey());
if (key.isEncryptionKey()) {
// Find out a little about the keys in the public key
ring.
log.info("Key Strength = " + key.getBitStrength());
log.info("Algorithm = " + key.getAlgorithm());
log.info("Bit strength = " + key.getBitStrength());
log.info("Version = " + key.getVersion());
return key;
}
}
}
return null;
}
private static PGPPrivateKey findSecretKey(InputStream keyIn, long
keyID, char[] pass)
throws IOException, PGPException, NoSuchProviderException {
PGPSecretKeyRingCollection pgpSec = new
PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyIn));
PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
if (pgpSecKey == null) {
return null;
}
return pgpSecKey.extractPrivateKey(pass, "BC");
}
}
Any idea what could be causing this? I've found numerous reports using
Google, but no resolutions or follow-ups.