1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-12-22 19:08:00 +01:00

Kotlin conversion: KeyRingBuilder

This commit is contained in:
Paul Schaub 2023-10-09 12:33:45 +02:00
parent 43335cbcd3
commit eaef1fe44a
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
6 changed files with 293 additions and 384 deletions

View file

@ -1,313 +0,0 @@
// SPDX-FileCopyrightText: 2018 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.key.generation;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
import org.bouncycastle.openpgp.PGPPrivateKey;
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.PGPSignatureSubpacketVector;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
import org.bouncycastle.util.Strings;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.algorithm.PublicKeyAlgorithm;
import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.key.generation.type.KeyType;
import org.pgpainless.key.protection.UnlockSecretKey;
import org.pgpainless.policy.Policy;
import org.pgpainless.provider.ProviderFactory;
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper;
import org.pgpainless.util.Passphrase;
public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
private static final long YEAR_IN_SECONDS = 1000L * 60 * 60 * 24 * 365;
private KeySpec primaryKeySpec;
private final List<KeySpec> subkeySpecs = new ArrayList<>();
private final Map<String, SelfSignatureSubpackets.Callback> userIds = new LinkedHashMap<>();
private Passphrase passphrase = Passphrase.emptyPassphrase();
private Date expirationDate = new Date(System.currentTimeMillis() + YEAR_IN_SECONDS * 5); // Expiration in 5 years
@Override
public KeyRingBuilder setPrimaryKey(@Nonnull KeySpec keySpec) {
verifyKeySpecCompliesToPolicy(keySpec, PGPainless.getPolicy());
verifyMasterKeyCanCertify(keySpec);
this.primaryKeySpec = keySpec;
return this;
}
@Override
public KeyRingBuilder addSubkey(@Nonnull KeySpec keySpec) {
verifyKeySpecCompliesToPolicy(keySpec, PGPainless.getPolicy());
this.subkeySpecs.add(keySpec);
return this;
}
@Override
public KeyRingBuilder addUserId(@Nonnull String userId) {
this.userIds.put(userId.trim(), null);
return this;
}
public KeyRingBuilder addUserId(
@Nonnull String userId,
@Nullable SelfSignatureSubpackets.Callback subpacketsCallback) {
this.userIds.put(userId.trim(), subpacketsCallback);
return this;
}
@Override
public KeyRingBuilder addUserId(@Nonnull byte[] userId) {
return addUserId(Strings.fromUTF8ByteArray(userId));
}
@Override
public KeyRingBuilder setExpirationDate(@Nullable Date expirationDate) {
if (expirationDate == null) {
// No expiration
this.expirationDate = null;
return this;
}
Date now = new Date();
if (now.after(expirationDate)) {
throw new IllegalArgumentException("Expiration date must be in the future.");
}
this.expirationDate = expirationDate;
return this;
}
@Override
public KeyRingBuilder setPassphrase(@Nonnull Passphrase passphrase) {
this.passphrase = passphrase;
return this;
}
private void verifyKeySpecCompliesToPolicy(KeySpec keySpec, Policy policy) {
PublicKeyAlgorithm publicKeyAlgorithm = keySpec.getKeyType().getAlgorithm();
int bitStrength = keySpec.getKeyType().getBitStrength();
if (!policy.getPublicKeyAlgorithmPolicy().isAcceptable(publicKeyAlgorithm, bitStrength)) {
throw new IllegalArgumentException("Public key algorithm policy violation: " +
publicKeyAlgorithm + " with bit strength " + bitStrength + " is not acceptable.");
}
}
private void verifyMasterKeyCanCertify(KeySpec spec) {
if (!keyIsCertificationCapable(spec)) {
throw new IllegalArgumentException("Key algorithm " + spec.getKeyType().getName() + " is not capable of creating certifications.");
}
}
private boolean keyIsCertificationCapable(KeySpec keySpec) {
return keySpec.getKeyType().canCertify();
}
@Override
public PGPSecretKeyRing build() throws NoSuchAlgorithmException, PGPException,
InvalidAlgorithmParameterException {
PGPDigestCalculator keyFingerprintCalculator = ImplementationFactory.getInstance().getV4FingerprintCalculator();
PBESecretKeyEncryptor secretKeyEncryptor = buildSecretKeyEncryptor(keyFingerprintCalculator);
PBESecretKeyDecryptor secretKeyDecryptor = buildSecretKeyDecryptor();
passphrase.clear();
// Generate Primary Key
PGPKeyPair certKey = generateKeyPair(primaryKeySpec);
PGPContentSignerBuilder signer = buildContentSigner(certKey);
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(signer);
SignatureSubpackets hashedSubPacketGenerator = primaryKeySpec.getSubpacketGenerator();
hashedSubPacketGenerator.setIssuerFingerprintAndKeyId(certKey.getPublicKey());
if (expirationDate != null) {
hashedSubPacketGenerator.setKeyExpirationTime(certKey.getPublicKey(), expirationDate);
}
if (!userIds.isEmpty()) {
hashedSubPacketGenerator.setPrimaryUserId();
}
PGPSignatureSubpacketGenerator generator = new PGPSignatureSubpacketGenerator();
SignatureSubpacketsHelper.applyTo(hashedSubPacketGenerator, generator);
PGPSignatureSubpacketVector hashedSubPackets = generator.generate();
PGPKeyRingGenerator ringGenerator;
if (userIds.isEmpty()) {
ringGenerator = new PGPKeyRingGenerator(
certKey,
keyFingerprintCalculator,
hashedSubPackets,
null,
signer,
secretKeyEncryptor);
} else {
String primaryUserId = userIds.entrySet().iterator().next().getKey();
ringGenerator = new PGPKeyRingGenerator(
SignatureType.POSITIVE_CERTIFICATION.getCode(), certKey,
primaryUserId, keyFingerprintCalculator,
hashedSubPackets, null, signer, secretKeyEncryptor);
}
addSubKeys(certKey, ringGenerator);
// Generate secret key ring with only primary user id
PGPSecretKeyRing secretKeyRing = ringGenerator.generateSecretKeyRing();
Iterator<PGPSecretKey> secretKeys = secretKeyRing.getSecretKeys();
// Attempt to add additional user-ids to the primary public key
PGPPublicKey primaryPubKey = secretKeys.next().getPublicKey();
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKeyRing.getSecretKey(), secretKeyDecryptor);
Iterator<Map.Entry<String, SelfSignatureSubpackets.Callback>> userIdIterator =
this.userIds.entrySet().iterator();
if (userIdIterator.hasNext()) {
userIdIterator.next(); // Skip primary user id
}
while (userIdIterator.hasNext()) {
Map.Entry<String, SelfSignatureSubpackets.Callback> additionalUserId = userIdIterator.next();
String userIdString = additionalUserId.getKey();
SelfSignatureSubpackets.Callback callback = additionalUserId.getValue();
SelfSignatureSubpackets subpackets = null;
if (callback == null) {
subpackets = hashedSubPacketGenerator;
subpackets.setPrimaryUserId(null);
// additional user-ids are not primary
} else {
subpackets = SignatureSubpackets.createHashedSubpackets(primaryPubKey);
callback.modifyHashedSubpackets(subpackets);
}
signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.getCode(), privateKey);
signatureGenerator.setHashedSubpackets(
SignatureSubpacketsHelper.toVector((SignatureSubpackets) subpackets));
PGPSignature additionalUserIdSignature =
signatureGenerator.generateCertification(userIdString, primaryPubKey);
primaryPubKey = PGPPublicKey.addCertification(primaryPubKey,
userIdString, additionalUserIdSignature);
}
// "reassemble" secret key ring with modified primary key
PGPSecretKey primarySecKey = new PGPSecretKey(
privateKey, primaryPubKey, keyFingerprintCalculator, true, secretKeyEncryptor);
List<PGPSecretKey> secretKeyList = new ArrayList<>();
secretKeyList.add(primarySecKey);
while (secretKeys.hasNext()) {
secretKeyList.add(secretKeys.next());
}
secretKeyRing = new PGPSecretKeyRing(secretKeyList);
return secretKeyRing;
}
private void addSubKeys(PGPKeyPair primaryKey, PGPKeyRingGenerator ringGenerator)
throws NoSuchAlgorithmException, PGPException, InvalidAlgorithmParameterException {
for (KeySpec subKeySpec : subkeySpecs) {
PGPKeyPair subKey = generateKeyPair(subKeySpec);
if (subKeySpec.isInheritedSubPackets()) {
ringGenerator.addSubKey(subKey);
} else {
PGPSignatureSubpacketVector hashedSubpackets = subKeySpec.getSubpackets();
try {
hashedSubpackets = addPrimaryKeyBindingSignatureIfNecessary(
primaryKey, subKey, hashedSubpackets);
} catch (IOException e) {
throw new PGPException("Exception while adding primary key binding signature to signing subkey", e);
}
ringGenerator.addSubKey(subKey, hashedSubpackets, null);
}
}
}
private PGPSignatureSubpacketVector addPrimaryKeyBindingSignatureIfNecessary(
PGPKeyPair primaryKey, PGPKeyPair subKey, PGPSignatureSubpacketVector hashedSubpackets)
throws PGPException, IOException {
int keyFlagMask = hashedSubpackets.getKeyFlags();
if (!KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.SIGN_DATA) &&
!KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.CERTIFY_OTHER)) {
return hashedSubpackets;
}
PGPSignatureGenerator bindingSignatureGenerator = new PGPSignatureGenerator(buildContentSigner(subKey));
bindingSignatureGenerator.init(SignatureType.PRIMARYKEY_BINDING.getCode(), subKey.getPrivateKey());
PGPSignature primaryKeyBindingSig = bindingSignatureGenerator.generateCertification(primaryKey.getPublicKey(), subKey.getPublicKey());
PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator(hashedSubpackets);
subpacketGenerator.addEmbeddedSignature(false, primaryKeyBindingSig);
return subpacketGenerator.generate();
}
private PGPContentSignerBuilder buildContentSigner(PGPKeyPair certKey) {
HashAlgorithm hashAlgorithm = PGPainless.getPolicy()
.getSignatureHashAlgorithmPolicy().defaultHashAlgorithm();
return ImplementationFactory.getInstance().getPGPContentSignerBuilder(
certKey.getPublicKey().getAlgorithm(),
hashAlgorithm.getAlgorithmId());
}
private PBESecretKeyEncryptor buildSecretKeyEncryptor(PGPDigestCalculator keyFingerprintCalculator) {
SymmetricKeyAlgorithm keyEncryptionAlgorithm = PGPainless.getPolicy()
.getSymmetricKeyEncryptionAlgorithmPolicy()
.getDefaultSymmetricKeyAlgorithm();
if (!passphrase.isValid()) {
throw new IllegalStateException("Passphrase was cleared.");
}
return passphrase.isEmpty() ? null : // unencrypted key pair, otherwise AES-256 encrypted
ImplementationFactory.getInstance().getPBESecretKeyEncryptor(
keyEncryptionAlgorithm, keyFingerprintCalculator, passphrase);
}
private PBESecretKeyDecryptor buildSecretKeyDecryptor() throws PGPException {
if (!passphrase.isValid()) {
throw new IllegalStateException("Passphrase was cleared.");
}
return passphrase.isEmpty() ? null :
ImplementationFactory.getInstance().getPBESecretKeyDecryptor(passphrase);
}
public static PGPKeyPair generateKeyPair(KeySpec spec)
throws NoSuchAlgorithmException, PGPException,
InvalidAlgorithmParameterException {
KeyType type = spec.getKeyType();
KeyPairGenerator certKeyGenerator = KeyPairGenerator.getInstance(type.getName(),
ProviderFactory.getProvider());
certKeyGenerator.initialize(type.getAlgorithmSpec());
// Create raw Key Pair
KeyPair keyPair = certKeyGenerator.generateKeyPair();
Date keyCreationDate = spec.getKeyCreationDate() != null ? spec.getKeyCreationDate() : new Date();
// Form PGP key pair
PGPKeyPair pgpKeyPair = ImplementationFactory.getInstance()
.getPGPKeyPair(type.getAlgorithm(), keyPair, keyCreationDate);
return pgpKeyPair;
}
}

