让我首先说我对这一切都是新手。我想要做的是在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:传递给ElGamal的未知密钥类型 在org.bouncycastle.jcajce.provider.asymmetric.elgamal.CipherSpi.engineInit(未知来源) 在org.bouncycastle.jcajce.provider.asymmetric.elgamal.CipherSpi.engineInit(未知来源) 在javax.crypto.Cipher.init(DashoA13 * ..) 在javax.crypto.Cipher.init(DashoA13 * ..) ......还有8个
我在这里提出了许多建议,从“不要使用gpg,使用x代替”到“不要使用充气城堡,使用x代替”来介于两者之间。谢谢!
答案 0 :(得分:9)
如果有兴趣知道如何使用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<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();
}
现在这里是如何调用/运行上面的内容:
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());
}
答案 1 :(得分:1)
第一个Google结果为this。看起来您正在尝试解密ElGamal数据,但您没有传入ElGamal密钥。
有两种简单的可能性:
你选择了带有ElGamal加密的DSA,所以我怀疑至少是后者:子键是由主密钥签名的; ElGamal不是签名算法(我不知道DSA和ElGamal是否可以使用相同的密钥,但通常认为使用不同的密钥用于不同的目的是一个好主意。)
我认为你想要这样的东西(同样,secretKeyRing
可能应该重命名为secretKeyRingCollection
):
PGPSecretKey secretKey = secretKeyRing.getSecretKey(publicKeyEncryptedData.getKeyID());
答案 2 :(得分:1)
我决定采用一种截然不同的方法,即完全放弃使用充气城堡,而只是简单地使用运行时过程。对我来说,这个解决方案正在发挥作用,完全消除了有关充气城堡的复杂性:
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()));
执行此操作后,您需要记住在进程执行时耗尽输入流,否则程序可能会挂起,具体取决于您输出的数量。看看我在这个帖子中的答案(以及Cameron Skinner和Matthew Wilson的答案,让我走上了正确的道路)以获得更多背景:Calling GnuPG in Java via a Runtime Process to encrypt and decrypt files - Decrypt always hangs
答案 3 :(得分:1)
对于寻找替代解决方案的任何人,请参阅https://stackoverflow.com/a/42176529/7550201
final InputStream plaintextStream = BouncyGPG
.decryptAndVerifyStream()
.withConfig(keyringConfig)
.andRequireSignatureFromAllKeys("sender@example.com")
.fromEncryptedInputStream(cipherTextStream)
长话短说:Bouncycastle编程常常是很多cargo cult programming而且我写了一个库来改变它。
答案 4 :(得分:-1)
该错误消息很困难,因为它不完全准确。除了非法的密钥大小或默认参数外,该异常并不表示它可能由于加密权限检查失败而失败。这意味着您没有正确设置JCE权限。您需要安装JCE Unlimited Strength Policy。
您可以通过在jvm上设置系统属性来查看调试消息
java -Djava.security.debug=access ....