From a6039dfa9392ca64a3b0efe591aaf7b8441d13fc Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 25 Feb 2014 17:16:34 +0700 Subject: Port some openpgp updates from Java build for secret keys --- crypto/src/openpgp/PgpSecretKey.cs | 78 +++++-- crypto/src/openpgp/PgpSecretKeyRing.cs | 377 +++++++++++++++++---------------- crypto/src/util/Arrays.cs | 13 ++ 3 files changed, 262 insertions(+), 206 deletions(-) (limited to 'crypto/src') diff --git a/crypto/src/openpgp/PgpSecretKey.cs b/crypto/src/openpgp/PgpSecretKey.cs index 94c892a87..872316dd7 100644 --- a/crypto/src/openpgp/PgpSecretKey.cs +++ b/crypto/src/openpgp/PgpSecretKey.cs @@ -5,6 +5,7 @@ using System.IO; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Bcpg.OpenPgp { @@ -75,28 +76,36 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp pOut.WriteObject(secKey); byte[] keyData = bOut.ToArray(); - byte[] checksumBytes = Checksum(useSha1, keyData, keyData.Length); + byte[] checksumData = Checksum(useSha1, keyData, keyData.Length); - pOut.Write(checksumBytes); - - byte[] bOutData = bOut.ToArray(); + keyData = Arrays.Concatenate(keyData, checksumData); if (encAlgorithm == SymmetricKeyAlgorithmTag.Null) { if (isMasterKey) { - this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, null, null, bOutData); + this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, null, null, keyData); } else { - this.secret = new SecretSubkeyPacket(pub.publicPk, encAlgorithm, null, null, bOutData); + this.secret = new SecretSubkeyPacket(pub.publicPk, encAlgorithm, null, null, keyData); } } else { S2k s2k; byte[] iv; - byte[] encData = EncryptKeyData(bOutData, encAlgorithm, passPhrase, rand, out s2k, out iv); + + byte[] encData; + if (pub.Version >= 4) + { + encData = EncryptKeyData(keyData, encAlgorithm, passPhrase, rand, out s2k, out iv); + } + else + { + // TODO v3 RSA key encryption + throw Platform.CreateNotImplementedException("v3 RSA"); + } int s2kUsage = useSha1 ? SecretKeyPacket.UsageSha1 @@ -145,11 +154,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp PgpSignatureSubpacketVector hashedPackets, PgpSignatureSubpacketVector unhashedPackets, SecureRandom rand) - : this(keyPair.PrivateKey, certifiedPublicKey(certificationLevel, keyPair, id, hashedPackets, unhashedPackets), encAlgorithm, passPhrase, useSha1, rand, true) + : this(keyPair.PrivateKey, CertifiedPublicKey(certificationLevel, keyPair, id, hashedPackets, unhashedPackets), encAlgorithm, passPhrase, useSha1, rand, true) { } - private static PgpPublicKey certifiedPublicKey( + private static PgpPublicKey CertifiedPublicKey( int certificationLevel, PgpKeyPair keyPair, string id, @@ -254,6 +263,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp get { return pub.IsMasterKey; } } + /// Detect if the Secret Key's Private Key is empty or not + public bool IsPrivateKeyEmpty + { + get + { + byte[] secKeyData = secret.GetSecretKeyData(); + + return secKeyData == null || secKeyData.Length < 1; + } + } + /// The algorithm the key is encrypted with. public SymmetricKeyAlgorithmTag KeyEncryptionAlgorithm { @@ -293,9 +313,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp byte[] encData = secret.GetSecretKeyData(); if (alg == SymmetricKeyAlgorithmTag.Null) + // TODO Check checksum here? return encData; - byte[] data; IBufferedCipher c = null; try { @@ -307,13 +327,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp throw new PgpException("Exception creating cipher", e); } - // TODO Factor this block out as 'encryptData' + // TODO Factor this block out as 'decryptData' try { KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(secret.EncAlgorithm, secret.S2k, passPhrase); byte[] iv = secret.GetIV(); + byte[] data; - if (secret.PublicKeyPacket.Version == 4) + if (secret.PublicKeyPacket.Version >= 4) { c.Init(false, new ParametersWithIV(key, iv)); @@ -334,6 +355,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp { data = new byte[encData.Length]; + iv = Arrays.Clone(iv); + // // read in the four numbers // @@ -359,11 +382,15 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } // - // verify Checksum + // verify and copy checksum // + + data[pos] = encData[pos]; + data[pos + 1] = encData[pos + 1]; + int cs = ((encData[pos] << 8) & 0xff00) | (encData[pos + 1] & 0xff); int calcCs = 0; - for (int j=0; j < data.Length-2; j++) + for (int j = 0; j < pos; j++) { calcCs += data[j] & 0xff; } @@ -393,8 +420,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp public PgpPrivateKey ExtractPrivateKey( char[] passPhrase) { - byte[] secKeyData = secret.GetSecretKeyData(); - if (secKeyData == null || secKeyData.Length < 1) + if (IsPrivateKeyEmpty) return null; PublicKeyPacket pubPk = secret.PublicKeyPacket; @@ -559,11 +585,15 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp 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); int s2kUsage = key.secret.S2kUsage; byte[] iv = null; S2k s2k = null; byte[] keyData; + PublicKeyPacket pubKeyPacket = key.secret.PublicKeyPacket; if (newEncAlgorithm == SymmetricKeyAlgorithmTag.Null) { @@ -588,7 +618,15 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp { try { - keyData = EncryptKeyData(rawKeyData, newEncAlgorithm, newPassPhrase, rand, out s2k, out iv); + if (pubKeyPacket.Version >= 4) + { + keyData = EncryptKeyData(rawKeyData, newEncAlgorithm, newPassPhrase, rand, out s2k, out iv); + } + else + { + // TODO v3 RSA key encryption + throw Platform.CreateNotImplementedException("v3 RSA"); + } } catch (PgpException e) { @@ -603,13 +641,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp SecretKeyPacket secret; if (key.secret is SecretSubkeyPacket) { - secret = new SecretSubkeyPacket(key.secret.PublicKeyPacket, - newEncAlgorithm, s2kUsage, s2k, iv, keyData); + secret = new SecretSubkeyPacket(pubKeyPacket, newEncAlgorithm, s2kUsage, s2k, iv, keyData); } else { - secret = new SecretKeyPacket(key.secret.PublicKeyPacket, - newEncAlgorithm, s2kUsage, s2k, iv, keyData); + secret = new SecretKeyPacket(pubKeyPacket, newEncAlgorithm, s2kUsage, s2k, iv, keyData); } return new PgpSecretKey(secret, key.pub); diff --git a/crypto/src/openpgp/PgpSecretKeyRing.cs b/crypto/src/openpgp/PgpSecretKeyRing.cs index 3e646eaa1..70cd7217c 100644 --- a/crypto/src/openpgp/PgpSecretKeyRing.cs +++ b/crypto/src/openpgp/PgpSecretKeyRing.cs @@ -8,57 +8,57 @@ using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Bcpg.OpenPgp { - /// - /// Class to hold a single master secret key and its subkeys. - ///

- /// Often PGP keyring files consist of multiple master keys, if you are trying to process - /// or construct one of these you should use the PgpSecretKeyRingBundle class. - ///

- ///
- public class PgpSecretKeyRing - : PgpKeyRing + /// + /// Class to hold a single master secret key and its subkeys. + ///

+ /// Often PGP keyring files consist of multiple master keys, if you are trying to process + /// or construct one of these you should use the PgpSecretKeyRingBundle class. + ///

+ ///
+ public class PgpSecretKeyRing + : PgpKeyRing { private readonly IList keys; - private readonly IList extraPubKeys; - - internal PgpSecretKeyRing( - IList keys) - : this(keys, Platform.CreateArrayList()) - { - } - - private PgpSecretKeyRing( - IList keys, - IList extraPubKeys) - { - this.keys = keys; - this.extraPubKeys = extraPubKeys; - } - - public PgpSecretKeyRing( + private readonly IList extraPubKeys; + + internal PgpSecretKeyRing( + IList keys) + : this(keys, Platform.CreateArrayList()) + { + } + + private PgpSecretKeyRing( + IList keys, + IList extraPubKeys) + { + this.keys = keys; + this.extraPubKeys = extraPubKeys; + } + + public PgpSecretKeyRing( byte[] encoding) : this(new MemoryStream(encoding)) { } - public PgpSecretKeyRing( + public PgpSecretKeyRing( Stream inputStream) { - this.keys = Platform.CreateArrayList(); + this.keys = Platform.CreateArrayList(); this.extraPubKeys = Platform.CreateArrayList(); - BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream); + BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream); - PacketTag initialTag = bcpgInput.NextPacketTag(); - if (initialTag != PacketTag.SecretKey && initialTag != PacketTag.SecretSubkey) + PacketTag initialTag = bcpgInput.NextPacketTag(); + if (initialTag != PacketTag.SecretKey && initialTag != PacketTag.SecretSubkey) { throw new IOException("secret key ring doesn't start with secret key tag: " - + "tag 0x" + ((int)initialTag).ToString("X")); + + "tag 0x" + ((int)initialTag).ToString("X")); } - SecretKeyPacket secret = (SecretKeyPacket) bcpgInput.ReadPacket(); + SecretKeyPacket secret = (SecretKeyPacket) bcpgInput.ReadPacket(); - // + // // ignore GPG comment packets if found. // while (bcpgInput.NextPacketTag() == PacketTag.Experimental2) @@ -66,65 +66,65 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp bcpgInput.ReadPacket(); } - TrustPacket trust = ReadOptionalTrustPacket(bcpgInput); + TrustPacket trust = ReadOptionalTrustPacket(bcpgInput); - // revocation and direct signatures - IList keySigs = ReadSignaturesAndTrust(bcpgInput); + // revocation and direct signatures + IList keySigs = ReadSignaturesAndTrust(bcpgInput); - IList ids, idTrusts, idSigs; - ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs); + IList ids, idTrusts, idSigs; + ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs); - keys.Add(new PgpSecretKey(secret, new PgpPublicKey(secret.PublicKeyPacket, trust, keySigs, ids, idTrusts, idSigs))); + keys.Add(new PgpSecretKey(secret, new PgpPublicKey(secret.PublicKeyPacket, trust, keySigs, ids, idTrusts, idSigs))); - // Read subkeys - while (bcpgInput.NextPacketTag() == PacketTag.SecretSubkey - || bcpgInput.NextPacketTag() == PacketTag.PublicSubkey) + // Read subkeys + while (bcpgInput.NextPacketTag() == PacketTag.SecretSubkey + || bcpgInput.NextPacketTag() == PacketTag.PublicSubkey) { - if (bcpgInput.NextPacketTag() == PacketTag.SecretSubkey) - { - SecretSubkeyPacket sub = (SecretSubkeyPacket) bcpgInput.ReadPacket(); - - // - // ignore GPG comment packets if found. - // - while (bcpgInput.NextPacketTag() == PacketTag.Experimental2) - { - bcpgInput.ReadPacket(); - } - - TrustPacket subTrust = ReadOptionalTrustPacket(bcpgInput); - IList sigList = ReadSignaturesAndTrust(bcpgInput); - - keys.Add(new PgpSecretKey(sub, new PgpPublicKey(sub.PublicKeyPacket, subTrust, sigList))); - } - else - { - PublicSubkeyPacket sub = (PublicSubkeyPacket) bcpgInput.ReadPacket(); - - TrustPacket subTrust = ReadOptionalTrustPacket(bcpgInput); - IList sigList = ReadSignaturesAndTrust(bcpgInput); - - extraPubKeys.Add(new PgpPublicKey(sub, subTrust, sigList)); - } + if (bcpgInput.NextPacketTag() == PacketTag.SecretSubkey) + { + SecretSubkeyPacket sub = (SecretSubkeyPacket) bcpgInput.ReadPacket(); + + // + // ignore GPG comment packets if found. + // + while (bcpgInput.NextPacketTag() == PacketTag.Experimental2) + { + bcpgInput.ReadPacket(); + } + + TrustPacket subTrust = ReadOptionalTrustPacket(bcpgInput); + IList sigList = ReadSignaturesAndTrust(bcpgInput); + + keys.Add(new PgpSecretKey(sub, new PgpPublicKey(sub.PublicKeyPacket, subTrust, sigList))); + } + else + { + PublicSubkeyPacket sub = (PublicSubkeyPacket) bcpgInput.ReadPacket(); + + TrustPacket subTrust = ReadOptionalTrustPacket(bcpgInput); + IList sigList = ReadSignaturesAndTrust(bcpgInput); + + extraPubKeys.Add(new PgpPublicKey(sub, subTrust, sigList)); + } } } - /// Return the public key for the master key. + /// Return the public key for the master key. public PgpPublicKey GetPublicKey() { return ((PgpSecretKey) keys[0]).PublicKey; } - /// Return the master private key. + /// Return the master private key. public PgpSecretKey GetSecretKey() { return (PgpSecretKey) keys[0]; } - /// Allows enumeration of the secret keys. - /// An IEnumerable of PgpSecretKey objects. - public IEnumerable GetSecretKeys() + /// Allows enumeration of the secret keys. + /// An IEnumerable of PgpSecretKey objects. + public IEnumerable GetSecretKeys() { return new EnumerableProxy(keys); } @@ -132,29 +132,29 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp public PgpSecretKey GetSecretKey( long keyId) { - foreach (PgpSecretKey k in keys) - { - if (keyId == k.KeyId) - { - return k; - } - } - - return null; + foreach (PgpSecretKey k in keys) + { + if (keyId == k.KeyId) + { + return k; + } + } + + return null; + } + + /// + /// Return an iterator of the public keys in the secret key ring that + /// have no matching private key. At the moment only personal certificate data + /// appears in this fashion. + /// + /// An IEnumerable of unattached, or extra, public keys. + public IEnumerable GetExtraPublicKeys() + { + return new EnumerableProxy(extraPubKeys); } - /// - /// Return an iterator of the public keys in the secret key ring that - /// have no matching private key. At the moment only personal certificate data - /// appears in this fashion. - /// - /// An IEnumerable of unattached, or extra, public keys. - public IEnumerable GetExtraPublicKeys() - { - return new EnumerableProxy(extraPubKeys); - } - - public byte[] GetEncoded() + public byte[] GetEncoded() { MemoryStream bOut = new MemoryStream(); @@ -166,117 +166,124 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp public void Encode( Stream outStr) { - if (outStr == null) - throw new ArgumentNullException("outStr"); - - foreach (PgpSecretKey key in keys) - { - key.Encode(outStr); - } - foreach (PgpPublicKey extraPubKey in extraPubKeys) - { - extraPubKey.Encode(outStr); - } + if (outStr == null) + throw new ArgumentNullException("outStr"); + + foreach (PgpSecretKey key in keys) + { + key.Encode(outStr); + } + foreach (PgpPublicKey extraPubKey in extraPubKeys) + { + extraPubKey.Encode(outStr); + } } - /// - /// Replace the public key set on the secret ring with the corresponding key off the public ring. - /// - /// Secret ring to be changed. - /// Public ring containing the new public key set. - public static PgpSecretKeyRing ReplacePublicKeys( - PgpSecretKeyRing secretRing, - PgpPublicKeyRing publicRing) - { + /// + /// Replace the public key set on the secret ring with the corresponding key off the public ring. + /// + /// Secret ring to be changed. + /// Public ring containing the new public key set. + public static PgpSecretKeyRing ReplacePublicKeys( + PgpSecretKeyRing secretRing, + PgpPublicKeyRing publicRing) + { IList newList = Platform.CreateArrayList(secretRing.keys.Count); - foreach (PgpSecretKey sk in secretRing.keys) - { - PgpPublicKey pk = publicRing.GetPublicKey(sk.KeyId); - - newList.Add(PgpSecretKey.ReplacePublicKey(sk, pk)); - } - - return new PgpSecretKeyRing(newList); - } - - /// - /// Return a copy of the passed in secret key ring, with the master key and sub keys encrypted - /// using a new password and the passed in algorithm. - /// - /// The PgpSecretKeyRing to be copied. - /// The current password for key. - /// The new password for the key. - /// The algorithm to be used for the encryption. - /// Source of randomness. - public static PgpSecretKeyRing CopyWithNewPassword( - PgpSecretKeyRing ring, - char[] oldPassPhrase, - char[] newPassPhrase, - SymmetricKeyAlgorithmTag newEncAlgorithm, - SecureRandom rand) - { + foreach (PgpSecretKey sk in secretRing.keys) + { + PgpPublicKey pk = publicRing.GetPublicKey(sk.KeyId); + + newList.Add(PgpSecretKey.ReplacePublicKey(sk, pk)); + } + + return new PgpSecretKeyRing(newList); + } + + /// + /// Return a copy of the passed in secret key ring, with the master key and sub keys encrypted + /// using a new password and the passed in algorithm. + /// + /// The PgpSecretKeyRing to be copied. + /// The current password for key. + /// The new password for the key. + /// The algorithm to be used for the encryption. + /// Source of randomness. + public static PgpSecretKeyRing CopyWithNewPassword( + PgpSecretKeyRing ring, + char[] oldPassPhrase, + char[] newPassPhrase, + SymmetricKeyAlgorithmTag newEncAlgorithm, + SecureRandom rand) + { IList newKeys = Platform.CreateArrayList(ring.keys.Count); - foreach (PgpSecretKey secretKey in ring.GetSecretKeys()) - { - newKeys.Add(PgpSecretKey.CopyWithNewPassword(secretKey, oldPassPhrase, newPassPhrase, newEncAlgorithm, rand)); - } - - return new PgpSecretKeyRing(newKeys, ring.extraPubKeys); - } - - /// - /// Returns a new key ring with the secret key passed in either added or - /// replacing an existing one with the same key ID. - /// - /// The secret key ring to be modified. - /// The secret key to be inserted. - /// A new PgpSecretKeyRing - public static PgpSecretKeyRing InsertSecretKey( + foreach (PgpSecretKey secretKey in ring.GetSecretKeys()) + { + if (secretKey.IsPrivateKeyEmpty) + { + newKeys.Add(secretKey); + } + else + { + newKeys.Add(PgpSecretKey.CopyWithNewPassword(secretKey, oldPassPhrase, newPassPhrase, newEncAlgorithm, rand)); + } + } + + return new PgpSecretKeyRing(newKeys, ring.extraPubKeys); + } + + /// + /// Returns a new key ring with the secret key passed in either added or + /// replacing an existing one with the same key ID. + /// + /// The secret key ring to be modified. + /// The secret key to be inserted. + /// A new PgpSecretKeyRing + public static PgpSecretKeyRing InsertSecretKey( PgpSecretKeyRing secRing, PgpSecretKey secKey) { IList keys = Platform.CreateArrayList(secRing.keys); bool found = false; - bool masterFound = false; + bool masterFound = false; - for (int i = 0; i != keys.Count; i++) + for (int i = 0; i != keys.Count; i++) { PgpSecretKey key = (PgpSecretKey) keys[i]; - if (key.KeyId == secKey.KeyId) + if (key.KeyId == secKey.KeyId) { found = true; keys[i] = secKey; } - if (key.IsMasterKey) - { - masterFound = true; - } - } + if (key.IsMasterKey) + { + masterFound = true; + } + } if (!found) { - if (secKey.IsMasterKey) - { - if (masterFound) - throw new ArgumentException("cannot add a master key to a ring that already has one"); - - keys.Insert(0, secKey); - } - else - { - keys.Add(secKey); - } + if (secKey.IsMasterKey) + { + if (masterFound) + throw new ArgumentException("cannot add a master key to a ring that already has one"); + + keys.Insert(0, secKey); + } + else + { + keys.Add(secKey); + } } - return new PgpSecretKeyRing(keys, secRing.extraPubKeys); - } + return new PgpSecretKeyRing(keys, secRing.extraPubKeys); + } - /// Returns a new key ring with the secret key passed in removed from the key ring. - /// The secret key ring to be modified. - /// The secret key to be removed. - /// A new PgpSecretKeyRing, or null if secKey is not found. + /// Returns a new key ring with the secret key passed in removed from the key ring. + /// The secret key ring to be modified. + /// The secret key to be removed. + /// A new PgpSecretKeyRing, or null if secKey is not found. public static PgpSecretKeyRing RemoveSecretKey( PgpSecretKeyRing secRing, PgpSecretKey secKey) @@ -284,18 +291,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp IList keys = Platform.CreateArrayList(secRing.keys); bool found = false; - for (int i = 0; i < keys.Count; i++) + for (int i = 0; i < keys.Count; i++) { PgpSecretKey key = (PgpSecretKey)keys[i]; - if (key.KeyId == secKey.KeyId) + if (key.KeyId == secKey.KeyId) { found = true; keys.RemoveAt(i); } } - return found ? new PgpSecretKeyRing(keys, secRing.extraPubKeys) : null; + return found ? new PgpSecretKeyRing(keys, secRing.extraPubKeys) : null; } } } diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs index 3632587de..cc2025ef3 100644 --- a/crypto/src/util/Arrays.cs +++ b/crypto/src/util/Arrays.cs @@ -329,5 +329,18 @@ namespace Org.BouncyCastle.Utilities Array.Copy(data, off, result, 0, len); return result; } + + public static byte[] Concatenate(byte[] a, byte[] b) + { + if (a == null) + return Clone(b); + if (b == null) + return Clone(a); + + byte[] rv = new byte[a.Length + b.Length]; + Array.Copy(a, 0, rv, 0, a.Length); + Array.Copy(b, 0, rv, a.Length, b.Length); + return rv; + } } } -- cgit 1.4.1