Skip to content

Commit 8295977

Browse files
author
gefeili
committed
#1752 Introduce KeyIdentifier class
1 parent 394e709 commit 8295977

13 files changed

+896
-0
lines changed

pg/src/main/java/org/bouncycastle/bcpg/AEADEncDataPacket.java

+4
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ public byte getVersion()
6767
return version;
6868
}
6969

70+
/**
71+
* Return the algorithm-id of the symmetric encryption algorithm used to encrypt the data.
72+
* @return symmetric encryption algorithm
73+
*/
7074
public byte getAlgorithm()
7175
{
7276
return algorithm;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
package org.bouncycastle.openpgp;
2+
3+
import org.bouncycastle.bcpg.FingerprintUtil;
4+
import org.bouncycastle.bcpg.SignatureSubpacket;
5+
import org.bouncycastle.bcpg.SignatureSubpacketTags;
6+
import org.bouncycastle.bcpg.sig.IssuerFingerprint;
7+
import org.bouncycastle.bcpg.sig.IssuerKeyID;
8+
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
9+
import org.bouncycastle.util.Arrays;
10+
import org.bouncycastle.util.encoders.Hex;
11+
12+
import java.util.List;
13+
14+
/**
15+
* Utility class for matching key-ids / fingerprints.
16+
* A {@link KeyIdentifier} can be created from either a 64-bit key-id, a fingerprint, or both.
17+
* This class was created to enable a seamless transition from use of key-ids in the API
18+
* towards identifying keys via fingerprints.
19+
*/
20+
public class KeyIdentifier
21+
{
22+
private final byte[] fingerprint;
23+
private final long keyId;
24+
25+
/**
26+
* Create a new {@link KeyIdentifier} based on a keys fingerprint.
27+
* For fingerprints matching the format of a v4, v5 or v6 key, the constructor will
28+
* try to derive the corresponding key-id from the fingerprint.
29+
*
30+
* @param fingerprint fingerprint
31+
*/
32+
public KeyIdentifier(byte[] fingerprint)
33+
{
34+
this.fingerprint = Arrays.clone(fingerprint);
35+
36+
// v4
37+
if (fingerprint.length == 20)
38+
{
39+
keyId = FingerprintUtil.keyIdFromV4Fingerprint(fingerprint);
40+
}
41+
// v5, v6
42+
else if (fingerprint.length == 32)
43+
{
44+
keyId = FingerprintUtil.keyIdFromV6Fingerprint(fingerprint);
45+
}
46+
else
47+
{
48+
keyId = 0L;
49+
}
50+
}
51+
52+
/**
53+
* Create a {@link KeyIdentifier} based on the given fingerprint and key-id.
54+
*
55+
* @param fingerprint fingerprint
56+
* @param keyId key-id
57+
*/
58+
public KeyIdentifier(byte[] fingerprint, long keyId)
59+
{
60+
this.fingerprint = Arrays.clone(fingerprint);
61+
this.keyId = keyId;
62+
}
63+
64+
/**
65+
* Create a {@link KeyIdentifier} based on the given key-id.
66+
* {@code fingerprint} will be set to {@code null}.
67+
*
68+
* @param keyId key-id
69+
*/
70+
public KeyIdentifier(long keyId)
71+
{
72+
this(null, keyId);
73+
}
74+
75+
/**
76+
* Create a {@link KeyIdentifier} for the given {@link PGPPublicKey}.
77+
*
78+
* @param key key
79+
*/
80+
public KeyIdentifier(PGPPublicKey key)
81+
{
82+
this(key.getFingerprint(), key.getKeyID());
83+
}
84+
85+
/**
86+
* Create a {@link KeyIdentifier} for the given {@link PGPSecretKey}.
87+
*
88+
* @param key key
89+
*/
90+
public KeyIdentifier(PGPSecretKey key)
91+
{
92+
this(key.getPublicKey());
93+
}
94+
95+
/**
96+
* Create a {@link KeyIdentifier} for the given {@link PGPPrivateKey}.
97+
*
98+
* @param key key
99+
* @param fingerprintCalculator calculate the fingerprint
100+
* @throws PGPException if an exception happens while calculating the fingerprint
101+
*/
102+
public KeyIdentifier(PGPPrivateKey key, KeyFingerPrintCalculator fingerprintCalculator)
103+
throws PGPException
104+
{
105+
this(new PGPPublicKey(key.getPublicKeyPacket(), fingerprintCalculator));
106+
}
107+
108+
/**
109+
* Create a wildcard {@link KeyIdentifier}.
110+
*/
111+
private KeyIdentifier()
112+
{
113+
this(new byte[0], 0L);
114+
}
115+
116+
/**
117+
* Create a wildcard {@link KeyIdentifier}.
118+
*
119+
* @return wildcard key identifier
120+
*/
121+
public static KeyIdentifier wildcard()
122+
{
123+
return new KeyIdentifier();
124+
}
125+
126+
/**
127+
* Return the fingerprint of the {@link KeyIdentifier}.
128+
* {@code fingerprint} might be null, if the {@link KeyIdentifier} was created from just a key-id.
129+
* If {@link #isWildcard()} returns true, this method returns an empty, but non-null array.
130+
*
131+
* @return fingerprint
132+
*/
133+
public byte[] getFingerprint()
134+
{
135+
return fingerprint;
136+
}
137+
138+
/**
139+
* Return the key-id of the {@link KeyIdentifier}.
140+
* This might be {@code 0L} if {@link #isWildcard()} returns true, or if an unknown
141+
* fingerprint was passed in.
142+
*
143+
* @return key-id
144+
*/
145+
public long getKeyId()
146+
{
147+
return keyId;
148+
}
149+
150+
/**
151+
* Return true, if this {@link KeyIdentifier} matches the given {@link PGPPublicKey}.
152+
* This will return true if the fingerprint matches, or if the key-id matches,
153+
* or if {@link #isWildcard()} returns true.
154+
*
155+
* @param key key
156+
* @return if the identifier matches the key
157+
*/
158+
public boolean matches(PGPPublicKey key)
159+
{
160+
if (isWildcard())
161+
{
162+
return true;
163+
}
164+
165+
if (fingerprint != null)
166+
{
167+
return Arrays.constantTimeAreEqual(fingerprint, key.getFingerprint());
168+
}
169+
else
170+
{
171+
return keyId == key.getKeyID();
172+
}
173+
}
174+
175+
/**
176+
* Return true if this {@link KeyIdentifier} matches the given {@link PGPSecretKey}.
177+
* This will return true if the fingerprint matches, or if the key-id matches,
178+
* or if {@link #isWildcard()} returns true.
179+
*
180+
* @param key key
181+
* @return whether the identifier matches the key
182+
*/
183+
public boolean matches(PGPSecretKey key)
184+
{
185+
return matches(key.getPublicKey());
186+
}
187+
188+
/**
189+
* Return true if this {@link KeyIdentifier} matches the given {@link PGPPrivateKey}.
190+
* This will return true if the fingerprint matches, or if the key-id matches,
191+
* or in case that {@link #isWildcard()} is true.
192+
*
193+
* @param key key
194+
* @param fingerprintCalculator to calculate the fingerprint
195+
* @return whether the identifier matches the key
196+
* @throws PGPException if an exception happens while calculating the fingerprint
197+
*/
198+
public boolean matches(PGPPrivateKey key,
199+
KeyFingerPrintCalculator fingerprintCalculator)
200+
throws PGPException
201+
{
202+
return matches(new PGPPublicKey(key.getPublicKeyPacket(), fingerprintCalculator));
203+
}
204+
205+
public boolean matches(PGPSignature sig)
206+
{
207+
if (isWildcard())
208+
{
209+
return true;
210+
}
211+
212+
PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
213+
PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
214+
215+
return matches(hashed) || matches(unhashed);
216+
}
217+
218+
private boolean matches(PGPSignatureSubpacketVector subpackets)
219+
{
220+
if (fingerprint != null)
221+
{
222+
for (SignatureSubpacket subpacket : subpackets.getSubpackets(SignatureSubpacketTags.ISSUER_FINGERPRINT))
223+
{
224+
IssuerFingerprint issuer = (IssuerFingerprint) subpacket;
225+
if (Arrays.constantTimeAreEqual(fingerprint, issuer.getFingerprint()))
226+
{
227+
return true;
228+
}
229+
// wildcard fingerprint
230+
if (issuer.getFingerprint().length == 0)
231+
{
232+
return true;
233+
}
234+
}
235+
}
236+
237+
for (SignatureSubpacket subpacket : subpackets.getSubpackets(SignatureSubpacketTags.ISSUER_KEY_ID))
238+
{
239+
IssuerKeyID issuer = (IssuerKeyID) subpacket;
240+
if (issuer.getKeyID() == keyId)
241+
{
242+
return true;
243+
}
244+
// wildcard key-id
245+
if (issuer.getKeyID() == 0)
246+
{
247+
return true;
248+
}
249+
}
250+
251+
return false;
252+
}
253+
254+
/**
255+
* Returns true, if the {@link KeyIdentifier} specifies a wildcard (matches anything).
256+
* This is for example used with anonymous recipient key-ids / fingerprints, where the recipient
257+
* needs to try all available keys to decrypt the message.
258+
*
259+
* @return is wildcard
260+
*/
261+
public boolean isWildcard()
262+
{
263+
return keyId == 0L && fingerprint.length == 0;
264+
}
265+
266+
/**
267+
* Return true, if any of the {@link KeyIdentifier KeyIdentifiers} in the {@code identifiers} list
268+
* matches the given {@link PGPPublicKey}.
269+
*
270+
* @param identifiers list of identifiers
271+
* @param key key
272+
* @return true if any matches, false if none matches
273+
*/
274+
public static boolean matches(List<KeyIdentifier> identifiers, PGPPublicKey key)
275+
{
276+
for (KeyIdentifier identifier : identifiers)
277+
{
278+
if (identifier.matches(key))
279+
{
280+
return true;
281+
}
282+
}
283+
return false;
284+
}
285+
286+
/**
287+
* Return true, if any of the {@link KeyIdentifier KeyIdentifiers} in the {@code identifiers} list
288+
* matches the given {@link PGPSecretKey}.
289+
*
290+
* @param identifiers list of identifiers
291+
* @param key key
292+
* @return true if any matches, false if none matches
293+
*/
294+
public static boolean matches(List<KeyIdentifier> identifiers, PGPSecretKey key)
295+
{
296+
for (KeyIdentifier identifier : identifiers)
297+
{
298+
if (identifier.matches(key))
299+
{
300+
return true;
301+
}
302+
}
303+
return false;
304+
}
305+
306+
/**
307+
* Return true, if any of the {@link KeyIdentifier KeyIdentifiers} in the {@code identifiers} list
308+
* matches the given {@link PGPPrivateKey}.
309+
*
310+
* @param identifiers list of identifiers
311+
* @param key key
312+
* @param fingerprintCalculator to calculate the fingerprint
313+
* @return true if any matches, false if none matches
314+
*/
315+
public static boolean matches(List<KeyIdentifier> identifiers,
316+
PGPPrivateKey key,
317+
KeyFingerPrintCalculator fingerprintCalculator)
318+
throws PGPException
319+
{
320+
for (KeyIdentifier identifier : identifiers)
321+
{
322+
if (identifier.matches(key, fingerprintCalculator))
323+
{
324+
return true;
325+
}
326+
}
327+
return false;
328+
}
329+
330+
public String toString()
331+
{
332+
if (isWildcard())
333+
{
334+
return "*";
335+
}
336+
337+
if (getFingerprint() == null)
338+
{
339+
return "" + keyId;
340+
}
341+
342+
// -DM Hex.toHexString
343+
return Hex.toHexString(fingerprint).toUpperCase();
344+
}
345+
}

pg/src/main/java/org/bouncycastle/openpgp/PGPKeyPair.java

+10
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ public long getKeyID()
4141
{
4242
return pub.getKeyID();
4343
}
44+
45+
/**
46+
* Return the {@link KeyIdentifier} associated with the public key.
47+
*
48+
* @return key identifier
49+
*/
50+
public KeyIdentifier getKeyIdentifier()
51+
{
52+
return new KeyIdentifier(getPublicKey());
53+
}
4454

4555
public PGPPublicKey getPublicKey()
4656
{

pg/src/main/java/org/bouncycastle/openpgp/PGPKeyRing.java

+6
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,19 @@ static void readUserIDs(
125125
*/
126126
public abstract PGPPublicKey getPublicKey(byte[] fingerprint);
127127

128+
public abstract PGPPublicKey getPublicKey(KeyIdentifier identifier);
129+
130+
public abstract Iterator<PGPPublicKey> getPublicKeys(KeyIdentifier identifier);
131+
128132
/**
129133
* Return an iterator containing all the public keys carrying signatures issued from key keyID.
130134
*
131135
* @return an iterator (possibly empty) of the public keys associated with keyID.
132136
*/
133137
public abstract Iterator<PGPPublicKey> getKeysWithSignaturesBy(long keyID);
134138

139+
public abstract Iterator<PGPPublicKey> getKeysWithSignaturesBy(KeyIdentifier identifier);
140+
135141
/**
136142
* Return the number of keys in the key ring.
137143
*

0 commit comments

Comments
 (0)