Skip to content

Commit a012951

Browse files
committed
Merge branch '1857-5-aead-reencrypt' into 'main'
Add AEADProtectedPGPSecretKeyTest.reencryptKey, PublicKeyUtils, and update PGPSecretKey See merge request root/bc-java!49
2 parents f97b21e + 9f618f6 commit a012951

File tree

3 files changed

+133
-7
lines changed

3 files changed

+133
-7
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package org.bouncycastle.bcpg;
2+
3+
/**
4+
* Utility methods related to OpenPGP public key algorithms.
5+
*/
6+
public class PublicKeyUtils
7+
{
8+
9+
/**
10+
* Return true, if the public key algorithm that corresponds to the given ID is capable of signing.
11+
*
12+
* @param publicKeyAlgorithm public key algorithm id
13+
* @return true if algorithm can sign
14+
*/
15+
public static boolean isSigningAlgorithm(int publicKeyAlgorithm)
16+
{
17+
switch (publicKeyAlgorithm)
18+
{
19+
case PublicKeyAlgorithmTags.RSA_GENERAL:
20+
case PublicKeyAlgorithmTags.RSA_SIGN:
21+
case PublicKeyAlgorithmTags.DSA:
22+
case PublicKeyAlgorithmTags.ECDSA:
23+
case PublicKeyAlgorithmTags.ELGAMAL_GENERAL:
24+
case PublicKeyAlgorithmTags.EDDSA_LEGACY:
25+
case PublicKeyAlgorithmTags.Ed25519:
26+
case PublicKeyAlgorithmTags.Ed448:
27+
return true;
28+
default:
29+
return false;
30+
}
31+
}
32+
33+
// /**
34+
// * Return true, if the public key algorithm that corresponds to the given ID is capable of encryption.
35+
// *
36+
// * @param publicKeyAlgorithm public key algorithm id
37+
// * @return true if algorithm can encrypt
38+
// */
39+
// public static boolean isEncryptionAlgorithm(int publicKeyAlgorithm)
40+
// {
41+
// switch (publicKeyAlgorithm)
42+
// {
43+
// case PublicKeyAlgorithmTags.RSA_GENERAL:
44+
// case PublicKeyAlgorithmTags.RSA_ENCRYPT:
45+
// case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT:
46+
// case PublicKeyAlgorithmTags.ECDH:
47+
// case PublicKeyAlgorithmTags.ELGAMAL_GENERAL:
48+
// case PublicKeyAlgorithmTags.DIFFIE_HELLMAN:
49+
// case PublicKeyAlgorithmTags.X25519:
50+
// case PublicKeyAlgorithmTags.X448:
51+
// return true;
52+
// default:
53+
// return false;
54+
// }
55+
// }
56+
}

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

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.bouncycastle.bcpg.KeyIdentifier;
2323
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
2424
import org.bouncycastle.bcpg.PublicKeyPacket;
25+
import org.bouncycastle.bcpg.PublicKeyUtils;
2526
import org.bouncycastle.bcpg.PublicSubkeyPacket;
2627
import org.bouncycastle.bcpg.RSASecretBCPGKey;
2728
import org.bouncycastle.bcpg.S2K;
@@ -418,11 +419,7 @@ private static PGPPublicKey certifiedPublicKey(
418419
*/
419420
public boolean isSigningKey()
420421
{
421-
int algorithm = pub.getAlgorithm();
422-
423-
return ((algorithm == PGPPublicKey.RSA_GENERAL) || (algorithm == PGPPublicKey.RSA_SIGN)
424-
|| (algorithm == PGPPublicKey.DSA) || (algorithm == PGPPublicKey.ECDSA) || (algorithm == PGPPublicKey.EDDSA_LEGACY)
425-
|| (algorithm == PGPPublicKey.ELGAMAL_GENERAL) || (algorithm == PGPPublicKey.Ed448) || (algorithm == PGPPublicKey.Ed25519));
422+
return PublicKeyUtils.isSigningAlgorithm(pub.getAlgorithm());
426423
}
427424

428425
/**
@@ -460,12 +457,13 @@ public int getKeyEncryptionAlgorithm()
460457
/**
461458
* Return the AEAD algorithm the key is encrypted with.
462459
* Returns <pre>0</pre> if no AEAD is used.
460+
*
463461
* @return aead key encryption algorithm
464462
*/
465463
public int getAEADKeyEncryptionAlgorithm()
466464
{
467465
return secret.getAeadAlgorithm();
468-
}
466+
}
469467

