mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-23 04:42:06 +01:00
parent
deb23fb7e3
commit
710f961984
13 changed files with 510 additions and 204 deletions
|
@ -22,6 +22,7 @@ import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import org.bouncycastle.bcpg.sig.PrimaryUserID;
|
import org.bouncycastle.bcpg.sig.PrimaryUserID;
|
||||||
|
import org.bouncycastle.bcpg.sig.RevocationReason;
|
||||||
import org.bouncycastle.openpgp.PGPKeyRing;
|
import org.bouncycastle.openpgp.PGPKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
@ -38,9 +39,10 @@ import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
import org.pgpainless.exception.KeyValidationError;
|
import org.pgpainless.exception.KeyValidationError;
|
||||||
import org.pgpainless.key.OpenPgpFingerprint;
|
import org.pgpainless.key.OpenPgpFingerprint;
|
||||||
import org.pgpainless.key.SubkeyIdentifier;
|
import org.pgpainless.key.SubkeyIdentifier;
|
||||||
|
import org.pgpainless.key.util.RevocationAttributes;
|
||||||
import org.pgpainless.policy.Policy;
|
import org.pgpainless.policy.Policy;
|
||||||
import org.pgpainless.signature.consumer.SignaturePicker;
|
|
||||||
import org.pgpainless.signature.SignatureUtils;
|
import org.pgpainless.signature.SignatureUtils;
|
||||||
|
import org.pgpainless.signature.consumer.SignaturePicker;
|
||||||
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
|
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,6 +54,8 @@ public class KeyRingInfo {
|
||||||
|
|
||||||
private final PGPKeyRing keys;
|
private final PGPKeyRing keys;
|
||||||
private Signatures signatures;
|
private Signatures signatures;
|
||||||
|
private final Date evaluationDate;
|
||||||
|
private final String primaryUserId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluate the key ring at creation time of the given signature.
|
* Evaluate the key ring at creation time of the given signature.
|
||||||
|
@ -82,6 +86,8 @@ public class KeyRingInfo {
|
||||||
public KeyRingInfo(PGPKeyRing keys, Date validationDate) {
|
public KeyRingInfo(PGPKeyRing keys, Date validationDate) {
|
||||||
this.keys = keys;
|
this.keys = keys;
|
||||||
this.signatures = new Signatures(keys, validationDate, PGPainless.getPolicy());
|
this.signatures = new Signatures(keys, validationDate, PGPainless.getPolicy());
|
||||||
|
this.evaluationDate = validationDate;
|
||||||
|
this.primaryUserId = findPrimaryUserId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -251,45 +257,67 @@ public class KeyRingInfo {
|
||||||
return OpenPgpFingerprint.of(getPublicKey());
|
return OpenPgpFingerprint.of(getPublicKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @Nullable String getPrimaryUserId() {
|
||||||
|
return primaryUserId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the primary user-id of the key ring.
|
* Return the primary user-id of the key ring.
|
||||||
*
|
*
|
||||||
* Note: If no user-id is marked as primary key using a {@link PrimaryUserID} packet,
|
* Note: If no user-id is marked as primary key using a {@link PrimaryUserID} packet,
|
||||||
* this method returns the first valid user-id, otherwise null.
|
* this method returns the latest added user-id, otherwise null.
|
||||||
*
|
*
|
||||||
* @return primary user-id or null
|
* @return primary user-id or null
|
||||||
*/
|
*/
|
||||||
public @Nullable String getPrimaryUserId() {
|
private String findPrimaryUserId() {
|
||||||
|
String nonPrimaryUserId = null;
|
||||||
String primaryUserId = null;
|
String primaryUserId = null;
|
||||||
Date modificationDate = null;
|
Date modificationDate = null;
|
||||||
|
|
||||||
List<String> validUserIds = getValidUserIds();
|
List<String> userIds = getUserIds();
|
||||||
if (validUserIds.isEmpty()) {
|
if (userIds.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String userId : validUserIds) {
|
if (userIds.size() == 1) {
|
||||||
|
return userIds.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
PGPSignature signature = signatures.userIdCertifications.get(userId);
|
for (String userId : userIds) {
|
||||||
if (signature == null) {
|
PGPSignature certification = signatures.userIdCertifications.get(userId);
|
||||||
|
if (certification == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Date creationTime = certification.getCreationTime();
|
||||||
|
|
||||||
PrimaryUserID subpacket = SignatureSubpacketsUtil.getPrimaryUserId(signature);
|
if (certification.getHashedSubPackets().isPrimaryUserID()) {
|
||||||
if (subpacket != null && subpacket.isPrimaryUserID()) {
|
if (nonPrimaryUserId != null) {
|
||||||
// if there are multiple primary userIDs, return most recently signed
|
nonPrimaryUserId = null;
|
||||||
if (modificationDate == null || !signature.getCreationTime().before(modificationDate)) {
|
modificationDate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modificationDate == null || creationTime.after(modificationDate)) {
|
||||||
primaryUserId = userId;
|
primaryUserId = userId;
|
||||||
modificationDate = signature.getCreationTime();
|
modificationDate = creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (primaryUserId != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modificationDate == null || creationTime.after(modificationDate)) {
|
||||||
|
nonPrimaryUserId = userId;
|
||||||
|
modificationDate = creationTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Workaround for keys with only one user-id but no primary user-id packet.
|
|
||||||
if (primaryUserId == null) {
|
if (primaryUserId != null) {
|
||||||
return validUserIds.get(0);
|
return primaryUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return primaryUserId;
|
return nonPrimaryUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -314,7 +342,7 @@ public class KeyRingInfo {
|
||||||
List<String> valid = new ArrayList<>();
|
List<String> valid = new ArrayList<>();
|
||||||
List<String> userIds = getUserIds();
|
List<String> userIds = getUserIds();
|
||||||
for (String userId : userIds) {
|
for (String userId : userIds) {
|
||||||
if (isUserIdValid(userId)) {
|
if (isUserIdBound(userId)) {
|
||||||
valid.add(userId);
|
valid.add(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,6 +356,18 @@ public class KeyRingInfo {
|
||||||
* @return true if user-id is valid
|
* @return true if user-id is valid
|
||||||
*/
|
*/
|
||||||
public boolean isUserIdValid(String userId) {
|
public boolean isUserIdValid(String userId) {
|
||||||
|
if (!userId.equals(primaryUserId)) {
|
||||||
|
if (!isUserIdBound(primaryUserId)) {
|
||||||
|
// primary user-id not valid
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isUserIdBound(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isUserIdBound(String userId) {
|
||||||
|
|
||||||
PGPSignature certification = signatures.userIdCertifications.get(userId);
|
PGPSignature certification = signatures.userIdCertifications.get(userId);
|
||||||
PGPSignature revocation = signatures.userIdRevocations.get(userId);
|
PGPSignature revocation = signatures.userIdRevocations.get(userId);
|
||||||
|
|
||||||
|
@ -338,6 +378,12 @@ public class KeyRingInfo {
|
||||||
if (SignatureUtils.isSignatureExpired(certification)) {
|
if (SignatureUtils.isSignatureExpired(certification)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (certification.getHashedSubPackets().isPrimaryUserID()) {
|
||||||
|
Date keyExpiration = SignatureSubpacketsUtil.getKeyExpirationTimeAsDate(certification, keys.getPublicKey());
|
||||||
|
if (keyExpiration != null && evaluationDate.after(keyExpiration)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Not revoked -> valid
|
// Not revoked -> valid
|
||||||
if (revocation == null) {
|
if (revocation == null) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -588,7 +634,7 @@ public class KeyRingInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPSignature primaryUserIdCertification = getLatestUserIdCertification(getPrimaryUserId());
|
PGPSignature primaryUserIdCertification = getLatestUserIdCertification(getPossiblyExpiredUserId());
|
||||||
if (primaryUserIdCertification != null) {
|
if (primaryUserIdCertification != null) {
|
||||||
return SignatureSubpacketsUtil.getKeyExpirationTimeAsDate(primaryUserIdCertification, getPublicKey());
|
return SignatureSubpacketsUtil.getKeyExpirationTimeAsDate(primaryUserIdCertification, getPublicKey());
|
||||||
}
|
}
|
||||||
|
@ -596,6 +642,38 @@ public class KeyRingInfo {
|
||||||
throw new NoSuchElementException("No suitable signatures found on the key.");
|
throw new NoSuchElementException("No suitable signatures found on the key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getPossiblyExpiredUserId() {
|
||||||
|
String validPrimaryUserId = getPrimaryUserId();
|
||||||
|
if (validPrimaryUserId != null) {
|
||||||
|
return validPrimaryUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
Date latestCreationTime = null;
|
||||||
|
String primaryUserId = null;
|
||||||
|
boolean foundPrimary = false;
|
||||||
|
for (String userId : getUserIds()) {
|
||||||
|
PGPSignature signature = getLatestUserIdCertification(userId);
|
||||||
|
if (signature == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isPrimary = signature.getHashedSubPackets().isPrimaryUserID();
|
||||||
|
if (foundPrimary && !isPrimary) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Date creationTime = signature.getCreationTime();
|
||||||
|
if (latestCreationTime == null || creationTime.after(latestCreationTime) || isPrimary && !foundPrimary) {
|
||||||
|
latestCreationTime = creationTime;
|
||||||
|
primaryUserId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
foundPrimary |= isPrimary;
|
||||||
|
}
|
||||||
|
|
||||||
|
return primaryUserId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the expiration date of the subkey with the provided fingerprint.
|
* Return the expiration date of the subkey with the provided fingerprint.
|
||||||
*
|
*
|
||||||
|
@ -668,6 +746,15 @@ public class KeyRingInfo {
|
||||||
return primaryExpiration;
|
return primaryExpiration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isHardRevoked(String userId) {
|
||||||
|
PGPSignature revocation = signatures.userIdRevocations.get(userId);
|
||||||
|
if (revocation == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
RevocationReason revocationReason = revocation.getHashedSubPackets().getRevocationReason();
|
||||||
|
return revocationReason == null || RevocationAttributes.Reason.isHardRevocation(revocationReason.getRevocationReason());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if the key ring is a {@link PGPSecretKeyRing}.
|
* Return true if the key ring is a {@link PGPSecretKeyRing}.
|
||||||
* If it is a {@link PGPPublicKeyRing} return false and if it is neither, throw an {@link AssertionError}.
|
* If it is a {@link PGPPublicKeyRing} return false and if it is neither, throw an {@link AssertionError}.
|
||||||
|
|
|
@ -22,17 +22,14 @@ import javax.annotation.Nullable;
|
||||||
|
|
||||||
import org.bouncycastle.bcpg.S2K;
|
import org.bouncycastle.bcpg.S2K;
|
||||||
import org.bouncycastle.bcpg.SecretKeyPacket;
|
import org.bouncycastle.bcpg.SecretKeyPacket;
|
||||||
|
import org.bouncycastle.bcpg.sig.KeyExpirationTime;
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPKeyPair;
|
import org.bouncycastle.openpgp.PGPKeyPair;
|
||||||
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
|
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
|
||||||
import org.bouncycastle.openpgp.PGPPrivateKey;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
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.PBESecretKeyDecryptor;
|
||||||
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
|
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
|
||||||
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
|
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
|
||||||
|
@ -47,7 +44,6 @@ import org.pgpainless.algorithm.SignatureType;
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator;
|
import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator;
|
||||||
import org.pgpainless.implementation.ImplementationFactory;
|
import org.pgpainless.implementation.ImplementationFactory;
|
||||||
import org.pgpainless.key.OpenPgpFingerprint;
|
|
||||||
import org.pgpainless.key.generation.KeyRingBuilder;
|
import org.pgpainless.key.generation.KeyRingBuilder;
|
||||||
import org.pgpainless.key.generation.KeySpec;
|
import org.pgpainless.key.generation.KeySpec;
|
||||||
import org.pgpainless.key.info.KeyRingInfo;
|
import org.pgpainless.key.info.KeyRingInfo;
|
||||||
|
@ -55,18 +51,16 @@ import org.pgpainless.key.protection.CachingSecretKeyRingProtector;
|
||||||
import org.pgpainless.key.protection.KeyRingProtectionSettings;
|
import org.pgpainless.key.protection.KeyRingProtectionSettings;
|
||||||
import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector;
|
import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector;
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
import org.pgpainless.key.protection.UnlockSecretKey;
|
|
||||||
import org.pgpainless.key.protection.UnprotectedKeysProtector;
|
import org.pgpainless.key.protection.UnprotectedKeysProtector;
|
||||||
import org.pgpainless.key.protection.fixes.S2KUsageFix;
|
import org.pgpainless.key.protection.fixes.S2KUsageFix;
|
||||||
import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider;
|
import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider;
|
||||||
import org.pgpainless.key.util.KeyRingUtils;
|
import org.pgpainless.key.util.KeyRingUtils;
|
||||||
import org.pgpainless.key.util.RevocationAttributes;
|
import org.pgpainless.key.util.RevocationAttributes;
|
||||||
import org.pgpainless.signature.SignatureUtils;
|
import org.pgpainless.signature.builder.DirectKeySignatureBuilder;
|
||||||
import org.pgpainless.signature.builder.RevocationSignatureBuilder;
|
import org.pgpainless.signature.builder.RevocationSignatureBuilder;
|
||||||
import org.pgpainless.signature.builder.SelfSignatureBuilder;
|
import org.pgpainless.signature.builder.SelfSignatureBuilder;
|
||||||
import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets;
|
import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets;
|
||||||
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets;
|
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets;
|
||||||
import org.pgpainless.signature.subpackets.SignatureSubpacketGeneratorUtil;
|
|
||||||
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.signature.subpackets.SignatureSubpacketsUtil;
|
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
|
||||||
|
@ -107,6 +101,9 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
|
||||||
|
|
||||||
// retain key flags from previous signature
|
// retain key flags from previous signature
|
||||||
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing);
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing);
|
||||||
|
if (info.isHardRevoked(userId.toString())) {
|
||||||
|
throw new IllegalArgumentException("User-ID " + userId + " is hard revoked and cannot be re-certified.");
|
||||||
|
}
|
||||||
List<KeyFlag> keyFlags = info.getKeyFlagsOf(info.getKeyId());
|
List<KeyFlag> keyFlags = info.getKeyFlagsOf(info.getKeyId());
|
||||||
|
|
||||||
Set<HashAlgorithm> hashAlgorithmPreferences;
|
Set<HashAlgorithm> hashAlgorithmPreferences;
|
||||||
|
@ -146,15 +143,54 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
|
||||||
public SecretKeyRingEditorInterface addPrimaryUserId(
|
public SecretKeyRingEditorInterface addPrimaryUserId(
|
||||||
@Nonnull CharSequence userId, @Nonnull SecretKeyRingProtector protector)
|
@Nonnull CharSequence userId, @Nonnull SecretKeyRingProtector protector)
|
||||||
throws PGPException {
|
throws PGPException {
|
||||||
return addUserId(
|
|
||||||
|
// Determine previous key expiration date
|
||||||
|
PGPPublicKey primaryKey = secretKeyRing.getSecretKey().getPublicKey();
|
||||||
|
/*
|
||||||
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing);
|
||||||
|
String primaryUserId = info.getPrimaryUserId();
|
||||||
|
PGPSignature signature = primaryUserId == null ?
|
||||||
|
info.getLatestDirectKeySelfSignature() : info.getLatestUserIdCertification(primaryUserId);
|
||||||
|
final Date previousKeyExpiration = signature == null ? null :
|
||||||
|
SignatureSubpacketsUtil.getKeyExpirationTimeAsDate(signature, primaryKey);
|
||||||
|
*/
|
||||||
|
final Date previousKeyExpiration = null;
|
||||||
|
|
||||||
|
// Add new primary user-id signature
|
||||||
|
addUserId(
|
||||||
userId,
|
userId,
|
||||||
new SelfSignatureSubpackets.Callback() {
|
new SelfSignatureSubpackets.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) {
|
public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) {
|
||||||
hashedSubpackets.setPrimaryUserId();
|
hashedSubpackets.setPrimaryUserId();
|
||||||
|
if (previousKeyExpiration != null) {
|
||||||
|
hashedSubpackets.setKeyExpirationTime(primaryKey, previousKeyExpiration);
|
||||||
|
} else {
|
||||||
|
hashedSubpackets.setKeyExpirationTime(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
protector);
|
protector);
|
||||||
|
|
||||||
|
// unmark previous primary user-ids to be non-primary
|
||||||
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing);
|
||||||
|
for (String otherUserId : info.getValidUserIds()) {
|
||||||
|
if (userId.toString().equals(otherUserId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to unmark this user-id as primary
|
||||||
|
if (info.getLatestUserIdCertification(otherUserId).getHashedSubPackets().isPrimaryUserID()) {
|
||||||
|
addUserId(otherUserId, new SelfSignatureSubpackets.Callback() {
|
||||||
|
@Override
|
||||||
|
public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) {
|
||||||
|
hashedSubpackets.setPrimaryUserId(null);
|
||||||
|
hashedSubpackets.setKeyExpirationTime(null); // non-primary
|
||||||
|
}
|
||||||
|
}, protector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Move to utility class?
|
// TODO: Move to utility class?
|
||||||
|
@ -468,118 +504,119 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
|
||||||
@Nullable Date expiration,
|
@Nullable Date expiration,
|
||||||
@Nonnull SecretKeyRingProtector secretKeyRingProtector)
|
@Nonnull SecretKeyRingProtector secretKeyRingProtector)
|
||||||
throws PGPException {
|
throws PGPException {
|
||||||
return setExpirationDate(OpenPgpFingerprint.of(secretKeyRing), expiration, secretKeyRingProtector);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SecretKeyRingEditorInterface setExpirationDate(
|
|
||||||
@Nonnull OpenPgpFingerprint fingerprint,
|
|
||||||
@Nullable Date expiration,
|
|
||||||
@Nonnull SecretKeyRingProtector secretKeyRingProtector)
|
|
||||||
throws PGPException {
|
|
||||||
|
|
||||||
List<PGPSecretKey> secretKeyList = new ArrayList<>();
|
|
||||||
PGPSecretKey primaryKey = secretKeyRing.getSecretKey();
|
PGPSecretKey primaryKey = secretKeyRing.getSecretKey();
|
||||||
if (!primaryKey.isMasterKey()) {
|
if (!primaryKey.isMasterKey()) {
|
||||||
throw new IllegalArgumentException("Key Ring does not appear to contain a primary secret key.");
|
throw new IllegalArgumentException("Key Ring does not appear to contain a primary secret key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean found = false;
|
// reissue direct key sig
|
||||||
for (PGPSecretKey secretKey : secretKeyRing) {
|
PGPSignature prevDirectKeySig = getPreviousDirectKeySignature();
|
||||||
// Skip over unaffected subkeys
|
if (prevDirectKeySig != null) {
|
||||||
if (secretKey.getKeyID() != fingerprint.getKeyId()) {
|
PGPSignature directKeySig = reissueDirectKeySignature(expiration, secretKeyRingProtector, prevDirectKeySig);
|
||||||
secretKeyList.add(secretKey);
|
secretKeyRing = KeyRingUtils.injectCertification(secretKeyRing, primaryKey.getPublicKey(), directKeySig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reissue primary user-id sig
|
||||||
|
String primaryUserId = PGPainless.inspectKeyRing(secretKeyRing).getPossiblyExpiredUserId();
|
||||||
|
if (primaryUserId != null) {
|
||||||
|
PGPSignature prevUserIdSig = getPreviousUserIdSignatures(primaryUserId);
|
||||||
|
PGPSignature userIdSig = reissuePrimaryUserIdSig(expiration, secretKeyRingProtector, primaryUserId, prevUserIdSig);
|
||||||
|
secretKeyRing = KeyRingUtils.injectCertification(secretKeyRing, primaryUserId, userIdSig);
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing);
|
||||||
|
for (String userId : info.getValidUserIds()) {
|
||||||
|
if (userId.equals(primaryUserId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// We found the target subkey
|
|
||||||
found = true;
|
|
||||||
secretKey = setExpirationDate(primaryKey, secretKey, expiration, secretKeyRingProtector);
|
|
||||||
secretKeyList.add(secretKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
PGPSignature prevUserIdSig = info.getLatestUserIdCertification(userId);
|
||||||
throw new IllegalArgumentException("Key Ring does not contain secret key with fingerprint " + fingerprint);
|
if (prevUserIdSig == null) {
|
||||||
}
|
throw new AssertionError("A valid user-id shall never have no user-id signature.");
|
||||||
|
}
|
||||||
|
|
||||||
secretKeyRing = new PGPSecretKeyRing(secretKeyList);
|
if (prevUserIdSig.getHashedSubPackets().isPrimaryUserID()) {
|
||||||
|
PGPSignature userIdSig = reissueNonPrimaryUserId(secretKeyRingProtector, userId, prevUserIdSig);
|
||||||
|
secretKeyRing = KeyRingUtils.injectCertification(secretKeyRing, primaryUserId, userIdSig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PGPSecretKey setExpirationDate(PGPSecretKey primaryKey,
|
private PGPSignature reissueNonPrimaryUserId(
|
||||||
PGPSecretKey subjectKey,
|
SecretKeyRingProtector secretKeyRingProtector,
|
||||||
Date expiration,
|
String userId,
|
||||||
SecretKeyRingProtector secretKeyRingProtector)
|
PGPSignature prevUserIdSig)
|
||||||
throws PGPException {
|
throws PGPException {
|
||||||
|
SelfSignatureBuilder builder = new SelfSignatureBuilder(secretKeyRing.getSecretKey(), secretKeyRingProtector, prevUserIdSig);
|
||||||
if (expiration != null && expiration.before(subjectKey.getPublicKey().getCreationTime())) {
|
builder.applyCallback(new SelfSignatureSubpackets.Callback() {
|
||||||
throw new IllegalArgumentException("Expiration date cannot be before creation date.");
|
@Override
|
||||||
}
|
public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) {
|
||||||
|
// unmark as primary
|
||||||
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(primaryKey, secretKeyRingProtector);
|
hashedSubpackets.setPrimaryUserId(null);
|
||||||
PGPPublicKey subjectPubKey = subjectKey.getPublicKey();
|
|
||||||
|
|
||||||
PGPSignature oldSignature = getPreviousSignature(primaryKey, subjectPubKey);
|
|
||||||
|
|
||||||
PGPSignatureSubpacketVector oldSubpackets = oldSignature.getHashedSubPackets();
|
|
||||||
PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator(oldSubpackets);
|
|
||||||
SignatureSubpacketGeneratorUtil.setSignatureCreationTimeInSubpacketGenerator(new Date(), subpacketGenerator);
|
|
||||||
SignatureSubpacketGeneratorUtil.setKeyExpirationDateInSubpacketGenerator(
|
|
||||||
expiration, subjectPubKey.getCreationTime(), subpacketGenerator);
|
|
||||||
|
|
||||||
PGPSignatureGenerator signatureGenerator = SignatureUtils.getSignatureGeneratorFor(primaryKey);
|
|
||||||
signatureGenerator.setHashedSubpackets(subpacketGenerator.generate());
|
|
||||||
|
|
||||||
if (primaryKey.getKeyID() == subjectKey.getKeyID()) {
|
|
||||||
signatureGenerator.init(PGPSignature.POSITIVE_CERTIFICATION, privateKey);
|
|
||||||
|
|
||||||
for (Iterator<String> it = subjectKey.getUserIDs(); it.hasNext(); ) {
|
|
||||||
String userId = it.next();
|
|
||||||
PGPSignature signature = signatureGenerator.generateCertification(userId, subjectPubKey);
|
|
||||||
subjectPubKey = PGPPublicKey.addCertification(subjectPubKey, userId, signature);
|
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
signatureGenerator.init(PGPSignature.SUBKEY_BINDING, privateKey);
|
return builder.build(secretKeyRing.getPublicKey(), userId);
|
||||||
|
|
||||||
PGPSignature signature = signatureGenerator.generateCertification(
|
|
||||||
primaryKey.getPublicKey(), subjectPubKey);
|
|
||||||
subjectPubKey = PGPPublicKey.addCertification(subjectPubKey, signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
subjectKey = PGPSecretKey.replacePublicKey(subjectKey, subjectPubKey);
|
|
||||||
return subjectKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PGPSignature getPreviousSignature(PGPSecretKey primaryKey, PGPPublicKey subjectPubKey) {
|
private PGPSignature reissuePrimaryUserIdSig(
|
||||||
PGPSignature oldSignature = null;
|
@Nullable Date expiration,
|
||||||
if (primaryKey.getKeyID() == subjectPubKey.getKeyID()) {
|
@Nonnull SecretKeyRingProtector secretKeyRingProtector,
|
||||||
Iterator<PGPSignature> keySignatures = subjectPubKey.getSignaturesForKeyID(primaryKey.getKeyID());
|
@Nonnull String primaryUserId,
|
||||||
while (keySignatures.hasNext()) {
|
@Nonnull PGPSignature prevUserIdSig)
|
||||||
PGPSignature next = keySignatures.next();
|
throws PGPException {
|
||||||
SignatureType type = SignatureType.valueOf(next.getSignatureType());
|
PGPSecretKey primaryKey = secretKeyRing.getSecretKey();
|
||||||
if (type == SignatureType.POSITIVE_CERTIFICATION ||
|
PGPPublicKey publicKey = primaryKey.getPublicKey();
|
||||||
type == SignatureType.CASUAL_CERTIFICATION ||
|
|
||||||
type == SignatureType.GENERIC_CERTIFICATION) {
|
SelfSignatureBuilder builder = new SelfSignatureBuilder(primaryKey, secretKeyRingProtector, prevUserIdSig);
|
||||||
oldSignature = next;
|
builder.applyCallback(new SelfSignatureSubpackets.Callback() {
|
||||||
|
@Override
|
||||||
|
public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) {
|
||||||
|
if (expiration != null) {
|
||||||
|
hashedSubpackets.setKeyExpirationTime(true, publicKey.getCreationTime(), expiration);
|
||||||
|
} else {
|
||||||
|
hashedSubpackets.setKeyExpirationTime(new KeyExpirationTime(true, 0));
|
||||||
|
}
|
||||||
|
hashedSubpackets.setPrimaryUserId();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return builder.build(publicKey, primaryUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PGPSignature reissueDirectKeySignature(
|
||||||
|
Date expiration,
|
||||||
|
SecretKeyRingProtector secretKeyRingProtector,
|
||||||
|
PGPSignature prevDirectKeySig)
|
||||||
|
throws PGPException {
|
||||||
|
PGPSecretKey primaryKey = secretKeyRing.getSecretKey();
|
||||||
|
PGPPublicKey publicKey = primaryKey.getPublicKey();
|
||||||
|
final Date keyCreationTime = publicKey.getCreationTime();
|
||||||
|
|
||||||
|
DirectKeySignatureBuilder builder = new DirectKeySignatureBuilder(primaryKey, secretKeyRingProtector, prevDirectKeySig);
|
||||||
|
builder.applyCallback(new SelfSignatureSubpackets.Callback() {
|
||||||
|
@Override
|
||||||
|
public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) {
|
||||||
|
if (expiration != null) {
|
||||||
|
hashedSubpackets.setKeyExpirationTime(keyCreationTime, expiration);
|
||||||
|
} else {
|
||||||
|
hashedSubpackets.setKeyExpirationTime(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (oldSignature == null) {
|
});
|
||||||
throw new IllegalStateException("Key " + OpenPgpFingerprint.of(subjectPubKey) +
|
|
||||||
" does not have a previous positive/casual/generic certification signature.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Iterator<PGPSignature> bindingSignatures = subjectPubKey.getSignaturesOfType(
|
|
||||||
SignatureType.SUBKEY_BINDING.getCode());
|
|
||||||
while (bindingSignatures.hasNext()) {
|
|
||||||
oldSignature = bindingSignatures.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldSignature == null) {
|
return builder.build(publicKey);
|
||||||
throw new IllegalStateException("Key " + OpenPgpFingerprint.of(subjectPubKey) +
|
}
|
||||||
" does not have a previous subkey binding signature.");
|
|
||||||
}
|
private PGPSignature getPreviousDirectKeySignature() {
|
||||||
return oldSignature;
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing);
|
||||||
|
return info.getLatestDirectKeySelfSignature();
|
||||||
|
}
|
||||||
|
|
||||||
|
private PGPSignature getPreviousUserIdSignatures(String userId) {
|
||||||
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing);
|
||||||
|
return info.getLatestUserIdCertification(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -366,20 +366,6 @@ public interface SecretKeyRingEditorInterface {
|
||||||
@Nonnull SecretKeyRingProtector secretKeyRingProtector)
|
@Nonnull SecretKeyRingProtector secretKeyRingProtector)
|
||||||
throws PGPException;
|
throws PGPException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Set key expiration time.
|
|
||||||
*
|
|
||||||
* @param fingerprint key that will have its expiration date adjusted
|
|
||||||
* @param expiration target expiration time or @{code null} for no expiration
|
|
||||||
* @param secretKeyRingProtector protector to unlock the priary key
|
|
||||||
* @return the builder
|
|
||||||
*/
|
|
||||||
SecretKeyRingEditorInterface setExpirationDate(
|
|
||||||
@Nonnull OpenPgpFingerprint fingerprint,
|
|
||||||
@Nullable Date expiration,
|
|
||||||
@Nonnull SecretKeyRingProtector secretKeyRingProtector)
|
|
||||||
throws PGPException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a detached revocation certificate, which can be used to revoke the whole key.
|
* Create a detached revocation certificate, which can be used to revoke the whole key.
|
||||||
*
|
*
|
||||||
|
|
|
@ -21,6 +21,21 @@ public class SubkeyBindingSignatureBuilder extends AbstractSignatureBuilder<Subk
|
||||||
super(SignatureType.SUBKEY_BINDING, signingKey, protector);
|
super(SignatureType.SUBKEY_BINDING, signingKey, protector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SubkeyBindingSignatureBuilder(
|
||||||
|
PGPSecretKey signingKey,
|
||||||
|
SecretKeyRingProtector protector,
|
||||||
|
PGPSignature oldSubkeyBinding)
|
||||||
|
throws PGPException {
|
||||||
|
super(signingKey, protector, requireValidSignatureType(oldSubkeyBinding));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PGPSignature requireValidSignatureType(PGPSignature signature) {
|
||||||
|
if (signature.getSignatureType() == SignatureType.SUBKEY_BINDING.getCode()) {
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Invalid signature type.");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isValidSignatureType(SignatureType type) {
|
protected boolean isValidSignatureType(SignatureType type) {
|
||||||
return type == SignatureType.SUBKEY_BINDING;
|
return type == SignatureType.SUBKEY_BINDING;
|
||||||
|
|
|
@ -237,10 +237,6 @@ public final class SignaturePicker {
|
||||||
SignatureValidator.signatureIsCertification().verify(signature);
|
SignatureValidator.signatureIsCertification().verify(signature);
|
||||||
SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(signature);
|
SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(signature);
|
||||||
SignatureValidator.signatureIsAlreadyEffective(validationDate).verify(signature);
|
SignatureValidator.signatureIsAlreadyEffective(validationDate).verify(signature);
|
||||||
// if the currently latest signature is not yet expired, check if the next candidate is not yet expired
|
|
||||||
if (latestUserIdCert != null && !SignatureUtils.isSignatureExpired(latestUserIdCert, validationDate)) {
|
|
||||||
SignatureValidator.signatureIsNotYetExpired(validationDate).verify(signature);
|
|
||||||
}
|
|
||||||
SignatureValidator.correctSignatureOverUserId(userId, primaryKey, primaryKey).verify(signature);
|
SignatureValidator.correctSignatureOverUserId(userId, primaryKey, primaryKey).verify(signature);
|
||||||
} catch (SignatureValidationException e) {
|
} catch (SignatureValidationException e) {
|
||||||
// User-id certification is not valid
|
// User-id certification is not valid
|
||||||
|
|
|
@ -195,13 +195,14 @@ public final class SignatureSubpacketsUtil {
|
||||||
* @return key expiration time as date
|
* @return key expiration time as date
|
||||||
*/
|
*/
|
||||||
public static Date getKeyExpirationTimeAsDate(PGPSignature signature, PGPPublicKey signingKey) {
|
public static Date getKeyExpirationTimeAsDate(PGPSignature signature, PGPPublicKey signingKey) {
|
||||||
|
if (signature.getKeyID() != signingKey.getKeyID()) {
|
||||||
|
throw new IllegalArgumentException("Provided key (" + Long.toHexString(signingKey.getKeyID()) + ") did not create the signature (" + Long.toHexString(signature.getKeyID()) + ")");
|
||||||
|
}
|
||||||
KeyExpirationTime subpacket = getKeyExpirationTime(signature);
|
KeyExpirationTime subpacket = getKeyExpirationTime(signature);
|
||||||
if (subpacket == null) {
|
if (subpacket == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (signature.getKeyID() != signingKey.getKeyID()) {
|
|
||||||
throw new IllegalArgumentException("Provided key (" + Long.toHexString(signingKey.getKeyID()) + ") did not create the signature (" + Long.toHexString(signature.getKeyID()) + ")");
|
|
||||||
}
|
|
||||||
return SignatureUtils.datePlusSeconds(signingKey.getCreationTime(), subpacket.getTime());
|
return SignatureUtils.datePlusSeconds(signingKey.getCreationTime(), subpacket.getTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ package org.pgpainless.example;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@ -26,7 +25,6 @@ import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.algorithm.EncryptionPurpose;
|
import org.pgpainless.algorithm.EncryptionPurpose;
|
||||||
import org.pgpainless.algorithm.KeyFlag;
|
import org.pgpainless.algorithm.KeyFlag;
|
||||||
import org.pgpainless.exception.WrongPassphraseException;
|
import org.pgpainless.exception.WrongPassphraseException;
|
||||||
import org.pgpainless.key.OpenPgpFingerprint;
|
|
||||||
import org.pgpainless.key.generation.KeySpec;
|
import org.pgpainless.key.generation.KeySpec;
|
||||||
import org.pgpainless.key.generation.type.KeyType;
|
import org.pgpainless.key.generation.type.KeyType;
|
||||||
import org.pgpainless.key.generation.type.ecc.EllipticCurve;
|
import org.pgpainless.key.generation.type.ecc.EllipticCurve;
|
||||||
|
@ -223,33 +221,6 @@ public class ModifyKeys {
|
||||||
DateUtil.formatUTCDate(info.getExpirationDateForUse(KeyFlag.SIGN_DATA)));
|
DateUtil.formatUTCDate(info.getExpirationDateForUse(KeyFlag.SIGN_DATA)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This example demonstrates how to set an expiration date for single subkeys.
|
|
||||||
*
|
|
||||||
* @throws PGPException
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void setSubkeyExpirationDate() throws PGPException {
|
|
||||||
Date expirationDate = DateUtil.parseUTCDate("2032-01-13 22:30:01 UTC");
|
|
||||||
SecretKeyRingProtector protector = SecretKeyRingProtector
|
|
||||||
.unlockEachKeyWith(Passphrase.fromPassword(originalPassphrase), secretKey);
|
|
||||||
|
|
||||||
secretKey = PGPainless.modifyKeyRing(secretKey)
|
|
||||||
.setExpirationDate(
|
|
||||||
OpenPgpFingerprint.of(secretKey.getPublicKey(encryptionSubkeyId)),
|
|
||||||
expirationDate,
|
|
||||||
protector
|
|
||||||
)
|
|
||||||
.done();
|
|
||||||
|
|
||||||
|
|
||||||
KeyRingInfo info = PGPainless.inspectKeyRing(secretKey);
|
|
||||||
assertNull(info.getPrimaryKeyExpirationDate());
|
|
||||||
assertNull(info.getExpirationDateForUse(KeyFlag.SIGN_DATA));
|
|
||||||
assertEquals(DateUtil.formatUTCDate(expirationDate),
|
|
||||||
DateUtil.formatUTCDate(info.getExpirationDateForUse(KeyFlag.ENCRYPT_COMMS)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This example demonstrates how to revoke a user-id on a key.
|
* This example demonstrates how to revoke a user-id on a key.
|
||||||
*
|
*
|
||||||
|
|
|
@ -237,19 +237,15 @@ public class KeyRingInfoTest {
|
||||||
|
|
||||||
calendar.setTime(now);
|
calendar.setTime(now);
|
||||||
calendar.add(Calendar.DATE, 10);
|
calendar.add(Calendar.DATE, 10);
|
||||||
Date encryptionKeyExpiration = calendar.getTime(); // in 10 days
|
|
||||||
PGPSecretKey encryptionKey = keys.next();
|
PGPSecretKey encryptionKey = keys.next();
|
||||||
|
|
||||||
calendar.setTime(now);
|
calendar.setTime(now);
|
||||||
calendar.add(Calendar.DATE, 3);
|
calendar.add(Calendar.DATE, 3);
|
||||||
Date signingKeyExpiration = calendar.getTime(); // in 3 days
|
|
||||||
PGPSecretKey signingKey = keys.next();
|
PGPSecretKey signingKey = keys.next();
|
||||||
|
|
||||||
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
|
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
|
||||||
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||||
.setExpirationDate(new OpenPgpV4Fingerprint(primaryKey), primaryKeyExpiration, protector)
|
.setExpirationDate(primaryKeyExpiration, protector)
|
||||||
.setExpirationDate(new OpenPgpV4Fingerprint(encryptionKey), encryptionKeyExpiration, protector)
|
|
||||||
.setExpirationDate(new OpenPgpV4Fingerprint(signingKey), signingKeyExpiration, protector)
|
|
||||||
.done();
|
.done();
|
||||||
|
|
||||||
KeyRingInfo info = new KeyRingInfo(secretKeys);
|
KeyRingInfo info = new KeyRingInfo(secretKeys);
|
||||||
|
@ -267,7 +263,6 @@ public class KeyRingInfoTest {
|
||||||
assertEquals(primaryKey.getKeyID(), certKeys.get(0).getKeyID());
|
assertEquals(primaryKey.getKeyID(), certKeys.get(0).getKeyID());
|
||||||
|
|
||||||
assertEquals(primaryKeyExpiration.getTime(), info.getPrimaryKeyExpirationDate().getTime(), 5);
|
assertEquals(primaryKeyExpiration.getTime(), info.getPrimaryKeyExpirationDate().getTime(), 5);
|
||||||
assertEquals(signingKeyExpiration.getTime(), info.getExpirationDateForUse(KeyFlag.SIGN_DATA).getTime(), 5);
|
|
||||||
|
|
||||||
// Encryption key expires after primary key, so we return primary key expiration instead.
|
// Encryption key expires after primary key, so we return primary key expiration instead.
|
||||||
assertEquals(primaryKeyExpiration.getTime(), info.getExpirationDateForUse(KeyFlag.ENCRYPT_STORAGE).getTime(), 5);
|
assertEquals(primaryKeyExpiration.getTime(), info.getExpirationDateForUse(KeyFlag.ENCRYPT_STORAGE).getTime(), 5);
|
||||||
|
|
|
@ -9,12 +9,14 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.junit.jupiter.api.TestTemplate;
|
import org.junit.jupiter.api.TestTemplate;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.JUtils;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import org.pgpainless.key.TestKeys;
|
import org.pgpainless.key.TestKeys;
|
||||||
|
@ -68,16 +70,17 @@ public class ChangeExpirationTest {
|
||||||
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
|
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
|
||||||
KeyRingInfo sInfo = PGPainless.inspectKeyRing(secretKeys);
|
KeyRingInfo sInfo = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
|
||||||
assertNull(sInfo.getSubkeyExpirationDate(subKeyFingerprint));
|
|
||||||
assertNull(sInfo.getPrimaryKeyExpirationDate());
|
assertNull(sInfo.getPrimaryKeyExpirationDate());
|
||||||
|
|
||||||
Date date = DateUtil.parseUTCDate("2020-11-27 16:10:32 UTC");
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.setTime(new Date());
|
||||||
|
calendar.add(Calendar.DATE, 5);
|
||||||
|
Date expiration = calendar.getTime();
|
||||||
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||||
.setExpirationDate(subKeyFingerprint, date, new UnprotectedKeysProtector()).done();
|
.setExpirationDate(expiration, new UnprotectedKeysProtector()).done();
|
||||||
sInfo = PGPainless.inspectKeyRing(secretKeys);
|
sInfo = PGPainless.inspectKeyRing(secretKeys);
|
||||||
assertNotNull(sInfo.getSubkeyExpirationDate(subKeyFingerprint));
|
assertNotNull(sInfo.getPrimaryKeyExpirationDate());
|
||||||
assertEquals(date.getTime(), sInfo.getSubkeyExpirationDate(subKeyFingerprint).getTime());
|
JUtils.assertDateEquals(expiration, sInfo.getPrimaryKeyExpirationDate());
|
||||||
assertNull(sInfo.getPrimaryKeyExpirationDate());
|
|
||||||
|
|
||||||
// We need to wait for one second as OpenPGP signatures have coarse-grained (up to a second)
|
// We need to wait for one second as OpenPGP signatures have coarse-grained (up to a second)
|
||||||
// accuracy. Creating two signatures within a short amount of time will make the second one
|
// accuracy. Creating two signatures within a short amount of time will make the second one
|
||||||
|
@ -85,10 +88,9 @@ public class ChangeExpirationTest {
|
||||||
Thread.sleep(1100);
|
Thread.sleep(1100);
|
||||||
|
|
||||||
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||||
.setExpirationDate(subKeyFingerprint, null, new UnprotectedKeysProtector()).done();
|
.setExpirationDate(null, new UnprotectedKeysProtector()).done();
|
||||||
|
|
||||||
sInfo = PGPainless.inspectKeyRing(secretKeys);
|
sInfo = PGPainless.inspectKeyRing(secretKeys);
|
||||||
assertNull(sInfo.getSubkeyExpirationDate(subKeyFingerprint));
|
|
||||||
assertNull(sInfo.getPrimaryKeyExpirationDate());
|
assertNull(sInfo.getPrimaryKeyExpirationDate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,224 @@
|
||||||
|
// SPDX-FileCopyrightText: 2020 Paul Schaub <vanitasvitae@fsfe.org>, 2021 Flowcrypt a.s.
|
||||||
|
//
|
||||||
|
// 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.assertThrows;
|
||||||
|
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 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 org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
|
|
||||||
|
public class ChangePrimaryUserIdAndExpirationDatesTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generateA_primaryB_revokeA_cantSecondaryA()
|
||||||
|
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InterruptedException {
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||||
|
.modernKeyRing("A", null);
|
||||||
|
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
|
||||||
|
|
||||||
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
assertFalse(info.isHardRevoked("A"));
|
||||||
|
assertFalse(info.isHardRevoked("B"));
|
||||||
|
assertIsPrimaryUserId("A", info);
|
||||||
|
assertIsNotValid("B", info);
|
||||||
|
assertIsNotPrimaryUserId("B", info);
|
||||||
|
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||||
|
.addPrimaryUserId("B", protector)
|
||||||
|
.done();
|
||||||
|
info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
|
||||||
|
assertIsPrimaryUserId("B", info);
|
||||||
|
assertIsNotPrimaryUserId("A", info);
|
||||||
|
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||||
|
.revokeUserId("A", protector) // hard revoke A
|
||||||
|
.done();
|
||||||
|
info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
|
||||||
|
assertTrue(info.isHardRevoked("A"));
|
||||||
|
assertFalse(info.isHardRevoked("B"));
|
||||||
|
assertIsPrimaryUserId("B", info);
|
||||||
|
assertIsNotValid("A", info);
|
||||||
|
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
PGPSecretKeyRing finalSecretKeys = secretKeys;
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
PGPainless.modifyKeyRing(finalSecretKeys).addUserId("A", protector));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generateA_primaryExpire_isExpired()
|
||||||
|
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InterruptedException {
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||||
|
.modernKeyRing("A", null);
|
||||||
|
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
|
||||||
|
|
||||||
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
assertIsPrimaryUserId("A", info);
|
||||||
|
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||||
|
.setExpirationDate(new Date(), protector) // expire the whole key
|
||||||
|
.done();
|
||||||
|
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
assertFalse(info.isUserIdValid("A")); // is expired by now
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generateA_primaryB_primaryExpire_bIsStillPrimary()
|
||||||
|
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InterruptedException {
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||||
|
.modernKeyRing("A", null);
|
||||||
|
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
|
||||||
|
|
||||||
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
assertIsPrimaryUserId("A", info);
|
||||||
|
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||||
|
.addPrimaryUserId("B", protector)
|
||||||
|
.done();
|
||||||
|
info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
|
||||||
|
assertIsPrimaryUserId("B", info);
|
||||||
|
assertIsNotPrimaryUserId("A", info);
|
||||||
|
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||||
|
.setExpirationDate(new Date(new Date().getTime() + 1000), protector) // expire the whole key in 1 sec
|
||||||
|
.done();
|
||||||
|
|
||||||
|
info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
assertIsValid("A", info);
|
||||||
|
assertIsValid("B", info);
|
||||||
|
assertIsPrimaryUserId("B", info);
|
||||||
|
assertIsNotPrimaryUserId("A", info);
|
||||||
|
|
||||||
|
Thread.sleep(2000);
|
||||||
|
|
||||||
|
info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
assertIsPrimaryUserId("B", info); // B is still primary, even though
|
||||||
|
assertFalse(info.isUserIdValid("A")); // key is expired by now
|
||||||
|
assertFalse(info.isUserIdValid("B"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generateA_expire_certify() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InterruptedException {
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("A", null);
|
||||||
|
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
|
||||||
|
|
||||||
|
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||||
|
.setExpirationDate(new Date(new Date().getTime() + 1000), protector)
|
||||||
|
.done();
|
||||||
|
|
||||||
|
Thread.sleep(2000);
|
||||||
|
|
||||||
|
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||||
|
.setExpirationDate(new Date(new Date().getTime() + 2000), protector)
|
||||||
|
.done();
|
||||||
|
|
||||||
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
assertIsValid("A", info);
|
||||||
|
assertIsPrimaryUserId("A", info);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void generateA_expire_primaryB_expire_isPrimaryB()
|
||||||
|
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InterruptedException, IOException {
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("A", null);
|
||||||
|
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
|
||||||
|
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||||
|
.setExpirationDate(new Date(), protector)
|
||||||
|
.done();
|
||||||
|
|
||||||
|
Thread.sleep(2000);
|
||||||
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
|
||||||
|
assertIsPrimaryUserId("A", info);
|
||||||
|
assertIsNotValid("A", info);
|
||||||
|
|
||||||
|
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||||
|
.addPrimaryUserId("B", protector)
|
||||||
|
.done();
|
||||||
|
|
||||||
|
info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
|
||||||
|
assertIsPrimaryUserId("B", info);
|
||||||
|
assertIsValid("B", info);
|
||||||
|
assertIsNotValid("A", info); // A is still expired
|
||||||
|
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||||
|
.setExpirationDate(new Date(new Date().getTime() + 10000), protector)
|
||||||
|
.done();
|
||||||
|
|
||||||
|
Thread.sleep(1000);
|
||||||
|
info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
|
||||||
|
assertIsValid("B", info);
|
||||||
|
assertIsNotValid("A", info); // A was expired when the expiration date was changed, so it was not re-certified
|
||||||
|
assertIsPrimaryUserId("B", info);
|
||||||
|
|
||||||
|
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||||
|
.addUserId("A", protector) // re-certify A as non-primary user-id
|
||||||
|
.done();
|
||||||
|
info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
|
||||||
|
assertIsValid("B", info);
|
||||||
|
assertIsValid("A", info);
|
||||||
|
assertIsPrimaryUserId("B", info);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertIsPrimaryUserId(String userId, KeyRingInfo info) {
|
||||||
|
assertEquals(userId, info.getPrimaryUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertIsNotPrimaryUserId(String userId, KeyRingInfo info) {
|
||||||
|
PGPSignature signature = info.getLatestUserIdCertification(userId);
|
||||||
|
if (signature == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertFalse(signature.getHashedSubPackets().isPrimaryUserID());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertIsValid(String userId, KeyRingInfo info) {
|
||||||
|
assertTrue(info.isUserIdValid(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertIsNotValid(String userId, KeyRingInfo info) {
|
||||||
|
assertFalse(info.isUserIdValid(userId));
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
@ -19,7 +20,6 @@ import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
|
||||||
import org.junit.jupiter.api.TestTemplate;
|
import org.junit.jupiter.api.TestTemplate;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
|
||||||
import org.pgpainless.key.protection.UnprotectedKeysProtector;
|
import org.pgpainless.key.protection.UnprotectedKeysProtector;
|
||||||
import org.pgpainless.util.TestAllImplementations;
|
import org.pgpainless.util.TestAllImplementations;
|
||||||
|
|
||||||
|
@ -32,18 +32,22 @@ public class OldSignatureSubpacketsArePreservedOnNewSig {
|
||||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||||
.simpleEcKeyRing("Alice <alice@wonderland.lit>");
|
.simpleEcKeyRing("Alice <alice@wonderland.lit>");
|
||||||
|
|
||||||
OpenPgpV4Fingerprint subkeyFingerprint = new OpenPgpV4Fingerprint(PGPainless.inspectKeyRing(secretKeys).getPublicKeys().get(1));
|
PGPSignature oldSignature = PGPainless.inspectKeyRing(secretKeys).getLatestUserIdCertification("Alice <alice@wonderland.lit>");
|
||||||
|
|
||||||
PGPSignature oldSignature = PGPainless.inspectKeyRing(secretKeys).getCurrentSubkeyBindingSignature(subkeyFingerprint.getKeyId());
|
|
||||||
PGPSignatureSubpacketVector oldPackets = oldSignature.getHashedSubPackets();
|
PGPSignatureSubpacketVector oldPackets = oldSignature.getHashedSubPackets();
|
||||||
|
|
||||||
assertEquals(0, oldPackets.getKeyExpirationTime());
|
assertEquals(0, oldPackets.getKeyExpirationTime());
|
||||||
|
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
|
Date now = new Date();
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.setTime(now);
|
||||||
|
calendar.add(Calendar.DATE, 5);
|
||||||
|
Date expiration = calendar.getTime(); // in 5 days
|
||||||
|
|
||||||
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||||
.setExpirationDate(subkeyFingerprint, new Date(), new UnprotectedKeysProtector())
|
.setExpirationDate(expiration, new UnprotectedKeysProtector())
|
||||||
.done();
|
.done();
|
||||||
PGPSignature newSignature = PGPainless.inspectKeyRing(secretKeys).getCurrentSubkeyBindingSignature(subkeyFingerprint.getKeyId());
|
PGPSignature newSignature = PGPainless.inspectKeyRing(secretKeys).getLatestUserIdCertification("Alice <alice@wonderland.lit>");
|
||||||
PGPSignatureSubpacketVector newPackets = newSignature.getHashedSubPackets();
|
PGPSignatureSubpacketVector newPackets = newSignature.getHashedSubPackets();
|
||||||
|
|
||||||
assertNotEquals(0, newPackets.getKeyExpirationTime());
|
assertNotEquals(0, newPackets.getKeyExpirationTime());
|
||||||
|
|
|
@ -5,18 +5,14 @@
|
||||||
package org.pgpainless.key.modification;
|
package org.pgpainless.key.modification;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.junit.JUtils;
|
import org.junit.JUtils;
|
||||||
import org.junit.jupiter.api.TestTemplate;
|
import org.junit.jupiter.api.TestTemplate;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
|
||||||
import org.pgpainless.key.info.KeyRingInfo;
|
import org.pgpainless.key.info.KeyRingInfo;
|
||||||
import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditorInterface;
|
import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditorInterface;
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
|
@ -106,24 +102,15 @@ public class RevokeKeyWithoutPreferredAlgorithmsOnPrimaryKey {
|
||||||
throws IOException, PGPException {
|
throws IOException, PGPException {
|
||||||
Date expirationDate = DateUtil.parseUTCDate(DateUtil.formatUTCDate(new Date()));
|
Date expirationDate = DateUtil.parseUTCDate(DateUtil.formatUTCDate(new Date()));
|
||||||
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(KEY);
|
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(KEY);
|
||||||
List<OpenPgpV4Fingerprint> fingerprintList = new ArrayList<>();
|
|
||||||
for (PGPSecretKey secretKey : secretKeys) {
|
|
||||||
fingerprintList.add(new OpenPgpV4Fingerprint(secretKey));
|
|
||||||
}
|
|
||||||
SecretKeyRingProtector protector = new UnprotectedKeysProtector();
|
SecretKeyRingProtector protector = new UnprotectedKeysProtector();
|
||||||
|
|
||||||
SecretKeyRingEditorInterface modify = PGPainless.modifyKeyRing(secretKeys)
|
SecretKeyRingEditorInterface modify = PGPainless.modifyKeyRing(secretKeys)
|
||||||
.setExpirationDate(expirationDate, protector);
|
.setExpirationDate(expirationDate, protector);
|
||||||
for (int i = 1; i < fingerprintList.size(); i++) {
|
|
||||||
modify.setExpirationDate(fingerprintList.get(i), expirationDate, protector);
|
|
||||||
}
|
|
||||||
secretKeys = modify.done();
|
secretKeys = modify.done();
|
||||||
|
|
||||||
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
|
||||||
JUtils.assertDateEquals(expirationDate, info.getPrimaryKeyExpirationDate());
|
JUtils.assertDateEquals(expirationDate, info.getPrimaryKeyExpirationDate());
|
||||||
for (OpenPgpV4Fingerprint fingerprint : fingerprintList) {
|
|
||||||
JUtils.assertDateEquals(expirationDate, info.getSubkeyExpirationDate(fingerprint));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,8 @@ public class SignatureSubpacketsUtilTest {
|
||||||
.setExpirationDate(expiration, SecretKeyRingProtector.unprotectedKeys())
|
.setExpirationDate(expiration, SecretKeyRingProtector.unprotectedKeys())
|
||||||
.done();
|
.done();
|
||||||
|
|
||||||
PGPSignature expirationSig = SignaturePicker.pickCurrentUserIdCertificationSignature(secretKeys, "Expire", Policy.getInstance(), new Date());
|
PGPSignature expirationSig = SignaturePicker.pickCurrentUserIdCertificationSignature(
|
||||||
|
secretKeys, "Expire", Policy.getInstance(), new Date());
|
||||||
PGPPublicKey notTheRightKey = PGPainless.inspectKeyRing(secretKeys).getSigningSubkeys().get(0);
|
PGPPublicKey notTheRightKey = PGPainless.inspectKeyRing(secretKeys).getSigningSubkeys().get(0);
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class, () ->
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
|
Loading…
Reference in a new issue