diff options
Diffstat (limited to 'crypto/src/openpgp/PgpSecretKeyRing.cs')
-rw-r--r-- | crypto/src/openpgp/PgpSecretKeyRing.cs | 301 |
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; + } + } +} |