mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-22 19:08:00 +01:00
Kolin conversion: Some OpenPgpFingerprint classes
This commit is contained in:
parent
84e554fc0d
commit
344421a0f4
6 changed files with 297 additions and 457 deletions
|
@ -1,189 +0,0 @@
|
|||
// 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.PGPSecretKey;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
|
||||
/**
|
||||
* Abstract super class of different version OpenPGP fingerprints.
|
||||
*
|
||||
*/
|
||||
public abstract class OpenPgpFingerprint implements CharSequence, Comparable<OpenPgpFingerprint> {
|
||||
@SuppressWarnings("CharsetObjectCanBeUsed")
|
||||
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(PGPSecretKey key) {
|
||||
return of(key.getPublicKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
if (key.getVersion() == 5) {
|
||||
return new OpenPgpV5Fingerprint(key);
|
||||
}
|
||||
if (key.getVersion() == 6) {
|
||||
return new OpenPgpV6Fingerprint(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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to parse an {@link OpenPgpFingerprint} from the given fingerprint string.
|
||||
* If the trimmed fingerprint without whitespace is 64 characters long, it is either a v5 or v6 fingerprint.
|
||||
* In this case, we return a {@link _64DigitFingerprint}. Since this is ambiguous, it is generally recommended
|
||||
* to know the version of the key beforehand.
|
||||
*
|
||||
* @param fingerprint fingerprint
|
||||
* @return parsed fingerprint
|
||||
* @deprecated Use the constructor methods of the versioned fingerprint subclasses instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static OpenPgpFingerprint parse(String fingerprint) {
|
||||
String fp = fingerprint.replace(" ", "").trim().toUpperCase();
|
||||
if (fp.matches("^[0-9A-F]{40}$")) {
|
||||
return new OpenPgpV4Fingerprint(fp);
|
||||
}
|
||||
if (fp.matches("^[0-9A-F]{64}$")) {
|
||||
// Might be v5 or v6 :/
|
||||
return new _64DigitFingerprint(fp);
|
||||
}
|
||||
throw new IllegalArgumentException("Fingerprint does not appear to match any known fingerprint patterns.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a binary OpenPGP fingerprint into an {@link OpenPgpFingerprint} object.
|
||||
*
|
||||
* @param binaryFingerprint binary representation of the fingerprint
|
||||
* @return parsed fingerprint
|
||||
* @deprecated use the parse() methods of the versioned fingerprint subclasses instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static OpenPgpFingerprint parseFromBinary(byte[] binaryFingerprint) {
|
||||
String hex = Hex.toHexString(binaryFingerprint).toUpperCase();
|
||||
return parse(hex);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package org.pgpainless.key
|
||||
|
||||
import org.bouncycastle.openpgp.PGPKeyRing
|
||||
import org.bouncycastle.openpgp.PGPPublicKey
|
||||
import org.bouncycastle.openpgp.PGPSecretKey
|
||||
import org.bouncycastle.util.encoders.Hex
|
||||
import java.nio.charset.Charset
|
||||
|
||||
/**
|
||||
* Abstract super class of different version OpenPGP fingerprints.
|
||||
*
|
||||
*/
|
||||
abstract class OpenPgpFingerprint : CharSequence, Comparable<OpenPgpFingerprint> {
|
||||
val fingerprint: String
|
||||
|
||||
/**
|
||||
* Return the version of the fingerprint.
|
||||
*
|
||||
* @return version
|
||||
*/
|
||||
abstract fun getVersion(): Int
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
abstract val keyId: Long
|
||||
|
||||
constructor(fingerprint: String) {
|
||||
val prep = fingerprint.replace(" ", "").trim().uppercase()
|
||||
if (!isValid(prep)) {
|
||||
throw IllegalArgumentException("Fingerprint '$fingerprint' does not appear to be a valid OpenPGP V${getVersion()} fingerprint.")
|
||||
}
|
||||
this.fingerprint = prep
|
||||
}
|
||||
|
||||
constructor(bytes: ByteArray): this(Hex.toHexString(bytes))
|
||||
|
||||
constructor(key: PGPPublicKey): this(key.fingerprint) {
|
||||
if (key.version != getVersion()) {
|
||||
throw IllegalArgumentException("Key is not a v${getVersion()} OpenPgp key.")
|
||||
}
|
||||
}
|
||||
|
||||
constructor(key: PGPSecretKey): this(key.publicKey)
|
||||
|
||||
constructor(keys: PGPKeyRing): this(keys.publicKey)
|
||||
|
||||
/**
|
||||
* Check, whether the fingerprint consists of 40 valid hexadecimal characters.
|
||||
* @param fp fingerprint to check.
|
||||
* @return true if fingerprint is valid.
|
||||
*/
|
||||
protected abstract fun isValid(fingerprint: String): Boolean
|
||||
|
||||
override val length: Int
|
||||
get() = fingerprint.length
|
||||
|
||||
override fun get(index: Int) = fingerprint.get(index)
|
||||
|
||||
override fun subSequence(startIndex: Int, endIndex: Int) = fingerprint.subSequence(startIndex, endIndex)
|
||||
override fun compareTo(other: OpenPgpFingerprint): Int {
|
||||
return fingerprint.compareTo(other.fingerprint)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return toString() == other.toString()
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return toString().hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String = fingerprint
|
||||
|
||||
abstract fun prettyPrint(): String
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val utf8: Charset = Charset.forName("UTF-8")
|
||||
|
||||
/**
|
||||
* Return the fingerprint of the given key.
|
||||
* This method automatically matches key versions to fingerprint implementations.
|
||||
*
|
||||
* @param key key
|
||||
* @return fingerprint
|
||||
*/
|
||||
@JvmStatic
|
||||
fun of(key: PGPSecretKey): OpenPgpFingerprint = of(key.publicKey)
|
||||
|
||||
/**
|
||||
* Return the fingerprint of the given key.
|
||||
* This method automatically matches key versions to fingerprint implementations.
|
||||
*
|
||||
* @param key key
|
||||
* @return fingerprint
|
||||
*/
|
||||
@JvmStatic
|
||||
fun of (key: PGPPublicKey): OpenPgpFingerprint = when(key.version) {
|
||||
4 -> OpenPgpV4Fingerprint(key)
|
||||
5 -> OpenPgpV5Fingerprint(key)
|
||||
6 -> OpenPgpV6Fingerprint(key)
|
||||
else -> throw IllegalArgumentException("OpenPGP keys of version ${key.version} 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
|
||||
*/
|
||||
@JvmStatic
|
||||
fun of (keys: PGPKeyRing): OpenPgpFingerprint = of(keys.publicKey)
|
||||
|
||||
/**
|
||||
* Try to parse an {@link OpenPgpFingerprint} from the given fingerprint string.
|
||||
* If the trimmed fingerprint without whitespace is 64 characters long, it is either a v5 or v6 fingerprint.
|
||||
* In this case, we return a {@link _64DigitFingerprint}. Since this is ambiguous, it is generally recommended
|
||||
* to know the version of the key beforehand.
|
||||
*
|
||||
* @param fingerprint fingerprint
|
||||
* @return parsed fingerprint
|
||||
* @deprecated Use the constructor methods of the versioned fingerprint subclasses instead.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Deprecated("Use the constructor methods of the versioned fingerprint subclasses instead.")
|
||||
fun parse(fingerprint: String): OpenPgpFingerprint {
|
||||
val prep = fingerprint.replace(" ", "").trim().uppercase()
|
||||
if (prep.matches("^[0-9A-F]{40}$".toRegex())) {
|
||||
return OpenPgpV4Fingerprint(prep)
|
||||
}
|
||||
if (prep.matches("^[0-9A-F]{64}$".toRegex())) {
|
||||
// Might be v5 or v6 :/
|
||||
return _64DigitFingerprint(prep)
|
||||
}
|
||||
throw IllegalArgumentException("Fingerprint does not appear to match any known fingerprint pattern.")
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a binary OpenPGP fingerprint into an {@link OpenPgpFingerprint} object.
|
||||
*
|
||||
* @param binaryFingerprint binary representation of the fingerprint
|
||||
* @return parsed fingerprint
|
||||
* @deprecated use the parse() methods of the versioned fingerprint subclasses instead.
|
||||
*/
|
||||
@JvmStatic
|
||||
@Deprecated("use the parse() methods of the versioned fingerprint subclasses instead.")
|
||||
fun parseFromBinary(binaryFingerprint: ByteArray): OpenPgpFingerprint =
|
||||
parse(Hex.toHexString(binaryFingerprint))
|
||||
}
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2018 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.key;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
|
||||
/**
|
||||
* This class represents a hex encoded, uppercase OpenPGP v4 fingerprint.
|
||||
*/
|
||||
public class OpenPgpV4Fingerprint extends OpenPgpFingerprint {
|
||||
|
||||
public static final String SCHEME = "openpgp4fpr";
|
||||
|
||||
/**
|
||||
* Create an {@link OpenPgpV4Fingerprint}.
|
||||
*
|
||||
* @param fingerprint uppercase hexadecimal fingerprint of length 40
|
||||
*/
|
||||
public OpenPgpV4Fingerprint(@Nonnull String fingerprint) {
|
||||
super(fingerprint);
|
||||
}
|
||||
|
||||
public OpenPgpV4Fingerprint(@Nonnull byte[] bytes) {
|
||||
super(Hex.encode(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 int getVersion() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValid(@Nonnull String fp) {
|
||||
return fp.matches("^[0-9A-F]{40}$");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getKeyId() {
|
||||
byte[] bytes = Hex.decode(toString().getBytes(utf8));
|
||||
ByteBuffer buf = ByteBuffer.wrap(bytes);
|
||||
|
||||
// The key id is the right-most 8 bytes (conveniently a long)
|
||||
// We have to cast here in order to be compatible with java 8
|
||||
// https://github.com/eclipse/jetty.project/issues/3244
|
||||
((Buffer) buf).position(12); // 20 - 8 bytes = offset 12
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(other instanceof CharSequence)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.toString().equals(other.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toString().hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fingerprint as an openpgp4fpr {@link URI}.
|
||||
* An example would be 'openpgp4fpr:7F9116FEA90A5983936C7CFAA027DB2F3E1E118A'.
|
||||
*
|
||||
* @return openpgp4fpr fingerprint uri
|
||||
* @see <a href="https://metacode.biz/openpgp/openpgp4fpr">openpgp4fpr URI scheme</a>
|
||||
*/
|
||||
public URI toUri() {
|
||||
try {
|
||||
return new URI(OpenPgpV4Fingerprint.SCHEME, toString(), null);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an openpgp4fpr URI to an {@link OpenPgpV4Fingerprint}.
|
||||
*
|
||||
* @param uri {@link URI} with scheme 'openpgp4fpr'
|
||||
* @return fingerprint parsed from the uri
|
||||
* @see <a href="https://metacode.biz/openpgp/openpgp4fpr">openpgp4fpr URI scheme</a>
|
||||
*/
|
||||
public static OpenPgpV4Fingerprint fromUri(URI uri) {
|
||||
if (!SCHEME.equals(uri.getScheme())) {
|
||||
throw new IllegalArgumentException("URI scheme MUST equal '" + SCHEME + "'");
|
||||
}
|
||||
return new OpenPgpV4Fingerprint(uri.getSchemeSpecificPart());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@Nonnull OpenPgpFingerprint openPgpFingerprint) {
|
||||
return toString().compareTo(openPgpFingerprint.toString());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package org.pgpainless.key
|
||||
|
||||
import org.bouncycastle.openpgp.PGPKeyRing
|
||||
import org.bouncycastle.openpgp.PGPPublicKey
|
||||
import org.bouncycastle.openpgp.PGPSecretKey
|
||||
import org.bouncycastle.util.encoders.Hex
|
||||
import java.net.URI
|
||||
import java.nio.Buffer
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class OpenPgpV4Fingerprint: OpenPgpFingerprint {
|
||||
|
||||
constructor(fingerprint: String): super(fingerprint)
|
||||
constructor(bytes: ByteArray): super(bytes)
|
||||
constructor(key: PGPPublicKey): super(key)
|
||||
constructor(key: PGPSecretKey): super(key)
|
||||
constructor(keys: PGPKeyRing): super(keys)
|
||||
|
||||
override fun getVersion() = 4
|
||||
|
||||
override val keyId: Long
|
||||
get() {
|
||||
val bytes = Hex.decode(toString().toByteArray(Charset.forName("UTF-8")))
|
||||
val buf = ByteBuffer.wrap(bytes)
|
||||
|
||||
// The key id is the right-most 8 bytes (conveniently a long)
|
||||
// We have to cast here in order to be compatible with java 8
|
||||
// https://github.com/eclipse/jetty.project/issues/3244
|
||||
(buf as Buffer).position(12) // 20 - 8 bytes = offset 12
|
||||
return buf.getLong()
|
||||
}
|
||||
|
||||
override fun isValid(fingerprint: String): Boolean {
|
||||
return fingerprint.matches("^[0-9A-F]{40}$".toRegex())
|
||||
}
|
||||
|
||||
fun toUri(): URI = URI(SCHEME, toString(), null)
|
||||
|
||||
override fun prettyPrint(): String {
|
||||
return buildString {
|
||||
for (i in 0..4) {
|
||||
append(fingerprint, i * 4, (i + 1) * 4).append(' ')
|
||||
}
|
||||
append(' ')
|
||||
for (i in 5 .. 8) {
|
||||
append(fingerprint, i * 4, (i + 1) * 4).append(' ')
|
||||
}
|
||||
append(fingerprint, 36, 40)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val SCHEME = "openpgp4fpr"
|
||||
|
||||
@JvmStatic
|
||||
fun fromUri(uri: URI): OpenPgpV4Fingerprint {
|
||||
if (SCHEME != uri.scheme) {
|
||||
throw IllegalArgumentException("URI scheme MUST equal '$SCHEME'.")
|
||||
}
|
||||
return OpenPgpV4Fingerprint(uri.schemeSpecificPart)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.key;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
|
||||
/**
|
||||
* This class represents a hex encoded, upper case OpenPGP v5 or v6 fingerprint.
|
||||
* Since both fingerprints use the same format, this class is used when parsing the fingerprint without knowing the
|
||||
* key version.
|
||||
*/
|
||||
public class _64DigitFingerprint extends OpenPgpFingerprint {
|
||||
|
||||
/**
|
||||
* Create an {@link _64DigitFingerprint}.
|
||||
*
|
||||
* @param fingerprint uppercase hexadecimal fingerprint of length 64
|
||||
*/
|
||||
protected _64DigitFingerprint(@Nonnull String fingerprint) {
|
||||
super(fingerprint);
|
||||
}
|
||||
|
||||
protected _64DigitFingerprint(@Nonnull byte[] bytes) {
|
||||
super(Hex.encode(bytes));
|
||||
}
|
||||
|
||||
protected _64DigitFingerprint(@Nonnull PGPPublicKey key) {
|
||||
super(key);
|
||||
}
|
||||
|
||||
protected _64DigitFingerprint(@Nonnull PGPSecretKey key) {
|
||||
this(key.getPublicKey());
|
||||
}
|
||||
|
||||
protected _64DigitFingerprint(@Nonnull PGPPublicKeyRing ring) {
|
||||
super(ring);
|
||||
}
|
||||
|
||||
protected _64DigitFingerprint(@Nonnull PGPSecretKeyRing ring) {
|
||||
super(ring);
|
||||
}
|
||||
|
||||
protected _64DigitFingerprint(@Nonnull PGPKeyRing ring) {
|
||||
super(ring);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return -1; // might be v5 or v6
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValid(@Nonnull String fp) {
|
||||
return fp.matches("^[0-9A-F]{64}$");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getKeyId() {
|
||||
byte[] bytes = Hex.decode(toString().getBytes(utf8));
|
||||
ByteBuffer buf = ByteBuffer.wrap(bytes);
|
||||
|
||||
// The key id is the left-most 8 bytes (conveniently a long).
|
||||
// We have to cast here in order to be compatible with java 8
|
||||
// https://github.com/eclipse/jetty.project/issues/3244
|
||||
((Buffer) buf).position(0);
|
||||
|
||||
return buf.getLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prettyPrint() {
|
||||
String fp = toString();
|
||||
StringBuilder pretty = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
pretty.append(fp, i * 8, (i + 1) * 8).append(' ');
|
||||
}
|
||||
pretty.append(' ');
|
||||
for (int i = 4; i < 7; i++) {
|
||||
pretty.append(fp, i * 8, (i + 1) * 8).append(' ');
|
||||
}
|
||||
pretty.append(fp, 56, 64);
|
||||
return pretty.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(other instanceof CharSequence)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.toString().equals(other.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toString().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(OpenPgpFingerprint openPgpFingerprint) {
|
||||
return toString().compareTo(openPgpFingerprint.toString());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.key;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
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.PGPSecretKey;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
|
||||
/**
|
||||
* This class represents a hex encoded, upper case OpenPGP v5 or v6 fingerprint.
|
||||
* Since both fingerprints use the same format, this class is used when parsing the fingerprint without knowing the
|
||||
* key version.
|
||||
*/
|
||||
open class _64DigitFingerprint: OpenPgpFingerprint {
|
||||
|
||||
/**
|
||||
* Create an {@link _64DigitFingerprint}.
|
||||
*
|
||||
* @param fingerprint uppercase hexadecimal fingerprint of length 64
|
||||
*/
|
||||
constructor(fingerprint: String): super(fingerprint)
|
||||
constructor(bytes: ByteArray): super(bytes)
|
||||
constructor(key: PGPPublicKey): super(key)
|
||||
constructor(key: PGPSecretKey): super(key)
|
||||
constructor(keys: PGPKeyRing): super(keys)
|
||||
|
||||
override val keyId: Long
|
||||
get() {
|
||||
val bytes = Hex.decode(fingerprint.toByteArray(Charset.forName("UTF-8")))
|
||||
val buf = ByteBuffer.wrap(bytes);
|
||||
|
||||
// The key id is the left-most 8 bytes (conveniently a long).
|
||||
// We have to cast here in order to be compatible with java 8
|
||||
// https://github.com/eclipse/jetty.project/issues/3244
|
||||
(buf as Buffer).position(0);
|
||||
|
||||
return buf.getLong()
|
||||
}
|
||||
|
||||
override fun getVersion(): Int {
|
||||
return -1 // might be v5 or v6
|
||||
}
|
||||
|
||||
override fun isValid(fingerprint: String): Boolean {
|
||||
return fingerprint.matches(("^[0-9A-F]{64}$".toRegex()))
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return super.toString()
|
||||
}
|
||||
|
||||
override fun prettyPrint(): String {
|
||||
return buildString {
|
||||
for (i in 0 until 4) {
|
||||
append(fingerprint, i * 8, (i + 1) * 8).append(' ');
|
||||
}
|
||||
append(' ');
|
||||
for (i in 4 until 7) {
|
||||
append(fingerprint, i * 8, (i + 1) * 8).append(' ');
|
||||
}
|
||||
append(fingerprint, 56, 64);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue