mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-23 12:52:07 +01:00
Throw WrongPassphraseException when wrong passphrase is provided to unlock secret key
This commit is contained in:
parent
32e1b0c838
commit
5a56949dd7
11 changed files with 172 additions and 24 deletions
|
@ -60,6 +60,7 @@ import org.pgpainless.implementation.ImplementationFactory;
|
|||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
import org.pgpainless.key.SubkeyIdentifier;
|
||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||
import org.pgpainless.key.protection.UnlockSecretKey;
|
||||
import org.pgpainless.signature.DetachedSignature;
|
||||
import org.pgpainless.signature.OnePassSignature;
|
||||
import org.pgpainless.util.IntegrityProtectedInputStream;
|
||||
|
@ -209,7 +210,7 @@ public final class DecryptionStreamFactory {
|
|||
if (!encryptedData.isIntegrityProtected()) {
|
||||
throw new MessageNotIntegrityProtectedException();
|
||||
}
|
||||
|
||||
// Data is passphrase encrypted
|
||||
if (encryptedData instanceof PGPPBEEncryptedData) {
|
||||
PGPPBEEncryptedData pbeEncryptedData = (PGPPBEEncryptedData) encryptedData;
|
||||
if (decryptionPassphrase != null) {
|
||||
|
@ -228,8 +229,9 @@ public final class DecryptionStreamFactory {
|
|||
LOGGER.log(LEVEL, "Probable passphrase mismatch, skip PBE encrypted data block", e);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (encryptedData instanceof PGPPublicKeyEncryptedData) {
|
||||
}
|
||||
// data is public key encrypted
|
||||
else if (encryptedData instanceof PGPPublicKeyEncryptedData) {
|
||||
PGPPublicKeyEncryptedData publicKeyEncryptedData = (PGPPublicKeyEncryptedData) encryptedData;
|
||||
long keyId = publicKeyEncryptedData.getKeyID();
|
||||
if (decryptionKeys != null) {
|
||||
|
@ -242,7 +244,7 @@ public final class DecryptionStreamFactory {
|
|||
LOGGER.log(LEVEL, "Found respective secret key " + Long.toHexString(keyId));
|
||||
// Watch out! This assignment is possibly done multiple times.
|
||||
encryptedSessionKey = publicKeyEncryptedData;
|
||||
decryptionKey = secretKey.extractPrivateKey(decryptionKeyDecryptor.getDecryptor(keyId));
|
||||
decryptionKey = UnlockSecretKey.unlockSecretKey(secretKey, decryptionKeyDecryptor);
|
||||
resultBuilder.setDecryptionFingerprint(new OpenPgpV4Fingerprint(secretKey));
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.pgpainless.key.KeyRingValidator;
|
|||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
import org.pgpainless.key.SubkeyIdentifier;
|
||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||
import org.pgpainless.key.protection.UnlockSecretKey;
|
||||
import org.pgpainless.util.Passphrase;
|
||||
import org.pgpainless.util.Tuple;
|
||||
import org.pgpainless.util.selection.key.PublicKeySelectionStrategy;
|
||||
|
@ -345,7 +346,7 @@ public class EncryptionBuilder implements EncryptionBuilderInterface {
|
|||
PGPSecretKeyRing secretKeyRing = signingKeys.get(signingKey);
|
||||
PGPSecretKey secretKey = secretKeyRing.getSecretKey(signingKey.getSubkeyFingerprint().getKeyId());
|
||||
PBESecretKeyDecryptor decryptor = signingKeysDecryptor.getDecryptor(secretKey.getKeyID());
|
||||
PGPPrivateKey privateKey = secretKey.extractPrivateKey(decryptor);
|
||||
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKey, decryptor);
|
||||
privateKeys.put(signingKey, new Tuple<>(secretKeyRing, privateKey));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.exception;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
|
||||
public class WrongPassphraseException extends PGPException {
|
||||
|
||||
public WrongPassphraseException(long keyId, PGPException cause) {
|
||||
this("Wrong passphrase provided for key " + Long.toHexString(keyId), cause);
|
||||
}
|
||||
|
||||
public WrongPassphraseException(String message, PGPException cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -57,6 +57,7 @@ import org.pgpainless.key.generation.type.KeyType;
|
|||
import org.pgpainless.key.generation.type.eddsa.EdDSACurve;
|
||||
import org.pgpainless.key.generation.type.rsa.RsaLength;
|
||||
import org.pgpainless.key.generation.type.xdh.XDHCurve;
|
||||
import org.pgpainless.key.protection.UnlockSecretKey;
|
||||
import org.pgpainless.key.util.UserId;
|
||||
import org.pgpainless.provider.ProviderFactory;
|
||||
import org.pgpainless.util.Passphrase;
|
||||
|
@ -376,7 +377,7 @@ public class KeyRingBuilder implements KeyRingBuilderInterface {
|
|||
|
||||
// Attempt to add additional user-ids to the primary public key
|
||||
PGPPublicKey primaryPubKey = secretKeys.next().getPublicKey();
|
||||
PGPPrivateKey privateKey = secretKeyRing.getSecretKey().extractPrivateKey(secretKeyDecryptor);
|
||||
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKeyRing.getSecretKey(), secretKeyDecryptor);
|
||||
for (String additionalUserId : additionalUserIds) {
|
||||
signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.getCode(), privateKey);
|
||||
PGPSignature additionalUserIdSignature =
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package org.pgpainless.key.modification.secretkeyring;
|
||||
|
||||
import static org.pgpainless.key.util.KeyRingUtils.unlockSecretKey;
|
||||
import static org.pgpainless.util.CollectionUtils.iteratorToList;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
|
@ -59,6 +58,7 @@ import org.pgpainless.key.protection.KeyRingProtectionSettings;
|
|||
import org.pgpainless.key.protection.PassphraseMapKeyRingProtector;
|
||||
import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector;
|
||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||
import org.pgpainless.key.protection.UnlockSecretKey;
|
||||
import org.pgpainless.key.protection.UnprotectedKeysProtector;
|
||||
import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider;
|
||||
import org.pgpainless.key.util.KeyRingUtils;
|
||||
|
@ -102,7 +102,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
|
|||
if (secretKey.getKeyID() == keyId) {
|
||||
found = true;
|
||||
PGPPublicKey publicKey = secretKey.getPublicKey();
|
||||
PGPPrivateKey privateKey = unlockSecretKey(secretKey, secretKeyRingProtector);
|
||||
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKey, secretKeyRingProtector);
|
||||
publicKey = addUserIdToPubKey(userId, privateKey, publicKey);
|
||||
secretKey = PGPSecretKey.replacePublicKey(secretKey, publicKey);
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
|
|||
HashAlgorithm.SHA256.getAlgorithmId() // TODO: Why SHA256?
|
||||
);
|
||||
|
||||
PGPPrivateKey privateSubKey = unlockSecretKey(secretSubKey, subKeyProtector);
|
||||
PGPPrivateKey privateSubKey = UnlockSecretKey.unlockSecretKey(secretSubKey, subKeyProtector);
|
||||
PGPKeyPair subKeyPair = new PGPKeyPair(secretSubKey.getPublicKey(), privateSubKey);
|
||||
|
||||
PGPKeyRingGenerator keyRingGenerator = new PGPKeyRingGenerator(
|
||||
|
@ -362,7 +362,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
|
|||
RevocationAttributes revocationAttributes) throws PGPException {
|
||||
PGPPublicKey publicKey = KeyRingUtils.requirePublicKeyFrom(secretKeyRing, subKeyId);
|
||||
PGPSecretKey primaryKey = secretKeyRing.getSecretKey();
|
||||
PGPPrivateKey privateKey = unlockSecretKey(primaryKey, protector);
|
||||
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(primaryKey, protector);
|
||||
|
||||
PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator();
|
||||
subpacketGenerator.setSignatureCreationTime(false, new Date());
|
||||
|
@ -445,7 +445,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
|
|||
throw new IllegalArgumentException("Expiration date cannot be before creation date.");
|
||||
}
|
||||
|
||||
PGPPrivateKey privateKey = KeyRingUtils.unlockSecretKey(primaryKey, secretKeyRingProtector);
|
||||
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(primaryKey, secretKeyRingProtector);
|
||||
PGPPublicKey subjectPubKey = subjectKey.getPublicKey();
|
||||
|
||||
PGPSignature oldSignature = getPreviousSignature(primaryKey, subjectPubKey);
|
||||
|
@ -562,7 +562,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
|
|||
PGPSignatureSubpacketVector subPackets = subpacketGenerator.generate();
|
||||
signatureGenerator.setHashedSubpackets(subPackets);
|
||||
|
||||
PGPPrivateKey privateKey = primaryKey.extractPrivateKey(protector.getDecryptor(primaryKey.getKeyID()));
|
||||
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(primaryKey, protector);
|
||||
|
||||
PGPSignature revocation;
|
||||
if (revokeeSubKey.isMasterKey()) {
|
||||
|
@ -675,7 +675,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
|
|||
Iterator<PGPSecretKey> secretKeyIterator = secretKeys.getSecretKeys();
|
||||
while (secretKeyIterator.hasNext()) {
|
||||
PGPSecretKey secretKey = secretKeyIterator.next();
|
||||
PGPPrivateKey privateKey = unlockSecretKey(secretKey, oldProtector);
|
||||
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKey, oldProtector);
|
||||
secretKey = lockPrivateKey(privateKey, secretKey.getPublicKey(), newProtector);
|
||||
newlyEncryptedSecretKeys.add(secretKey);
|
||||
}
|
||||
|
@ -689,7 +689,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
|
|||
|
||||
if (secretKey.getPublicKey().getKeyID() == keyId) {
|
||||
// Re-encrypt only the selected subkey
|
||||
PGPPrivateKey privateKey = unlockSecretKey(secretKey, oldProtector);
|
||||
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKey, oldProtector);
|
||||
secretKey = lockPrivateKey(privateKey, secretKey.getPublicKey(), newProtector);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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.key.protection;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||
import org.pgpainless.exception.WrongPassphraseException;
|
||||
import org.pgpainless.util.Passphrase;
|
||||
|
||||
public class UnlockSecretKey {
|
||||
|
||||
public static PGPPrivateKey unlockSecretKey(PGPSecretKey secretKey, SecretKeyRingProtector protector)
|
||||
throws WrongPassphraseException {
|
||||
try {
|
||||
PBESecretKeyDecryptor decryptor = protector.getDecryptor(secretKey.getKeyID());
|
||||
return secretKey.extractPrivateKey(decryptor);
|
||||
} catch (PGPException e) {
|
||||
throw new WrongPassphraseException(secretKey.getKeyID(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static PGPPrivateKey unlockSecretKey(PGPSecretKey secretKey, SecretKeyRingProtector2 protector) throws WrongPassphraseException {
|
||||
try {
|
||||
PBESecretKeyDecryptor decryptor = protector.getDecryptor(secretKey);
|
||||
return secretKey.extractPrivateKey(decryptor);
|
||||
} catch (PGPException e) {
|
||||
throw new WrongPassphraseException(secretKey.getKeyID(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static PGPPrivateKey unlockSecretKey(PGPSecretKey secretKey, PBESecretKeyDecryptor decryptor) throws WrongPassphraseException {
|
||||
try {
|
||||
return secretKey.extractPrivateKey(decryptor);
|
||||
} catch (PGPException e) {
|
||||
throw new WrongPassphraseException(secretKey.getKeyID(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static PGPPrivateKey unlockSecretKey(PGPSecretKey secretKey, Passphrase passphrase) throws WrongPassphraseException {
|
||||
return unlockSecretKey(secretKey, SecretKeyRingProtector.unlockSingleKeyWith(passphrase, secretKey));
|
||||
}
|
||||
}
|
|
@ -27,8 +27,8 @@ import org.bouncycastle.openpgp.PGPPublicKey;
|
|||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||
import org.pgpainless.key.protection.UnlockSecretKey;
|
||||
|
||||
public class KeyRingUtils {
|
||||
|
||||
|
@ -136,8 +136,6 @@ public class KeyRingUtils {
|
|||
* @throws PGPException if something goes wrong (eg. wrong passphrase)
|
||||
*/
|
||||
public static PGPPrivateKey unlockSecretKey(PGPSecretKey secretKey, SecretKeyRingProtector protector) throws PGPException {
|
||||
PBESecretKeyDecryptor secretKeyDecryptor = protector.getDecryptor(secretKey.getKeyID());
|
||||
PGPPrivateKey privateKey = secretKey.extractPrivateKey(secretKeyDecryptor);
|
||||
return privateKey;
|
||||
return UnlockSecretKey.unlockSecretKey(secretKey, protector);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ import org.pgpainless.key.generation.KeySpec;
|
|||
import org.pgpainless.key.generation.type.ecc.EllipticCurve;
|
||||
import org.pgpainless.key.generation.type.ecc.ecdsa.ECDSA;
|
||||
import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector;
|
||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||
import org.pgpainless.key.protection.UnlockSecretKey;
|
||||
import org.pgpainless.util.Passphrase;
|
||||
|
||||
public class AddSubKeyTest {
|
||||
|
@ -73,9 +75,8 @@ public class AddSubKeyTest {
|
|||
long subKeyId = keyIdsAfter.get(0);
|
||||
|
||||
PGPSecretKey subKey = secretKeys.getSecretKey(subKeyId);
|
||||
PGPPrivateKey privateKey = subKey.extractPrivateKey(
|
||||
PasswordBasedSecretKeyRingProtector
|
||||
.forKey(subKey, Passphrase.fromPassword("subKeyPassphrase"))
|
||||
.getDecryptor(subKeyId));
|
||||
SecretKeyRingProtector protector = SecretKeyRingProtector.unlockAllKeysWith(
|
||||
Passphrase.fromPassword("subKeyPassphrase"), secretKeys);
|
||||
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(subKey, protector);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.pgpainless.encryption_signing.EncryptionStream;
|
|||
import org.pgpainless.implementation.ImplementationFactory;
|
||||
import org.pgpainless.key.protection.KeyRingProtectionSettings;
|
||||
import org.pgpainless.key.protection.PasswordBasedSecretKeyRingProtector;
|
||||
import org.pgpainless.key.protection.UnlockSecretKey;
|
||||
import org.pgpainless.util.Passphrase;
|
||||
|
||||
public class ChangeSecretKeyRingPassphraseTest {
|
||||
|
@ -184,7 +185,7 @@ public class ChangeSecretKeyRingPassphraseTest {
|
|||
PBESecretKeyDecryptor decryptor = passphrase.isEmpty() ? null : new BcPBESecretKeyDecryptorBuilder(digestCalculatorProvider)
|
||||
.build(passphrase.getChars());
|
||||
|
||||
secretKey.extractPrivateKey(decryptor);
|
||||
UnlockSecretKey.unlockSecretKey(secretKey, decryptor);
|
||||
}
|
||||
|
||||
private void signDummyMessageWithKeysAndPassphrase(PGPSecretKeyRing keyRing, Passphrase passphrase) throws IOException, PGPException {
|
||||
|
|
|
@ -140,7 +140,7 @@ public class SecretKeyRingProtectorTest {
|
|||
|
||||
PGPSecretKeyRing secretKeys = TestKeys.getEmilSecretKeyRing();
|
||||
for (PGPSecretKey secretKey : secretKeys) {
|
||||
secretKey.extractPrivateKey(protector.getDecryptor(secretKey));
|
||||
UnlockSecretKey.unlockSecretKey(secretKey, protector);
|
||||
assertNotNull(protector.getEncryptor(secretKey));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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.key.protection;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||
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.util.Passphrase;
|
||||
|
||||
public class UnlockSecretKeyTest {
|
||||
|
||||
@Test
|
||||
public void testUnlockSecretKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
||||
PGPSecretKeyRing secretKeyRing = PGPainless.generateKeyRing()
|
||||
.simpleEcKeyRing("alice@wonderland.lit", "heureka!");
|
||||
PGPSecretKey secretKey = secretKeyRing.getSecretKey();
|
||||
|
||||
SecretKeyRingProtector correctPassphrase = SecretKeyRingProtector.unlockAllKeysWith(Passphrase.fromPassword("heureka!"), secretKeyRing);
|
||||
SecretKeyRingProtector incorrectPassphrase = SecretKeyRingProtector.unlockAllKeysWith(Passphrase.fromPassword("bazinga!"), secretKeyRing);
|
||||
SecretKeyRingProtector emptyPassphrase = SecretKeyRingProtector.unlockAllKeysWith(Passphrase.emptyPassphrase(), secretKeyRing);
|
||||
Passphrase cleared = Passphrase.fromPassword("cleared");
|
||||
cleared.clear();
|
||||
SecretKeyRingProtector invalidPassphrase = SecretKeyRingProtector.unlockAllKeysWith(cleared, secretKeyRing);
|
||||
// Correct passphrase works
|
||||
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKey, correctPassphrase);
|
||||
assertNotNull(privateKey);
|
||||
|
||||
assertThrows(WrongPassphraseException.class, () ->
|
||||
UnlockSecretKey.unlockSecretKey(secretKey, incorrectPassphrase));
|
||||
assertThrows(WrongPassphraseException.class, () ->
|
||||
UnlockSecretKey.unlockSecretKey(secretKey, emptyPassphrase));
|
||||
assertThrows(WrongPassphraseException.class, () ->
|
||||
UnlockSecretKey.unlockSecretKey(secretKey, invalidPassphrase));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue