让GPG解密工作在Java(Bouncy Castle)

首先让我说我对这一切都是新手。 我想要做的是使用Java中的gpg来解密加密文件。

我成功完成了什么:

  • 如果同事使用我的公钥和他的私钥加密文件并成功解密它。

  • 走了另一条路

  • 让另一位同事尝试解密一个不适合他的文件:失败(如预期的那样)

我的密钥是这样生成的……

(gpg –version告诉我我正在使用1.4.5而我正在使用Bouncy Castle 1.47)

gpg –gen-ley

选择选项“DSA和Elgamal(默认)”

填写其他字段并生成密钥。

该文件使用我的公钥和另一个密钥加密。 我想解密它。 我编写了以下Java代码来完成此任务。 我正在使用几种不推荐使用的方法,但我无法弄清楚如何正确实现使用非弃用版本所需的工厂方法,所以如果有人对我应该使用的那些实现有一个想法,那将是一个很好的奖金。

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"); 

因此,当我运行此代码时,我了解到我的密钥的算法和格式如下:

算法:DSA格式:PKCS#8

然后它在最后一行打破:

 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) 

引起:java.security.InvalidKeyException:未知密钥类型在org.bouncycastle.jcajce.provider.asymmetric.elgamal.CipherSpi的org.bouncycastle.jcajce.provider.asymmetric.elgamal.CipherSpi.engineInit(未知来源)传递给ElGamal。在javax.crypto.Cipher.init(DashoA13 * ..)的javax.crypto.Cipher.init(DashoA13 * ..)… 8更多的engineInit(未知来源)

我在这里提出了很多建议,从“不要使用gpg,使用x代替”到“不要使用充气城堡,使用x代替”来介于两者之间。 谢谢!

如果有人有兴趣知道如何使用bouncy castle openPGP库加密和解密gpg文件,请检查以下java代码:

以下是您需要的4种方法:

以下方法将从.asc文件中读取并导入您的密钥:

 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; } 

以下方法将从.asc文件中读取并导入您的公钥:

 @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; } 

以下2种解密和加密gpg文件的方法:

 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 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(); } 

现在这里是如何调用/运行上面的:

 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()); } 

我决定采用一种截然不同的方法,即完全放弃使用充气城堡,而只是简单地使用运行时过程。 对我来说,这个解决方案正在发挥作用,完全消除了充气城堡的复杂性:

 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())); 

执行此操作后,您需要记住在进程执行时耗尽输入流,否则程序可能会挂起,具体取决于您输出的数量。 看看我在这个post中的答案(以及Cameron Skinner和Matthew Wilson的答案,让我走上正确的道路)以获得更多上下文: 通过运行时进程在Java中调用GnuPG来加密和解密文件 – Decrypt始终挂起

谷歌的第一个结果就是这个 。 看起来您正在尝试解密ElGamal数据,但您没有传入ElGamal密钥。

有两种简单的可能性:

  • 您的钥匙圈系列有多个钥匙圈。
  • 你的密钥环有子密钥。

你已经选择了带有ElGamal加密的DSA,所以我怀疑至少是后者:子键是由主密钥签名的; ElGamal不是签名算法(我不知道DSA和ElGamal是否可以使用相同的密钥,但通常认为使用不同密钥用于不同目的是个好主意)。

我想你想要这样的东西(另外, secretKeyRing应该可以重命名为secretKeyRingCollection ):

 PGPSecretKey secretKey = secretKeyRing.getSecretKey(publicKeyEncryptedData.getKeyID()); 

对于寻找替代解决方案的任何人,请参阅https://stackoverflow.com/a/42176529/7550201

 final InputStream plaintextStream = BouncyGPG .decryptAndVerifyStream() .withConfig(keyringConfig) .andRequireSignatureFromAllKeys("sender@example.com") .fromEncryptedInputStream(cipherTextStream) 

长话短说:Bouncycastle编程通常是很多货物编程 ,我写了一个库来改变它。

Interesting Posts