1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-12-23 11:27:57 +01:00

Re-encrypting whole keyring successful

This commit is contained in:
Paul Schaub 2020-10-25 20:43:09 +01:00
parent 623c4c930d
commit 99af9e0171
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
4 changed files with 109 additions and 19 deletions

View file

@ -16,8 +16,10 @@
package org.pgpainless.key.modification;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -29,6 +31,7 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
@ -37,7 +40,11 @@ import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.key.generation.KeySpec;
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.UnprotectedKeysProtector;
import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider;
import org.pgpainless.key.util.OpenPgpKeyAttributeUtil;
import org.pgpainless.util.Passphrase;
@ -100,12 +107,14 @@ public class KeyRingEditor implements KeyRingEditorInterface {
return preferredHashAlgorithms.get(0);
}
// TODO: Move to utility class
private PGPPrivateKey unlockSecretKey(PGPSecretKey secretKey, SecretKeyRingProtector protector) throws PGPException {
PBESecretKeyDecryptor secretKeyDecryptor = protector.getDecryptor(secretKey.getKeyID());
PGPPrivateKey privateKey = secretKey.extractPrivateKey(secretKeyDecryptor);
return privateKey;
}
// TODO: Move to utility class?
private String sanitizeUserId(String userId) {
userId = userId.trim();
// TODO: Further research how to sanitize user IDs.
@ -146,14 +155,22 @@ public class KeyRingEditor implements KeyRingEditorInterface {
@Override
public WithKeyRingEncryptionSettings changePassphraseFromOldPassphrase(@Nullable Passphrase oldPassphrase,
@Nonnull KeyRingProtectionSettings oldProtectionSettings) {
return new WithKeyRingEncryptionSettingsImpl();
SecretKeyRingProtector protector = new PasswordBasedSecretKeyRingProtector(
oldProtectionSettings,
new SolitaryPassphraseProvider(oldPassphrase));
return new WithKeyRingEncryptionSettingsImpl(null, protector);
}
@Override
public WithKeyRingEncryptionSettings changeSubKeyPassphraseFromOldPassphrase(@Nonnull Long keyId,
@Nullable Passphrase oldPassphrase,
@Nonnull KeyRingProtectionSettings oldProtectionSettings) {
return new WithKeyRingEncryptionSettingsImpl();
Map<Long, Passphrase> passphraseMap = Collections.singletonMap(keyId, oldPassphrase);
SecretKeyRingProtector protector = new PassphraseMapKeyRingProtector(
passphraseMap, oldProtectionSettings, null);
return new WithKeyRingEncryptionSettingsImpl(keyId, protector);
}
@Override
@ -161,7 +178,16 @@ public class KeyRingEditor implements KeyRingEditorInterface {
return secretKeyRing;
}
private class WithKeyRingEncryptionSettingsImpl implements WithKeyRingEncryptionSettings {
private final class WithKeyRingEncryptionSettingsImpl implements WithKeyRingEncryptionSettings {
private final Long keyId;
// Protector to unlock the key with the old passphrase
private final SecretKeyRingProtector oldProtector;
private WithKeyRingEncryptionSettingsImpl(Long keyId, SecretKeyRingProtector oldProtector) {
this.keyId = keyId;
this.oldProtector = oldProtector;
}
@Override
public WithPassphrase withSecureDefaultSettings() {
@ -170,20 +196,85 @@ public class KeyRingEditor implements KeyRingEditorInterface {
@Override
public WithPassphrase withCustomSettings(KeyRingProtectionSettings settings) {
return new WithPassphraseImpl();
return new WithPassphraseImpl(keyId, oldProtector, settings);
}
}
private class WithPassphraseImpl implements WithPassphrase {
private final class WithPassphraseImpl implements WithPassphrase {
private final SecretKeyRingProtector oldProtector;
private final KeyRingProtectionSettings newProtectionSettings;
private final Long keyId;
private WithPassphraseImpl(Long keyId, SecretKeyRingProtector oldProtector, KeyRingProtectionSettings newProtectionSettings) {
this.keyId = keyId;
this.oldProtector = oldProtector;
this.newProtectionSettings = newProtectionSettings;
}
@Override
public KeyRingEditorInterface toNewPassphrase(Passphrase passphrase) {
public KeyRingEditorInterface toNewPassphrase(Passphrase passphrase) throws PGPException {
SecretKeyRingProtector newProtector = new PasswordBasedSecretKeyRingProtector(
newProtectionSettings, new SolitaryPassphraseProvider(passphrase));
PGPSecretKeyRing secretKeys = changePassphrase(keyId, KeyRingEditor.this.secretKeyRing, oldProtector, newProtector);
KeyRingEditor.this.secretKeyRing = secretKeys;
return KeyRingEditor.this;
}
@Override
public KeyRingEditorInterface noPassphrase() {
public KeyRingEditorInterface noPassphrase() throws PGPException {
SecretKeyRingProtector newProtector = new UnprotectedKeysProtector();
PGPSecretKeyRing secretKeys = changePassphrase(keyId, KeyRingEditor.this.secretKeyRing, oldProtector, newProtector);
KeyRingEditor.this.secretKeyRing = secretKeys;
return KeyRingEditor.this;
}
private PGPSecretKeyRing changePassphrase(Long keyId,
PGPSecretKeyRing secretKeys,
SecretKeyRingProtector oldProtector,
SecretKeyRingProtector newProtector) throws PGPException {
if (keyId == null) {
// change passphrase of whole key ring
List<PGPSecretKey> newlyEncryptedSecretKeys = new ArrayList<>();
Iterator<PGPSecretKey> secretKeyIterator = secretKeys.getSecretKeys();
while (secretKeyIterator.hasNext()) {
PGPSecretKey secretKey = secretKeyIterator.next();
PGPPrivateKey privateKey = unlockSecretKey(secretKey, oldProtector);
secretKey = lockPrivateKey(privateKey, secretKey.getPublicKey(), newProtector);
newlyEncryptedSecretKeys.add(secretKey);
}
return new PGPSecretKeyRing(newlyEncryptedSecretKeys);
} else {
// change passphrase of selected subkey only
List<PGPSecretKey> secretKeyList = new ArrayList<>();
Iterator<PGPSecretKey> secretKeyIterator = secretKeys.getSecretKeys();
while (secretKeyIterator.hasNext()) {
PGPSecretKey secretKey = secretKeyIterator.next();
if (secretKey.getPublicKey().getKeyID() == keyId) {
// Re-encrypt only the selected subkey
PGPPrivateKey privateKey = unlockSecretKey(secretKey, oldProtector);
secretKey = lockPrivateKey(privateKey, secretKey.getPublicKey(), newProtector);
}
secretKeyList.add(secretKey);
}
return new PGPSecretKeyRing(secretKeyList);
}
}
// TODO: Move to utility class
private PGPSecretKey lockPrivateKey(PGPPrivateKey privateKey, PGPPublicKey publicKey, SecretKeyRingProtector protector) throws PGPException {
PGPDigestCalculator checksumCalculator = new BcPGPDigestCalculatorProvider()
// TODO: Again, SHA1?
.get(HashAlgorithm.SHA1.getAlgorithmId());
PBESecretKeyEncryptor encryptor = protector.getEncryptor(publicKey.getKeyID());
PGPSecretKey secretKey = new PGPSecretKey(privateKey, publicKey, checksumCalculator, publicKey.isMasterKey(), encryptor);
return secretKey;
}
}
}

View file

@ -153,14 +153,14 @@ public interface KeyRingEditorInterface {
* @param passphrase passphrase
* @return editor builder
*/
KeyRingEditorInterface toNewPassphrase(Passphrase passphrase);
KeyRingEditorInterface toNewPassphrase(Passphrase passphrase) throws PGPException;
/**
* Leave the key unprotected.
*
* @return editor builder
*/
KeyRingEditorInterface noPassphrase();
KeyRingEditorInterface noPassphrase() throws PGPException;
}
/**

View file

@ -18,7 +18,6 @@ package org.pgpainless.key.modification;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
@ -35,7 +34,7 @@ import org.pgpainless.util.Passphrase;
public class AddUserIdTest {
@Test
public void addUserIdToExistingKeyRing() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException {
public void addUserIdToExistingKeyRing() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException {
PGPKeyRing keyRing = PGPainless.generateKeyRing().simpleEcKeyRing("alice@wonderland.lit", "rabb1th0le");
PGPSecretKeyRing secretKeys = keyRing.getSecretKeys();

View file

@ -36,7 +36,7 @@ import org.pgpainless.util.Passphrase;
public class ChangeSecretKeyRingPassphraseTest {
@Test
public void changePassphraseTest() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException {
public void changePassphraseOfWholeKeyRingTest() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException {
PGPKeyRing keyRing = PGPainless.generateKeyRing().simpleEcKeyRing("password@encryp.ted", "weakPassphrase");
PGPSecretKeyRing secretKeys = PGPainless.modifyKeyRing(keyRing.getSecretKeys())
.changePassphraseFromOldPassphrase(Passphrase.fromPassword("weakPassphrase"))
@ -62,13 +62,13 @@ public class ChangeSecretKeyRingPassphraseTest {
private void signDummyMessageWithKeysAndPassphrase(PGPKeyRing keyRing, Passphrase passphrase) throws IOException, PGPException {
String dummyMessage = "dummy";
ByteArrayOutputStream dummy = new ByteArrayOutputStream();
EncryptionStream stream = PGPainless.createEncryptor().onOutputStream(dummy)
.doNotEncrypt()
.signWith(PasswordBasedSecretKeyRingProtector.forKey(keyRing.getSecretKeys(), passphrase), keyRing.getSecretKeys())
.noArmor();
ByteArrayOutputStream dummy = new ByteArrayOutputStream();
EncryptionStream stream = PGPainless.createEncryptor().onOutputStream(dummy)
.doNotEncrypt()
.signWith(PasswordBasedSecretKeyRingProtector.forKey(keyRing.getSecretKeys(), passphrase), keyRing.getSecretKeys())
.noArmor();
Streams.pipeAll(new ByteArrayInputStream(dummyMessage.getBytes()), stream);
stream.close();
Streams.pipeAll(new ByteArrayInputStream(dummyMessage.getBytes()), stream);
stream.close();
}
}