This commit is contained in:
Paul Schaub 2021-11-20 16:07:27 +01:00
parent 25c95804ce
commit 176574df50
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
10 changed files with 238 additions and 374 deletions

View File

@ -42,7 +42,6 @@ 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.SecretKeyRingProtector;
import org.pgpainless.key.protection.UnlockSecretKey;
import org.pgpainless.provider.ProviderFactory;
import org.pgpainless.signature.subpackets.SignatureSubpackets;
@ -133,6 +132,8 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
PGPKeyPair certKey = generateKeyPair(primaryKeySpec);
PGPContentSignerBuilder signer = buildContentSigner(certKey);
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(signer);
// Prepare primary user-id sig
SignatureSubpackets hashedSubPacketGenerator = primaryKeySpec.getSubpacketGenerator();
hashedSubPacketGenerator.setPrimaryUserId();
if (expirationDate != null) {
@ -142,9 +143,7 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
SignatureSubpacketsHelper.applyTo(hashedSubPacketGenerator, generator);
PGPSignatureSubpacketVector hashedSubPackets = generator.generate();
// Generator which the user can get the key pair from
PGPKeyRingGenerator ringGenerator = buildRingGenerator(certKey, signer, keyFingerprintCalculator, hashedSubPackets, secretKeyEncryptor);
addSubKeys(certKey, ringGenerator);
// Generate secret key ring with only primary user id
@ -155,10 +154,10 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
// Attempt to add additional user-ids to the primary public key
PGPPublicKey primaryPubKey = secretKeys.next().getPublicKey();
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKeyRing.getSecretKey(), secretKeyDecryptor);
Iterator<String> additionalUserIds = userIds.iterator();
additionalUserIds.next(); // Skip primary user id
while (additionalUserIds.hasNext()) {
String additionalUserId = additionalUserIds.next();
Iterator<String> userIdIterator = this.userIds.iterator();
userIdIterator.next(); // Skip primary user id
while (userIdIterator.hasNext()) {
String additionalUserId = userIdIterator.next();
signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.getCode(), privateKey);
PGPSignature additionalUserIdSignature =
signatureGenerator.generateCertification(additionalUserId, primaryPubKey);
@ -175,7 +174,6 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
secretKeyList.add(secretKeys.next());
}
secretKeyRing = new PGPSecretKeyRing(secretKeyList);
return secretKeyRing;
}
@ -266,31 +264,4 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
PGPKeyPair pgpKeyPair = ImplementationFactory.getInstance().getPGPKeyPair(type.getAlgorithm(), keyPair, new Date());
return pgpKeyPair;
}
private static SecretKeyRingProtector protectorFromPassphrase(@Nonnull Passphrase passphrase, long keyId) {
if (!passphrase.isValid()) {
throw new IllegalStateException("Passphrase has been cleared.");
}
if (passphrase.isEmpty()) {
return SecretKeyRingProtector.unprotectedKeys();
} else {
return SecretKeyRingProtector.unlockSingleKeyWith(passphrase, keyId);
}
}
public static PGPSecretKey generatePGPSecretKey(KeySpec keySpec, @Nonnull Passphrase passphrase, boolean isPrimary)
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
PGPDigestCalculator keyFingerprintCalculator = ImplementationFactory.getInstance()
.getV4FingerprintCalculator();
PGPKeyPair keyPair = generateKeyPair(keySpec);
SecretKeyRingProtector protector = protectorFromPassphrase(passphrase, keyPair.getKeyID());
return new PGPSecretKey(
keyPair.getPrivateKey(),
keyPair.getPublicKey(),
keyFingerprintCalculator,
isPrimary,
protector.getEncryptor(keyPair.getKeyID()));
}
}

View File