470468
/**
471469
* Return the keyID of the public key associated with this key.
@@ -499,6 +497,13 @@ public byte[] getFingerprint()
499497

500498
/**
501499
* Return the S2K usage associated with this key.
500+
* This value indicates, how the secret key material is protected:
501+
* <ul>
502+
* <li>{@link SecretKeyPacket#USAGE_NONE}: Unprotected</li>
503+
* <li>{@link SecretKeyPacket#USAGE_CHECKSUM}: Password-protected using malleable CFB (deprecated)</li>
504+
* <li>{@link SecretKeyPacket#USAGE_SHA1}: Password-protected using CFB</li>
505+
* <li>{@link SecretKeyPacket#USAGE_AEAD}: Password-protected using AEAD (recommended)</li>
506+
* </ul>
502507
*
503508
* @return the key's S2K usage
504509
*/
@@ -992,7 +997,15 @@ public static PGPSecretKey copyWithNewPassword(
992997

993998
SecretKeyPacket secret;
994999

995-
secret = generateSecretKeyPacket(!(key.secret instanceof SecretSubkeyPacket), key.secret.getPublicKeyPacket(), newEncAlgorithm, s2kUsage, s2k, iv, keyData);
1000+
if (newKeyEncryptor!= null && newKeyEncryptor.getAeadAlgorithm() > 0)
1001+
{
1002+
s2kUsage = SecretKeyPacket.USAGE_AEAD;
1003+
secret = generateSecretKeyPacket(!(key.secret instanceof SecretSubkeyPacket), key.secret.getPublicKeyPacket(), newEncAlgorithm, newKeyEncryptor.getAeadAlgorithm(), s2kUsage, s2k, iv, keyData);
1004+
}
1005+
else
1006+
{
1007+
secret = generateSecretKeyPacket(!(key.secret instanceof SecretSubkeyPacket), key.secret.getPublicKeyPacket(), newEncAlgorithm, s2kUsage, s2k, iv, keyData);
1008+
}
9961009

9971010
return new PGPSecretKey(secret, key.pub);
9981011
}

pg/src/test/java/org/bouncycastle/openpgp/test/AEADProtectedPGPSecretKeyTest.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,12 @@
3131
import org.bouncycastle.openpgp.PGPSecretKey;
3232
import org.bouncycastle.openpgp.PGPSecretKeyRing;
3333
import org.bouncycastle.openpgp.bc.BcPGPObjectFactory;
34+
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
35+
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
3436
import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
3537
import org.bouncycastle.openpgp.operator.bc.BcAEADSecretKeyEncryptorBuilder;
3638
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
39+
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyEncryptorBuilder;
3740
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
3841
import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
3942
import org.bouncycastle.openpgp.operator.jcajce.JcaAEADSecretKeyEncryptorBuilder;
@@ -65,6 +68,8 @@ public void performTest()
6568

6669
testUnlockKeyWithWrongPassphraseBc();
6770
testUnlockKeyWithWrongPassphraseJca();
71+
72+
reencryptKey();
6873
}
6974

7075
private void unlockTestVector()
@@ -360,6 +365,58 @@ private void lockUnlockKeyJca(
360365
keyPair.getPrivateKey().getPrivateKeyDataPacket().getEncoded(), dec.getPrivateKeyDataPacket().getEncoded());
361366
}
362367

368+
private void reencryptKey() throws PGPException {
369+
reencryptKeyBc();
370+
reencryptKeyJca();
371+
}
372+
373+
private void reencryptKeyJca()
374+
{
375+
376+
}
377+
378+
private void reencryptKeyBc()
379+
throws PGPException
380+
{
381+
Ed25519KeyPairGenerator gen = new Ed25519KeyPairGenerator();
382+
gen.init(new Ed25519KeyGenerationParameters(new SecureRandom()));
383+
AsymmetricCipherKeyPair kp = gen.generateKeyPair();
384+
Date creationTime = currentTimeRounded();
385+
String passphrase = "recycle";
386+
PGPKeyPair keyPair = new BcPGPKeyPair(PublicKeyPacket.VERSION_6, PublicKeyAlgorithmTags.Ed25519, kp, creationTime);
387+
388+
PBESecretKeyEncryptor cfbEncBuilder = new BcPBESecretKeyEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_128)
389+
.build(passphrase.toCharArray());
390+
PGPDigestCalculatorProvider digestProv = new BcPGPDigestCalculatorProvider();
391+
392+
// Encrypt key using CFB mode
393+
PGPSecretKey cfbEncKey = new PGPSecretKey(
394+
keyPair.getPrivateKey(),
395+
keyPair.getPublicKey(),
396+
digestProv.get(HashAlgorithmTags.SHA1),
397+
true,
398+
cfbEncBuilder);
399+
400+
PBESecretKeyDecryptor cfbDecryptor = new BcPBESecretKeyDecryptorBuilder(digestProv)
401+
.build(passphrase.toCharArray());
402+
403+
BcAEADSecretKeyEncryptorBuilder aeadEncBuilder = new BcAEADSecretKeyEncryptorBuilder(
404+
AEADAlgorithmTags.OCB, SymmetricKeyAlgorithmTags.AES_128,
405+
S2K.Argon2Params.memoryConstrainedParameters());
406+
407+
// Reencrypt key using AEAD
408+
PGPSecretKey aeadEncKey = PGPSecretKey.copyWithNewPassword(
409+
cfbEncKey,
410+
cfbDecryptor,
411+
aeadEncBuilder.build(
412+
passphrase.toCharArray(),
413+
cfbEncKey.getPublicKey().getPublicKeyPacket()));
414+
415+
PBESecretKeyDecryptor aeadDecryptor = new BcPBESecretKeyDecryptorBuilder(digestProv)
416+
.build(passphrase.toCharArray());
417+
isNotNull(aeadEncKey.extractPrivateKey(aeadDecryptor));
418+
}
419+
363420
public static void main(String[] args)
364421
{
365422
runTest(new AEADProtectedPGPSecretKeyTest());

0 commit comments

Comments
 (0)