Skip to content

Commit cfcd237

Browse files
committed
Fix subkey encryption
1 parent 4304a22 commit cfcd237

File tree

5 files changed

+96
-4
lines changed

5 files changed

+96
-4
lines changed

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

+15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.bouncycastle.openpgp;
22

33
import org.bouncycastle.bcpg.KeyIdentifier;
4+
import org.bouncycastle.bcpg.PublicSubkeyPacket;
5+
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
46

57
/**
68
* General class to handle JCA key pairs and convert them into OpenPGP ones.
@@ -63,4 +65,17 @@ public PGPPrivateKey getPrivateKey()
6365
{
6466
return priv;
6567
}
68+
69+
public PGPKeyPair asSubkey(KeyFingerPrintCalculator fingerPrintCalculator)
70+
throws PGPException
71+
{
72+
PublicSubkeyPacket pubSubPkt = new PublicSubkeyPacket(
73+
pub.getVersion(),
74+
pub.getAlgorithm(),
75+
pub.getCreationTime(),
76+
pub.getPublicKeyPacket().getKey());
77+
return new PGPKeyPair(
78+
new PGPPublicKey(pubSubPkt, fingerPrintCalculator),
79+
new PGPPrivateKey(pub.getKeyID(), pubSubPkt, priv.getPrivateKeyDataPacket()));
80+
}
6681
}

pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPV6KeyGenerator.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
import org.bouncycastle.openpgp.PGPSignature;
2020
import org.bouncycastle.openpgp.PGPSignatureGenerator;
2121
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
22+
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
2223
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
2324
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptorFactory;
2425
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilderProvider;
2526
import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
2627
import org.bouncycastle.openpgp.operator.PGPKeyPairGenerator;
2728
import org.bouncycastle.openpgp.operator.PGPKeyPairGeneratorProvider;
29+
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
2830
import org.bouncycastle.util.Arrays;
2931

3032
import java.io.IOException;
@@ -131,9 +133,10 @@ public OpenPGPV6KeyGenerator(
131133
PGPContentSignerBuilderProvider contentSignerBuilderProvider,
132134
PGPDigestCalculatorProvider digestCalculatorProvider,
133135
PBESecretKeyEncryptorFactory keyEncryptionBuilderProvider,
136+
KeyFingerPrintCalculator keyFingerPrintCalculator,
134137
Date creationTime)
135138
{
136-
this.impl = new Implementation(kpGenProvider, contentSignerBuilderProvider, digestCalculatorProvider, keyEncryptionBuilderProvider);
139+
this.impl = new Implementation(kpGenProvider, contentSignerBuilderProvider, digestCalculatorProvider, keyEncryptionBuilderProvider, keyFingerPrintCalculator);
137140
this.conf = new Configuration(new Date((creationTime.getTime() / 1000) * 1000));
138141
}
139142

@@ -696,7 +699,9 @@ public WithPrimaryKey addEncryptionSubkey(KeyPairGeneratorCallback keyGenCallbac
696699
char[] passphrase)
697700
throws PGPException
698701
{
699-
PGPKeyPair subkey = keyGenCallback.generateFrom(impl.kpGenProvider.get(PublicKeyPacket.VERSION_6, conf.keyCreationTime));
702+
PGPKeyPair subkey = keyGenCallback.generateFrom(
703+
impl.kpGenProvider.get(PublicKeyPacket.VERSION_6, conf.keyCreationTime));
704+
subkey = subkey.asSubkey(impl.keyFingerprintCalculator);
700705
PBESecretKeyEncryptor keyEncryptor = impl.keyEncryptorBuilderProvider.build(passphrase, subkey.getPublicKey().getPublicKeyPacket());
701706
return addEncryptionSubkey(subkey, bindingSignatureCallback, keyEncryptor);
702707
}
@@ -839,6 +844,7 @@ public WithPrimaryKey addSigningSubkey(KeyPairGeneratorCallback keyGenCallback,
839844
throws PGPException
840845
{
841846
PGPKeyPair subkey = keyGenCallback.generateFrom(impl.kpGenProvider.get(PublicKeyPacket.VERSION_6, conf.keyCreationTime));
847+
subkey = subkey.asSubkey(impl.keyFingerprintCalculator);
842848
PBESecretKeyEncryptor keyEncryptor = impl.keyEncryptorBuilderProvider.build(passphrase, subkey.getPublicKey().getPublicKeyPacket());
843849
return addSigningSubkey(subkey, bindingSignatureCallback, backSignatureCallback, keyEncryptor);
844850
}
@@ -1033,16 +1039,19 @@ private static class Implementation
10331039
final PGPContentSignerBuilderProvider contentSignerBuilderProvider;
10341040
final PGPDigestCalculatorProvider digestCalculatorProvider;
10351041
final PBESecretKeyEncryptorFactory keyEncryptorBuilderProvider;
1042+
final KeyFingerPrintCalculator keyFingerprintCalculator;
10361043

10371044
public Implementation(PGPKeyPairGeneratorProvider keyPairGeneratorProvider,
10381045
PGPContentSignerBuilderProvider contentSignerBuilderProvider,
10391046
PGPDigestCalculatorProvider digestCalculatorProvider,
1040-
PBESecretKeyEncryptorFactory keyEncryptorBuilderProvider)
1047+
PBESecretKeyEncryptorFactory keyEncryptorBuilderProvider,
1048+
KeyFingerPrintCalculator keyFingerPrintCalculator)
10411049
{
10421050
this.kpGenProvider = keyPairGeneratorProvider;
10431051
this.contentSignerBuilderProvider = contentSignerBuilderProvider;
10441052
this.digestCalculatorProvider = digestCalculatorProvider;
10451053
this.keyEncryptorBuilderProvider = keyEncryptorBuilderProvider;
1054+
this.keyFingerprintCalculator = keyFingerPrintCalculator;
10461055
}
10471056
}
10481057

pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPV6KeyGenerator.java

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptorFactory;
55
import org.bouncycastle.openpgp.operator.bc.BcAEADSecretKeyEncryptorFactory;
66
import org.bouncycastle.openpgp.operator.bc.BcCFBSecretKeyEncryptorFactory;
7+
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
78
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilderProvider;
89
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
910
import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPairGeneratorProvider;
@@ -60,6 +61,7 @@ public BcOpenPGPV6KeyGenerator(int signatureHashAlgorithm, Date creationTime, bo
6061
new BcPGPContentSignerBuilderProvider(signatureHashAlgorithm),
6162
new BcPGPDigestCalculatorProvider(),
6263
keyEncryptorFactory(aeadProtection),
64+
new BcKeyFingerprintCalculator(),
6365
creationTime);
6466
}
6567

pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPV6KeyGenerator.java

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptorFactory;
66
import org.bouncycastle.openpgp.operator.jcajce.JcaAEADSecretKeyEncryptorFactory;
77
import org.bouncycastle.openpgp.operator.jcajce.JcaCFBSecretKeyEncryptorFactory;
8+
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
89
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilderProvider;
910
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
1011
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPairGeneratorProvider;
@@ -52,6 +53,7 @@ public JcaOpenPGPV6KeyGenerator(int signatureHashAlgorithm, Date creationTime, b
5253
.setProvider(provider)
5354
.build(),
5455
keyEncryptorFactory(provider, aeadProtection),
56+
new JcaKeyFingerprintCalculator(),
5557
creationTime);
5658
}
5759

pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV6KeyGeneratorTest.java

+65-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.bouncycastle.openpgp.api.test;
22

3+
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
34
import org.bouncycastle.bcpg.PublicKeyPacket;
45
import org.bouncycastle.bcpg.PublicKeyUtils;
56
import org.bouncycastle.bcpg.SecretKeyPacket;
@@ -69,14 +70,15 @@ public OpenPGPV6KeyGenerator getKeyGenerator(int signatureHashAlgorithm,
6970
private void performTests(APIProvider apiProvider)
7071
throws PGPException, IOException
7172
{
73+
testGenerateCustomKey(apiProvider);
74+
7275
testGenerateSignOnlyKeyBaseCase(apiProvider);
7376
testGenerateAEADProtectedSignOnlyKey(apiProvider);
7477
testGenerateCFBProtectedSignOnlyKey(apiProvider);
7578

7679
testGenerateClassicKeyBaseCase(apiProvider);
7780
testGenerateProtectedTypicalKey(apiProvider);
7881

79-
testGenerateCustomKey(apiProvider);
8082
}
8183

8284
private void testGenerateSignOnlyKeyBaseCase(APIProvider apiProvider)
@@ -268,6 +270,68 @@ private void testGenerateCustomKey(APIProvider apiProvider)
268270
.addEncryptionSubkey(PGPKeyPairGenerator::generateX448KeyPair,
269271
"encryption-key-passphrase".toCharArray())
270272
.build();
273+
274+
Iterator<PGPSecretKey> keyIt = secretKey.getSecretKeys();
275+
PGPSecretKey primaryKey = keyIt.next();
276+
isEquals("Primary key MUST be RSA_GENERAL",
277+
PublicKeyAlgorithmTags.RSA_GENERAL, primaryKey.getPublicKey().getAlgorithm());
278+
isEquals("Primary key MUST be 4096 bits", 4096, primaryKey.getPublicKey().getBitStrength());
279+
isEquals("Primary key creation time mismatch",
280+
creationTime, primaryKey.getPublicKey().getCreationTime());
281+
PGPSignature directKeySig = primaryKey.getPublicKey().getKeySignatures().next();
282+
PGPSignatureSubpacketVector hashedSubpackets = directKeySig.getHashedSubPackets();
283+
isEquals("Primary key key flags mismatch",
284+
KeyFlags.CERTIFY_OTHER, hashedSubpackets.getKeyFlags());
285+
isEquals("Primary key features mismatch",
286+
Features.FEATURE_SEIPD_V2, hashedSubpackets.getFeatures().getFeatures());
287+
isEquals("Primary key sig notation data mismatch",
288+
"CYBER",
289+
hashedSubpackets.getNotationDataOccurrences("[email protected]")[0].getNotationValue());
290+
291+
Iterator<String> uids = primaryKey.getUserIDs();
292+
String uid = uids.next();
293+
isFalse("Unexpected additional UID", uids.hasNext());
294+
PGPSignature uidSig = primaryKey.getPublicKey().getSignaturesForID(uid).next();
295+
isEquals("UID binding sig type mismatch",
296+
PGPSignature.DEFAULT_CERTIFICATION, uidSig.getSignatureType());
297+
298+
PGPSecretKey signingSubkey = keyIt.next();
299+
isEquals("Subkey MUST be Ed448",
300+
PublicKeyAlgorithmTags.Ed448, signingSubkey.getPublicKey().getAlgorithm());
301+
isEquals("Subkey creation time mismatch",
302+
creationTime, signingSubkey.getPublicKey().getCreationTime());
303+
PGPSignature sigSubBinding = signingSubkey.getPublicKey().getKeySignatures().next();
304+
PGPSignatureSubpacketVector sigSubBindHashPkts = sigSubBinding.getHashedSubPackets();
305+
isEquals("Encryption subkey key flags mismatch",
306+
KeyFlags.SIGN_DATA, sigSubBindHashPkts.getKeyFlags());
307+
isEquals("Subkey notation data mismatch",
308+
"ZAUBER",
309+
sigSubBindHashPkts.getNotationDataOccurrences("[email protected]")[0].getNotationValue());
310+
isFalse("Missing embedded primary key binding signature",
311+
sigSubBindHashPkts.getEmbeddedSignatures().isEmpty());
312+
313+
PGPSecretKey encryptionSubkey = keyIt.next();
314+
isFalse("Unexpected additional subkey", keyIt.hasNext());
315+
isEquals("Subkey MUST be X448",
316+
PublicKeyAlgorithmTags.X448, encryptionSubkey.getPublicKey().getAlgorithm());
317+
isEquals("Subkey creation time mismatch",
318+
creationTime, encryptionSubkey.getPublicKey().getCreationTime());
319+
PGPSignature encryptionBinding = encryptionSubkey.getPublicKey().getKeySignatures().next();
320+
PGPSignatureSubpacketVector encBindHashPkts = encryptionBinding.getHashedSubPackets();
321+
isEquals("Encryption subkey key flags mismatch",
322+
KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, encBindHashPkts.getKeyFlags());
323+
isTrue("Unexpected embedded primary key binding signature in encryption subkey binding",
324+
encBindHashPkts.getEmbeddedSignatures().isEmpty());
325+
326+
BcPBESecretKeyDecryptorBuilder keyDecryptorBuilder = new BcPBESecretKeyDecryptorBuilder(
327+
new BcPGPDigestCalculatorProvider());
328+
329+
isNotNull("Could not decrypt primary key using correct passphrase",
330+
primaryKey.extractPrivateKey(keyDecryptorBuilder.build("primary-key-passphrase".toCharArray())));
331+
isNotNull("Could not decrypt signing subkey using correct passphrase",
332+
signingSubkey.extractPrivateKey(keyDecryptorBuilder.build("signing-key-passphrase".toCharArray())));
333+
isNotNull("Could not decrypt encryption subkey using correct passphrase",
334+
encryptionSubkey.extractPrivateKey(keyDecryptorBuilder.build("encryption-key-passphrase".toCharArray())));
271335
}
272336

273337
private abstract static class APIProvider

0 commit comments

Comments
 (0)