diff options
3 files changed, 96 insertions, 10 deletions
diff --git a/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java b/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java index 0bab60ce..8ac0bd86 100644 --- a/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java +++ b/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java @@ -822,7 +822,7 @@ public class PGPSecretKey byte[] encKey = newKeyEncryptor.getKey(); keyData = new byte[rawKeyData.length]; - if (newKeyEncryptor.getS2K() != null) + if (newKeyEncryptor.getHashAlgorithm() != HashAlgorithmTags.MD5) { throw new PGPException("MD5 Digest Calculator required for version 3 key encryptor."); } diff --git a/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyEncryptor.java b/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyEncryptor.java index a0d44570..0530638c 100644 --- a/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyEncryptor.java +++ b/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyEncryptor.java @@ -2,7 +2,6 @@ package org.bouncycastle.openpgp.operator; import java.security.SecureRandom; -import org.bouncycastle.bcpg.HashAlgorithmTags; import org.bouncycastle.bcpg.S2K; import org.bouncycastle.openpgp.PGPException; @@ -41,18 +40,19 @@ public abstract class PBESecretKeyEncryptor return encAlgorithm; } - public byte[] getKey() - throws PGPException + public int getHashAlgorithm() { - if (s2k == null && s2kDigestCalculator.getAlgorithm() != HashAlgorithmTags.MD5) + if (s2kDigestCalculator != null) { - byte[] iv = new byte[8]; - - random.nextBytes(iv); - - s2k = new S2K(s2kDigestCalculator.getAlgorithm(), iv, s2kCount); + return s2kDigestCalculator.getAlgorithm(); } + return -1; + } + + public byte[] getKey() + throws PGPException + { return PGPUtil.makeKeyFromPassPhrase(s2kDigestCalculator, encAlgorithm, s2k, passPhrase); } @@ -61,9 +61,27 @@ public abstract class PBESecretKeyEncryptor return s2k; } + /** + * Key encryption method invoked for V4 keys and greater. + * + * @param keyData raw key data + * @param keyOff offset into rawe key data + * @param keyLen length of key data to use. + * @return an encryption of the passed in keyData. + * @throws PGPException on error in the underlying encryption process. + */ public byte[] encryptKeyData(byte[] keyData, int keyOff, int keyLen) throws PGPException { + if (s2k == null) + { + byte[] iv = new byte[8]; + + random.nextBytes(iv); + + s2k = new S2K(s2kDigestCalculator.getAlgorithm(), iv, s2kCount); + } + return encryptKeyData(getKey(), keyData, keyOff, keyLen); } diff --git a/src/test/java/org/bouncycastle/openpgp/test/PGPKeyRingTest.java b/src/test/java/org/bouncycastle/openpgp/test/PGPKeyRingTest.java index 8e044aff..d52f56ee 100644 --- a/src/test/java/org/bouncycastle/openpgp/test/PGPKeyRingTest.java +++ b/src/test/java/org/bouncycastle/openpgp/test/PGPKeyRingTest.java @@ -27,6 +27,7 @@ import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider; import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; @@ -2354,6 +2355,72 @@ public class PGPKeyRingTest } } + private void rewrapTestMD5() + throws Exception + { + // Read the secret key rings + PGPSecretKeyRingCollection privRings = new PGPSecretKeyRingCollection( + new ByteArrayInputStream(rewrapKey)); + char[] newPass = "fred".toCharArray(); + + Iterator rIt = privRings.getKeyRings(); + + if (rIt.hasNext()) + { + PGPSecretKeyRing pgpPriv= (PGPSecretKeyRing)rIt.next(); + + Iterator it = pgpPriv.getSecretKeys(); + + PGPDigestCalculatorProvider calcProvider = new JcaPGPDigestCalculatorProviderBuilder().setProvider("BC").build(); + + while (it.hasNext()) + { + PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + long oldKeyID = pgpKey.getKeyID(); + + // re-encrypt the key with an empty password + pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); + pgpKey = PGPSecretKey.copyWithNewPassword( + pgpKey, + new JcePBESecretKeyDecryptorBuilder(calcProvider).setProvider("BC").build(rewrapPass), + null); + pgpPriv = PGPSecretKeyRing.insertSecretKey(pgpPriv, pgpKey); + + // this should succeed + PGPPrivateKey privTmp = pgpKey.extractPrivateKey(null); + + if (pgpKey.getKeyID() != oldKeyID) + { + fail("key ID mismatch"); + } + } + + it = pgpPriv.getSecretKeys(); + + while (it.hasNext()) + { + PGPSecretKey pgpKey = (PGPSecretKey)it.next(); + long oldKeyID = pgpKey.getKeyID(); + + // re-encrypt the key with an empty password + pgpPriv = PGPSecretKeyRing.removeSecretKey(pgpPriv, pgpKey); + pgpKey = PGPSecretKey.copyWithNewPassword( + pgpKey, + null, + new JcePBESecretKeyEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5, calcProvider.get(HashAlgorithmTags.MD5)).setProvider("BC").build(newPass)); + pgpPriv = PGPSecretKeyRing.insertSecretKey(pgpPriv, pgpKey); + + // this should succeed + PGPPrivateKey privTmp = pgpKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder(calcProvider).setProvider("BC").build(newPass)); + + if (pgpKey.getKeyID() != oldKeyID) + { + fail("key ID mismatch"); + } + } + } + } + private void testPublicKeyRingWithX509() throws Exception { @@ -2508,6 +2575,7 @@ public class PGPKeyRingTest generateSha1Test(); rewrapTest(); rewrapTestV3(); + rewrapTestMD5(); testPublicKeyRingWithX509(); testSecretKeyRingWithPersonalCertificate(); insertMasterTest(); |