diff --git a/build.gradle b/build.gradle index 5802b4b9..906f6060 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,6 @@ repositories { dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' - compile 'org.bouncycastle:bcprov-debug-jdk15on:1.59' + compile 'org.bouncycastle:bcprov-jdk15on:1.59' compile 'org.bouncycastle:bcpg-jdk15on:1.59' } diff --git a/src/main/java/org/pgpainless/pgpainless/PGPainless.java b/src/main/java/org/pgpainless/pgpainless/PGPainless.java index fcc17f1e..5465b6e1 100644 --- a/src/main/java/org/pgpainless/pgpainless/PGPainless.java +++ b/src/main/java/org/pgpainless/pgpainless/PGPainless.java @@ -22,7 +22,7 @@ import org.pgpainless.pgpainless.decryption_verification.DecryptionBuilder; import org.pgpainless.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.pgpainless.encryption_signing.EncryptionBuilder; import org.pgpainless.pgpainless.encryption_signing.EncryptionStream; -import org.pgpainless.pgpainless.key.KeyRingReader; +import org.pgpainless.pgpainless.key.parsing.KeyRingReader; import org.pgpainless.pgpainless.key.generation.KeyRingBuilder; import org.pgpainless.pgpainless.symmetric_encryption.SymmetricEncryptorDecryptor; diff --git a/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionBuilder.java b/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionBuilder.java index 5ada109f..0823e095 100644 --- a/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionBuilder.java +++ b/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionBuilder.java @@ -25,7 +25,7 @@ import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; -import org.pgpainless.pgpainless.key.SecretKeyRingProtector; +import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector; public class DecryptionBuilder implements DecryptionBuilderInterface { diff --git a/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionBuilderInterface.java b/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionBuilderInterface.java index 2339c53c..081c68bf 100644 --- a/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionBuilderInterface.java +++ b/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionBuilderInterface.java @@ -23,7 +23,7 @@ import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; -import org.pgpainless.pgpainless.key.SecretKeyRingProtector; +import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector; public interface DecryptionBuilderInterface { diff --git a/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionStreamFactory.java b/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionStreamFactory.java index bd9c44b9..f89d5573 100644 --- a/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionStreamFactory.java +++ b/src/main/java/org/pgpainless/pgpainless/decryption_verification/DecryptionStreamFactory.java @@ -48,7 +48,7 @@ import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory; import org.pgpainless.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.pgpainless.algorithm.SymmetricKeyAlgorithm; -import org.pgpainless.pgpainless.key.SecretKeyRingProtector; +import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector; public final class DecryptionStreamFactory { diff --git a/src/main/java/org/pgpainless/pgpainless/encryption_signing/EncryptionBuilder.java b/src/main/java/org/pgpainless/pgpainless/encryption_signing/EncryptionBuilder.java index 8cc0b801..9c329873 100644 --- a/src/main/java/org/pgpainless/pgpainless/encryption_signing/EncryptionBuilder.java +++ b/src/main/java/org/pgpainless/pgpainless/encryption_signing/EncryptionBuilder.java @@ -32,7 +32,7 @@ import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.pgpainless.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.pgpainless.algorithm.SymmetricKeyAlgorithm; -import org.pgpainless.pgpainless.key.SecretKeyRingProtector; +import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.pgpainless.key.selection.key.PublicKeySelectionStrategy; import org.pgpainless.pgpainless.key.selection.key.SecretKeySelectionStrategy; import org.pgpainless.pgpainless.key.selection.key.impl.And; diff --git a/src/main/java/org/pgpainless/pgpainless/encryption_signing/EncryptionBuilderInterface.java b/src/main/java/org/pgpainless/pgpainless/encryption_signing/EncryptionBuilderInterface.java index 5420a2ca..51cd37a2 100644 --- a/src/main/java/org/pgpainless/pgpainless/encryption_signing/EncryptionBuilderInterface.java +++ b/src/main/java/org/pgpainless/pgpainless/encryption_signing/EncryptionBuilderInterface.java @@ -29,7 +29,7 @@ import org.pgpainless.pgpainless.SecretKeyNotFoundException; import org.pgpainless.pgpainless.algorithm.CompressionAlgorithm; import org.pgpainless.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.pgpainless.algorithm.SymmetricKeyAlgorithm; -import org.pgpainless.pgpainless.key.SecretKeyRingProtector; +import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.pgpainless.key.selection.keyring.PublicKeyRingSelectionStrategy; import org.pgpainless.pgpainless.key.selection.keyring.SecretKeyRingSelectionStrategy; import org.pgpainless.pgpainless.util.MultiMap; diff --git a/src/main/java/org/pgpainless/pgpainless/key/KeyRingReader.java b/src/main/java/org/pgpainless/pgpainless/key/parsing/KeyRingReader.java similarity index 98% rename from src/main/java/org/pgpainless/pgpainless/key/KeyRingReader.java rename to src/main/java/org/pgpainless/pgpainless/key/parsing/KeyRingReader.java index af601905..37d2f18d 100644 --- a/src/main/java/org/pgpainless/pgpainless/key/KeyRingReader.java +++ b/src/main/java/org/pgpainless/pgpainless/key/parsing/KeyRingReader.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.pgpainless.pgpainless.key; +package org.pgpainless.pgpainless.key.parsing; import java.io.ByteArrayInputStream; import java.io.IOException; diff --git a/src/main/java/org/pgpainless/pgpainless/key/parsing/package-info.java b/src/main/java/org/pgpainless/pgpainless/key/parsing/package-info.java new file mode 100644 index 00000000..ecc226f9 --- /dev/null +++ b/src/main/java/org/pgpainless/pgpainless/key/parsing/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2018 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. + */ +/** + * Classes related to OpenPGP key reading. + */ +package org.pgpainless.pgpainless.key.parsing; diff --git a/src/main/java/org/pgpainless/pgpainless/key/KeyRingProtectionSettings.java b/src/main/java/org/pgpainless/pgpainless/key/protection/KeyRingProtectionSettings.java similarity index 96% rename from src/main/java/org/pgpainless/pgpainless/key/KeyRingProtectionSettings.java rename to src/main/java/org/pgpainless/pgpainless/key/protection/KeyRingProtectionSettings.java index 5962bbc1..7d1d0a87 100644 --- a/src/main/java/org/pgpainless/pgpainless/key/KeyRingProtectionSettings.java +++ b/src/main/java/org/pgpainless/pgpainless/key/protection/KeyRingProtectionSettings.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.pgpainless.pgpainless.key; +package org.pgpainless.pgpainless.key.protection; import org.pgpainless.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.pgpainless.algorithm.SymmetricKeyAlgorithm; diff --git a/src/main/java/org/pgpainless/pgpainless/key/PassphraseMapKeyRingProtector.java b/src/main/java/org/pgpainless/pgpainless/key/protection/PassphraseMapKeyRingProtector.java similarity index 98% rename from src/main/java/org/pgpainless/pgpainless/key/PassphraseMapKeyRingProtector.java rename to src/main/java/org/pgpainless/pgpainless/key/protection/PassphraseMapKeyRingProtector.java index ba895ed1..e2565d95 100644 --- a/src/main/java/org/pgpainless/pgpainless/key/PassphraseMapKeyRingProtector.java +++ b/src/main/java/org/pgpainless/pgpainless/key/protection/PassphraseMapKeyRingProtector.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.pgpainless.pgpainless.key; +package org.pgpainless.pgpainless.key.protection; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/org/pgpainless/pgpainless/key/SecretKeyRingProtector.java b/src/main/java/org/pgpainless/pgpainless/key/protection/SecretKeyRingProtector.java similarity index 96% rename from src/main/java/org/pgpainless/pgpainless/key/SecretKeyRingProtector.java rename to src/main/java/org/pgpainless/pgpainless/key/protection/SecretKeyRingProtector.java index 1427ea81..ff820ce0 100644 --- a/src/main/java/org/pgpainless/pgpainless/key/SecretKeyRingProtector.java +++ b/src/main/java/org/pgpainless/pgpainless/key/protection/SecretKeyRingProtector.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.pgpainless.pgpainless.key; +package org.pgpainless.pgpainless.key.protection; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; diff --git a/src/main/java/org/pgpainless/pgpainless/key/UnprotectedKeysProtector.java b/src/main/java/org/pgpainless/pgpainless/key/protection/UnprotectedKeysProtector.java similarity index 95% rename from src/main/java/org/pgpainless/pgpainless/key/UnprotectedKeysProtector.java rename to src/main/java/org/pgpainless/pgpainless/key/protection/UnprotectedKeysProtector.java index 8c15386a..82711013 100644 --- a/src/main/java/org/pgpainless/pgpainless/key/UnprotectedKeysProtector.java +++ b/src/main/java/org/pgpainless/pgpainless/key/protection/UnprotectedKeysProtector.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.pgpainless.pgpainless.key; +package org.pgpainless.pgpainless.key.protection; import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; diff --git a/src/main/java/org/pgpainless/pgpainless/key/protection/package-info.java b/src/main/java/org/pgpainless/pgpainless/key/protection/package-info.java new file mode 100644 index 00000000..57f95712 --- /dev/null +++ b/src/main/java/org/pgpainless/pgpainless/key/protection/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2018 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. + */ +/** + * Classes related to OpenPGP secret key password protection. + */ +package org.pgpainless.pgpainless.key.protection; diff --git a/src/main/java/org/pgpainless/pgpainless/util/BCUtil.java b/src/main/java/org/pgpainless/pgpainless/util/BCUtil.java index 9ac6fd6b..33583bd8 100644 --- a/src/main/java/org/pgpainless/pgpainless/util/BCUtil.java +++ b/src/main/java/org/pgpainless/pgpainless/util/BCUtil.java @@ -30,7 +30,12 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import org.bouncycastle.bcpg.S2K; +import org.bouncycastle.bcpg.SecretKeyPacket; +import org.bouncycastle.bcpg.SecretSubkeyPacket; +import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyRing; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; @@ -214,6 +219,18 @@ public class BCUtil { return null; } + public static PGPPublicKey getMasterKeyFrom(PGPKeyRing ring) { + Iterator it = ring.getPublicKeys(); + while (it.hasNext()) { + PGPPublicKey k = it.next(); + if (k.isMasterKey()) { + // There can only be one master key, so we can immediately return + return k; + } + } + return null; + } + public static Set signingKeyIds(PGPSecretKeyRing ring) { Set ids = new HashSet<>(); Iterator it = ring.getPublicKeys(); @@ -262,4 +279,54 @@ public class BCUtil { public static boolean keyRingContainsKeyWithId(PGPSecretKeyRing ring, long keyId) { return ring.getSecretKey(keyId) != null; } + + /* + public static PGPKeyRing merge(PGPKeyRing one, PGPKeyRing other) { + + PGPPublicKey masterOne = getMasterKeyFrom(one); + if (masterOne == null) { + throw new IllegalArgumentException("First KeyRing has no master key"); + } + + PGPPublicKey masterOther = getMasterKeyFrom(other); + if (masterOther == null) { + throw new IllegalArgumentException("Other KeyRing has no master key"); + } + + if (masterOne.getKeyID() != masterOther.getKeyID() || + Arrays.equals(masterOne.getFingerprint(), masterOther.getFingerprint())) { + throw new IllegalArgumentException("Keys are not the same."); + } + + PGPKeyRing merged = one; + + boolean mergedIsSecret = (merged instanceof PGPSecretKeyRing); + boolean otherIsSecret = (other instanceof PGPSecretKeyRing); + + for (Iterator it = other.getPublicKeys(); it.hasNext(); ) { + + PGPPublicKey nextPublicKey = (PGPPublicKey) it.next(); + PGPPublicKey pendant = merged.getPublicKey(nextPublicKey.getKeyID()); + + if (pendant == null) { + if (mergedIsSecret && otherIsSecret) { + // Add secret key + PGPSecretKey secretKey = ((PGPSecretKeyRing) other).getSecretKey(nextPublicKey.getKeyID()); + merged = PGPSecretKeyRing.insertSecretKey((PGPSecretKeyRing) merged, secretKey); + } else { + if (mergedIsSecret) { + PGPSecretKeyRing mergedAsSecret = (PGPSecretKeyRing) merged; + PGPSecretKey secretKey = mergedAsSecret.getSecretKey(nextPublicKey.getKeyID()); + if (secretKey == null) { + PGPPublicKeyRing mergedAsPublic = publicKeyRingFromSecretKeyRing((PGPSecretKeyRing) merged); + mergedAsPublic = PGPPublicKeyRing.insertPublicKey(mergedAsPublic, nextPublicKey); + mergedAsSecret = PGPSecretKeyRing.replacePublicKeys(mergedAsSecret, mergedAsPublic); + merged = mergedAsSecret; + } + } + } + } + } + } + */ } diff --git a/src/test/java/org/pgpainless/pgpainless/EncryptDecryptTest.java b/src/test/java/org/pgpainless/pgpainless/EncryptDecryptTest.java index cfdc4ea6..938e25af 100644 --- a/src/test/java/org/pgpainless/pgpainless/EncryptDecryptTest.java +++ b/src/test/java/org/pgpainless/pgpainless/EncryptDecryptTest.java @@ -43,8 +43,8 @@ import org.pgpainless.pgpainless.algorithm.SymmetricKeyAlgorithm; import org.pgpainless.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.pgpainless.decryption_verification.PainlessResult; import org.pgpainless.pgpainless.encryption_signing.EncryptionStream; -import org.pgpainless.pgpainless.key.SecretKeyRingProtector; -import org.pgpainless.pgpainless.key.UnprotectedKeysProtector; +import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector; +import org.pgpainless.pgpainless.key.protection.UnprotectedKeysProtector; import org.pgpainless.pgpainless.key.generation.KeySpec; import org.pgpainless.pgpainless.key.generation.type.ElGamal_GENERAL; import org.pgpainless.pgpainless.key.generation.type.RSA_GENERAL; diff --git a/src/test/java/org/pgpainless/pgpainless/LengthTest.java b/src/test/java/org/pgpainless/pgpainless/LengthTest.java index 8592c15c..9e4a2fde 100644 --- a/src/test/java/org/pgpainless/pgpainless/LengthTest.java +++ b/src/test/java/org/pgpainless/pgpainless/LengthTest.java @@ -31,8 +31,8 @@ import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.util.io.Streams; import org.junit.Ignore; -import org.pgpainless.pgpainless.key.SecretKeyRingProtector; -import org.pgpainless.pgpainless.key.UnprotectedKeysProtector; +import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector; +import org.pgpainless.pgpainless.key.protection.UnprotectedKeysProtector; import org.pgpainless.pgpainless.key.generation.type.length.RsaLength; import org.pgpainless.pgpainless.util.BCUtil; diff --git a/src/test/java/org/pgpainless/pgpainless/TestKeysTest.java b/src/test/java/org/pgpainless/pgpainless/TestKeysTest.java index 809a3142..0abe4083 100644 --- a/src/test/java/org/pgpainless/pgpainless/TestKeysTest.java +++ b/src/test/java/org/pgpainless/pgpainless/TestKeysTest.java @@ -31,7 +31,7 @@ import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.util.io.Streams; import org.junit.Test; import org.pgpainless.pgpainless.decryption_verification.DecryptionStream; -import org.pgpainless.pgpainless.key.UnprotectedKeysProtector; +import org.pgpainless.pgpainless.key.protection.UnprotectedKeysProtector; import org.pgpainless.pgpainless.util.BCUtil; public class TestKeysTest extends AbstractPGPainlessTest {