mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-23 11:27:57 +01:00
Progress
This commit is contained in:
parent
04ada88188
commit
3f09fa0cc7
9 changed files with 163 additions and 103 deletions
|
@ -59,6 +59,10 @@ public abstract class ImplementationFactory {
|
|||
|
||||
public abstract PBESecretKeyDecryptor getPBESecretKeyDecryptor(Passphrase passphrase) throws PGPException;
|
||||
|
||||
public PGPDigestCalculator getV4FingerprintCalculator() throws PGPException {
|
||||
return getPGPDigestCalculator(HashAlgorithm.SHA1);
|
||||
}
|
||||
|
||||
public PGPDigestCalculator getPGPDigestCalculator(HashAlgorithm algorithm) throws PGPException {
|
||||
return getPGPDigestCalculator(algorithm.getAlgorithmId());
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ 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.SignatureSubpacketGeneratorUtil;
|
||||
|
@ -52,13 +53,13 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
|||
private final Charset UTF8 = Charset.forName("UTF-8");
|
||||
|
||||
private PGPSignatureGenerator signatureGenerator;
|
||||
private PGPDigestCalculator digestCalculator;
|
||||
private PGPDigestCalculator keyFingerprintCalculator;
|
||||
private PBESecretKeyEncryptor secretKeyEncryptor;
|
||||
|
||||
private KeySpec primaryKeySpec;
|
||||
private final List<KeySpec> subkeySpecs = new ArrayList<>();
|
||||
private final Set<String> userIds = new LinkedHashSet<>();
|
||||
private Passphrase passphrase = null;
|
||||
private Passphrase passphrase = Passphrase.emptyPassphrase();
|
||||
private Date expirationDate = null;
|
||||
|
||||
@Override
|
||||
|
@ -126,13 +127,11 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
|||
if (userIds.isEmpty()) {
|
||||
throw new IllegalStateException("At least one user-id is required.");
|
||||
}
|
||||
digestCalculator = buildDigestCalculator();
|
||||
keyFingerprintCalculator = ImplementationFactory.getInstance().getV4FingerprintCalculator();
|
||||
secretKeyEncryptor = buildSecretKeyEncryptor();
|
||||
PBESecretKeyDecryptor secretKeyDecryptor = buildSecretKeyDecryptor();
|
||||
|
||||
if (passphrase != null) {
|
||||
passphrase.clear();
|
||||
}
|
||||
passphrase.clear();
|
||||
|
||||
// Generate Primary Key
|
||||
PGPKeyPair certKey = generateKeyPair(primaryKeySpec);
|
||||
|
@ -171,8 +170,7 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
|||
|
||||
// "reassemble" secret key ring with modified primary key
|
||||
PGPSecretKey primarySecKey = new PGPSecretKey(
|
||||
privateKey,
|
||||
primaryPubKey, digestCalculator, true, secretKeyEncryptor);
|
||||
privateKey, primaryPubKey, keyFingerprintCalculator, true, secretKeyEncryptor);
|
||||
List<PGPSecretKey> secretKeyList = new ArrayList<>();
|
||||
secretKeyList.add(primarySecKey);
|
||||
while (secretKeys.hasNext()) {
|
||||
|
@ -190,7 +188,7 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
|||
String primaryUserId = userIds.iterator().next();
|
||||
return new PGPKeyRingGenerator(
|
||||
SignatureType.POSITIVE_CERTIFICATION.getCode(), certKey,
|
||||
primaryUserId, digestCalculator,
|
||||
primaryUserId, keyFingerprintCalculator,
|
||||
hashedSubPackets, null, signer, secretKeyEncryptor);
|
||||
}
|
||||
|
||||
|
@ -236,22 +234,20 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
|||
private PBESecretKeyEncryptor buildSecretKeyEncryptor() {
|
||||
SymmetricKeyAlgorithm keyEncryptionAlgorithm = PGPainless.getPolicy().getSymmetricKeyEncryptionAlgorithmPolicy()
|
||||
.getDefaultSymmetricKeyAlgorithm();
|
||||
PBESecretKeyEncryptor encryptor = passphrase == null || passphrase.isEmpty() ?
|
||||
null : // unencrypted key pair, otherwise AES-256 encrypted
|
||||
if (!passphrase.isValid()) {
|
||||
throw new IllegalStateException("Passphrase was cleared.");
|
||||
}
|
||||
return passphrase.isEmpty() ? null : // unencrypted key pair, otherwise AES-256 encrypted
|
||||
ImplementationFactory.getInstance().getPBESecretKeyEncryptor(
|
||||
keyEncryptionAlgorithm, digestCalculator, passphrase);
|
||||
return encryptor;
|
||||
keyEncryptionAlgorithm, keyFingerprintCalculator, passphrase);
|
||||
}
|
||||
|
||||
private PBESecretKeyDecryptor buildSecretKeyDecryptor() throws PGPException {
|
||||
PBESecretKeyDecryptor decryptor = passphrase == null || passphrase.isEmpty() ?
|
||||
null :
|
||||
if (!passphrase.isValid()) {
|
||||
throw new IllegalStateException("Passphrase was cleared.");
|
||||
}
|
||||
return passphrase.isEmpty() ? null :
|
||||
ImplementationFactory.getInstance().getPBESecretKeyDecryptor(passphrase);
|
||||
return decryptor;
|
||||
}
|
||||
|
||||
private PGPDigestCalculator buildDigestCalculator() throws PGPException {
|
||||
return ImplementationFactory.getInstance().getPGPDigestCalculator(HashAlgorithm.SHA1);
|
||||
}
|
||||
|
||||
public static PGPKeyPair generateKeyPair(KeySpec spec)
|
||||
|
@ -269,4 +265,30 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
|||
PGPKeyPair pgpKeyPair = ImplementationFactory.getInstance().getPGPKeyPair(type.getAlgorithm(), keyPair, new Date());
|
||||
return pgpKeyPair;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
synchronized (passphrase.lock) {
|
||||
if (!passphrase.isValid()) {
|
||||
throw new IllegalStateException("Passphrase has been cleared.");
|
||||
}
|
||||
if (!passphrase.isEmpty()) {
|
||||
protector = SecretKeyRingProtector.unlockSingleKeyWith(passphrase, keyPair.getKeyID());
|
||||
} else {
|
||||
protector = SecretKeyRingProtector.unprotectedKeys();
|
||||
}
|
||||
|
||||
return new PGPSecretKey(
|
||||
keyPair.getPrivateKey(),
|
||||
keyPair.getPublicKey(),
|
||||
keyFingerprintCalculator,
|
||||
isPrimary,
|
||||
protector.getEncryptor(keyPair.getKeyID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ 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.OpenPgpFingerprint;
|
||||
import org.pgpainless.key.generation.KeyRingBuilder;
|
||||
|
@ -182,28 +181,27 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
|
|||
KeyFlag keyFlag,
|
||||
KeyFlag... additionalKeyFlags) throws PGPException, IOException {
|
||||
KeyFlag[] flags = concat(keyFlag, additionalKeyFlags);
|
||||
SignatureSubpacketsUtil.assureKeyCanCarryFlags(PublicKeyAlgorithm.fromId(subkey.getPublicKey().getAlgorithm()));
|
||||
|
||||
boolean isSigningKey = CollectionUtils.contains(flags, KeyFlag.SIGN_DATA) ||
|
||||
CollectionUtils.contains(flags, KeyFlag.CERTIFY_OTHER);
|
||||
if (!isSigningKey) {
|
||||
return addSubKey(subkey.getPublicKey(),
|
||||
bindingSignatureCallback,
|
||||
primaryKeyProtector,
|
||||
keyFlag,
|
||||
additionalKeyFlags);
|
||||
}
|
||||
PublicKeyAlgorithm subkeyAlgorithm = PublicKeyAlgorithm.fromId(subkey.getPublicKey().getAlgorithm());
|
||||
SignatureSubpacketsUtil.assureKeyCanCarryFlags(subkeyAlgorithm);
|
||||
|
||||
PGPSecretKey primaryKey = secretKeyRing.getSecretKey();
|
||||
SubkeyBindingSignatureBuilder bindingSigBuilder = new SubkeyBindingSignatureBuilder(primaryKey, primaryKeyProtector);
|
||||
SubkeyBindingSignatureBuilder bindingSigBuilder =
|
||||
new SubkeyBindingSignatureBuilder(primaryKey, primaryKeyProtector);
|
||||
|
||||
bindingSigBuilder.applyCallback(bindingSignatureCallback);
|
||||
bindingSigBuilder.getHashedSubpackets().setKeyFlags(flags);
|
||||
|
||||
PrimaryKeyBindingSignatureBuilder backSigBuilder = new PrimaryKeyBindingSignatureBuilder(subkey, subkeyProtector);
|
||||
backSigBuilder.applyCallback(backSignatureCallback);
|
||||
PGPSignature backSig = backSigBuilder.build(primaryKey.getPublicKey());
|
||||
boolean isSigningKey = CollectionUtils.contains(flags, KeyFlag.SIGN_DATA) ||
|
||||
CollectionUtils.contains(flags, KeyFlag.CERTIFY_OTHER);
|
||||
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);
|
||||
}
|
||||
|
||||
bindingSigBuilder.getHashedSubpackets().addEmbeddedSignature(backSig);
|
||||
PGPSignature bindingSig = bindingSigBuilder.build(subkey.getPublicKey());
|
||||
subkey = KeyRingUtils.secretKeyPlusSignature(subkey, bindingSig);
|
||||
secretKeyRing = KeyRingUtils.secretKeysPlusSecretKey(secretKeyRing, subkey);
|
||||
|
@ -211,54 +209,10 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKeyRingEditorInterface addSubKey(PGPPublicKey subkey,
|
||||
SelfSignatureSubpackets.Callback bindingSignatureCallback,
|
||||
SecretKeyRingProtector primaryKeyProtector,
|
||||
KeyFlag keyFlag,
|
||||
KeyFlag... additionalKeyFlags) throws PGPException {
|
||||
KeyFlag[] flags = concat(keyFlag, additionalKeyFlags);
|
||||
boolean isSigningKey = CollectionUtils.contains(flags, KeyFlag.SIGN_DATA) ||
|
||||
CollectionUtils.contains(flags, KeyFlag.CERTIFY_OTHER);
|
||||
if (isSigningKey) {
|
||||
throw new IllegalArgumentException("Cannot bind a signing capable subkey without access to the secret subkey.\n" +
|
||||
"Please use addSubKey(PGPSecretKey secretSubKey, [...]) instead.");
|
||||
}
|
||||
|
||||
PGPSignature bindingSignature = createSubkeyBindingSignature(subkey, bindingSignatureCallback, primaryKeyProtector, flags);
|
||||
subkey = PGPPublicKey.addCertification(subkey, bindingSignature);
|
||||
|
||||
secretKeyRing = KeyRingUtils.secretKeysPlusPublicKey(secretKeyRing, subkey);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private PGPSignature createSubkeyBindingSignature(PGPPublicKey subkey,
|
||||
SelfSignatureSubpackets.Callback bindingSignatureCallback,
|
||||
SecretKeyRingProtector primaryKeyProtector,
|
||||
KeyFlag... keyFlags) throws PGPException {
|
||||
PGPSecretKey primaryKey = secretKeyRing.getSecretKey();
|
||||
SubkeyBindingSignatureBuilder builder = new SubkeyBindingSignatureBuilder(primaryKey, primaryKeyProtector);
|
||||
builder.applyCallback(bindingSignatureCallback);
|
||||
builder.getHashedSubpackets().setKeyFlags(keyFlags);
|
||||
|
||||
PGPSignature signature = builder.build(subkey);
|
||||
return signature;
|
||||
}
|
||||
|
||||
private PGPSecretKey generateSubKey(@Nonnull KeySpec keySpec,
|
||||
@Nonnull Passphrase subKeyPassphrase)
|
||||
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
||||
PGPDigestCalculator checksumCalculator = ImplementationFactory.getInstance()
|
||||
.getPGPDigestCalculator(defaultDigestHashAlgorithm);
|
||||
|
||||
PBESecretKeyEncryptor subKeyEncryptor = subKeyPassphrase.isEmpty() ? null :
|
||||
ImplementationFactory.getInstance().getPBESecretKeyEncryptor(SymmetricKeyAlgorithm.AES_256, subKeyPassphrase);
|
||||
|
||||
PGPKeyPair keyPair = KeyRingBuilder.generateKeyPair(keySpec);
|
||||
PGPSecretKey secretKey = new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
|
||||
checksumCalculator, false, subKeyEncryptor);
|
||||
return secretKey;
|
||||
return KeyRingBuilder.generatePGPSecretKey(keySpec, subKeyPassphrase, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -12,7 +12,6 @@ import javax.annotation.Nonnull;
|
|||
import javax.annotation.Nullable;
|
||||
|
||||
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;
|
||||
|
@ -59,7 +58,7 @@ public interface SecretKeyRingEditorInterface {
|
|||
* @return the builder
|
||||
*/
|
||||
SecretKeyRingEditorInterface addSubKey(@Nonnull KeySpec keySpec,
|
||||
@Nonnull Passphrase subKeyPassphrase,
|
||||
@Nullable Passphrase subKeyPassphrase,
|
||||
SecretKeyRingProtector secretKeyRingProtector)
|
||||
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException;
|
||||
|
||||
|
@ -78,12 +77,6 @@ public interface SecretKeyRingEditorInterface {
|
|||
KeyFlag keyFlag,
|
||||
KeyFlag... additionalKeyFlags) throws PGPException, IOException;
|
||||
|
||||
SecretKeyRingEditorInterface addSubKey(PGPPublicKey subkey,
|
||||
SelfSignatureSubpackets.Callback bindingSignatureCallback,
|
||||
SecretKeyRingProtector primaryKeyProtector,
|
||||
KeyFlag keyFlag,
|
||||
KeyFlag... additionalKeyFlags) throws PGPException;
|
||||
|
||||
/**
|
||||
* Revoke the key ring.
|
||||
* The revocation will be a hard revocation, rendering the whole key invalid for any past or future signatures.
|
||||
|
|
|
@ -56,7 +56,7 @@ public class CachingSecretKeyRingProtector implements SecretKeyRingProtector, Se
|
|||
* @param keyId id of the key
|
||||
* @param passphrase passphrase
|
||||
*/
|
||||
public void addPassphrase(@Nonnull Long keyId, @Nullable Passphrase passphrase) {
|
||||
public void addPassphrase(@Nonnull Long keyId, @Nonnull Passphrase passphrase) {
|
||||
this.cache.put(keyId, passphrase);
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ public class CachingSecretKeyRingProtector implements SecretKeyRingProtector, Se
|
|||
* @param keyRing key ring
|
||||
* @param passphrase passphrase
|
||||
*/
|
||||
public void addPassphrase(@Nonnull PGPKeyRing keyRing, @Nullable Passphrase passphrase) {
|
||||
public void addPassphrase(@Nonnull PGPKeyRing keyRing, @Nonnull Passphrase passphrase) {
|
||||
Iterator<PGPPublicKey> keys = keyRing.getPublicKeys();
|
||||
while (keys.hasNext()) {
|
||||
PGPPublicKey publicKey = keys.next();
|
||||
|
@ -80,11 +80,11 @@ public class CachingSecretKeyRingProtector implements SecretKeyRingProtector, Se
|
|||
* @param key key
|
||||
* @param passphrase passphrase
|
||||
*/
|
||||
public void addPassphrase(@Nonnull PGPPublicKey key, @Nullable Passphrase passphrase) {
|
||||
public void addPassphrase(@Nonnull PGPPublicKey key, @Nonnull Passphrase passphrase) {
|
||||
addPassphrase(key.getKeyID(), passphrase);
|
||||
}
|
||||
|
||||
public void addPassphrase(@Nonnull OpenPgpFingerprint fingerprint, @Nullable Passphrase passphrase) {
|
||||
public void addPassphrase(@Nonnull OpenPgpFingerprint fingerprint, @Nonnull Passphrase passphrase) {
|
||||
addPassphrase(fingerprint.getKeyId(), passphrase);
|
||||
}
|
||||
|
||||
|
@ -95,9 +95,10 @@ public class CachingSecretKeyRingProtector implements SecretKeyRingProtector, Se
|
|||
* @param keyId id of the key
|
||||
*/
|
||||
public void forgetPassphrase(@Nonnull Long keyId) {
|
||||
Passphrase passphrase = cache.get(keyId);
|
||||
passphrase.clear();
|
||||
cache.remove(keyId);
|
||||
Passphrase passphrase = cache.remove(keyId);
|
||||
if (passphrase != null) {
|
||||
passphrase.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -140,12 +141,13 @@ public class CachingSecretKeyRingProtector implements SecretKeyRingProtector, Se
|
|||
|
||||
@Override
|
||||
public boolean hasPassphrase(Long keyId) {
|
||||
return cache.containsKey(keyId);
|
||||
Passphrase passphrase = cache.get(keyId);
|
||||
return passphrase != null && passphrase.isValid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPassphraseFor(Long keyId) {
|
||||
return cache.containsKey(keyId);
|
||||
return hasPassphrase(keyId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -64,12 +64,16 @@ public class PasswordBasedSecretKeyRingProtector implements SecretKeyRingProtect
|
|||
}
|
||||
|
||||
public static PasswordBasedSecretKeyRingProtector forKey(PGPSecretKey key, Passphrase passphrase) {
|
||||
return forKeyId(key.getPublicKey().getKeyID(), passphrase);
|
||||
}
|
||||
|
||||
public static PasswordBasedSecretKeyRingProtector forKeyId(long singleKeyId, Passphrase passphrase) {
|
||||
KeyRingProtectionSettings protectionSettings = KeyRingProtectionSettings.secureDefaultSettings();
|
||||
SecretKeyPassphraseProvider passphraseProvider = new SecretKeyPassphraseProvider() {
|
||||
@Override
|
||||
@Nullable
|
||||
@Override
|
||||
public Passphrase getPassphraseFor(Long keyId) {
|
||||
if (key.getKeyID() == keyId) {
|
||||
if (keyId == singleKeyId) {
|
||||
return passphrase;
|
||||
}
|
||||
return null;
|
||||
|
@ -77,7 +81,7 @@ public class PasswordBasedSecretKeyRingProtector implements SecretKeyRingProtect
|
|||
|
||||
@Override
|
||||
public boolean hasPassphrase(Long keyId) {
|
||||
return keyId == key.getKeyID();
|
||||
return keyId == singleKeyId;
|
||||
}
|
||||
};
|
||||
return new PasswordBasedSecretKeyRingProtector(protectionSettings, passphraseProvider);
|
||||
|
|
|
@ -98,6 +98,10 @@ public interface SecretKeyRingProtector {
|
|||
return PasswordBasedSecretKeyRingProtector.forKey(key, passphrase);
|
||||
}
|
||||
|
||||
static SecretKeyRingProtector unlockSingleKeyWith(Passphrase passphrase, long keyId) {
|
||||
return PasswordBasedSecretKeyRingProtector.forKeyId(keyId, passphrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Protector for unprotected keys.
|
||||
* This protector returns null for all {@link #getEncryptor(Long)}/{@link #getDecryptor(Long)} calls,
|
||||
|
|
|
@ -10,7 +10,7 @@ import java.util.Arrays;
|
|||
|
||||
public class Passphrase {
|
||||
|
||||
private final Object lock = new Object();
|
||||
public final Object lock = new Object();
|
||||
|
||||
private final char[] chars;
|
||||
private boolean valid = true;
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.key.modification;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Date;
|
||||
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.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.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 {
|
||||
|
||||
@Test
|
||||
public void bindEncryptionSubkeyAndModifyBindingSignatureHashedSubpackets() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
||||
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.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);
|
||||
|
||||
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)
|
||||
.done();
|
||||
|
||||
KeyRingInfo after = PGPainless.inspectKeyRing(secretKeys);
|
||||
|
||||
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);
|
||||
assertTrue(newKey.getSignatures().hasNext());
|
||||
PGPSignature binding = newKey.getSignatures().next();
|
||||
List<NotationData> notations = SignatureSubpacketsUtil.getHashedNotationData(binding);
|
||||
assertEquals(1, notations.size());
|
||||
assertEquals("test@test.test", notations.get(0).getNotationName());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue