mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-01-09 11:48:00 +01:00
Prepare for V5 keys: Extract abstract super class OpenPgpFingerprint from OpenPgpV4Fingerprint
This commit is contained in:
parent
bc2afea7ed
commit
383f51277e
2 changed files with 185 additions and 87 deletions
|
@ -0,0 +1,135 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.key;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
|
||||
/**
|
||||
* Abstract super class of different version OpenPGP fingerprints.
|
||||
*
|
||||
* @param <C> subclass type
|
||||
*/
|
||||
public abstract class OpenPgpFingerprint<C extends OpenPgpV4Fingerprint> implements CharSequence, Comparable<C> {
|
||||
protected static final Charset utf8 = Charset.forName("UTF-8");
|
||||
protected final String fingerprint;
|
||||
|
||||
/**
|
||||
* Return the fingerprint of the given key.
|
||||
* This method automatically matches key versions to fingerprint implementations.
|
||||
*
|
||||
* @param key key
|
||||
* @return fingerprint
|
||||
*/
|
||||
public static OpenPgpFingerprint<?> of(PGPPublicKey key) {
|
||||
if (key.getVersion() == 4) {
|
||||
return new OpenPgpV4Fingerprint(key);
|
||||
}
|
||||
throw new IllegalArgumentException("OpenPGP keys of version " + key.getVersion() + " are not supported.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fingerprint of the primary key of the given key ring.
|
||||
* This method automatically matches key versions to fingerprint implementations.
|
||||
*
|
||||
* @param ring key ring
|
||||
* @return fingerprint
|
||||
*/
|
||||
public static OpenPgpFingerprint<?> of(PGPKeyRing ring) {
|
||||
return of(ring.getPublicKey());
|
||||
}
|
||||
|
||||
public OpenPgpFingerprint(String fingerprint) {
|
||||
String fp = fingerprint.replace(" ", "").trim().toUpperCase();
|
||||
if (!isValid(fp)) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Fingerprint '%s' does not appear to be a valid OpenPGP V%d fingerprint.", fingerprint, getVersion())
|
||||
);
|
||||
}
|
||||
this.fingerprint = fp;
|
||||
}
|
||||
|
||||
public OpenPgpFingerprint(@Nonnull byte[] bytes) {
|
||||
this(new String(bytes, utf8));
|
||||
}
|
||||
|
||||
public OpenPgpFingerprint(PGPPublicKey key) {
|
||||
this(Hex.encode(key.getFingerprint()));
|
||||
if (key.getVersion() != getVersion()) {
|
||||
throw new IllegalArgumentException(String.format("Key is not a v%d OpenPgp key.", getVersion()));
|
||||
}
|
||||
}
|
||||
|
||||
public OpenPgpFingerprint(@Nonnull PGPPublicKeyRing ring) {
|
||||
this(ring.getPublicKey());
|
||||
}
|
||||
|
||||
public OpenPgpFingerprint(@Nonnull PGPSecretKeyRing ring) {
|
||||
this(ring.getPublicKey());
|
||||
}
|
||||
|
||||
public OpenPgpFingerprint(@Nonnull PGPKeyRing ring) {
|
||||
this(ring.getPublicKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the version of the fingerprint.
|
||||
*
|
||||
* @return version
|
||||
*/
|
||||
public abstract int getVersion();
|
||||
|
||||
/**
|
||||
* Check, whether the fingerprint consists of 40 valid hexadecimal characters.
|
||||
* @param fp fingerprint to check.
|
||||
* @return true if fingerprint is valid.
|
||||
*/
|
||||
protected abstract boolean isValid(@Nonnull String fp);
|
||||
|
||||
/**
|
||||
* Return the key id of the OpenPGP public key this {@link OpenPgpFingerprint} belongs to.
|
||||
* This method can be implemented for V4 and V5 fingerprints.
|
||||
* V3 key-IDs cannot be derived from the fingerprint, but we don't care, since V3 is deprecated.
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc4880#section-12.2">
|
||||
* RFC-4880 §12.2: Key IDs and Fingerprints</a>
|
||||
* @return key id
|
||||
*/
|
||||
public abstract long getKeyId();
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return fingerprint.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int i) {
|
||||
return fingerprint.charAt(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int i, int i1) {
|
||||
return fingerprint.subSequence(i, i1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public String toString() {
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pretty printed representation of the fingerprint.
|
||||
*
|
||||
* @return pretty printed fingerprint
|
||||
*/
|
||||
public abstract String prettyPrint();
|
||||
}
|
|
@ -8,7 +8,6 @@ import java.net.URI;
|
|||
import java.net.URISyntaxException;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPKeyRing;
|
||||
|
@ -21,13 +20,10 @@ import org.bouncycastle.util.encoders.Hex;
|
|||
/**
|
||||
* This class represents an hex encoded, uppercase OpenPGP v4 fingerprint.
|
||||
*/
|
||||
public class OpenPgpV4Fingerprint implements CharSequence, Comparable<OpenPgpV4Fingerprint> {
|
||||
public class OpenPgpV4Fingerprint extends OpenPgpFingerprint<OpenPgpV4Fingerprint> {
|
||||
|
||||
public static final String SCHEME = "openpgp4fpr";
|
||||
|
||||
private static final Charset utf8 = Charset.forName("UTF-8");
|
||||
private final String fingerprint;
|
||||
|
||||
/**
|
||||
* Create an {@link OpenPgpV4Fingerprint}.
|
||||
* @see <a href="https://xmpp.org/extensions/xep-0373.html#annoucning-pubkey">
|
||||
|
@ -35,57 +31,20 @@ public class OpenPgpV4Fingerprint implements CharSequence, Comparable<OpenPgpV4F
|
|||
* @param fingerprint hexadecimal representation of the fingerprint.
|
||||
*/
|
||||
public OpenPgpV4Fingerprint(@Nonnull String fingerprint) {
|
||||
String fp = fingerprint.replace(" ", "").trim().toUpperCase();
|
||||
if (!isValid(fp)) {
|
||||
throw new IllegalArgumentException("Fingerprint " + fingerprint +
|
||||
" does not appear to be a valid OpenPGP v4 fingerprint.");
|
||||
}
|
||||
this.fingerprint = fp;
|
||||
super(fingerprint);
|
||||
}
|
||||
|
||||
public OpenPgpV4Fingerprint(@Nonnull byte[] bytes) {
|
||||
this(new String(bytes, utf8));
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
public OpenPgpV4Fingerprint(@Nonnull PGPPublicKey key) {
|
||||
this(Hex.encode(key.getFingerprint()));
|
||||
if (key.getVersion() != 4) {
|
||||
throw new IllegalArgumentException("Key is not a v4 OpenPgp key.");
|
||||
}
|
||||
}
|
||||
|
||||
public OpenPgpV4Fingerprint(@Nonnull PGPSecretKey key) {
|
||||
this(key.getPublicKey());
|
||||
}
|
||||
|
||||
public OpenPgpV4Fingerprint(@Nonnull PGPPublicKeyRing ring) {
|
||||
this(ring.getPublicKey());
|
||||
}
|
||||
|
||||
public OpenPgpV4Fingerprint(@Nonnull PGPSecretKeyRing ring) {
|
||||
this(ring.getPublicKey());
|
||||
}
|
||||
|
||||
public OpenPgpV4Fingerprint(@Nonnull PGPKeyRing ring) {
|
||||
this(ring.getPublicKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check, whether the fingerprint consists of 40 valid hexadecimal characters.
|
||||
* @param fp fingerprint to check.
|
||||
* @return true if fingerprint is valid.
|
||||
*/
|
||||
private static boolean isValid(@Nonnull String fp) {
|
||||
@Override
|
||||
protected boolean isValid(@Nonnull String fp) {
|
||||
return fp.matches("[0-9A-F]{40}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key id of the OpenPGP public key this {@link OpenPgpV4Fingerprint} belongs to.
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc4880#section-12.2">
|
||||
* RFC-4880 §12.2: Key IDs and Fingerprints</a>
|
||||
* @return key id
|
||||
*/
|
||||
@Override
|
||||
public long getKeyId() {
|
||||
byte[] bytes = Hex.decode(toString().getBytes(utf8));
|
||||
ByteBuffer buf = ByteBuffer.wrap(bytes);
|
||||
|
@ -97,6 +56,45 @@ public class OpenPgpV4Fingerprint implements CharSequence, Comparable<OpenPgpV4F
|
|||
return buf.getLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prettyPrint() {
|
||||
String fp = toString();
|
||||
StringBuilder pretty = new StringBuilder();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
pretty.append(fp, i * 4, (i + 1) * 4).append(' ');
|
||||
}
|
||||
pretty.append(' ');
|
||||
for (int i = 5; i < 9; i++) {
|
||||
pretty.append(fp, i * 4, (i + 1) * 4).append(' ');
|
||||
}
|
||||
pretty.append(fp, 36, 40);
|
||||
return pretty.toString();
|
||||
}
|
||||
|
||||
public OpenPgpV4Fingerprint(@Nonnull byte[] bytes) {
|
||||
super(bytes);
|
||||
}
|
||||
|
||||
public OpenPgpV4Fingerprint(@Nonnull PGPPublicKey key) {
|
||||
super(key);
|
||||
}
|
||||
|
||||
public OpenPgpV4Fingerprint(@Nonnull PGPSecretKey key) {
|
||||
this(key.getPublicKey());
|
||||
}
|
||||
|
||||
public OpenPgpV4Fingerprint(@Nonnull PGPPublicKeyRing ring) {
|
||||
super(ring);
|
||||
}
|
||||
|
||||
public OpenPgpV4Fingerprint(@Nonnull PGPSecretKeyRing ring) {
|
||||
super(ring);
|
||||
}
|
||||
|
||||
public OpenPgpV4Fingerprint(@Nonnull PGPKeyRing ring) {
|
||||
super(ring);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == null) {
|
||||
|
@ -112,42 +110,7 @@ public class OpenPgpV4Fingerprint implements CharSequence, Comparable<OpenPgpV4F
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return fingerprint.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return fingerprint.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int i) {
|
||||
return fingerprint.charAt(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int i, int i1) {
|
||||
return fingerprint.subSequence(i, i1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public String toString() {
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
public String prettyPrint() {
|
||||
String fp = toString();
|
||||
StringBuilder pretty = new StringBuilder();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
pretty.append(fp, i * 4, (i + 1) * 4).append(' ');
|
||||
}
|
||||
pretty.append(' ');
|
||||
for (int i = 5; i < 9; i++) {
|
||||
pretty.append(fp, i * 4, (i + 1) * 4).append(' ');
|
||||
}
|
||||
pretty.append(fp, 36, 40);
|
||||
return pretty.toString();
|
||||
return toString().hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -159,7 +122,7 @@ public class OpenPgpV4Fingerprint implements CharSequence, Comparable<OpenPgpV4F
|
|||
*/
|
||||
public URI toUri() {
|
||||
try {
|
||||
return new URI(SCHEME, toString(), null);
|
||||
return new URI(OpenPgpV4Fingerprint.SCHEME, toString(), null);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
@ -181,6 +144,6 @@ public class OpenPgpV4Fingerprint implements CharSequence, Comparable<OpenPgpV4F
|
|||
|
||||
@Override
|
||||
public int compareTo(@Nonnull OpenPgpV4Fingerprint openPgpV4Fingerprint) {
|
||||
return fingerprint.compareTo(openPgpV4Fingerprint.fingerprint);
|
||||
return toString().compareTo(openPgpV4Fingerprint.toString());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue