bcgit / bc-java

Bouncy Castle Java Distribution (Mirror)
https://www.bouncycastle.org/java.html
MIT License
2.28k stars 1.13k forks source link

GNUPG 2.2.X file format support #794

Open sarfudheen opened 3 years ago

sarfudheen commented 3 years ago

I'm trying to load GNUPG 2.2.23 secret keys from D:\Users\xxxxx\AppData\Roaming\gnupg\private-keys-v1.d using bouncy castle bcpg-jdk15on-1.66.jar.

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.bouncycastle.gpg.SExprParser;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.operator.PBEProtectionRemoverFactory;
import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePBEProtectionRemoverFactory;
import org.bouncycastle.util.encoders.Hex;

public class LoadKey {
    public static void main(String[] args) throws IOException, PGPException {
        String filePath = "D:\\Users\\XXXX\\AppData\\Roaming\\gnupg\\private-keys-v1.d\\4B8379C14DED7818D546E3C4AF7F1D0DF5716BCD.key";
        String pass = "siv_test";
            PGPDigestCalculatorProvider calculatorProvider = new JcaPGPDigestCalculatorProviderBuilder()
                    .build();
            PBEProtectionRemoverFactory passphraseProvider = new JcePBEProtectionRemoverFactory(
                pass.toCharArray());
        InputStream in = new FileInputStream(filePath);

        PGPSecretKey key =

        new SExprParser(calculatorProvider).parseSecretKey(
                new BufferedInputStream(in), passphraseProvider,
                new JcaKeyFingerprintCalculator());

        System.out.println("Found key "
                + Hex.toHexString(key.getPublicKey().getFingerprint()));

    }
}

But I'm getting

Exception in thread "main" java.io.IOException: unknown character encountered: K at org.bouncycastle.gpg.SXprUtils.skipOpenParenthesis(Unknown Source) at org.bouncycastle.gpg.SExprParser.parseSecretKey(Unknown Source)

**gpg (GnuPG) 2.2.23

libgcrypt 1.8.6**

It seems to secret key file format has been changed between GNUPG 2.1.X et GNUPG 2.2.X

https://lists.gnupg.org/pipermail/gnupg-devel/2017-December/033295.html

tomaswolf commented 3 years ago

Yes, this would be useful.

  1. The file format is different. See keyformat.txt The "Extended Private Key Format" is the default since gpg-agent 2.2.20.
  2. Keys are encrypted using AES/OCB/NoPadding. The JcePBEProtectionRemoverFactory is hard-coded to AES/CBC/NoPadding. The AAD is the full key binary s-expression (starting with the key type) minus the protected sub-list.
  3. AES/OCB/NoPadding encrypted secret keys have no hash in the protected material. (Not needed thanks to the OCB MAC check.)

Sample code for this is in JGit. If anyone's interested in doing this properly in BC itself, feel free to take that code as inspiration.

bodhi-one commented 7 months ago

Bump