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;
}
}
}