View file

@ -1,45 +0,0 @@
// SPDX-FileCopyrightText: 2018 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.key.generation;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import javax.annotation.Nonnull;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.pgpainless.key.util.UserId;
import org.pgpainless.util.Passphrase;
public interface KeyRingBuilderInterface<B extends KeyRingBuilderInterface<B>> {
B setPrimaryKey(@Nonnull KeySpec keySpec);
default B setPrimaryKey(@Nonnull KeySpecBuilder builder) {
return setPrimaryKey(builder.build());
}
B addSubkey(@Nonnull KeySpec keySpec);
default B addSubkey(@Nonnull KeySpecBuilder builder) {
return addSubkey(builder.build());
}
default B addUserId(UserId userId) {
return addUserId(userId.toString());
}
B addUserId(@Nonnull String userId);
B addUserId(@Nonnull byte[] userId);
B setExpirationDate(@Nonnull Date expirationDate);
B setPassphrase(@Nonnull Passphrase passphrase);
PGPSecretKeyRing build() throws NoSuchAlgorithmException, PGPException,
InvalidAlgorithmParameterException;
}

View file

@ -1,26 +0,0 @@
// SPDX-FileCopyrightText: 2018 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.key.generation;
import javax.annotation.Nonnull;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import java.util.Date;
public interface KeySpecBuilderInterface {
KeySpecBuilder overridePreferredCompressionAlgorithms(@Nonnull CompressionAlgorithm... compressionAlgorithms);
KeySpecBuilder overridePreferredHashAlgorithms(@Nonnull HashAlgorithm... preferredHashAlgorithms);
KeySpecBuilder overridePreferredSymmetricKeyAlgorithms(@Nonnull SymmetricKeyAlgorithm... preferredSymmetricKeyAlgorithms);
KeySpecBuilder setKeyCreationDate(@Nonnull Date creationDate);
KeySpec build();
}

