using System; using System.Collections.Generic; using System.IO; using Org.BouncyCastle.Asn1.Cryptlib; using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.Gnu; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; using Org.BouncyCastle.Math.EC.Rfc7748; using Org.BouncyCastle.Math.EC.Rfc8032; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Bcpg.OpenPgp { /// General class to handle a PGP public key object. public class PgpPublicKey : PgpObject { // We default to these as they are specified as mandatory in RFC 6631. private static readonly PgpKdfParameters DefaultKdfParameters = new PgpKdfParameters(HashAlgorithmTag.Sha256, SymmetricKeyAlgorithmTag.Aes128); public static byte[] CalculateFingerprint(PublicKeyPacket publicPk) { IBcpgKey key = publicPk.Key; IDigest digest; if (publicPk.Version <= 3) { RsaPublicBcpgKey rK = (RsaPublicBcpgKey)key; try { digest = PgpUtilities.CreateDigest(HashAlgorithmTag.MD5); UpdateDigest(digest, rK.Modulus); UpdateDigest(digest, rK.PublicExponent); } catch (Exception e) { throw new PgpException("can't encode key components: " + e.Message, e); } } else { try { byte[] kBytes = publicPk.GetEncodedContents(); digest = PgpUtilities.CreateDigest(HashAlgorithmTag.Sha1); digest.Update(0x99); digest.Update((byte)(kBytes.Length >> 8)); digest.Update((byte)kBytes.Length); digest.BlockUpdate(kBytes, 0, kBytes.Length); } catch (Exception e) { throw new PgpException("can't encode key components: " + e.Message, e); } } return DigestUtilities.DoFinal(digest); } private static void UpdateDigest(IDigest d, BigInteger b) { byte[] bytes = b.ToByteArrayUnsigned(); d.BlockUpdate(bytes, 0, bytes.Length); } private static readonly int[] MasterKeyCertificationTypes = new int[] { PgpSignature.PositiveCertification, PgpSignature.CasualCertification, PgpSignature.NoCertification, PgpSignature.DefaultCertification, PgpSignature.DirectKey, }; internal PublicKeyPacket publicPk; internal TrustPacket trustPk; internal IList keySigs = new List(); internal IList ids = new List(); internal IList idTrusts = new List(); internal IList> idSigs = new List>(); internal IList subSigs = null; private long keyId; private byte[] fingerprint; private int keyStrength; private void Init() { IBcpgKey key = publicPk.Key; this.fingerprint = CalculateFingerprint(publicPk); if (publicPk.Version <= 3) { RsaPublicBcpgKey rK = (RsaPublicBcpgKey) key; this.keyId = rK.Modulus.LongValue; this.keyStrength = rK.Modulus.BitLength; } else { this.keyId = (long)Pack.BE_To_UInt64(fingerprint, fingerprint.Length - 8); if (key is RsaPublicBcpgKey) { this.keyStrength = ((RsaPublicBcpgKey)key).Modulus.BitLength; } else if (key is DsaPublicBcpgKey) { this.keyStrength = ((DsaPublicBcpgKey)key).P.BitLength; } else if (key is ElGamalPublicBcpgKey) { this.keyStrength = ((ElGamalPublicBcpgKey)key).P.BitLength; } else if (key is EdDsaPublicBcpgKey eddsaK) { var curveOid = eddsaK.CurveOid; if (EdECObjectIdentifiers.id_Ed25519.Equals(curveOid) || GnuObjectIdentifiers.Ed25519.Equals(curveOid) || EdECObjectIdentifiers.id_X25519.Equals(curveOid) || CryptlibObjectIdentifiers.curvey25519.Equals(curveOid)) { this.keyStrength = 256; } else if (EdECObjectIdentifiers.id_Ed448.Equals(curveOid) || EdECObjectIdentifiers.id_X448.Equals(curveOid)) { this.keyStrength = 448; } else { this.keyStrength = -1; // unknown } } else if (key is ECPublicBcpgKey ecK) { var curveOid = ecK.CurveOid; X9ECParametersHolder ecParameters = ECKeyPairGenerator.FindECCurveByOidLazy(curveOid); if (ecParameters != null) { this.keyStrength = ecParameters.Curve.FieldSize; } else { this.keyStrength = -1; // unknown } } } } /// /// Create a PgpPublicKey from the passed in lightweight one. /// /// /// Note: the time passed in affects the value of the key's keyId, so you probably only want /// to do this once for a lightweight key, or make sure you keep track of the time you used. /// /// Asymmetric algorithm type representing the public key. /// Actual public key to associate. /// Date of creation. /// If pubKey is not public. /// On key creation problem. public PgpPublicKey(PublicKeyAlgorithmTag algorithm, AsymmetricKeyParameter pubKey, DateTime time) { if (pubKey.IsPrivate) throw new ArgumentException("Expected a public key", nameof(pubKey)); IBcpgKey bcpgKey; if (pubKey is RsaKeyParameters rK) { bcpgKey = new RsaPublicBcpgKey(rK.Modulus, rK.Exponent); } else if (pubKey is DsaPublicKeyParameters dK) { DsaParameters dP = dK.Parameters; bcpgKey = new DsaPublicBcpgKey(dP.P, dP.Q, dP.G, dK.Y); } else if (pubKey is ElGamalPublicKeyParameters eK) { ElGamalParameters eS = eK.Parameters; bcpgKey = new ElGamalPublicBcpgKey(eS.P, eS.G, eK.Y); } else if (pubKey is ECPublicKeyParameters ecK) { if (algorithm == PublicKeyAlgorithmTag.ECDH) { bcpgKey = new ECDHPublicBcpgKey(ecK.PublicKeyParamSet, ecK.Q, HashAlgorithmTag.Sha256, SymmetricKeyAlgorithmTag.Aes128); } else if (algorithm == PublicKeyAlgorithmTag.ECDsa) { bcpgKey = new ECDsaPublicBcpgKey(ecK.PublicKeyParamSet, ecK.Q); } else { throw new PgpException("unknown EC algorithm"); } } else if (pubKey is Ed25519PublicKeyParameters ed25519PubKey) { byte[] pointEnc = new byte[1 + Ed25519PublicKeyParameters.KeySize]; pointEnc[0] = 0x40; ed25519PubKey.Encode(pointEnc, 1); bcpgKey = new EdDsaPublicBcpgKey(GnuObjectIdentifiers.Ed25519, new BigInteger(1, pointEnc)); } else if (pubKey is Ed448PublicKeyParameters ed448PubKey) { byte[] pointEnc = new byte[Ed448PublicKeyParameters.KeySize]; ed448PubKey.Encode(pointEnc, 0); bcpgKey = new EdDsaPublicBcpgKey(EdECObjectIdentifiers.id_Ed448, new BigInteger(1, pointEnc)); } else if (pubKey is X25519PublicKeyParameters x25519PubKey) { byte[] pointEnc = new byte[1 + X25519PublicKeyParameters.KeySize]; pointEnc[0] = 0x40; x25519PubKey.Encode(pointEnc, 1); PgpKdfParameters kdfParams = DefaultKdfParameters; bcpgKey = new ECDHPublicBcpgKey(CryptlibObjectIdentifiers.curvey25519, new BigInteger(1, pointEnc), kdfParams.HashAlgorithm, kdfParams.SymmetricWrapAlgorithm); } else if (pubKey is X448PublicKeyParameters x448PubKey) { byte[] pointEnc = new byte[X448PublicKeyParameters.KeySize]; x448PubKey.Encode(pointEnc, 0); PgpKdfParameters kdfParams = DefaultKdfParameters; bcpgKey = new ECDHPublicBcpgKey(EdECObjectIdentifiers.id_X448, new BigInteger(1, pointEnc), kdfParams.HashAlgorithm, kdfParams.SymmetricWrapAlgorithm); } else { throw new PgpException("unknown key class"); } this.publicPk = new PublicKeyPacket(algorithm, time, bcpgKey); this.ids = new List(); this.idSigs = new List>(); try { Init(); } catch (IOException e) { throw new PgpException("exception calculating keyId", e); } } public PgpPublicKey(PublicKeyPacket publicPk) : this(publicPk, new List(), new List>()) { } /// Constructor for a sub-key. internal PgpPublicKey(PublicKeyPacket publicPk, TrustPacket trustPk, IList sigs) { this.publicPk = publicPk; this.trustPk = trustPk; this.subSigs = sigs; Init(); } internal PgpPublicKey( PgpPublicKey key, TrustPacket trust, IList subSigs) { this.publicPk = key.publicPk; this.trustPk = trust; this.subSigs = subSigs; this.fingerprint = key.fingerprint; this.keyId = key.keyId; this.keyStrength = key.keyStrength; } /// Copy constructor. /// The public key to copy. internal PgpPublicKey( PgpPublicKey pubKey) { this.publicPk = pubKey.publicPk; this.keySigs = new List(pubKey.keySigs); this.ids = new List(pubKey.ids); this.idTrusts = new List(pubKey.idTrusts); this.idSigs = new List>(pubKey.idSigs.Count); for (int i = 0; i < pubKey.idSigs.Count; ++i) { this.idSigs.Add(new List(pubKey.idSigs[i])); } if (pubKey.subSigs != null) { this.subSigs = new List(pubKey.subSigs); } this.fingerprint = pubKey.fingerprint; this.keyId = pubKey.keyId; this.keyStrength = pubKey.keyStrength; } internal PgpPublicKey( PublicKeyPacket publicPk, TrustPacket trustPk, IList keySigs, IList ids, IList idTrusts, IList> idSigs) { this.publicPk = publicPk; this.trustPk = trustPk; this.keySigs = keySigs; this.ids = ids; this.idTrusts = idTrusts; this.idSigs = idSigs; Init(); } internal PgpPublicKey( PublicKeyPacket publicPk, IList ids, IList> idSigs) { this.publicPk = publicPk; this.ids = ids; this.idSigs = idSigs; Init(); } internal PgpPublicKey( PgpPublicKey original, TrustPacket trustPk, List keySigs, List ids, List idTrusts, IList> idSigs) { this.publicPk = original.publicPk; this.fingerprint = original.fingerprint; this.keyStrength = original.keyStrength; this.keyId = original.keyId; this.trustPk = trustPk; this.keySigs = keySigs; this.ids = ids; this.idTrusts = idTrusts; this.idSigs = idSigs; } /// The version of this key. public int Version { get { return publicPk.Version; } } /// The creation time of this key. public DateTime CreationTime { get { return publicPk.GetTime(); } } /// Return the trust data associated with the public key, if present. /// A byte array with trust data, null otherwise. public byte[] GetTrustData() { if (trustPk == null) { return null; } return Arrays.Clone(trustPk.GetLevelAndTrustAmount()); } /// The number of valid seconds from creation time - zero means no expiry. public long GetValidSeconds() { if (publicPk.Version <= 3) { return (long)publicPk.ValidDays * (24 * 60 * 60); } if (IsMasterKey) { for (int i = 0; i != MasterKeyCertificationTypes.Length; i++) { long seconds = GetExpirationTimeFromSig(true, MasterKeyCertificationTypes[i]); if (seconds >= 0) { return seconds; } } } else { long seconds = GetExpirationTimeFromSig(false, PgpSignature.SubkeyBinding); if (seconds >= 0) { return seconds; } seconds = GetExpirationTimeFromSig(false, PgpSignature.DirectKey); if (seconds >= 0) { return seconds; } } return 0; } private long GetExpirationTimeFromSig(bool selfSigned, int signatureType) { long expiryTime = -1; long lastDate = -1; foreach (PgpSignature sig in GetSignaturesOfType(signatureType)) { if (selfSigned && sig.KeyId != this.KeyId) continue; PgpSignatureSubpacketVector hashed = sig.GetHashedSubPackets(); if (hashed == null) continue; if (!hashed.HasSubpacket(SignatureSubpacketTag.KeyExpireTime)) continue; long current = hashed.GetKeyExpirationTime(); if (sig.KeyId == this.KeyId) { if (sig.CreationTime.Ticks > lastDate) { lastDate = sig.CreationTime.Ticks; expiryTime = current; } } else if (current == 0 || current > expiryTime) { expiryTime = current; } } return expiryTime; } /// The key ID associated with the public key. public long KeyId { get { return keyId; } } /// The fingerprint of the public key public byte[] GetFingerprint() { return (byte[]) fingerprint.Clone(); } public bool HasFingerprint(byte[] fingerprint) { return Arrays.AreEqual(this.fingerprint, fingerprint); } /// /// Check if this key has an algorithm type that makes it suitable to use for encryption. /// /// /// Note: with version 4 keys KeyFlags subpackets should also be considered when present for /// determining the preferred use of the key. /// /// /// true if this key algorithm is suitable for encryption. /// public bool IsEncryptionKey { get { switch (publicPk.Algorithm) { case PublicKeyAlgorithmTag.ECDH: case PublicKeyAlgorithmTag.ElGamalEncrypt: case PublicKeyAlgorithmTag.ElGamalGeneral: case PublicKeyAlgorithmTag.RsaEncrypt: case PublicKeyAlgorithmTag.RsaGeneral: return true; default: return false; } } } /// True, if this could be a master key. public bool IsMasterKey { get { // this might seem a bit excessive, but we're also trying to flag something can't be a master key. return !(publicPk is PublicSubkeyPacket) && !(this.IsEncryptionKey && publicPk.Algorithm != PublicKeyAlgorithmTag.RsaGeneral); } } /// The algorithm code associated with the public key. public PublicKeyAlgorithmTag Algorithm { get { return publicPk.Algorithm; } } /// The strength of the key in bits. public int BitStrength { get { return keyStrength; } } /// The public key contained in the object. /// A lightweight public key. /// If the key algorithm is not recognised. public AsymmetricKeyParameter GetKey() { try { switch (publicPk.Algorithm) { case PublicKeyAlgorithmTag.RsaEncrypt: case PublicKeyAlgorithmTag.RsaGeneral: case PublicKeyAlgorithmTag.RsaSign: RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey)publicPk.Key; return new RsaKeyParameters(false, rsaK.Modulus, rsaK.PublicExponent); case PublicKeyAlgorithmTag.Dsa: DsaPublicBcpgKey dsaK = (DsaPublicBcpgKey)publicPk.Key; return new DsaPublicKeyParameters(dsaK.Y, new DsaParameters(dsaK.P, dsaK.Q, dsaK.G)); case PublicKeyAlgorithmTag.ECDsa: ECDsaPublicBcpgKey ecdsaK = (ECDsaPublicBcpgKey)publicPk.Key; return GetECKey("ECDSA", ecdsaK); case PublicKeyAlgorithmTag.ECDH: { ECDHPublicBcpgKey ecdhK = (ECDHPublicBcpgKey)publicPk.Key; var curveOid = ecdhK.CurveOid; if (EdECObjectIdentifiers.id_X25519.Equals(curveOid) || CryptlibObjectIdentifiers.curvey25519.Equals(curveOid)) { byte[] pEnc = BigIntegers.AsUnsignedByteArray(1 + X25519.PointSize, ecdhK.EncodedPoint); if (pEnc[0] != 0x40) throw new ArgumentException("Invalid X25519 public key"); return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo( new AlgorithmIdentifier(curveOid), #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER pEnc.AsSpan(1))); #else Arrays.CopyOfRange(pEnc, 1, pEnc.Length))); #endif } else if (EdECObjectIdentifiers.id_X448.Equals(curveOid)) { byte[] pEnc = BigIntegers.AsUnsignedByteArray(1 + X448.PointSize, ecdhK.EncodedPoint); if (pEnc[0] != 0x40) throw new ArgumentException("Invalid X448 public key"); return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo( new AlgorithmIdentifier(curveOid), #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER pEnc.AsSpan(1))); #else Arrays.CopyOfRange(pEnc, 1, pEnc.Length))); #endif } else { return GetECKey("ECDH", ecdhK); } } case PublicKeyAlgorithmTag.EdDsa_Legacy: { EdDsaPublicBcpgKey eddsaK = (EdDsaPublicBcpgKey)publicPk.Key; var curveOid = eddsaK.CurveOid; if (EdECObjectIdentifiers.id_Ed25519.Equals(curveOid) || GnuObjectIdentifiers.Ed25519.Equals(curveOid)) { byte[] pEnc = BigIntegers.AsUnsignedByteArray(1 + Ed25519.PublicKeySize, eddsaK.EncodedPoint); if (pEnc[0] != 0x40) throw new ArgumentException("Invalid Ed25519 public key"); return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo( new AlgorithmIdentifier(curveOid), #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER pEnc.AsSpan(1))); #else Arrays.CopyOfRange(pEnc, 1, pEnc.Length))); #endif } else if (EdECObjectIdentifiers.id_Ed448.Equals(curveOid)) { byte[] pEnc = BigIntegers.AsUnsignedByteArray(1 + Ed448.PublicKeySize, eddsaK.EncodedPoint); if (pEnc[0] != 0x40) throw new ArgumentException("Invalid Ed448 public key"); return PublicKeyFactory.CreateKey(new SubjectPublicKeyInfo( new AlgorithmIdentifier(curveOid), #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER pEnc.AsSpan(1))); #else Arrays.CopyOfRange(pEnc, 1, pEnc.Length))); #endif } else { throw new InvalidOperationException(); } } case PublicKeyAlgorithmTag.ElGamalEncrypt: case PublicKeyAlgorithmTag.ElGamalGeneral: ElGamalPublicBcpgKey elK = (ElGamalPublicBcpgKey)publicPk.Key; return new ElGamalPublicKeyParameters(elK.Y, new ElGamalParameters(elK.P, elK.G)); default: throw new PgpException("unknown public key algorithm encountered"); } } catch (PgpException) { throw; } catch (Exception e) { throw new PgpException("exception constructing public key", e); } } private ECPublicKeyParameters GetECKey(string algorithm, ECPublicBcpgKey ecK) { X9ECParameters x9 = ECKeyPairGenerator.FindECCurveByOid(ecK.CurveOid); BigInteger encodedPoint = ecK.EncodedPoint; #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER int encodedLength = BigIntegers.GetUnsignedByteLength(encodedPoint); Span encoding = encodedLength <= 512 ? stackalloc byte[encodedLength] : new byte[encodedLength]; BigIntegers.AsUnsignedByteArray(encodedPoint, encoding); ECPoint q = x9.Curve.DecodePoint(encoding); #else ECPoint q = x9.Curve.DecodePoint(BigIntegers.AsUnsignedByteArray(encodedPoint)); #endif return new ECPublicKeyParameters(algorithm, q, ecK.CurveOid); } /// Allows enumeration of any user IDs associated with the key. /// An IEnumerable of string objects. public IEnumerable GetUserIds() { var result = new List(); foreach (var id in ids) { if (id is UserIdPacket userId) { result.Add(userId.GetId()); } } return CollectionUtilities.Proxy(result); } /// Return any userIDs associated with the key in raw byte form. /// No attempt is made to convert the IDs into strings. /// An IEnumerable of byte[]. public IEnumerable GetRawUserIds() { var result = new List(); foreach (var id in ids) { if (id is UserIdPacket userId) { result.Add(userId.GetRawId()); } } return CollectionUtilities.Proxy(result); } /// Allows enumeration of any user attribute vectors associated with the key. /// An IEnumerable of PgpUserAttributeSubpacketVector objects. public IEnumerable GetUserAttributes() { var result = new List(); foreach (var id in ids) { if (id is PgpUserAttributeSubpacketVector v) { result.Add(v); } } return CollectionUtilities.Proxy(result); } /// Allows enumeration of any signatures associated with the passed in id. /// The ID to be matched. /// An IEnumerable of PgpSignature objects. public IEnumerable GetSignaturesForId(string id) { if (id == null) throw new ArgumentNullException(nameof(id)); return GetSignaturesForId(new UserIdPacket(id)); } public IEnumerable GetSignaturesForId(byte[] rawId) { if (rawId == null) throw new ArgumentNullException(nameof(rawId)); return GetSignaturesForId(new UserIdPacket(rawId)); } private IEnumerable GetSignaturesForId(UserIdPacket id) { var signatures = new List(); bool userIdFound = false; for (int i = 0; i != ids.Count; i++) { if (id.Equals(ids[i])) { userIdFound = true; signatures.AddRange(idSigs[i]); } } return userIdFound ? signatures : null; } /// Return any signatures associated with the passed in key identifier keyID. /// the key id to be matched. /// An IEnumerable of PgpSignature objects issued by the key with keyID. public IEnumerable GetSignaturesForKeyID(long keyID) { var sigs = new List(); foreach (var sig in GetSignatures()) { if (sig.KeyId == keyID) { sigs.Add(sig); } } return CollectionUtilities.Proxy(sigs); } /// Allows enumeration of signatures associated with the passed in user attributes. /// The vector of user attributes to be matched. /// An IEnumerable of PgpSignature objects. public IEnumerable GetSignaturesForUserAttribute(PgpUserAttributeSubpacketVector userAttributes) { if (userAttributes == null) throw new ArgumentNullException(nameof(userAttributes)); var result = new List(); bool attributeFound = false; for (int i = 0; i != ids.Count; i++) { if (userAttributes.Equals(ids[i])) { attributeFound = true; result.AddRange(idSigs[i]); } } return attributeFound ? CollectionUtilities.Proxy(result) : null; } /// Allows enumeration of signatures of the passed in type that are on this key. /// The type of the signature to be returned. /// An IEnumerable of PgpSignature objects. public IEnumerable GetSignaturesOfType(int signatureType) { var result = new List(); foreach (PgpSignature sig in GetSignatures()) { if (sig.SignatureType == signatureType) { result.Add(sig); } } return CollectionUtilities.Proxy(result); } /// Allows enumeration of all signatures/certifications associated with this key. /// An IEnumerable with all signatures/certifications. public IEnumerable GetSignatures() { var result = subSigs; if (result == null) { var temp = new List(keySigs); foreach (var extraSigs in idSigs) { temp.AddRange(extraSigs); } result = temp; } return CollectionUtilities.Proxy(result); } /** * Return all signatures/certifications directly associated with this key (ie, not to a user id). * * @return an iterator (possibly empty) with all signatures/certifications. */ public IEnumerable GetKeySignatures() { var result = subSigs ?? new List(keySigs); return CollectionUtilities.Proxy(result); } public PublicKeyPacket PublicKeyPacket { get { return publicPk; } } public byte[] GetEncoded() { MemoryStream bOut = new MemoryStream(); Encode(bOut); return bOut.ToArray(); } public void Encode(Stream outStr) { Encode(outStr, false); } /** * Encode the key to outStream, with trust packets stripped out if forTransfer is true. * * @param outStream stream to write the key encoding to. * @param forTransfer if the purpose of encoding is to send key to other users. * @throws IOException in case of encoding error. */ public void Encode(Stream outStr, bool forTransfer) { BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr); bcpgOut.WritePacket(publicPk); if (!forTransfer && trustPk != null) { bcpgOut.WritePacket(trustPk); } if (subSigs == null) // not a sub-key { foreach (PgpSignature keySig in keySigs) { keySig.Encode(bcpgOut); } for (int i = 0; i != ids.Count; i++) { if (ids[i] is UserIdPacket id) { bcpgOut.WritePacket(id); } else { PgpUserAttributeSubpacketVector v = (PgpUserAttributeSubpacketVector)ids[i]; bcpgOut.WritePacket(new UserAttributePacket(v.ToSubpacketArray())); } if (!forTransfer && idTrusts[i] != null) { bcpgOut.WritePacket((TrustPacket)idTrusts[i]); } foreach (PgpSignature sig in idSigs[i]) { sig.Encode(bcpgOut, forTransfer); } } } else { foreach (PgpSignature subSig in subSigs) { subSig.Encode(bcpgOut); } } } /// Check whether this (sub)key has a revocation signature on it. /// True, if this (sub)key has been revoked. public bool IsRevoked() { int ns = 0; bool revoked = false; if (IsMasterKey) // Master key { while (!revoked && (ns < keySigs.Count)) { if (((PgpSignature)keySigs[ns++]).SignatureType == PgpSignature.KeyRevocation) { revoked = true; } } } else // Sub-key { while (!revoked && (ns < subSigs.Count)) { if (((PgpSignature)subSigs[ns++]).SignatureType == PgpSignature.SubkeyRevocation) { revoked = true; } } } return revoked; } /// Add a certification for an id to the given public key. /// The key the certification is to be added to. /// The ID the certification is associated with. /// The new certification. /// The re-certified key. public static PgpPublicKey AddCertification( PgpPublicKey key, string id, PgpSignature certification) { return AddCert(key, new UserIdPacket(id), certification); } /// Add a certification for the given UserAttributeSubpackets to the given public key. /// The key the certification is to be added to. /// The attributes the certification is associated with. /// The new certification. /// The re-certified key. public static PgpPublicKey AddCertification( PgpPublicKey key, PgpUserAttributeSubpacketVector userAttributes, PgpSignature certification) { return AddCert(key, userAttributes, certification); } private static PgpPublicKey AddCert( PgpPublicKey key, IUserDataPacket id, PgpSignature certification) { PgpPublicKey returnKey = new PgpPublicKey(key); IList sigList = null; for (int i = 0; i != returnKey.ids.Count; i++) { if (id.Equals(returnKey.ids[i])) { sigList = returnKey.idSigs[i]; } } if (sigList != null) { sigList.Add(certification); } else { sigList = new List(); sigList.Add(certification); returnKey.ids.Add(id); returnKey.idTrusts.Add(null); returnKey.idSigs.Add(sigList); } return returnKey; } /// /// Remove any certifications associated with a user attribute subpacket on a key. /// /// The key the certifications are to be removed from. /// The attributes to be removed. /// /// The re-certified key, or null if the user attribute subpacket was not found on the key. /// public static PgpPublicKey RemoveCertification(PgpPublicKey key, PgpUserAttributeSubpacketVector userAttributes) { return RemoveCert(key, userAttributes); } /// Remove any certifications associated with a given ID on a key. /// The key the certifications are to be removed from. /// The ID that is to be removed. /// The re-certified key, or null if the ID was not found on the key. public static PgpPublicKey RemoveCertification(PgpPublicKey key, string id) { return RemoveCert(key, new UserIdPacket(id)); } /// Remove any certifications associated with a given ID on a key. /// The key the certifications are to be removed from. /// The ID that is to be removed in raw byte form. /// The re-certified key, or null if the ID was not found on the key. public static PgpPublicKey RemoveCertification(PgpPublicKey key, byte[] rawId) { return RemoveCert(key, new UserIdPacket(rawId)); } private static PgpPublicKey RemoveCert(PgpPublicKey key, IUserDataPacket id) { PgpPublicKey returnKey = new PgpPublicKey(key); bool found = false; for (int i = 0; i < returnKey.ids.Count; i++) { if (id.Equals(returnKey.ids[i])) { found = true; returnKey.ids.RemoveAt(i); returnKey.idTrusts.RemoveAt(i); returnKey.idSigs.RemoveAt(i); } } return found ? returnKey : null; } /// Remove a certification associated with a given ID on a key. /// The key the certifications are to be removed from. /// The ID that the certfication is to be removed from (in its raw byte form). /// The certfication to be removed. /// The re-certified key, or null if the certification was not found. public static PgpPublicKey RemoveCertification(PgpPublicKey key, byte[] id, PgpSignature certification) { return RemoveCert(key, new UserIdPacket(id), certification); } /// Remove a certification associated with a given ID on a key. /// The key the certifications are to be removed from. /// The ID that the certfication is to be removed from. /// The certfication to be removed. /// The re-certified key, or null if the certification was not found. public static PgpPublicKey RemoveCertification(PgpPublicKey key, string id, PgpSignature certification) { return RemoveCert(key, new UserIdPacket(id), certification); } /// Remove a certification associated with a given user attributes on a key. /// The key the certifications are to be removed from. /// The user attributes that the certfication is to be removed from. /// The certification to be removed. /// The re-certified key, or null if the certification was not found. public static PgpPublicKey RemoveCertification(PgpPublicKey key, PgpUserAttributeSubpacketVector userAttributes, PgpSignature certification) { return RemoveCert(key, userAttributes, certification); } private static PgpPublicKey RemoveCert(PgpPublicKey key, IUserDataPacket id, PgpSignature certification) { PgpPublicKey returnKey = new PgpPublicKey(key); bool found = false; for (int i = 0; i < returnKey.ids.Count; i++) { if (id.Equals(returnKey.ids[i])) { found |= returnKey.idSigs[i].Remove(certification); } } return found ? returnKey : null; } /// Add a revocation or some other key certification to a key. /// The key the revocation is to be added to. /// The key signature to be added. /// The new changed public key object. public static PgpPublicKey AddCertification(PgpPublicKey key, PgpSignature certification) { if (key.IsMasterKey) { if (certification.SignatureType == PgpSignature.SubkeyRevocation) throw new ArgumentException("signature type incorrect for master key revocation."); } else { if (certification.SignatureType == PgpSignature.KeyRevocation) throw new ArgumentException("signature type incorrect for sub-key revocation."); } PgpPublicKey returnKey = new PgpPublicKey(key); var sigs = returnKey.subSigs ?? returnKey.keySigs; sigs.Add(certification); return returnKey; } /// Remove a certification from the key. /// The key the certifications are to be removed from. /// The certfication to be removed. /// The modified key, null if the certification was not found. public static PgpPublicKey RemoveCertification(PgpPublicKey key, PgpSignature certification) { var returnKey = new PgpPublicKey(key); var sigs = returnKey.subSigs ?? returnKey.keySigs; bool found = sigs.Remove(certification); foreach (var idSigs in returnKey.idSigs) { found |= idSigs.Remove(certification); } return found ? returnKey : null; } /// /// Merge the given local public key with another, potentially fresher copy. The resulting public key /// contains the sum of both keys' user-ids and signatures. /// /// /// If joinTrustPackets is set to true and the copy carries a trust packet, the joined key will copy the /// trust-packet from the copy. Otherwise, it will carry the trust packet of the local key. /// /// local public key. /// copy of the public key (e.g. from a key server). /// if true, trust packets from the copy are copied over into the resulting key. /// /// if true, subkey signatures on the copy will be present in the /// merged key, even if key was not a subkey before. /// joined key. public static PgpPublicKey Join(PgpPublicKey key, PgpPublicKey copy, bool joinTrustPackets, bool allowSubkeySigsOnNonSubkey) { if (key.KeyId != copy.keyId) throw new ArgumentException("Key-ID mismatch."); TrustPacket trustPk = key.trustPk; List keySigs = new List(key.keySigs); List ids = new List(key.ids); List idTrusts = new List(key.idTrusts); List> idSigs = new List>(key.idSigs); List subSigs = key.subSigs == null ? null : new List(key.subSigs); if (joinTrustPackets) { if (copy.trustPk != null) { trustPk = copy.trustPk; } } // key signatures foreach (PgpSignature keySig in copy.keySigs) { bool found = false; for (int i = 0; i < keySigs.Count; i++) { PgpSignature existingKeySig = keySigs[i]; if (PgpSignature.IsSignatureEncodingEqual(existingKeySig, keySig)) { found = true; // join existing sig with copy to apply modifications in unhashed subpackets existingKeySig = PgpSignature.Join(existingKeySig, keySig); keySigs[i] = existingKeySig; break; } } if (found) break; keySigs.Add(keySig); } // user-ids and id sigs for (int idIdx = 0; idIdx < copy.ids.Count; idIdx++) { IUserDataPacket copyId = copy.ids[idIdx]; List copyIdSigs = new List(copy.idSigs[idIdx]); TrustPacket copyTrust = copy.idTrusts[idIdx]; int existingIdIndex = -1; for (int i = 0; i < ids.Count; i++) { IUserDataPacket existingId = ids[i]; if (existingId.Equals(copyId)) { existingIdIndex = i; break; } } // new user-id if (existingIdIndex == -1) { ids.Add(copyId); idSigs.Add(copyIdSigs); idTrusts.Add(joinTrustPackets ? copyTrust : null); continue; } // existing user-id if (joinTrustPackets && copyTrust != null) { TrustPacket existingTrust = idTrusts[existingIdIndex]; if (existingTrust == null || Arrays.AreEqual(copyTrust.GetLevelAndTrustAmount(), existingTrust.GetLevelAndTrustAmount())) { idTrusts[existingIdIndex] = copyTrust; } } var existingIdSigs = idSigs[existingIdIndex]; foreach (PgpSignature newSig in copyIdSigs) { bool found = false; for (int i = 0; i < existingIdSigs.Count; i++) { PgpSignature existingSig = existingIdSigs[i]; if (PgpSignature.IsSignatureEncodingEqual(newSig, existingSig)) { found = true; // join existing sig with copy to apply modifications in unhashed subpackets existingSig = PgpSignature.Join(existingSig, newSig); existingIdSigs[i] = existingSig; break; } } if (!found) { existingIdSigs.Add(newSig); } } } // subSigs if (copy.subSigs != null) { if (subSigs == null && allowSubkeySigsOnNonSubkey) { subSigs = new List(copy.subSigs); } else { foreach (PgpSignature copySubSig in copy.subSigs) { bool found = false; for (int i = 0; subSigs != null && i < subSigs.Count; i++) { PgpSignature existingSubSig = subSigs[i]; if (PgpSignature.IsSignatureEncodingEqual(existingSubSig, copySubSig)) { found = true; // join existing sig with copy to apply modifications in unhashed subpackets existingSubSig = PgpSignature.Join(existingSubSig, copySubSig); subSigs[i] = existingSubSig; break; } } if (!found && subSigs != null) { subSigs.Add(copySubSig); } } } } PgpPublicKey merged = new PgpPublicKey(key, trustPk, keySigs, ids, idTrusts, idSigs); merged.subSigs = subSigs; return merged; } } }