Implement different key generation profiles

This commit is contained in:
Paul Schaub 2025-01-20 17:58:07 +01:00
parent 0a196432ff
commit a083d0ebfa
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
2 changed files with 159 additions and 20 deletions

View file

@ -1,10 +1,15 @@
package org.pgpainless.bouncycastle.sop.operation; package org.pgpainless.bouncycastle.sop.operation;
import org.bouncycastle.bcpg.PublicKeyPacket;
import org.bouncycastle.openpgp.PGPException; 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.OpenPGPApi;
import org.bouncycastle.openpgp.api.OpenPGPKey; 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 org.jetbrains.annotations.NotNull;
import sop.Profile;
import sop.Ready; import sop.Ready;
import sop.exception.SOPGPException; import sop.exception.SOPGPException;
import sop.operation.GenerateKey; import sop.operation.GenerateKey;
@ -13,6 +18,7 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -20,10 +26,17 @@ public class BCGenerateKey
extends AbstractBCOperation extends AbstractBCOperation
implements GenerateKey { 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<Profile> PROFILES = Arrays.asList(DEFAULT, SECURITY, PERFORMANCE, COMPATIBILITY);
private boolean armor = true; private boolean armor = true;
private boolean signOnly = false; private boolean signOnly = false;
private final List<String> userIds = new ArrayList<>(); private final List<String> userIds = new ArrayList<>();
private char[] passphrase; private char[] passphrase;
private Profile profile = DEFAULT;
public BCGenerateKey(OpenPGPApi api) { public BCGenerateKey(OpenPGPApi api) {
super(api); super(api);
@ -37,24 +50,10 @@ public class BCGenerateKey
@Override @Override
public void writeTo(@NotNull OutputStream outputStream) throws IOException 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; OpenPGPKey key;
try try {
{ OpenPGPKeyGenerator.WithPrimaryKey builder = generatorForProfile(api, profile, signOnly);
OpenPGPV6KeyGenerator.WithPrimaryKey builder;
if (signOnly)
{
builder = generator.signOnlyKey();
}
else
{
builder = generator.ed25519x25519Key(null);
}
for (String userId : userIds) for (String userId : userIds)
{ {
builder.addUserId(userId); 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 @NotNull
@Override @Override
public GenerateKey noArmor() { public GenerateKey noArmor() {
@ -102,7 +230,13 @@ public class BCGenerateKey
@NotNull @NotNull
@Override @Override
public GenerateKey profile(@NotNull String s) { 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 @NotNull

View file

@ -5,6 +5,7 @@ import org.jetbrains.annotations.NotNull;
import sop.Profile; import sop.Profile;
import sop.operation.ListProfiles; import sop.operation.ListProfiles;
import java.util.Collections;
import java.util.List; import java.util.List;
public class BCListProfiles public class BCListProfiles
@ -18,6 +19,10 @@ public class BCListProfiles
@NotNull @NotNull
@Override @Override
public List<Profile> subcommand(@NotNull String s) { public List<Profile> subcommand(@NotNull String s) {
return List.of(); switch (s) {
case "generate-key":
return BCGenerateKey.PROFILES;
}
return Collections.emptyList();
} }
} }