diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilder.java index 537bc255..88ed6ecd 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilder.java @@ -305,9 +305,11 @@ public class KeyRingBuilder implements KeyRingBuilderInterface { // Create raw Key Pair KeyPair keyPair = certKeyGenerator.generateKeyPair(); + Date keyCreationDate = spec.getKeyCreationDate() != null ? spec.getKeyCreationDate() : new Date(); + // Form PGP key pair PGPKeyPair pgpKeyPair = ImplementationFactory.getInstance() - .getPGPKeyPair(type.getAlgorithm(), keyPair, new Date()); + .getPGPKeyPair(type.getAlgorithm(), keyPair, keyCreationDate); return pgpKeyPair; } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java index bd5a5063..63645edd 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java @@ -5,6 +5,7 @@ package org.pgpainless.key.generation; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; import org.pgpainless.algorithm.KeyFlag; @@ -12,18 +13,23 @@ import org.pgpainless.key.generation.type.KeyType; import org.pgpainless.signature.subpackets.SignatureSubpackets; import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper; +import java.util.Date; + public class KeySpec { private final KeyType keyType; private final SignatureSubpackets subpacketGenerator; private final boolean inheritedSubPackets; + private final Date keyCreationDate; KeySpec(@Nonnull KeyType type, @Nonnull SignatureSubpackets subpacketGenerator, - boolean inheritedSubPackets) { + boolean inheritedSubPackets, + @Nullable Date keyCreationDate) { this.keyType = type; this.subpacketGenerator = subpacketGenerator; this.inheritedSubPackets = inheritedSubPackets; + this.keyCreationDate = keyCreationDate; } @Nonnull @@ -45,6 +51,11 @@ public class KeySpec { return inheritedSubPackets; } + @Nullable + public Date getKeyCreationDate() { + return keyCreationDate; + } + public static KeySpecBuilder getBuilder(KeyType type, KeyFlag flag, KeyFlag... flags) { return new KeySpecBuilder(type, flag, flags); } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java index 07b53383..2d7010d8 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilder.java @@ -5,6 +5,7 @@ package org.pgpainless.key.generation; import java.util.Arrays; +import java.util.Date; import java.util.LinkedHashSet; import java.util.Set; import javax.annotation.Nonnull; @@ -31,6 +32,7 @@ public class KeySpecBuilder implements KeySpecBuilderInterface { private Set preferredCompressionAlgorithms = algorithmSuite.getCompressionAlgorithms(); private Set preferredHashAlgorithms = algorithmSuite.getHashAlgorithms(); private Set preferredSymmetricAlgorithms = algorithmSuite.getSymmetricKeyAlgorithms(); + private Date keyCreationDate; KeySpecBuilder(@Nonnull KeyType type, KeyFlag flag, KeyFlag... flags) { if (flag == null) { @@ -66,6 +68,11 @@ public class KeySpecBuilder implements KeySpecBuilderInterface { return this; } + @Override + public KeySpecBuilder setKeyCreationDate(@Nonnull Date creationDate) { + this.keyCreationDate = creationDate; + return this; + } @Override public KeySpec build() { @@ -75,6 +82,6 @@ public class KeySpecBuilder implements KeySpecBuilderInterface { this.hashedSubpackets.setPreferredSymmetricKeyAlgorithms(preferredSymmetricAlgorithms); this.hashedSubpackets.setFeatures(Feature.MODIFICATION_DETECTION); - return new KeySpec(type, (SignatureSubpackets) hashedSubpackets, false); + return new KeySpec(type, (SignatureSubpackets) hashedSubpackets, false, keyCreationDate); } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilderInterface.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilderInterface.java index cd68e8b4..4a99bc8d 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilderInterface.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpecBuilderInterface.java @@ -10,6 +10,8 @@ import org.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; +import java.util.Date; + public interface KeySpecBuilderInterface { KeySpecBuilder overridePreferredCompressionAlgorithms(@Nonnull CompressionAlgorithm... compressionAlgorithms); @@ -18,5 +20,7 @@ public interface KeySpecBuilderInterface { KeySpecBuilder overridePreferredSymmetricKeyAlgorithms(@Nonnull SymmetricKeyAlgorithm... preferredSymmetricKeyAlgorithms); + KeySpecBuilder setKeyCreationDate(@Nonnull Date creationDate); + KeySpec build(); } diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateKeyWithCustomCreationDateTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateKeyWithCustomCreationDateTest.java new file mode 100644 index 00000000..e77602dd --- /dev/null +++ b/pgpainless-core/src/test/java/org/pgpainless/key/generation/GenerateKeyWithCustomCreationDateTest.java @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.key.generation; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.junit.JUtils; +import org.junit.jupiter.api.Test; +import org.pgpainless.PGPainless; +import org.pgpainless.algorithm.KeyFlag; +import org.pgpainless.key.generation.type.KeyType; +import org.pgpainless.key.generation.type.eddsa.EdDSACurve; +import org.pgpainless.key.generation.type.xdh.XDHSpec; +import org.pgpainless.util.DateUtil; + +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.util.Date; +import java.util.Iterator; + +public class GenerateKeyWithCustomCreationDateTest { + + @Test + public void generateKeyWithCustomCreationDateTest() + throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { + Date creationDate = DateUtil.parseUTCDate("2018-06-11 14:12:09 UTC"); + PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing() + .addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)) + .setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) + .setKeyCreationDate(creationDate)) // primary key with custom creation time + .addUserId("Alice") + .build(); + + Iterator iterator = secretKeys.iterator(); + PGPPublicKey primaryKey = iterator.next().getPublicKey(); + PGPPublicKey subkey = iterator.next().getPublicKey(); + + JUtils.assertDateEquals(creationDate, primaryKey.getCreationTime()); + // subkey has no creation date override, so it was generated "just now" + JUtils.assertDateNotEquals(creationDate, subkey.getCreationTime()); + } +}