diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2015-10-17 23:21:07 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2015-10-17 23:21:07 +0700 |
commit | a8866af2bf98dd3be651ae853ddf463a313e972a (patch) | |
tree | c013866e5a76a6dab34d0f505cc7bbfe63498dc7 /crypto/src/openpgp | |
parent | Fix various warnings from recent commits (diff) | |
download | BouncyCastle.NET-ed25519-a8866af2bf98dd3be651ae853ddf463a313e972a.tar.xz |
https://github.com/bcgit/bc-csharp/issues/37
- Add alternative PGP methods involving passphrases to support UTF8 or caller-defined encodings
Diffstat (limited to 'crypto/src/openpgp')
-rw-r--r-- | crypto/src/openpgp/PgpEncryptedDataGenerator.cs | 49 | ||||
-rw-r--r-- | crypto/src/openpgp/PgpKeyRingGenerator.cs | 158 | ||||
-rw-r--r-- | crypto/src/openpgp/PgpPbeEncryptedData.cs | 37 | ||||
-rw-r--r-- | crypto/src/openpgp/PgpSecretKey.cs | 346 | ||||
-rw-r--r-- | crypto/src/openpgp/PgpUtilities.cs | 50 |
5 files changed, 567 insertions, 73 deletions
diff --git a/crypto/src/openpgp/PgpEncryptedDataGenerator.cs b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs index 2a2e63961..06868eab1 100644 --- a/crypto/src/openpgp/PgpEncryptedDataGenerator.cs +++ b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs @@ -271,26 +271,55 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp /// <summary> /// Add a PBE encryption method to the encrypted object using the default algorithm (S2K_SHA1). /// </summary> - public void AddMethod( - char[] passPhrase) + /// <remarks> + /// Conversion of the passphrase characters to bytes is performed using Convert.ToByte(), which is + /// the historical behaviour of the library (1.7 and earlier). + /// </remarks> + [Obsolete("Use version that takes an explicit s2kDigest parameter")] + public void AddMethod(char[] passPhrase) { AddMethod(passPhrase, HashAlgorithmTag.Sha1); } - /// <summary>Add a PBE encryption method to the encrypted object.</summary> - public void AddMethod( - char[] passPhrase, - HashAlgorithmTag s2kDigest) + /// <summary>Add a PBE encryption method to the encrypted object.</summary> + /// <remarks> + /// Conversion of the passphrase characters to bytes is performed using Convert.ToByte(), which is + /// the historical behaviour of the library (1.7 and earlier). + /// </remarks> + public void AddMethod(char[] passPhrase, HashAlgorithmTag s2kDigest) + { + DoAddMethod(PgpUtilities.EncodePassPhrase(passPhrase, false), true, s2kDigest); + } + + /// <summary>Add a PBE encryption method to the encrypted object.</summary> + /// <remarks> + /// The passphrase is encoded to bytes using UTF8 (Encoding.UTF8.GetBytes). + /// </remarks> + public void AddMethodUtf8(char[] passPhrase, HashAlgorithmTag s2kDigest) + { + DoAddMethod(PgpUtilities.EncodePassPhrase(passPhrase, true), true, s2kDigest); + } + + /// <summary>Add a PBE encryption method to the encrypted object.</summary> + /// <remarks> + /// Allows the caller to handle the encoding of the passphrase to bytes. + /// </remarks> + public void AddMethodRaw(byte[] rawPassPhrase, HashAlgorithmTag s2kDigest) + { + DoAddMethod(rawPassPhrase, false, s2kDigest); + } + + internal void DoAddMethod(byte[] rawPassPhrase, bool clearPassPhrase, HashAlgorithmTag s2kDigest) { byte[] iv = new byte[8]; - rand.NextBytes(iv); + rand.NextBytes(iv); - S2k s2k = new S2k(s2kDigest, iv, 0x60); + S2k s2k = new S2k(s2kDigest, iv, 0x60); - methods.Add(new PbeMethod(defAlgorithm, s2k, PgpUtilities.MakeKeyFromPassPhrase(defAlgorithm, s2k, passPhrase))); + methods.Add(new PbeMethod(defAlgorithm, s2k, PgpUtilities.DoMakeKeyFromPassPhrase(defAlgorithm, s2k, rawPassPhrase, clearPassPhrase))); } - /// <summary>Add a public key encrypted session key to the encrypted object.</summary> + /// <summary>Add a public key encrypted session key to the encrypted object.</summary> public void AddMethod( PgpPublicKey key) { diff --git a/crypto/src/openpgp/PgpKeyRingGenerator.cs b/crypto/src/openpgp/PgpKeyRingGenerator.cs index efeea9d5b..4f6a4b12f 100644 --- a/crypto/src/openpgp/PgpKeyRingGenerator.cs +++ b/crypto/src/openpgp/PgpKeyRingGenerator.cs @@ -17,7 +17,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp private SymmetricKeyAlgorithmTag encAlgorithm; private HashAlgorithmTag hashAlgorithm; private int certificationLevel; - private char[] passPhrase; + private byte[] rawPassPhrase; private bool useSha1; private PgpKeyPair masterKey; private PgpSignatureSubpacketVector hashedPacketVector; @@ -28,7 +28,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp /// Create a new key ring generator using old style checksumming. It is recommended to use /// SHA1 checksumming where possible. /// </summary> - /// <param name="certificationLevel">The certification level for keys on this ring.</param> + /// <remarks> + /// Conversion of the passphrase characters to bytes is performed using Convert.ToByte(), which is + /// the historical behaviour of the library (1.7 and earlier). + /// </remarks> + /// <param name="certificationLevel">The certification level for keys on this ring.</param> /// <param name="masterKey">The master key pair.</param> /// <param name="id">The id to be associated with the ring.</param> /// <param name="encAlgorithm">The algorithm to be used to protect secret keys.</param> @@ -36,6 +40,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp /// <param name="hashedPackets">Packets to be included in the certification hash.</param> /// <param name="unhashedPackets">Packets to be attached unhashed to the certification.</param> /// <param name="rand">input secured random.</param> + [Obsolete("Use version taking an explicit 'useSha1' parameter instead")] public PgpKeyRingGenerator( int certificationLevel, PgpKeyPair masterKey, @@ -52,7 +57,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp /// <summary> /// Create a new key ring generator. /// </summary> - /// <param name="certificationLevel">The certification level for keys on this ring.</param> + /// <remarks> + /// Conversion of the passphrase characters to bytes is performed using Convert.ToByte(), which is + /// the historical behaviour of the library (1.7 and earlier). + /// </remarks> + /// <param name="certificationLevel">The certification level for keys on this ring.</param> /// <param name="masterKey">The master key pair.</param> /// <param name="id">The id to be associated with the ring.</param> /// <param name="encAlgorithm">The algorithm to be used to protect secret keys.</param> @@ -71,23 +80,86 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp PgpSignatureSubpacketVector hashedPackets, PgpSignatureSubpacketVector unhashedPackets, SecureRandom rand) + : this(certificationLevel, masterKey, id, encAlgorithm, false, passPhrase, useSha1, hashedPackets, unhashedPackets, rand) + { + } + + /// <summary> + /// Create a new key ring generator. + /// </summary> + /// <param name="certificationLevel">The certification level for keys on this ring.</param> + /// <param name="masterKey">The master key pair.</param> + /// <param name="id">The id to be associated with the ring.</param> + /// <param name="encAlgorithm">The algorithm to be used to protect secret keys.</param> + /// <param name="utf8PassPhrase"> + /// If true, conversion of the passphrase to bytes uses Encoding.UTF8.GetBytes(), otherwise the conversion + /// is performed using Convert.ToByte(), which is the historical behaviour of the library (1.7 and earlier). + /// </param> + /// <param name="passPhrase">The passPhrase to be used to protect secret keys.</param> + /// <param name="useSha1">Checksum the secret keys with SHA1 rather than the older 16 bit checksum.</param> + /// <param name="hashedPackets">Packets to be included in the certification hash.</param> + /// <param name="unhashedPackets">Packets to be attached unhashed to the certification.</param> + /// <param name="rand">input secured random.</param> + public PgpKeyRingGenerator( + int certificationLevel, + PgpKeyPair masterKey, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + bool utf8PassPhrase, + char[] passPhrase, + bool useSha1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(certificationLevel, masterKey, id, encAlgorithm, + PgpUtilities.EncodePassPhrase(passPhrase, utf8PassPhrase), + useSha1, hashedPackets, unhashedPackets, rand) + { + } + + /// <summary> + /// Create a new key ring generator. + /// </summary> + /// <param name="certificationLevel">The certification level for keys on this ring.</param> + /// <param name="masterKey">The master key pair.</param> + /// <param name="id">The id to be associated with the ring.</param> + /// <param name="encAlgorithm">The algorithm to be used to protect secret keys.</param> + /// <param name="rawPassPhrase">The passPhrase to be used to protect secret keys.</param> + /// <param name="useSha1">Checksum the secret keys with SHA1 rather than the older 16 bit checksum.</param> + /// <param name="hashedPackets">Packets to be included in the certification hash.</param> + /// <param name="unhashedPackets">Packets to be attached unhashed to the certification.</param> + /// <param name="rand">input secured random.</param> + public PgpKeyRingGenerator( + int certificationLevel, + PgpKeyPair masterKey, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + byte[] rawPassPhrase, + bool useSha1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) { this.certificationLevel = certificationLevel; this.masterKey = masterKey; this.id = id; this.encAlgorithm = encAlgorithm; - this.passPhrase = passPhrase; + this.rawPassPhrase = rawPassPhrase; this.useSha1 = useSha1; this.hashedPacketVector = hashedPackets; this.unhashedPacketVector = unhashedPackets; this.rand = rand; - keys.Add(new PgpSecretKey(certificationLevel, masterKey, id, encAlgorithm, passPhrase, useSha1, hashedPackets, unhashedPackets, rand)); + keys.Add(new PgpSecretKey(certificationLevel, masterKey, id, encAlgorithm, rawPassPhrase, false, useSha1, hashedPackets, unhashedPackets, rand)); } /// <summary> /// Create a new key ring generator. /// </summary> + /// <remarks> + /// Conversion of the passphrase characters to bytes is performed using Convert.ToByte(), which is + /// the historical behaviour of the library (1.7 and earlier). + /// </remarks> /// <param name="certificationLevel">The certification level for keys on this ring.</param> /// <param name="masterKey">The master key pair.</param> /// <param name="id">The id to be associated with the ring.</param> @@ -109,19 +181,85 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp PgpSignatureSubpacketVector hashedPackets, PgpSignatureSubpacketVector unhashedPackets, SecureRandom rand) + : this(certificationLevel, masterKey, id, encAlgorithm, hashAlgorithm, false, passPhrase, useSha1, hashedPackets, unhashedPackets, rand) + { + } + + /// <summary> + /// Create a new key ring generator. + /// </summary> + /// <param name="certificationLevel">The certification level for keys on this ring.</param> + /// <param name="masterKey">The master key pair.</param> + /// <param name="id">The id to be associated with the ring.</param> + /// <param name="encAlgorithm">The algorithm to be used to protect secret keys.</param> + /// <param name="hashAlgorithm">The hash algorithm.</param> + /// <param name="utf8PassPhrase"> + /// If true, conversion of the passphrase to bytes uses Encoding.UTF8.GetBytes(), otherwise the conversion + /// is performed using Convert.ToByte(), which is the historical behaviour of the library (1.7 and earlier). + /// </param> + /// <param name="passPhrase">The passPhrase to be used to protect secret keys.</param> + /// <param name="useSha1">Checksum the secret keys with SHA1 rather than the older 16 bit checksum.</param> + /// <param name="hashedPackets">Packets to be included in the certification hash.</param> + /// <param name="unhashedPackets">Packets to be attached unhashed to the certification.</param> + /// <param name="rand">input secured random.</param> + public PgpKeyRingGenerator( + int certificationLevel, + PgpKeyPair masterKey, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + HashAlgorithmTag hashAlgorithm, + bool utf8PassPhrase, + char[] passPhrase, + bool useSha1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(certificationLevel, masterKey, id, encAlgorithm, hashAlgorithm, + PgpUtilities.EncodePassPhrase(passPhrase, utf8PassPhrase), + useSha1, hashedPackets, unhashedPackets, rand) + { + } + + /// <summary> + /// Create a new key ring generator. + /// </summary> + /// <remarks> + /// Allows the caller to handle the encoding of the passphrase to bytes. + /// </remarks> + /// <param name="certificationLevel">The certification level for keys on this ring.</param> + /// <param name="masterKey">The master key pair.</param> + /// <param name="id">The id to be associated with the ring.</param> + /// <param name="encAlgorithm">The algorithm to be used to protect secret keys.</param> + /// <param name="hashAlgorithm">The hash algorithm.</param> + /// <param name="rawPassPhrase">The passPhrase to be used to protect secret keys.</param> + /// <param name="useSha1">Checksum the secret keys with SHA1 rather than the older 16 bit checksum.</param> + /// <param name="hashedPackets">Packets to be included in the certification hash.</param> + /// <param name="unhashedPackets">Packets to be attached unhashed to the certification.</param> + /// <param name="rand">input secured random.</param> + public PgpKeyRingGenerator( + int certificationLevel, + PgpKeyPair masterKey, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + HashAlgorithmTag hashAlgorithm, + byte[] rawPassPhrase, + bool useSha1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) { this.certificationLevel = certificationLevel; this.masterKey = masterKey; this.id = id; this.encAlgorithm = encAlgorithm; - this.passPhrase = passPhrase; + this.rawPassPhrase = rawPassPhrase; this.useSha1 = useSha1; this.hashedPacketVector = hashedPackets; this.unhashedPacketVector = unhashedPackets; this.rand = rand; this.hashAlgorithm = hashAlgorithm; - keys.Add(new PgpSecretKey(certificationLevel, masterKey, id, encAlgorithm, hashAlgorithm, passPhrase, useSha1, hashedPackets, unhashedPackets, rand)); + keys.Add(new PgpSecretKey(certificationLevel, masterKey, id, encAlgorithm, hashAlgorithm, rawPassPhrase, false, useSha1, hashedPackets, unhashedPackets, rand)); } /// <summary>Add a subkey to the key ring to be generated with default certification.</summary> @@ -172,7 +310,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp subSigs.Add(sGen.GenerateCertification(masterKey.PublicKey, keyPair.PublicKey)); - keys.Add(new PgpSecretKey(keyPair.PrivateKey, new PgpPublicKey(keyPair.PublicKey, null, subSigs), encAlgorithm, passPhrase, useSha1, rand)); + keys.Add(new PgpSecretKey(keyPair.PrivateKey, new PgpPublicKey(keyPair.PublicKey, null, subSigs), encAlgorithm, + rawPassPhrase, false, useSha1, rand, false)); } catch (PgpException e) { @@ -215,7 +354,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp IList subSigs = Platform.CreateArrayList(); subSigs.Add(sGen.GenerateCertification(masterKey.PublicKey, keyPair.PublicKey)); - keys.Add(new PgpSecretKey(keyPair.PrivateKey, new PgpPublicKey(keyPair.PublicKey, null, subSigs), encAlgorithm, passPhrase, useSha1, rand)); + keys.Add(new PgpSecretKey(keyPair.PrivateKey, new PgpPublicKey(keyPair.PublicKey, null, subSigs), encAlgorithm, + rawPassPhrase, false, useSha1, rand, false)); } catch (PgpException) { diff --git a/crypto/src/openpgp/PgpPbeEncryptedData.cs b/crypto/src/openpgp/PgpPbeEncryptedData.cs index c5fe89407..f43f2f512 100644 --- a/crypto/src/openpgp/PgpPbeEncryptedData.cs +++ b/crypto/src/openpgp/PgpPbeEncryptedData.cs @@ -30,18 +30,43 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } /// <summary>Return the decrypted input stream, using the passed in passphrase.</summary> - public Stream GetDataStream( - char[] passPhrase) + /// <remarks> + /// Conversion of the passphrase characters to bytes is performed using Convert.ToByte(), which is + /// the historical behaviour of the library (1.7 and earlier). + /// </remarks> + public Stream GetDataStream(char[] passPhrase) + { + return DoGetDataStream(PgpUtilities.EncodePassPhrase(passPhrase, false), true); + } + + /// <summary>Return the decrypted input stream, using the passed in passphrase.</summary> + /// <remarks> + /// The passphrase is encoded to bytes using UTF8 (Encoding.UTF8.GetBytes). + /// </remarks> + public Stream GetDataStreamUtf8(char[] passPhrase) + { + return DoGetDataStream(PgpUtilities.EncodePassPhrase(passPhrase, true), true); + } + + /// <summary>Return the decrypted input stream, using the passed in passphrase.</summary> + /// <remarks> + /// Allows the caller to handle the encoding of the passphrase to bytes. + /// </remarks> + public Stream GetDataStreamRaw(byte[] rawPassPhrase) + { + return DoGetDataStream(rawPassPhrase, false); + } + + internal Stream DoGetDataStream(byte[] rawPassPhrase, bool clearPassPhrase) { try { SymmetricKeyAlgorithmTag keyAlgorithm = keyData.EncAlgorithm; - KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase( - keyAlgorithm, keyData.S2k, passPhrase); - + KeyParameter key = PgpUtilities.DoMakeKeyFromPassPhrase( + keyAlgorithm, keyData.S2k, rawPassPhrase, clearPassPhrase); - byte[] secKeyData = keyData.GetSecKeyData(); + byte[] secKeyData = keyData.GetSecKeyData(); if (secKeyData != null && secKeyData.Length > 0) { IBufferedCipher keyCipher = CipherUtilities.GetCipher( diff --git a/crypto/src/openpgp/PgpSecretKey.cs b/crypto/src/openpgp/PgpSecretKey.cs index 1027393ce..0f472c1a4 100644 --- a/crypto/src/openpgp/PgpSecretKey.cs +++ b/crypto/src/openpgp/PgpSecretKey.cs @@ -30,18 +30,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp PgpPrivateKey privKey, PgpPublicKey pubKey, SymmetricKeyAlgorithmTag encAlgorithm, - char[] passPhrase, - bool useSha1, - SecureRandom rand) - : this(privKey, pubKey, encAlgorithm, passPhrase, useSha1, rand, false) - { - } - - internal PgpSecretKey( - PgpPrivateKey privKey, - PgpPublicKey pubKey, - SymmetricKeyAlgorithmTag encAlgorithm, - char[] passPhrase, + byte[] rawPassPhrase, + bool clearPassPhrase, bool useSha1, SecureRandom rand, bool isMasterKey) @@ -107,7 +97,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp byte[] encData; if (pub.Version >= 4) { - encData = EncryptKeyData(keyData, encAlgorithm, passPhrase, rand, out s2k, out iv); + encData = EncryptKeyData(keyData, encAlgorithm, rawPassPhrase, clearPassPhrase, rand, out s2k, out iv); } else { @@ -139,6 +129,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } + /// <remarks> + /// Conversion of the passphrase characters to bytes is performed using Convert.ToByte(), which is + /// the historical behaviour of the library (1.7 and earlier). + /// </remarks> + [Obsolete("Use the constructor taking an explicit 'useSha1' parameter instead")] public PgpSecretKey( int certificationLevel, PgpKeyPair keyPair, @@ -152,32 +147,151 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp { } + /// <remarks> + /// Conversion of the passphrase characters to bytes is performed using Convert.ToByte(), which is + /// the historical behaviour of the library (1.7 and earlier). + /// </remarks> + public PgpSecretKey( + int certificationLevel, + PgpKeyPair keyPair, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + bool useSha1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(certificationLevel, keyPair, id, encAlgorithm, false, passPhrase, useSha1, hashedPackets, unhashedPackets, rand) + { + } + + /// <remarks> + /// If utf8PassPhrase is true, conversion of the passphrase to bytes uses Encoding.UTF8.GetBytes(), otherwise the conversion + /// is performed using Convert.ToByte(), which is the historical behaviour of the library (1.7 and earlier). + /// </remarks> public PgpSecretKey( int certificationLevel, PgpKeyPair keyPair, string id, SymmetricKeyAlgorithmTag encAlgorithm, + bool utf8PassPhrase, char[] passPhrase, bool useSha1, PgpSignatureSubpacketVector hashedPackets, PgpSignatureSubpacketVector unhashedPackets, SecureRandom rand) - : this(keyPair.PrivateKey, CertifiedPublicKey(certificationLevel, keyPair, id, hashedPackets, unhashedPackets), encAlgorithm, passPhrase, useSha1, rand, true) + : this(certificationLevel, keyPair, id, encAlgorithm, + PgpUtilities.EncodePassPhrase(passPhrase, utf8PassPhrase), true, + useSha1, hashedPackets, unhashedPackets, rand) + { + } + + /// <remarks> + /// Allows the caller to handle the encoding of the passphrase to bytes. + /// </remarks> + public PgpSecretKey( + int certificationLevel, + PgpKeyPair keyPair, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + byte[] rawPassPhrase, + bool useSha1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(certificationLevel, keyPair, id, encAlgorithm, rawPassPhrase, false, useSha1, hashedPackets, unhashedPackets, rand) + { + } + + internal PgpSecretKey( + int certificationLevel, + PgpKeyPair keyPair, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + byte[] rawPassPhrase, + bool clearPassPhrase, + bool useSha1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(keyPair.PrivateKey, CertifiedPublicKey(certificationLevel, keyPair, id, hashedPackets, unhashedPackets), + encAlgorithm, rawPassPhrase, clearPassPhrase, useSha1, rand, true) + { + } + + /// <remarks> + /// Conversion of the passphrase characters to bytes is performed using Convert.ToByte(), which is + /// the historical behaviour of the library (1.7 and earlier). + /// </remarks> + public PgpSecretKey( + int certificationLevel, + PgpKeyPair keyPair, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + HashAlgorithmTag hashAlgorithm, + char[] passPhrase, + bool useSha1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(certificationLevel, keyPair, id, encAlgorithm, hashAlgorithm, false, passPhrase, useSha1, hashedPackets, unhashedPackets, rand) { } + /// <remarks> + /// If utf8PassPhrase is true, conversion of the passphrase to bytes uses Encoding.UTF8.GetBytes(), otherwise the conversion + /// is performed using Convert.ToByte(), which is the historical behaviour of the library (1.7 and earlier). + /// </remarks> public PgpSecretKey( int certificationLevel, PgpKeyPair keyPair, string id, SymmetricKeyAlgorithmTag encAlgorithm, HashAlgorithmTag hashAlgorithm, + bool utf8PassPhrase, char[] passPhrase, bool useSha1, PgpSignatureSubpacketVector hashedPackets, PgpSignatureSubpacketVector unhashedPackets, SecureRandom rand) - : this(keyPair.PrivateKey, CertifiedPublicKey(certificationLevel, keyPair, id, hashedPackets, unhashedPackets, hashAlgorithm), encAlgorithm, passPhrase, useSha1, rand, true) + : this(certificationLevel, keyPair, id, encAlgorithm, hashAlgorithm, + PgpUtilities.EncodePassPhrase(passPhrase, utf8PassPhrase), true, + useSha1, hashedPackets, unhashedPackets, rand) + { + } + + /// <remarks> + /// Allows the caller to handle the encoding of the passphrase to bytes. + /// </remarks> + public PgpSecretKey( + int certificationLevel, + PgpKeyPair keyPair, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + HashAlgorithmTag hashAlgorithm, + byte[] rawPassPhrase, + bool useSha1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(certificationLevel, keyPair, id, encAlgorithm, hashAlgorithm, rawPassPhrase, false, useSha1, hashedPackets, unhashedPackets, rand) + { + } + + internal PgpSecretKey( + int certificationLevel, + PgpKeyPair keyPair, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + HashAlgorithmTag hashAlgorithm, + byte[] rawPassPhrase, + bool clearPassPhrase, + bool useSha1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(keyPair.PrivateKey, CertifiedPublicKey(certificationLevel, keyPair, id, hashedPackets, unhashedPackets, hashAlgorithm), + encAlgorithm, rawPassPhrase, clearPassPhrase, useSha1, rand, true) { } @@ -269,7 +383,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp SecureRandom rand) : this(certificationLevel, new PgpKeyPair(algorithm, pubKey, privKey, time), - id, encAlgorithm, passPhrase, hashedPackets, unhashedPackets, rand) + id, encAlgorithm, passPhrase, false, hashedPackets, unhashedPackets, rand) { } @@ -367,8 +481,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp get { return pub.GetUserAttributes(); } } - private byte[] ExtractKeyData( - char[] passPhrase) + private byte[] ExtractKeyData(byte[] rawPassPhrase, bool clearPassPhrase) { SymmetricKeyAlgorithmTag encAlgorithm = secret.EncAlgorithm; byte[] encData = secret.GetSecretKeyData(); @@ -380,7 +493,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp // TODO Factor this block out as 'decryptData' try { - KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(secret.EncAlgorithm, secret.S2k, passPhrase); + KeyParameter key = PgpUtilities.DoMakeKeyFromPassPhrase(secret.EncAlgorithm, secret.S2k, rawPassPhrase, clearPassPhrase); byte[] iv = secret.GetIV(); byte[] data; @@ -483,8 +596,34 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } /// <summary>Extract a <c>PgpPrivateKey</c> from this secret key's encrypted contents.</summary> - public PgpPrivateKey ExtractPrivateKey( - char[] passPhrase) + /// <remarks> + /// Conversion of the passphrase characters to bytes is performed using Convert.ToByte(), which is + /// the historical behaviour of the library (1.7 and earlier). + /// </remarks> + public PgpPrivateKey ExtractPrivateKey(char[] passPhrase) + { + return DoExtractPrivateKey(PgpUtilities.EncodePassPhrase(passPhrase, false), true); + } + + /// <summary>Extract a <c>PgpPrivateKey</c> from this secret key's encrypted contents.</summary> + /// <remarks> + /// The passphrase is encoded to bytes using UTF8 (Encoding.UTF8.GetBytes). + /// </remarks> + public PgpPrivateKey ExtractPrivateKeyUtf8(char[] passPhrase) + { + return DoExtractPrivateKey(PgpUtilities.EncodePassPhrase(passPhrase, true), true); + } + + /// <summary>Extract a <c>PgpPrivateKey</c> from this secret key's encrypted contents.</summary> + /// <remarks> + /// Allows the caller to handle the encoding of the passphrase to bytes. + /// </remarks> + public PgpPrivateKey ExtractPrivateKeyRaw(byte[] rawPassPhrase) + { + return DoExtractPrivateKey(rawPassPhrase, false); + } + + internal PgpPrivateKey DoExtractPrivateKey(byte[] rawPassPhrase, bool clearPassPhrase) { if (IsPrivateKeyEmpty) return null; @@ -492,7 +631,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp PublicKeyPacket pubPk = secret.PublicKeyPacket; try { - byte[] data = ExtractKeyData(passPhrase); + byte[] data = ExtractKeyData(rawPassPhrase, clearPassPhrase); BcpgInputStream bcpgIn = BcpgInputStream.Wrap(new MemoryStream(data, false)); AsymmetricKeyParameter privateKey; switch (pubPk.Algorithm) @@ -652,6 +791,10 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp /// Return a copy of the passed in secret key, encrypted using a new password /// and the passed in algorithm. /// </summary> + /// <remarks> + /// Conversion of the passphrase characters to bytes is performed using Convert.ToByte(), which is + /// the historical behaviour of the library (1.7 and earlier). + /// </remarks> /// <param name="key">The PgpSecretKey to be copied.</param> /// <param name="oldPassPhrase">The current password for the key.</param> /// <param name="newPassPhrase">The new password for the key.</param> @@ -664,11 +807,67 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp SymmetricKeyAlgorithmTag newEncAlgorithm, SecureRandom rand) { + return DoCopyWithNewPassword(key, PgpUtilities.EncodePassPhrase(oldPassPhrase, false), + PgpUtilities.EncodePassPhrase(newPassPhrase, false), true, newEncAlgorithm, rand); + } + + /// <summary> + /// Return a copy of the passed in secret key, encrypted using a new password + /// and the passed in algorithm. + /// </summary> + /// <remarks> + /// The passphrase is encoded to bytes using UTF8 (Encoding.UTF8.GetBytes). + /// </remarks> + /// <param name="key">The PgpSecretKey to be copied.</param> + /// <param name="oldPassPhrase">The current password for the key.</param> + /// <param name="newPassPhrase">The new password for the key.</param> + /// <param name="newEncAlgorithm">The algorithm to be used for the encryption.</param> + /// <param name="rand">Source of randomness.</param> + public static PgpSecretKey CopyWithNewPasswordUtf8( + PgpSecretKey key, + char[] oldPassPhrase, + char[] newPassPhrase, + SymmetricKeyAlgorithmTag newEncAlgorithm, + SecureRandom rand) + { + return DoCopyWithNewPassword(key, PgpUtilities.EncodePassPhrase(oldPassPhrase, true), + PgpUtilities.EncodePassPhrase(newPassPhrase, true), true, newEncAlgorithm, rand); + } + + /// <summary> + /// Return a copy of the passed in secret key, encrypted using a new password + /// and the passed in algorithm. + /// </summary> + /// <remarks> + /// Allows the caller to handle the encoding of the passphrase to bytes. + /// </remarks> + /// <param name="key">The PgpSecretKey to be copied.</param> + /// <param name="rawOldPassPhrase">The current password for the key.</param> + /// <param name="rawNewPassPhrase">The new password for the key.</param> + /// <param name="newEncAlgorithm">The algorithm to be used for the encryption.</param> + /// <param name="rand">Source of randomness.</param> + public static PgpSecretKey CopyWithNewPasswordRaw( + PgpSecretKey key, + byte[] rawOldPassPhrase, + byte[] rawNewPassPhrase, + SymmetricKeyAlgorithmTag newEncAlgorithm, + SecureRandom rand) + { + return DoCopyWithNewPassword(key, rawOldPassPhrase, rawNewPassPhrase, false, newEncAlgorithm, rand); + } + internal static PgpSecretKey DoCopyWithNewPassword( + PgpSecretKey key, + byte[] rawOldPassPhrase, + byte[] rawNewPassPhrase, + bool clearPassPhrase, + SymmetricKeyAlgorithmTag newEncAlgorithm, + SecureRandom rand) + { if (key.IsPrivateKeyEmpty) throw new PgpException("no private key in this SecretKey - public key present only."); - byte[] rawKeyData = key.ExtractKeyData(oldPassPhrase); + byte[] rawKeyData = key.ExtractKeyData(rawOldPassPhrase, clearPassPhrase); int s2kUsage = key.secret.S2kUsage; byte[] iv = null; S2k s2k = null; @@ -696,11 +895,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } else { + if (s2kUsage == SecretKeyPacket.UsageNone) + { + s2kUsage = SecretKeyPacket.UsageChecksum; + } + try { if (pubKeyPacket.Version >= 4) { - keyData = EncryptKeyData(rawKeyData, newEncAlgorithm, newPassPhrase, rand, out s2k, out iv); + keyData = EncryptKeyData(rawKeyData, newEncAlgorithm, rawNewPassPhrase, clearPassPhrase, rand, out s2k, out iv); } else { @@ -749,7 +953,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp private static byte[] EncryptKeyData( byte[] rawKeyData, SymmetricKeyAlgorithmTag encAlgorithm, - char[] passPhrase, + byte[] rawPassPhrase, + bool clearPassPhrase, SecureRandom random, out S2k s2k, out byte[] iv) @@ -769,7 +974,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp random.NextBytes(s2kIV); s2k = new S2k(HashAlgorithmTag.Sha1, s2kIV, 0x60); - KeyParameter kp = PgpUtilities.MakeKeyFromPassPhrase(encAlgorithm, s2k, passPhrase); + KeyParameter kp = PgpUtilities.DoMakeKeyFromPassPhrase(encAlgorithm, s2k, rawPassPhrase, clearPassPhrase); iv = new byte[c.GetBlockSize()]; random.NextBytes(iv); @@ -779,13 +984,42 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return c.DoFinal(rawKeyData); } - /** - * Parse a secret key from one of the GPG S expression keys associating it with the passed in public key. - * - * @return a secret key object. - */ + /// <summary> + /// Parse a secret key from one of the GPG S expression keys associating it with the passed in public key. + /// </summary> + /// <remarks> + /// Conversion of the passphrase characters to bytes is performed using Convert.ToByte(), which is + /// the historical behaviour of the library (1.7 and earlier). + /// </remarks> public static PgpSecretKey ParseSecretKeyFromSExpr(Stream inputStream, char[] passPhrase, PgpPublicKey pubKey) { + return DoParseSecretKeyFromSExpr(inputStream, PgpUtilities.EncodePassPhrase(passPhrase, false), true, pubKey); + } + + /// <summary> + /// Parse a secret key from one of the GPG S expression keys associating it with the passed in public key. + /// </summary> + /// <remarks> + /// The passphrase is encoded to bytes using UTF8 (Encoding.UTF8.GetBytes). + /// </remarks> + public static PgpSecretKey ParseSecretKeyFromSExprUtf8(Stream inputStream, char[] passPhrase, PgpPublicKey pubKey) + { + return DoParseSecretKeyFromSExpr(inputStream, PgpUtilities.EncodePassPhrase(passPhrase, true), true, pubKey); + } + + /// <summary> + /// Parse a secret key from one of the GPG S expression keys associating it with the passed in public key. + /// </summary> + /// <remarks> + /// Allows the caller to handle the encoding of the passphrase to bytes. + /// </remarks> + public static PgpSecretKey ParseSecretKeyFromSExprRaw(Stream inputStream, byte[] rawPassPhrase, PgpPublicKey pubKey) + { + return DoParseSecretKeyFromSExpr(inputStream, rawPassPhrase, false, pubKey); + } + + internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[] rawPassPhrase, bool clearPassPhrase, PgpPublicKey pubKey) + { SXprUtilities.SkipOpenParenthesis(inputStream); string type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); @@ -826,7 +1060,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp SXprUtilities.SkipCloseParenthesis(inputStream); - byte[] dValue = GetDValue(inputStream, passPhrase, curveName); + byte[] dValue = GetDValue(inputStream, rawPassPhrase, clearPassPhrase, curveName); // TODO: check SHA-1 hash. return new PgpSecretKey(new SecretKeyPacket(pubKey.PublicKeyPacket, SymmetricKeyAlgorithmTag.Null, null, null, @@ -836,13 +1070,45 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp throw new PgpException("unknown key type found"); } - /** - * Parse a secret key from one of the GPG S expression keys. - * - * @return a secret key object. - */ + /// <summary> + /// Parse a secret key from one of the GPG S expression keys. + /// </summary> + /// <remarks> + /// Conversion of the passphrase characters to bytes is performed using Convert.ToByte(), which is + /// the historical behaviour of the library (1.7 and earlier). + /// </remarks> public static PgpSecretKey ParseSecretKeyFromSExpr(Stream inputStream, char[] passPhrase) { + return DoParseSecretKeyFromSExpr(inputStream, PgpUtilities.EncodePassPhrase(passPhrase, false), true); + } + + /// <summary> + /// Parse a secret key from one of the GPG S expression keys. + /// </summary> + /// <remarks> + /// The passphrase is encoded to bytes using UTF8 (Encoding.UTF8.GetBytes). + /// </remarks> + public static PgpSecretKey ParseSecretKeyFromSExprUtf8(Stream inputStream, char[] passPhrase) + { + return DoParseSecretKeyFromSExpr(inputStream, PgpUtilities.EncodePassPhrase(passPhrase, true), true); + } + + /// <summary> + /// Parse a secret key from one of the GPG S expression keys. + /// </summary> + /// <remarks> + /// Allows the caller to handle the encoding of the passphrase to bytes. + /// </remarks> + public static PgpSecretKey ParseSecretKeyFromSExprRaw(Stream inputStream, byte[] rawPassPhrase) + { + return DoParseSecretKeyFromSExpr(inputStream, rawPassPhrase, false); + } + + /// <summary> + /// Parse a secret key from one of the GPG S expression keys. + /// </summary> + internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[] rawPassPhrase, bool clearPassPhrase) + { SXprUtilities.SkipOpenParenthesis(inputStream); string type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte()); @@ -891,7 +1157,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp SXprUtilities.SkipCloseParenthesis(inputStream); - byte[] dValue = GetDValue(inputStream, passPhrase, curveName); + byte[] dValue = GetDValue(inputStream, rawPassPhrase, clearPassPhrase, curveName); // TODO: check SHA-1 hash. return new PgpSecretKey(new SecretKeyPacket(pubPacket, SymmetricKeyAlgorithmTag.Null, null, null, @@ -901,7 +1167,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp throw new PgpException("unknown key type found"); } - private static byte[] GetDValue(Stream inputStream, char[] passPhrase, string curveName) + private static byte[] GetDValue(Stream inputStream, byte[] rawPassPhrase, bool clearPassPhrase, string curveName) { string type; SXprUtilities.SkipOpenParenthesis(inputStream); @@ -932,7 +1198,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } // TODO: recognise other algorithms - KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag.Aes128, s2k, passPhrase); + KeyParameter key = PgpUtilities.DoMakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag.Aes128, s2k, rawPassPhrase, clearPassPhrase); byte[] data = RecoverKeyData(SymmetricKeyAlgorithmTag.Aes128, "/CBC/NoPadding", key, iv, secKeyData, 0, secKeyData.Length); diff --git a/crypto/src/openpgp/PgpUtilities.cs b/crypto/src/openpgp/PgpUtilities.cs index e4551db07..65c07b2e2 100644 --- a/crypto/src/openpgp/PgpUtilities.cs +++ b/crypto/src/openpgp/PgpUtilities.cs @@ -193,13 +193,44 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return MakeKey(algorithm, keyBytes); } - public static KeyParameter MakeKeyFromPassPhrase( - SymmetricKeyAlgorithmTag algorithm, - S2k s2k, - char[] passPhrase) + internal static byte[] EncodePassPhrase(char[] passPhrase, bool utf8) + { + return passPhrase == null + ? null + : utf8 + ? Encoding.UTF8.GetBytes(passPhrase) + : Strings.ToByteArray(passPhrase); + } + + /// <remarks> + /// Conversion of the passphrase characters to bytes is performed using Convert.ToByte(), which is + /// the historical behaviour of the library (1.7 and earlier). + /// </remarks> + public static KeyParameter MakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag algorithm, S2k s2k, char[] passPhrase) + { + return DoMakeKeyFromPassPhrase(algorithm, s2k, EncodePassPhrase(passPhrase, false), true); + } + + /// <remarks> + /// The passphrase is encoded to bytes using UTF8 (Encoding.UTF8.GetBytes). + /// </remarks> + public static KeyParameter MakeKeyFromPassPhraseUtf8(SymmetricKeyAlgorithmTag algorithm, S2k s2k, char[] passPhrase) + { + return DoMakeKeyFromPassPhrase(algorithm, s2k, EncodePassPhrase(passPhrase, true), true); + } + + /// <remarks> + /// Allows the caller to handle the encoding of the passphrase to bytes. + /// </remarks> + public static KeyParameter MakeKeyFromPassPhraseRaw(SymmetricKeyAlgorithmTag algorithm, S2k s2k, byte[] rawPassPhrase) + { + return DoMakeKeyFromPassPhrase(algorithm, s2k, rawPassPhrase, false); + } + + internal static KeyParameter DoMakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag algorithm, S2k s2k, byte[] rawPassPhrase, bool clearPassPhrase) { int keySize = GetKeySize(algorithm); - byte[] pBytes = Encoding.UTF8.GetBytes(passPhrase); + byte[] pBytes = rawPassPhrase; byte[] keyBytes = new byte[(keySize + 7) / 8]; int generatedBytes = 0; @@ -308,12 +339,15 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp loopCount++; } - Array.Clear(pBytes, 0, pBytes.Length); + if (clearPassPhrase && rawPassPhrase != null) + { + Array.Clear(rawPassPhrase, 0, rawPassPhrase.Length); + } - return MakeKey(algorithm, keyBytes); + return MakeKey(algorithm, keyBytes); } - /// <summary>Write out the passed in file as a literal data packet.</summary> + /// <summary>Write out the passed in file as a literal data packet.</summary> public static void WriteFileToLiteralData( Stream output, char fileType, |