pgpainless/pgpainless-sop/src/main/java/org/pgpainless/sop/RevokeKeyImpl.java

124 lines
5.0 KiB
Java

// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.sop;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.CharacterCodingException;
import java.util.ArrayList;
import java.util.List;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.PublicKeyPacket;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.pgpainless.PGPainless;
import org.pgpainless.exception.WrongPassphraseException;
import org.pgpainless.key.OpenPgpFingerprint;
import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditorInterface;
import org.pgpainless.key.util.KeyRingUtils;
import org.pgpainless.key.util.RevocationAttributes;
import org.pgpainless.util.ArmoredOutputStreamFactory;
import org.pgpainless.util.Passphrase;
import sop.Ready;
import sop.exception.SOPGPException;
import sop.operation.RevokeKey;
import sop.util.UTF8Util;
import javax.annotation.Nonnull;
public class RevokeKeyImpl implements RevokeKey {
private final MatchMakingSecretKeyRingProtector protector = new MatchMakingSecretKeyRingProtector();
private boolean armor = true;
@Override
@Nonnull
public RevokeKey noArmor() {
this.armor = false;
return this;
}
/**
* Provide the decryption password for the secret key.
*
* @param password password
* @return builder instance
* @throws sop.exception.SOPGPException.UnsupportedOption if the implementation does not support key passwords
* @throws sop.exception.SOPGPException.PasswordNotHumanReadable if the password is not human-readable
*/
@Override
@Nonnull
public RevokeKey withKeyPassword(@Nonnull byte[] password)
throws SOPGPException.UnsupportedOption,
SOPGPException.PasswordNotHumanReadable {
String string;
try {
string = UTF8Util.decodeUTF8(password);
} catch (CharacterCodingException e) {
throw new SOPGPException.PasswordNotHumanReadable("Cannot UTF8-decode password.");
}
protector.addPassphrase(Passphrase.fromPassword(string));
return this;
}
@Override
@Nonnull
public Ready keys(@Nonnull InputStream keys) throws SOPGPException.BadData {
PGPSecretKeyRingCollection secretKeyRings;
try {
secretKeyRings = KeyReader.readSecretKeys(keys, true);
} catch (IOException e) {
throw new SOPGPException.BadData("Cannot decode secret keys.", e);
}
for (PGPSecretKeyRing secretKeys : secretKeyRings) {
protector.addSecretKey(secretKeys);
}
final List<PGPPublicKeyRing> revocationCertificates = new ArrayList<>();
for (PGPSecretKeyRing secretKeys : secretKeyRings) {
SecretKeyRingEditorInterface editor = PGPainless.modifyKeyRing(secretKeys);
try {
RevocationAttributes revocationAttributes = RevocationAttributes.createKeyRevocation()
.withReason(RevocationAttributes.Reason.NO_REASON)
.withoutDescription();
if (secretKeys.getPublicKey().getVersion() == PublicKeyPacket.VERSION_6) {
PGPPublicKeyRing revocation = editor.createMinimalRevocationCertificate(protector, revocationAttributes);
revocationCertificates.add(revocation);
} else {
PGPPublicKeyRing certificate = PGPainless.extractCertificate(secretKeys);
PGPSignature revocation = editor.createRevocation(protector, revocationAttributes);
certificate = KeyRingUtils.injectCertification(certificate, revocation);
revocationCertificates.add(certificate);
}
} catch (WrongPassphraseException e) {
throw new SOPGPException.KeyIsProtected("Missing or wrong passphrase for key " + OpenPgpFingerprint.of(secretKeys), e);
}
catch (PGPException e) {
throw new RuntimeException("Cannot generate revocation certificate.", e);
}
}
return new Ready() {
@Override
public void writeTo(@Nonnull OutputStream outputStream) throws IOException {
PGPPublicKeyRingCollection certificateCollection = new PGPPublicKeyRingCollection(revocationCertificates);
if (armor) {
ArmoredOutputStream out = ArmoredOutputStreamFactory.get(outputStream);
certificateCollection.encode(out);
out.close();
} else {
certificateCollection.encode(outputStream);
}
}
};
}
}