diff --git a/pgpainless-core/src/test/java/org/pgpainless/example/UnlockSecretKeys.java b/pgpainless-core/src/test/java/org/pgpainless/example/UnlockSecretKeys.java new file mode 100644 index 00000000..5666c022 --- /dev/null +++ b/pgpainless-core/src/test/java/org/pgpainless/example/UnlockSecretKeys.java @@ -0,0 +1,139 @@ +/* + * 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.example; + +import java.io.IOException; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.junit.jupiter.api.Test; +import org.pgpainless.PGPainless; +import org.pgpainless.exception.WrongPassphraseException; +import org.pgpainless.key.OpenPgpV4Fingerprint; +import org.pgpainless.key.TestKeys; +import org.pgpainless.key.protection.CachingSecretKeyRingProtector; +import org.pgpainless.key.protection.SecretKeyRingProtector; +import org.pgpainless.key.protection.UnlockSecretKey; +import org.pgpainless.util.Passphrase; + +/** + * {@link PGPSecretKey PGPSecretKeys} are often password protected to prevent unauthorized access. + * To perform certain actions with secret keys, such as creating signatures or decrypting encrypted messages, + * the secret key needs to be unlocked to access the underlying {@link org.bouncycastle.openpgp.PGPPrivateKey}. + * + * Providing the required {@link org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor}/{@link org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor} + * is a task that needs to be performed by the {@link SecretKeyRingProtector}. + * There are different implementations available that implement this interface. + * + * Below are some examples of how to use these implementations in different scenarios. + */ +public class UnlockSecretKeys { + + /** + * This example demonstrates how to create a {@link SecretKeyRingProtector} for unprotected secret keys. + * + * @throws PGPException + * @throws IOException + */ + @Test + public void unlockUnprotectedKeys() throws PGPException, IOException { + PGPSecretKeyRing unprotectedKey = TestKeys.getJulietSecretKeyRing(); + // This protector will only unlock unprotected keys + SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys(); + + + assertProtectorUnlocksAllSecretKeys(unprotectedKey, protector); + } + + /** + * This example demonstrates how to create a {@link SecretKeyRingProtector} using a single passphrase to unlock + * all secret subkeys of a key. + * + * @throws PGPException + * @throws IOException + */ + @Test + public void unlockWholeKeyWithSamePassphrase() throws PGPException, IOException { + PGPSecretKeyRing secretKey = TestKeys.getCryptieSecretKeyRing(); + // Unlock all subkeys in the secret key with the same passphrase + SecretKeyRingProtector protector = SecretKeyRingProtector.unlockAllKeysWith( + Passphrase.fromPassword(TestKeys.CRYPTIE_PASSWORD), secretKey); + + + assertProtectorUnlocksAllSecretKeys(secretKey, protector); + } + + /** + * This example demonstrates how to create a {@link SecretKeyRingProtector} that uses different + * passphrases per subkey to unlock the secret keys. + * + * @throws PGPException + * @throws IOException + */ + @Test + public void unlockWithPerSubkeyPassphrases() throws PGPException, IOException { + String pgpPrivateKeyBlock = "" + + "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + + "Version: PGPainless\n" + + "Comment: 4F28 5D16 A201 21BB 5B89 E76E 5D71 171E 059A 1D2F\n" + + "Comment: alice@pgpainless.org\n" + + "\n" + + "lIYEYNc8xhYJKwYBBAHaRw8BAQdAQiDSNqkU4b4TmdacOxi9mfw06pI23NNiTj/C\n" + + "K1P+Q0/+CQMCm+6zb2ORC8BgHltvucr4KKx7QZdO5jIDWDZe1DjeS2JsJXoqOMeK\n" + + "yjxB8aVoSZTmsAm1KMbeDWcqtsltgm+DipRUyWFxeYOoj+CugJ42GbQUYWxpY2VA\n" + + "cGdwYWlubGVzcy5vcmeIeAQTFgoAIAUCYNc8xgIbAQUWAgMBAAQLCQgHBRUKCQgL\n" + + "Ah4BAhkBAAoJEF1xFx4Fmh0viTEBAJmfpCJsVi7BzMh2iP6ecWZSRYtgqAhKjGTT\n" + + "4i9IKgIUAP47SbJr4qexi3jWj9W9ng//+CKEQ857Up6iSR6u+3poApyLBGDXPMYS\n" + + "CisGAQQBl1UBBQEBB0BUfOJMRcgWdPeyEz2kL79JfhckuDRAwZJyGd8mcFBofQMB\n" + + "CAf+CQMCKdEpNMEpflBguXamH33Vhx2tr3iYleiWI0VfhPrQledNzJ1uCHFH5q+k\n" + + "UnALeSCLJXIekPl3q1ux9C2MQkD/X4+mHh+fE4gSd1G5nd3oh4h1BBgWCgAdBQJg\n" + + "1zzGAhsMBRYCAwEABAsJCAcFFQoJCAsCHgEACgkQXXEXHgWaHS/8DgD/Qnpe5g6C\n" + + "WHeXvgg06PJR7HRPkpE5NSnqEWP9X0tPe2EBAIdTiDozZ7HL6NVI89MnLBkw+524\n" + + "y5YzlNpQn4Al3tMCnIYEYNc8xhYJKwYBBAHaRw8BAQdAXdTUv2F0XGfi4qFnIPrL\n" + + "YbOpEZIWYjGVf5Ggbs9OBrb+CQMClCGIqeO7yJlgR9z490pJUD4al/ATofqGPPqx\n" + + "VsTz4gl1IkkWKQn7GJv2AYn09jZgnWm2a7u16cS6HZLJjRl2XvMzMQp3dRsOPHTP\n" + + "nulJ7YjVBBgWCgB9BQJg1zzGAhsCBRYCAwEABAsJCAcFFQoJCAsCHgFfIAQZFgoA\n" + + "BgUCYNc8xgAKCRD2BAJwjnXZQQLiAQCJGx9PF5ywwE93nMu8nZrhgDtl/eiCsryM\n" + + "qjDfY5XyCgEAim9m50QU9I9gAzBgLeH2NSJhlHYZZ2LKsRE02tGvXQMACgkQXXEX\n" + + "HgWaHS806AD+KUmSoKja11wJqCMVhYSU2IMGdGYEwa7Dqpbhyzu/LtAA/jmF10Ss\n" + + "UPPI6jsYqxEHzRGex8t971atnDAjvDiS31YN\n" + + "=fTmB\n" + + "-----END PGP PRIVATE KEY BLOCK-----"; + PGPSecretKeyRing secretKey = PGPainless.readKeyRing().secretKeyRing(pgpPrivateKeyBlock); + + CachingSecretKeyRingProtector protector = SecretKeyRingProtector.defaultSecretKeyRingProtector(null); + // Add passphrases for subkeys via public key + protector.addPassphrase(secretKey.getPublicKey(), + Passphrase.fromPassword("pr1maryK3y")); + // or via subkey-id + protector.addPassphrase(3907509425258753406L, + Passphrase.fromPassword("f1rs7subk3y")); + // or via fingerprint + protector.addPassphrase(new OpenPgpV4Fingerprint("DD8E1195E4B1720E7FB10EF7F60402708E75D941"), + Passphrase.fromPassword("s3c0ndsubk3y")); + + + assertProtectorUnlocksAllSecretKeys(secretKey, protector); + } + + private void assertProtectorUnlocksAllSecretKeys(PGPSecretKeyRing secretKey, SecretKeyRingProtector protector) + throws WrongPassphraseException { + for (PGPSecretKey key : secretKey) { + UnlockSecretKey.unlockSecretKey(key, protector); + } + } +}