mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-23 03:17:58 +01:00
Merge branch 'key_generator_rework'
This commit is contained in:
commit
1aa6541766
28 changed files with 489 additions and 779 deletions
33
README.md
33
README.md
|
@ -75,26 +75,19 @@ There are some predefined key archetypes, but it is possible to fully customize
|
|||
|
||||
// Customized key
|
||||
PGPSecretKeyRing keyRing = PGPainless.generateKeyRing()
|
||||
.withSubKey(
|
||||
KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256))
|
||||
.withKeyFlags(KeyFlag.SIGN_DATA)
|
||||
.withDetailedConfiguration()
|
||||
.withDefaultSymmetricAlgorithms()
|
||||
.withDefaultHashAlgorithms()
|
||||
.withPreferredCompressionAlgorithms(CompressionAlgorithm.ZLIB)
|
||||
.withFeature(Feature.MODIFICATION_DETECTION)
|
||||
.done()
|
||||
).withSubKey(
|
||||
KeySpec.getBuilder(ECDH.fromCurve(EllipticCurve._P256))
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)
|
||||
.withDefaultAlgorithms()
|
||||
).withMasterKey(
|
||||
KeySpec.getBuilder(RSA.withLength(RsaLength._8192))
|
||||
.withKeyFlags(KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER)
|
||||
.withDefaultAlgorithms()
|
||||
).withPrimaryUserId("Juliet <juliet@montague.lit>")
|
||||
.withAdditionalUserId("xmpp:juliet@capulet.lit")
|
||||
.withPassphrase("romeo_oh_Romeo<3")
|
||||
.setPrimaryKey(KeySpec.getBuilder(
|
||||
RSA.withLength(RsaLength._8192),
|
||||
KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER))
|
||||
.addSubkey(
|
||||
KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256), KeyFlag.SIGN_DATA)
|
||||
.overrideCompressionAlgorithms(CompressionAlgorithm.ZLIB)
|
||||
).addSubkey(
|
||||
KeySpec.getBuilder(
|
||||
ECDH.fromCurve(EllipticCurve._P256),
|
||||
KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)
|
||||
).addUserId("Juliet <juliet@montague.lit>")
|
||||
.addUserId("xmpp:juliet@capulet.lit")
|
||||
.setPassphrase("romeo_oh_Romeo<3")
|
||||
.build();
|
||||
```
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package org.pgpainless.algorithm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
|
@ -45,9 +44,9 @@ public class AlgorithmSuite {
|
|||
CompressionAlgorithm.UNCOMPRESSED)
|
||||
);
|
||||
|
||||
private Set<SymmetricKeyAlgorithm> symmetricKeyAlgorithms;
|
||||
private Set<HashAlgorithm> hashAlgorithms;
|
||||
private Set<CompressionAlgorithm> compressionAlgorithms;
|
||||
private final Set<SymmetricKeyAlgorithm> symmetricKeyAlgorithms;
|
||||
private final Set<HashAlgorithm> hashAlgorithms;
|
||||
private final Set<CompressionAlgorithm> compressionAlgorithms;
|
||||
|
||||
public AlgorithmSuite(List<SymmetricKeyAlgorithm> symmetricKeyAlgorithms,
|
||||
List<HashAlgorithm> hashAlgorithms,
|
||||
|
@ -57,57 +56,18 @@ public class AlgorithmSuite {
|
|||
this.compressionAlgorithms = Collections.unmodifiableSet(new LinkedHashSet<>(compressionAlgorithms));
|
||||
}
|
||||
|
||||
public void setSymmetricKeyAlgorithms(List<SymmetricKeyAlgorithm> symmetricKeyAlgorithms) {
|
||||
this.symmetricKeyAlgorithms = Collections.unmodifiableSet(new LinkedHashSet<>(symmetricKeyAlgorithms));
|
||||
}
|
||||
|
||||
public Set<SymmetricKeyAlgorithm> getSymmetricKeyAlgorithms() {
|
||||
return new LinkedHashSet<>(symmetricKeyAlgorithms);
|
||||
}
|
||||
|
||||
public int[] getSymmetricKeyAlgorithmIds() {
|
||||
int[] array = new int[symmetricKeyAlgorithms.size()];
|
||||
List<SymmetricKeyAlgorithm> list = new ArrayList<>(getSymmetricKeyAlgorithms());
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
array[i] = list.get(i).getAlgorithmId();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public void setHashAlgorithms(List<HashAlgorithm> hashAlgorithms) {
|
||||
this.hashAlgorithms = Collections.unmodifiableSet(new LinkedHashSet<>(hashAlgorithms));
|
||||
}
|
||||
|
||||
public Set<HashAlgorithm> getHashAlgorithms() {
|
||||
return new LinkedHashSet<>(hashAlgorithms);
|
||||
}
|
||||
|
||||
public int[] getHashAlgorithmIds() {
|
||||
int[] array = new int[hashAlgorithms.size()];
|
||||
List<HashAlgorithm> list = new ArrayList<>(getHashAlgorithms());
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
array[i] = list.get(i).getAlgorithmId();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public void setCompressionAlgorithms(List<CompressionAlgorithm> compressionAlgorithms) {
|
||||
this.compressionAlgorithms = Collections.unmodifiableSet(new LinkedHashSet<>(compressionAlgorithms));
|
||||
}
|
||||
|
||||
public Set<CompressionAlgorithm> getCompressionAlgorithms() {
|
||||
return new LinkedHashSet<>(compressionAlgorithms);
|
||||
}
|
||||
|
||||
public int[] getCompressionAlgorithmIds() {
|
||||
int[] array = new int[compressionAlgorithms.size()];
|
||||
List<CompressionAlgorithm> list = new ArrayList<>(getCompressionAlgorithms());
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
array[i] = list.get(i).getAlgorithmId();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public static AlgorithmSuite getDefaultAlgorithmSuite() {
|
||||
return defaultAlgorithmSuite;
|
||||
}
|
||||
|
|
|
@ -466,7 +466,7 @@ public final class DecryptionStreamFactory {
|
|||
}
|
||||
|
||||
private void throwIfAlgorithmIsRejected(SymmetricKeyAlgorithm algorithm) throws UnacceptableAlgorithmException {
|
||||
if (!PGPainless.getPolicy().getSymmetricKeyDecryptionAlgoritmPolicy().isAcceptable(algorithm)) {
|
||||
if (!PGPainless.getPolicy().getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(algorithm)) {
|
||||
throw new UnacceptableAlgorithmException("Data is "
|
||||
+ (algorithm == SymmetricKeyAlgorithm.NULL ? "unencrypted" : "encrypted with symmetric algorithm " + algorithm) + " which is not acceptable as per PGPainless' policy.\n" +
|
||||
"To mark this algorithm as acceptable, use PGPainless.getPolicy().setSymmetricKeyDecryptionAlgorithmPolicy().");
|
||||
|
|
|
@ -61,14 +61,18 @@ import org.pgpainless.provider.ProviderFactory;
|
|||
import org.pgpainless.util.Passphrase;
|
||||
import org.pgpainless.signature.subpackets.SignatureSubpacketGeneratorUtil;
|
||||
|
||||
public class KeyRingBuilder implements KeyRingBuilderInterface {
|
||||
public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
|
||||
|
||||
private final Charset UTF8 = Charset.forName("UTF-8");
|
||||
|
||||
private final List<KeySpec> keySpecs = new ArrayList<>();
|
||||
private String userId;
|
||||
private final Set<String> additionalUserIds = new LinkedHashSet<>();
|
||||
private Passphrase passphrase;
|
||||
private PGPSignatureGenerator signatureGenerator;
|
||||
private PGPDigestCalculator digestCalculator;
|
||||
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 Date expirationDate = null;
|
||||
|
||||
/**
|
||||
|
@ -126,18 +130,14 @@ public class KeyRingBuilder implements KeyRingBuilderInterface {
|
|||
*/
|
||||
public PGPSecretKeyRing simpleRsaKeyRing(@Nonnull String userId, @Nonnull RsaLength length, String password)
|
||||
throws PGPException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
WithAdditionalUserIdOrPassphrase builder = this
|
||||
.withPrimaryKey(
|
||||
KeySpec.getBuilder(KeyType.RSA(length))
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryUserId(userId);
|
||||
KeyRingBuilder builder = new KeyRingBuilder()
|
||||
.setPrimaryKey(KeySpec.getBuilder(KeyType.RSA(length), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS))
|
||||
.addUserId(userId);
|
||||
|
||||
if (password == null) {
|
||||
return builder.withoutPassphrase().build();
|
||||
} else {
|
||||
return builder.withPassphrase(new Passphrase(password.toCharArray())).build();
|
||||
if (!isNullOrEmpty(password)) {
|
||||
builder.setPassphrase(Passphrase.fromPassword(password));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -195,22 +195,15 @@ public class KeyRingBuilder implements KeyRingBuilderInterface {
|
|||
*/
|
||||
public PGPSecretKeyRing simpleEcKeyRing(@Nonnull String userId, String password)
|
||||
throws PGPException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
WithAdditionalUserIdOrPassphrase builder = this
|
||||
.withSubKey(
|
||||
KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519))
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryKey(
|
||||
KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryUserId(userId);
|
||||
KeyRingBuilder builder = new KeyRingBuilder()
|
||||
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA))
|
||||
.addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS))
|
||||
.addUserId(userId);
|
||||
|
||||
if (password == null) {
|
||||
return builder.withoutPassphrase().build();
|
||||
} else {
|
||||
return builder.withPassphrase(new Passphrase(password.toCharArray())).build();
|
||||
if (!isNullOrEmpty(password)) {
|
||||
builder.setPassphrase(Passphrase.fromPassword(password));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -223,40 +216,59 @@ public class KeyRingBuilder implements KeyRingBuilderInterface {
|
|||
*/
|
||||
public PGPSecretKeyRing modernKeyRing(String userId, String password)
|
||||
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException {
|
||||
WithAdditionalUserIdOrPassphrase builder = this
|
||||
.withSubKey(
|
||||
KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519))
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS)
|
||||
.withDefaultAlgorithms())
|
||||
.withSubKey(
|
||||
KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||
.withKeyFlags(KeyFlag.SIGN_DATA)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryKey(
|
||||
KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryUserId(userId);
|
||||
|
||||
if (password == null) {
|
||||
return builder.withoutPassphrase().build();
|
||||
} else {
|
||||
return builder.withPassphrase(new Passphrase(password.toCharArray())).build();
|
||||
KeyRingBuilder builder = new KeyRingBuilder()
|
||||
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER))
|
||||
.addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS))
|
||||
.addSubkey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA))
|
||||
.addUserId(userId);
|
||||
if (!isNullOrEmpty(password)) {
|
||||
builder.setPassphrase(Passphrase.fromPassword(password));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyRingBuilderInterface withSubKey(@Nonnull KeySpec type) {
|
||||
KeyRingBuilder.this.keySpecs.add(type);
|
||||
public KeyRingBuilder setPrimaryKey(@Nonnull KeySpec keySpec) {
|
||||
verifyMasterKeyCanCertify(keySpec);
|
||||
this.primaryKeySpec = keySpec;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WithPrimaryUserId withPrimaryKey(@Nonnull KeySpec spec) {
|
||||
verifyMasterKeyCanCertify(spec);
|
||||
public KeyRingBuilder addSubkey(@Nonnull KeySpec keySpec) {
|
||||
this.subkeySpecs.add(keySpec);
|
||||
return this;
|
||||
}
|
||||
|
||||
KeyRingBuilder.this.keySpecs.add(0, spec);
|
||||
return new WithPrimaryUserIdImpl();
|
||||
@Override
|
||||
public KeyRingBuilder addUserId(@Nonnull String userId) {
|
||||
this.userIds.add(userId.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyRingBuilder addUserId(@Nonnull byte[] userId) {
|
||||
return addUserId(new String(userId, UTF8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyRingBuilder setExpirationDate(@Nonnull Date expirationDate) {
|
||||
Date now = new Date();
|
||||
if (now.after(expirationDate)) {
|
||||
throw new IllegalArgumentException("Expiration date must be in the future.");
|
||||
}
|
||||
this.expirationDate = expirationDate;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyRingBuilder setPassphrase(@Nonnull Passphrase passphrase) {
|
||||
this.passphrase = passphrase;
|
||||
return this;
|
||||
}
|
||||
|
||||
private static boolean isNullOrEmpty(String password) {
|
||||
return password == null || password.trim().isEmpty();
|
||||
}
|
||||
|
||||
private void verifyMasterKeyCanCertify(KeySpec spec) {
|
||||
|
@ -276,196 +288,139 @@ public class KeyRingBuilder implements KeyRingBuilderInterface {
|
|||
return keySpec.getKeyType().canCertify();
|
||||
}
|
||||
|
||||
class WithPrimaryUserIdImpl implements WithPrimaryUserId {
|
||||
@Override
|
||||
public PGPSecretKeyRing build() throws NoSuchAlgorithmException, PGPException,
|
||||
InvalidAlgorithmParameterException {
|
||||
if (userIds.isEmpty()) {
|
||||
throw new IllegalStateException("At least one user-id is required.");
|
||||
}
|
||||
digestCalculator = buildDigestCalculator();
|
||||
secretKeyEncryptor = buildSecretKeyEncryptor();
|
||||
PBESecretKeyDecryptor secretKeyDecryptor = buildSecretKeyDecryptor();
|
||||
|
||||
@Override
|
||||
public WithAdditionalUserIdOrPassphrase withPrimaryUserId(@Nonnull String userId) {
|
||||
KeyRingBuilder.this.userId = userId.trim();
|
||||
return new WithAdditionalUserIdOrPassphraseImpl();
|
||||
if (passphrase != null) {
|
||||
passphrase.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WithAdditionalUserIdOrPassphrase withPrimaryUserId(@Nonnull byte[] userId) {
|
||||
return withPrimaryUserId(new String(userId, UTF8));
|
||||
// Generate Primary Key
|
||||
PGPKeyPair certKey = generateKeyPair(primaryKeySpec);
|
||||
PGPContentSignerBuilder signer = buildContentSigner(certKey);
|
||||
signatureGenerator = new PGPSignatureGenerator(signer);
|
||||
PGPSignatureSubpacketGenerator hashedSubPacketGenerator = primaryKeySpec.getSubpacketGenerator();
|
||||
hashedSubPacketGenerator.setPrimaryUserID(false, true);
|
||||
if (expirationDate != null) {
|
||||
SignatureSubpacketGeneratorUtil.setExpirationDateInSubpacketGenerator(
|
||||
expirationDate, new Date(), hashedSubPacketGenerator);
|
||||
}
|
||||
PGPSignatureSubpacketVector hashedSubPackets = hashedSubPacketGenerator.generate();
|
||||
|
||||
// Generator which the user can get the key pair from
|
||||
PGPKeyRingGenerator ringGenerator = buildRingGenerator(certKey, signer, hashedSubPackets);
|
||||
|
||||
addSubKeys(certKey, ringGenerator);
|
||||
|
||||
// Generate secret key ring with only primary user id
|
||||
PGPSecretKeyRing secretKeyRing = ringGenerator.generateSecretKeyRing();
|
||||
|
||||
Iterator<PGPSecretKey> secretKeys = secretKeyRing.getSecretKeys();
|
||||
|
||||
// Attempt to add additional user-ids to the primary public key
|
||||
PGPPublicKey primaryPubKey = secretKeys.next().getPublicKey();
|
||||
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKeyRing.getSecretKey(), secretKeyDecryptor);
|
||||
Iterator<String> additionalUserIds = userIds.iterator();
|
||||
additionalUserIds.next(); // Skip primary user id
|
||||
while (additionalUserIds.hasNext()) {
|
||||
String additionalUserId = additionalUserIds.next();
|
||||
signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.getCode(), privateKey);
|
||||
PGPSignature additionalUserIdSignature =
|
||||
signatureGenerator.generateCertification(additionalUserId, primaryPubKey);
|
||||
primaryPubKey = PGPPublicKey.addCertification(primaryPubKey,
|
||||
additionalUserId, additionalUserIdSignature);
|
||||
}
|
||||
|
||||
// "reassemble" secret key ring with modified primary key
|
||||
PGPSecretKey primarySecKey = new PGPSecretKey(
|
||||
privateKey,
|
||||
primaryPubKey, digestCalculator, true, secretKeyEncryptor);
|
||||
List<PGPSecretKey> secretKeyList = new ArrayList<>();
|
||||
secretKeyList.add(primarySecKey);
|
||||
while (secretKeys.hasNext()) {
|
||||
secretKeyList.add(secretKeys.next());
|
||||
}
|
||||
secretKeyRing = new PGPSecretKeyRing(secretKeyList);
|
||||
|
||||
return secretKeyRing;
|
||||
}
|
||||
|
||||
private PGPKeyRingGenerator buildRingGenerator(PGPKeyPair certKey,
|
||||
PGPContentSignerBuilder signer,
|
||||
PGPSignatureSubpacketVector hashedSubPackets)
|
||||
throws PGPException {
|
||||
String primaryUserId = userIds.iterator().next();
|
||||
return new PGPKeyRingGenerator(
|
||||
SignatureType.POSITIVE_CERTIFICATION.getCode(), certKey,
|
||||
primaryUserId, digestCalculator,
|
||||
hashedSubPackets, null, signer, secretKeyEncryptor);
|
||||
}
|
||||
|
||||
private void addSubKeys(PGPKeyPair primaryKey, PGPKeyRingGenerator ringGenerator)
|
||||
throws NoSuchAlgorithmException, PGPException, InvalidAlgorithmParameterException {
|
||||
for (KeySpec subKeySpec : subkeySpecs) {
|
||||
PGPKeyPair subKey = generateKeyPair(subKeySpec);
|
||||
if (subKeySpec.isInheritedSubPackets()) {
|
||||
ringGenerator.addSubKey(subKey);
|
||||
} else {
|
||||
PGPSignatureSubpacketVector hashedSubpackets = subKeySpec.getSubpackets();
|
||||
try {
|
||||
hashedSubpackets = addPrimaryKeyBindingSignatureIfNecessary(primaryKey, subKey, hashedSubpackets);
|
||||
} catch (IOException e) {
|
||||
throw new PGPException("Exception while adding primary key binding signature to signing subkey", e);
|
||||
}
|
||||
ringGenerator.addSubKey(subKey, hashedSubpackets, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WithAdditionalUserIdOrPassphraseImpl implements WithAdditionalUserIdOrPassphrase {
|
||||
|
||||
@Override
|
||||
public WithAdditionalUserIdOrPassphrase setExpirationDate(@Nonnull Date expirationDate) {
|
||||
Date now = new Date();
|
||||
if (now.after(expirationDate)) {
|
||||
throw new IllegalArgumentException("Expiration date must be in the future.");
|
||||
}
|
||||
KeyRingBuilder.this.expirationDate = expirationDate;
|
||||
return this;
|
||||
private PGPSignatureSubpacketVector addPrimaryKeyBindingSignatureIfNecessary(PGPKeyPair primaryKey, PGPKeyPair subKey, PGPSignatureSubpacketVector hashedSubpackets) throws PGPException, IOException {
|
||||
int keyFlagMask = hashedSubpackets.getKeyFlags();
|
||||
if (!KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.SIGN_DATA) && !KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.CERTIFY_OTHER)) {
|
||||
return hashedSubpackets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WithAdditionalUserIdOrPassphrase withAdditionalUserId(@Nonnull String userId) {
|
||||
String trimmed = userId.trim();
|
||||
if (KeyRingBuilder.this.userId.equals(trimmed)) {
|
||||
throw new IllegalArgumentException("Additional user-id MUST NOT be equal to primary user-id.");
|
||||
}
|
||||
KeyRingBuilder.this.additionalUserIds.add(trimmed);
|
||||
return this;
|
||||
}
|
||||
PGPSignatureGenerator bindingSignatureGenerator = new PGPSignatureGenerator(buildContentSigner(subKey));
|
||||
bindingSignatureGenerator.init(SignatureType.PRIMARYKEY_BINDING.getCode(), subKey.getPrivateKey());
|
||||
PGPSignature primaryKeyBindingSig = bindingSignatureGenerator.generateCertification(primaryKey.getPublicKey(), subKey.getPublicKey());
|
||||
PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator(hashedSubpackets);
|
||||
subpacketGenerator.addEmbeddedSignature(false, primaryKeyBindingSig);
|
||||
return subpacketGenerator.generate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WithAdditionalUserIdOrPassphrase withAdditionalUserId(@Nonnull byte[] userId) {
|
||||
return withAdditionalUserId(new String(userId, UTF8));
|
||||
}
|
||||
private PGPContentSignerBuilder buildContentSigner(PGPKeyPair certKey) {
|
||||
HashAlgorithm hashAlgorithm = PGPainless.getPolicy().getSignatureHashAlgorithmPolicy().defaultHashAlgorithm();
|
||||
return ImplementationFactory.getInstance().getPGPContentSignerBuilder(
|
||||
certKey.getPublicKey().getAlgorithm(),
|
||||
hashAlgorithm.getAlgorithmId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Build withPassphrase(@Nonnull Passphrase passphrase) {
|
||||
KeyRingBuilder.this.passphrase = passphrase;
|
||||
return new BuildImpl();
|
||||
}
|
||||
private PBESecretKeyEncryptor buildSecretKeyEncryptor() {
|
||||
SymmetricKeyAlgorithm keyEncryptionAlgorithm = PGPainless.getPolicy().getSymmetricKeyEncryptionAlgorithmPolicy()
|
||||
.getDefaultSymmetricKeyAlgorithm();
|
||||
PBESecretKeyEncryptor encryptor = passphrase == null || passphrase.isEmpty() ?
|
||||
null : // unencrypted key pair, otherwise AES-256 encrypted
|
||||
ImplementationFactory.getInstance().getPBESecretKeyEncryptor(
|
||||
keyEncryptionAlgorithm, digestCalculator, passphrase);
|
||||
return encryptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Build withoutPassphrase() {
|
||||
KeyRingBuilder.this.passphrase = null;
|
||||
return new BuildImpl();
|
||||
}
|
||||
private PBESecretKeyDecryptor buildSecretKeyDecryptor() throws PGPException {
|
||||
PBESecretKeyDecryptor decryptor = passphrase == null || passphrase.isEmpty() ?
|
||||
null :
|
||||
ImplementationFactory.getInstance().getPBESecretKeyDecryptor(passphrase);
|
||||
return decryptor;
|
||||
}
|
||||
|
||||
class BuildImpl implements Build {
|
||||
|
||||
private PGPSignatureGenerator signatureGenerator;
|
||||
private PGPDigestCalculator digestCalculator;
|
||||
private PBESecretKeyEncryptor secretKeyEncryptor;
|
||||
|
||||
@Override
|
||||
public PGPSecretKeyRing build() throws NoSuchAlgorithmException, PGPException,
|
||||
InvalidAlgorithmParameterException {
|
||||
digestCalculator = buildDigestCalculator();
|
||||
secretKeyEncryptor = buildSecretKeyEncryptor();
|
||||
PBESecretKeyDecryptor secretKeyDecryptor = buildSecretKeyDecryptor();
|
||||
|
||||
if (passphrase != null) {
|
||||
passphrase.clear();
|
||||
}
|
||||
|
||||
// First key is the Master Key
|
||||
KeySpec certKeySpec = keySpecs.remove(0);
|
||||
|
||||
// Generate Master Key
|
||||
PGPKeyPair certKey = generateKeyPair(certKeySpec);
|
||||
PGPContentSignerBuilder signer = buildContentSigner(certKey);
|
||||
signatureGenerator = new PGPSignatureGenerator(signer);
|
||||
PGPSignatureSubpacketGenerator hashedSubPacketGenerator = certKeySpec.getSubpacketGenerator();
|
||||
hashedSubPacketGenerator.setPrimaryUserID(false, true);
|
||||
if (expirationDate != null) {
|
||||
SignatureSubpacketGeneratorUtil.setExpirationDateInSubpacketGenerator(
|
||||
expirationDate, new Date(), hashedSubPacketGenerator);
|
||||
}
|
||||
PGPSignatureSubpacketVector hashedSubPackets = hashedSubPacketGenerator.generate();
|
||||
|
||||
// Generator which the user can get the key pair from
|
||||
PGPKeyRingGenerator ringGenerator = buildRingGenerator(certKey, signer, hashedSubPackets);
|
||||
|
||||
addSubKeys(certKey, ringGenerator);
|
||||
|
||||
// Generate secret key ring with only primary user id
|
||||
PGPSecretKeyRing secretKeyRing = ringGenerator.generateSecretKeyRing();
|
||||
|
||||
Iterator<PGPSecretKey> secretKeys = secretKeyRing.getSecretKeys();
|
||||
|
||||
// Attempt to add additional user-ids to the primary public key
|
||||
PGPPublicKey primaryPubKey = secretKeys.next().getPublicKey();
|
||||
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKeyRing.getSecretKey(), secretKeyDecryptor);
|
||||
for (String additionalUserId : additionalUserIds) {
|
||||
signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.getCode(), privateKey);
|
||||
PGPSignature additionalUserIdSignature =
|
||||
signatureGenerator.generateCertification(additionalUserId, primaryPubKey);
|
||||
primaryPubKey = PGPPublicKey.addCertification(primaryPubKey,
|
||||
additionalUserId, additionalUserIdSignature);
|
||||
}
|
||||
|
||||
// "reassemble" secret key ring with modified primary key
|
||||
PGPSecretKey primarySecKey = new PGPSecretKey(
|
||||
privateKey,
|
||||
primaryPubKey, digestCalculator, true, secretKeyEncryptor);
|
||||
List<PGPSecretKey> secretKeyList = new ArrayList<>();
|
||||
secretKeyList.add(primarySecKey);
|
||||
while (secretKeys.hasNext()) {
|
||||
secretKeyList.add(secretKeys.next());
|
||||
}
|
||||
secretKeyRing = new PGPSecretKeyRing(secretKeyList);
|
||||
|
||||
return secretKeyRing;
|
||||
}
|
||||
|
||||
private PGPKeyRingGenerator buildRingGenerator(PGPKeyPair certKey,
|
||||
PGPContentSignerBuilder signer,
|
||||
PGPSignatureSubpacketVector hashedSubPackets)
|
||||
throws PGPException {
|
||||
return new PGPKeyRingGenerator(
|
||||
SignatureType.POSITIVE_CERTIFICATION.getCode(), certKey,
|
||||
userId, digestCalculator,
|
||||
hashedSubPackets, null, signer, secretKeyEncryptor);
|
||||
}
|
||||
|
||||
private void addSubKeys(PGPKeyPair primaryKey, PGPKeyRingGenerator ringGenerator)
|
||||
throws NoSuchAlgorithmException, PGPException, InvalidAlgorithmParameterException {
|
||||
for (KeySpec subKeySpec : keySpecs) {
|
||||
PGPKeyPair subKey = generateKeyPair(subKeySpec);
|
||||
if (subKeySpec.isInheritedSubPackets()) {
|
||||
ringGenerator.addSubKey(subKey);
|
||||
} else {
|
||||
PGPSignatureSubpacketVector hashedSubpackets = subKeySpec.getSubpackets();
|
||||
try {
|
||||
hashedSubpackets = addPrimaryKeyBindingSignatureIfNecessary(primaryKey, subKey, hashedSubpackets);
|
||||
} catch (IOException e) {
|
||||
throw new PGPException("Exception while adding primary key binding signature to signing subkey", e);
|
||||
}
|
||||
ringGenerator.addSubKey(subKey, hashedSubpackets, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PGPSignatureSubpacketVector addPrimaryKeyBindingSignatureIfNecessary(PGPKeyPair primaryKey, PGPKeyPair subKey, PGPSignatureSubpacketVector hashedSubpackets) throws PGPException, IOException {
|
||||
int keyFlagMask = hashedSubpackets.getKeyFlags();
|
||||
if (!KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.SIGN_DATA) && !KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.CERTIFY_OTHER)) {
|
||||
return hashedSubpackets;
|
||||
}
|
||||
|
||||
PGPSignatureGenerator bindingSignatureGenerator = new PGPSignatureGenerator(buildContentSigner(subKey));
|
||||
bindingSignatureGenerator.init(SignatureType.PRIMARYKEY_BINDING.getCode(), subKey.getPrivateKey());
|
||||
PGPSignature primaryKeyBindingSig = bindingSignatureGenerator.generateCertification(primaryKey.getPublicKey(), subKey.getPublicKey());
|
||||
PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator(hashedSubpackets);
|
||||
subpacketGenerator.addEmbeddedSignature(false, primaryKeyBindingSig);
|
||||
return subpacketGenerator.generate();
|
||||
}
|
||||
|
||||
private PGPContentSignerBuilder buildContentSigner(PGPKeyPair certKey) {
|
||||
HashAlgorithm hashAlgorithm = PGPainless.getPolicy().getSignatureHashAlgorithmPolicy().defaultHashAlgorithm();
|
||||
return ImplementationFactory.getInstance().getPGPContentSignerBuilder(
|
||||
certKey.getPublicKey().getAlgorithm(),
|
||||
hashAlgorithm.getAlgorithmId());
|
||||
}
|
||||
|
||||
private PBESecretKeyEncryptor buildSecretKeyEncryptor() {
|
||||
SymmetricKeyAlgorithm keyEncryptionAlgorithm = PGPainless.getPolicy().getSymmetricKeyEncryptionAlgorithmPolicy()
|
||||
.getDefaultSymmetricKeyAlgorithm();
|
||||
PBESecretKeyEncryptor encryptor = passphrase == null || passphrase.isEmpty() ?
|
||||
null : // unencrypted key pair, otherwise AES-256 encrypted
|
||||
ImplementationFactory.getInstance().getPBESecretKeyEncryptor(
|
||||
keyEncryptionAlgorithm, digestCalculator, passphrase);
|
||||
return encryptor;
|
||||
}
|
||||
|
||||
private PBESecretKeyDecryptor buildSecretKeyDecryptor() throws PGPException {
|
||||
PBESecretKeyDecryptor decryptor = passphrase == null || passphrase.isEmpty() ?
|
||||
null :
|
||||
ImplementationFactory.getInstance().getPBESecretKeyDecryptor(passphrase);
|
||||
return decryptor;
|
||||
}
|
||||
|
||||
private PGPDigestCalculator buildDigestCalculator() throws PGPException {
|
||||
return ImplementationFactory.getInstance().getPGPDigestCalculator(HashAlgorithm.SHA1);
|
||||
}
|
||||
}
|
||||
private PGPDigestCalculator buildDigestCalculator() throws PGPException {
|
||||
return ImplementationFactory.getInstance().getPGPDigestCalculator(HashAlgorithm.SHA1);
|
||||
}
|
||||
|
||||
public static PGPKeyPair generateKeyPair(KeySpec spec)
|
||||
|
|
|
@ -25,50 +25,32 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
|||
import org.pgpainless.key.util.UserId;
|
||||
import org.pgpainless.util.Passphrase;
|
||||
|
||||
public interface KeyRingBuilderInterface {
|
||||
public interface KeyRingBuilderInterface<B extends KeyRingBuilderInterface<B>> {
|
||||
|
||||
KeyRingBuilderInterface withSubKey(@Nonnull KeySpec keySpec);
|
||||
|
||||
WithPrimaryUserId withPrimaryKey(@Nonnull KeySpec keySpec);
|
||||
|
||||
interface WithPrimaryUserId {
|
||||
|
||||
default WithAdditionalUserIdOrPassphrase withPrimaryUserId(@Nonnull UserId userId) {
|
||||
return withPrimaryUserId(userId.toString());
|
||||
}
|
||||
|
||||
WithAdditionalUserIdOrPassphrase withPrimaryUserId(@Nonnull String userId);
|
||||
|
||||
WithAdditionalUserIdOrPassphrase withPrimaryUserId(@Nonnull byte[] userId);
|
||||
B setPrimaryKey(@Nonnull KeySpec keySpec);
|
||||
|
||||
default B setPrimaryKey(@Nonnull KeySpecBuilder builder) {
|
||||
return setPrimaryKey(builder.build());
|
||||
}
|
||||
|
||||
interface WithAdditionalUserIdOrPassphrase {
|
||||
B addSubkey(@Nonnull KeySpec keySpec);
|
||||
|
||||
default WithAdditionalUserIdOrPassphrase withAdditionalUserId(@Nonnull UserId userId) {
|
||||
return withAdditionalUserId(userId.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an expiration date for the key.
|
||||
*
|
||||
* @param expirationDate date on which the key will expire.
|
||||
* @return builder
|
||||
*/
|
||||
WithAdditionalUserIdOrPassphrase setExpirationDate(@Nonnull Date expirationDate);
|
||||
|
||||
WithAdditionalUserIdOrPassphrase withAdditionalUserId(@Nonnull String userId);
|
||||
|
||||
WithAdditionalUserIdOrPassphrase withAdditionalUserId(@Nonnull byte[] userId);
|
||||
|
||||
Build withPassphrase(@Nonnull Passphrase passphrase);
|
||||
|
||||
Build withoutPassphrase();
|
||||
default B addSubkey(@Nonnull KeySpecBuilder builder) {
|
||||
return addSubkey(builder.build());
|
||||
}
|
||||
|
||||
interface Build {
|
||||
default B addUserId(UserId userId) {
|
||||
return addUserId(userId.toString());
|
||||
}
|
||||
|
||||
PGPSecretKeyRing build() throws NoSuchAlgorithmException, PGPException,
|
||||
B addUserId(@Nonnull String userId);
|
||||
|
||||
B addUserId(@Nonnull byte[] userId);
|
||||
|
||||
B setExpirationDate(@Nonnull Date expirationDate);
|
||||
|
||||
B setPassphrase(@Nonnull Passphrase passphrase);
|
||||
|
||||
PGPSecretKeyRing build() throws NoSuchAlgorithmException, PGPException,
|
||||
InvalidAlgorithmParameterException;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import javax.annotation.Nullable;
|
|||
|
||||
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
|
||||
import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
|
||||
import org.pgpainless.algorithm.KeyFlag;
|
||||
import org.pgpainless.key.generation.type.KeyType;
|
||||
|
||||
public class KeySpec {
|
||||
|
@ -54,7 +55,7 @@ public class KeySpec {
|
|||
return inheritedSubPackets;
|
||||
}
|
||||
|
||||
public static KeySpecBuilder getBuilder(KeyType type) {
|
||||
return new KeySpecBuilder(type);
|
||||
public static KeySpecBuilder getBuilder(KeyType type, KeyFlag flag, KeyFlag... flags) {
|
||||
return new KeySpecBuilder(type, flag, flags);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,14 @@
|
|||
*/
|
||||
package org.pgpainless.key.generation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bouncycastle.bcpg.sig.Features;
|
||||
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.algorithm.AlgorithmSuite;
|
||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||
import org.pgpainless.algorithm.Feature;
|
||||
|
@ -26,24 +30,89 @@ import org.pgpainless.algorithm.HashAlgorithm;
|
|||
import org.pgpainless.algorithm.KeyFlag;
|
||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||
import org.pgpainless.key.generation.type.KeyType;
|
||||
import org.pgpainless.util.CollectionUtils;
|
||||
|
||||
public class KeySpecBuilder implements KeySpecBuilderInterface {
|
||||
|
||||
private final KeyType type;
|
||||
private final KeyFlag[] keyFlags;
|
||||
private final PGPSignatureSubpacketGenerator hashedSubPackets = new PGPSignatureSubpacketGenerator();
|
||||
private final AlgorithmSuite algorithmSuite = PGPainless.getPolicy().getKeyGenerationAlgorithmSuite();
|
||||
private Set<CompressionAlgorithm> preferredCompressionAlgorithms = algorithmSuite.getCompressionAlgorithms();
|
||||
private Set<HashAlgorithm> preferredHashAlgorithms = algorithmSuite.getHashAlgorithms();
|
||||
private Set<SymmetricKeyAlgorithm> preferredSymmetricAlgorithms = algorithmSuite.getSymmetricKeyAlgorithms();
|
||||
|
||||
KeySpecBuilder(@Nonnull KeyType type) {
|
||||
KeySpecBuilder(@Nonnull KeyType type, KeyFlag flag, KeyFlag... flags) {
|
||||
if (flag == null) {
|
||||
throw new IllegalArgumentException("Key MUST carry at least one key flag");
|
||||
}
|
||||
if (flags == null) {
|
||||
throw new IllegalArgumentException("List of additional flags MUST NOT be null.");
|
||||
}
|
||||
flags = CollectionUtils.concat(flag, flags);
|
||||
assureKeyCanCarryFlags(type, flags);
|
||||
this.type = type;
|
||||
this.keyFlags = flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WithDetailedConfiguration withKeyFlags(@Nonnull KeyFlag... flags) {
|
||||
assureKeyCanCarryFlags(flags);
|
||||
this.hashedSubPackets.setKeyFlags(false, KeyFlag.toBitmask(flags));
|
||||
return new WithDetailedConfigurationImpl();
|
||||
public KeySpecBuilder overridePreferredCompressionAlgorithms(@Nonnull CompressionAlgorithm... compressionAlgorithms) {
|
||||
this.preferredCompressionAlgorithms = new LinkedHashSet<>(Arrays.asList(compressionAlgorithms));
|
||||
return this;
|
||||
}
|
||||
|
||||
private void assureKeyCanCarryFlags(KeyFlag... flags) {
|
||||
@Override
|
||||
public KeySpecBuilder overridePreferredHashAlgorithms(@Nonnull HashAlgorithm... preferredHashAlgorithms) {
|
||||
this.preferredHashAlgorithms = new LinkedHashSet<>(Arrays.asList(preferredHashAlgorithms));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeySpecBuilder overridePreferredSymmetricKeyAlgorithms(@Nonnull SymmetricKeyAlgorithm... preferredSymmetricKeyAlgorithms) {
|
||||
this.preferredSymmetricAlgorithms = new LinkedHashSet<>(Arrays.asList(preferredSymmetricKeyAlgorithms));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public KeySpec build() {
|
||||
this.hashedSubPackets.setKeyFlags(false, KeyFlag.toBitmask(keyFlags));
|
||||
this.hashedSubPackets.setPreferredCompressionAlgorithms(false, getPreferredCompressionAlgorithmIDs());
|
||||
this.hashedSubPackets.setPreferredHashAlgorithms(false, getPreferredHashAlgorithmIDs());
|
||||
this.hashedSubPackets.setPreferredSymmetricAlgorithms(false, getPreferredSymmetricKeyAlgorithmIDs());
|
||||
this.hashedSubPackets.setFeature(false, Feature.MODIFICATION_DETECTION.getFeatureId());
|
||||
|
||||
return new KeySpec(type, hashedSubPackets, false);
|
||||
}
|
||||
|
||||
private int[] getPreferredCompressionAlgorithmIDs() {
|
||||
int[] ids = new int[preferredCompressionAlgorithms.size()];
|
||||
Iterator<CompressionAlgorithm> iterator = preferredCompressionAlgorithms.iterator();
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
ids[i] = iterator.next().getAlgorithmId();
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
private int[] getPreferredHashAlgorithmIDs() {
|
||||
int[] ids = new int[preferredHashAlgorithms.size()];
|
||||
Iterator<HashAlgorithm> iterator = preferredHashAlgorithms.iterator();
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
ids[i] = iterator.next().getAlgorithmId();
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
private int[] getPreferredSymmetricKeyAlgorithmIDs() {
|
||||
int[] ids = new int[preferredSymmetricAlgorithms.size()];
|
||||
Iterator<SymmetricKeyAlgorithm> iterator = preferredSymmetricAlgorithms.iterator();
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
ids[i] = iterator.next().getAlgorithmId();
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
private static void assureKeyCanCarryFlags(KeyType type, KeyFlag... flags) {
|
||||
final int mask = KeyFlag.toBitmask(flags);
|
||||
|
||||
if (!type.canCertify() && KeyFlag.hasKeyFlag(mask, KeyFlag.CERTIFY_OTHER)) {
|
||||
|
@ -66,120 +135,4 @@ public class KeySpecBuilder implements KeySpecBuilderInterface {
|
|||
throw new IllegalArgumentException("KeyType " + type.getName() + " cannot carry key flag AUTHENTIACTION.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeySpec withInheritedSubPackets() {
|
||||
return new KeySpec(type, null, true);
|
||||
}
|
||||
|
||||
class WithDetailedConfigurationImpl implements WithDetailedConfiguration {
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public WithPreferredSymmetricAlgorithms withDetailedConfiguration() {
|
||||
return new WithPreferredSymmetricAlgorithmsImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeySpec withDefaultAlgorithms() {
|
||||
AlgorithmSuite defaultSuite = AlgorithmSuite.getDefaultAlgorithmSuite();
|
||||
hashedSubPackets.setPreferredCompressionAlgorithms(false, defaultSuite.getCompressionAlgorithmIds());
|
||||
hashedSubPackets.setPreferredSymmetricAlgorithms(false, defaultSuite.getSymmetricKeyAlgorithmIds());
|
||||
hashedSubPackets.setPreferredHashAlgorithms(false, defaultSuite.getHashAlgorithmIds());
|
||||
hashedSubPackets.setFeature(false, Features.FEATURE_MODIFICATION_DETECTION);
|
||||
|
||||
return new KeySpec(
|
||||
KeySpecBuilder.this.type,
|
||||
KeySpecBuilder.this.hashedSubPackets,
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
class WithPreferredSymmetricAlgorithmsImpl implements WithPreferredSymmetricAlgorithms {
|
||||
|
||||
@Override
|
||||
public WithPreferredHashAlgorithms withPreferredSymmetricAlgorithms(@Nonnull SymmetricKeyAlgorithm... algorithms) {
|
||||
int[] ids = new int[algorithms.length];
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
ids[i] = algorithms[i].getAlgorithmId();
|
||||
}
|
||||
KeySpecBuilder.this.hashedSubPackets.setPreferredSymmetricAlgorithms(false, ids);
|
||||
return new WithPreferredHashAlgorithmsImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WithPreferredHashAlgorithms withDefaultSymmetricAlgorithms() {
|
||||
KeySpecBuilder.this.hashedSubPackets.setPreferredSymmetricAlgorithms(false,
|
||||
AlgorithmSuite.getDefaultAlgorithmSuite().getSymmetricKeyAlgorithmIds());
|
||||
return new WithPreferredHashAlgorithmsImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WithFeatures withDefaultAlgorithms() {
|
||||
hashedSubPackets.setPreferredSymmetricAlgorithms(false,
|
||||
AlgorithmSuite.getDefaultAlgorithmSuite().getSymmetricKeyAlgorithmIds());
|
||||
hashedSubPackets.setPreferredCompressionAlgorithms(false,
|
||||
AlgorithmSuite.getDefaultAlgorithmSuite().getCompressionAlgorithmIds());
|
||||
hashedSubPackets.setPreferredHashAlgorithms(false,
|
||||
AlgorithmSuite.getDefaultAlgorithmSuite().getHashAlgorithmIds());
|
||||
return new WithFeaturesImpl();
|
||||
}
|
||||
}
|
||||
|
||||
class WithPreferredHashAlgorithmsImpl implements WithPreferredHashAlgorithms {
|
||||
|
||||
@Override
|
||||
public WithPreferredCompressionAlgorithms withPreferredHashAlgorithms(@Nonnull HashAlgorithm... algorithms) {
|
||||
int[] ids = new int[algorithms.length];
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
ids[i] = algorithms[i].getAlgorithmId();
|
||||
}
|
||||
KeySpecBuilder.this.hashedSubPackets.setPreferredHashAlgorithms(false, ids);
|
||||
return new WithPreferredCompressionAlgorithmsImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WithPreferredCompressionAlgorithms withDefaultHashAlgorithms() {
|
||||
KeySpecBuilder.this.hashedSubPackets.setPreferredHashAlgorithms(false,
|
||||
AlgorithmSuite.getDefaultAlgorithmSuite().getHashAlgorithmIds());
|
||||
return new WithPreferredCompressionAlgorithmsImpl();
|
||||
}
|
||||
}
|
||||
|
||||
class WithPreferredCompressionAlgorithmsImpl implements WithPreferredCompressionAlgorithms {
|
||||
|
||||
@Override
|
||||
public WithFeatures withPreferredCompressionAlgorithms(@Nonnull CompressionAlgorithm... algorithms) {
|
||||
int[] ids = new int[algorithms.length];
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
ids[i] = algorithms[i].getAlgorithmId();
|
||||
}
|
||||
KeySpecBuilder.this.hashedSubPackets.setPreferredCompressionAlgorithms(false, ids);
|
||||
return new WithFeaturesImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WithFeatures withDefaultCompressionAlgorithms() {
|
||||
KeySpecBuilder.this.hashedSubPackets.setPreferredCompressionAlgorithms(false,
|
||||
AlgorithmSuite.getDefaultAlgorithmSuite().getCompressionAlgorithmIds());
|
||||
return new WithFeaturesImpl();
|
||||
}
|
||||
}
|
||||
|
||||
class WithFeaturesImpl implements WithFeatures {
|
||||
|
||||
@Override
|
||||
public WithFeatures withFeature(@Nonnull Feature feature) {
|
||||
KeySpecBuilder.this.hashedSubPackets.setFeature(false, feature.getFeatureId());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeySpec done() {
|
||||
return new KeySpec(
|
||||
KeySpecBuilder.this.type,
|
||||
hashedSubPackets,
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,55 +18,16 @@ package org.pgpainless.key.generation;
|
|||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||
import org.pgpainless.algorithm.Feature;
|
||||
import org.pgpainless.algorithm.HashAlgorithm;
|
||||
import org.pgpainless.algorithm.KeyFlag;
|
||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||
|
||||
public interface KeySpecBuilderInterface {
|
||||
|
||||
WithDetailedConfiguration withKeyFlags(@Nonnull KeyFlag... flags);
|
||||
KeySpecBuilder overridePreferredCompressionAlgorithms(@Nonnull CompressionAlgorithm... compressionAlgorithms);
|
||||
|
||||
KeySpec withInheritedSubPackets();
|
||||
KeySpecBuilder overridePreferredHashAlgorithms(@Nonnull HashAlgorithm... preferredHashAlgorithms);
|
||||
|
||||
interface WithDetailedConfiguration {
|
||||
|
||||
WithPreferredSymmetricAlgorithms withDetailedConfiguration();
|
||||
|
||||
KeySpec withDefaultAlgorithms();
|
||||
}
|
||||
|
||||
interface WithPreferredSymmetricAlgorithms {
|
||||
|
||||
WithPreferredHashAlgorithms withPreferredSymmetricAlgorithms(@Nonnull SymmetricKeyAlgorithm... algorithms);
|
||||
|
||||
WithPreferredHashAlgorithms withDefaultSymmetricAlgorithms();
|
||||
|
||||
WithFeatures withDefaultAlgorithms();
|
||||
|
||||
}
|
||||
|
||||
interface WithPreferredHashAlgorithms {
|
||||
|
||||
WithPreferredCompressionAlgorithms withPreferredHashAlgorithms(@Nonnull HashAlgorithm... algorithms);
|
||||
|
||||
WithPreferredCompressionAlgorithms withDefaultHashAlgorithms();
|
||||
|
||||
}
|
||||
|
||||
interface WithPreferredCompressionAlgorithms {
|
||||
|
||||
WithFeatures withPreferredCompressionAlgorithms(@Nonnull CompressionAlgorithm... algorithms);
|
||||
|
||||
WithFeatures withDefaultCompressionAlgorithms();
|
||||
|
||||
}
|
||||
|
||||
interface WithFeatures {
|
||||
|
||||
WithFeatures withFeature(@Nonnull Feature feature);
|
||||
|
||||
KeySpec done();
|
||||
}
|
||||
KeySpecBuilder overridePreferredSymmetricKeyAlgorithms(@Nonnull SymmetricKeyAlgorithm... preferredSymmetricKeyAlgorithms);
|
||||
|
||||
KeySpec build();
|
||||
}
|
||||
|
|
|
@ -21,6 +21,9 @@ import java.util.EnumMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.pgpainless.algorithm.AlgorithmSuite;
|
||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||
import org.pgpainless.algorithm.HashAlgorithm;
|
||||
import org.pgpainless.algorithm.PublicKeyAlgorithm;
|
||||
|
@ -48,6 +51,8 @@ public final class Policy {
|
|||
PublicKeyAlgorithmPolicy.defaultPublicKeyAlgorithmPolicy();
|
||||
private final NotationRegistry notationRegistry = new NotationRegistry();
|
||||
|
||||
private AlgorithmSuite keyGenerationAlgorithmSuite = AlgorithmSuite.getDefaultAlgorithmSuite();
|
||||
|
||||
Policy() {
|
||||
}
|
||||
|
||||
|
@ -122,7 +127,7 @@ public final class Policy {
|
|||
*
|
||||
* @return symmetric algorithm policy for decryption
|
||||
*/
|
||||
public SymmetricKeyAlgorithmPolicy getSymmetricKeyDecryptionAlgoritmPolicy() {
|
||||
public SymmetricKeyAlgorithmPolicy getSymmetricKeyDecryptionAlgorithmPolicy() {
|
||||
return symmetricKeyDecryptionAlgorithmPolicy;
|
||||
}
|
||||
|
||||
|
@ -459,4 +464,21 @@ public final class Policy {
|
|||
public NotationRegistry getNotationRegistry() {
|
||||
return notationRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current {@link AlgorithmSuite} which defines preferred algorithms used during key generation.
|
||||
* @return current algorithm suite
|
||||
*/
|
||||
public @Nonnull AlgorithmSuite getKeyGenerationAlgorithmSuite() {
|
||||
return keyGenerationAlgorithmSuite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom {@link AlgorithmSuite} which defines preferred algorithms used during key generation.
|
||||
*
|
||||
* @param algorithmSuite custom algorithm suite
|
||||
*/
|
||||
public void setKeyGenerationAlgorithmSuite(@Nonnull AlgorithmSuite algorithmSuite) {
|
||||
this.keyGenerationAlgorithmSuite = algorithmSuite;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.pgpainless.util;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -33,4 +34,11 @@ public final class CollectionUtils {
|
|||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
public static <T> T[] concat(T t, T[] ts) {
|
||||
T[] concat = (T[]) Array.newInstance(t.getClass(), ts.length + 1);
|
||||
concat[0] = t;
|
||||
System.arraycopy(ts, 0, concat, 1, ts.length);
|
||||
return concat;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Copyright 2021 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pgpainless.algorithm;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class AlgorithmSuiteTest {
|
||||
|
||||
private AlgorithmSuite suite;
|
||||
|
||||
@BeforeEach
|
||||
public void resetEmptyAlgorithmSuite() {
|
||||
suite = new AlgorithmSuite(
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setSymmetricAlgorithmsTest() {
|
||||
List<SymmetricKeyAlgorithm> algorithmList = Arrays.asList(
|
||||
SymmetricKeyAlgorithm.AES_128, SymmetricKeyAlgorithm.AES_192, SymmetricKeyAlgorithm.AES_256
|
||||
);
|
||||
|
||||
suite.setSymmetricKeyAlgorithms(algorithmList);
|
||||
|
||||
assertEquals(algorithmList, new ArrayList<>(suite.getSymmetricKeyAlgorithms()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setHashAlgorithmsTest() {
|
||||
List<HashAlgorithm> algorithmList = Arrays.asList(
|
||||
HashAlgorithm.SHA256, HashAlgorithm.SHA384, HashAlgorithm.SHA512
|
||||
);
|
||||
|
||||
suite.setHashAlgorithms(algorithmList);
|
||||
|
||||
assertEquals(algorithmList, new ArrayList<>(suite.getHashAlgorithms()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setCompressionAlgorithmsTest() {
|
||||
List<CompressionAlgorithm> algorithmList = Arrays.asList(
|
||||
CompressionAlgorithm.ZLIB, CompressionAlgorithm.ZIP, CompressionAlgorithm.BZIP2
|
||||
);
|
||||
|
||||
suite.setCompressionAlgorithms(algorithmList);
|
||||
|
||||
assertEquals(algorithmList, new ArrayList<>(suite.getCompressionAlgorithms()));
|
||||
}
|
||||
}
|
|
@ -88,9 +88,13 @@ public class EncryptDecryptTest {
|
|||
ImplementationFactory.setFactoryImplementation(implementationFactory);
|
||||
PGPSecretKeyRing sender = PGPainless.generateKeyRing().simpleRsaKeyRing("romeo@montague.lit", RsaLength._3072);
|
||||
PGPSecretKeyRing recipient = PGPainless.generateKeyRing()
|
||||
.withSubKey(KeySpec.getBuilder(ElGamal.withLength(ElGamalLength._3072)).withKeyFlags(KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS).withDefaultAlgorithms())
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._4096)).withKeyFlags(KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER).withDefaultAlgorithms())
|
||||
.withPrimaryUserId("juliet@capulet.lit").withoutPassphrase().build();
|
||||
.setPrimaryKey(KeySpec.getBuilder(
|
||||
KeyType.RSA(RsaLength._4096),
|
||||
KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER))
|
||||
.addSubkey(KeySpec.getBuilder(
|
||||
ElGamal.withLength(ElGamalLength._3072),
|
||||
KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS))
|
||||
.addUserId("juliet@capulet.lit").build();
|
||||
|
||||
encryptDecryptForSecretKeyRings(sender, recipient);
|
||||
}
|
||||
|
@ -262,7 +266,7 @@ public class EncryptDecryptTest {
|
|||
EncryptionStream signer = PGPainless.encryptAndOrSign().onOutputStream(signOut)
|
||||
.withOptions(ProducerOptions.sign(
|
||||
SigningOptions.get()
|
||||
.addInlineSignature(keyRingProtector, signingKeys, DocumentSignatureType.BINARY_DOCUMENT)
|
||||
.addInlineSignature(keyRingProtector, signingKeys, DocumentSignatureType.BINARY_DOCUMENT)
|
||||
).setAsciiArmor(true));
|
||||
Streams.pipeAll(inputStream, signer);
|
||||
signer.close();
|
||||
|
|
|
@ -59,15 +59,13 @@ public class EncryptionOptionsTest {
|
|||
@BeforeAll
|
||||
public static void generateKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
||||
secretKeys = PGPainless.generateKeyRing()
|
||||
.withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519))
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_COMMS).withDefaultAlgorithms())
|
||||
.withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519))
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_STORAGE).withDefaultAlgorithms())
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryUserId("test@pgpainless.org")
|
||||
.withoutPassphrase()
|
||||
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER)
|
||||
.build())
|
||||
.addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS)
|
||||
.build())
|
||||
.addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_STORAGE)
|
||||
.build())
|
||||
.addUserId("test@pgpainless.org")
|
||||
.build();
|
||||
|
||||
publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys);
|
||||
|
@ -140,10 +138,9 @@ public class EncryptionOptionsTest {
|
|||
public void testAddRecipient_KeyWithoutEncryptionKeyFails() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
||||
EncryptionOptions options = new EncryptionOptions();
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA).withDefaultAlgorithms())
|
||||
.withPrimaryUserId("test@pgpainless.org")
|
||||
.withoutPassphrase().build();
|
||||
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA))
|
||||
.addUserId("test@pgpainless.org")
|
||||
.build();
|
||||
PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys);
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> options.addRecipient(publicKeys));
|
||||
|
|
|
@ -21,22 +21,21 @@ 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.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.algorithm.AlgorithmSuite;
|
||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||
import org.pgpainless.algorithm.EncryptionPurpose;
|
||||
import org.pgpainless.algorithm.Feature;
|
||||
import org.pgpainless.algorithm.HashAlgorithm;
|
||||
import org.pgpainless.algorithm.KeyFlag;
|
||||
import org.pgpainless.algorithm.PublicKeyAlgorithm;
|
||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||
import org.pgpainless.key.generation.KeySpec;
|
||||
import org.pgpainless.key.generation.KeySpecBuilderInterface;
|
||||
import org.pgpainless.key.generation.KeySpecBuilder;
|
||||
import org.pgpainless.key.generation.type.KeyType;
|
||||
import org.pgpainless.key.generation.type.ecc.EllipticCurve;
|
||||
import org.pgpainless.key.generation.type.eddsa.EdDSACurve;
|
||||
|
@ -159,35 +158,34 @@ public class GenerateKeys {
|
|||
* algorithm preferences.
|
||||
*
|
||||
* If the target key amalgamation (key ring) should consist of more than just a single (sub-)key, start by providing
|
||||
* the specifications for the subkeys first (in {@link org.pgpainless.key.generation.KeyRingBuilderInterface#withSubKey(KeySpec)})
|
||||
* and add the primary key specification last (in {@link org.pgpainless.key.generation.KeyRingBuilderInterface#withPrimaryKey(KeySpec)}.
|
||||
* the primary key specification using {@link org.pgpainless.key.generation.KeyRingBuilder#setPrimaryKey(KeySpec)}.
|
||||
* Any additional subkeys can be then added using {@link org.pgpainless.key.generation.KeyRingBuilder#addSubkey(KeySpec)}.
|
||||
*
|
||||
* {@link KeySpec} objects can best be obtained by using the {@link KeySpec#getBuilder(KeyType)} method and providing a {@link KeyType}.
|
||||
* {@link KeySpec} objects can best be obtained by using the {@link KeySpec#getBuilder(KeyType, KeyFlag, KeyFlag...)}
|
||||
* method and providing a {@link KeyType}.
|
||||
* There are a bunch of factory methods for different {@link KeyType} implementations present in {@link KeyType} itself
|
||||
* (such as {@link KeyType#ECDH(EllipticCurve)}.
|
||||
*
|
||||
* After that, the {@link org.pgpainless.key.generation.KeySpecBuilder} needs to be further configured.
|
||||
* First of all, the keys {@link KeyFlag KeyFlags} need to be specified. {@link KeyFlag KeyFlags} determine
|
||||
* (such as {@link KeyType#ECDH(EllipticCurve)}. {@link KeyFlag KeyFlags} determine
|
||||
* the use of the key, like encryption, signing data or certifying subkeys.
|
||||
* KeyFlags can be set with {@link org.pgpainless.key.generation.KeySpecBuilder#withKeyFlags(KeyFlag...)}.
|
||||
*
|
||||
* Next is algorithm setup. You can either trust PGPainless' defaults (see {@link AlgorithmSuite#getDefaultAlgorithmSuite()}),
|
||||
* or specify your own algorithm preferences.
|
||||
* To go with the defaults, call {@link KeySpecBuilderInterface.WithDetailedConfiguration#withDefaultAlgorithms()},
|
||||
* otherwise start detailed config with {@link KeySpecBuilderInterface.WithDetailedConfiguration#withDetailedConfiguration()}.
|
||||
* If you so desire, you can now specify your own algorithm preferences.
|
||||
* For that, see {@link org.pgpainless.key.generation.KeySpecBuilder#overridePreferredCompressionAlgorithms(CompressionAlgorithm...)},
|
||||
* {@link org.pgpainless.key.generation.KeySpecBuilder#overridePreferredHashAlgorithms(HashAlgorithm...)} or
|
||||
* {@link org.pgpainless.key.generation.KeySpecBuilder#overridePreferredSymmetricKeyAlgorithms(SymmetricKeyAlgorithm...)}.
|
||||
*
|
||||
* Note, that if you set preferred algorithms, the preference lists are sorted from high priority to low priority.
|
||||
*
|
||||
* When setting the primary key spec ({@link org.pgpainless.key.generation.KeyRingBuilder#withPrimaryKey(KeySpec)}),
|
||||
* make sure that the primary key spec has the {@link KeyFlag} {@link KeyFlag#CERTIFY_OTHER} set, as this is an requirement
|
||||
* When setting the primary key spec ({@link org.pgpainless.key.generation.KeyRingBuilder#setPrimaryKey(KeySpecBuilder)}),
|
||||
* make sure that the primary key spec has the {@link KeyFlag} {@link KeyFlag#CERTIFY_OTHER} set, as this is a requirement
|
||||
* for primary keys.
|
||||
*
|
||||
* Furthermore you have to set at least the primary user-id via
|
||||
* {@link org.pgpainless.key.generation.KeyRingBuilderInterface.WithPrimaryUserId#withPrimaryUserId(String)},
|
||||
* but you can also add additional user-ids via
|
||||
* {@link org.pgpainless.key.generation.KeyRingBuilderInterface.WithAdditionalUserIdOrPassphrase#withAdditionalUserId(String)}.
|
||||
* {@link org.pgpainless.key.generation.KeyRingBuilder#addUserId(String)},
|
||||
* but you can also add additional user-ids.
|
||||
*
|
||||
* Lastly you can decide whether or not to set a passphrase to protect the secret key.
|
||||
* If you want the key to expire at a certain point in time, call
|
||||
* {@link org.pgpainless.key.generation.KeyRingBuilder#setExpirationDate(Date)}.
|
||||
* Lastly you can decide whether to set a passphrase to protect the secret key using
|
||||
* {@link org.pgpainless.key.generation.KeyRingBuilder#setPassphrase(Passphrase)}.
|
||||
*
|
||||
* @throws PGPException
|
||||
* @throws InvalidAlgorithmParameterException
|
||||
|
@ -208,48 +206,35 @@ public class GenerateKeys {
|
|||
Passphrase passphrase = Passphrase.fromPassword("1nters3x");
|
||||
|
||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
||||
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519),
|
||||
// The primary key MUST carry the CERTIFY_OTHER flag, but CAN carry additional flags
|
||||
KeyFlag.CERTIFY_OTHER))
|
||||
// Add the first subkey (in this case encryption)
|
||||
.withSubKey(
|
||||
KeySpec.getBuilder(
|
||||
.addSubkey(KeySpec.getBuilder(
|
||||
// We choose an ECDH key over the brainpoolp256r1 curve
|
||||
KeyType.ECDH(EllipticCurve._BRAINPOOLP256R1)
|
||||
)
|
||||
KeyType.ECDH(EllipticCurve._BRAINPOOLP256R1),
|
||||
// Our key can encrypt both communication data, as well as data at rest
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS)
|
||||
// Optionally: Configure the subkey with custom algorithm preferences
|
||||
// Is is recommended though to go with PGPainless' defaults which can be found in the
|
||||
// AlgorithmSuite class.
|
||||
.withDetailedConfiguration()
|
||||
.withPreferredSymmetricAlgorithms(SymmetricKeyAlgorithm.AES_256, SymmetricKeyAlgorithm.AES_192, SymmetricKeyAlgorithm.AES_128)
|
||||
.withPreferredHashAlgorithms(HashAlgorithm.SHA512, HashAlgorithm.SHA384, HashAlgorithm.SHA256)
|
||||
.withPreferredCompressionAlgorithms(CompressionAlgorithm.ZIP, CompressionAlgorithm.BZIP2, CompressionAlgorithm.ZLIB)
|
||||
// Modification Detection is highly recommended
|
||||
.withFeature(Feature.MODIFICATION_DETECTION)
|
||||
.done()
|
||||
)
|
||||
// Add the second subkey (signing)
|
||||
.withSubKey(
|
||||
KeySpec.getBuilder(
|
||||
KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1)
|
||||
KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS
|
||||
)
|
||||
// This key is used for creating signatures only
|
||||
.withKeyFlags(KeyFlag.SIGN_DATA)
|
||||
// Instead of manually specifying algorithm preferences, we can simply use PGPainless' sane defaults
|
||||
.withDefaultAlgorithms()
|
||||
)
|
||||
// Lastly we add the primary key (certification only in our case)
|
||||
.withPrimaryKey(
|
||||
KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||
// The primary key MUST carry the CERTIFY_OTHER flag, but CAN carry additional flags
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER)
|
||||
.withDefaultAlgorithms()
|
||||
)
|
||||
// Optionally: Configure the subkey with custom algorithm preferences
|
||||
// Is is recommended though to go with PGPainless' defaults which can be found in the
|
||||
// AlgorithmSuite class.
|
||||
.overridePreferredSymmetricKeyAlgorithms(SymmetricKeyAlgorithm.AES_256, SymmetricKeyAlgorithm.AES_192, SymmetricKeyAlgorithm.AES_128)
|
||||
.overridePreferredHashAlgorithms(HashAlgorithm.SHA512, HashAlgorithm.SHA384, HashAlgorithm.SHA256)
|
||||
.overridePreferredCompressionAlgorithms(CompressionAlgorithm.ZIP, CompressionAlgorithm.BZIP2, CompressionAlgorithm.ZLIB)
|
||||
.build())
|
||||
// Add the second subkey (signing)
|
||||
.addSubkey(KeySpec.getBuilder(
|
||||
KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1),
|
||||
// This key is used for creating signatures only
|
||||
KeyFlag.SIGN_DATA
|
||||
))
|
||||
// Set primary user-id
|
||||
.withPrimaryUserId(userId)
|
||||
.addUserId(userId)
|
||||
// Add an additional user id. This step can be repeated
|
||||
.withAdditionalUserId(additionalUserId)
|
||||
.addUserId(additionalUserId)
|
||||
// Set passphrase. Alternatively use .withoutPassphrase() to leave key unprotected.
|
||||
.withPassphrase(passphrase)
|
||||
.setPassphrase(passphrase)
|
||||
.build();
|
||||
|
||||
|
||||
|
|
|
@ -187,9 +187,8 @@ public class ModifyKeys {
|
|||
Passphrase subkeyPassphrase = Passphrase.fromPassword("subk3yP4ssphr4s3");
|
||||
secretKey = PGPainless.modifyKeyRing(secretKey)
|
||||
.addSubKey(
|
||||
KeySpec.getBuilder(KeyType.ECDH(EllipticCurve._BRAINPOOLP512R1))
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_COMMS)
|
||||
.withDefaultAlgorithms(),
|
||||
KeySpec.getBuilder(KeyType.ECDH(EllipticCurve._BRAINPOOLP512R1), KeyFlag.ENCRYPT_COMMS)
|
||||
.build(),
|
||||
subkeyPassphrase,
|
||||
protector)
|
||||
.done();
|
||||
|
|
|
@ -54,12 +54,10 @@ public class BrainpoolKeyGenerationTest {
|
|||
|
||||
for (EllipticCurve curve : EllipticCurve.values()) {
|
||||
PGPSecretKeyRing secretKeys = generateKey(
|
||||
KeySpec.getBuilder(KeyType.ECDSA(curve))
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)
|
||||
.withDefaultAlgorithms(),
|
||||
KeySpec.getBuilder(KeyType.ECDH(curve))
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)
|
||||
.withDefaultAlgorithms(),
|
||||
KeySpec.getBuilder(
|
||||
KeyType.ECDSA(curve), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA).build(),
|
||||
KeySpec.getBuilder(
|
||||
KeyType.ECDH(curve), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE).build(),
|
||||
"Elliptic Curve <elliptic@curve.key>");
|
||||
|
||||
assertEquals(PublicKeyAlgorithm.ECDSA, PublicKeyAlgorithm.fromId(secretKeys.getPublicKey().getAlgorithm()));
|
||||
|
@ -85,20 +83,15 @@ public class BrainpoolKeyGenerationTest {
|
|||
ImplementationFactory.setFactoryImplementation(implementationFactory);
|
||||
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.withSubKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||
.withKeyFlags(KeyFlag.SIGN_DATA)
|
||||
.withDefaultAlgorithms())
|
||||
.withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519))
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)
|
||||
.withDefaultAlgorithms())
|
||||
.withSubKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072))
|
||||
.withKeyFlags(KeyFlag.SIGN_DATA)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1))
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryUserId(UserId.nameAndEmail("Alice", "alice@pgpainless.org"))
|
||||
.withPassphrase(Passphrase.fromPassword("passphrase"))
|
||||
.setPrimaryKey(KeySpec.getBuilder(
|
||||
KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1), KeyFlag.CERTIFY_OTHER))
|
||||
.addSubkey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA))
|
||||
.addSubkey(KeySpec.getBuilder(
|
||||
KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE))
|
||||
.addSubkey(KeySpec.getBuilder(
|
||||
KeyType.RSA(RsaLength._3072), KeyFlag.SIGN_DATA))
|
||||
.addUserId(UserId.nameAndEmail("Alice", "alice@pgpainless.org"))
|
||||
.setPassphrase(Passphrase.fromPassword("passphrase"))
|
||||
.build();
|
||||
|
||||
for (PGPSecretKey key : secretKeys) {
|
||||
|
@ -136,10 +129,9 @@ public class BrainpoolKeyGenerationTest {
|
|||
|
||||
public PGPSecretKeyRing generateKey(KeySpec primaryKey, KeySpec subKey, String userId) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException {
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.withSubKey(subKey)
|
||||
.withPrimaryKey(primaryKey)
|
||||
.withPrimaryUserId(userId)
|
||||
.withoutPassphrase()
|
||||
.setPrimaryKey(primaryKey)
|
||||
.addSubkey(subKey)
|
||||
.addUserId(userId)
|
||||
.build();
|
||||
return secretKeys;
|
||||
}
|
||||
|
|
|
@ -46,12 +46,9 @@ public class CertificationKeyMustBeAbleToCertifyTest {
|
|||
for (KeyType type : typesIncapableOfCreatingVerifications) {
|
||||
assertThrows(IllegalArgumentException.class, () -> PGPainless
|
||||
.generateKeyRing()
|
||||
.withPrimaryKey(KeySpec
|
||||
.getBuilder(type)
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryUserId("should@throw.ex")
|
||||
.withoutPassphrase().build());
|
||||
.setPrimaryKey(KeySpec.getBuilder(type, KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA))
|
||||
.addUserId("should@throw.ex")
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,14 +41,11 @@ public class GenerateEllipticCurveKeyTest {
|
|||
public void generateEllipticCurveKeys(ImplementationFactory implementationFactory) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException {
|
||||
ImplementationFactory.setFactoryImplementation(implementationFactory);
|
||||
PGPSecretKeyRing keyRing = PGPainless.generateKeyRing()
|
||||
.withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519))
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_COMMS)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryUserId(UserId.onlyEmail("alice@wonderland.lit").toString())
|
||||
.withoutPassphrase()
|
||||
.setPrimaryKey(KeySpec.getBuilder(
|
||||
KeyType.EDDSA(EdDSACurve._Ed25519),
|
||||
KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA))
|
||||
.addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS))
|
||||
.addUserId(UserId.onlyEmail("alice@wonderland.lit").toString())
|
||||
.build();
|
||||
|
||||
assertEquals(PublicKeyAlgorithm.EDDSA.getAlgorithmId(), keyRing.getPublicKey().getAlgorithm());
|
||||
|
|
|
@ -47,15 +47,14 @@ public class GenerateKeyWithAdditionalUserIdTest {
|
|||
ImplementationFactory.setFactoryImplementation(implementationFactory);
|
||||
Date expiration = new Date(DateUtil.now().getTime() + 60 * 1000);
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072))
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryUserId(UserId.onlyEmail("primary@user.id"))
|
||||
.withAdditionalUserId(UserId.onlyEmail("additional@user.id"))
|
||||
.withAdditionalUserId(UserId.onlyEmail("additional2@user.id"))
|
||||
.withAdditionalUserId("\ttrimThis@user.id ")
|
||||
.setPrimaryKey(KeySpec.getBuilder(
|
||||
KeyType.RSA(RsaLength._3072),
|
||||
KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS))
|
||||
.addUserId(UserId.onlyEmail("primary@user.id"))
|
||||
.addUserId(UserId.onlyEmail("additional@user.id"))
|
||||
.addUserId(UserId.onlyEmail("additional2@user.id"))
|
||||
.addUserId("\ttrimThis@user.id ")
|
||||
.setExpirationDate(expiration)
|
||||
.withoutPassphrase()
|
||||
.build();
|
||||
PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys);
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ import org.pgpainless.util.Passphrase;
|
|||
* The issue is that the implementation of {@link Passphrase#emptyPassphrase()} would set the underlying
|
||||
* char array to null, which caused an NPE later on.
|
||||
*/
|
||||
public class GenerateWithEmptyPassphrase {
|
||||
public class GenerateWithEmptyPassphraseTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("org.pgpainless.util.TestImplementationFactoryProvider#provideImplementationFactories")
|
||||
|
@ -46,11 +46,11 @@ public class GenerateWithEmptyPassphrase {
|
|||
ImplementationFactory.setFactoryImplementation(implementationFactory);
|
||||
|
||||
assertNotNull(PGPainless.generateKeyRing()
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072))
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryUserId("primary@user.id")
|
||||
.withPassphrase(Passphrase.emptyPassphrase())
|
||||
.setPrimaryKey(KeySpec.getBuilder(
|
||||
KeyType.RSA(RsaLength._3072),
|
||||
KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS))
|
||||
.addUserId("primary@user.id")
|
||||
.setPassphrase(Passphrase.emptyPassphrase())
|
||||
.build());
|
||||
}
|
||||
}
|
|
@ -19,7 +19,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
|||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.algorithm.KeyFlag;
|
||||
import org.pgpainless.implementation.ImplementationFactory;
|
||||
import org.pgpainless.key.generation.type.KeyType;
|
||||
|
@ -32,29 +31,19 @@ public class IllegalKeyFlagsTest {
|
|||
@MethodSource("org.pgpainless.util.TestImplementationFactoryProvider#provideImplementationFactories")
|
||||
public void testKeyCannotCarryFlagsTest(ImplementationFactory implementationFactory) {
|
||||
ImplementationFactory.setFactoryImplementation(implementationFactory);
|
||||
assertThrows(IllegalArgumentException.class, () -> PGPainless.generateKeyRing()
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519))
|
||||
.withKeyFlags(KeyFlag.SIGN_DATA) // <- should throw
|
||||
.withDefaultAlgorithms()));
|
||||
assertThrows(IllegalArgumentException.class, () -> KeySpec.getBuilder(
|
||||
KeyType.XDH(XDHSpec._X25519), KeyFlag.SIGN_DATA));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> PGPainless.generateKeyRing()
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519))
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER) // <- should throw
|
||||
.withDefaultAlgorithms()));
|
||||
assertThrows(IllegalArgumentException.class, () -> KeySpec.getBuilder(
|
||||
KeyType.XDH(XDHSpec._X25519), KeyFlag.CERTIFY_OTHER));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> PGPainless.generateKeyRing()
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519))
|
||||
.withKeyFlags(KeyFlag.AUTHENTICATION) // <- should throw
|
||||
.withDefaultAlgorithms()));
|
||||
assertThrows(IllegalArgumentException.class, () -> KeySpec.getBuilder(
|
||||
KeyType.XDH(XDHSpec._X25519), KeyFlag.AUTHENTICATION));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> PGPainless.generateKeyRing()
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_COMMS) // <- should throw
|
||||
.withDefaultAlgorithms()));
|
||||
assertThrows(IllegalArgumentException.class, () -> KeySpec.getBuilder(
|
||||
KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.ENCRYPT_COMMS));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> PGPainless.generateKeyRing()
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_STORAGE) // <- should throw as well
|
||||
.withDefaultAlgorithms()));
|
||||
assertThrows(IllegalArgumentException.class, () -> KeySpec.getBuilder(
|
||||
KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.ENCRYPT_STORAGE));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -221,11 +221,14 @@ public class KeyRingInfoTest {
|
|||
ImplementationFactory.setFactoryImplementation(implementationFactory);
|
||||
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.withSubKey(KeySpec.getBuilder(KeyType.ECDH(EllipticCurve._BRAINPOOLP384R1)).withKeyFlags(KeyFlag.ENCRYPT_STORAGE).withDefaultAlgorithms())
|
||||
.withSubKey(KeySpec.getBuilder(KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1)).withKeyFlags(KeyFlag.SIGN_DATA).withDefaultAlgorithms())
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519)).withKeyFlags(KeyFlag.CERTIFY_OTHER).withDefaultAlgorithms())
|
||||
.withPrimaryUserId(UserId.newBuilder().withName("Alice").withEmail("alice@pgpainless.org").build())
|
||||
.withoutPassphrase()
|
||||
.setPrimaryKey(KeySpec.getBuilder(
|
||||
KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER))
|
||||
.addSubkey(KeySpec.getBuilder(
|
||||
KeyType.ECDH(EllipticCurve._BRAINPOOLP384R1),
|
||||
KeyFlag.ENCRYPT_STORAGE))
|
||||
.addSubkey(KeySpec.getBuilder(
|
||||
KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1), KeyFlag.SIGN_DATA))
|
||||
.addUserId(UserId.newBuilder().withName("Alice").withEmail("alice@pgpainless.org").build())
|
||||
.build();
|
||||
|
||||
Iterator<PGPSecretKey> keys = secretKeys.iterator();
|
||||
|
|
|
@ -51,15 +51,13 @@ public class UserIdRevocationTest {
|
|||
@Test
|
||||
public void testRevocationWithoutRevocationAttributes() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519))
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_COMMS)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||
.withKeyFlags(KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryUserId("primary@key.id")
|
||||
.withAdditionalUserId("secondary@key.id")
|
||||
.withoutPassphrase()
|
||||
.setPrimaryKey(KeySpec.getBuilder(
|
||||
KeyType.EDDSA(EdDSACurve._Ed25519),
|
||||
KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER))
|
||||
.addSubkey(KeySpec.getBuilder(
|
||||
KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS))
|
||||
.addUserId("primary@key.id")
|
||||
.addUserId("secondary@key.id")
|
||||
.build();
|
||||
|
||||
// make a copy with revoked subkey
|
||||
|
@ -91,15 +89,12 @@ public class UserIdRevocationTest {
|
|||
@Test
|
||||
public void testRevocationWithRevocationReason() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException {
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.withSubKey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519))
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_COMMS)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||
.withKeyFlags(KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryUserId("primary@key.id")
|
||||
.withAdditionalUserId("secondary@key.id")
|
||||
.withoutPassphrase()
|
||||
.setPrimaryKey(KeySpec.getBuilder(
|
||||
KeyType.EDDSA(EdDSACurve._Ed25519),
|
||||
KeyFlag.SIGN_DATA, KeyFlag.CERTIFY_OTHER))
|
||||
.addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS))
|
||||
.addUserId("primary@key.id")
|
||||
.addUserId("secondary@key.id")
|
||||
.build();
|
||||
|
||||
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||
|
@ -146,6 +141,6 @@ public class UserIdRevocationTest {
|
|||
assertThrows(IllegalArgumentException.class, () -> PGPainless.modifyKeyRing(secretKeys)
|
||||
.revokeUserId("cryptie@encrypted.key", protector,
|
||||
RevocationAttributes.createKeyRevocation().withReason(RevocationAttributes.Reason.KEY_RETIRED)
|
||||
.withDescription("This is not a valid certification revocation reason.")));
|
||||
.withDescription("This is not a valid certification revocation reason.")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,9 +61,7 @@ public class AddSubKeyTest {
|
|||
|
||||
secretKeys = PGPainless.modifyKeyRing(secretKeys)
|
||||
.addSubKey(
|
||||
KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256))
|
||||
.withKeyFlags(KeyFlag.SIGN_DATA)
|
||||
.withDefaultAlgorithms(),
|
||||
KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256), KeyFlag.SIGN_DATA).build(),
|
||||
Passphrase.fromPassword("subKeyPassphrase"),
|
||||
PasswordBasedSecretKeyRingProtector.forKey(secretKeys, Passphrase.fromPassword("password123")))
|
||||
.done();
|
||||
|
|
|
@ -89,14 +89,14 @@ public class PolicyTest {
|
|||
|
||||
@Test
|
||||
public void testAcceptableSymmetricKeyDecryptionAlgorithm() {
|
||||
assertTrue(policy.getSymmetricKeyDecryptionAlgoritmPolicy().isAcceptable(SymmetricKeyAlgorithm.BLOWFISH));
|
||||
assertTrue(policy.getSymmetricKeyDecryptionAlgoritmPolicy().isAcceptable(SymmetricKeyAlgorithm.BLOWFISH.getAlgorithmId()));
|
||||
assertTrue(policy.getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(SymmetricKeyAlgorithm.BLOWFISH));
|
||||
assertTrue(policy.getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(SymmetricKeyAlgorithm.BLOWFISH.getAlgorithmId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnAcceptableSymmetricKeyDecryptionAlgorithm() {
|
||||
assertFalse(policy.getSymmetricKeyDecryptionAlgoritmPolicy().isAcceptable(SymmetricKeyAlgorithm.CAMELLIA_128));
|
||||
assertFalse(policy.getSymmetricKeyDecryptionAlgoritmPolicy().isAcceptable(SymmetricKeyAlgorithm.CAMELLIA_128.getAlgorithmId()));
|
||||
assertFalse(policy.getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(SymmetricKeyAlgorithm.CAMELLIA_128));
|
||||
assertFalse(policy.getSymmetricKeyDecryptionAlgorithmPolicy().isAcceptable(SymmetricKeyAlgorithm.CAMELLIA_128.getAlgorithmId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -47,13 +47,12 @@ public class BCUtilTest {
|
|||
throws PGPException, NoSuchAlgorithmException, InvalidAlgorithmParameterException,
|
||||
IOException {
|
||||
PGPSecretKeyRing sec = PGPainless.generateKeyRing()
|
||||
.withSubKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072))
|
||||
.withKeyFlags(KeyFlag.ENCRYPT_COMMS)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072))
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryUserId("donald@duck.tails").withoutPassphrase().build();
|
||||
.setPrimaryKey(KeySpec.getBuilder(
|
||||
KeyType.RSA(RsaLength._3072),
|
||||
KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA))
|
||||
.addSubkey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072), KeyFlag.ENCRYPT_COMMS))
|
||||
.addUserId("donald@duck.tails")
|
||||
.build();
|
||||
|
||||
PGPPublicKeyRing pub = KeyRingUtils.publicKeyRingFrom(sec);
|
||||
|
||||
|
|
|
@ -41,17 +41,12 @@ public class GuessPreferredHashAlgorithmTest {
|
|||
@Test
|
||||
public void guessPreferredHashAlgorithmsAssumesHashAlgoUsedBySelfSig() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException {
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519))
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)
|
||||
.withDetailedConfiguration()
|
||||
// Do not specify preferred algorithms
|
||||
.withPreferredSymmetricAlgorithms(new SymmetricKeyAlgorithm[] {})
|
||||
.withPreferredHashAlgorithms(new HashAlgorithm[] {})
|
||||
.withPreferredCompressionAlgorithms(new CompressionAlgorithm[] {})
|
||||
|
||||
.done())
|
||||
.withPrimaryUserId("test@test.test")
|
||||
.withoutPassphrase()
|
||||
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519),
|
||||
KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)
|
||||
.overridePreferredHashAlgorithms(new HashAlgorithm[] {})
|
||||
.overridePreferredSymmetricKeyAlgorithms(new SymmetricKeyAlgorithm[] {})
|
||||
.overridePreferredCompressionAlgorithms(new CompressionAlgorithm[] {}))
|
||||
.addUserId("test@test.test")
|
||||
.build();
|
||||
|
||||
PGPPublicKey publicKey = secretKeys.getPublicKey();
|
||||
|
|
|
@ -38,14 +38,13 @@ public class TestEncryptCommsStorageFlagsDifferentiated {
|
|||
@Test
|
||||
public void testThatEncryptionDifferentiatesBetweenPurposeKeyFlags() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException {
|
||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||
.withPrimaryKey(KeySpec.getBuilder(KeyType.RSA(RsaLength._3072))
|
||||
.withKeyFlags(KeyFlag.CERTIFY_OTHER,
|
||||
.setPrimaryKey(KeySpec.getBuilder(
|
||||
KeyType.RSA(RsaLength._3072),
|
||||
KeyFlag.CERTIFY_OTHER,
|
||||
KeyFlag.SIGN_DATA,
|
||||
KeyFlag.ENCRYPT_STORAGE // no ENCRYPT_COMMS
|
||||
)
|
||||
.withDefaultAlgorithms())
|
||||
.withPrimaryUserId("cannot@encrypt.comms")
|
||||
.withoutPassphrase()
|
||||
))
|
||||
.addUserId("cannot@encrypt.comms")
|
||||
.build();
|
||||
|
||||
PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys);
|
||||
|
|
Loading…
Reference in a new issue