mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-06-17 09:04:50 +02:00
Paul Schaub
68575f9f1e
Move ArmorUtils to org.pgpainless.ascii_armor Move Armored*StreamFactory to org.pgpainless.ascii_armor Move CRCingArmoredInputStreamWrapper to org.pgpainless.ascii_armor Move SessionKey to org.pgpainless.s2k Move RevocationAttributes to org.pgpainless.key Move UserId to org.pgpainless.key Move Passphrase to org.pgpainless.s2k Move NotationRegistry to org.pgpainless.policy
222 lines
12 KiB
Java
222 lines
12 KiB
Java
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package org.pgpainless.example;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
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.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;
|
|
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;
|
|
import org.pgpainless.key.generation.type.rsa.RsaLength;
|
|
import org.pgpainless.key.info.KeyRingInfo;
|
|
import org.pgpainless.key.UserId;
|
|
import org.pgpainless.s2k.Passphrase;
|
|
|
|
/**
|
|
* 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
|
|
* such as {@link org.pgpainless.key.generation.KeyRingTemplates#modernKeyRing(String, String)} or
|
|
* {@link org.pgpainless.key.generation.KeyRingTemplates#simpleRsaKeyRing(String, RsaLength)}.
|
|
*
|
|
* 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
|
|
public void generateModernEcKey()
|
|
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
|
// 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
|
|
PGPPublicKeyRing publicKey = PGPainless.extractCertificate(secretKey);
|
|
// Encode the public key to an ASCII armored string ready for sharing
|
|
String asciiArmoredPublicKey = PGPainless.asciiArmor(publicKey);
|
|
assertTrue(asciiArmoredPublicKey.startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----"));
|
|
|
|
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(),
|
|
keyInfo.getEncryptionSubkeys(EncryptionPurpose.ANY).get(0).getAlgorithm());
|
|
}
|
|
|
|
/**
|
|
* This example demonstrates how to generate a simple OpenPGP key consisting of a 4096-bit RSA key.
|
|
* 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
|
|
public void generateSimpleRSAKey()
|
|
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
|
// 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.
|
|
* The key consists of an ECDSA primary key that is used both for certification of subkeys, and signing of data,
|
|
* and a single ECDH encryption subkey.
|
|
*
|
|
* This method is recommended if small keys and high performance are desired.
|
|
*/
|
|
@Test
|
|
public void generateSimpleECKey()
|
|
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
|
// Define a primary user-id
|
|
String userId = "mhelms@pgpainless.org";
|
|
// Set a password to protect the secret key
|
|
String password = "tr4ns";
|
|
// Generate the OpenPGP key
|
|
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.
|
|
*
|
|
* If the target key amalgamation (key ring) should consist of more than just a single (sub-)key, start by providing
|
|
* 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, 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)}). {@link KeyFlag KeyFlags} determine
|
|
* the use of the key, like encryption, signing data or certifying subkeys.
|
|
*
|
|
* 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#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.KeyRingBuilder#addUserId(String)},
|
|
* but you can also add additional user-ids.
|
|
*
|
|
* 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)}.
|
|
*/
|
|
@Test
|
|
public void generateCustomOpenPGPKey()
|
|
throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
|
// 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();
|
|
String additionalUserId = "mcarpenter@christopher.street";
|
|
|
|
// It is recommended to use the Passphrase class, as it can be used to safely invalidate passwords from memory
|
|
Passphrase passphrase = Passphrase.fromPassword("1nters3x");
|
|
|
|
PGPSecretKeyRing secretKey = PGPainless.buildKeyRing()
|
|
.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)
|
|
.addSubkey(KeySpec.getBuilder(
|
|
// We choose an ECDH key over the brainpoolp256r1 curve
|
|
KeyType.ECDH(EllipticCurve._BRAINPOOLP256R1),
|
|
// Our key can encrypt both communication data, and data at rest
|
|
KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS
|
|
)
|
|
// Optionally: Configure the subkey with custom algorithm preferences
|
|
// It 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
|
|
.addUserId(userId)
|
|
// Add an additional user id. This step can be repeated
|
|
.addUserId(additionalUserId)
|
|
// Set passphrase. Alternatively use .withoutPassphrase() to leave key unprotected.
|
|
.setPassphrase(passphrase)
|
|
.build();
|
|
|
|
|
|
KeyRingInfo keyInfo = new KeyRingInfo(secretKey);
|
|
assertEquals(3, keyInfo.getSecretKeys().size());
|
|
assertEquals("Morgan Carpenter (Pride!) <mcarpenter@pgpainless.org>", keyInfo.getPrimaryUserId());
|
|
assertTrue(keyInfo.isUserIdValid(additionalUserId));
|
|
}
|
|
|
|
}
|