diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2014-02-25 17:16:34 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2014-02-25 17:16:34 +0700 |
commit | a6039dfa9392ca64a3b0efe591aaf7b8441d13fc (patch) | |
tree | 8fcf9779569fe77fd0e65757ccce44345fc6fa92 | |
parent | Check a few more points in the encoding test (diff) | |
download | BouncyCastle.NET-ed25519-a6039dfa9392ca64a3b0efe591aaf7b8441d13fc.tar.xz |
Port some openpgp updates from Java build for secret keys
-rw-r--r-- | crypto/crypto.csproj | 5 | ||||
-rw-r--r-- | crypto/src/openpgp/PgpSecretKey.cs | 78 | ||||
-rw-r--r-- | crypto/src/openpgp/PgpSecretKeyRing.cs | 377 | ||||
-rw-r--r-- | crypto/src/util/Arrays.cs | 13 | ||||
-rw-r--r-- | crypto/test/src/openpgp/test/PGPNoPrivateKeyTest.cs | 169 |
5 files changed, 436 insertions, 206 deletions
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj index 1a2f8c132..8fae3a786 100644 --- a/crypto/crypto.csproj +++ b/crypto/crypto.csproj @@ -10577,6 +10577,11 @@ BuildAction = "Compile" /> <File + RelPath = "test\src\openpgp\test\PGPNoPrivateKeyTest.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "test\src\openpgp\test\PGPPacketTest.cs" SubType = "Code" BuildAction = "Compile" 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; } } + /// <summary>Detect if the Secret Key's Private Key is empty or not</summary> + public bool IsPrivateKeyEmpty + { + get + { + byte[] secKeyData = secret.GetSecretKeyData(); + + return secKeyData == null || secKeyData.Length < 1; + } + } + /// <summary>The algorithm the key is encrypted with.</summary> 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 { - /// <remarks> - /// Class to hold a single master secret key and its subkeys. - /// <p> - /// Often PGP keyring files consist of multiple master keys, if you are trying to process - /// or construct one of these you should use the <c>PgpSecretKeyRingBundle</c> class. - /// </p> - /// </remarks> - public class PgpSecretKeyRing - : PgpKeyRing + /// <remarks> + /// Class to hold a single master secret key and its subkeys. + /// <p> + /// Often PGP keyring files consist of multiple master keys, if you are trying to process + /// or construct one of these you should use the <c>PgpSecretKeyRingBundle</c> class. + /// </p> + /// </remarks> + 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)); + } } } - /// <summary>Return the public key for the master key.</summary> + /// <summary>Return the public key for the master key.</summary> public PgpPublicKey GetPublicKey() { return ((PgpSecretKey) keys[0]).PublicKey; } - /// <summary>Return the master private key.</summary> + /// <summary>Return the master private key.</summary> public PgpSecretKey GetSecretKey() { return (PgpSecretKey) keys[0]; } - /// <summary>Allows enumeration of the secret keys.</summary> - /// <returns>An <c>IEnumerable</c> of <c>PgpSecretKey</c> objects.</returns> - public IEnumerable GetSecretKeys() + /// <summary>Allows enumeration of the secret keys.</summary> + /// <returns>An <c>IEnumerable</c> of <c>PgpSecretKey</c> objects.</returns> + 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; + } + + /// <summary> + /// 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. + /// </summary> + /// <returns>An <c>IEnumerable</c> of unattached, or extra, public keys.</returns> + public IEnumerable GetExtraPublicKeys() + { + return new EnumerableProxy(extraPubKeys); } - /// <summary> - /// 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. - /// </summary> - /// <returns>An <c>IEnumerable</c> of unattached, or extra, public keys.</returns> - 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); + } } - /// <summary> - /// Replace the public key set on the secret ring with the corresponding key off the public ring. - /// </summary> - /// <param name="secretRing">Secret ring to be changed.</param> - /// <param name="publicRing">Public ring containing the new public key set.</param> - public static PgpSecretKeyRing ReplacePublicKeys( - PgpSecretKeyRing secretRing, - PgpPublicKeyRing publicRing) - { + /// <summary> + /// Replace the public key set on the secret ring with the corresponding key off the public ring. + /// </summary> + /// <param name="secretRing">Secret ring to be changed.</param> + /// <param name="publicRing">Public ring containing the new public key set.</param> + 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); - } - - /// <summary> - /// 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. - /// </summary> - /// <param name="ring">The <c>PgpSecretKeyRing</c> to be copied.</param> - /// <param name="oldPassPhrase">The current password for 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 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); + } + + /// <summary> + /// 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. + /// </summary> + /// <param name="ring">The <c>PgpSecretKeyRing</c> to be copied.</param> + /// <param name="oldPassPhrase">The current password for 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 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); - } - - /// <summary> - /// Returns a new key ring with the secret key passed in either added or - /// replacing an existing one with the same key ID. - /// </summary> - /// <param name="secRing">The secret key ring to be modified.</param> - /// <param name="secKey">The secret key to be inserted.</param> - /// <returns>A new <c>PgpSecretKeyRing</c></returns> - 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); + } + + /// <summary> + /// Returns a new key ring with the secret key passed in either added or + /// replacing an existing one with the same key ID. + /// </summary> + /// <param name="secRing">The secret key ring to be modified.</param> + /// <param name="secKey">The secret key to be inserted.</param> + /// <returns>A new <c>PgpSecretKeyRing</c></returns> + 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); + } - /// <summary>Returns a new key ring with the secret key passed in removed from the key ring.</summary> - /// <param name="secRing">The secret key ring to be modified.</param> - /// <param name="secKey">The secret key to be removed.</param> - /// <returns>A new <c>PgpSecretKeyRing</c>, or null if secKey is not found.</returns> + /// <summary>Returns a new key ring with the secret key passed in removed from the key ring.</summary> + /// <param name="secRing">The secret key ring to be modified.</param> + /// <param name="secKey">The secret key to be removed.</param> + /// <returns>A new <c>PgpSecretKeyRing</c>, or null if secKey is not found.</returns> 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; + } } } diff --git a/crypto/test/src/openpgp/test/PGPNoPrivateKeyTest.cs b/crypto/test/src/openpgp/test/PGPNoPrivateKeyTest.cs new file mode 100644 index 000000000..edb96b149 --- /dev/null +++ b/crypto/test/src/openpgp/test/PGPNoPrivateKeyTest.cs @@ -0,0 +1,169 @@ +using System; +using System.IO; + +using NUnit.Framework; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.Test; + +namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests +{ + [TestFixture] + public class PGPNoPrivateKeyTest + : SimpleTest + { + private static string pgpOldPass = "test"; + private static string pgpNewPass = "newtest"; + + private static readonly byte[] pgpPrivateEmpty = Base64.Decode( + "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" + + "+b3JLdM="); + + private static readonly byte[] pgpPrivateFull = Base64.Decode( + "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"); + + public override void PerformTest() + { + PgpSecretKeyRing pgpSecRing = new PgpSecretKeyRing(pgpPrivateFull); + PgpSecretKey pgpSecKey = pgpSecRing.GetSecretKey(); + bool isFullEmpty = pgpSecKey.IsPrivateKeyEmpty; + + pgpSecRing = new PgpSecretKeyRing(pgpPrivateEmpty); + pgpSecKey = pgpSecRing.GetSecretKey(); + bool isEmptyEmpty = pgpSecKey.IsPrivateKeyEmpty; + + // + // Check isPrivateKeyEmpty() is public + // + + if (isFullEmpty || !isEmptyEmpty) + { + Fail("Empty private keys not detected correctly."); + } + + // + // Check copyWithNewPassword doesn't throw an exception for secret + // keys without private keys (PGPException: unknown S2K type: 101). + // + + SecureRandom rand = new SecureRandom(); + + try + { + PgpSecretKey pgpChangedKey = PgpSecretKey.CopyWithNewPassword(pgpSecKey, + pgpOldPass.ToCharArray(), pgpNewPass.ToCharArray(), pgpSecKey.KeyEncryptionAlgorithm, rand); + } + catch (PgpException e) + { + if (!e.Message.Equals("no private key in this SecretKey - public key present only.")) + { + Fail("wrong exception."); + } + } + } + + public override string Name + { + get { return "PGPNoPrivateKeyTest"; } + } + + public static void Main( + string[] args) + { + RunTest(new PGPNoPrivateKeyTest()); + } + + [Test] + public void TestFunction() + { + string resultText = Perform().ToString(); + + Assert.AreEqual(Name + ": Okay", resultText); + } + } +} |