BouncyCastle PGP and McAfee eBusiness Server 8.6 Incompatibility

For several weeks now, I have been banging my head against the wall, trying to understand why our bank cannot decrypt a message signed and encrypted using BouncyCastle PGP. The bank uses McAfee E-Business Server 8.6 for decryption.

Data is encrypted with a public bank key and signed with our private key.

Using our own public key for encryption, I can successfully decrypt and verify the signature in the file created using the code below. Gnupg can decrypt and verify the file just fine.

However, the bank cannot decrypt the file. I tried to disable compression first, and then turn off ASCII. None of the options work, and they always get the same error message no matter what parameters I try to execute:

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 < XXXXXX@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 that I use to make a 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 found many reports using Google, but no resolutions or follow-up.

+6
source share
1 answer

I answer my question in the hope that this will help someone else in the queue, since the answer to this question is VERY hard to find anywhere on the Internet.

In versions of McAfee E-Business Server prior to 8.6, there was a compatibility issue with PGP BouncyCastle, and most people seemed to be unable to get it working. Therefore, if your supplier / client / bank uses the version of E-Business Server up to 8.6, you may well be SOL and you may need to find another encryption package.

Source: https://kc.mcafee.com/corporate/index?page=content&id=KB60816&cat=CORP_EBUSINESS_SERVER&actp=LIST

"Decrypting a file that has been encrypted with Bouncy Castle v1.37 may result in an Access Violation Error (or SIGSEG on UNIX). This issue was in this release."

Fortunately, our bank uses McAfee E-Business Server 8.6. However, this is only part of the equation. To solve the incompatibility problem, we had to disable BOTH and ASCII compression before they could successfully decrypt and verify our file. Therefore, using the source code that I published, you call it that for clients using E-Business Server 8.6:

 PGPService.signAndEncrypt(clearTextFileName, secureFileName, privKeyIn, privateKeyFilePassword, pubKeyIn, true, false, false); 

Of course, this means that you cannot use ASCII reservations, which may or may not be a problem with you. If so, David on the BouncyCastle dev mailing list suggested you use BouncyCastle in non-packet mode. IE: Do not pass a byte buffer to open commands in a stream. Or write and encrypt the file in two passes.

For example, calling:

 public static void signFile(String fileNameIn, String fileNameOut, InputStream privKeyIn, String password, boolean armoredOutput) { OutputStream out = null; BCPGOutputStream bOut = null; OutputStream lOut = null; InputStream fIn = null; try { out = new FileOutputStream(fileNameOut); if (armoredOutput) { out = new ArmoredOutputStream(out); } PGPSecretKey pgpSec = readSecretKey(privKeyIn); PGPPrivateKey pgpPrivKey = pgpSec.extractPrivateKey(password.toCharArray(), "BC"); PGPSignatureGenerator sGen = new PGPSignatureGenerator(pgpSec.getPublicKey().getAlgorithm(), PGPUtil.SHA1, "BC"); sGen.initSign(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.ZLIB); bOut = new BCPGOutputStream(cGen.open(out)); sGen.generateOnePassVersion(false).encode(bOut); File file = new File(fileNameIn); PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); lOut = lGen.open(bOut, PGPLiteralData.BINARY, file); fIn = new FileInputStream(file); int ch = 0; while ((ch = fIn.read()) >= 0) { lOut.write(ch); sGen.update((byte) ch); } lGen.close(); sGen.generate().encode(bOut); cGen.close(); } catch (Exception e) { log.error(e); throw new RuntimeException(e); } finally { if (lOut != null) try { lOut.close(); } catch (IOException e) {} if (bOut != null) try { bOut.close(); } catch (IOException e) {} if (out != null) try { out.close(); } catch (IOException e) {} if (fIn != null) try { fIn.close(); } catch (IOException e) {} } } 

The following is a call:

 public static byte[] encrypt(byte[] data, InputStream pubKeyIn, boolean isPublicKeyArmored) { FileOutputStream fos = null; BufferedReader isr = null; try { if (isPublicKeyArmored) pubKeyIn = new ArmoredInputStream(pubKeyIn); PGPPublicKey key = readPublicKeyFromCol(pubKeyIn); log.info("Creating a temp file..."); // Create a file and write the string to it. File tempfile = File.createTempFile("pgp", null); fos = new FileOutputStream(tempfile); fos.write(data); fos.close(); log.info("Temp file created at: " + tempfile.getAbsolutePath()); log.info("Reading the temp file to make sure that the bits were written...\n"); isr = new BufferedReader(new FileReader(tempfile)); String line = ""; while ((line = isr.readLine()) != null ) { log.info(line + "\n"); } int count = 0; for (java.util.Iterator iterator = key.getUserIDs(); iterator.hasNext();) { count++; log.info(iterator.next()); } log.info("Key Count = " + count); // Encrypt the data. ByteArrayOutputStream baos = new ByteArrayOutputStream(); _encrypt(tempfile.getAbsolutePath(), baos, key); log.info("Encrypted text length = " + baos.size()); tempfile.delete(); return baos.toByteArray(); } catch (PGPException e) { log.error(e); throw new RuntimeException(e); } catch (Exception e) { log.error(e); throw new RuntimeException(e); } finally { if (fos != null) try { fos.close(); } catch (IOException e) {} if (isr != null) try { isr.close(); } catch (IOException e) {} } } 

Caution emptor, as I have not been able to test this method to make sure that this even solves the incompatibility problem. But this is a way you can try if you have no options, and use ASCII reservations for the E-Business Server recipient.

More information can be found in the BouncyCastle dev mailing list archive, which can help if you decide to go this route. Specifically, this thread: http://www.bouncycastle.org/devmailarchive/msg12080.html

+6
source

Source: https://habr.com/ru/post/892277/


All Articles