@ -47,7 +47,7 @@ public class KeySpec {
}
@Nonnull
SignatureSubpackets getSubpacketGenerator() {
public SignatureSubpackets getSubpacketGenerator() {
return subpacketGenerator;
}

View File

@ -26,7 +26,6 @@ import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
@ -36,11 +35,12 @@ 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.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.negotiation.HashAlgorithmNegotiator;
import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.key.OpenPgpFingerprint;
import org.pgpainless.key.generation.KeyRingBuilder;
@ -57,14 +57,14 @@ import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvi
import org.pgpainless.key.util.KeyRingUtils;
import org.pgpainless.key.util.RevocationAttributes;
import org.pgpainless.signature.SignatureUtils;
import org.pgpainless.signature.builder.PrimaryKeyBindingSignatureBuilder;
import org.pgpainless.signature.builder.RevocationSignatureBuilder;
import org.pgpainless.signature.builder.SelfSignatureBuilder;
import org.pgpainless.signature.builder.SignatureFactory;
import org.pgpainless.signature.builder.SubkeyBindingSignatureBuilder;
import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets;
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpacketGeneratorUtil;
import org.pgpainless.signature.subpackets.SignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper;
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
import org.pgpainless.util.CollectionUtils;
import org.pgpainless.util.Passphrase;
@ -95,33 +95,21 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
SecretKeyRingProtector protector) throws PGPException {
userId = sanitizeUserId(userId);
List<PGPSecretKey> secretKeyList = new ArrayList<>();
Iterator<PGPSecretKey> secretKeyIterator = secretKeyRing.getSecretKeys();
// user-id certifications live on the primary key
PGPSecretKey primaryKey = secretKeyRing.getSecretKey();
// add user-id certificate to primary key
PGPSecretKey primaryKey = secretKeyIterator.next();
PGPPublicKey publicKey = primaryKey.getPublicKey();
// retain key flags from previous signature
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing);
List<KeyFlag> keyFlags = info.getKeyFlagsOf(info.getKeyId());
SelfSignatureBuilder builder = SignatureFactory.selfCertifyUserId(
primaryKey,
protector,
signatureSubpacketCallback,
keyFlags.toArray(new KeyFlag[0]));
keyFlags);
builder.setSignatureType(SignatureType.POSITIVE_CERTIFICATION);
builder.applyCallback(signatureSubpacketCallback);
PGPSignature signature = builder.build(publicKey, userId);
publicKey = PGPPublicKey.addCertification(publicKey,
userId, signature);
primaryKey = PGPSecretKey.replacePublicKey(primaryKey, publicKey);
secretKeyList.add(primaryKey);
while (secretKeyIterator.hasNext()) {
secretKeyList.add(secretKeyIterator.next());
}
secretKeyRing = new PGPSecretKeyRing(secretKeyList);
PGPSignature signature = builder.build(primaryKey.getPublicKey(), userId);
secretKeyRing = KeyRingUtils.injectCertification(secretKeyRing, userId, signature);
return this;
}
@ -138,51 +126,30 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
public SecretKeyRingEditorInterface addSubKey(@Nonnull KeySpec keySpec,
@Nonnull Passphrase subKeyPassphrase,
SecretKeyRingProtector secretKeyRingProtector)
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException {
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException {
PGPKeyPair keyPair = KeyRingBuilder.generateKeyPair(keySpec);
PGPSecretKey secretSubKey = generateSubKey(keySpec, subKeyPassphrase);
SecretKeyRingProtector subKeyProtector = PasswordBasedSecretKeyRingProtector
.forKey(secretSubKey, subKeyPassphrase);
PGPSignatureSubpacketVector hashedSubpackets = keySpec.getSubpackets();
PGPSignatureSubpacketVector unhashedSubpackets = null;
.forKeyId(keyPair.getKeyID(), subKeyPassphrase);
return addSubKey(secretSubKey, hashedSubpackets, unhashedSubpackets, subKeyProtector, secretKeyRingProtector);
SelfSignatureSubpackets.Callback callback = new SelfSignatureSubpackets.Callback() {
@Override
public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) {
SignatureSubpacketsHelper.applyFrom(keySpec.getSubpackets(), (SignatureSubpackets) hashedSubpackets);
}
};
List<KeyFlag> keyFlags = KeyFlag.fromBitmask(keySpec.getSubpackets().getKeyFlags());
KeyFlag firstFlag = keyFlags.remove(0);
KeyFlag[] otherFlags = keyFlags.toArray(new KeyFlag[0]);
return addSubKey(keyPair, callback, subKeyProtector, secretKeyRingProtector, firstFlag, otherFlags);
}
@Override
@Deprecated
public SecretKeyRingEditorInterface addSubKey(PGPSecretKey secretSubKey,
PGPSignatureSubpacketVector hashedSubpackets,
PGPSignatureSubpacketVector unhashedSubpackets,
SecretKeyRingProtector subKeyProtector,
SecretKeyRingProtector keyRingProtector)
throws PGPException {
PGPPublicKey primaryKey = secretKeyRing.getSecretKey().getPublicKey();
PBESecretKeyDecryptor ringDecryptor = keyRingProtector.getDecryptor(primaryKey.getKeyID());
PBESecretKeyEncryptor subKeyEncryptor = subKeyProtector.getEncryptor(secretSubKey.getKeyID());
PGPDigestCalculator digestCalculator = ImplementationFactory.getInstance().getV4FingerprintCalculator();
PGPContentSignerBuilder contentSignerBuilder =
SignatureUtils.getPgpContentSignerBuilderForKey(primaryKey);
PGPPrivateKey privateSubKey = UnlockSecretKey.unlockSecretKey(secretSubKey, subKeyProtector);
PGPKeyPair subKeyPair = new PGPKeyPair(secretSubKey.getPublicKey(), privateSubKey);
PGPKeyRingGenerator keyRingGenerator = new PGPKeyRingGenerator(
secretKeyRing, ringDecryptor, digestCalculator, contentSignerBuilder, subKeyEncryptor);
keyRingGenerator.addSubKey(subKeyPair, hashedSubpackets, unhashedSubpackets);
secretKeyRing = keyRingGenerator.generateSecretKeyRing();
return this;
}
@Override
public SecretKeyRingEditorInterface addSubKey(PGPSecretKey subkey,
public SecretKeyRingEditorInterface addSubKey(PGPKeyPair subkey,
@Nullable SelfSignatureSubpackets.Callback bindingSignatureCallback,
@Nullable SelfSignatureSubpackets.Callback backSignatureCallback,
SecretKeyRingProtector subkeyProtector,
SecretKeyRingProtector primaryKeyProtector,
KeyFlag keyFlag,
@ -193,36 +160,49 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
SignatureSubpacketsUtil.assureKeyCanCarryFlags(subkeyAlgorithm);
PGPSecretKey primaryKey = secretKeyRing.getSecretKey();
SubkeyBindingSignatureBuilder bindingSigBuilder =
new SubkeyBindingSignatureBuilder(primaryKey, primaryKeyProtector);
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing);
PublicKeyAlgorithm signingKeyAlgorithm = PublicKeyAlgorithm.fromId(primaryKey.getPublicKey().getAlgorithm());
HashAlgorithm hashAlgorithm = HashAlgorithmNegotiator
.negotiateSignatureHashAlgorithm(PGPainless.getPolicy())
.negotiateHashAlgorithm(info.getPreferredHashAlgorithms());
bindingSigBuilder.applyCallback(bindingSignatureCallback);
bindingSigBuilder.getHashedSubpackets().setKeyFlags(flags);
// While we'd like to rely on our own BindingSignatureBuilder implementation,
// unfortunately we have to use BCs PGPKeyRingGenerator class since there is no public constructor
// for subkeys. See https://github.com/bcgit/bc-java/pull/1063
PGPKeyRingGenerator ringGenerator = new PGPKeyRingGenerator(
secretKeyRing,
primaryKeyProtector.getDecryptor(primaryKey.getKeyID()),
ImplementationFactory.getInstance().getV4FingerprintCalculator(),
ImplementationFactory.getInstance().getPGPContentSignerBuilder(
signingKeyAlgorithm, hashAlgorithm),
subkeyProtector.getEncryptor(subkey.getKeyID()));
SelfSignatureSubpackets hashedSubpackets = SignatureSubpackets.createHashedSubpackets(primaryKey.getPublicKey());
SelfSignatureSubpackets unhashedSubpackets = SignatureSubpackets.createEmptySubpackets();
hashedSubpackets.setKeyFlags(flags);
if (bindingSignatureCallback != null) {
bindingSignatureCallback.modifyHashedSubpackets(hashedSubpackets);
bindingSignatureCallback.modifyUnhashedSubpackets(unhashedSubpackets);
}
boolean isSigningKey = CollectionUtils.contains(flags, KeyFlag.SIGN_DATA) ||
CollectionUtils.contains(flags, KeyFlag.CERTIFY_OTHER);
PGPContentSignerBuilder primaryKeyBindingSigner = null;
if (isSigningKey) {
// Add embedded back-signature made by subkey over primary key
PrimaryKeyBindingSignatureBuilder backSigBuilder =
new PrimaryKeyBindingSignatureBuilder(subkey, subkeyProtector);
backSigBuilder.applyCallback(backSignatureCallback);
PGPSignature backSig = backSigBuilder.build(primaryKey.getPublicKey());
bindingSigBuilder.getHashedSubpackets().addEmbeddedSignature(backSig);
primaryKeyBindingSigner = ImplementationFactory.getInstance().getPGPContentSignerBuilder(subkeyAlgorithm, hashAlgorithm);
}
PGPSignature bindingSig = bindingSigBuilder.build(subkey.getPublicKey());
subkey = KeyRingUtils.secretKeyPlusSignature(subkey, bindingSig);
secretKeyRing = KeyRingUtils.secretKeysPlusSecretKey(secretKeyRing, subkey);
ringGenerator.addSubKey(subkey,
SignatureSubpacketsHelper.toVector((SignatureSubpackets) hashedSubpackets),
SignatureSubpacketsHelper.toVector((SignatureSubpackets) unhashedSubpackets),
primaryKeyBindingSigner);
secretKeyRing = ringGenerator.generateSecretKeyRing();
return this;
}
private PGPSecretKey generateSubKey(@Nonnull KeySpec keySpec,
@Nonnull Passphrase subKeyPassphrase)
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
return KeyRingBuilder.generatePGPSecretKey(keySpec, subKeyPassphrase, false);
}
@Override
public SecretKeyRingEditorInterface revoke(SecretKeyRingProtector secretKeyRingProtector,
@Nullable RevocationAttributes revocationAttributes)
@ -252,15 +232,13 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
SecretKeyRingProtector secretKeyRingProtector,
@Nullable RevocationSignatureSubpackets.Callback subpacketsCallback)
throws PGPException {
// retrieve subkey to be revoked
PGPPublicKey revokeeSubKey = KeyRingUtils.requirePublicKeyFrom(secretKeyRing, keyID);
// create revocation
PGPSignature subKeyRevocation = generateRevocation(secretKeyRingProtector, revokeeSubKey,
subpacketsCallback);
revokeeSubKey = PGPPublicKey.addCertification(revokeeSubKey, subKeyRevocation);
// Inject revoked public key into key ring
PGPPublicKeyRing publicKeyRing = KeyRingUtils.publicKeyRingFrom(secretKeyRing);
publicKeyRing = PGPPublicKeyRing.insertPublicKey(publicKeyRing, revokeeSubKey);
secretKeyRing = PGPSecretKeyRing.replacePublicKeys(secretKeyRing, publicKeyRing);
// inject revocation sig into key ring
secretKeyRing = KeyRingUtils.injectCertification(secretKeyRing, revokeeSubKey, subKeyRevocation);
return this;
}
@ -285,6 +263,16 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
return generateRevocation(secretKeyRingProtector, revokeeSubkey, callback);
}
@Override
public PGPSignature createRevocationCertificate(
long subkeyId,
SecretKeyRingProtector secretKeyRingProtector,
@Nullable RevocationSignatureSubpackets.Callback certificateSubpacketsCallback)
throws PGPException {
PGPPublicKey revokeeSubkey = KeyRingUtils.requirePublicKeyFrom(secretKeyRing, subkeyId);
return generateRevocation(secretKeyRingProtector, revokeeSubkey, certificateSubpacketsCallback);
}
private PGPSignature generateRevocation(SecretKeyRingProtector protector,
PGPPublicKey revokeeSubKey,
@Nullable RevocationSignatureSubpackets.Callback callback)
@ -371,12 +359,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
signatureBuilder.applyCallback(callback);
PGPSignature revocationSignature = signatureBuilder.build(userId);
primaryPublicKey = PGPPublicKey.addCertification(primaryPublicKey, userId, revocationSignature);
PGPPublicKeyRing publicKeyRing = KeyRingUtils.publicKeyRingFrom(secretKeyRing);
publicKeyRing = PGPPublicKeyRing.insertPublicKey(publicKeyRing, primaryPublicKey);
secretKeyRing = PGPSecretKeyRing.replacePublicKeys(secretKeyRing, publicKeyRing);
secretKeyRing = KeyRingUtils.injectCertification(secretKeyRing, userId, revocationSignature);
return this;
}

