15

首先让我说我对这一切都非常陌生。我想要做的是在 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.engineInit(Unknown Source) 在 org.bouncycastle.jcajce.provider.asymmetric.elgamal.CipherSpi 处传递给 ElGamal 的未知密钥类型。 engineInit(Unknown Source) at javax.crypto.Cipher.init(DashoA13*..) at javax.crypto.Cipher.init(DashoA13*..) ... 还有 8 个

我在这里接受很多建议,从“不要使用 gpg,改用 x”到“不要使用充气城堡,改用 x”到介于两者之间的任何建议。谢谢!

4

5 回答 5

15

如果有人有兴趣知道如何使用 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());
        }
于 2015-10-19T13:53:43.750 回答
4

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

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

长话短说:Bouncycastle 的编程通常是大量的货物崇拜编程,我编写了一个库来改变这一点。

于 2017-02-11T13:50:02.107 回答
3

我决定采用一种截然不同的方法,即完全放弃使用充气城堡,而只使用运行时进程。对我来说,这个解决方案是有效的,并且完全消除了充气城堡周围的复杂性:

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

于 2013-02-21T14:14:19.867 回答
1

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

有两种简单的可能性:

  • 您的钥匙圈集合有多个钥匙圈。
  • 您的密钥环有子密钥。

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

我认为你想要这样的东西(也secretKeyRing可能应该重命名为secretKeyRingCollection):

PGPSecretKey secretKey = secretKeyRing.getSecretKey(publicKeyEncryptedData.getKeyID());
于 2013-02-21T02:35:43.970 回答
-1

错误消息很困难,因为它并不完全准确。除了非法的密钥大小或默认参数之外,异常并没有说它可能因为加密权限检查失败而失败。这意味着您没有正确设置 JCE 权限。您需要安装JCE Unlimited Strength Policy

您可以通过在 jvm 上设置系统属性来查看调试消息

java -Djava.security.debug=access ....
于 2020-04-03T15:47:19.813 回答