2021-10-07 15:48:52 +02:00
|
|
|
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2021-06-23 21:18:46 +02:00
|
|
|
package org.pgpainless.example;
|
|
|
|
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
2021-06-24 13:57:53 +02:00
|
|
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
2021-06-23 21:18:46 +02:00
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.security.InvalidAlgorithmParameterException;
|
|
|
|
import java.security.NoSuchAlgorithmException;
|
2021-09-20 12:30:03 +02:00
|
|
|
import java.util.Date;
|
2021-06-23 21:18:46 +02:00
|
|
|
|
|
|
|
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.CompressionAlgorithm;
|
|
|
|
import org.pgpainless.algorithm.EncryptionPurpose;
|
|
|
|
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;
|
2021-09-20 12:30:03 +02:00
|
|
|
import org.pgpainless.key.generation.KeySpecBuilder;
|
2021-06-23 21:18:46 +02:00
|
|
|
import org.pgpainless.key.generation.type.KeyType;
|
|
|
|
import org.pgpainless.key.generation.type.ecc.EllipticCurve;
|
|
|
|
import org.pgpainless.key.generation.type.eddsa.EdDSACurve;
|
|
|
|
import org.pgpainless.key.generation.type.rsa.RsaLength;
|
|
|
|
import org.pgpainless.key.info.KeyRingInfo;
|
2022-09-07 13:35:58 +02:00
|
|
|
import org.pgpainless.key.UserId;
|
|
|
|
import org.pgpainless.s2k.Passphrase;
|
2021-06-23 21:18:46 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This class demonstrates how to use PGPainless to generate secret keys.
|
|
|
|
* In general the starting point for generating secret keys using PGPainless is {@link PGPainless#generateKeyRing()}.
|
|
|
|
* The result ({@link org.pgpainless.key.generation.KeyRingBuilder}) provides some factory methods for key archetypes
|
2021-11-02 12:23:05 +01:00
|
|
|
* such as {@link org.pgpainless.key.generation.KeyRingTemplates#modernKeyRing(String, String)} or
|
|
|
|
* {@link org.pgpainless.key.generation.KeyRingTemplates#simpleRsaKeyRing(String, RsaLength)}.
|
2021-06-23 21:18:46 +02:00
|
|
|
*
|
|
|
|
* Those methods always take a user-id which is used as primary user-id, as well as a passphrase which is used to encrypt
|
|
|
|
* the secret key.
|
|
|
|
* To generate unencrypted secret keys, just pass {@code null} as passphrase.
|
|
|
|
*
|
|
|
|
* Besides the archetype methods, it is possible to generate fully customized keys (see {@link #generateCustomOpenPGPKey()}).
|
|
|
|
*/
|
|
|
|
public class GenerateKeys {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This example demonstrates how to generate a modern OpenPGP key which consists of an ed25519 EdDSA primary key
|
|
|
|
* used solely for certification of subkeys, as well as an ed25519 EdDSA signing subkey, and an X25519 ECDH
|
|
|
|
* encryption subkey.
|
|
|
|
*
|
|
|
|
* This is the recommended way to generate OpenPGP keys with PGPainless.
|
|
|
|
*/
|
|
|
|
@Test
|
2021-12-28 13:32:50 +01:00
|
|
|
public void generateModernEcKey()
|
|
|
|
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
2021-06-23 21:18:46 +02:00
|
|
|
// Define a primary user-id
|
|
|
|
String userId = "gbaker@pgpainless.org";
|
|
|
|
// Set a password to protect the secret key
|
|
|
|
String password = "ra1nb0w";
|
|
|
|
// Generate the OpenPGP key
|
|
|
|
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
|
|
|
.modernKeyRing(userId, password);
|
|
|
|
// Extract public key
|
2021-12-28 13:32:50 +01:00
|
|
|
PGPPublicKeyRing publicKey = PGPainless.extractCertificate(secretKey);
|
2021-06-23 21:18:46 +02:00
|
|
|
// Encode the public key to an ASCII armored string ready for sharing
|
2021-09-23 18:06:54 +02:00
|
|
|
String asciiArmoredPublicKey = PGPainless.asciiArmor(publicKey);
|
2021-12-28 13:32:50 +01:00
|
|
|
assertTrue(asciiArmoredPublicKey.startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----"));
|
2021-06-23 21:18:46 +02:00
|
|
|
|
|
|
|
KeyRingInfo keyInfo = new KeyRingInfo(secretKey);
|
|
|
|
assertEquals(3, keyInfo.getSecretKeys().size());
|
|
|
|
assertEquals(userId, keyInfo.getPrimaryUserId());
|
|
|
|
assertEquals(PublicKeyAlgorithm.EDDSA.getAlgorithmId(),
|
|
|
|
keyInfo.getPublicKey().getAlgorithm());
|
|
|
|
assertEquals(PublicKeyAlgorithm.EDDSA.getAlgorithmId(),
|
|
|
|
keyInfo.getSigningSubkeys().get(0).getAlgorithm());
|
|
|
|
assertEquals(PublicKeyAlgorithm.ECDH.getAlgorithmId(),
|
2021-11-02 11:30:44 +01:00
|
|
|
keyInfo.getEncryptionSubkeys(EncryptionPurpose.ANY).get(0).getAlgorithm());
|
2021-06-23 21:18:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-12-28 13:53:25 +01:00
|
|
|
* This example demonstrates how to generate a simple OpenPGP key consisting of a 4096-bit RSA key.
|
2021-06-23 21:18:46 +02:00
|
|
|
* The RSA key is used for both signing and certifying, as well as encryption.
|
|
|
|
*
|
|
|
|
* This method is recommended if the application has to deal with legacy clients with poor algorithm support.
|
|
|
|
*/
|
|
|
|
@Test
|
2021-12-28 13:32:50 +01:00
|
|
|
public void generateSimpleRSAKey()
|
|
|
|
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
2021-06-23 21:18:46 +02:00
|
|
|
// Define a primary user-id
|
|
|
|
String userId = "mpage@pgpainless.org";
|
|
|
|
// Set a password to protect the secret key
|
|
|
|
String password = "b1angl3s";
|
|
|
|
// Generate the OpenPGP key
|
|
|
|
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
|
|
|
.simpleRsaKeyRing(userId, RsaLength._4096, password);
|
|
|
|
|
|
|
|
KeyRingInfo keyInfo = new KeyRingInfo(secretKey);
|
|
|
|
assertEquals(1, keyInfo.getSecretKeys().size());
|
|
|
|
assertEquals(userId, keyInfo.getPrimaryUserId());
|
|
|
|
assertEquals(PublicKeyAlgorithm.RSA_GENERAL.getAlgorithmId(), keyInfo.getPublicKey().getAlgorithm());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This example demonstrates how to generate a simple OpenPGP key based on elliptic curves.
|
2021-12-28 13:53:25 +01:00
|
|
|
* The key consists of an ECDSA primary key that is used both for certification of subkeys, and signing of data,
|
2021-06-23 21:18:46 +02:00
|
|
|
* and a single ECDH encryption subkey.
|
|
|
|
*
|
|
|
|
* This method is recommended if small keys and high performance are desired.
|
|
|
|
*/
|
|
|
|
@Test
|
2021-12-28 13:32:50 +01:00
|
|
|
public void generateSimpleECKey()
|
|
|
|
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
2021-06-24 13:57:53 +02:00
|
|
|
// Define a primary user-id
|
2021-06-23 21:18:46 +02:00
|
|
|
String userId = "mhelms@pgpainless.org";
|
2021-06-24 13:57:53 +02:00
|
|
|
// Set a password to protect the secret key
|
2021-06-23 21:18:46 +02:00
|
|
|
String password = "tr4ns";
|
2021-06-24 13:57:53 +02:00
|
|
|
// Generate the OpenPGP key
|
2021-06-23 21:18:46 +02:00
|
|
|
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
|
|
|
.simpleEcKeyRing(userId, password);
|
|
|
|
|
|
|
|
|
|
|
|
KeyRingInfo keyInfo = new KeyRingInfo(secretKey);
|
|
|
|
assertEquals(2, keyInfo.getSecretKeys().size());
|
|
|
|
assertEquals(userId, keyInfo.getPrimaryUserId());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This example demonstrates how to generate a custom OpenPGP secret key.
|
|
|
|
* Among user-id and password, the user can add an arbitrary number of subkeys and specify their algorithms and
|
|
|
|
* algorithm preferences.
|
|
|
|
*
|
2021-06-24 13:57:53 +02:00
|
|
|
* If the target key amalgamation (key ring) should consist of more than just a single (sub-)key, start by providing
|
2021-09-20 12:30:03 +02:00
|
|
|
* 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)}.
|
2021-06-24 13:57:53 +02:00
|
|
|
*
|
2021-09-20 12:30:03 +02:00
|
|
|
* {@link KeySpec} objects can best be obtained by using the {@link KeySpec#getBuilder(KeyType, KeyFlag, KeyFlag...)}
|
|
|
|
* method and providing a {@link KeyType}.
|
2021-06-24 13:57:53 +02:00
|
|
|
* There are a bunch of factory methods for different {@link KeyType} implementations present in {@link KeyType} itself
|
2021-12-28 13:53:25 +01:00
|
|
|
* (such as {@link KeyType#ECDH(EllipticCurve)}). {@link KeyFlag KeyFlags} determine
|
2021-06-24 13:57:53 +02:00
|
|
|
* the use of the key, like encryption, signing data or certifying subkeys.
|
|
|
|
*
|
2021-09-13 19:20:19 +02:00
|
|
|
* 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...)}.
|
2021-06-24 13:57:53 +02:00
|
|
|
*
|
|
|
|
* Note, that if you set preferred algorithms, the preference lists are sorted from high priority to low priority.
|
|
|
|
*
|
2021-09-20 12:30:03 +02:00
|
|
|
* When setting the primary key spec ({@link org.pgpainless.key.generation.KeyRingBuilder#setPrimaryKey(KeySpecBuilder)}),
|
2021-09-13 19:20:19 +02:00
|
|
|
* make sure that the primary key spec has the {@link KeyFlag} {@link KeyFlag#CERTIFY_OTHER} set, as this is a requirement
|
2021-06-24 13:57:53 +02:00
|
|
|
* for primary keys.
|
|
|
|
*
|
2021-12-28 13:53:25 +01:00
|
|
|
* Furthermore, you have to set at least the primary user-id via
|
2021-09-20 12:30:03 +02:00
|
|
|
* {@link org.pgpainless.key.generation.KeyRingBuilder#addUserId(String)},
|
|
|
|
* but you can also add additional user-ids.
|
2021-06-24 13:57:53 +02:00
|
|
|
*
|
2021-09-20 12:30:03 +02:00
|
|
|
* 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)}.
|
2021-06-23 21:18:46 +02:00
|
|
|
*/
|
|
|
|
@Test
|
2021-12-28 13:32:50 +01:00
|
|
|
public void generateCustomOpenPGPKey()
|
|
|
|
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
2021-06-23 21:18:46 +02:00
|
|
|
// Instead of providing a string, we can assemble a user-id by using the user-id builder.
|
|
|
|
// The example below corresponds to "Morgan Carpenter (Pride!) <mcarpenter@pgpainless.org>"
|
|
|
|
UserId userId = UserId.newBuilder()
|
|
|
|
.withName("Morgan Carpenter")
|
|
|
|
.withEmail("mcarpenter@pgpainless.org")
|
|
|
|
.withComment("Pride!")
|
|
|
|
.build();
|
2021-06-24 13:57:53 +02:00
|
|
|
String additionalUserId = "mcarpenter@christopher.street";
|
2021-06-23 21:18:46 +02:00
|
|
|
|
|
|
|
// It is recommended to use the Passphrase class, as it can be used to safely invalidate passwords from memory
|
|
|
|
Passphrase passphrase = Passphrase.fromPassword("1nters3x");
|
|
|
|
|
2021-11-02 12:23:05 +01:00
|
|
|
PGPSecretKeyRing secretKey = PGPainless.buildKeyRing()
|
2021-09-20 12:30:03 +02:00
|
|
|
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519),
|
|
|
|
// The primary key MUST carry the CERTIFY_OTHER flag, but CAN carry additional flags
|
|
|
|
KeyFlag.CERTIFY_OTHER))
|
2021-06-23 21:18:46 +02:00
|
|
|
// Add the first subkey (in this case encryption)
|
2021-09-20 12:30:03 +02:00
|
|
|
.addSubkey(KeySpec.getBuilder(
|
|
|
|
// We choose an ECDH key over the brainpoolp256r1 curve
|
|
|
|
KeyType.ECDH(EllipticCurve._BRAINPOOLP256R1),
|
2021-12-28 13:53:25 +01:00
|
|
|
// Our key can encrypt both communication data, and data at rest
|
2021-09-20 12:30:03 +02:00
|
|
|
KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS
|
|
|
|
)
|
|
|
|
// Optionally: Configure the subkey with custom algorithm preferences
|
2021-12-28 13:53:25 +01:00
|
|
|
// It is recommended though to go with PGPainless' defaults which can be found in the
|
2021-09-20 12:30:03 +02:00
|
|
|
// 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())
|
2021-06-23 21:18:46 +02:00
|
|
|
// Add the second subkey (signing)
|
2021-09-20 12:30:03 +02:00
|
|
|
.addSubkey(KeySpec.getBuilder(
|
|
|
|
KeyType.ECDSA(EllipticCurve._BRAINPOOLP384R1),
|
|
|
|
// This key is used for creating signatures only
|
|
|
|
KeyFlag.SIGN_DATA
|
|
|
|
))
|
2021-06-24 13:57:53 +02:00
|
|
|
// Set primary user-id
|
2021-09-20 12:30:03 +02:00
|
|
|
.addUserId(userId)
|
2021-06-24 13:57:53 +02:00
|
|
|
// Add an additional user id. This step can be repeated
|
2021-09-20 12:30:03 +02:00
|
|
|
.addUserId(additionalUserId)
|
2021-06-24 13:57:53 +02:00
|
|
|
// Set passphrase. Alternatively use .withoutPassphrase() to leave key unprotected.
|
2021-09-20 12:30:03 +02:00
|
|
|
.setPassphrase(passphrase)
|
2021-06-23 21:18:46 +02:00
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
|
|
KeyRingInfo keyInfo = new KeyRingInfo(secretKey);
|
|
|
|
assertEquals(3, keyInfo.getSecretKeys().size());
|
|
|
|
assertEquals("Morgan Carpenter (Pride!) <mcarpenter@pgpainless.org>", keyInfo.getPrimaryUserId());
|
2021-06-24 13:57:53 +02:00
|
|
|
assertTrue(keyInfo.isUserIdValid(additionalUserId));
|
2021-06-23 21:18:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|