summary refs log tree commit diff
path: root/crypto/src/openpgp/PgpSecretKeyRing.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/openpgp/PgpSecretKeyRing.cs')
-rw-r--r--crypto/src/openpgp/PgpSecretKeyRing.cs301
1 files changed, 301 insertions, 0 deletions
diff --git a/crypto/src/openpgp/PgpSecretKeyRing.cs b/crypto/src/openpgp/PgpSecretKeyRing.cs
new file mode 100644
index 000000000..3e646eaa1
--- /dev/null
+++ b/crypto/src/openpgp/PgpSecretKeyRing.cs
@@ -0,0 +1,301 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+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
+    {
+        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(
+            byte[] encoding)
+            : this(new MemoryStream(encoding))
+        {
+        }
+
+		public PgpSecretKeyRing(
+            Stream inputStream)
+        {
+			this.keys = Platform.CreateArrayList();
+            this.extraPubKeys = Platform.CreateArrayList();
+
+			BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream);
+
+			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"));
+            }
+
+			SecretKeyPacket secret = (SecretKeyPacket) bcpgInput.ReadPacket();
+
+			//
+            // ignore GPG comment packets if found.
+            //
+            while (bcpgInput.NextPacketTag() == PacketTag.Experimental2)
+            {
+                bcpgInput.ReadPacket();
+            }
+
+			TrustPacket trust = ReadOptionalTrustPacket(bcpgInput);
+
+			// revocation and direct signatures
+			IList keySigs = ReadSignaturesAndTrust(bcpgInput);
+
+			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)));
+
+
+			// 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));
+				}
+            }
+        }
+
+		/// <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>
+        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()
+        {
+            return new EnumerableProxy(keys);
+        }
+
+        public PgpSecretKey GetSecretKey(
+            long keyId)
+        {
+			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);
+		}
+
+		public byte[] GetEncoded()
+        {
+            MemoryStream bOut = new MemoryStream();
+
+            Encode(bOut);
+
+            return bOut.ToArray();
+        }
+
+        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);
+			}
+        }
+
+		/// <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)
+		{
+            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(
+            PgpSecretKeyRing  secRing,
+            PgpSecretKey      secKey)
+        {
+            IList keys = Platform.CreateArrayList(secRing.keys);
+            bool found = false;
+			bool masterFound = false;
+
+			for (int i = 0; i != keys.Count; i++)
+            {
+                PgpSecretKey key = (PgpSecretKey) keys[i];
+
+				if (key.KeyId == secKey.KeyId)
+                {
+                    found = true;
+                    keys[i] = secKey;
+                }
+				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);
+				}
+            }
+
+			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>
+        public static PgpSecretKeyRing RemoveSecretKey(
+            PgpSecretKeyRing  secRing,
+            PgpSecretKey      secKey)
+        {
+            IList keys = Platform.CreateArrayList(secRing.keys);
+            bool found = false;
+
+			for (int i = 0; i < keys.Count; i++)
+            {
+                PgpSecretKey key = (PgpSecretKey)keys[i];
+
+				if (key.KeyId == secKey.KeyId)
+                {
+                    found = true;
+                    keys.RemoveAt(i);
+                }
+            }
+
+			return found ? new PgpSecretKeyRing(keys, secRing.extraPubKeys) : null;
+        }
+    }
+}