mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-01-09 19:57:57 +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.net.URISyntaxException;
|
||||||
import java.nio.Buffer;
|
import java.nio.Buffer;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPKeyRing;
|
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.
|
* 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";
|
public static final String SCHEME = "openpgp4fpr";
|
||||||
|
|
||||||
private static final Charset utf8 = Charset.forName("UTF-8");
|
|
||||||
private final String fingerprint;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an {@link OpenPgpV4Fingerprint}.
|
* Create an {@link OpenPgpV4Fingerprint}.
|
||||||
* @see <a href="https://xmpp.org/extensions/xep-0373.html#annoucning-pubkey">
|
* @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.
|
* @param fingerprint hexadecimal representation of the fingerprint.
|
||||||
*/
|
*/
|
||||||
public OpenPgpV4Fingerprint(@Nonnull String fingerprint) {
|
public OpenPgpV4Fingerprint(@Nonnull String fingerprint) {
|
||||||
String fp = fingerprint.replace(" ", "").trim().toUpperCase();
|
super(fingerprint);
|
||||||
if (!isValid(fp)) {
|
|
||||||
throw new IllegalArgumentException("Fingerprint " + fingerprint +
|
|
||||||
" does not appear to be a valid OpenPGP v4 fingerprint.");
|
|
||||||
}
|
|
||||||
this.fingerprint = fp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenPgpV4Fingerprint(@Nonnull byte[] bytes) {
|
@Override
|
||||||
this(new String(bytes, utf8));
|
public int getVersion() {
|
||||||
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenPgpV4Fingerprint(@Nonnull PGPPublicKey key) {
|
@Override
|
||||||
this(Hex.encode(key.getFingerprint()));
|
protected boolean isValid(@Nonnull String fp) {
|
||||||
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) {
|
|
||||||
return fp.matches("[0-9A-F]{40}");
|
return fp.matches("[0-9A-F]{40}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
public long getKeyId() {
|
public long getKeyId() {
|
||||||
byte[] bytes = Hex.decode(toString().getBytes(utf8));
|
byte[] bytes = Hex.decode(toString().getBytes(utf8));
|
||||||
ByteBuffer buf = ByteBuffer.wrap(bytes);
|
ByteBuffer buf = ByteBuffer.wrap(bytes);
|
||||||
|
@ -97,6 +56,45 @@ public class OpenPgpV4Fingerprint implements CharSequence, Comparable<OpenPgpV4F
|
||||||
return buf.getLong();
|
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
|
@Override
|
||||||
public boolean equals(Object other) {
|
public boolean equals(Object other) {
|
||||||
if (other == null) {
|
if (other == null) {
|
||||||
|
@ -112,42 +110,7 @@ public class OpenPgpV4Fingerprint implements CharSequence, Comparable<OpenPgpV4F
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return fingerprint.hashCode();
|
return toString().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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -159,7 +122,7 @@ public class OpenPgpV4Fingerprint implements CharSequence, Comparable<OpenPgpV4F
|
||||||
*/
|
*/
|
||||||
public URI toUri() {
|
public URI toUri() {
|
||||||
try {
|
try {
|
||||||
return new URI(SCHEME, toString(), null);
|
return new URI(OpenPgpV4Fingerprint.SCHEME, toString(), null);
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
|
@ -181,6 +144,6 @@ public class OpenPgpV4Fingerprint implements CharSequence, Comparable<OpenPgpV4F
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(@Nonnull OpenPgpV4Fingerprint openPgpV4Fingerprint) {
|
public int compareTo(@Nonnull OpenPgpV4Fingerprint openPgpV4Fingerprint) {
|
||||||
return fingerprint.compareTo(openPgpV4Fingerprint.fingerprint);
|
return toString().compareTo(openPgpV4Fingerprint.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue