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:
parent
623c4c930d
commit
99af9e0171
4 changed files with 109 additions and 19 deletions
|
@ -16,8 +16,10 @@
|
||||||
package org.pgpainless.key.modification;
|
package org.pgpainless.key.modification;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
@ -29,6 +31,7 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.bouncycastle.openpgp.PGPSignatureGenerator;
|
import org.bouncycastle.openpgp.PGPSignatureGenerator;
|
||||||
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
|
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||||
|
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
|
||||||
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
|
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
|
||||||
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
|
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
|
||||||
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
|
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.OpenPgpV4Fingerprint;
|
||||||
import org.pgpainless.key.generation.KeySpec;
|
import org.pgpainless.key.generation.KeySpec;
|
||||||
import org.pgpainless.key.protection.KeyRingProtectionSettings;
|
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.SecretKeyRingProtector;
|
||||||
|
import org.pgpainless.key.protection.UnprotectedKeysProtector;
|
||||||
|
import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider;
|
||||||
import org.pgpainless.key.util.OpenPgpKeyAttributeUtil;
|
import org.pgpainless.key.util.OpenPgpKeyAttributeUtil;
|
||||||
import org.pgpainless.util.Passphrase;
|
import org.pgpainless.util.Passphrase;
|
||||||
|
|
||||||
|
@ -100,12 +107,14 @@ public class KeyRingEditor implements KeyRingEditorInterface {
|
||||||
return preferredHashAlgorithms.get(0);
|
return preferredHashAlgorithms.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move to utility class
|
||||||
private PGPPrivateKey unlockSecretKey(PGPSecretKey secretKey, SecretKeyRingProtector protector) throws PGPException {
|
private PGPPrivateKey unlockSecretKey(PGPSecretKey secretKey, SecretKeyRingProtector protector) throws PGPException {
|
||||||
PBESecretKeyDecryptor secretKeyDecryptor = protector.getDecryptor(secretKey.getKeyID());
|
PBESecretKeyDecryptor secretKeyDecryptor = protector.getDecryptor(secretKey.getKeyID());
|
||||||
PGPPrivateKey privateKey = secretKey.extractPrivateKey(secretKeyDecryptor);
|
PGPPrivateKey privateKey = secretKey.extractPrivateKey(secretKeyDecryptor);
|
||||||
return privateKey;
|
return privateKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move to utility class?
|
||||||
private String sanitizeUserId(String userId) {
|
private String sanitizeUserId(String userId) {
|
||||||
userId = userId.trim();
|
userId = userId.trim();
|
||||||
// TODO: Further research how to sanitize user IDs.
|
// TODO: Further research how to sanitize user IDs.
|
||||||
|
@ -146,14 +155,22 @@ public class KeyRingEditor implements KeyRingEditorInterface {
|
||||||
@Override
|
@Override
|
||||||
public WithKeyRingEncryptionSettings changePassphraseFromOldPassphrase(@Nullable Passphrase oldPassphrase,
|
public WithKeyRingEncryptionSettings changePassphraseFromOldPassphrase(@Nullable Passphrase oldPassphrase,
|
||||||
@Nonnull KeyRingProtectionSettings oldProtectionSettings) {
|
@Nonnull KeyRingProtectionSettings oldProtectionSettings) {
|
||||||
return new WithKeyRingEncryptionSettingsImpl();
|
SecretKeyRingProtector protector = new PasswordBasedSecretKeyRingProtector(
|
||||||
|
oldProtectionSettings,
|
||||||
|
new SolitaryPassphraseProvider(oldPassphrase));
|
||||||
|
|
||||||
|
return new WithKeyRingEncryptionSettingsImpl(null, protector);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WithKeyRingEncryptionSettings changeSubKeyPassphraseFromOldPassphrase(@Nonnull Long keyId,
|
public WithKeyRingEncryptionSettings changeSubKeyPassphraseFromOldPassphrase(@Nonnull Long keyId,
|
||||||
@Nullable Passphrase oldPassphrase,
|
@Nullable Passphrase oldPassphrase,
|
||||||
@Nonnull KeyRingProtectionSettings oldProtectionSettings) {
|
@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
|
@Override
|
||||||
|
@ -161,7 +178,16 @@ public class KeyRingEditor implements KeyRingEditorInterface {
|
||||||
return secretKeyRing;
|
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
|
@Override
|
||||||
public WithPassphrase withSecureDefaultSettings() {
|
public WithPassphrase withSecureDefaultSettings() {
|
||||||
|
@ -170,20 +196,85 @@ public class KeyRingEditor implements KeyRingEditorInterface {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WithPassphrase withCustomSettings(KeyRingProtectionSettings settings) {
|
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
|
@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;
|
return KeyRingEditor.this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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;
|
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
|
* @param passphrase passphrase
|
||||||
* @return editor builder
|
* @return editor builder
|
||||||
*/
|
*/
|
||||||
KeyRingEditorInterface toNewPassphrase(Passphrase passphrase);
|
KeyRingEditorInterface toNewPassphrase(Passphrase passphrase) throws PGPException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Leave the key unprotected.
|
* Leave the key unprotected.
|
||||||
*
|
*
|
||||||
* @return editor builder
|
* @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.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -35,7 +34,7 @@ import org.pgpainless.util.Passphrase;
|
||||||
public class AddUserIdTest {
|
public class AddUserIdTest {
|
||||||
|
|
||||||
@Test
|
@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");
|
PGPKeyRing keyRing = PGPainless.generateKeyRing().simpleEcKeyRing("alice@wonderland.lit", "rabb1th0le");
|
||||||
PGPSecretKeyRing secretKeys = keyRing.getSecretKeys();
|
PGPSecretKeyRing secretKeys = keyRing.getSecretKeys();
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ import org.pgpainless.util.Passphrase;
|
||||||
public class ChangeSecretKeyRingPassphraseTest {
|
public class ChangeSecretKeyRingPassphraseTest {
|
||||||
|
|
||||||
@Test
|
@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");
|
PGPKeyRing keyRing = PGPainless.generateKeyRing().simpleEcKeyRing("password@encryp.ted", "weakPassphrase");
|
||||||
PGPSecretKeyRing secretKeys = PGPainless.modifyKeyRing(keyRing.getSecretKeys())
|
PGPSecretKeyRing secretKeys = PGPainless.modifyKeyRing(keyRing.getSecretKeys())
|
||||||
.changePassphraseFromOldPassphrase(Passphrase.fromPassword("weakPassphrase"))
|
.changePassphraseFromOldPassphrase(Passphrase.fromPassword("weakPassphrase"))
|
||||||
|
@ -62,13 +62,13 @@ public class ChangeSecretKeyRingPassphraseTest {
|
||||||
|
|
||||||
private void signDummyMessageWithKeysAndPassphrase(PGPKeyRing keyRing, Passphrase passphrase) throws IOException, PGPException {
|
private void signDummyMessageWithKeysAndPassphrase(PGPKeyRing keyRing, Passphrase passphrase) throws IOException, PGPException {
|
||||||
String dummyMessage = "dummy";
|
String dummyMessage = "dummy";
|
||||||
ByteArrayOutputStream dummy = new ByteArrayOutputStream();
|
ByteArrayOutputStream dummy = new ByteArrayOutputStream();
|
||||||
EncryptionStream stream = PGPainless.createEncryptor().onOutputStream(dummy)
|
EncryptionStream stream = PGPainless.createEncryptor().onOutputStream(dummy)
|
||||||
.doNotEncrypt()
|
.doNotEncrypt()
|
||||||
.signWith(PasswordBasedSecretKeyRingProtector.forKey(keyRing.getSecretKeys(), passphrase), keyRing.getSecretKeys())
|
.signWith(PasswordBasedSecretKeyRingProtector.forKey(keyRing.getSecretKeys(), passphrase), keyRing.getSecretKeys())
|
||||||
.noArmor();
|
.noArmor();
|
||||||
|
|
||||||
Streams.pipeAll(new ByteArrayInputStream(dummyMessage.getBytes()), stream);
|
Streams.pipeAll(new ByteArrayInputStream(dummyMessage.getBytes()), stream);
|
||||||
stream.close();
|
stream.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue