mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-23 03:17:58 +01:00
Re-encrypting whole keyring successful
This commit is contained in:
parent
623c4c930d
commit
99af9e0171
4 changed files with 109 additions and 19 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue