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(
+ "lQCVBFGSNGwBBACwABZRIEW/4vDQajcO0FW39yNDcsHBDwPkGT95D7jiVTTRoSs6"
+ + "ACWRAAwGlz4V62U0+nEgasxpifHnu6jati5zxwS16qNvBcxcqZrdZWdvolzCWWsr"
+ + "pFd0juhwesrvvUb5dN/xCJKyLPkp6A+uwv35/cxVSOHFvbW7nnronwinYQARAQAB"
+ + "/gJlAkdOVQG0HlRlc3QgVGVzdGVyc29uIDx0ZXN0QHRlc3QubmV0Poi4BBMBAgAi"
+ + "BQJRkjRsAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDSr6Hh9tuk5NfI"
+ + "A/4iMPF9k2/7KanWksNrBqhKemsyI7hLTxAwv+AA9B0rOO2QoJYe9OjuKn199fNO"
+ + "JPsAgwy7okvDe3QAUz3WA9GlghM5STYvonFJtl7o4kyjcZ4HO2ZI5Bdc5O9i63QA"
+ + "rNv40qVp++A3Mf+13z7cftKufj0vOfw6YeayLVXcV4h95J0B/gRRkjSNAQQA2l3d"
+ + "ZnFFYXYDoNHz1cOX4787CbKdBIfiALFfdbyQ6TzYkCTJTnVCZlQs2aeyrcdTSZUx"
+ + "N4y9bih4nfJ8uRKyQvLm6O0u6bG16kUDDnnwlsGn3uvTXfUwnSPq8pFY2acde6ZG"
+ + "N25vezNK1R6C7kU3+puNHqBIRANfHTsBElaD2V0AEQEAAf4CAwIUI0+QlwBVFdNa"
+ + "S/ppOwSht7Gr19AK4SHe92VWDKnCBPN2W3vhM4NcZSQCV2oiEMI0akLZ26jqCiRl"
+ + "AvTjLSVDho1rUWbaSxFfKlDQNbxCJKlMQeVfbsWXJMeDkn1AhPru3PBLl6Y1jocd"
+ + "vIVM7aQugNQlwEuFWgtZeODxcgBfX2lQeEMIv0AtWTAMt6MVT8AgnFqiqC4+14t0"
+ + "j2CHP2hqCDr5zw9gerAYQ0F03OS34vDm4Y5DmQFjyB05QO2cIN4DZ9gJg8NAQT+P"
+ + "+bwWR3/i9pTq3InNkoi2uT41OnHsYWgKoEQn62BDxjbvO359crUiq9VvS52v2UXh"
+ + "b6Z+fF3PoXXsobS1QQwTPXAeA/mlAflTp+HrkckatY7DgWbON1SSn4Z1XcWPKBSY"
+ + "epS5+90Tj3byZvN7Laj61ZlXVBvU3x7z6MaBZDf4479fklcUnJ13v+P6uGnTI4YE"
+ + "Q5pPjHn1dDqD2Nl8ZW9ufK9pPYkBPQQYAQIACQUCUZI0jQIbAgCoCRDSr6Hh9tuk"
+ + "5J0gBBkBAgAGBQJRkjSNAAoJEPIU7wJ5Ws2K0F0D/jHx4jrZq7SCv69/4hictjgz"
+ + "nNNFSOm20/brHXMBdp6p9mBqt28WU8fgRkxS0mz+1i7VNTv6ZwUXawfTyOVCPR5B"
+ + "QEC+FA+LvdX0UcJBJpa9tT4koz1JBxmppxxLYdS2A5sslPD5If8QHUaOMEX9O1I+"
+ + "So3rEh3+DuhQj88FUuG8uJAD/3Xtpf/5nEpghLOZdQ/7QkLCoRZk7fwjChQNFSJU"
+ + "5xiZbZ/GsSvU1IqAP/NZBmBO0qDm5m7ahXy71O1bMFtaiUaw2Mb7dwqqDvppbjIB"
+ + "OHdIhSnAorRLcnjm8z51QVMzHmgvKt5/e1q1fzsVzza6DWtYr2X/1VsuouSC1uz1"
+ + "nPdgnQH+BFGSNJ4BBAC3KliQlchs0rctsXbhA/GEfiO0s9tAgVsfJL1PWUkC+26M"
+ + "yBbqkVg5RV+J6dyTSeT6cDI8PMu8XFPO6H2WWdovfs7X9K1lxfnNWxQB2L6t2xre"
+ + "XyFqvTsYEFuGvYmbNyUYvA+daHD0xqX8UrC0J6TYg5ie5I685X8gFKVEtGYG/wAR"
+ + "AQAB/gIDAuMt34hcdJPX03uBj9LtjcnrMNLyF7PVJv4wBXEt7T9Kp8cYZ80Sxpd2"
+ + "11LHzjgiPg1kkkImJ9Ie1qbPZjc9tyiGf47m0TIORnKtwNb2YN+sKLpqZ+ienfTs"
+ + "vc0uyuVGW+8PCt409M9R++0q66sxvb3oKBp2zsr3BbGaISs4OVxY2L8uU3t5j9pi"
+ + "qKdV2XTiV9OZJ+2f1au1tMwhNPzjVJ4GH53TxewSkshRJTZtw2ouUJkdA/bizfNO"
+ + "9XYYvV8sW1/ASe1dnOs+ANDGzumzSA00dWPSveURroG+ZtVXVgkakJJtDwdAYutP"
+ + "kSm28cnsl1OmrBKPonB5N3uDjTlq56vji1d2F5ugAXTTD5PptiML1wEB/TqsRJRX"
+ + "uY7DLy+8iukOVOyoVw63UMX27YUz61JJZYcB7U28gNeRyBsnTEbjmvteoFsYnaGg"
+ + "Owgc+1Zx4rQdZEqxZRmfwmiUgHGyI9OpvoVaTIuDIqDd2ZRWiJ8EGAECAAkFAlGS"
+ + "NJ4CGwwACgkQ0q+h4fbbpOScsgQAmMymSfAmltnHQzKr5k2GvlAqIzl9MqKVm9wA"
+ + "0Cx3grwzPaiqmfspPIueQ8Phexiy6dwfPrwNoKnJOEjM6/sOcWEmLiIoYi+/oQjU"
+ + "12zwogOfzT/1hPpG5zs+GBGX4sorCK663PuovwCEoNrWm+7nItfTwdnFavNuj7s4"
+ + "+b3JLdM=");
+
+ private static readonly byte[] pgpPrivateFull = Base64.Decode(
+ "lQH+BFGSNGwBBACwABZRIEW/4vDQajcO0FW39yNDcsHBDwPkGT95D7jiVTTRoSs6"
+ + "ACWRAAwGlz4V62U0+nEgasxpifHnu6jati5zxwS16qNvBcxcqZrdZWdvolzCWWsr"
+ + "pFd0juhwesrvvUb5dN/xCJKyLPkp6A+uwv35/cxVSOHFvbW7nnronwinYQARAQAB"
+ + "/gIDAuqTuDp/Chfq0TKnSxmm2ZpDuiHD+NFVnCyNuJpvCQk0PnVwmGMH4xvsAZB2"
+ + "TOrfh2XHf/n9J4vjxB6p6Zs1kGBgg9hcHoWf+oEf1Tz/PE/c1tUXG2Hz9wlAgstU"
+ + "my2NpDTYUjQs45p+LaM+WFtLNXzBeqELKlMevs8Xb7n+VHwiTuM3KfXETLCoLz0Q"
+ + "3GmmpOuNnvXBdza7RsDwke0r66HzwX4Le8cMH9Pe7kSMakx9S1UR/uIsxsZYZOKb"
+ + "BieGEumxiAnew0Ri5/8wTd5yYC7BWbYvBUgdMQ1gzkzmJcVky8NVfoZKQ0GkdvMo"
+ + "fMThIVXN1U6+aqzAuUMFCPYQ7fEpfoNLhCnzQPv3RE7Wo2vFMjWBod2J4MSLhBuq"
+ + "Ut+FYLqYqU21Qe4PEyPmGnkVu7Wd8FGjBF+IKZg+ycPi++h/twloD/h7LEaq907C"
+ + "4R3rdOzjZnefDfxVWjLLhqKSSuXxtjSSKwMNdbjYVVJ/tB5UZXN0IFRlc3RlcnNv"
+ + "biA8dGVzdEB0ZXN0Lm5ldD6IuAQTAQIAIgUCUZI0bAIbAwYLCQgHAwIGFQgCCQoL"
+ + "BBYCAwECHgECF4AACgkQ0q+h4fbbpOTXyAP+IjDxfZNv+ymp1pLDawaoSnprMiO4"
+ + "S08QML/gAPQdKzjtkKCWHvTo7ip9ffXzTiT7AIMMu6JLw3t0AFM91gPRpYITOUk2"
+ + "L6JxSbZe6OJMo3GeBztmSOQXXOTvYut0AKzb+NKlafvgNzH/td8+3H7Srn49Lzn8"
+ + "OmHmsi1V3FeIfeSdAf4EUZI0jQEEANpd3WZxRWF2A6DR89XDl+O/OwmynQSH4gCx"
+ + "X3W8kOk82JAkyU51QmZULNmnsq3HU0mVMTeMvW4oeJ3yfLkSskLy5ujtLumxtepF"
+ + "Aw558JbBp97r0131MJ0j6vKRWNmnHXumRjdub3szStUegu5FN/qbjR6gSEQDXx07"
+ + "ARJWg9ldABEBAAH+AgMCFCNPkJcAVRXTWkv6aTsEobexq9fQCuEh3vdlVgypwgTz"
+ + "dlt74TODXGUkAldqIhDCNGpC2duo6gokZQL04y0lQ4aNa1Fm2ksRXypQ0DW8QiSp"
+ + "TEHlX27FlyTHg5J9QIT67tzwS5emNY6HHbyFTO2kLoDUJcBLhVoLWXjg8XIAX19p"
+ + "UHhDCL9ALVkwDLejFU/AIJxaoqguPteLdI9ghz9oagg6+c8PYHqwGENBdNzkt+Lw"
+ + "5uGOQ5kBY8gdOUDtnCDeA2fYCYPDQEE/j/m8Fkd/4vaU6tyJzZKItrk+NTpx7GFo"
+ + "CqBEJ+tgQ8Y27zt+fXK1IqvVb0udr9lF4W+mfnxdz6F17KG0tUEMEz1wHgP5pQH5"
+ + "U6fh65HJGrWOw4FmzjdUkp+GdV3FjygUmHqUufvdE4928mbzey2o+tWZV1Qb1N8e"
+ + "8+jGgWQ3+OO/X5JXFJydd7/j+rhp0yOGBEOaT4x59XQ6g9jZfGVvbnyvaT2JAT0E"
+ + "GAECAAkFAlGSNI0CGwIAqAkQ0q+h4fbbpOSdIAQZAQIABgUCUZI0jQAKCRDyFO8C"
+ + "eVrNitBdA/4x8eI62au0gr+vf+IYnLY4M5zTRUjpttP26x1zAXaeqfZgardvFlPH"
+ + "4EZMUtJs/tYu1TU7+mcFF2sH08jlQj0eQUBAvhQPi73V9FHCQSaWvbU+JKM9SQcZ"
+ + "qaccS2HUtgObLJTw+SH/EB1GjjBF/TtSPkqN6xId/g7oUI/PBVLhvLiQA/917aX/"
+ + "+ZxKYISzmXUP+0JCwqEWZO38IwoUDRUiVOcYmW2fxrEr1NSKgD/zWQZgTtKg5uZu"
+ + "2oV8u9TtWzBbWolGsNjG+3cKqg76aW4yATh3SIUpwKK0S3J45vM+dUFTMx5oLyre"
+ + "f3tatX87Fc82ug1rWK9l/9VbLqLkgtbs9Zz3YJ0B/gRRkjSeAQQAtypYkJXIbNK3"
+ + "LbF24QPxhH4jtLPbQIFbHyS9T1lJAvtujMgW6pFYOUVfienck0nk+nAyPDzLvFxT"
+ + "zuh9llnaL37O1/StZcX5zVsUAdi+rdsa3l8har07GBBbhr2JmzclGLwPnWhw9Mal"
+ + "/FKwtCek2IOYnuSOvOV/IBSlRLRmBv8AEQEAAf4CAwLjLd+IXHST19N7gY/S7Y3J"
+ + "6zDS8hez1Sb+MAVxLe0/SqfHGGfNEsaXdtdSx844Ij4NZJJCJifSHtamz2Y3Pbco"
+ + "hn+O5tEyDkZyrcDW9mDfrCi6amfonp307L3NLsrlRlvvDwreNPTPUfvtKuurMb29"
+ + "6Cgads7K9wWxmiErODlcWNi/LlN7eY/aYqinVdl04lfTmSftn9WrtbTMITT841Se"
+ + "Bh+d08XsEpLIUSU2bcNqLlCZHQP24s3zTvV2GL1fLFtfwEntXZzrPgDQxs7ps0gN"
+ + "NHVj0r3lEa6BvmbVV1YJGpCSbQ8HQGLrT5EptvHJ7JdTpqwSj6JweTd7g405auer"
+ + "44tXdheboAF00w+T6bYjC9cBAf06rESUV7mOwy8vvIrpDlTsqFcOt1DF9u2FM+tS"
+ + "SWWHAe1NvIDXkcgbJ0xG45r7XqBbGJ2hoDsIHPtWceK0HWRKsWUZn8JolIBxsiPT"
+ + "qb6FWkyLgyKg3dmUVoifBBgBAgAJBQJRkjSeAhsMAAoJENKvoeH226TknLIEAJjM"
+ + "pknwJpbZx0Myq+ZNhr5QKiM5fTKilZvcANAsd4K8Mz2oqpn7KTyLnkPD4XsYsunc"
+ + "Hz68DaCpyThIzOv7DnFhJi4iKGIvv6EI1Nds8KIDn80/9YT6Ruc7PhgRl+LKKwiu"
+ + "utz7qL8AhKDa1pvu5yLX08HZxWrzbo+7OPm9yS3T");
+
+ 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);
+ }
+ }
+}
|