View File

@ -12,10 +12,9 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.key.OpenPgpFingerprint;
import org.pgpainless.key.generation.KeySpec;
@ -66,18 +65,10 @@ public interface SecretKeyRingEditorInterface {
SecretKeyRingEditorInterface addSubKey(@Nonnull KeySpec keySpec,
@Nullable Passphrase subKeyPassphrase,
SecretKeyRingProtector secretKeyRingProtector)
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException;
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException;
@Deprecated
SecretKeyRingEditorInterface addSubKey(PGPSecretKey subKey,
PGPSignatureSubpacketVector hashedSubpackets,
PGPSignatureSubpacketVector unhashedSubpackets,
SecretKeyRingProtector subKeyProtector, SecretKeyRingProtector keyRingProtector)
throws PGPException;
SecretKeyRingEditorInterface addSubKey(PGPSecretKey subkey,
SecretKeyRingEditorInterface addSubKey(PGPKeyPair subkey,
@Nullable SelfSignatureSubpackets.Callback bindingSignatureCallback,
@Nullable SelfSignatureSubpackets.Callback backSignatureCallback,
SecretKeyRingProtector subkeyProtector,
SecretKeyRingProtector primaryKeyProtector,
KeyFlag keyFlag,
@ -244,17 +235,22 @@ public interface SecretKeyRingEditorInterface {
* @return revocation certificate
*/
PGPSignature createRevocationCertificate(SecretKeyRingProtector secretKeyRingProtector,
RevocationAttributes revocationAttributes)
@Nullable RevocationAttributes revocationAttributes)
throws PGPException;
PGPSignature createRevocationCertificate(long subkeyId,
SecretKeyRingProtector secretKeyRingProtector,
RevocationAttributes revocationAttributes)
@Nullable RevocationAttributes revocationAttributes)
throws PGPException;
PGPSignature createRevocationCertificate(long subkeyId,
SecretKeyRingProtector secretKeyRingProtector,
@Nullable RevocationSignatureSubpackets.Callback certificateSubpacketsCallback)
throws PGPException;
default PGPSignature createRevocationCertificate(OpenPgpFingerprint subkeyFingerprint,
SecretKeyRingProtector secretKeyRingProtector,
RevocationAttributes revocationAttributes)
@Nullable RevocationAttributes revocationAttributes)
throws PGPException {
return createRevocationCertificate(subkeyFingerprint.getKeyId(), secretKeyRingProtector, revocationAttributes);
}

View File

@ -22,9 +22,10 @@ import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector;
import org.pgpainless.PGPainless;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.protection.UnlockSecretKey;
import org.pgpainless.util.CollectionUtils;
public final class KeyRingUtils {
@ -203,22 +204,121 @@ public final class KeyRingUtils {
return publicKeys;
}
public static PGPSecretKeyRing secretKeysPlusPublicKey(PGPSecretKeyRing secretKeys, PGPPublicKey subkey) {
PGPPublicKeyRing publicKeys = publicKeyRingFrom(secretKeys);
PGPPublicKeyRing newPublicKeys = publicKeysPlusPublicKey(publicKeys, subkey);
PGPSecretKeyRing newSecretKeys = PGPSecretKeyRing.replacePublicKeys(secretKeys, newPublicKeys);
return newSecretKeys;
public static <T extends PGPKeyRing> T injectCertification(T keyRing, PGPPublicKey certifiedKey, PGPSignature certification) {
PGPSecretKeyRing secretKeys = null;
PGPPublicKeyRing publicKeys;
if (keyRing instanceof PGPSecretKeyRing) {
secretKeys = (PGPSecretKeyRing) keyRing;
publicKeys = PGPainless.extractCertificate(secretKeys);
} else {
publicKeys = (PGPPublicKeyRing) keyRing;
}
certifiedKey = PGPPublicKey.addCertification(certifiedKey, certification);
List<PGPPublicKey> publicKeyList = new ArrayList<>();
Iterator<PGPPublicKey> publicKeyIterator = publicKeys.iterator();
boolean added = false;
while (publicKeyIterator.hasNext()) {
PGPPublicKey key = publicKeyIterator.next();
if (key.getKeyID() == certifiedKey.getKeyID()) {
added = true;
publicKeyList.add(certifiedKey);
} else {
publicKeyList.add(key);
}
}
if (!added) {
throw new NoSuchElementException("Cannot find public key with id " + Long.toHexString(certifiedKey.getKeyID()) + " in the provided key ring.");
}
publicKeys = new PGPPublicKeyRing(publicKeyList);
if (secretKeys == null) {
return (T) publicKeys;
} else {
secretKeys = PGPSecretKeyRing.replacePublicKeys(secretKeys, publicKeys);
return (T) secretKeys;
}
}
public static PGPPublicKeyRing publicKeysPlusPublicKey(PGPPublicKeyRing publicKeys, PGPPublicKey subkey) {
List<PGPPublicKey> publicKeyList = CollectionUtils.iteratorToList(publicKeys.getPublicKeys());
publicKeyList.add(subkey);
PGPPublicKeyRing newPublicKeys = new PGPPublicKeyRing(publicKeyList);
return newPublicKeys;
public static <T extends PGPKeyRing> T injectCertification(T keyRing, String userId, PGPSignature certification) {
PGPSecretKeyRing secretKeys = null;
PGPPublicKeyRing publicKeys;
if (keyRing instanceof PGPSecretKeyRing) {
secretKeys = (PGPSecretKeyRing) keyRing;
publicKeys = PGPainless.extractCertificate(secretKeys);
} else {
publicKeys = (PGPPublicKeyRing) keyRing;
}
Iterator<PGPPublicKey> publicKeyIterator = publicKeys.iterator();
PGPPublicKey primaryKey = publicKeyIterator.next();
primaryKey = PGPPublicKey.addCertification(primaryKey, userId, certification);
List<PGPPublicKey> publicKeyList = new ArrayList<>();
publicKeyList.add(primaryKey);
while (publicKeyIterator.hasNext()) {
publicKeyList.add(publicKeyIterator.next());
}
publicKeys = new PGPPublicKeyRing(publicKeyList);
if (secretKeys == null) {
return (T) publicKeys;
} else {
secretKeys = PGPSecretKeyRing.replacePublicKeys(secretKeys, publicKeys);
return (T) secretKeys;
}
}
public static PGPSecretKeyRing secretKeysPlusSecretKey(PGPSecretKeyRing secretKeys, PGPSecretKey subkey) {
return PGPSecretKeyRing.insertSecretKey(secretKeys, subkey);
public static <T extends PGPKeyRing> T injectCertification(T keyRing, PGPUserAttributeSubpacketVector userAttributes, PGPSignature certification) {
PGPSecretKeyRing secretKeys = null;
PGPPublicKeyRing publicKeys;
if (keyRing instanceof PGPSecretKeyRing) {
secretKeys = (PGPSecretKeyRing) keyRing;
publicKeys = PGPainless.extractCertificate(secretKeys);
} else {
publicKeys = (PGPPublicKeyRing) keyRing;
}
Iterator<PGPPublicKey> publicKeyIterator = publicKeys.iterator();
PGPPublicKey primaryKey = publicKeyIterator.next();
primaryKey = PGPPublicKey.addCertification(primaryKey, userAttributes, certification);
List<PGPPublicKey> publicKeyList = new ArrayList<>();
publicKeyList.add(primaryKey);
while (publicKeyIterator.hasNext()) {
publicKeyList.add(publicKeyIterator.next());
}
publicKeys = new PGPPublicKeyRing(publicKeyList);
if (secretKeys == null) {
return (T) publicKeys;
} else {
secretKeys = PGPSecretKeyRing.replacePublicKeys(secretKeys, publicKeys);
return (T) secretKeys;
}
}
public static <T extends PGPKeyRing> T keysPlusPublicKey(T keyRing, PGPPublicKey publicKey) {
PGPSecretKeyRing secretKeys = null;
PGPPublicKeyRing publicKeys;
if (keyRing instanceof PGPSecretKeyRing) {
secretKeys = (PGPSecretKeyRing) keyRing;
publicKeys = PGPainless.extractCertificate(secretKeys);
} else {
publicKeys = (PGPPublicKeyRing) keyRing;
}
publicKeys = PGPPublicKeyRing.insertPublicKey(publicKeys, publicKey);
if (secretKeys == null) {
return (T) publicKeys;
} else {
secretKeys = PGPSecretKeyRing.replacePublicKeys(secretKeys, publicKeys);
return (T) secretKeys;
}
}
public static PGPSecretKeyRing keysPlusSecretKey(PGPSecretKeyRing secretKeys, PGPSecretKey secretKey) {
return PGPSecretKeyRing.insertSecretKey(secretKeys, secretKey);
}
public static PGPSecretKey secretKeyPlusSignature(PGPSecretKey secretKey, PGPSignature signature) {
@ -227,4 +327,5 @@ public final class KeyRingUtils {
PGPSecretKey newSecretKey = PGPSecretKey.replacePublicKey(secretKey, publicKey);
return newSecretKey;
}
}

View File

@ -4,18 +4,14 @@
package org.pgpainless.signature.builder;
import java.io.IOException;
import java.util.List;
import javax.annotation.Nullable;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSignature;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.exception.WrongPassphraseException;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.signature.subpackets.BaseSignatureSubpackets;
import org.pgpainless.signature.subpackets.CertificationSubpackets;
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets;
public final class SignatureFactory {
@ -24,66 +20,14 @@ public final class SignatureFactory {
}
public static SubkeyBindingSignatureBuilder bindNonSigningSubkey(
public static SelfSignatureBuilder selfCertifyUserId(
PGPSecretKey primaryKey,
SecretKeyRingProtector primaryKeyProtector,
@Nullable SelfSignatureSubpackets.Callback subkeyBindingSubpacketsCallback,
KeyFlag... flags) throws WrongPassphraseException {
if (hasSignDataFlag(flags)) {
throw new IllegalArgumentException("Binding a subkey with SIGN_DATA flag requires primary key backsig.\n" +
"Please use the method bindSigningSubkey() instead.");
}
return bindSubkey(primaryKey, primaryKeyProtector, subkeyBindingSubpacketsCallback, flags);
}
public static SubkeyBindingSignatureBuilder bindSigningSubkey(
PGPSecretKey primaryKey,
SecretKeyRingProtector primaryKeyProtector,
PGPSecretKey subkey,
SecretKeyRingProtector subkeyProtector,
@Nullable SelfSignatureSubpackets.Callback subkeyBindingSubpacketsCallback,
@Nullable SelfSignatureSubpackets.Callback primaryKeyBindingSubpacketsCallback,
KeyFlag... flags)
throws PGPException, IOException {
SubkeyBindingSignatureBuilder subkeyBinder = bindSubkey(primaryKey, primaryKeyProtector, subkeyBindingSubpacketsCallback, flags);
if (hasSignDataFlag(flags)) {
PGPSignature backsig = bindPrimaryKey(
subkey, subkeyProtector, primaryKeyBindingSubpacketsCallback)
.build(primaryKey.getPublicKey());
subkeyBinder.getHashedSubpackets().addEmbeddedSignature(backsig);
}
return subkeyBinder;
}
private static SubkeyBindingSignatureBuilder bindSubkey(PGPSecretKey primaryKey,
SecretKeyRingProtector primaryKeyProtector,
@Nullable SelfSignatureSubpackets.Callback subkeyBindingSubpacketsCallback,
KeyFlag... flags) throws WrongPassphraseException {
if (flags.length == 0) {
throw new IllegalArgumentException("Keyflags for subkey binding cannot be empty.");
}
SubkeyBindingSignatureBuilder subkeyBinder = new SubkeyBindingSignatureBuilder(primaryKey, primaryKeyProtector);
SelfSignatureSubpackets hashedSubpackets = subkeyBinder.getHashedSubpackets();
hashedSubpackets.setKeyFlags(flags);
subkeyBinder.applyCallback(subkeyBindingSubpacketsCallback);
return subkeyBinder;
}
public static PrimaryKeyBindingSignatureBuilder bindPrimaryKey(
PGPSecretKey subkey,
SecretKeyRingProtector subkeyProtector,
@Nullable SelfSignatureSubpackets.Callback primaryKeyBindingSubpacketsCallback) throws WrongPassphraseException {
PrimaryKeyBindingSignatureBuilder primaryKeyBinder = new PrimaryKeyBindingSignatureBuilder(subkey, subkeyProtector);
primaryKeyBinder.applyCallback(primaryKeyBindingSubpacketsCallback);
return primaryKeyBinder;
@Nullable SelfSignatureSubpackets.Callback selfSignatureCallback,
List<KeyFlag> keyFlags)
throws WrongPassphraseException {
KeyFlag[] keyFlagArray = keyFlags.toArray(new KeyFlag[0]);
return selfCertifyUserId(primaryKey, primaryKeyProtector, selfSignatureCallback, keyFlagArray);
}
public static SelfSignatureBuilder selfCertifyUserId(
@ -100,55 +44,4 @@ public final class SignatureFactory {
return certifier;
}
public static SelfSignatureBuilder renewSelfCertification(
PGPSecretKey primaryKey,
SecretKeyRingProtector primaryKeyProtector,
@Nullable SelfSignatureSubpackets.Callback selfSignatureCallback,
PGPSignature oldCertification) throws WrongPassphraseException {
SelfSignatureBuilder certifier = new SelfSignatureBuilder(
primaryKey, primaryKeyProtector, oldCertification);
certifier.applyCallback(selfSignatureCallback);
return certifier;
}
public static CertificationSignatureBuilder certifyUserId(
PGPSecretKey signingKey,
SecretKeyRingProtector signingKeyProtector,
@Nullable CertificationSubpackets.Callback subpacketsCallback)
throws WrongPassphraseException {
CertificationSignatureBuilder certifier = new CertificationSignatureBuilder(signingKey, signingKeyProtector);
certifier.applyCallback(subpacketsCallback);
return certifier;
}
public static UniversalSignatureBuilder universalSignature(
SignatureType signatureType,
PGPSecretKey signingKey,
SecretKeyRingProtector signingKeyProtector,
@Nullable BaseSignatureSubpackets.Callback callback)
throws WrongPassphraseException {
UniversalSignatureBuilder builder =
new UniversalSignatureBuilder(signatureType, signingKey, signingKeyProtector);
builder.applyCallback(callback);
return builder;
}
private static boolean hasSignDataFlag(KeyFlag... flags) {
if (flags == null) {
return false;
}
for (KeyFlag flag : flags) {
if (flag == KeyFlag.SIGN_DATA) {
return true;
}
}
return false;
}
}

View File

@ -170,7 +170,7 @@ public class ModifyKeys {
* @throws NoSuchAlgorithmException
*/
@Test
public void addSubkey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
public void addSubkey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
// Protector for unlocking the existing secret key
SecretKeyRingProtector protector = SecretKeyRingProtector.unlockAllKeysWith(Passphrase.fromPassword(originalPassphrase), secretKey);
Passphrase subkeyPassphrase = Passphrase.fromPassword("subk3yP4ssphr4s3");

View File

@ -16,28 +16,28 @@ import java.util.List;
import org.bouncycastle.bcpg.sig.NotationData;
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.junit.JUtils;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.EncryptionPurpose;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.key.generation.KeyRingBuilder;
import org.pgpainless.key.generation.KeySpec;
import org.pgpainless.key.generation.type.KeyType;
import org.pgpainless.key.generation.type.xdh.XDHSpec;
import org.pgpainless.key.generation.type.eddsa.EdDSACurve;
import org.pgpainless.key.info.KeyRingInfo;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
import org.pgpainless.util.Passphrase;
public class AddSubkeyWithModifiedBindingSignatureSubpackets {
public static long MILLIS_IN_SEC = 1000;
@Test
public void bindEncryptionSubkeyAndModifyBindingSignatureHashedSubpackets() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
@ -45,29 +45,30 @@ public class AddSubkeyWithModifiedBindingSignatureSubpackets {
.modernKeyRing("Alice <alice@pgpainless.org>", null);
KeyRingInfo before = PGPainless.inspectKeyRing(secretKeys);
PGPSecretKey secretSubkey = KeyRingBuilder.generatePGPSecretKey(
KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS).build(),
Passphrase.emptyPassphrase(), false);
PGPKeyPair secretSubkey = KeyRingBuilder.generateKeyPair(
KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA).build());
long secondsUntilExpiration = 1000;
secretKeys = PGPainless.modifyKeyRing(secretKeys)
.addSubKey(secretSubkey, new SelfSignatureSubpackets.Callback() {
@Override
public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) {
hashedSubpackets.setKeyExpirationTime(true, 1000);
hashedSubpackets.addNotationData(false, "test@test.test", "test");
}
}, null, SecretKeyRingProtector.unprotectedKeys(), protector, KeyFlag.ENCRYPT_COMMS)
@Override
public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) {
hashedSubpackets.setKeyExpirationTime(true, secondsUntilExpiration);
hashedSubpackets.addNotationData(false, "test@test.test", "test");
}
}, SecretKeyRingProtector.unprotectedKeys(), protector, KeyFlag.SIGN_DATA)
.done();
KeyRingInfo after = PGPainless.inspectKeyRing(secretKeys);
List<PGPPublicKey> signingKeys = after.getSigningSubkeys();
signingKeys.removeAll(before.getSigningSubkeys());
assertFalse(signingKeys.isEmpty());
List<PGPPublicKey> encryptionKeys = after.getEncryptionSubkeys(EncryptionPurpose.COMMUNICATIONS);
encryptionKeys.removeAll(before.getEncryptionSubkeys(EncryptionPurpose.COMMUNICATIONS));
assertFalse(encryptionKeys.isEmpty());
assertEquals(1, encryptionKeys.size());
PGPPublicKey newKey = encryptionKeys.get(0);
JUtils.assertEquals(new Date().getTime() + 1000 * 1000, after.getSubkeyExpirationDate(new OpenPgpV4Fingerprint(newKey)).getTime(), 2000);
PGPPublicKey newKey = signingKeys.get(0);
Date now = new Date();
JUtils.assertEquals(
now.getTime() + MILLIS_IN_SEC * secondsUntilExpiration,
after.getSubkeyExpirationDate(new OpenPgpV4Fingerprint(newKey)).getTime(), 2 * MILLIS_IN_SEC);
assertTrue(newKey.getSignatures().hasNext());
PGPSignature binding = newKey.getSignatures().next();
List<NotationData> notations = SignatureSubpacketsUtil.getHashedNotationData(binding);

View File

@ -39,7 +39,6 @@ import org.pgpainless.key.util.RevocationAttributes;
import org.pgpainless.signature.SignatureUtils;
import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
import org.pgpainless.util.ArmorUtils;
import org.pgpainless.util.Passphrase;
public class RevokeSubKeyTest {
@ -82,11 +81,6 @@ public class RevokeSubKeyTest {
.withReason(RevocationAttributes.Reason.KEY_RETIRED)
.withDescription("Key no longer used."));
// CHECKSTYLE:OFF
System.out.println("Revocation Certificate:");
System.out.println(ArmorUtils.toAsciiArmoredString(revocationCertificate.getEncoded()));
// CHECKSTYLE:ON
PGPPublicKey publicKey = secretKeys.getPublicKey();
assertFalse(publicKey.hasRevocation());
@ -107,8 +101,8 @@ public class RevokeSubKeyTest {
.forKey(secretKeys, Passphrase.fromPassword("password123"));
SecretKeyRingEditorInterface editor = PGPainless.modifyKeyRing(secretKeys);
PGPSignature keyRevocation = editor.createRevocationCertificate(primaryKey.getKeyID(), protector, null);
PGPSignature subkeyRevocation = editor.createRevocationCertificate(subKey.getKeyID(), protector, null);
PGPSignature keyRevocation = editor.createRevocationCertificate(primaryKey.getKeyID(), protector, (RevocationAttributes) null);
PGPSignature subkeyRevocation = editor.createRevocationCertificate(subKey.getKeyID(), protector, (RevocationAttributes) null);
assertEquals(SignatureType.KEY_REVOCATION.getCode(), keyRevocation.getSignatureType());
assertEquals(SignatureType.SUBKEY_REVOCATION.getCode(), subkeyRevocation.getSignatureType());

View File

@ -1,75 +0,0 @@
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.signature.builder;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.List;
import org.bouncycastle.bcpg.sig.NotationData;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.EncryptionPurpose;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.key.info.KeyRingInfo;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
import org.pgpainless.util.Passphrase;
public class SubkeyBindingSignatureBuilderTest {
@Test
public void testBindSubkeyWithCustomNotation() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
.modernKeyRing("Alice <alice@pgpainless.org>", "passphrase");
KeyRingInfo info = PGPainless.inspectKeyRing(secretKey);
List<PGPPublicKey> previousSubkeys = info.getEncryptionSubkeys(EncryptionPurpose.ANY);
SecretKeyRingProtector protector = SecretKeyRingProtector.unlockAllKeysWith(Passphrase.fromPassword("passphrase"), secretKey);
PGPSecretKeyRing tempSubkeyRing = PGPainless.generateKeyRing()
.modernKeyRing("Subkeys", null);
PGPPublicKey subkeyPub = PGPainless.inspectKeyRing(tempSubkeyRing)
.getEncryptionSubkeys(EncryptionPurpose.ANY).get(0);
PGPSecretKey subkeySec = tempSubkeyRing.getSecretKey(subkeyPub.getKeyID());
PGPSignature binding = SignatureFactory.bindNonSigningSubkey(
secretKey.getSecretKey(), protector,
new SelfSignatureSubpackets.Callback() {
@Override
public void modifyHashedSubpackets(SelfSignatureSubpackets subpackets) {
subpackets.addNotationData(false, "testnotation@pgpainless.org", "hello-world");
}
}, KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)
.build(subkeyPub);
subkeyPub = PGPPublicKey.addCertification(subkeyPub, binding);
subkeySec = PGPSecretKey.replacePublicKey(subkeySec, subkeyPub);
secretKey = PGPSecretKeyRing.insertSecretKey(secretKey, subkeySec);
info = PGPainless.inspectKeyRing(secretKey);
List<PGPPublicKey> nextSubkeys = info.getEncryptionSubkeys(EncryptionPurpose.ANY);
assertEquals(previousSubkeys.size() + 1, nextSubkeys.size());
subkeyPub = secretKey.getPublicKey(subkeyPub.getKeyID());
Iterator<PGPSignature> newBindingSigs = subkeyPub.getSignaturesForKeyID(secretKey.getPublicKey().getKeyID());
PGPSignature bindingSig = newBindingSigs.next();
assertNotNull(bindingSig);
List<NotationData> notations = SignatureSubpacketsUtil.getHashedNotationData(bindingSig);
assertEquals(1, notations.size());
assertEquals("testnotation@pgpainless.org", notations.get(0).getNotationName());
assertEquals("hello-world", notations.get(0).getNotationValue());
}
}