View file

@ -0,0 +1,236 @@
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.key.generation
import org.bouncycastle.openpgp.*
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder
import org.bouncycastle.openpgp.operator.PGPDigestCalculator
import org.bouncycastle.util.Strings
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.KeyFlag
import org.pgpainless.algorithm.SignatureType
import org.pgpainless.implementation.ImplementationFactory
import org.pgpainless.key.protection.UnlockSecretKey
import org.pgpainless.policy.Policy
import org.pgpainless.provider.ProviderFactory
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets
import org.pgpainless.signature.subpackets.SignatureSubpackets
import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper
import org.pgpainless.util.Passphrase
import java.io.IOException
import java.security.KeyPairGenerator
import java.util.*
const val MILLIS_IN_YEAR = 1000L * 60 * 60 * 24 * 365
class KeyRingBuilder : KeyRingBuilderInterface<KeyRingBuilder> {
private var primaryKeySpec: KeySpec? = null
private val subKeySpecs = mutableListOf<KeySpec>()
private val userIds = mutableMapOf<String, SelfSignatureSubpackets.Callback?>()
private var passphrase = Passphrase.emptyPassphrase()
private var expirationDate: Date? = Date(System.currentTimeMillis() + (5 * MILLIS_IN_YEAR))
override fun setPrimaryKey(keySpec: KeySpec): KeyRingBuilder = apply {
verifyKeySpecCompliesToPolicy(keySpec, PGPainless.getPolicy())
verifyPrimaryKeyCanCertify(keySpec)
this.primaryKeySpec = keySpec
}
override fun addSubkey(keySpec: KeySpec): KeyRingBuilder = apply {
verifyKeySpecCompliesToPolicy(keySpec, PGPainless.getPolicy())
subKeySpecs.add(keySpec)
}
override fun addUserId(userId: CharSequence): KeyRingBuilder = apply {
userIds[userId.toString().trim()] = null
}
override fun addUserId(userId: ByteArray): KeyRingBuilder = addUserId(Strings.fromUTF8ByteArray(userId))
override fun setExpirationDate(expirationDate: Date?): KeyRingBuilder = apply {
if (expirationDate == null) {
this.expirationDate = null
return@apply
}
this.expirationDate = expirationDate.let {
require(Date() < expirationDate) {
"Expiration date must be in the future."
}
expirationDate
}
}
override fun setPassphrase(passphrase: Passphrase): KeyRingBuilder = apply {
this.passphrase = passphrase
}
private fun verifyKeySpecCompliesToPolicy(keySpec: KeySpec, policy: Policy) {
val algorithm = keySpec.keyType.algorithm
val bitStrength = keySpec.keyType.bitStrength
require(policy.publicKeyAlgorithmPolicy.isAcceptable(algorithm, bitStrength)) {
"Public key algorithm policy violation: $algorithm with bit strength $bitStrength is not acceptable."
}
}
private fun verifyPrimaryKeyCanCertify(spec: KeySpec) {
require(keyIsCertificationCapable(spec)) {
"Key algorithm ${spec.keyType.name} is not capable of creation certifications."
}
}
private fun keyIsCertificationCapable(keySpec: KeySpec) = keySpec.keyType.canCertify()
override fun build(): PGPSecretKeyRing {
val keyFingerprintCalculator = ImplementationFactory.getInstance()
.v4FingerprintCalculator
val secretKeyEncryptor = buildSecretKeyEncryptor(keyFingerprintCalculator)
val secretKeyDecryptor = buildSecretKeyDecryptor()
passphrase.clear() // Passphrase was used above, so we can get rid of it
// generate primary key
requireNotNull(primaryKeySpec) {
"Primary Key spec required."
}
val certKey = generateKeyPair(primaryKeySpec!!)
val signer = buildContentSigner(certKey)
val signatureGenerator = PGPSignatureGenerator(signer)
val hashedSubPacketGenerator = primaryKeySpec!!.subpacketGenerator
hashedSubPacketGenerator.setIssuerFingerprintAndKeyId(certKey.publicKey)
expirationDate?.let { hashedSubPacketGenerator.setKeyExpirationTime(certKey.publicKey, it) }
if (userIds.isNotEmpty()) {
hashedSubPacketGenerator.setPrimaryUserId()
}
val generator = PGPSignatureSubpacketGenerator()
SignatureSubpacketsHelper.applyTo(hashedSubPacketGenerator, generator)
val hashedSubPackets = generator.generate()
val ringGenerator = if (userIds.isEmpty()) {
PGPKeyRingGenerator(certKey, keyFingerprintCalculator, hashedSubPackets, null, signer,
secretKeyEncryptor)
} else {
userIds.keys.first().let { primaryUserId ->
PGPKeyRingGenerator(SignatureType.POSITIVE_CERTIFICATION.code, certKey, primaryUserId,
keyFingerprintCalculator, hashedSubPackets, null, signer,
secretKeyEncryptor)
}
}
addSubKeys(certKey, ringGenerator)
// Generate secret key ring with only primary userId
val secretKeyRing = ringGenerator.generateSecretKeyRing()
val secretKeys = secretKeyRing.secretKeys
// Attempt to add additional user-ids to the primary public key
var primaryPubKey = secretKeys.next().publicKey
val privateKey = UnlockSecretKey.unlockSecretKey(secretKeyRing.secretKey, secretKeyDecryptor)
val userIdIterator = userIds.entries.iterator()
if (userIdIterator.hasNext()) {
userIdIterator.next() // Skip primary userId
}
while (userIdIterator.hasNext()) {
val additionalUserId = userIdIterator.next()
val userIdString = additionalUserId.key
val callback = additionalUserId.value
val subpackets = if (callback == null) {
hashedSubPacketGenerator.also { it.setPrimaryUserId(null) }
} else {
SignatureSubpackets.createHashedSubpackets(primaryPubKey).also { callback.modifyHashedSubpackets(it) }
}
signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.code, privateKey)
signatureGenerator.setHashedSubpackets(
SignatureSubpacketsHelper.toVector(subpackets))
val additionalUserIdSignature = signatureGenerator.generateCertification(userIdString, primaryPubKey)
primaryPubKey = PGPPublicKey.addCertification(primaryPubKey, userIdString, additionalUserIdSignature)
}
// Reassemble secret key ring with modified primary key
val primarySecretKey = PGPSecretKey(
privateKey, primaryPubKey, keyFingerprintCalculator, true, secretKeyEncryptor)
val secretKeyList = mutableListOf(primarySecretKey)
while (secretKeys.hasNext()) {
secretKeyList.add(secretKeys.next())
}
return PGPSecretKeyRing(secretKeyList)
}
private fun addSubKeys(primaryKey: PGPKeyPair, ringGenerator: PGPKeyRingGenerator) {
for (subKeySpec in subKeySpecs) {
val subKey = generateKeyPair(subKeySpec)
if (subKeySpec.isInheritedSubPackets) {
ringGenerator.addSubKey(subKey)
} else {
var hashedSubpackets = subKeySpec.subpackets
try {
hashedSubpackets = addPrimaryKeyBindingSignatureIfNecessary(primaryKey, subKey, hashedSubpackets)
} catch (e: IOException) {
throw PGPException("Exception while adding primary key binding signature to signing subkey.", e)
}
ringGenerator.addSubKey(subKey, hashedSubpackets, null)
}
}
}
private fun addPrimaryKeyBindingSignatureIfNecessary(primaryKey: PGPKeyPair, subKey: PGPKeyPair, hashedSubpackets: PGPSignatureSubpacketVector): PGPSignatureSubpacketVector {
val keyFlagMask = hashedSubpackets.keyFlags
if (!KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.SIGN_DATA) &&
!KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.CERTIFY_OTHER)) {
return hashedSubpackets
}
val bindingSignatureGenerator = PGPSignatureGenerator(buildContentSigner(subKey))
bindingSignatureGenerator.init(SignatureType.PRIMARYKEY_BINDING.code, subKey.privateKey)
val primaryKeyBindingSig = bindingSignatureGenerator.generateCertification(primaryKey.publicKey, subKey.publicKey)
val subpacketGenerator = PGPSignatureSubpacketGenerator(hashedSubpackets)
subpacketGenerator.addEmbeddedSignature(false, primaryKeyBindingSig)
return subpacketGenerator.generate()
}
private fun buildContentSigner(certKey: PGPKeyPair): PGPContentSignerBuilder {
val hashAlgorithm = PGPainless.getPolicy().signatureHashAlgorithmPolicy.defaultHashAlgorithm
return ImplementationFactory.getInstance().getPGPContentSignerBuilder(
certKey.publicKey.algorithm, hashAlgorithm.algorithmId)
}
private fun buildSecretKeyEncryptor(keyFingerprintCalculator: PGPDigestCalculator): PBESecretKeyEncryptor? {
val keyEncryptionAlgorithm = PGPainless.getPolicy().symmetricKeyEncryptionAlgorithmPolicy.defaultSymmetricKeyAlgorithm
check(passphrase.isValid) {
"Passphrase was cleared."
}
return if (passphrase.isEmpty) null else ImplementationFactory.getInstance()
.getPBESecretKeyEncryptor(keyEncryptionAlgorithm, keyFingerprintCalculator, passphrase)
}
private fun buildSecretKeyDecryptor(): PBESecretKeyDecryptor? {
check(passphrase.isValid) {
"Passphrase was cleared."
}
return if (passphrase.isEmpty) null else ImplementationFactory.getInstance()
.getPBESecretKeyDecryptor(passphrase)
}
companion object {
@JvmStatic
fun generateKeyPair(spec: KeySpec): PGPKeyPair {
spec.keyType.let { type ->
// Create raw Key Pair
val keyPair = KeyPairGenerator.getInstance(type.name, ProviderFactory.getProvider())
.also { it.initialize(type.algorithmSpec) }
.generateKeyPair()
val keyCreationDate = spec.keyCreationDate ?: Date()
// Form PGP Key Pair
return ImplementationFactory.getInstance()
.getPGPKeyPair(type.algorithm, keyPair, keyCreationDate)
}
}
}
}

