mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-27 06:42:05 +01:00
Wip: Add test for signature structure, set fingerprint on primary user-id self sig
This commit is contained in:
parent
76e19359b4
commit
6a137698c4
2 changed files with 77 additions and 16 deletions
|
@ -14,10 +14,11 @@ import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Map;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import org.bouncycastle.bcpg.sig.KeyFlags;
|
import org.bouncycastle.bcpg.sig.KeyFlags;
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
@ -44,6 +45,7 @@ import org.pgpainless.implementation.ImplementationFactory;
|
||||||
import org.pgpainless.key.generation.type.KeyType;
|
import org.pgpainless.key.generation.type.KeyType;
|
||||||
import org.pgpainless.key.protection.UnlockSecretKey;
|
import org.pgpainless.key.protection.UnlockSecretKey;
|
||||||
import org.pgpainless.provider.ProviderFactory;
|
import org.pgpainless.provider.ProviderFactory;
|
||||||
|
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets;
|
||||||
import org.pgpainless.signature.subpackets.SignatureSubpackets;
|
import org.pgpainless.signature.subpackets.SignatureSubpackets;
|
||||||
import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper;
|
import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper;
|
||||||
import org.pgpainless.util.Passphrase;
|
import org.pgpainless.util.Passphrase;
|
||||||
|
@ -54,7 +56,7 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
||||||
|
|
||||||
private KeySpec primaryKeySpec;
|
private KeySpec primaryKeySpec;
|
||||||
private final List<KeySpec> subkeySpecs = new ArrayList<>();
|
private final List<KeySpec> subkeySpecs = new ArrayList<>();
|
||||||
private final Set<String> userIds = new LinkedHashSet<>();
|
private final Map<String, SelfSignatureSubpackets.Callback> userIds = new LinkedHashMap<>();
|
||||||
private Passphrase passphrase = Passphrase.emptyPassphrase();
|
private Passphrase passphrase = Passphrase.emptyPassphrase();
|
||||||
private Date expirationDate = null;
|
private Date expirationDate = null;
|
||||||
|
|
||||||
|
@ -73,7 +75,14 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeyRingBuilder addUserId(@Nonnull String userId) {
|
public KeyRingBuilder addUserId(@Nonnull String userId) {
|
||||||
this.userIds.add(userId.trim());
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,6 +144,7 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
||||||
|
|
||||||
// Prepare primary user-id sig
|
// Prepare primary user-id sig
|
||||||
SignatureSubpackets hashedSubPacketGenerator = primaryKeySpec.getSubpacketGenerator();
|
SignatureSubpackets hashedSubPacketGenerator = primaryKeySpec.getSubpacketGenerator();
|
||||||
|
hashedSubPacketGenerator.setIssuerFingerprintAndKeyId(certKey.getPublicKey());
|
||||||
hashedSubPacketGenerator.setPrimaryUserId();
|
hashedSubPacketGenerator.setPrimaryUserId();
|
||||||
if (expirationDate != null) {
|
if (expirationDate != null) {
|
||||||
hashedSubPacketGenerator.setKeyExpirationTime(certKey.getPublicKey(), expirationDate);
|
hashedSubPacketGenerator.setKeyExpirationTime(certKey.getPublicKey(), expirationDate);
|
||||||
|
@ -143,7 +153,8 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
||||||
SignatureSubpacketsHelper.applyTo(hashedSubPacketGenerator, generator);
|
SignatureSubpacketsHelper.applyTo(hashedSubPacketGenerator, generator);
|
||||||
PGPSignatureSubpacketVector hashedSubPackets = generator.generate();
|
PGPSignatureSubpacketVector hashedSubPackets = generator.generate();
|
||||||
|
|
||||||
PGPKeyRingGenerator ringGenerator = buildRingGenerator(certKey, signer, keyFingerprintCalculator, hashedSubPackets, secretKeyEncryptor);
|
PGPKeyRingGenerator ringGenerator = buildRingGenerator(
|
||||||
|
certKey, signer, keyFingerprintCalculator, hashedSubPackets, secretKeyEncryptor);
|
||||||
addSubKeys(certKey, ringGenerator);
|
addSubKeys(certKey, ringGenerator);
|
||||||
|
|
||||||
// Generate secret key ring with only primary user id
|
// Generate secret key ring with only primary user id
|
||||||
|
@ -154,15 +165,27 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
||||||
// Attempt to add additional user-ids to the primary public key
|
// Attempt to add additional user-ids to the primary public key
|
||||||
PGPPublicKey primaryPubKey = secretKeys.next().getPublicKey();
|
PGPPublicKey primaryPubKey = secretKeys.next().getPublicKey();
|
||||||
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKeyRing.getSecretKey(), secretKeyDecryptor);
|
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKeyRing.getSecretKey(), secretKeyDecryptor);
|
||||||
Iterator<String> userIdIterator = this.userIds.iterator();
|
Iterator<Map.Entry<String, SelfSignatureSubpackets.Callback>> userIdIterator =
|
||||||
|
this.userIds.entrySet().iterator();
|
||||||
userIdIterator.next(); // Skip primary user id
|
userIdIterator.next(); // Skip primary user id
|
||||||
while (userIdIterator.hasNext()) {
|
while (userIdIterator.hasNext()) {
|
||||||
String additionalUserId = userIdIterator.next();
|
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;
|
||||||
|
} else {
|
||||||
|
subpackets = SignatureSubpackets.createHashedSubpackets(primaryPubKey);
|
||||||
|
callback.modifyHashedSubpackets(subpackets);
|
||||||
|
}
|
||||||
signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.getCode(), privateKey);
|
signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.getCode(), privateKey);
|
||||||
|
signatureGenerator.setHashedSubpackets(
|
||||||
|
SignatureSubpacketsHelper.toVector((SignatureSubpackets) subpackets));
|
||||||
PGPSignature additionalUserIdSignature =
|
PGPSignature additionalUserIdSignature =
|
||||||
signatureGenerator.generateCertification(additionalUserId, primaryPubKey);
|
signatureGenerator.generateCertification(userIdString, primaryPubKey);
|
||||||
primaryPubKey = PGPPublicKey.addCertification(primaryPubKey,
|
primaryPubKey = PGPPublicKey.addCertification(primaryPubKey,
|
||||||
additionalUserId, additionalUserIdSignature);
|
userIdString, additionalUserIdSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
// "reassemble" secret key ring with modified primary key
|
// "reassemble" secret key ring with modified primary key
|
||||||
|
@ -183,7 +206,7 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
||||||
PGPSignatureSubpacketVector hashedSubPackets,
|
PGPSignatureSubpacketVector hashedSubPackets,
|
||||||
PBESecretKeyEncryptor secretKeyEncryptor)
|
PBESecretKeyEncryptor secretKeyEncryptor)
|
||||||
throws PGPException {
|
throws PGPException {
|
||||||
String primaryUserId = userIds.iterator().next();
|
String primaryUserId = userIds.entrySet().iterator().next().getKey();
|
||||||
return new PGPKeyRingGenerator(
|
return new PGPKeyRingGenerator(
|
||||||
SignatureType.POSITIVE_CERTIFICATION.getCode(), certKey,
|
SignatureType.POSITIVE_CERTIFICATION.getCode(), certKey,
|
||||||
primaryUserId, keyFingerprintCalculator,
|
primaryUserId, keyFingerprintCalculator,
|
||||||
|
@ -199,7 +222,8 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
||||||
} else {
|
} else {
|
||||||
PGPSignatureSubpacketVector hashedSubpackets = subKeySpec.getSubpackets();
|
PGPSignatureSubpacketVector hashedSubpackets = subKeySpec.getSubpackets();
|
||||||
try {
|
try {
|
||||||
hashedSubpackets = addPrimaryKeyBindingSignatureIfNecessary(primaryKey, subKey, hashedSubpackets);
|
hashedSubpackets = addPrimaryKeyBindingSignatureIfNecessary(
|
||||||
|
primaryKey, subKey, hashedSubpackets);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new PGPException("Exception while adding primary key binding signature to signing subkey", e);
|
throw new PGPException("Exception while adding primary key binding signature to signing subkey", e);
|
||||||
}
|
}
|
||||||
|
@ -208,10 +232,12 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PGPSignatureSubpacketVector addPrimaryKeyBindingSignatureIfNecessary(PGPKeyPair primaryKey, PGPKeyPair subKey, PGPSignatureSubpacketVector hashedSubpackets)
|
private PGPSignatureSubpacketVector addPrimaryKeyBindingSignatureIfNecessary(
|
||||||
|
PGPKeyPair primaryKey, PGPKeyPair subKey, PGPSignatureSubpacketVector hashedSubpackets)
|
||||||
throws PGPException, IOException {
|
throws PGPException, IOException {
|
||||||
int keyFlagMask = hashedSubpackets.getKeyFlags();
|
int keyFlagMask = hashedSubpackets.getKeyFlags();
|
||||||
if (!KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.SIGN_DATA) && !KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.CERTIFY_OTHER)) {
|
if (!KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.SIGN_DATA) &&
|
||||||
|
!KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.CERTIFY_OTHER)) {
|
||||||
return hashedSubpackets;
|
return hashedSubpackets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,14 +250,16 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private PGPContentSignerBuilder buildContentSigner(PGPKeyPair certKey) {
|
private PGPContentSignerBuilder buildContentSigner(PGPKeyPair certKey) {
|
||||||
HashAlgorithm hashAlgorithm = PGPainless.getPolicy().getSignatureHashAlgorithmPolicy().defaultHashAlgorithm();
|
HashAlgorithm hashAlgorithm = PGPainless.getPolicy()
|
||||||
|
.getSignatureHashAlgorithmPolicy().defaultHashAlgorithm();
|
||||||
return ImplementationFactory.getInstance().getPGPContentSignerBuilder(
|
return ImplementationFactory.getInstance().getPGPContentSignerBuilder(
|
||||||
certKey.getPublicKey().getAlgorithm(),
|
certKey.getPublicKey().getAlgorithm(),
|
||||||
hashAlgorithm.getAlgorithmId());
|
hashAlgorithm.getAlgorithmId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private PBESecretKeyEncryptor buildSecretKeyEncryptor(PGPDigestCalculator keyFingerprintCalculator) {
|
private PBESecretKeyEncryptor buildSecretKeyEncryptor(PGPDigestCalculator keyFingerprintCalculator) {
|
||||||
SymmetricKeyAlgorithm keyEncryptionAlgorithm = PGPainless.getPolicy().getSymmetricKeyEncryptionAlgorithmPolicy()
|
SymmetricKeyAlgorithm keyEncryptionAlgorithm = PGPainless.getPolicy()
|
||||||
|
.getSymmetricKeyEncryptionAlgorithmPolicy()
|
||||||
.getDefaultSymmetricKeyAlgorithm();
|
.getDefaultSymmetricKeyAlgorithm();
|
||||||
if (!passphrase.isValid()) {
|
if (!passphrase.isValid()) {
|
||||||
throw new IllegalStateException("Passphrase was cleared.");
|
throw new IllegalStateException("Passphrase was cleared.");
|
||||||
|
@ -261,7 +289,8 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
||||||
KeyPair keyPair = certKeyGenerator.generateKeyPair();
|
KeyPair keyPair = certKeyGenerator.generateKeyPair();
|
||||||
|
|
||||||
// Form PGP key pair
|
// Form PGP key pair
|
||||||
PGPKeyPair pgpKeyPair = ImplementationFactory.getInstance().getPGPKeyPair(type.getAlgorithm(), keyPair, new Date());
|
PGPKeyPair pgpKeyPair = ImplementationFactory.getInstance()
|
||||||
|
.getPGPKeyPair(type.getAlgorithm(), keyPair, new Date());
|
||||||
return pgpKeyPair;
|
return pgpKeyPair;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
// SPDX-FileCopyrightText: 2021 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.bouncycastle.openpgp.PGPSignature;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.pgpainless.PGPainless;
|
||||||
|
import org.pgpainless.key.info.KeyRingInfo;
|
||||||
|
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
public class KeyGenerationSubpacketsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void verifyDefaultSubpackets()
|
||||||
|
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("Alice", null);
|
||||||
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
|
||||||
|
PGPSignature userIdSig = info.getLatestUserIdCertification("Alice");
|
||||||
|
assertNotNull(userIdSig);
|
||||||
|
assertNotNull(userIdSig.getHashedSubPackets().getIssuerFingerprint());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue