001/* 002 * Copyright 2018 Paul Schaub. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.pgpainless.key; 017 018import javax.annotation.Nonnull; 019import java.math.BigInteger; 020import java.nio.ByteBuffer; 021import java.nio.charset.Charset; 022import java.util.Arrays; 023 024import org.bouncycastle.openpgp.PGPPublicKey; 025import org.bouncycastle.openpgp.PGPPublicKeyRing; 026import org.bouncycastle.openpgp.PGPSecretKey; 027import org.bouncycastle.openpgp.PGPSecretKeyRing; 028import org.bouncycastle.util.encoders.Hex; 029 030/** 031 * This class represents an hex encoded, uppercase OpenPGP v4 fingerprint. 032 */ 033public class OpenPgpV4Fingerprint implements CharSequence, Comparable<OpenPgpV4Fingerprint> { 034 035 private final String fingerprint; 036 037 /** 038 * Create an {@link OpenPgpV4Fingerprint}. 039 * @see <a href="https://xmpp.org/extensions/xep-0373.html#annoucning-pubkey"> 040 * XEP-0373 §4.1: The OpenPGP Public-Key Data Node about how to obtain the fingerprint</a> 041 * @param fingerprint hexadecimal representation of the fingerprint. 042 */ 043 public OpenPgpV4Fingerprint(@Nonnull String fingerprint) { 044 String fp = fingerprint.trim().toUpperCase(); 045 if (!isValid(fp)) { 046 throw new IllegalArgumentException("Fingerprint " + fingerprint + 047 " does not appear to be a valid OpenPGP v4 fingerprint."); 048 } 049 this.fingerprint = fp; 050 } 051 052 public OpenPgpV4Fingerprint(@Nonnull byte[] bytes) { 053 this(new String(bytes, Charset.forName("UTF-8"))); 054 } 055 056 public OpenPgpV4Fingerprint(@Nonnull PGPPublicKey key) { 057 this(Hex.encode(key.getFingerprint())); 058 if (key.getVersion() != 4) { 059 throw new IllegalArgumentException("Key is not a v4 OpenPgp key."); 060 } 061 } 062 063 public OpenPgpV4Fingerprint(@Nonnull PGPSecretKey key) { 064 this(key.getPublicKey()); 065 } 066 067 public OpenPgpV4Fingerprint(@Nonnull PGPPublicKeyRing ring) { 068 this(ring.getPublicKey()); 069 } 070 071 public OpenPgpV4Fingerprint(@Nonnull PGPSecretKeyRing ring) { 072 this(ring.getPublicKey()); 073 } 074 075 /** 076 * Check, whether the fingerprint consists of 40 valid hexadecimal characters. 077 * @param fp fingerprint to check. 078 * @return true if fingerprint is valid. 079 */ 080 private static boolean isValid(@Nonnull String fp) { 081 return fp.matches("[0-9A-F]{40}"); 082 } 083 084 /** 085 * Return the key id of the OpenPGP public key this {@link OpenPgpV4Fingerprint} belongs to. 086 * 087 * @see <a href="https://tools.ietf.org/html/rfc4880#section-12.2"> 088 * RFC-4880 §12.2: Key IDs and Fingerprints</a> 089 * @return key id 090 */ 091 public long getKeyId() { 092 093 byte[] bytes = new BigInteger(toString(), 16).toByteArray(); 094 if (bytes.length != toString().length() / 2) { 095 bytes = Arrays.copyOfRange(bytes, 1, bytes.length); 096 } 097 098 byte[] lower8Bytes = Arrays.copyOfRange(bytes, 12, 20); 099 ByteBuffer byteBuffer = ByteBuffer.allocate(8); 100 byteBuffer.put(lower8Bytes); 101 byteBuffer.flip(); 102 return byteBuffer.getLong(); 103 } 104 105 @Override 106 public boolean equals(Object other) { 107 if (other == null) { 108 return false; 109 } 110 111 if (!(other instanceof CharSequence)) { 112 return false; 113 } 114 115 return this.toString().equals(other.toString()); 116 } 117 118 @Override 119 public int hashCode() { 120 return fingerprint.hashCode(); 121 } 122 123 @Override 124 public int length() { 125 return fingerprint.length(); 126 } 127 128 @Override 129 public char charAt(int i) { 130 return fingerprint.charAt(i); 131 } 132 133 @Override 134 public CharSequence subSequence(int i, int i1) { 135 return fingerprint.subSequence(i, i1); 136 } 137 138 @Override 139 public String toString() { 140 return fingerprint; 141 } 142 143 @Override 144 public int compareTo(@Nonnull OpenPgpV4Fingerprint openPgpV4Fingerprint) { 145 return fingerprint.compareTo(openPgpV4Fingerprint.fingerprint); 146 } 147}