From 460e1277a571418fb47eea7ada2136d680d220a5 Mon Sep 17 00:00:00 2001 From: Slawomir Jaranowski Date: Wed, 23 Dec 2020 16:44:37 +0100 Subject: [PATCH] support for sub keys --- pom.xml | 7 +- src/it/big-artifact/pom-test.xml | 2 +- src/it/pom-packaging/pom-test.xml | 2 +- src/it/standard-packaging/pom-test.xml | 5 +- src/it/verify-sign/keys-map.txt | 3 +- src/it/verify-sign/pom-test.xml | 6 +- .../sign/openpgp/PGPSecretKeyUtils.java | 136 ++++++++++++++++++ .../plugins/sign/openpgp/PGPSigner.java | 43 +++--- .../simplify4u/plugins/sign/SignMojoTest.java | 2 +- .../plugins/sign/openpgp/PGPKeyInfoTest.java | 2 +- .../plugins/sign/openpgp/PGPSignerTest.java | 42 +++++- ...v-key-no-pass.asc => priv-key-no-pass.asc} | 0 .../{pgp-priv-key.asc => priv-key.asc} | 0 src/test/resources/priv-sub-key-no-pass.asc | 70 +++++++++ ...CECF1D8377ED2E01001AE20EA8FD0653A5CC70.asc | 50 +++++++ ...065DCAA903A7785FF79E6EAC71B3E31C0C0D38.asc | 0 16 files changed, 334 insertions(+), 36 deletions(-) create mode 100644 src/main/java/org/simplify4u/plugins/sign/openpgp/PGPSecretKeyUtils.java rename src/test/resources/{pgp-priv-key-no-pass.asc => priv-key-no-pass.asc} (100%) rename src/test/resources/{pgp-priv-key.asc => priv-key.asc} (100%) create mode 100644 src/test/resources/priv-sub-key-no-pass.asc create mode 100644 src/test/resources/public-keys/BB/CE/BBCECF1D8377ED2E01001AE20EA8FD0653A5CC70.asc rename src/test/resources/{ => public-keys}/BC/06/BC065DCAA903A7785FF79E6EAC71B3E31C0C0D38.asc (100%) diff --git a/pom.xml b/pom.xml index 4106512..0153306 100644 --- a/pom.xml +++ b/pom.xml @@ -308,6 +308,9 @@ + org.apache.maven apache-maven @@ -332,10 +335,10 @@ copy-resources - ${project.build.directory}/keys-cache/BC/06 + ${project.build.directory}/keys-cache - src/test/resources/BC/06 + src/test/resources/public-keys diff --git a/src/it/big-artifact/pom-test.xml b/src/it/big-artifact/pom-test.xml index ce43a5b..3344792 100644 --- a/src/it/big-artifact/pom-test.xml +++ b/src/it/big-artifact/pom-test.xml @@ -65,7 +65,7 @@ AC71B3E31C0C0D38 testPass - @project.basedir@/src/test/resources/pgp-priv-key.asc + @project.basedir@/src/test/resources/priv-key.asc diff --git a/src/it/pom-packaging/pom-test.xml b/src/it/pom-packaging/pom-test.xml index 01a6d95..dee5b6f 100644 --- a/src/it/pom-packaging/pom-test.xml +++ b/src/it/pom-packaging/pom-test.xml @@ -41,7 +41,7 @@ AC71B3E31C0C0D38 testPass - @project.basedir@/src/test/resources/pgp-priv-key.asc + @project.basedir@/src/test/resources/priv-key.asc diff --git a/src/it/standard-packaging/pom-test.xml b/src/it/standard-packaging/pom-test.xml index c04d2af..cc4dc7e 100644 --- a/src/it/standard-packaging/pom-test.xml +++ b/src/it/standard-packaging/pom-test.xml @@ -20,7 +20,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - test-group + test-group-sub standard-packaging ${revision} @@ -66,8 +66,7 @@ sign - testPass - @project.basedir@/src/test/resources/pgp-priv-key.asc + @project.basedir@/src/test/resources/priv-sub-key-no-pass.asc diff --git a/src/it/verify-sign/keys-map.txt b/src/it/verify-sign/keys-map.txt index ce5d924..0fb063a 100644 --- a/src/it/verify-sign/keys-map.txt +++ b/src/it/verify-sign/keys-map.txt @@ -14,4 +14,5 @@ # limitations under the License. # -test-group = 0xBC065DCAA903A7785FF79E6EAC71B3E31C0C0D38 +test-group = 0xBC065DCAA903A7785FF79E6EAC71B3E31C0C0D38 +test-group-sub = 0xF6A2666F8A3B4AF014BCA4B973593FDED8C63A19 # master key fingerprint diff --git a/src/it/verify-sign/pom-test.xml b/src/it/verify-sign/pom-test.xml index 46249c0..271b00d 100644 --- a/src/it/verify-sign/pom-test.xml +++ b/src/it/verify-sign/pom-test.xml @@ -41,18 +41,18 @@ - test-group + test-group-sub standard-packaging 1.1.1 - test-group + test-group-sub standard-packaging 1.1.1 dat - test-group + test-group-sub standard-packaging 1.1.1 dat diff --git a/src/main/java/org/simplify4u/plugins/sign/openpgp/PGPSecretKeyUtils.java b/src/main/java/org/simplify4u/plugins/sign/openpgp/PGPSecretKeyUtils.java new file mode 100644 index 0000000..e537eb3 --- /dev/null +++ b/src/main/java/org/simplify4u/plugins/sign/openpgp/PGPSecretKeyUtils.java @@ -0,0 +1,136 @@ +/* + * Copyright 2020 Slawomir Jaranowski + * + * 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.simplify4u.plugins.sign.openpgp; + +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSignature; + +/** + * Utility for operation on Secret Keys. + * + * @author Slawomir Jaranowski + */ +public final class PGPSecretKeyUtils { + + private PGPSecretKeyUtils() { + } + + /** + * Generate keyId as hex string. + * + * @param secretKey a key to print id + * + * @return keyId in hex format + */ + public static String getKeyId(PGPSecretKey secretKey) { + return String.format("%16X", secretKey.getKeyID()); + } + + /** + * List of user ids from secret key. If secret key is sub key list is taken from master key. + * + * @param secretKey a secret key for user ids + * @param secretKeyRing a keyRing of connected keys - need for sub key + * + * @return List user ids from key + */ + public static Collection getUserIDs(PGPSecretKey secretKey, PGPSecretKeyRing secretKeyRing) { + // use getRawUserIDs and standard java String to transform byte array to utf8 + // because BC generate exception if there is some problem in decoding utf8 + // https://github.com/s4u/pgpverify-maven-plugin/issues/61 + Set ret = new LinkedHashSet<>(); + secretKey.getPublicKey().getRawUserIDs().forEachRemaining(ret::add); + + getMasterKey(secretKey, secretKeyRing).ifPresent(masterKey -> + masterKey.getPublicKey().getRawUserIDs().forEachRemaining(ret::add) + ); + + return ret.stream() + .map(b -> new String(b, StandardCharsets.UTF_8)) + .collect(Collectors.toSet()); + } + + /** + * Generate string with key id description. + * + * @param secretKey given key + * @param secretKeyRing keys ring with master and sub keys + * + * @return string with key id description + */ + public static String keyIdDescription(PGPSecretKey secretKey, PGPSecretKeyRing secretKeyRing) { + + Optional masterKey = getMasterKey(secretKey, secretKeyRing); + + if (masterKey.isPresent()) { + return String.format("SubKeyId: 0x%016X of %s", secretKey.getKeyID(), fingerprint(masterKey.get())); + } else { + return "KeyId: " + fingerprint(secretKey); + } + } + + /** + * Return master key for given sub public key. + * + * @param secretKey given key + * @param secretKeyRing keys ring with master and sub keys + * + * @return master key of empty if not found or given key is master key + */ + @SuppressWarnings("unchecked") + public static Optional getMasterKey(PGPSecretKey secretKey, PGPSecretKeyRing secretKeyRing) { + + if (secretKey.isMasterKey()) { + return Optional.empty(); + } + + Iterable signatures = () -> + secretKey.getPublicKey().getSignaturesOfType(PGPSignature.SUBKEY_BINDING); + + return StreamSupport.stream(signatures.spliterator(), false) + .map(s -> secretKeyRing.getSecretKey(s.getKeyID())) + .findFirst(); + } + + /** + * Generate string version of key fingerprint + * + * @param secretKey given key + * + * @return fingerprint as string + */ + public static String fingerprint(PGPSecretKey secretKey) { + return fingerprintToString(secretKey.getPublicKey().getFingerprint()); + } + + private static String fingerprintToString(byte[] bytes) { + StringBuilder ret = new StringBuilder(); + ret.append("0x"); + for (byte b : bytes) { + ret.append(String.format("%02X", b)); + } + return ret.toString(); + } +} diff --git a/src/main/java/org/simplify4u/plugins/sign/openpgp/PGPSigner.java b/src/main/java/org/simplify4u/plugins/sign/openpgp/PGPSigner.java index 49cba49..ecf2853 100644 --- a/src/main/java/org/simplify4u/plugins/sign/openpgp/PGPSigner.java +++ b/src/main/java/org/simplify4u/plugins/sign/openpgp/PGPSigner.java @@ -20,11 +20,14 @@ import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; +import java.util.Optional; +import java.util.stream.StreamSupport; import javax.inject.Named; +import static org.simplify4u.plugins.sign.openpgp.PGPSecretKeyUtils.getKeyId; +import static org.simplify4u.plugins.sign.openpgp.PGPSecretKeyUtils.getUserIDs; +import static org.simplify4u.plugins.sign.openpgp.PGPSecretKeyUtils.keyIdDescription; + import lombok.extern.slf4j.Slf4j; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.bcpg.BCPGOutputStream; @@ -58,6 +61,7 @@ public class PGPSigner { private PGPSecretKey secretKey; private PGPPrivateKey pgpPrivateKey; private PGPSignatureSubpacketVector hashSubPackets; + private PGPSecretKeyRing secretKeyRing; PGPSigner() { // empty one @@ -65,6 +69,7 @@ public class PGPSigner { /** * Setup key info which will be used for signing + * * @param keyInfo private key info */ public void setKeyInfo(PGPKeyInfo keyInfo) { @@ -78,9 +83,9 @@ public void setKeyInfo(PGPKeyInfo keyInfo) { } if (LOGGER.isInfoEnabled()) { - List uIds = new ArrayList<>(); - secretKey.getUserIDs().forEachRemaining(uIds::add); - LOGGER.info("Loaded keyId: {}, uIds: {}", String.format("%16X", secretKey.getKeyID()), uIds); + LOGGER.info("Loaded {}, uids: {}", + keyIdDescription(secretKey, secretKeyRing), + getUserIDs(secretKey, secretKeyRing)); } } @@ -101,20 +106,20 @@ private void loadKey() throws IOException, PGPException { new JcaKeyFingerprintCalculator()); Long keyId = pgpKeyInfo.getId(); + Optional secretKeyOptional; if (keyId != null) { - secretKey = pgpSecretKeyRingCollection.getSecretKey(keyId); + secretKeyOptional = Optional.ofNullable(pgpSecretKeyRingCollection.getSecretKey(keyId)); } else { - // retrieve first master key - Iterator keyRings = pgpSecretKeyRingCollection.getKeyRings(); - if (keyRings.hasNext()) { - PGPSecretKeyRing secretKeys = keyRings.next(); - secretKey = secretKeys.getSecretKey(); - } + // retrieve first key with private key + secretKeyOptional = StreamSupport.stream(pgpSecretKeyRingCollection.spliterator(), false) + .flatMap(k -> StreamSupport.stream(k.spliterator(), false)) + .filter(key -> !key.isPrivateKeyEmpty()) + .findFirst(); } - if (secretKey == null) { - throw new PGPSignerException("Secret key not found"); - } + secretKey = secretKeyOptional.orElseThrow(() -> new PGPSignerException("Secret key not found")); + + secretKeyRing = pgpSecretKeyRingCollection.getSecretKeyRing(secretKey.getKeyID()); if (secretKey.getKeyEncryptionAlgorithm() == SymmetricKeyAlgorithmTags.NULL && pgpKeyInfo.getPass() != null) { LOGGER.warn("Plain secret key - password is not needed"); @@ -124,6 +129,10 @@ private void loadKey() throws IOException, PGPException { throw new PGPSignerException("Secret key is encrypted - keyPass is required"); } + if (secretKey.isPrivateKeyEmpty()) { + throw new PGPSignerException("Private key not found for keyId: " + getKeyId(secretKey)); + } + pgpPrivateKey = secretKey .extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().build(pgpKeyInfo.getPass())); } @@ -139,7 +148,7 @@ private void loadKey() throws IOException, PGPException { public void sign(InputStream inputStream, Path outputPath) { PGPSignatureGenerator sGen = new PGPSignatureGenerator( - new JcaPGPContentSignerBuilder(secretKey.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA256)); + new JcaPGPContentSignerBuilder(secretKey.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA512)); try { sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivateKey); diff --git a/src/test/java/org/simplify4u/plugins/sign/SignMojoTest.java b/src/test/java/org/simplify4u/plugins/sign/SignMojoTest.java index 8822431..8956384 100644 --- a/src/test/java/org/simplify4u/plugins/sign/SignMojoTest.java +++ b/src/test/java/org/simplify4u/plugins/sign/SignMojoTest.java @@ -104,7 +104,7 @@ void setup() { when(artifactSignerFactory.getSigner(any())).thenReturn(artifactSigner); //setup default values of mojo - mojo.setKeyFile(new File(getClass().getResource("/pgp-priv-key-no-pass.asc").getFile())); + mojo.setKeyFile(new File(getClass().getResource("/priv-key-no-pass.asc").getFile())); } @Test diff --git a/src/test/java/org/simplify4u/plugins/sign/openpgp/PGPKeyInfoTest.java b/src/test/java/org/simplify4u/plugins/sign/openpgp/PGPKeyInfoTest.java index fab41d7..404b6e4 100644 --- a/src/test/java/org/simplify4u/plugins/sign/openpgp/PGPKeyInfoTest.java +++ b/src/test/java/org/simplify4u/plugins/sign/openpgp/PGPKeyInfoTest.java @@ -33,7 +33,7 @@ class PGPKeyInfoTest { private static final String KEY_PASS_STR = "pass"; private static final char[] KEY_PASS = KEY_PASS_STR.toCharArray(); - private static final File KEY_FILE = new File(PGPKeyInfo.class.getResource("/pgp-priv-key-no-pass.asc").getFile()); + private static final File KEY_FILE = new File(PGPKeyInfo.class.getResource("/priv-key-no-pass.asc").getFile()); @Test void keyFromFileAllPropertiesSet() throws FileNotFoundException { diff --git a/src/test/java/org/simplify4u/plugins/sign/openpgp/PGPSignerTest.java b/src/test/java/org/simplify4u/plugins/sign/openpgp/PGPSignerTest.java index 89e263a..139d6cd 100644 --- a/src/test/java/org/simplify4u/plugins/sign/openpgp/PGPSignerTest.java +++ b/src/test/java/org/simplify4u/plugins/sign/openpgp/PGPSignerTest.java @@ -34,7 +34,7 @@ void loadKeyWithAllProperties() throws PGPSignerException { PGPKeyInfo keyInfo = PGPKeyInfo.builder() .keyId("AC71B3E31C0C0D38") .keyPass("testPass") - .keyFile(new File(getClass().getResource("/pgp-priv-key.asc").getFile())) + .keyFile(new File(getClass().getResource("/priv-key.asc").getFile())) .build(); @@ -49,7 +49,7 @@ void loadDefaultKey() throws PGPSignerException { // given PGPKeyInfo keyInfo = PGPKeyInfo.builder() .keyPass("testPass") - .keyFile(new File(getClass().getResource("/pgp-priv-key.asc").getFile())) + .keyFile(new File(getClass().getResource("/priv-key.asc").getFile())) .build(); @@ -63,7 +63,7 @@ void loadDefaultKeyWithOutPass() throws PGPSignerException { // given PGPKeyInfo keyInfo = PGPKeyInfo.builder() - .keyFile(new File(getClass().getResource("/pgp-priv-key-no-pass.asc").getFile())) + .keyFile(new File(getClass().getResource("/priv-key-no-pass.asc").getFile())) .build(); @@ -72,13 +72,43 @@ void loadDefaultKeyWithOutPass() throws PGPSignerException { .doesNotThrowAnyException(); } + @Test + void loadSubKeyWithOutPass() throws PGPSignerException { + + // given + PGPKeyInfo keyInfo = PGPKeyInfo.builder() + .keyFile(new File(getClass().getResource("/priv-sub-key-no-pass.asc").getFile())) + .build(); + + + // when + assertThatCode(() -> pgpSigner.setKeyInfo(keyInfo)) + .doesNotThrowAnyException(); + } + + @Test + void loadSubKeyWithMasterKey() throws PGPSignerException { + + // given + PGPKeyInfo keyInfo = PGPKeyInfo.builder() + .keyId("73593FDED8C63A19") + .keyFile(new File(getClass().getResource("/priv-sub-key-no-pass.asc").getFile())) + .build(); + + + // when + assertThatThrownBy(() -> pgpSigner.setKeyInfo(keyInfo)) + .isExactlyInstanceOf(PGPSignerException.class) + .hasMessage("Private key not found for keyId: 73593FDED8C63A19"); + } + @Test void keyWithOutPassButPassGiven() throws PGPSignerException { // given PGPKeyInfo keyInfo = PGPKeyInfo.builder() .keyPass("testPass") - .keyFile(new File(getClass().getResource("/pgp-priv-key-no-pass.asc").getFile())) + .keyFile(new File(getClass().getResource("/priv-key-no-pass.asc").getFile())) .build(); @@ -92,7 +122,7 @@ void requireNullPassThrowException() throws PGPSignerException { // given PGPKeyInfo keyInfo = PGPKeyInfo.builder() - .keyFile(new File(getClass().getResource("/pgp-priv-key.asc").getFile())) + .keyFile(new File(getClass().getResource("/priv-key.asc").getFile())) .build(); @@ -109,7 +139,7 @@ void requireInvalidPassThrowException() throws PGPSignerException { // given PGPKeyInfo keyInfo = PGPKeyInfo.builder() .keyPass("xxx") - .keyFile(new File(getClass().getResource("/pgp-priv-key.asc").getFile())) + .keyFile(new File(getClass().getResource("/priv-key.asc").getFile())) .build(); diff --git a/src/test/resources/pgp-priv-key-no-pass.asc b/src/test/resources/priv-key-no-pass.asc similarity index 100% rename from src/test/resources/pgp-priv-key-no-pass.asc rename to src/test/resources/priv-key-no-pass.asc diff --git a/src/test/resources/pgp-priv-key.asc b/src/test/resources/priv-key.asc similarity index 100% rename from src/test/resources/pgp-priv-key.asc rename to src/test/resources/priv-key.asc diff --git a/src/test/resources/priv-sub-key-no-pass.asc b/src/test/resources/priv-sub-key-no-pass.asc new file mode 100644 index 0000000..0b31810 --- /dev/null +++ b/src/test/resources/priv-sub-key-no-pass.asc @@ -0,0 +1,70 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +=vtwp +-----END PGP PRIVATE KEY BLOCK----- diff --git a/src/test/resources/public-keys/BB/CE/BBCECF1D8377ED2E01001AE20EA8FD0653A5CC70.asc b/src/test/resources/public-keys/BB/CE/BBCECF1D8377ED2E01001AE20EA8FD0653A5CC70.asc new file mode 100644 index 0000000..67c55e2 --- /dev/null +++ b/src/test/resources/public-keys/BB/CE/BBCECF1D8377ED2E01001AE20EA8FD0653A5CC70.asc @@ -0,0 +1,50 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +KvoEertDdbb8lbzLlbNXBNQd3vDo0A== +=xTUj +-----END PGP PUBLIC KEY BLOCK----- diff --git a/src/test/resources/BC/06/BC065DCAA903A7785FF79E6EAC71B3E31C0C0D38.asc b/src/test/resources/public-keys/BC/06/BC065DCAA903A7785FF79E6EAC71B3E31C0C0D38.asc similarity index 100% rename from src/test/resources/BC/06/BC065DCAA903A7785FF79E6EAC71B3E31C0C0D38.asc rename to src/test/resources/public-keys/BC/06/BC065DCAA903A7785FF79E6EAC71B3E31C0C0D38.asc