All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bouncycastle.openpgp.api.OpenPGPV6KeyGenerator Maven / Gradle / Ivy

Go to download

The Bouncy Castle Java API for handling the OpenPGP protocol. This jar contains the OpenPGP API for JDK 1.4. The APIs can be used in conjunction with a JCE/JCA provider such as the one provided with the Bouncy Castle Cryptography APIs.

The newest version!
package org.bouncycastle.openpgp.api;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.bouncycastle.bcpg.AEADAlgorithmTags;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyPacket;
import org.bouncycastle.bcpg.PublicKeyUtils;
import org.bouncycastle.bcpg.PublicSubkeyPacket;
import org.bouncycastle.bcpg.S2K;
import org.bouncycastle.bcpg.SignatureSubpacketTags;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.bcpg.sig.Features;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptorFactory;
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilderProvider;
import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.PGPKeyPairGenerator;
import org.bouncycastle.openpgp.operator.PGPKeyPairGeneratorProvider;
import org.bouncycastle.util.Arrays;

/**
 * High-level generator class for OpenPGP v6 keys.
 */
public class OpenPGPV6KeyGenerator
{
    /**
     * Hash algorithm for key signatures if no other one is provided during construction.
     */
    public static final int DEFAULT_SIGNATURE_HASH_ALGORITHM = HashAlgorithmTags.SHA3_512;

    // SECONDS
    private static final long SECONDS_PER_MINUTE = 60;
    private static final long SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE;
    private static final long SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR;
    private static final long SECONDS_PER_YEAR = 365 * SECONDS_PER_DAY;

