diff --git a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCGenerateKey.java b/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCGenerateKey.java index adbad8b..a00e552 100644 --- a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCGenerateKey.java +++ b/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCGenerateKey.java @@ -1,10 +1,15 @@ package org.pgpainless.bouncycastle.sop.operation; +import org.bouncycastle.bcpg.PublicKeyPacket; import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyPair; +import org.bouncycastle.openpgp.api.KeyPairGeneratorCallback; import org.bouncycastle.openpgp.api.OpenPGPApi; import org.bouncycastle.openpgp.api.OpenPGPKey; -import org.bouncycastle.openpgp.api.OpenPGPV6KeyGenerator; +import org.bouncycastle.openpgp.api.OpenPGPKeyGenerator; +import org.bouncycastle.openpgp.operator.PGPKeyPairGenerator; import org.jetbrains.annotations.NotNull; +import sop.Profile; import sop.Ready; import sop.exception.SOPGPException; import sop.operation.GenerateKey; @@ -13,6 +18,7 @@ import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.List; @@ -20,10 +26,17 @@ public class BCGenerateKey extends AbstractBCOperation implements GenerateKey { + public static final Profile DEFAULT = new Profile("default", "Use the implementer's recommendations."); + public static final Profile SECURITY = new Profile("security", "Higher security, maybe reduced performance."); + public static final Profile PERFORMANCE = new Profile("performance", "Higher performance, maybe reduced security."); + public static final Profile COMPATIBILITY = new Profile("compatibility", "Use algorithms and features from rfc4480."); + public static final List PROFILES = Arrays.asList(DEFAULT, SECURITY, PERFORMANCE, COMPATIBILITY); + private boolean armor = true; private boolean signOnly = false; private final List userIds = new ArrayList<>(); private char[] passphrase; + private Profile profile = DEFAULT; public BCGenerateKey(OpenPGPApi api) { super(api); @@ -37,24 +50,10 @@ public class BCGenerateKey @Override public void writeTo(@NotNull OutputStream outputStream) throws IOException { - OpenPGPV6KeyGenerator generator; - try { - generator = api.generateKey(new Date()); - } catch (PGPException e) { - throw new RuntimeException(e); - } OpenPGPKey key; - try - { - OpenPGPV6KeyGenerator.WithPrimaryKey builder; - if (signOnly) - { - builder = generator.signOnlyKey(); - } - else - { - builder = generator.ed25519x25519Key(null); - } + try { + OpenPGPKeyGenerator.WithPrimaryKey builder = generatorForProfile(api, profile, signOnly); + for (String userId : userIds) { builder.addUserId(userId); @@ -78,6 +77,135 @@ public class BCGenerateKey }; } + private OpenPGPKeyGenerator.WithPrimaryKey generatorForProfile(OpenPGPApi api, + Profile profile, + boolean signOnly) + throws PGPException { + Date creationTime = new Date(); + OpenPGPKeyGenerator.WithPrimaryKey builder; + switch (profile.getName()) + { + case "performance": + builder = api.generateKey(PublicKeyPacket.VERSION_6, creationTime, false) + .withPrimaryKey(new KeyPairGeneratorCallback() + { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateEd25519KeyPair(); + } + }) + .addSigningSubkey(new KeyPairGeneratorCallback() + { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateEd25519KeyPair(); + } + }); + + if (!signOnly) + { + builder.addEncryptionSubkey(new KeyPairGeneratorCallback() + { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateX25519KeyPair(); + } + }); + } + break; + + case "compatibility": + builder = api.generateKey(PublicKeyPacket.VERSION_4, creationTime, false) + .withPrimaryKey(new KeyPairGeneratorCallback() + { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateRsaKeyPair(3072); + } + }) + .addSigningSubkey(new KeyPairGeneratorCallback() + { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateRsaKeyPair(3072); + } + }); + + if (!signOnly) + { + builder.addEncryptionSubkey(new KeyPairGeneratorCallback() + { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateRsaKeyPair(3072); + } + }); + } + break; + + case "default": + builder = api.generateKey(PublicKeyPacket.VERSION_6, creationTime, true) + .withPrimaryKey() + .addSigningSubkey(); + if (!signOnly) + { + builder.addEncryptionSubkey(); + } + break; + + case "security": + builder = api.generateKey(PublicKeyPacket.VERSION_6, creationTime, true) + .withPrimaryKey(new KeyPairGeneratorCallback() + { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateEd448KeyPair(); + } + }) + .addSigningSubkey(new KeyPairGeneratorCallback() + { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateEd448KeyPair(); + } + }); + + if (!signOnly) + { + builder.addEncryptionSubkey(new KeyPairGeneratorCallback() + { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateX448KeyPair(); + } + }); + } + break; + + default: + throw new SOPGPException.UnsupportedProfile("generate-key", profile.getName()); + } + return builder; + } + @NotNull @Override public GenerateKey noArmor() { @@ -102,7 +230,13 @@ public class BCGenerateKey @NotNull @Override public GenerateKey profile(@NotNull String s) { - return this; + for (Profile p : PROFILES) { + if (p.getName().equals(s)) { + this.profile = p; + return this; + } + } + throw new SOPGPException.UnsupportedProfile("generate-key", s); } @NotNull diff --git a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCListProfiles.java b/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCListProfiles.java index 9f81c1b..8d1f8a7 100644 --- a/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCListProfiles.java +++ b/bc-sop-api/src/main/java/org/pgpainless/bouncycastle/sop/operation/BCListProfiles.java @@ -5,6 +5,7 @@ import org.jetbrains.annotations.NotNull; import sop.Profile; import sop.operation.ListProfiles; +import java.util.Collections; import java.util.List; public class BCListProfiles @@ -18,6 +19,10 @@ public class BCListProfiles @NotNull @Override public List subcommand(@NotNull String s) { - return List.of(); + switch (s) { + case "generate-key": + return BCGenerateKey.PROFILES; + } + return Collections.emptyList(); } }