1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-06-24 12:34:50 +02:00
pgpainless/pgpainless-core/src/main/java/org/pgpainless/key/modification/KeyRingEditor.java

190 lines
7.6 KiB
Java
Raw Normal View History

2020-10-25 19:54:03 +01:00
/*
* Copyright 2020 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.modification;
2020-10-23 16:44:21 +02:00
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
2020-10-23 16:44:21 +02:00
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
2020-10-23 16:44:21 +02:00
import org.bouncycastle.openpgp.PGPSignature;
2020-10-25 19:54:03 +01:00
import org.bouncycastle.openpgp.PGPSignatureGenerator;
2020-10-23 16:44:21 +02:00
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
2020-10-25 19:54:03 +01:00
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.pgpainless.algorithm.HashAlgorithm;
2020-10-23 16:44:21 +02:00
import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.key.generation.KeySpec;
2020-10-23 16:44:21 +02:00
import org.pgpainless.key.protection.KeyRingProtectionSettings;
import org.pgpainless.key.protection.SecretKeyRingProtector;
2020-10-25 19:54:03 +01:00
import org.pgpainless.key.util.OpenPgpKeyAttributeUtil;
import org.pgpainless.util.Passphrase;
public class KeyRingEditor implements KeyRingEditorInterface {
private PGPSecretKeyRing secretKeyRing;
public KeyRingEditor(PGPSecretKeyRing secretKeyRing) {
if (secretKeyRing == null) {
throw new NullPointerException("SecretKeyRing MUST NOT be null.");
}
this.secretKeyRing = secretKeyRing;
}
@Override
2020-10-23 16:44:21 +02:00
public KeyRingEditorInterface addUserId(String userId, SecretKeyRingProtector secretKeyRingProtector) throws PGPException {
userId = sanitizeUserId(userId);
2020-10-25 19:54:03 +01:00
Iterator<PGPSecretKey> secretKeys = secretKeyRing.getSecretKeys();
PGPSecretKey primarySecKey = secretKeys.next();
2020-10-23 16:44:21 +02:00
PGPPublicKey primaryPubKey = secretKeyRing.getPublicKey();
2020-10-25 19:54:03 +01:00
PGPPrivateKey privateKey = unlockSecretKey(primarySecKey, secretKeyRingProtector);
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
getPgpContentSignerBuilderForKey(primarySecKey));
2020-10-23 16:44:21 +02:00
signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.getCode(), privateKey);
PGPSignature userIdSignature = signatureGenerator.generateCertification(userId, primaryPubKey);
primaryPubKey = PGPPublicKey.addCertification(primaryPubKey,
userId, userIdSignature);
2020-10-25 19:54:03 +01:00
PGPDigestCalculator digestCalculator = new BcPGPDigestCalculatorProvider().get(
// TODO: Is SHA1 still a good choice?
// If not, what to use/how to make a proper choice?
HashAlgorithm.SHA1.getAlgorithmId());
2020-10-23 16:44:21 +02:00
// "reassemble" secret key ring with modified primary key
2020-10-25 19:54:03 +01:00
primarySecKey = new PGPSecretKey(privateKey, primaryPubKey, digestCalculator, true,
secretKeyRingProtector.getEncryptor(primaryPubKey.getKeyID()));
2020-10-23 16:44:21 +02:00
List<PGPSecretKey> secretKeyList = new ArrayList<>();
secretKeyList.add(primarySecKey);
while (secretKeys.hasNext()) {
secretKeyList.add(secretKeys.next());
}
secretKeyRing = new PGPSecretKeyRing(secretKeyList);
return this;
}
2020-10-25 19:54:03 +01:00
private static BcPGPContentSignerBuilder getPgpContentSignerBuilderForKey(PGPSecretKey secretKey) {
List<HashAlgorithm> preferredHashAlgorithms = OpenPgpKeyAttributeUtil.getPreferredHashAlgorithms(secretKey.getPublicKey());
HashAlgorithm hashAlgorithm = negotiateHashAlgorithm(preferredHashAlgorithms);
return new BcPGPContentSignerBuilder(secretKey.getPublicKey().getAlgorithm(), hashAlgorithm.getAlgorithmId());
}
private static HashAlgorithm negotiateHashAlgorithm(List<HashAlgorithm> preferredHashAlgorithms) {
// TODO: Match our list of supported hash algorithms against the list, to determine the best suitable algo.
// For now we just take the first algorithm in the list and hope that BC has support for it.
return preferredHashAlgorithms.get(0);
}
private PGPPrivateKey unlockSecretKey(PGPSecretKey secretKey, SecretKeyRingProtector protector) throws PGPException {
PBESecretKeyDecryptor secretKeyDecryptor = protector.getDecryptor(secretKey.getKeyID());
2020-10-23 16:44:21 +02:00
PGPPrivateKey privateKey = secretKey.extractPrivateKey(secretKeyDecryptor);
return privateKey;
}
private String sanitizeUserId(String userId) {
userId = userId.trim();
// TODO: Further research how to sanitize user IDs.
// eg. what about newlines?
return userId;
}
@Override
2020-10-25 19:54:03 +01:00
public KeyRingEditorInterface deleteUserId(String userId, SecretKeyRingProtector protector) {
return this;
}
@Override
2020-10-25 19:54:03 +01:00
public KeyRingEditorInterface addSubKey(KeySpec keySpec, SecretKeyRingProtector protector) {
return this;
}
@Override
2020-10-25 19:54:03 +01:00
public KeyRingEditorInterface deleteSubKey(OpenPgpV4Fingerprint fingerprint, SecretKeyRingProtector protector) {
return this;
}
@Override
2020-10-25 19:54:03 +01:00
public KeyRingEditorInterface deleteSubKey(long subKeyId, SecretKeyRingProtector protector) {
return this;
}
@Override
2020-10-25 19:54:03 +01:00
public KeyRingEditorInterface revokeSubKey(OpenPgpV4Fingerprint fingerprint, SecretKeyRingProtector protector) {
return this;
}
@Override
2020-10-25 19:54:03 +01:00
public KeyRingEditorInterface revokeSubKey(long subKeyId, SecretKeyRingProtector protector) {
return this;
}
2020-10-23 16:44:21 +02:00
@Override
public WithKeyRingEncryptionSettings changePassphraseFromOldPassphrase(@Nullable Passphrase oldPassphrase,
@Nonnull KeyRingProtectionSettings oldProtectionSettings) {
return new WithKeyRingEncryptionSettingsImpl();
}
@Override
public WithKeyRingEncryptionSettings changeSubKeyPassphraseFromOldPassphrase(@Nonnull Long keyId,
@Nullable Passphrase oldPassphrase,
@Nonnull KeyRingProtectionSettings oldProtectionSettings) {
return new WithKeyRingEncryptionSettingsImpl();
}
@Override
public PGPSecretKeyRing done() {
return secretKeyRing;
}
2020-10-23 16:44:21 +02:00
private class WithKeyRingEncryptionSettingsImpl implements WithKeyRingEncryptionSettings {
@Override
public WithPassphrase withSecureDefaultSettings() {
return withCustomSettings(KeyRingProtectionSettings.secureDefaultSettings());
}
@Override
public WithPassphrase withCustomSettings(KeyRingProtectionSettings settings) {
return new WithPassphraseImpl();
}
}
private class WithPassphraseImpl implements WithPassphrase {
@Override
public KeyRingEditorInterface toNewPassphrase(Passphrase passphrase) {
return KeyRingEditor.this;
}
@Override
public KeyRingEditorInterface noPassphrase() {
return KeyRingEditor.this;
}
}
}