View file

@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.key.generation
import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPSecretKeyRing
import org.pgpainless.util.Passphrase
import java.security.InvalidAlgorithmParameterException
import java.security.NoSuchAlgorithmException
import java.util.*
interface KeyRingBuilderInterface<B : KeyRingBuilderInterface<B>> {
fun setPrimaryKey(keySpec: KeySpec): B
fun setPrimaryKey(builder: KeySpecBuilder): B = setPrimaryKey(builder.build())
fun addSubkey(keySpec: KeySpec): B
fun addSubkey(builder: KeySpecBuilder): B = addSubkey(builder.build())
fun addUserId(userId: CharSequence): B
fun addUserId(userId: ByteArray): B
fun setExpirationDate(expirationDate: Date?): B
fun setPassphrase(passphrase: Passphrase): B
@Throws(NoSuchAlgorithmException::class, PGPException::class, InvalidAlgorithmParameterException::class)
fun build(): PGPSecretKeyRing
}

View file

@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.key.generation
import org.pgpainless.algorithm.CompressionAlgorithm
import org.pgpainless.algorithm.HashAlgorithm
import org.pgpainless.algorithm.SymmetricKeyAlgorithm
import java.util.*
interface KeySpecBuilderInterface {
fun overridePreferredCompressionAlgorithms(vararg algorithms: CompressionAlgorithm): KeySpecBuilder
fun overridePreferredHashAlgorithms(vararg algorithms: HashAlgorithm): KeySpecBuilder
fun overridePreferredSymmetricKeyAlgorithms(vararg algorithms: SymmetricKeyAlgorithm): KeySpecBuilder
fun setKeyCreationDate(creationDate: Date): KeySpecBuilder
fun build(): KeySpec
}