From 344421a0f48834040e3971ba1ade83b2d3c6efb1 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sun, 6 Aug 2023 15:56:44 +0200 Subject: [PATCH] Kolin conversion: Some OpenPgpFingerprint classes --- .../pgpainless/key/OpenPgpFingerprint.java | 189 ------------------ .../org/pgpainless/key/OpenPgpFingerprint.kt | 158 +++++++++++++++ .../pgpainless/key/OpenPgpV4Fingerprint.java | 149 -------------- .../pgpainless/key/OpenPgpV4Fingerprint.kt | 65 ++++++ .../pgpainless/key/_64DigitFingerprint.java | 119 ----------- .../org/pgpainless/key/_64DigitFingerprint.kt | 74 +++++++ 6 files changed, 297 insertions(+), 457 deletions(-) delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpFingerprint.java create mode 100644 pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpFingerprint.kt delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpV4Fingerprint.java create mode 100644 pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpV4Fingerprint.kt delete mode 100644 pgpainless-core/src/main/java/org/pgpainless/key/_64DigitFingerprint.java create mode 100644 pgpainless-core/src/main/java/org/pgpainless/key/_64DigitFingerprint.kt diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpFingerprint.java b/pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpFingerprint.java deleted file mode 100644 index 13804061..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpFingerprint.java +++ /dev/null @@ -1,189 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// 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 { - @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 - * RFC-4880 §12.2: Key IDs and Fingerprints - * @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(); -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpFingerprint.kt b/pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpFingerprint.kt new file mode 100644 index 00000000..9c7409de --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpFingerprint.kt @@ -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 { + 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 + * RFC-4880 §12.2: Key IDs and Fingerprints + * @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)) + } +} \ No newline at end of file diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpV4Fingerprint.java b/pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpV4Fingerprint.java deleted file mode 100644 index 13a79201..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpV4Fingerprint.java +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-FileCopyrightText: 2018 Paul Schaub -// -// 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 openpgp4fpr URI scheme - */ - 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 openpgp4fpr URI scheme - */ - 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()); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpV4Fingerprint.kt b/pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpV4Fingerprint.kt new file mode 100644 index 00000000..72c31f4e --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/key/OpenPgpV4Fingerprint.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/_64DigitFingerprint.java b/pgpainless-core/src/main/java/org/pgpainless/key/_64DigitFingerprint.java deleted file mode 100644 index 11f18058..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/key/_64DigitFingerprint.java +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Paul Schaub -// -// 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()); - } -} diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/_64DigitFingerprint.kt b/pgpainless-core/src/main/java/org/pgpainless/key/_64DigitFingerprint.kt new file mode 100644 index 00000000..0f7dd705 --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/key/_64DigitFingerprint.kt @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// 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); + } + } +}