diff --git a/pg/src/main/java/org/bouncycastle/bcpg/FingerprintUtil.java b/pg/src/main/java/org/bouncycastle/bcpg/FingerprintUtil.java index 6e258c623f..5dce45654c 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/FingerprintUtil.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/FingerprintUtil.java @@ -1,10 +1,35 @@ package org.bouncycastle.bcpg; import org.bouncycastle.util.Pack; +import org.bouncycastle.util.encoders.Hex; public class FingerprintUtil { + /** + * Derive a key-id from the given key fingerprint. + * This method can derive key-ids from v4, v5 (LibrePGP) and v6 keys. + * For keys with other versions (2,3) it will return 0. + * + * @param keyVersion version of the key + * @param fingerprint fingerprint of the key + * @return derived key-id + */ + public static long keyIdFromFingerprint(int keyVersion, byte[] fingerprint) + { + switch (keyVersion) + { + case PublicKeyPacket.VERSION_4: + return keyIdFromV4Fingerprint(fingerprint); + case PublicKeyPacket.LIBREPGP_5: + return keyIdFromLibrePgpFingerprint(fingerprint); + case PublicKeyPacket.VERSION_6: + return keyIdFromV6Fingerprint(fingerprint); + default: + return 0; + } + } + /** * Derive a 64 bit key-id from a version 6 OpenPGP fingerprint. * For v6 keys, the key-id corresponds to the left-most 8 octets of the fingerprint. @@ -112,4 +137,55 @@ public static void writeKeyID(long keyID, byte[] bytes) { writeKeyID(keyID, bytes, 0); } + + public static String prettifyFingerprint(byte[] fingerprint) + { + // -DM Hex.toHexString + String hex = Hex.toHexString(fingerprint).toUpperCase(); + StringBuilder sb = new StringBuilder(); + switch (hex.length()) + { + case 32: + // v3 keys + for (int i = 0; i < 4; i++) + { + sb.append(hex, i * 4, (i + 1) * 4).append(' '); + } + sb.append(' '); + for (int i = 4; i < 7; i++) + { + sb.append(hex, i * 4, (i + 1) * 4).append(' '); + } + sb.append(hex, 28, 32); + return sb.toString(); + case 40: + // v4 keys + for (int i = 0; i <= 4; i++) + { + sb.append(hex, i * 4, (i + 1) * 4).append(' '); + } + sb.append(' '); + for (int i = 5; i <= 8; i++) + { + sb.append(hex, i * 4, (i + 1) * 4).append(' '); + } + sb.append(hex, 36, 40); + return sb.toString(); + case 64: + // v5, v6 keys + for (int i = 0; i < 4; i++) + { + sb.append(hex, i * 8, (i + 1) * 8).append(' '); + } + sb.append(' '); + for (int i = 4; i < 7; i++) + { + sb.append(hex, i * 8, (i + 1) * 8).append(' '); + } + sb.append(hex, 56, 64); + return sb.toString(); + default: + return hex; + } + } } diff --git a/pg/src/test/java/org/bouncycastle/bcpg/test/FingerprintUtilTest.java b/pg/src/test/java/org/bouncycastle/bcpg/test/FingerprintUtilTest.java index 3648c4b7fe..970a33f4ce 100644 --- a/pg/src/test/java/org/bouncycastle/bcpg/test/FingerprintUtilTest.java +++ b/pg/src/test/java/org/bouncycastle/bcpg/test/FingerprintUtilTest.java @@ -47,6 +47,19 @@ private void testLibrePgpKeyIdFromFingerprint() -3812177997909612905L, FingerprintUtil.keyIdFromLibrePgpFingerprint(decoded)); } + private void testKeyIdFromFingerprint() + { + isEquals("v4 key-id from fingerprint mismatch", + -5425419407118114754L, FingerprintUtil.keyIdFromFingerprint( + 4, Hex.decode("1D018C772DF8C5EF86A1DCC9B4B509CB5936E03E"))); + isEquals("v5 key-id from fingerprint mismatch", + -3812177997909612905L, FingerprintUtil.keyIdFromFingerprint( + 5, Hex.decode("cb186c4f0609a697e4d52dfa6c722b0c1f1e27c18a56708f6525ec27bad9acc9"))); + isEquals("v6 key-id from fingerprint mismatch", + -3812177997909612905L, FingerprintUtil.keyIdFromFingerprint( + 6, Hex.decode("cb186c4f0609a697e4d52dfa6c722b0c1f1e27c18a56708f6525ec27bad9acc9"))); + } + private void testLeftMostEqualsRightMostFor8Bytes() { byte[] bytes = new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; @@ -79,6 +92,23 @@ private void testWriteKeyIdToBytes() } } + private void testPrettifyFingerprint() + { + isEquals("Prettified v4 fingerprint mismatch", + "1D01 8C77 2DF8 C5EF 86A1 DCC9 B4B5 09CB 5936 E03E", + FingerprintUtil.prettifyFingerprint(Hex.decode("1D018C772DF8C5EF86A1DCC9B4B509CB5936E03E"))); + isEquals("Prettified v5/v6 fingerprint mismatch", + "CB186C4F 0609A697 E4D52DFA 6C722B0C 1F1E27C1 8A56708F 6525EC27 BAD9ACC9", + FingerprintUtil.prettifyFingerprint(Hex.decode("cb186c4f0609a697e4d52dfa6c722b0c1f1e27c18a56708f6525ec27bad9acc9"))); + } + + private void testPrettifyFingerprintReturnsHexForUnknownFormat() + { + String fp = "C0FFEE1DECAFF0"; + isEquals("Prettifying fingerprint with unknown format MUST return uppercase hex fingerprint", + fp, FingerprintUtil.prettifyFingerprint(Hex.decode(fp))); + } + @Override public String getName() { @@ -95,6 +125,9 @@ public void performTest() testLibrePgpKeyIdFromFingerprint(); testLeftMostEqualsRightMostFor8Bytes(); testWriteKeyIdToBytes(); + testKeyIdFromFingerprint(); + testPrettifyFingerprint(); + testPrettifyFingerprintReturnsHexForUnknownFormat(); } public static void main(String[] args)