    /**
     * Standard AEAD encryption preferences (SEIPDv2).
     * By default, only announce support for OCB + AES.
     */
    public static SignatureSubpacketsFunction DEFAULT_AEAD_ALGORITHM_PREFERENCES = new SignatureSubpacketsFunction()
    {
        public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets)
        {
            subpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS);
            subpackets.setPreferredAEADCiphersuites(PreferredAEADCiphersuites.builder(false)
                .addCombination(SymmetricKeyAlgorithmTags.AES_256, AEADAlgorithmTags.OCB)
                .addCombination(SymmetricKeyAlgorithmTags.AES_192, AEADAlgorithmTags.OCB)
                .addCombination(SymmetricKeyAlgorithmTags.AES_128, AEADAlgorithmTags.OCB));
            return subpackets;
        }
    };

    /**
     * Standard symmetric-key encryption preferences (SEIPDv1).
     * By default, announce support for AES.
     */
    public static SignatureSubpacketsFunction DEFAULT_SYMMETRIC_KEY_PREFERENCES = new SignatureSubpacketsFunction()
    {
        public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets)
        {
            subpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_SYM_ALGS);
            subpackets.setPreferredSymmetricAlgorithms(false, new int[]{
                SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192, SymmetricKeyAlgorithmTags.AES_128
            });
            return subpackets;
        }
    };

    /**
     * Standard signature hash algorithm preferences.
     * By default, only announce SHA3 and SHA2 algorithms.
     */
    public static SignatureSubpacketsFunction DEFAULT_HASH_ALGORITHM_PREFERENCES = new SignatureSubpacketsFunction()
    {
        public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets)
        {
            subpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_HASH_ALGS);
            subpackets.setPreferredHashAlgorithms(false, new int[]{
                HashAlgorithmTags.SHA3_512, HashAlgorithmTags.SHA3_256,
                HashAlgorithmTags.SHA512, HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA256
            });
            return subpackets;
        }
    };

    /**
     * Standard compression algorithm preferences.
     * By default, announce support for all known algorithms.
     */
    public static SignatureSubpacketsFunction DEFAULT_COMPRESSION_ALGORITHM_PREFERENCES = new SignatureSubpacketsFunction()
    {
        public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets)
        {
            subpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_COMP_ALGS);
            subpackets.setPreferredCompressionAlgorithms(false, new int[]{
                CompressionAlgorithmTags.UNCOMPRESSED, CompressionAlgorithmTags.ZIP,
                CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2
            });
            return subpackets;
        }
    };

    /**
     * Standard features to announce.
     * By default, announce SEIPDv1 (modification detection) and SEIPDv2.
     */
    public static SignatureSubpacketsFunction DEFAULT_FEATURES = new SignatureSubpacketsFunction()
    {
        public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets)
        {
            subpackets.removePacketsOfType(SignatureSubpacketTags.FEATURES);
            subpackets.setFeature(false, (byte)(Features.FEATURE_MODIFICATION_DETECTION | Features.FEATURE_SEIPD_V2));
            return subpackets;
        }
    };

    /**
     * Standard signature subpackets for signing subkey's binding signatures.
     * Sets the keyflag subpacket to SIGN_DATA.
     */
    public static SignatureSubpacketsFunction SIGNING_SUBKEY_SUBPACKETS = new SignatureSubpacketsFunction()
    {
        public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets)
        {
            subpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS);
            subpackets.setKeyFlags(true, KeyFlags.SIGN_DATA);
            return subpackets;
        }
    };

    /**
     * Standard signature subpackets for encryption subkey's binding signatures.
     * Sets the keyflag subpacket to ENCRYPT_STORAGE|ENCRYPT_COMMS.
     */
    public static SignatureSubpacketsFunction ENCRYPTION_SUBKEY_SUBPACKETS = new SignatureSubpacketsFunction()
    {
        public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets)
        {
            subpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS);
            subpackets.setKeyFlags(true, KeyFlags.ENCRYPT_STORAGE | KeyFlags.ENCRYPT_COMMS);
            return subpackets;
        }
    };

    /**
     * Standard signature subpackets for the direct-key signature.
     * Sets default features, hash-, compression-, symmetric-key-, and AEAD algorithm preferences.
     */
    public static SignatureSubpacketsFunction DIRECT_KEY_SIGNATURE_SUBPACKETS = new SignatureSubpacketsFunction()
    {
        public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets)
        {
            subpackets = DEFAULT_FEATURES.apply(subpackets);
            subpackets = DEFAULT_HASH_ALGORITHM_PREFERENCES.apply(subpackets);
            subpackets = DEFAULT_COMPRESSION_ALGORITHM_PREFERENCES.apply(subpackets);
            subpackets = DEFAULT_SYMMETRIC_KEY_PREFERENCES.apply(subpackets);
            subpackets = DEFAULT_AEAD_ALGORITHM_PREFERENCES.apply(subpackets);
            return subpackets;
        }
    };

    private final Implementation impl; // contains BC or JCA/JCE implementations
    private final Configuration conf;

    /**
     * Generate a new OpenPGP key generator for v6 keys.
     *
     * @param kpGenProvider                key pair generator provider
     * @param contentSignerBuilderProvider content signer builder provider
     * @param digestCalculatorProvider     digest calculator provider
     * @param keyEncryptionBuilderProvider secret key encryption builder provider (AEAD)
     * @param keyFingerPrintCalculator     calculator for key fingerprints
     * @param creationTime                 key creation time
     */
    public OpenPGPV6KeyGenerator(
        PGPKeyPairGeneratorProvider kpGenProvider,
        PGPContentSignerBuilderProvider contentSignerBuilderProvider,
        PGPDigestCalculatorProvider digestCalculatorProvider,
        PBESecretKeyEncryptorFactory keyEncryptionBuilderProvider,
        KeyFingerPrintCalculator keyFingerPrintCalculator,
        Date creationTime)
    {
        this.impl = new Implementation(kpGenProvider, contentSignerBuilderProvider, digestCalculatorProvider, keyEncryptionBuilderProvider, keyFingerPrintCalculator);
        this.conf = new Configuration(new Date((creationTime.getTime() / 1000) * 1000));
    }

    /**
     * Generate an OpenPGP key consisting of a certify-only primary key,
     * a dedicated signing-subkey and dedicated encryption-subkey.
     * The key will carry the provided user-id and be protected using the provided passphrase.
     * See {@link PGPKeyPairGenerator#generatePrimaryKey()} for the primary key type,
     * {@link PGPKeyPairGenerator#generateSigningSubkey()} for the signing-subkey type and
     * {@link PGPKeyPairGenerator#generateEncryptionSubkey()} for the encryption-subkey key type.
     *
     * @param userId     user id
     * @param passphrase nullable passphrase.
     * @return OpenPGP key
     * @throws PGPException if the key cannot be generated
     */
    public PGPSecretKeyRing classicKey(String userId, char[] passphrase)
        throws PGPException
    {
        return withPrimaryKey()
            .addUserId(userId)
            .addSigningSubkey()
            .addEncryptionSubkey()
            .build(passphrase);
    }

    /**
     * Generate an OpenPGP key consisting of an Ed25519 certify-only primary key,
     * a dedicated Ed25519 sign-only subkey and dedicated X25519 encryption-only subkey.
     * The key will carry the provided user-id and be protected using the provided passphrase.
     *
     * @param userId     user id
     * @param passphrase nullable passphrase
     * @return OpenPGP key
     * @throws PGPException if the key cannot be generated
     */
    public PGPSecretKeyRing ed25519x25519Key(String userId, char[] passphrase)
        throws PGPException
    {
        return withPrimaryKey(new KeyPairGeneratorCallback()
        {
            public PGPKeyPair generateFrom(PGPKeyPairGenerator generator)
                throws PGPException
            {
                return generator.generateEd25519KeyPair();
            }
        })
            .addSigningSubkey(new KeyPairGeneratorCallback()
            {
                public PGPKeyPair generateFrom(PGPKeyPairGenerator generator)
                    throws PGPException
                {
                    return generator.generateEd25519KeyPair();
                }
            })
            .addEncryptionSubkey(new KeyPairGeneratorCallback()
            {
                public PGPKeyPair generateFrom(PGPKeyPairGenerator generator)
                    throws PGPException
                {
                    return generator.generateX25519KeyPair();
                }
            })
            .addUserId(userId)
            .build(passphrase);
    }


    /**
     * Generate an OpenPGP key consisting of an Ed448 certify-only primary key,
     * a dedicated Ed448 sign-only subkey and dedicated X448 encryption-only subkey.
     * The key will carry the provided user-id and be protected using the provided passphrase.
     *
     * @param userId     user id
     * @param passphrase nullable passphrase
     * @return OpenPGP key
     * @throws PGPException if the key cannot be generated
     */
    public PGPSecretKeyRing ed448x448Key(String userId, char[] passphrase)
        throws PGPException
    {
        return withPrimaryKey(new KeyPairGeneratorCallback()
        {
            public PGPKeyPair generateFrom(PGPKeyPairGenerator generator)
                throws PGPException
            {
                return generator.generateEd448KeyPair();
            }
        })
            .addSigningSubkey(new KeyPairGeneratorCallback()
            {
                public PGPKeyPair generateFrom(PGPKeyPairGenerator generator)
                    throws PGPException
                {
                    return generator.generateEd448KeyPair();
                }
            })
            .addEncryptionSubkey(new KeyPairGeneratorCallback()
            {
                public PGPKeyPair generateFrom(PGPKeyPairGenerator generator)
                    throws PGPException
                {
                    return generator.generateX448KeyPair();
                }
            })
            .addUserId(userId)
            .build(passphrase);
    }

    /**
     * Generate a sign-only OpenPGP key.
     * The key consists of a single, user-id-less primary key, which is capable of signing and certifying.
     * See {@link PGPKeyPairGenerator#generatePrimaryKey()} for the key type.
     *
     * @param passphrase nullable passphrase to protect the key with
     * @return sign-only (+certify) OpenPGP key
     * @throws PGPException if the key cannot be generated
     */
    public PGPSecretKeyRing signOnlyKey(char[] passphrase)
        throws PGPException
    {
        return signOnlyKey(passphrase, null);
    }

    /**
     * Generate a sign-only OpenPGP key.
     * The key consists of a single, user-id-less primary key, which is capable of signing and certifying.
     * It carries a single direct-key signature with signing-related preferences whose subpackets can be
     * modified by providing a {@link SignatureSubpacketsFunction}.
     *
     * @param passphrase     nullable passphrase to protect the key with
     * @param userSubpackets callback to modify the direct-key signature subpackets with
     * @return sign-only (+certify) OpenPGP key
     * @throws PGPException if the key cannot be generated
     */
    public PGPSecretKeyRing signOnlyKey(
        char[] passphrase,
        SignatureSubpacketsFunction userSubpackets)
        throws PGPException
    {
        PGPKeyPair primaryKeyPair = impl.kpGenProvider.get(PublicKeyPacket.VERSION_6, conf.keyCreationTime)
            .generatePrimaryKey();
        PBESecretKeyEncryptor encryptor = impl.keyEncryptorBuilderProvider
            .build(passphrase, primaryKeyPair.getPublicKey().getPublicKeyPacket());
        return signOnlyKey(primaryKeyPair, encryptor, userSubpackets);
    }

    /**
     * Generate a sign-only OpenPGP key.
     * The key consists of a single, user-id-less primary key, which is capable of signing and certifying.
     * It carries a single direct-key signature with signing-related preferences whose subpackets can be
     * modified by providing a {@link SignatureSubpacketsFunction}.
     *
     * @param primaryKeyPair signing-capable primary key
     * @param keyEncryptor   nullable encryptor to protect the primary key with
     * @param userSubpackets callback to modify the direct-key signature subpackets with
     * @return sign-only (+certify) OpenPGP key
     * @throws PGPException if the key cannot be generated
     */
    public PGPSecretKeyRing signOnlyKey(
        PGPKeyPair primaryKeyPair,
        PBESecretKeyEncryptor keyEncryptor,
        SignatureSubpacketsFunction userSubpackets)
        throws PGPException
    {
        if (primaryKeyPair.getPublicKey().getPublicKeyPacket() instanceof PublicSubkeyPacket)
        {
            throw new IllegalArgumentException("Primary key MUST NOT consist of subkey packet.");
        }

        return primaryKeyWithDirectKeySig(primaryKeyPair,
            new SignatureSubpacketsFunction()
            {
                public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator baseSubpackets)
                {
                    // remove unrelated subpackets not needed for sign-only keys
                    baseSubpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS);
                    baseSubpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_SYM_ALGS);
                    baseSubpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_COMP_ALGS);

                    // replace key flags -> CERTIFY_OTHER|SIGN_DATA
                    baseSubpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS);
                    baseSubpackets.setKeyFlags(true, KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA);
                    return baseSubpackets;
                }
            },
            userSubpackets, // apply user-provided subpacket changes
            keyEncryptor)
            .build();
    }

    /**
     * Generate an OpenPGP key with a certification-capable primary key.
     * See {@link PGPKeyPairGenerator#generatePrimaryKey()} for the primary key type
     *
     * @return builder
     * @throws PGPException if the key cannot be generated
     */
    public WithPrimaryKey withPrimaryKey()
        throws PGPException
    {
        return withPrimaryKey((SignatureSubpacketsFunction)null);
    }

    public WithPrimaryKey withPrimaryKey(
        KeyPairGeneratorCallback keyGenCallback)
        throws PGPException
    {
        return withPrimaryKey(keyGenCallback, null);
    }

    /**
     * Generate an OpenPGP key with a certification-capable primary key.
     * See {@link PGPKeyPairGenerator#generatePrimaryKey()} for the primary key type
     * The key will carry a direct-key signature, whose subpackets can be modified by overriding the
     * given {@link SignatureSubpacketsFunction}.
     *
     * @param directKeySubpackets nullable callback to modify the direct-key signatures subpackets
     * @return builder
     * @throws PGPException if the key cannot be generated
     */
    public WithPrimaryKey withPrimaryKey(
        SignatureSubpacketsFunction directKeySubpackets)
        throws PGPException
    {
        return withPrimaryKey(
            new KeyPairGeneratorCallback()
            {
                public PGPKeyPair generateFrom(PGPKeyPairGenerator generator)
                    throws PGPException
                {
                    return generator.generatePrimaryKey();
                }
            },
            directKeySubpackets);
    }

    /**
     * Generate an OpenPGP key with a certification-capable primary key.
     * The {@link KeyPairGeneratorCallback} can be used to specify the primary key type.
     * The key will carry a direct-key signature, whose subpackets can be modified by overriding the
     * given {@link SignatureSubpacketsFunction}.
     *
     * @param keyGenCallback      callback to specify the primary key type
     * @param directKeySubpackets nullable callback to modify the direct-key signatures subpackets
     * @return builder
     * @throws PGPException if the key cannot be generated
     */
    public WithPrimaryKey withPrimaryKey(
        KeyPairGeneratorCallback keyGenCallback,
        SignatureSubpacketsFunction directKeySubpackets)
        throws PGPException
    {
        return withPrimaryKey(keyGenCallback, directKeySubpackets, null);
    }

    /**
     * Generate an OpenPGP key with a certification-capable primary key.
     * The key will carry a direct-key signature, whose subpackets can be modified by overriding the
     * given {@link SignatureSubpacketsFunction}.
     *
     * @param primaryKeyPair      primary key
     * @param directKeySubpackets nullable callback to modify the direct-key signatures subpackets
     * @return builder
     * @throws PGPException if the key cannot be generated
     */
    public WithPrimaryKey withPrimaryKey(
        PGPKeyPair primaryKeyPair,
        SignatureSubpacketsFunction directKeySubpackets)
        throws PGPException
    {
        return withPrimaryKey(
            primaryKeyPair,
            directKeySubpackets,
            null);
    }

    /**
     * Generate an OpenPGP key with a certification-capable primary key.
     * The {@link KeyPairGeneratorCallback} can be used to specify the primary key type.
     * The key will carry a direct-key signature, whose subpackets can be modified by overriding the
     * given {@link SignatureSubpacketsFunction}.
     * IMPORTANT: The custom primary key passphrase will only be used, if in the final step the key is retrieved
     * using {@link WithPrimaryKey#build()}.
     * If instead {@link WithPrimaryKey#build(char[])} is used, the key-specific passphrase is overwritten with the argument
     * passed into {@link WithPrimaryKey#build(char[])}.
     *
     * @param keyGenCallback      callback to specify the primary key type
     * @param directKeySubpackets nullable callback to modify the direct-key signatures subpackets
     * @param passphrase          nullable passphrase to protect the primary key with
     * @return builder
     * @throws PGPException if the key cannot be generated
     */
    public WithPrimaryKey withPrimaryKey(
        KeyPairGeneratorCallback keyGenCallback,
        SignatureSubpacketsFunction directKeySubpackets,
        char[] passphrase)
        throws PGPException
    {
        PGPKeyPair primaryKeyPair = keyGenCallback.generateFrom(
            impl.kpGenProvider.get(PublicKeyPacket.VERSION_6, conf.keyCreationTime));
        PBESecretKeyEncryptor keyEncryptor = impl.keyEncryptorBuilderProvider
            .build(passphrase, primaryKeyPair.getPublicKey().getPublicKeyPacket());
        return withPrimaryKey(primaryKeyPair, directKeySubpackets, keyEncryptor);
    }

    /**
     * Generate an OpenPGP key with a certification-capable primary key.
     * The {@link KeyPairGeneratorCallback} can be used to specify the primary key type.
     * The key will carry a direct-key signature, whose subpackets can be modified by overriding the
     * given {@link SignatureSubpacketsFunction}.
     * IMPORTANT: The custom keyEncryptor will only be used, if in the final step the key is retrieved
     * using {@link WithPrimaryKey#build()}.
     * If instead {@link WithPrimaryKey#build(char[])} is used, the key-specific encryptor is overwritten with
     * an encryptor built from the argument passed into {@link WithPrimaryKey#build(char[])}.
     *
     * @param primaryKeyPair      primary key
     * @param directKeySubpackets nullable callback to modify the direct-key signatures subpackets
     * @param keyEncryptor        nullable encryptor to protect the primary key with
     * @return builder
     * @throws PGPException if the key cannot be generated
     */
    public WithPrimaryKey withPrimaryKey(
        final PGPKeyPair primaryKeyPair,
        SignatureSubpacketsFunction directKeySubpackets,
        PBESecretKeyEncryptor keyEncryptor)
        throws PGPException
    {
        if (primaryKeyPair.getPublicKey().getPublicKeyPacket() instanceof PublicSubkeyPacket)
        {
            throw new IllegalArgumentException("Primary key MUST NOT consist of subkey packet.");
        }

        if (!PublicKeyUtils.isSigningAlgorithm(primaryKeyPair.getPublicKey().getAlgorithm()))
        {
            throw new PGPException("Primary key MUST use signing-capable algorithm.");
        }

        return primaryKeyWithDirectKeySig(
            primaryKeyPair,
            new SignatureSubpacketsFunction()
            {
                public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets)
                {
                    subpackets.setIssuerFingerprint(true, primaryKeyPair.getPublicKey());
                    subpackets.setSignatureCreationTime(conf.keyCreationTime);
                    subpackets.setKeyFlags(true, KeyFlags.CERTIFY_OTHER);
                    subpackets = DIRECT_KEY_SIGNATURE_SUBPACKETS.apply(subpackets);
                    subpackets.setKeyExpirationTime(false, 5 * SECONDS_PER_YEAR);
                    return subpackets;
                }
            },
            directKeySubpackets,
            keyEncryptor);
    }

    /**
     * Specify the primary key and attach a direct-key signature.
     * The direct-key signature's subpackets will first be modified using the baseSubpackets callback, followed
     * by the customSubpackets callback.
     * If both baseSubpackets and customSubpackets are null, no direct-key signature will be attached.
     *
     * @param primaryKeyPair   primary key pair
     * @param baseSubpackets   base signature subpackets callback
     * @param customSubpackets user-provided signature subpackets callback
     * @param keyEncryptor     key encryptor
     * @return builder
     * @throws PGPException if the key cannot be generated
     */
    private WithPrimaryKey primaryKeyWithDirectKeySig(
        PGPKeyPair primaryKeyPair,
        SignatureSubpacketsFunction baseSubpackets,
        SignatureSubpacketsFunction customSubpackets,
        PBESecretKeyEncryptor keyEncryptor)
        throws PGPException
    {
        if (baseSubpackets != null || customSubpackets != null)
        {
            // DK sig
            PGPSignatureGenerator dkSigGen = new PGPSignatureGenerator(
                impl.contentSignerBuilderProvider.get(primaryKeyPair.getPublicKey()),
                primaryKeyPair.getPublicKey());
            dkSigGen.init(PGPSignature.DIRECT_KEY, primaryKeyPair.getPrivateKey());

            PGPSignatureSubpacketGenerator subpackets = new PGPSignatureSubpacketGenerator();
            // application-dictated subpackets
            if (baseSubpackets != null)
            {
                subpackets = baseSubpackets.apply(subpackets);
            }

            // Allow the user to modify the direct-key signature subpackets
            if (customSubpackets != null)
            {
                subpackets = customSubpackets.apply(subpackets);
            }

            dkSigGen.setHashedSubpackets(subpackets.generate());

            PGPSignature dkSig = dkSigGen.generateCertification(primaryKeyPair.getPublicKey());
            primaryKeyPair = new PGPKeyPair(
                PGPPublicKey.addCertification(primaryKeyPair.getPublicKey(), dkSig),
                primaryKeyPair.getPrivateKey());
        }

        Key primaryKey = new Key(primaryKeyPair, keyEncryptor);

        return new WithPrimaryKey(impl, conf, primaryKey);
    }

    /**
     * Intermediate builder class.
     * Constructs an OpenPGP key from a specified primary key.
     */
    public static class WithPrimaryKey
    {

        private final Implementation impl;
        private final Configuration conf;
        private Key primaryKey;
        private final List  subkeys = new ArrayList ();

        /**
         * Builder.
         *
         * @param implementation cryptographic implementation
         * @param configuration  key configuration
         * @param primaryKey     specified primary key
         */
        private WithPrimaryKey(Implementation implementation, Configuration configuration, Key primaryKey)
        {
            this.impl = implementation;
            this.conf = configuration;
            this.primaryKey = primaryKey;
        }

        /**
         * Attach a User-ID with a positive certification to the key.
         *
         * @param userId user-id
         * @return builder
         * @throws PGPException if the user-id cannot be added
         */
        public WithPrimaryKey addUserId(String userId)
            throws PGPException
        {
            return addUserId(userId, null);
        }

        /**
         * Attach a User-ID with a positive certification to the key.
         * The subpackets of the user-id certification can be modified using the userIdSubpackets callback.
         *
         * @param userId           user-id
         * @param userIdSubpackets callback to modify the certification subpackets
         * @return builder
         * @throws PGPException if the user-id cannot be added
         */
        public WithPrimaryKey addUserId(
            String userId,
            SignatureSubpacketsFunction userIdSubpackets)
            throws PGPException
        {
            return addUserId(userId, PGPSignature.POSITIVE_CERTIFICATION, userIdSubpackets);
        }

        /**
         * Attach a User-ID with a positive certification to the key.
         * The subpackets of the user-id certification can be modified using the userIdSubpackets callback.
         *
         * @param userId            user-id
         * @param certificationType signature type
         * @param userIdSubpackets  callback to modify the certification subpackets
         * @return builder
         * @throws PGPException if the user-id cannot be added
         */
        public WithPrimaryKey addUserId(
            String userId,
            int certificationType,
            SignatureSubpacketsFunction userIdSubpackets)
            throws PGPException
        {
            if (userId == null || userId.trim().length() == 0)
            {
                throw new IllegalArgumentException("User-ID cannot be null or empty.");
            }

            if (!PGPSignature.isCertification(certificationType))
            {
                throw new IllegalArgumentException("Signature type MUST be a certification type (0x10 - 0x13)");
            }

            PGPSignatureGenerator uidSigGen = new PGPSignatureGenerator(
                impl.contentSignerBuilderProvider.get(primaryKey.pair.getPublicKey()),
                primaryKey.pair.getPublicKey());
            uidSigGen.init(certificationType, primaryKey.pair.getPrivateKey());

            PGPSignatureSubpacketGenerator subpackets = new PGPSignatureSubpacketGenerator();
            subpackets.setIssuerFingerprint(true, primaryKey.pair.getPublicKey());
            subpackets.setSignatureCreationTime(conf.keyCreationTime);

            if (userIdSubpackets != null)
            {
                subpackets = userIdSubpackets.apply(subpackets);
            }
            uidSigGen.setHashedSubpackets(subpackets.generate());

            PGPSignature uidSig = uidSigGen.generateCertification(userId, primaryKey.pair.getPublicKey());
            PGPPublicKey pubKey = PGPPublicKey.addCertification(primaryKey.pair.getPublicKey(), userId, uidSig);
            primaryKey = new Key(new PGPKeyPair(pubKey, primaryKey.pair.getPrivateKey()), primaryKey.encryptor);

            return this;
        }

        /**
         * Add an encryption-capable subkey to the OpenPGP key.
         * See {@link PGPKeyPairGenerator#generateEncryptionSubkey()} for the key type.
         *
         * @return builder
         * @throws PGPException if the key cannot be generated
         */
        public WithPrimaryKey addEncryptionSubkey()
            throws PGPException
        {
            return addEncryptionSubkey(new KeyPairGeneratorCallback()
            {
                public PGPKeyPair generateFrom(PGPKeyPairGenerator generator)
                    throws PGPException
                {
                    return generator.generateEncryptionSubkey();
                }
            });
        }

        /**
         * Add an encryption-capable subkey to the OpenPGP key.
         * The type of the subkey can be decided by implementing the {@link KeyPairGeneratorCallback}.
         *
         * @param keyGenCallback callback to decide the encryption subkey type
         * @return builder
         * @throws PGPException if the key cannot be generated
         */
        public WithPrimaryKey addEncryptionSubkey(KeyPairGeneratorCallback keyGenCallback)
            throws PGPException
        {
            return addEncryptionSubkey(keyGenCallback, (char[])null);
        }

        /**
         * Add an encryption-capable subkey to the OpenPGP key.
         * The type of the subkey can be decided by implementing the {@link KeyPairGeneratorCallback}.
         * The binding signature can be modified by implementing the {@link SignatureSubpacketsFunction}.
         *
         * @param generatorCallback         callback to specify the encryption key type.
         * @param bindingSubpacketsCallback nullable callback to modify the binding signature subpackets
         * @return builder
         * @throws PGPException if the key cannot be generated
         */
        public WithPrimaryKey addEncryptionSubkey(
            KeyPairGeneratorCallback generatorCallback,
            SignatureSubpacketsFunction bindingSubpacketsCallback)
            throws PGPException
        {
            PGPKeyPairGenerator generator = impl.kpGenProvider.get(
                primaryKey.pair.getPublicKey().getVersion(),
                conf.keyCreationTime
            );
            PGPKeyPair subkey = generatorCallback.generateFrom(generator);

            return addEncryptionSubkey(subkey, bindingSubpacketsCallback, null);
        }

        /**
         * Add an encryption-capable subkey to the OpenPGP key.
         * The subkey will be protected using the provided subkey passphrase.
         * IMPORTANT: The custom subkey passphrase will only be used, if in the final step the key is retrieved
         * using {@link #build()}.
         * If instead {@link #build(char[])} is used, the key-specific passphrase is overwritten with the argument
         * passed into {@link #build(char[])}.
         * See {@link PGPKeyPairGenerator#generateEncryptionSubkey()} for the key type.
         *
         * @param passphrase nullable subkey passphrase
         * @return builder
         * @throws PGPException if the key cannot be generated
         */
        public WithPrimaryKey addEncryptionSubkey(char[] passphrase)
            throws PGPException
        {
            return addEncryptionSubkey(new KeyPairGeneratorCallback()
            {
                public PGPKeyPair generateFrom(PGPKeyPairGenerator generator)
                    throws PGPException
                {
                    return generator.generateEncryptionSubkey();
                }
            }, passphrase);
        }

        /**
         * Add an encryption-capable subkey to the OpenPGP key.
         * The key type can be specified by overriding {@link KeyPairGeneratorCallback}.
         * The subkey will be protected using the provided subkey passphrase.
         * IMPORTANT: The custom subkey passphrase will only be used, if in the final step the key is retrieved
         * using {@link #build()}.
         * If instead {@link #build(char[])} is used, the key-specific passphrase is overwritten with the argument
         * passed into {@link #build(char[])}.
         *
         * @param keyGenCallback callback to specify the key type
         * @param passphrase     nullable passphrase for the encryption subkey
         * @return builder
         * @throws PGPException if the key cannot be generated
         */
        public WithPrimaryKey addEncryptionSubkey(KeyPairGeneratorCallback keyGenCallback,
                                                  char[] passphrase)
            throws PGPException
        {
            return addEncryptionSubkey(keyGenCallback, null, passphrase);
        }

        /**
         * Add an encryption-capable subkey to the OpenPGP key.
         * The key type can be specified by overriding {@link KeyPairGeneratorCallback}.
         * The binding signatures subpackets can be modified by overriding the {@link SignatureSubpacketsFunction}.
         * The subkey will be protected using the provided subkey passphrase.
         * IMPORTANT: The custom subkey passphrase will only be used, if in the final step the key is retrieved
         * using {@link #build()}.
         * If instead {@link #build(char[])} is used, the key-specific passphrase is overwritten with the argument
         * passed into {@link #build(char[])}.
         *
         * @param keyGenCallback           callback to specify the key type
         * @param bindingSignatureCallback nullable callback to modify the binding signature subpackets
         * @param passphrase               nullable passphrase for the encryption subkey
         * @return builder
         * @throws PGPException if the key cannot be generated
         */
        public WithPrimaryKey addEncryptionSubkey(KeyPairGeneratorCallback keyGenCallback,
                                                  SignatureSubpacketsFunction bindingSignatureCallback,
                                                  char[] passphrase)
            throws PGPException
        {
            PGPKeyPair subkey = keyGenCallback.generateFrom(
                impl.kpGenProvider.get(PublicKeyPacket.VERSION_6, conf.keyCreationTime));
            subkey = subkey.asSubkey(impl.keyFingerprintCalculator);
            PBESecretKeyEncryptor keyEncryptor = impl.keyEncryptorBuilderProvider.build(passphrase, subkey.getPublicKey().getPublicKeyPacket());
            return addEncryptionSubkey(subkey, bindingSignatureCallback, keyEncryptor);
        }


        /**
         * Add an encryption-capable subkey to the OpenPGP key.
         * IMPORTANT: The custom key encryptor will only be used, if in the final step the key is retrieved
         * using {@link #build()}.
         * If instead {@link #build(char[])} is used, the key-specific encryptor is overwritten with an encryptor
         * built from the argument passed into {@link #build(char[])}.
         *
         * @param encryptionSubkey          encryption subkey
         * @param bindingSubpacketsCallback nullable callback to modify the subkey binding signature subpackets
         * @param keyEncryptor              nullable encryptor to encrypt the encryption subkey
         * @return builder
         * @throws PGPException if the key cannot be generated
         */
        public WithPrimaryKey addEncryptionSubkey(
            PGPKeyPair encryptionSubkey,
            SignatureSubpacketsFunction bindingSubpacketsCallback,
            PBESecretKeyEncryptor keyEncryptor)
            throws PGPException
        {
            if (!(encryptionSubkey.getPublicKey().getPublicKeyPacket() instanceof PublicSubkeyPacket))
            {
                throw new IllegalArgumentException("Encryption subkey MUST NOT consist of a primary key packet.");
            }

            if (!encryptionSubkey.getPublicKey().isEncryptionKey())
            {
                throw new PGPException("Encryption key MUST use encryption-capable algorithm.");
            }
            // generate binding signature
            PGPSignatureSubpacketGenerator subpackets = new PGPSignatureSubpacketGenerator();
            subpackets.setIssuerFingerprint(true, primaryKey.pair.getPublicKey());
            subpackets.setSignatureCreationTime(conf.keyCreationTime);
            subpackets = ENCRYPTION_SUBKEY_SUBPACKETS.apply(subpackets);

            // allow subpacket customization
            PGPPublicKey publicSubkey = getPublicSubKey(encryptionSubkey, bindingSubpacketsCallback, subpackets);
            Key subkey = new Key(new PGPKeyPair(publicSubkey, encryptionSubkey.getPrivateKey()), keyEncryptor);
            subkeys.add(subkey);
            return this;
        }

        /**
         * Add a signing-capable subkey to the OpenPGP key.
         * The binding signature will contain a primary-key back-signature.
         * See {@link PGPKeyPairGenerator#generateSigningSubkey()} for the key type.
         *
         * @return builder
         * @throws PGPException if the key cannot be generated
         */
        public WithPrimaryKey addSigningSubkey()
            throws PGPException
        {
            return addSigningSubkey(new KeyPairGeneratorCallback()
            {
                public PGPKeyPair generateFrom(PGPKeyPairGenerator generator)
                    throws PGPException
                {
                    return generator.generateSigningSubkey();
                }
            });
        }

        /**
         * Add a signing-capable subkey to the OpenPGP key.
         * The binding signature will contain a primary-key back-signature.
         * The key type can be specified by overriding {@link KeyPairGeneratorCallback}.
         *
         * @param keyGenCallback callback to specify the signing-subkey type
         * @return builder
         * @throws PGPException if the key cannot be generated
         */
        public WithPrimaryKey addSigningSubkey(KeyPairGeneratorCallback keyGenCallback)
            throws PGPException
        {
            return addSigningSubkey(keyGenCallback, null);
        }

        /**
         * Add a signing-capable subkey to the OpenPGP key.
         * See {@link PGPKeyPairGenerator#generateSigningSubkey()} for the key type.
         * The binding signature will contain a primary-key back-signature.
         * IMPORTANT: The custom subkey passphrase will only be used, if in the final step the key is retrieved
         * using {@link #build()}.
         * If instead {@link #build(char[])} is used, the key-specific passphrase is overwritten with the argument
         * passed into {@link #build(char[])}.
         *
         * @param passphrase nullable passphrase
         * @return builder
         * @throws PGPException if the key cannot be generated
         */
        public WithPrimaryKey addSigningSubkey(char[] passphrase)
            throws PGPException
        {
            return addSigningSubkey(new KeyPairGeneratorCallback()
            {
                public PGPKeyPair generateFrom(PGPKeyPairGenerator generator)
                    throws PGPException
                {
                    return generator.generateSigningSubkey();
                }
            }, passphrase);
        }

        /**
         * Add a signing-capable subkey to the OpenPGP key.
         * The signing-key type can be specified by overriding the {@link KeyPairGeneratorCallback}.
         * The binding signature will contain a primary-key back-signature.
         * IMPORTANT: The custom subkey passphrase will only be used, if in the final step the key is retrieved
         * using {@link #build()}.
         * If instead {@link #build(char[])} is used, the key-specific passphrase is overwritten with the argument
         * passed into {@link #build(char[])}.
         *
         * @param keyGenCallback callback to specify the signing-key type
         * @param passphrase     nullable passphrase
         * @return builder
         * @throws PGPException if the key cannot be generated
         */
        public WithPrimaryKey addSigningSubkey(KeyPairGeneratorCallback keyGenCallback,
                                               char[] passphrase)
            throws PGPException
        {
            return addSigningSubkey(keyGenCallback, null, null, passphrase);
        }

        /**
         * Add a signing-capable subkey to the OpenPGP key.
         * The signing-key type can be specified by overriding the {@link KeyPairGeneratorCallback}.
         * The binding signature will contain a primary-key back-signature.
         * The contents of the binding signature(s) can be modified by overriding the respective
         * {@link SignatureSubpacketsFunction} instances.
         * IMPORTANT: The custom subkey passphrase will only be used, if in the final step the key is retrieved
         * using {@link #build()}.
         * If instead {@link #build(char[])} is used, the key-specific passphrase is overwritten with the argument
         * passed into {@link #build(char[])}.
         *
         * @param keyGenCallback           callback to specify the signing-key type
         * @param bindingSignatureCallback callback to modify the contents of the signing subkey binding signature
         * @param backSignatureCallback    callback to modify the contents of the embedded primary key binding signature
         * @param passphrase               nullable passphrase
         * @return builder
         * @throws PGPException if the key cannot be generated
         */
        public WithPrimaryKey addSigningSubkey(KeyPairGeneratorCallback keyGenCallback,
                                               SignatureSubpacketsFunction bindingSignatureCallback,
                                               SignatureSubpacketsFunction backSignatureCallback,
                                               char[] passphrase)
            throws PGPException
        {
            PGPKeyPair subkey = keyGenCallback.generateFrom(impl.kpGenProvider.get(PublicKeyPacket.VERSION_6, conf.keyCreationTime));
            subkey = subkey.asSubkey(impl.keyFingerprintCalculator);
            PBESecretKeyEncryptor keyEncryptor = impl.keyEncryptorBuilderProvider.build(passphrase, subkey.getPublicKey().getPublicKeyPacket());
            return addSigningSubkey(subkey, bindingSignatureCallback, backSignatureCallback, keyEncryptor);
        }

        /**
         * Add a signing-capable subkey to the OpenPGP key.
         * The signing-key type can be specified by overriding the {@link KeyPairGeneratorCallback}.
         * The binding signature will contain a primary-key back-signature.
         * The contents of the binding signature(s) can be modified by overriding the respective
         * {@link SignatureSubpacketsFunction} instances.
         * IMPORTANT: The custom key encryptor will only be used, if in the final step the key is retrieved
         * using {@link #build()}.
         * If instead {@link #build(char[])} is used, the key-specific encryptor is overwritten with an encryptor
         * built from the argument passed into {@link #build(char[])}.
         *
         * @param signingSubkey            signing subkey
         * @param bindingSignatureCallback callback to modify the contents of the signing subkey binding signature
         * @param backSignatureCallback    callback to modify the contents of the embedded primary key binding signature
         * @param keyEncryptor             nullable encryptor to protect the signing subkey
         * @return builder
         * @throws PGPException if the key cannot be generated
         */
        public WithPrimaryKey addSigningSubkey(PGPKeyPair signingSubkey,
                                               SignatureSubpacketsFunction bindingSignatureCallback,
                                               SignatureSubpacketsFunction backSignatureCallback,
                                               PBESecretKeyEncryptor keyEncryptor)
            throws PGPException
        {
            if (!(signingSubkey.getPublicKey().getPublicKeyPacket() instanceof PublicSubkeyPacket))
            {
                throw new IllegalArgumentException("Signing subkey MUST NOT consist of primary key packet.");
            }

            if (!PublicKeyUtils.isSigningAlgorithm(signingSubkey.getPublicKey().getAlgorithm()))
            {
                throw new PGPException("Signing key MUST use signing-capable algorithm.");
            }

            PGPSignatureSubpacketGenerator backSigSubpackets = new PGPSignatureSubpacketGenerator();
            backSigSubpackets.setIssuerFingerprint(true, signingSubkey.getPublicKey());
            backSigSubpackets.setSignatureCreationTime(conf.keyCreationTime);
            if (backSignatureCallback != null)
            {
                backSigSubpackets = backSignatureCallback.apply(backSigSubpackets);
            }

            PGPSignatureSubpacketGenerator bindingSigSubpackets = new PGPSignatureSubpacketGenerator();
            bindingSigSubpackets.setIssuerFingerprint(true, primaryKey.pair.getPublicKey());
            bindingSigSubpackets.setSignatureCreationTime(conf.keyCreationTime);

            bindingSigSubpackets = SIGNING_SUBKEY_SUBPACKETS.apply(bindingSigSubpackets);

            PGPSignatureGenerator backSigGen = new PGPSignatureGenerator(
                impl.contentSignerBuilderProvider.get(signingSubkey.getPublicKey()),
                signingSubkey.getPublicKey());
            backSigGen.init(PGPSignature.PRIMARYKEY_BINDING, signingSubkey.getPrivateKey());
            backSigGen.setHashedSubpackets(backSigSubpackets.generate());
            PGPSignature backSig = backSigGen.generateCertification(
                primaryKey.pair.getPublicKey(), signingSubkey.getPublicKey());

            try
            {
                bindingSigSubpackets.addEmbeddedSignature(false, backSig);
            }
            catch (IOException e)
            {
                throw new PGPException("Cannot embed back-signature.", e);
            }

            PGPPublicKey signingPubKey = getPublicSubKey(signingSubkey, bindingSignatureCallback, bindingSigSubpackets);
            signingSubkey = new PGPKeyPair(signingPubKey, signingSubkey.getPrivateKey());
            subkeys.add(new Key(signingSubkey, keyEncryptor));

            return this;
        }

        /**
         * Build the {@link PGPSecretKeyRing OpenPGP key}, allowing individual passphrases for the subkeys.
         *
         * @return OpenPGP key
         * @throws PGPException if the key cannot be generated
         */
        public PGPSecretKeyRing build()
            throws PGPException
        {
            PGPSecretKey primarySecretKey = new PGPSecretKey(
                primaryKey.pair.getPrivateKey(),
                primaryKey.pair.getPublicKey(),
                impl.digestCalculatorProvider.get(HashAlgorithmTags.SHA1),
                true,
                primaryKey.encryptor);
            List  keys = new ArrayList ();
            keys.add(primarySecretKey);

            for (Iterator it = subkeys.iterator(); it.hasNext();)
            {
                Key key = (Key)it.next();
                PGPSecretKey subkey = new PGPSecretKey(
                    key.pair.getPrivateKey(),
                    key.pair.getPublicKey(),
                    impl.digestCalculatorProvider.get(HashAlgorithmTags.SHA1),
                    false,
                    key.encryptor);
                keys.add(subkey);
            }

            return new PGPSecretKeyRing(keys);
        }

        /**
         * Build the {@link PGPSecretKeyRing OpenPGP key} using a single passphrase used to protect all subkeys.
         * The passphrase will override whichever key protectors were specified in previous builder steps.
         *
         * @param passphrase nullable passphrase
         * @return OpenPGP key
         * @throws PGPException if the key cannot be generated
         */
        public PGPSecretKeyRing build(char[] passphrase)
            throws PGPException
        {
            PBESecretKeyEncryptor primaryKeyEncryptor = impl.keyEncryptorBuilderProvider
                .build(passphrase, primaryKey.pair.getPublicKey().getPublicKeyPacket());
            sanitizeKeyEncryptor(primaryKeyEncryptor);
            PGPSecretKey primarySecretKey = new PGPSecretKey(
                primaryKey.pair.getPrivateKey(),
                primaryKey.pair.getPublicKey(),
                impl.digestCalculatorProvider.get(HashAlgorithmTags.SHA1),
                true,
                primaryKeyEncryptor);
            List  keys = new ArrayList ();
            keys.add(primarySecretKey);

            for (Iterator it = subkeys.iterator(); it.hasNext();)
            {
                Key key = (Key)it.next();
                PBESecretKeyEncryptor subkeyEncryptor = impl.keyEncryptorBuilderProvider
                    .build(passphrase, key.pair.getPublicKey().getPublicKeyPacket());
                sanitizeKeyEncryptor(subkeyEncryptor);
                PGPSecretKey subkey = new PGPSecretKey(
                    key.pair.getPrivateKey(),
                    key.pair.getPublicKey(),
                    impl.digestCalculatorProvider.get(HashAlgorithmTags.SHA1),
                    false,
                    subkeyEncryptor);
                keys.add(subkey);
            }

            if (passphrase != null)
            {
                Arrays.fill(passphrase, (char)0);
            }

            return new PGPSecretKeyRing(keys);
        }

        protected void sanitizeKeyEncryptor(PBESecretKeyEncryptor keyEncryptor)
        {
            if (keyEncryptor == null)
            {
                // Unprotected is okay
                return;
            }

            S2K s2k = keyEncryptor.getS2K();
            if (s2k.getType() == S2K.SIMPLE || s2k.getType() == S2K.SALTED)
            {
                throw new IllegalArgumentException("S2K specifiers SIMPLE and SALTED are not allowed for secret key encryption.");
            }
            else if (s2k.getType() == S2K.ARGON_2)
            {
                if (keyEncryptor.getAeadAlgorithm() == 0)
                {
                    throw new IllegalArgumentException("Argon2 MUST be used with AEAD.");
                }
            }
        }

        private PGPPublicKey getPublicSubKey(PGPKeyPair encryptionSubkey, SignatureSubpacketsFunction bindingSubpacketsCallback, PGPSignatureSubpacketGenerator subpackets)
            throws PGPException
        {
            if (bindingSubpacketsCallback != null)
            {
                subpackets = bindingSubpacketsCallback.apply(subpackets);
            }

            PGPSignatureGenerator bindingSigGen = new PGPSignatureGenerator(
                impl.contentSignerBuilderProvider.get(primaryKey.pair.getPublicKey()),
                primaryKey.pair.getPublicKey());
            bindingSigGen.init(PGPSignature.SUBKEY_BINDING, primaryKey.pair.getPrivateKey());
            bindingSigGen.setHashedSubpackets(subpackets.generate());

            PGPSignature bindingSig = bindingSigGen.generateCertification(primaryKey.pair.getPublicKey(), encryptionSubkey.getPublicKey());
            return PGPPublicKey.addCertification(encryptionSubkey.getPublicKey(), bindingSig);
        }
    }

    /**
     * Bundle implementation-specific provider classes.
     */
    private static class Implementation
    {
        final PGPKeyPairGeneratorProvider kpGenProvider;
        final PGPContentSignerBuilderProvider contentSignerBuilderProvider;
        final PGPDigestCalculatorProvider digestCalculatorProvider;
        final PBESecretKeyEncryptorFactory keyEncryptorBuilderProvider;
        final KeyFingerPrintCalculator keyFingerprintCalculator;

        public Implementation(PGPKeyPairGeneratorProvider keyPairGeneratorProvider,
                              PGPContentSignerBuilderProvider contentSignerBuilderProvider,
                              PGPDigestCalculatorProvider digestCalculatorProvider,
                              PBESecretKeyEncryptorFactory keyEncryptorBuilderProvider,
                              KeyFingerPrintCalculator keyFingerPrintCalculator)
        {
            this.kpGenProvider = keyPairGeneratorProvider;
            this.contentSignerBuilderProvider = contentSignerBuilderProvider;
            this.digestCalculatorProvider = digestCalculatorProvider;
            this.keyEncryptorBuilderProvider = keyEncryptorBuilderProvider;
            this.keyFingerprintCalculator = keyFingerPrintCalculator;
        }
    }

    /**
     * Bundle configuration-specific data.
     */
    private static class Configuration
    {
        final Date keyCreationTime;

        public Configuration(Date keyCreationTime)
        {
            this.keyCreationTime = keyCreationTime;
        }
    }

    /**
     * Tuple of a {@link PGPKeyPair} and (nullable) {@link PBESecretKeyEncryptor}.
     */
    private static class Key
    {
        private final PGPKeyPair pair;
        private final PBESecretKeyEncryptor encryptor;

        public Key(PGPKeyPair key, PBESecretKeyEncryptor encryptor)
        {
            this.pair = key;
            this.encryptor = encryptor;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy