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.
首先让我说我对这一切都是新手。我想要做的是使用Java中的gpg来解密加密文件。
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 --version告诉我我正在使用1.4.5而我正在使用Bouncy Castle 1.47)
gpg --gen-ley
gpg --gen-ley
Select option "DSA and Elgamal (default)"
选择选项“DSA和Elgamal(默认)”
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.
该文件使用我的公钥和另一个密钥加密。我想解密它。我编写了以下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");
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
算法:DSA格式: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
引起: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(未知来源)
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!
我在这里提出了很多建议,从“不要使用gpg,使用x代替”到“不要使用充气城堡,使用x代替”来介于两者之间。谢谢!
8
If anyone is interested to know how to encrypt and decrypt gpg files using bouncy castle openPGP library, check the below java code:
如果有人有兴趣知道如何使用bouncy castle openPGP库加密和解密gpg文件,请检查以下java代码:
The below are the 4 methods you going to need:
以下是您需要的4种方法:
The below method will read and import your secret key from .asc file:
以下方法将从.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;
}
The below method will read and import your public key from .asc file:
以下方法将从.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;
}
The below 2 methods to decrypt and encrypt gpg files:
以下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();
}
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());
}
2
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
执行此操作后,您需要记住在进程执行时耗尽输入流,否则程序可能会挂起,具体取决于您输出的数量。看看我在这个帖子中的答案(以及Cameron Skinner和Matthew Wilson的答案,让我走上正确的道路)以获得更多上下文:通过运行时进程在Java中调用GnuPG来加密和解密文件 - Decrypt始终挂起
1
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.
谷歌的第一个结果就是这个。看起来您正在尝试解密ElGamal数据,但您没有传入ElGamal密钥。
There are two easy possibilities:
有两种简单的可能性:
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).
你已经选择了带有ElGamal加密的DSA,所以我怀疑至少是后者:子键是由主密钥签名的; ElGamal不是签名算法(我不知道DSA和ElGamal是否可以使用相同的密钥,但通常认为使用不同密钥用于不同目的是个好主意)。
I think you want something like this (also, secretKeyRing
should probably be renamed to secretKeyRingCollection
):
我想你想要这样的东西(另外,secretKeyRing应该可以重命名为secretKeyRingCollection):
PGPSecretKey secretKey = secretKeyRing.getSecretKey(publicKeyEncryptedData.getKeyID());
0
To any one looking for an alternative solution, see https://stackoverflow.com/a/42176529/7550201
对于寻找替代解决方案的任何人,请参阅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.
长话短说:Bouncycastle编程通常是很多货物编程,我写了一个库来改变它。