Initial Commit

This commit is contained in:
2023-06-21 12:46:23 -04:00
commit c70248a520
1352 changed files with 336780 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
public interface IStreamGenerator
{
void Close();
}
}

View File

@@ -0,0 +1,77 @@
using System.Collections;
using System.IO;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
public abstract class PgpKeyRing
: PgpObject
{
internal PgpKeyRing()
{
}
internal static TrustPacket ReadOptionalTrustPacket(
BcpgInputStream bcpgInput)
{
return (bcpgInput.NextPacketTag() == PacketTag.Trust)
? (TrustPacket) bcpgInput.ReadPacket()
: null;
}
internal static ArrayList ReadSignaturesAndTrust(
BcpgInputStream bcpgInput)
{
try
{
ArrayList sigList = new ArrayList();
while (bcpgInput.NextPacketTag() == PacketTag.Signature)
{
SignaturePacket signaturePacket = (SignaturePacket) bcpgInput.ReadPacket();
TrustPacket trustPacket = ReadOptionalTrustPacket(bcpgInput);
sigList.Add(new PgpSignature(signaturePacket, trustPacket));
}
return sigList;
}
catch (PgpException e)
{
throw new IOException("can't create signature object: " + e.Message, e);
}
}
internal static void ReadUserIDs(
BcpgInputStream bcpgInput,
out ArrayList ids,
out ArrayList idTrusts,
out ArrayList idSigs)
{
ids = new ArrayList();
idTrusts = new ArrayList();
idSigs = new ArrayList();
while (bcpgInput.NextPacketTag() == PacketTag.UserId
|| bcpgInput.NextPacketTag() == PacketTag.UserAttribute)
{
Packet obj = bcpgInput.ReadPacket();
if (obj is UserIdPacket)
{
UserIdPacket id = (UserIdPacket)obj;
ids.Add(id.GetId());
}
else
{
UserAttributePacket user = (UserAttributePacket) obj;
ids.Add(new PgpUserAttributeSubpacketVector(user.GetSubpackets()));
}
idTrusts.Add(
ReadOptionalTrustPacket(bcpgInput));
idSigs.Add(
ReadSignaturesAndTrust(bcpgInput));
}
}
}
}

View File

@@ -0,0 +1,9 @@
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
public abstract class PgpObject
{
internal PgpObject()
{
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections;
using Org.BouncyCastle.Bcpg.Attr;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
public class PgpUserAttributeSubpacketVectorGenerator
{
private ArrayList list = new ArrayList();
public virtual void SetImageAttribute(
ImageAttrib.Format imageType,
byte[] imageData)
{
if (imageData == null)
throw new ArgumentException("attempt to set null image", "imageData");
list.Add(new ImageAttrib(imageType, imageData));
}
public virtual PgpUserAttributeSubpacketVector Generate()
{
return new PgpUserAttributeSubpacketVector(
(UserAttributeSubpacket[]) list.ToArray(typeof(UserAttributeSubpacket)));
}
}
}

View File

@@ -0,0 +1,50 @@
using System.IO;
using Org.BouncyCastle.Apache.Bzip2;
using Org.BouncyCastle.Utilities.Zlib;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>Compressed data objects</remarks>
public class PgpCompressedData
: PgpObject
{
private readonly CompressedDataPacket data;
public PgpCompressedData(
BcpgInputStream bcpgInput)
{
data = (CompressedDataPacket) bcpgInput.ReadPacket();
}
/// <summary>The algorithm used for compression</summary>
public CompressionAlgorithmTag Algorithm
{
get { return data.Algorithm; }
}
/// <summary>Get the raw input stream contained in the object.</summary>
public Stream GetInputStream()
{
return data.GetInputStream();
}
/// <summary>Return an uncompressed input stream which allows reading of the compressed data.</summary>
public Stream GetDataStream()
{
switch (Algorithm)
{
case CompressionAlgorithmTag.Uncompressed:
return GetInputStream();
case CompressionAlgorithmTag.Zip:
return new ZInflaterInputStream(GetInputStream(), true);
case CompressionAlgorithmTag.ZLib:
return new ZInflaterInputStream(GetInputStream());
case CompressionAlgorithmTag.BZip2:
return new CBZip2InputStream(GetInputStream());
default:
throw new PgpException("can't recognise compression algorithm: " + Algorithm);
}
}
}
}

View File

@@ -0,0 +1,177 @@
using System;
using System.IO;
using Org.BouncyCastle.Apache.Bzip2;
using Org.BouncyCastle.Utilities.Zlib;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>Class for producing compressed data packets.</remarks>
public class PgpCompressedDataGenerator
: IStreamGenerator
{
private readonly CompressionAlgorithmTag algorithm;
private readonly int compression;
private Stream dOut;
private BcpgOutputStream pkOut;
public PgpCompressedDataGenerator(
CompressionAlgorithmTag algorithm)
: this(algorithm, JZlib.Z_DEFAULT_COMPRESSION)
{
}
public PgpCompressedDataGenerator(
CompressionAlgorithmTag algorithm,
int compression)
{
switch (algorithm)
{
case CompressionAlgorithmTag.Uncompressed:
case CompressionAlgorithmTag.Zip:
case CompressionAlgorithmTag.ZLib:
case CompressionAlgorithmTag.BZip2:
break;
default:
throw new ArgumentException("unknown compression algorithm", "algorithm");
}
if (compression != JZlib.Z_DEFAULT_COMPRESSION)
{
if ((compression < JZlib.Z_NO_COMPRESSION) || (compression > JZlib.Z_BEST_COMPRESSION))
{
throw new ArgumentException("unknown compression level: " + compression);
}
}
this.algorithm = algorithm;
this.compression = compression;
}
/// <summary>
/// <p>
/// Return an output stream which will save the data being written to
/// the compressed object.
/// </p>
/// <p>
/// The stream created can be closed off by either calling Close()
/// on the stream or Close() on the generator. Closing the returned
/// stream does not close off the Stream parameter <c>outStr</c>.
/// </p>
/// </summary>
/// <param name="outStr">Stream to be used for output.</param>
/// <returns>A Stream for output of the compressed data.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidOperationException"></exception>
/// <exception cref="IOException"></exception>
public Stream Open(
Stream outStr)
{
if (dOut != null)
throw new InvalidOperationException("generator already in open state");
if (outStr == null)
throw new ArgumentNullException("outStr");
this.pkOut = new BcpgOutputStream(outStr, PacketTag.CompressedData);
doOpen();
return new WrappedGeneratorStream(this, dOut);
}
/// <summary>
/// <p>
/// Return an output stream which will compress the data as it is written to it.
/// The stream will be written out in chunks according to the size of the passed in buffer.
/// </p>
/// <p>
/// The stream created can be closed off by either calling Close()
/// on the stream or Close() on the generator. Closing the returned
/// stream does not close off the Stream parameter <c>outStr</c>.
/// </p>
/// <p>
/// <b>Note</b>: if the buffer is not a power of 2 in length only the largest power of 2
/// bytes worth of the buffer will be used.
/// </p>
/// <p>
/// <b>Note</b>: using this may break compatibility with RFC 1991 compliant tools.
/// Only recent OpenPGP implementations are capable of accepting these streams.
/// </p>
/// </summary>
/// <param name="outStr">Stream to be used for output.</param>
/// <param name="buffer">The buffer to use.</param>
/// <returns>A Stream for output of the compressed data.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidOperationException"></exception>
/// <exception cref="IOException"></exception>
/// <exception cref="PgpException"></exception>
public Stream Open(
Stream outStr,
byte[] buffer)
{
if (dOut != null)
throw new InvalidOperationException("generator already in open state");
if (outStr == null)
throw new ArgumentNullException("outStr");
if (buffer == null)
throw new ArgumentNullException("buffer");
this.pkOut = new BcpgOutputStream(outStr, PacketTag.CompressedData, buffer);
doOpen();
return new WrappedGeneratorStream(this, dOut);
}
private void doOpen()
{
pkOut.WriteByte((byte) algorithm);
switch (algorithm)
{
case CompressionAlgorithmTag.Uncompressed:
dOut = pkOut;
break;
case CompressionAlgorithmTag.Zip:
dOut = new ZDeflaterOutputStream(pkOut, compression, true);
break;
case CompressionAlgorithmTag.ZLib:
dOut = new ZDeflaterOutputStream(pkOut, compression, false);
break;
case CompressionAlgorithmTag.BZip2:
dOut = new CBZip2OutputStream(pkOut);
break;
default:
// Constructor should guard against this possibility
throw new InvalidOperationException();
}
}
/// <summary>Close the compressed object.</summary>summary>
public void Close()
{
if (dOut != null)
{
switch (algorithm)
{
case CompressionAlgorithmTag.BZip2:
((CBZip2OutputStream) dOut).Finish();
break;
case CompressionAlgorithmTag.Zip:
case CompressionAlgorithmTag.ZLib:
((ZDeflaterOutputStream) dOut).Finish();
break;
}
dOut.Flush();
pkOut.Finish();
pkOut.Flush();
dOut = null;
pkOut = null;
}
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>
/// Thrown if the IV at the start of a data stream indicates the wrong key is being used.
/// </remarks>
public class PgpDataValidationException
: PgpException
{
public PgpDataValidationException() : base() {}
public PgpDataValidationException(string message) : base(message) {}
public PgpDataValidationException(string message, Exception exception) : base(message, exception) {}
}
}

View File

@@ -0,0 +1,151 @@
using System;
using System.Diagnostics;
using System.IO;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.IO;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
public abstract class PgpEncryptedData
{
internal class TruncatedStream
: BaseInputStream
{
private const int LookAheadSize = 22;
private const int LookAheadBufSize = 512;
private const int LookAheadBufLimit = LookAheadBufSize - LookAheadSize;
private readonly Stream inStr;
private readonly byte[] lookAhead = new byte[LookAheadBufSize];
private int bufStart, bufEnd;
internal TruncatedStream(
Stream inStr)
{
int numRead = Streams.ReadFully(inStr, lookAhead, 0, lookAhead.Length);
if (numRead < LookAheadSize)
throw new EndOfStreamException();
this.inStr = inStr;
this.bufStart = 0;
this.bufEnd = numRead - LookAheadSize;
}
private int FillBuffer()
{
if (bufEnd < LookAheadBufLimit)
return 0;
Debug.Assert(bufStart == LookAheadBufLimit);
Debug.Assert(bufEnd == LookAheadBufLimit);
Array.Copy(lookAhead, LookAheadBufLimit, lookAhead, 0, LookAheadSize);
bufEnd = Streams.ReadFully(inStr, lookAhead, LookAheadSize, LookAheadBufLimit);
bufStart = 0;
return bufEnd;
}
public override int ReadByte()
{
if (bufStart < bufEnd)
return lookAhead[bufStart++];
if (FillBuffer() < 1)
return -1;
return lookAhead[bufStart++];
}
public override int Read(byte[] buf, int off, int len)
{
int avail = bufEnd - bufStart;
int pos = off;
while (len > avail)
{
Array.Copy(lookAhead, bufStart, buf, pos, avail);
bufStart += avail;
pos += avail;
len -= avail;
if ((avail = FillBuffer()) < 1)
return pos - off;
}
Array.Copy(lookAhead, bufStart, buf, pos, len);
bufStart += len;
return pos + len - off;;
}
internal byte[] GetLookAhead()
{
byte[] temp = new byte[LookAheadSize];
Array.Copy(lookAhead, bufStart, temp, 0, LookAheadSize);
return temp;
}
}
internal InputStreamPacket encData;
internal Stream encStream;
internal TruncatedStream truncStream;
internal PgpEncryptedData(
InputStreamPacket encData)
{
this.encData = encData;
}
/// <summary>Return the raw input stream for the data stream.</summary>
public virtual Stream GetInputStream()
{
return encData.GetInputStream();
}
/// <summary>Return true if the message is integrity protected.</summary>
/// <returns>True, if there is a modification detection code namespace associated
/// with this stream.</returns>
public bool IsIntegrityProtected()
{
return encData is SymmetricEncIntegrityPacket;
}
/// <summary>Note: This can only be called after the message has been read.</summary>
/// <returns>True, if the message verifies, false otherwise</returns>
public bool Verify()
{
if (!IsIntegrityProtected())
throw new PgpException("data not integrity protected.");
DigestStream dIn = (DigestStream) encStream;
//
// make sure we are at the end.
//
while (encStream.ReadByte() >= 0)
{
// do nothing
}
//
// process the MDC packet
//
byte[] lookAhead = truncStream.GetLookAhead();
IDigest hash = dIn.ReadDigest();
hash.BlockUpdate(lookAhead, 0, 2);
byte[] digest = DigestUtilities.DoFinal(hash);
byte[] streamDigest = new byte[digest.Length];
Array.Copy(lookAhead, 2, streamDigest, 0, streamDigest.Length);
return Arrays.AreEqual(digest, streamDigest);
}
}
}

View File

@@ -0,0 +1,495 @@
using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>Generator for encrypted objects.</remarks>
public class PgpEncryptedDataGenerator
: IStreamGenerator
{
private BcpgOutputStream pOut;
private CipherStream cOut;
private IBufferedCipher c;
private bool withIntegrityPacket;
private bool oldFormat;
private DigestStream digestOut;
private abstract class EncMethod
: ContainedPacket
{
protected byte[] sessionInfo;
protected SymmetricKeyAlgorithmTag encAlgorithm;
protected KeyParameter key;
public abstract void AddSessionInfo(byte[] si, SecureRandom random);
}
private class PbeMethod
: EncMethod
{
private S2k s2k;
internal PbeMethod(
SymmetricKeyAlgorithmTag encAlgorithm,
S2k s2k,
KeyParameter key)
{
this.encAlgorithm = encAlgorithm;
this.s2k = s2k;
this.key = key;
}
public KeyParameter GetKey()
{
return key;
}
public override void AddSessionInfo(
byte[] si,
SecureRandom random)
{
string cName = PgpUtilities.GetSymmetricCipherName(encAlgorithm);
IBufferedCipher c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding");
byte[] iv = new byte[c.GetBlockSize()];
c.Init(true, new ParametersWithRandom(new ParametersWithIV(key, iv), random));
this.sessionInfo = c.DoFinal(si, 0, si.Length - 2);
}
public override void Encode(BcpgOutputStream pOut)
{
SymmetricKeyEncSessionPacket pk = new SymmetricKeyEncSessionPacket(
encAlgorithm, s2k, sessionInfo);
pOut.WritePacket(pk);
}
}
private class PubMethod
: EncMethod
{
internal PgpPublicKey pubKey;
internal BigInteger[] data;
internal PubMethod(
PgpPublicKey pubKey)
{
this.pubKey = pubKey;
}
public override void AddSessionInfo(
byte[] si,
SecureRandom random)
{
IBufferedCipher c;
switch (pubKey.Algorithm)
{
case PublicKeyAlgorithmTag.RsaEncrypt:
case PublicKeyAlgorithmTag.RsaGeneral:
c = CipherUtilities.GetCipher("RSA//PKCS1Padding");
break;
case PublicKeyAlgorithmTag.ElGamalEncrypt:
case PublicKeyAlgorithmTag.ElGamalGeneral:
c = CipherUtilities.GetCipher("ElGamal/ECB/PKCS1Padding");
break;
case PublicKeyAlgorithmTag.Dsa:
throw new PgpException("Can't use DSA for encryption.");
case PublicKeyAlgorithmTag.ECDsa:
throw new PgpException("Can't use ECDSA for encryption.");
default:
throw new PgpException("unknown asymmetric algorithm: " + pubKey.Algorithm);
}
AsymmetricKeyParameter akp = pubKey.GetKey();
c.Init(true, new ParametersWithRandom(akp, random));
byte[] encKey = c.DoFinal(si);
switch (pubKey.Algorithm)
{
case PublicKeyAlgorithmTag.RsaEncrypt:
case PublicKeyAlgorithmTag.RsaGeneral:
data = new BigInteger[]{ new BigInteger(1, encKey) };
break;
case PublicKeyAlgorithmTag.ElGamalEncrypt:
case PublicKeyAlgorithmTag.ElGamalGeneral:
int halfLength = encKey.Length / 2;
data = new BigInteger[]
{
new BigInteger(1, encKey, 0, halfLength),
new BigInteger(1, encKey, halfLength, halfLength)
};
break;
default:
throw new PgpException("unknown asymmetric algorithm: " + encAlgorithm);
}
}
public override void Encode(BcpgOutputStream pOut)
{
PublicKeyEncSessionPacket pk = new PublicKeyEncSessionPacket(
pubKey.KeyId, pubKey.Algorithm, data);
pOut.WritePacket(pk);
}
}
private readonly ArrayList methods = new ArrayList();
private readonly SymmetricKeyAlgorithmTag defAlgorithm;
private readonly SecureRandom rand;
public PgpEncryptedDataGenerator(
SymmetricKeyAlgorithmTag encAlgorithm)
{
this.defAlgorithm = encAlgorithm;
this.rand = new SecureRandom();
}
public PgpEncryptedDataGenerator(
SymmetricKeyAlgorithmTag encAlgorithm,
bool withIntegrityPacket)
{
this.defAlgorithm = encAlgorithm;
this.withIntegrityPacket = withIntegrityPacket;
this.rand = new SecureRandom();
}
/// <summary>Existing SecureRandom constructor.</summary>
/// <param name="encAlgorithm">The symmetric algorithm to use.</param>
/// <param name="rand">Source of randomness.</param>
public PgpEncryptedDataGenerator(
SymmetricKeyAlgorithmTag encAlgorithm,
SecureRandom rand)
{
this.defAlgorithm = encAlgorithm;
this.rand = rand;
}
/// <summary>Creates a cipher stream which will have an integrity packet associated with it.</summary>
public PgpEncryptedDataGenerator(
SymmetricKeyAlgorithmTag encAlgorithm,
bool withIntegrityPacket,
SecureRandom rand)
{
this.defAlgorithm = encAlgorithm;
this.rand = rand;
this.withIntegrityPacket = withIntegrityPacket;
}
/// <summary>Base constructor.</summary>
/// <param name="encAlgorithm">The symmetric algorithm to use.</param>
/// <param name="rand">Source of randomness.</param>
/// <param name="oldFormat">PGP 2.6.x compatibility required.</param>
public PgpEncryptedDataGenerator(
SymmetricKeyAlgorithmTag encAlgorithm,
SecureRandom rand,
bool oldFormat)
{
this.defAlgorithm = encAlgorithm;
this.rand = rand;
this.oldFormat = oldFormat;
}
/// <summary>Add a PBE encryption method to the encrypted object.</summary>
public void AddMethod(
char[] passPhrase)
{
byte[] iv = new byte[8];
rand.NextBytes(iv);
S2k s2k = new S2k(HashAlgorithmTag.Sha1, iv, 0x60);
methods.Add(new PbeMethod(defAlgorithm, s2k, PgpUtilities.MakeKeyFromPassPhrase(defAlgorithm, s2k, passPhrase)));
}
/// <summary>Add a public key encrypted session key to the encrypted object.</summary>
public void AddMethod(
PgpPublicKey key)
{
if (!key.IsEncryptionKey)
{
throw new ArgumentException("passed in key not an encryption key!");
}
methods.Add(new PubMethod(key));
}
private void AddCheckSum(
byte[] sessionInfo)
{
Debug.Assert(sessionInfo != null);
Debug.Assert(sessionInfo.Length >= 3);
int check = 0;
for (int i = 1; i < sessionInfo.Length - 2; i++)
{
check += sessionInfo[i];
}
sessionInfo[sessionInfo.Length - 2] = (byte)(check >> 8);
sessionInfo[sessionInfo.Length - 1] = (byte)(check);
}
private byte[] CreateSessionInfo(
SymmetricKeyAlgorithmTag algorithm,
KeyParameter key)
{
byte[] keyBytes = key.GetKey();
byte[] sessionInfo = new byte[keyBytes.Length + 3];
sessionInfo[0] = (byte) algorithm;
keyBytes.CopyTo(sessionInfo, 1);
AddCheckSum(sessionInfo);
return sessionInfo;
}
/// <summary>
/// <p>
/// If buffer is non null stream assumed to be partial, otherwise the length will be used
/// to output a fixed length packet.
/// </p>
/// <p>
/// The stream created can be closed off by either calling Close()
/// on the stream or Close() on the generator. Closing the returned
/// stream does not close off the Stream parameter <c>outStr</c>.
/// </p>
/// </summary>
private Stream Open(
Stream outStr,
long length,
byte[] buffer)
{
if (cOut != null)
throw new InvalidOperationException("generator already in open state");
if (methods.Count == 0)
throw new InvalidOperationException("No encryption methods specified");
if (outStr == null)
throw new ArgumentNullException("outStr");
pOut = new BcpgOutputStream(outStr);
KeyParameter key;
if (methods.Count == 1)
{
if (methods[0] is PbeMethod)
{
PbeMethod m = (PbeMethod)methods[0];
key = m.GetKey();
}
else
{
key = PgpUtilities.MakeRandomKey(defAlgorithm, rand);
byte[] sessionInfo = CreateSessionInfo(defAlgorithm, key);
PubMethod m = (PubMethod)methods[0];
try
{
m.AddSessionInfo(sessionInfo, rand);
}
catch (Exception e)
{
throw new PgpException("exception encrypting session key", e);
}
}
pOut.WritePacket((ContainedPacket)methods[0]);
}
else // multiple methods
{
key = PgpUtilities.MakeRandomKey(defAlgorithm, rand);
byte[] sessionInfo = CreateSessionInfo(defAlgorithm, key);
for (int i = 0; i != methods.Count; i++)
{
EncMethod m = (EncMethod)methods[i];
try
{
m.AddSessionInfo(sessionInfo, rand);
}
catch (Exception e)
{
throw new PgpException("exception encrypting session key", e);
}
pOut.WritePacket(m);
}
}
string cName = PgpUtilities.GetSymmetricCipherName(defAlgorithm);
if (cName == null)
{
throw new PgpException("null cipher specified");
}
try
{
if (withIntegrityPacket)
{
cName += "/CFB/NoPadding";
}
else
{
cName += "/OpenPGPCFB/NoPadding";
}
c = CipherUtilities.GetCipher(cName);
// TODO Confirm the IV should be all zero bytes (not inLineIv - see below)
byte[] iv = new byte[c.GetBlockSize()];
c.Init(true, new ParametersWithRandom(new ParametersWithIV(key, iv), rand));
if (buffer == null)
{
//
// we have to Add block size + 2 for the Generated IV and + 1 + 22 if integrity protected
//
if (withIntegrityPacket)
{
pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricEncryptedIntegrityProtected, length + c.GetBlockSize() + 2 + 1 + 22);
pOut.WriteByte(1); // version number
}
else
{
pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricKeyEncrypted, length + c.GetBlockSize() + 2, oldFormat);
}
}
else
{
if (withIntegrityPacket)
{
pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricEncryptedIntegrityProtected, buffer);
pOut.WriteByte(1); // version number
}
else
{
pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricKeyEncrypted, buffer);
}
}
int blockSize = c.GetBlockSize();
byte[] inLineIv = new byte[blockSize + 2];
rand.NextBytes(inLineIv, 0, blockSize);
Array.Copy(inLineIv, inLineIv.Length - 4, inLineIv, inLineIv.Length - 2, 2);
Stream myOut = cOut = new CipherStream(pOut, null, c);
if (withIntegrityPacket)
{
string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1);
IDigest digest = DigestUtilities.GetDigest(digestName);
myOut = digestOut = new DigestStream(myOut, null, digest);
}
myOut.Write(inLineIv, 0, inLineIv.Length);
return new WrappedGeneratorStream(this, myOut);
}
catch (Exception e)
{
throw new PgpException("Exception creating cipher", e);
}
}
/// <summary>
/// <p>
/// Return an output stream which will encrypt the data as it is written to it.
/// </p>
/// <p>
/// The stream created can be closed off by either calling Close()
/// on the stream or Close() on the generator. Closing the returned
/// stream does not close off the Stream parameter <c>outStr</c>.
/// </p>
/// </summary>
public Stream Open(
Stream outStr,
long length)
{
return Open(outStr, length, null);
}
/// <summary>
/// <p>
/// Return an output stream which will encrypt the data as it is written to it.
/// The stream will be written out in chunks according to the size of the passed in buffer.
/// </p>
/// <p>
/// The stream created can be closed off by either calling Close()
/// on the stream or Close() on the generator. Closing the returned
/// stream does not close off the Stream parameter <c>outStr</c>.
/// </p>
/// <p>
/// <b>Note</b>: if the buffer is not a power of 2 in length only the largest power of 2
/// bytes worth of the buffer will be used.
/// </p>
/// </summary>
public Stream Open(
Stream outStr,
byte[] buffer)
{
return Open(outStr, 0, buffer);
}
/// <summary>
/// <p>
/// Close off the encrypted object - this is equivalent to calling Close() on the stream
/// returned by the Open() method.
/// </p>
/// <p>
/// <b>Note</b>: This does not close the underlying output stream, only the stream on top of
/// it created by the Open() method.
/// </p>
/// </summary>
public void Close()
{
if (cOut != null)
{
// TODO Should this all be under the try/catch block?
if (digestOut != null)
{
//
// hand code a mod detection packet
//
BcpgOutputStream bOut = new BcpgOutputStream(
digestOut, PacketTag.ModificationDetectionCode, 20);
bOut.Flush();
digestOut.Flush();
// TODO
byte[] dig = DigestUtilities.DoFinal(digestOut.WriteDigest());
cOut.Write(dig, 0, dig.Length);
}
cOut.Flush();
try
{
pOut.Write(c.DoFinal());
pOut.Finish();
}
catch (Exception e)
{
throw new IOException(e.Message, e);
}
cOut = null;
pOut = null;
}
}
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Collections;
using Org.BouncyCastle.Utilities.Collections;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>A holder for a list of PGP encryption method packets.</remarks>
public class PgpEncryptedDataList
: PgpObject
{
private ArrayList list = new ArrayList();
private InputStreamPacket data;
public PgpEncryptedDataList(
BcpgInputStream bcpgInput)
{
while (bcpgInput.NextPacketTag() == PacketTag.PublicKeyEncryptedSession
|| bcpgInput.NextPacketTag() == PacketTag.SymmetricKeyEncryptedSessionKey)
{
list.Add(bcpgInput.ReadPacket());
}
data = (InputStreamPacket)bcpgInput.ReadPacket();
for (int i = 0; i != list.Count; i++)
{
if (list[i] is SymmetricKeyEncSessionPacket)
{
list[i] = new PgpPbeEncryptedData((SymmetricKeyEncSessionPacket) list[i], data);
}
else
{
list[i] = new PgpPublicKeyEncryptedData((PublicKeyEncSessionPacket) list[i], data);
}
}
}
public PgpEncryptedData this[int index]
{
get { return (PgpEncryptedData) list[index]; }
}
[Obsolete("Use 'object[index]' syntax instead")]
public object Get(int index)
{
return this[index];
}
[Obsolete("Use 'Count' property instead")]
public int Size
{
get { return list.Count; }
}
public int Count
{
get { return list.Count; }
}
public bool IsEmpty
{
get { return list.Count == 0; }
}
public IEnumerable GetEncryptedDataObjects()
{
return new EnumerableProxy(list);
}
}
}

View File

@@ -0,0 +1,19 @@
using System;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>Generic exception class for PGP encoding/decoding problems.</remarks>
public class PgpException
: Exception
{
public PgpException() : base() {}
public PgpException(string message) : base(message) {}
public PgpException(string message, Exception exception) : base(message, exception) {}
[Obsolete("Use InnerException property")]
public Exception UnderlyingException
{
get { return InnerException; }
}
}
}

View File

@@ -0,0 +1,16 @@
using System;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
public class PgpExperimental
: PgpObject
{
private readonly ExperimentalPacket p;
public PgpExperimental(
BcpgInputStream bcpgIn)
{
p = (ExperimentalPacket) bcpgIn.ReadPacket();
}
}
}

View File

@@ -0,0 +1,13 @@
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>Key flag values for the KeyFlags subpacket.</remarks>
public abstract class PgpKeyFlags
{
public const int CanCertify = 0x01; // This key may be used to certify other keys.
public const int CanSign = 0x02; // This key may be used to sign data.
public const int CanEncryptCommunications = 0x04; // This key may be used to encrypt communications.
public const int CanEncryptStorage = 0x08; // This key may be used to encrypt storage.
public const int MaybeSplit = 0x10; // The private component of this key may have been split by a secret-sharing mechanism.
public const int MaybeShared = 0x80; // The private component of this key may be in the possession of more than one person.
}
}

View File

@@ -0,0 +1,67 @@
using System;
using Org.BouncyCastle.Crypto;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>
/// General class to handle JCA key pairs and convert them into OpenPGP ones.
/// <p>
/// A word for the unwary, the KeyId for an OpenPGP public key is calculated from
/// a hash that includes the time of creation, if you pass a different date to the
/// constructor below with the same public private key pair the KeyIs will not be the
/// same as for previous generations of the key, so ideally you only want to do
/// this once.
/// </p>
/// </remarks>
public class PgpKeyPair
{
private readonly PgpPublicKey pub;
private readonly PgpPrivateKey priv;
public PgpKeyPair(
PublicKeyAlgorithmTag algorithm,
AsymmetricCipherKeyPair keyPair,
DateTime time)
: this(algorithm, keyPair.Public, keyPair.Private, time)
{
}
public PgpKeyPair(
PublicKeyAlgorithmTag algorithm,
AsymmetricKeyParameter pubKey,
AsymmetricKeyParameter privKey,
DateTime time)
{
this.pub = new PgpPublicKey(algorithm, pubKey, time);
this.priv = new PgpPrivateKey(privKey, pub.KeyId);
}
/// <summary>Create a key pair from a PgpPrivateKey and a PgpPublicKey.</summary>
/// <param name="pub">The public key.</param>
/// <param name="priv">The private key.</param>
public PgpKeyPair(
PgpPublicKey pub,
PgpPrivateKey priv)
{
this.pub = pub;
this.priv = priv;
}
/// <summary>The keyId associated with this key pair.</summary>
public long KeyId
{
get { return pub.KeyId; }
}
public PgpPublicKey PublicKey
{
get { return pub; }
}
public PgpPrivateKey PrivateKey
{
get { return priv; }
}
}
}

View File

@@ -0,0 +1,166 @@
using System;
using System.Collections;
using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>
/// Generator for a PGP master and subkey ring.
/// This class will generate both the secret and public key rings
/// </remarks>
public class PgpKeyRingGenerator
{
private ArrayList keys = new ArrayList();
private string id;
private SymmetricKeyAlgorithmTag encAlgorithm;
private int certificationLevel;
private char[] passPhrase;
private bool useSha1;
private PgpKeyPair masterKey;
private PgpSignatureSubpacketVector hashedPacketVector;
private PgpSignatureSubpacketVector unhashedPacketVector;
private SecureRandom rand;
/// <summary>
/// Create a new key ring generator using old style checksumming. It is recommended to use
/// SHA1 checksumming where possible.
/// </summary>
/// <param name="certificationLevel">The certification level for keys on this ring.</param>
/// <param name="masterKey">The master key pair.</param>
/// <param name="id">The id to be associated with the ring.</param>
/// <param name="encAlgorithm">The algorithm to be used to protect secret keys.</param>
/// <param name="passPhrase">The passPhrase to be used to protect secret keys.</param>
/// <param name="hashedPackets">Packets to be included in the certification hash.</param>
/// <param name="unhashedPackets">Packets to be attached unhashed to the certification.</param>
/// <param name="rand">input secured random.</param>
public PgpKeyRingGenerator(
int certificationLevel,
PgpKeyPair masterKey,
string id,
SymmetricKeyAlgorithmTag encAlgorithm,
char[] passPhrase,
PgpSignatureSubpacketVector hashedPackets,
PgpSignatureSubpacketVector unhashedPackets,
SecureRandom rand)
: this(certificationLevel, masterKey, id, encAlgorithm, passPhrase, false, hashedPackets, unhashedPackets, rand)
{
}
/// <summary>
/// Create a new key ring generator.
/// </summary>
/// <param name="certificationLevel">The certification level for keys on this ring.</param>
/// <param name="masterKey">The master key pair.</param>
/// <param name="id">The id to be associated with the ring.</param>
/// <param name="encAlgorithm">The algorithm to be used to protect secret keys.</param>
/// <param name="passPhrase">The passPhrase to be used to protect secret keys.</param>
/// <param name="useSha1">Checksum the secret keys with SHA1 rather than the older 16 bit checksum.</param>
/// <param name="hashedPackets">Packets to be included in the certification hash.</param>
/// <param name="unhashedPackets">Packets to be attached unhashed to the certification.</param>
/// <param name="rand">input secured random.</param>
public PgpKeyRingGenerator(
int certificationLevel,
PgpKeyPair masterKey,
string id,
SymmetricKeyAlgorithmTag encAlgorithm,
char[] passPhrase,
bool useSha1,
PgpSignatureSubpacketVector hashedPackets,
PgpSignatureSubpacketVector unhashedPackets,
SecureRandom rand)
{
this.certificationLevel = certificationLevel;
this.masterKey = masterKey;
this.id = id;
this.encAlgorithm = encAlgorithm;
this.passPhrase = passPhrase;
this.useSha1 = useSha1;
this.hashedPacketVector = hashedPackets;
this.unhashedPacketVector = unhashedPackets;
this.rand = rand;
keys.Add(new PgpSecretKey(certificationLevel, masterKey, id, encAlgorithm, passPhrase, useSha1, hashedPackets, unhashedPackets, rand));
}
/// <summary>Add a subkey to the key ring to be generated with default certification.</summary>
public void AddSubKey(
PgpKeyPair keyPair)
{
AddSubKey(keyPair, this.hashedPacketVector, this.unhashedPacketVector);
}
/// <summary>
/// Add a subkey with specific hashed and unhashed packets associated with it and
/// default certification.
/// </summary>
/// <param name="keyPair">Public/private key pair.</param>
/// <param name="hashedPackets">Hashed packet values to be included in certification.</param>
/// <param name="unhashedPackets">Unhashed packets values to be included in certification.</param>
/// <exception cref="PgpException"></exception>
public void AddSubKey(
PgpKeyPair keyPair,
PgpSignatureSubpacketVector hashedPackets,
PgpSignatureSubpacketVector unhashedPackets)
{
try
{
PgpSignatureGenerator sGen = new PgpSignatureGenerator(
masterKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
//
// Generate the certification
//
sGen.InitSign(PgpSignature.SubkeyBinding, masterKey.PrivateKey);
sGen.SetHashedSubpackets(hashedPackets);
sGen.SetUnhashedSubpackets(unhashedPackets);
ArrayList subSigs = new ArrayList();
subSigs.Add(sGen.GenerateCertification(masterKey.PublicKey, keyPair.PublicKey));
keys.Add(new PgpSecretKey(keyPair, null, subSigs, encAlgorithm, passPhrase, useSha1, rand));
}
catch (PgpException e)
{
throw e;
}
catch (Exception e)
{
throw new PgpException("exception adding subkey: ", e);
}
}
/// <summary>Return the secret key ring.</summary>
public PgpSecretKeyRing GenerateSecretKeyRing()
{
return new PgpSecretKeyRing(keys);
}
/// <summary>Return the public key ring that corresponds to the secret key ring.</summary>
public PgpPublicKeyRing GeneratePublicKeyRing()
{
ArrayList pubKeys = new ArrayList();
IEnumerator enumerator = keys.GetEnumerator();
enumerator.MoveNext();
PgpSecretKey pgpSecretKey = (PgpSecretKey) enumerator.Current;
pubKeys.Add(pgpSecretKey.PublicKey);
while (enumerator.MoveNext())
{
pgpSecretKey = (PgpSecretKey) enumerator.Current;
PgpPublicKey k = new PgpPublicKey(pgpSecretKey.PublicKey);
k.publicPk = new PublicSubkeyPacket(
k.Algorithm, k.CreationTime, k.publicPk.Key);
pubKeys.Add(k);
}
return new PgpPublicKeyRing(pubKeys);
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>
/// Thrown if the key checksum is invalid.
/// </remarks>
public class PgpKeyValidationException
: PgpException
{
public PgpKeyValidationException() : base() {}
public PgpKeyValidationException(string message) : base(message) {}
public PgpKeyValidationException(string message, Exception exception) : base(message, exception) {}
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.IO;
using Org.BouncyCastle.Utilities.Date;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <summary>Class for processing literal data objects.</summary>
public class PgpLiteralData
: PgpObject
{
public const char Binary = 'b';
public const char Text = 't';
/// <summary>The special name indicating a "for your eyes only" packet.</summary>
public const string Console = "_CONSOLE";
private LiteralDataPacket data;
public PgpLiteralData(
BcpgInputStream bcpgInput)
{
data = (LiteralDataPacket) bcpgInput.ReadPacket();
}
/// <summary>The format of the data stream - Binary or Text</summary>
public int Format
{
get { return data.Format; }
}
/// <summary>The file name that's associated with the data stream.</summary>
public string FileName
{
get { return data.FileName; }
}
/// <summary>The modification time for the file.</summary>
public DateTime ModificationTime
{
get { return DateTimeUtilities.UnixMsToDateTime(data.ModificationTime); }
}
/// <summary>The raw input stream for the data stream.</summary>
public Stream GetInputStream()
{
return data.GetInputStream();
}
/// <summary>The input stream representing the data stream.</summary>
public Stream GetDataStream()
{
return GetInputStream();
}
}
}

View File

@@ -0,0 +1,177 @@
using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Date;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>Class for producing literal data packets.</remarks>
public class PgpLiteralDataGenerator
: IStreamGenerator
{
public const char Binary = PgpLiteralData.Binary;
public const char Text = PgpLiteralData.Text;
/// <summary>The special name indicating a "for your eyes only" packet.</summary>
public const string Console = PgpLiteralData.Console;
private BcpgOutputStream pkOut;
private bool oldFormat;
public PgpLiteralDataGenerator()
{
}
/// <summary>
/// Generates literal data objects in the old format.
/// This is important if you need compatibility with PGP 2.6.x.
/// </summary>
/// <param name="oldFormat">If true, uses old format.</param>
public PgpLiteralDataGenerator(
bool oldFormat)
{
this.oldFormat = oldFormat;
}
private void WriteHeader(
BcpgOutputStream outStr,
char format,
string name,
long modificationTime)
{
byte[] asciiName = Strings.ToByteArray(name);
outStr.Write(
(byte) format,
(byte) asciiName.Length);
outStr.Write(asciiName);
long modDate = modificationTime / 1000L;
outStr.Write(
(byte)(modDate >> 24),
(byte)(modDate >> 16),
(byte)(modDate >> 8),
(byte)modDate);
}
/// <summary>
/// <p>
/// Open a literal data packet, returning a stream to store the data inside the packet.
/// </p>
/// <p>
/// The stream created can be closed off by either calling Close()
/// on the stream or Close() on the generator. Closing the returned
/// stream does not close off the Stream parameter <c>outStr</c>.
/// </p>
/// </summary>
/// <param name="outStr">The stream we want the packet in.</param>
/// <param name="format">The format we are using.</param>
/// <param name="name">The name of the 'file'.</param>
/// <param name="length">The length of the data we will write.</param>
/// <param name="modificationTime">The time of last modification we want stored.</param>
public Stream Open(
Stream outStr,
char format,
string name,
long length,
DateTime modificationTime)
{
if (pkOut != null)
throw new InvalidOperationException("generator already in open state");
if (outStr == null)
throw new ArgumentNullException("outStr");
// Do this first, since it might throw an exception
long unixMs = DateTimeUtilities.DateTimeToUnixMs(modificationTime);
pkOut = new BcpgOutputStream(outStr, PacketTag.LiteralData,
length + 2 + name.Length + 4, oldFormat);
WriteHeader(pkOut, format, name, unixMs);
return new WrappedGeneratorStream(this, pkOut);
}
/// <summary>
/// <p>
/// Open a literal data packet, returning a stream to store the data inside the packet,
/// as an indefinite length stream. The stream is written out as a series of partial
/// packets with a chunk size determined by the size of the passed in buffer.
/// </p>
/// <p>
/// The stream created can be closed off by either calling Close()
/// on the stream or Close() on the generator. Closing the returned
/// stream does not close off the Stream parameter <c>outStr</c>.
/// </p>
/// <p>
/// <b>Note</b>: if the buffer is not a power of 2 in length only the largest power of 2
/// bytes worth of the buffer will be used.</p>
/// </summary>
/// <param name="outStr">The stream we want the packet in.</param>
/// <param name="format">The format we are using.</param>
/// <param name="name">The name of the 'file'.</param>
/// <param name="modificationTime">The time of last modification we want stored.</param>
/// <param name="buffer">The buffer to use for collecting data to put into chunks.</param>
public Stream Open(
Stream outStr,
char format,
string name,
DateTime modificationTime,
byte[] buffer)
{
if (pkOut != null)
throw new InvalidOperationException("generator already in open state");
if (outStr == null)
throw new ArgumentNullException("outStr");
// Do this first, since it might throw an exception
long unixMs = DateTimeUtilities.DateTimeToUnixMs(modificationTime);
pkOut = new BcpgOutputStream(outStr, PacketTag.LiteralData, buffer);
WriteHeader(pkOut, format, name, unixMs);
return new WrappedGeneratorStream(this, pkOut);
}
/// <summary>
/// <p>
/// Open a literal data packet for the passed in <c>FileInfo</c> object, returning
/// an output stream for saving the file contents.
/// </p>
/// <p>
/// The stream created can be closed off by either calling Close()
/// on the stream or Close() on the generator. Closing the returned
/// stream does not close off the Stream parameter <c>outStr</c>.
/// </p>
/// </summary>
/// <param name="outStr">The stream we want the packet in.</param>
/// <param name="format">The format we are using.</param>
/// <param name="file">The <c>FileInfo</c> object containg the packet details.</param>
public Stream Open(
Stream outStr,
char format,
FileInfo file)
{
return Open(outStr, format, file.Name, file.Length, file.LastWriteTime);
}
/// <summary>
/// Close the literal data packet - this is equivalent to calling Close()
/// on the stream returned by the Open() method.
/// </summary>
public void Close()
{
if (pkOut != null)
{
pkOut.Finish();
pkOut.Flush();
pkOut = null;
}
}
}
}

View File

@@ -0,0 +1,18 @@
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>
/// A PGP marker packet - in general these should be ignored other than where
/// the idea is to preserve the original input stream.
/// </remarks>
public class PgpMarker
: PgpObject
{
private readonly MarkerPacket p;
public PgpMarker(
BcpgInputStream bcpgIn)
{
p = (MarkerPacket) bcpgIn.ReadPacket();
}
}
}

View File

@@ -0,0 +1,130 @@
using System;
using System.Collections;
using System.IO;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>
/// General class for reading a PGP object stream.
/// <p>
/// Note: if this class finds a PgpPublicKey or a PgpSecretKey it
/// will create a PgpPublicKeyRing, or a PgpSecretKeyRing for each
/// key found. If all you are trying to do is read a key ring file use
/// either PgpPublicKeyRingBundle or PgpSecretKeyRingBundle.</p>
/// </remarks>
public class PgpObjectFactory
{
private readonly BcpgInputStream bcpgIn;
public PgpObjectFactory(
Stream inputStream)
{
this.bcpgIn = BcpgInputStream.Wrap(inputStream);
}
public PgpObjectFactory(
byte[] bytes)
: this(new MemoryStream(bytes, false))
{
}
/// <summary>Return the next object in the stream, or null if the end is reached.</summary>
/// <exception cref="IOException">On a parse error</exception>
public PgpObject NextPgpObject()
{
PacketTag tag = bcpgIn.NextPacketTag();
if ((int) tag == -1) return null;
switch (tag)
{
case PacketTag.Signature:
{
ArrayList l = new ArrayList();
while (bcpgIn.NextPacketTag() == PacketTag.Signature)
{
try
{
l.Add(new PgpSignature(bcpgIn));
}
catch (PgpException e)
{
throw new IOException("can't create signature object: " + e);
}
}
return new PgpSignatureList(
(PgpSignature[]) l.ToArray(typeof(PgpSignature)));
}
case PacketTag.SecretKey:
try
{
return new PgpSecretKeyRing(bcpgIn);
}
catch (PgpException e)
{
throw new IOException("can't create secret key object: " + e);
}
case PacketTag.PublicKey:
return new PgpPublicKeyRing(bcpgIn);
case PacketTag.CompressedData:
return new PgpCompressedData(bcpgIn);
case PacketTag.LiteralData:
return new PgpLiteralData(bcpgIn);
case PacketTag.PublicKeyEncryptedSession:
case PacketTag.SymmetricKeyEncryptedSessionKey:
return new PgpEncryptedDataList(bcpgIn);
case PacketTag.OnePassSignature:
{
ArrayList l = new ArrayList();
while (bcpgIn.NextPacketTag() == PacketTag.OnePassSignature)
{
try
{
l.Add(new PgpOnePassSignature(bcpgIn));
}
catch (PgpException e)
{
throw new IOException("can't create one pass signature object: " + e);
}
}
return new PgpOnePassSignatureList(
(PgpOnePassSignature[]) l.ToArray(typeof(PgpOnePassSignature)));
}
case PacketTag.Marker:
return new PgpMarker(bcpgIn);
case PacketTag.Experimental1:
case PacketTag.Experimental2:
case PacketTag.Experimental3:
case PacketTag.Experimental4:
return new PgpExperimental(bcpgIn);
}
throw new IOException("unknown object in stream " + bcpgIn.NextPacketTag());
}
[Obsolete("Use NextPgpObject() instead")]
public object NextObject()
{
return NextPgpObject();
}
/// <summary>
/// Return all available objects in a list.
/// </summary>
/// <returns>An <c>IList</c> containing all objects from this factory, in order.</returns>
public IList AllPgpObjects()
{
ArrayList result = new ArrayList();
PgpObject pgpObject;
while ((pgpObject = NextPgpObject()) != null)
{
result.Add(pgpObject);
}
return result;
}
}
}

View File

@@ -0,0 +1,179 @@
using System;
using System.IO;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>A one pass signature object.</remarks>
public class PgpOnePassSignature
{
private OnePassSignaturePacket sigPack;
private int signatureType;
private ISigner sig;
private byte lastb;
internal PgpOnePassSignature(
BcpgInputStream bcpgInput)
: this((OnePassSignaturePacket) bcpgInput.ReadPacket())
{
}
internal PgpOnePassSignature(
OnePassSignaturePacket sigPack)
{
this.sigPack = sigPack;
this.signatureType = sigPack.SignatureType;
try
{
this.sig = SignerUtilities.GetSigner(
PgpUtilities.GetSignatureName(sigPack.KeyAlgorithm, sigPack.HashAlgorithm));
}
catch (Exception e)
{
throw new PgpException("can't set up signature object.", e);
}
}
/// <summary>Initialise the signature object for verification.</summary>
public void InitVerify(
PgpPublicKey pubKey)
{
lastb = 0;
try
{
sig.Init(false, pubKey.GetKey());
}
catch (InvalidKeyException e)
{
throw new PgpException("invalid key.", e);
}
}
public void Update(
byte b)
{
if (signatureType == PgpSignature.CanonicalTextDocument)
{
doCanonicalUpdateByte(b);
}
else
{
sig.Update(b);
}
}
private void doCanonicalUpdateByte(
byte b)
{
if (b == '\r')
{
doUpdateCRLF();
}
else if (b == '\n')
{
if (lastb != '\r')
{
doUpdateCRLF();
}
}
else
{
sig.Update(b);
}
lastb = b;
}
private void doUpdateCRLF()
{
sig.Update((byte)'\r');
sig.Update((byte)'\n');
}
public void Update(
byte[] bytes)
{
if (signatureType == PgpSignature.CanonicalTextDocument)
{
for (int i = 0; i != bytes.Length; i++)
{
doCanonicalUpdateByte(bytes[i]);
}
}
else
{
sig.BlockUpdate(bytes, 0, bytes.Length);
}
}
public void Update(
byte[] bytes,
int off,
int length)
{
if (signatureType == PgpSignature.CanonicalTextDocument)
{
int finish = off + length;
for (int i = off; i != finish; i++)
{
doCanonicalUpdateByte(bytes[i]);
}
}
else
{
sig.BlockUpdate(bytes, off, length);
}
}
/// <summary>Verify the calculated signature against the passed in PgpSignature.</summary>
public bool Verify(
PgpSignature pgpSig)
{
byte[] trailer = pgpSig.GetSignatureTrailer();
sig.BlockUpdate(trailer, 0, trailer.Length);
return sig.VerifySignature(pgpSig.GetSignature());
}
public long KeyId
{
get { return sigPack.KeyId; }
}
public int SignatureType
{
get { return sigPack.SignatureType; }
}
public HashAlgorithmTag HashAlgorithm
{
get { return sigPack.HashAlgorithm; }
}
public PublicKeyAlgorithmTag KeyAlgorithm
{
get { return sigPack.KeyAlgorithm; }
}
public byte[] GetEncoded()
{
MemoryStream bOut = new MemoryStream();
Encode(bOut);
return bOut.ToArray();
}
public void Encode(
Stream outStr)
{
BcpgOutputStream.Wrap(outStr).WritePacket(sigPack);
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>Holder for a list of PgpOnePassSignature objects.</remarks>
public class PgpOnePassSignatureList
: PgpObject
{
private readonly PgpOnePassSignature[] sigs;
public PgpOnePassSignatureList(
PgpOnePassSignature[] sigs)
{
this.sigs = (PgpOnePassSignature[]) sigs.Clone();
}
public PgpOnePassSignatureList(
PgpOnePassSignature sig)
{
this.sigs = new PgpOnePassSignature[]{ sig };
}
public PgpOnePassSignature this[int index]
{
get { return sigs[index]; }
}
[Obsolete("Use 'object[index]' syntax instead")]
public PgpOnePassSignature Get(
int index)
{
return this[index];
}
[Obsolete("Use 'Count' property instead")]
public int Size
{
get { return sigs.Length; }
}
public int Count
{
get { return sigs.Length; }
}
public bool IsEmpty
{
get { return (sigs.Length == 0); }
}
}
}

View File

@@ -0,0 +1,135 @@
using System;
using System.IO;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.IO;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>A password based encryption object.</remarks>
public class PgpPbeEncryptedData
: PgpEncryptedData
{
private readonly SymmetricKeyEncSessionPacket keyData;
internal PgpPbeEncryptedData(
SymmetricKeyEncSessionPacket keyData,
InputStreamPacket encData)
: base(encData)
{
this.keyData = keyData;
}
/// <summary>Return the raw input stream for the data stream.</summary>
public override Stream GetInputStream()
{
return encData.GetInputStream();
}
/// <summary>Return the decrypted input stream, using the passed in passphrase.</summary>
public Stream GetDataStream(
char[] passPhrase)
{
try
{
SymmetricKeyAlgorithmTag keyAlgorithm = keyData.EncAlgorithm;
KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(
keyAlgorithm, keyData.S2k, passPhrase);
byte[] secKeyData = keyData.GetSecKeyData();
if (secKeyData != null && secKeyData.Length > 0)
{
IBufferedCipher keyCipher = CipherUtilities.GetCipher(
PgpUtilities.GetSymmetricCipherName(keyAlgorithm) + "/CFB/NoPadding");
keyCipher.Init(false,
new ParametersWithIV(key, new byte[keyCipher.GetBlockSize()]));
byte[] keyBytes = keyCipher.DoFinal(secKeyData);
keyAlgorithm = (SymmetricKeyAlgorithmTag) keyBytes[0];
key = ParameterUtilities.CreateKeyParameter(
PgpUtilities.GetSymmetricCipherName(keyAlgorithm),
keyBytes, 1, keyBytes.Length - 1);
}
IBufferedCipher c = CreateStreamCipher(keyAlgorithm);
byte[] iv = new byte[c.GetBlockSize()];
c.Init(false, new ParametersWithIV(key, iv));
encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), c, null));
if (encData is SymmetricEncIntegrityPacket)
{
truncStream = new TruncatedStream(encStream);
string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1);
IDigest digest = DigestUtilities.GetDigest(digestName);
encStream = new DigestStream(truncStream, digest, null);
}
if (Streams.ReadFully(encStream, iv, 0, iv.Length) < iv.Length)
throw new EndOfStreamException("unexpected end of stream.");
int v1 = encStream.ReadByte();
int v2 = encStream.ReadByte();
if (v1 < 0 || v2 < 0)
throw new EndOfStreamException("unexpected end of stream.");
// Note: the oracle attack on the "quick check" bytes is not deemed
// a security risk for PBE (see PgpPublicKeyEncryptedData)
bool repeatCheckPassed =
iv[iv.Length - 2] == (byte)v1
&& iv[iv.Length - 1] == (byte)v2;
// Note: some versions of PGP appear to produce 0 for the extra
// bytes rather than repeating the two previous bytes
bool zeroesCheckPassed =
v1 == 0
&& v2 == 0;
if (!repeatCheckPassed && !zeroesCheckPassed)
{
throw new PgpDataValidationException("quick check failed.");
}
return encStream;
}
catch (PgpException e)
{
throw e;
}
catch (Exception e)
{
throw new PgpException("Exception creating cipher", e);
}
}
private IBufferedCipher CreateStreamCipher(
SymmetricKeyAlgorithmTag keyAlgorithm)
{
string mode = (encData is SymmetricEncIntegrityPacket)
? "CFB"
: "OpenPGPCFB";
string cName = PgpUtilities.GetSymmetricCipherName(keyAlgorithm)
+ "/" + mode + "/NoPadding";
return CipherUtilities.GetCipher(cName);
}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using Org.BouncyCastle.Crypto;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>General class to contain a private key for use with other OpenPGP objects.</remarks>
public class PgpPrivateKey
{
private readonly long keyId;
private readonly AsymmetricKeyParameter privateKey;
/// <summary>
/// Create a PgpPrivateKey from a regular private key and the ID of its
/// associated public key.
/// </summary>
/// <param name="privateKey">Private key to use.</param>
/// <param name="keyId">ID of the corresponding public key.</param>
public PgpPrivateKey(
AsymmetricKeyParameter privateKey,
long keyId)
{
if (!privateKey.IsPrivate)
throw new ArgumentException("Expected a private key", "privateKey");
this.privateKey = privateKey;
this.keyId = keyId;
}
/// <summary>The keyId associated with the contained private key.</summary>
public long KeyId
{
get { return keyId; }
}
/// <summary>The contained private key.</summary>
public AsymmetricKeyParameter Key
{
get { return privateKey; }
}
}
}

View File

@@ -0,0 +1,835 @@
using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Collections;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>General class to handle a PGP public key object.</remarks>
public class PgpPublicKey
{
private static readonly int[] MasterKeyCertificationTypes = new int[]
{
PgpSignature.PositiveCertification,
PgpSignature.CasualCertification,
PgpSignature.NoCertification,
PgpSignature.DefaultCertification
};
private long keyId;
private byte[] fingerprint;
private int keyStrength;
internal PublicKeyPacket publicPk;
private TrustPacket trustPk;
private ArrayList keySigs = new ArrayList();
private ArrayList ids = new ArrayList();
private ArrayList idTrusts = new ArrayList();
private ArrayList idSigs = new ArrayList();
private ArrayList subSigs;
private void Init()
{
IBcpgKey key = publicPk.Key;
if (publicPk.Version <= 3)
{
RsaPublicBcpgKey rK = (RsaPublicBcpgKey) key;
this.keyId = rK.Modulus.LongValue;
try
{
IDigest digest = DigestUtilities.GetDigest("MD5");
byte[] bytes = rK.Modulus.ToByteArrayUnsigned();
digest.BlockUpdate(bytes, 0, bytes.Length);
bytes = rK.PublicExponent.ToByteArrayUnsigned();
digest.BlockUpdate(bytes, 0, bytes.Length);
this.fingerprint = DigestUtilities.DoFinal(digest);
}
//catch (NoSuchAlgorithmException)
catch (Exception)
{
throw new IOException("can't find MD5");
}
this.keyStrength = rK.Modulus.BitLength;
}
else
{
byte[] kBytes = publicPk.GetEncodedContents();
try
{
IDigest digest = DigestUtilities.GetDigest("SHA1");
digest.Update(0x99);
digest.Update((byte)(kBytes.Length >> 8));
digest.Update((byte)kBytes.Length);
digest.BlockUpdate(kBytes, 0, kBytes.Length);
this.fingerprint = DigestUtilities.DoFinal(digest);
}
//catch (NoSuchAlgorithmException)
catch (Exception)
{
throw new IOException("can't find SHA1");
}
this.keyId = (long)(((ulong)fingerprint[fingerprint.Length - 8] << 56)
| ((ulong)fingerprint[fingerprint.Length - 7] << 48)
| ((ulong)fingerprint[fingerprint.Length - 6] << 40)
| ((ulong)fingerprint[fingerprint.Length - 5] << 32)
| ((ulong)fingerprint[fingerprint.Length - 4] << 24)
| ((ulong)fingerprint[fingerprint.Length - 3] << 16)
| ((ulong)fingerprint[fingerprint.Length - 2] << 8)
| (ulong)fingerprint[fingerprint.Length - 1]);
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;
}
}
}
/// <summary>
/// Create a PgpPublicKey from the passed in lightweight one.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="algorithm">Asymmetric algorithm type representing the public key.</param>
/// <param name="pubKey">Actual public key to associate.</param>
/// <param name="time">Date of creation.</param>
/// <exception cref="ArgumentException">If <c>pubKey</c> is not public.</exception>
/// <exception cref="PgpException">On key creation problem.</exception>
public PgpPublicKey(
PublicKeyAlgorithmTag algorithm,
AsymmetricKeyParameter pubKey,
DateTime time)
{
if (pubKey.IsPrivate)
throw new ArgumentException("Expected a public key", "pubKey");
IBcpgKey bcpgKey;
if (pubKey is RsaKeyParameters)
{
RsaKeyParameters rK = (RsaKeyParameters) pubKey;
bcpgKey = new RsaPublicBcpgKey(rK.Modulus, rK.Exponent);
}
else if (pubKey is DsaPublicKeyParameters)
{
DsaPublicKeyParameters dK = (DsaPublicKeyParameters) pubKey;
DsaParameters dP = dK.Parameters;
bcpgKey = new DsaPublicBcpgKey(dP.P, dP.Q, dP.G, dK.Y);
}
else if (pubKey is ElGamalPublicKeyParameters)
{
ElGamalPublicKeyParameters eK = (ElGamalPublicKeyParameters) pubKey;
ElGamalParameters eS = eK.Parameters;
bcpgKey = new ElGamalPublicBcpgKey(eS.P, eS.G, eK.Y);
}
else
{
throw new PgpException("unknown key class");
}
this.publicPk = new PublicKeyPacket(algorithm, time, bcpgKey);
this.ids = new ArrayList();
this.idSigs = new ArrayList();
try
{
Init();
}
catch (IOException e)
{
throw new PgpException("exception calculating keyId", e);
}
}
/// <summary>Constructor for a sub-key.</summary>
internal PgpPublicKey(
PublicKeyPacket publicPk,
TrustPacket trustPk,
ArrayList sigs)
{
this.publicPk = publicPk;
this.trustPk = trustPk;
this.subSigs = sigs;
Init();
}
internal PgpPublicKey(
PgpPublicKey key,
TrustPacket trust,
ArrayList subSigs)
{
this.publicPk = key.publicPk;
this.trustPk = trust;
this.subSigs = subSigs;
this.fingerprint = key.fingerprint;
this.keyId = key.keyId;
this.keyStrength = key.keyStrength;
}
/// <summary>Copy constructor.</summary>
/// <param name="pubKey">The public key to copy.</param>
internal PgpPublicKey(
PgpPublicKey pubKey)
{
this.publicPk = pubKey.publicPk;
this.keySigs = new ArrayList(pubKey.keySigs);
this.ids = new ArrayList(pubKey.ids);
this.idTrusts = new ArrayList(pubKey.idTrusts);
this.idSigs = new ArrayList(pubKey.idSigs.Count);
for (int i = 0; i != pubKey.idSigs.Count; i++)
{
this.idSigs.Add(new ArrayList((ArrayList)pubKey.idSigs[i]));
}
if (pubKey.subSigs != null)
{
this.subSigs = new ArrayList(pubKey.subSigs.Count);
for (int i = 0; i != pubKey.subSigs.Count; i++)
{
this.subSigs.Add(pubKey.subSigs[i]);
}
}
this.fingerprint = pubKey.fingerprint;
this.keyId = pubKey.keyId;
this.keyStrength = pubKey.keyStrength;
}
internal PgpPublicKey(
PublicKeyPacket publicPk,
TrustPacket trustPk,
ArrayList keySigs,
ArrayList ids,
ArrayList idTrusts,
ArrayList idSigs)
{
this.publicPk = publicPk;
this.trustPk = trustPk;
this.keySigs = keySigs;
this.ids = ids;
this.idTrusts = idTrusts;
this.idSigs = idSigs;
Init();
}
internal PgpPublicKey(
PublicKeyPacket publicPk,
ArrayList ids,
ArrayList idSigs)
{
this.publicPk = publicPk;
this.ids = ids;
this.idSigs = idSigs;
Init();
}
/// <summary>The version of this key.</summary>
public int Version
{
get { return publicPk.Version; }
}
/// <summary>The creation time of this key.</summary>
public DateTime CreationTime
{
get { return publicPk.GetTime(); }
}
/// <summary>The number of valid days from creation time - zero means no expiry.</summary>
public int ValidDays
{
get
{
if (publicPk.Version > 3)
{
return (int)(GetValidSeconds() / (24 * 60 * 60));
}
return publicPk.ValidDays;
}
}
/// <summary>Return the trust data associated with the public key, if present.</summary>
/// <returns>A byte array with trust data, null otherwise.</returns>
public byte[] GetTrustData()
{
if (trustPk == null)
{
return null;
}
return trustPk.GetLevelAndTrustAmount();
}
/// <summary>The number of valid seconds from creation time - zero means no expiry.</summary>
public long GetValidSeconds()
{
if (publicPk.Version > 3)
{
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;
}
}
return 0;
}
return (long) publicPk.ValidDays * 24 * 60 * 60;
}
private long GetExpirationTimeFromSig(
bool selfSigned,
int signatureType)
{
foreach (PgpSignature sig in GetSignaturesOfType(signatureType))
{
if (!selfSigned || sig.KeyId == KeyId)
{
PgpSignatureSubpacketVector hashed = sig.GetHashedSubPackets();
if (hashed != null)
{
return hashed.GetKeyExpirationTime();
}
return 0;
}
}
return -1;
}
/// <summary>The keyId associated with the public key.</summary>
public long KeyId
{
get { return keyId; }
}
/// <summary>The fingerprint of the key</summary>
public byte[] GetFingerprint()
{
return (byte[]) fingerprint.Clone();
}
/// <summary>True, if this key is marked as suitable for encryption</summary>
/// <returns>True, if this key is marked as suitable for using for encryption.</returns>
public bool IsEncryptionKey
{
get
{
switch (publicPk.Algorithm)
{
case PublicKeyAlgorithmTag.ElGamalEncrypt:
case PublicKeyAlgorithmTag.ElGamalGeneral:
case PublicKeyAlgorithmTag.RsaEncrypt:
case PublicKeyAlgorithmTag.RsaGeneral:
return true;
default:
return false;
}
}
}
/// <summary>True, if this is a master key.</summary>
public bool IsMasterKey
{
get { return subSigs == null; }
}
/// <summary>The algorithm code associated with the public key.</summary>
public PublicKeyAlgorithmTag Algorithm
{
get { return publicPk.Algorithm; }
}
/// <summary>The strength of the key in bits.</summary>
public int BitStrength
{
get { return keyStrength; }
}
/// <summary>The public key contained in the object.</summary>
/// <returns>A lightweight public key.</returns>
/// <exception cref="PgpException">If the key algorithm is not recognised.</exception>
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.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 e)
{
throw e;
}
catch (Exception e)
{
throw new PgpException("exception constructing public key", e);
}
}
/// <summary>Allows enumeration of any user IDs associated with the key.</summary>
/// <returns>An <c>IEnumerable</c> of <c>string</c> objects.</returns>
public IEnumerable GetUserIds()
{
ArrayList temp = new ArrayList();
foreach (object o in ids)
{
if (o is string)
{
temp.Add(o);
}
}
return new EnumerableProxy(temp);
}
/// <summary>Allows enumeration of any user attribute vectors associated with the key.</summary>
/// <returns>An <c>IEnumerable</c> of <c>PgpUserAttributeSubpacketVector</c> objects.</returns>
public IEnumerable GetUserAttributes()
{
ArrayList temp = new ArrayList();
foreach (object o in ids)
{
if (o is PgpUserAttributeSubpacketVector)
{
temp.Add(o);
}
}
return new EnumerableProxy(temp);
}
/// <summary>Allows enumeration of any signatures associated with the passed in id.</summary>
/// <param name="id">The ID to be matched.</param>
/// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns>
public IEnumerable GetSignaturesForId(
string id)
{
if (id == null)
throw new ArgumentNullException("id");
for (int i = 0; i != ids.Count; i++)
{
if (id.Equals(ids[i]))
{
return new EnumerableProxy((ArrayList) idSigs[i]);
}
}
return null;
}
/// <summary>Allows enumeration of signatures associated with the passed in user attributes.</summary>
/// <param name="userAttributes">The vector of user attributes to be matched.</param>
/// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns>
public IEnumerable GetSignaturesForUserAttribute(
PgpUserAttributeSubpacketVector userAttributes)
{
for (int i = 0; i != ids.Count; i++)
{
if (userAttributes.Equals(ids[i]))
{
return new EnumerableProxy((ArrayList) idSigs[i]);
}
}
return null;
}
/// <summary>Allows enumeration of signatures of the passed in type that are on this key.</summary>
/// <param name="signatureType">The type of the signature to be returned.</param>
/// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns>
public IEnumerable GetSignaturesOfType(
int signatureType)
{
ArrayList temp = new ArrayList();
foreach (PgpSignature sig in GetSignatures())
{
if (sig.SignatureType == signatureType)
{
temp.Add(sig);
}
}
return new EnumerableProxy(temp);
}
/// <summary>Allows enumeration of all signatures/certifications associated with this key.</summary>
/// <returns>An <c>IEnumerable</c> with all signatures/certifications.</returns>
public IEnumerable GetSignatures()
{
ArrayList sigs;
if (subSigs != null)
{
sigs = subSigs;
}
else
{
sigs = new ArrayList(keySigs);
foreach (ICollection extraSigs in idSigs)
{
sigs.AddRange(extraSigs);
}
}
return new EnumerableProxy(sigs);
}
public byte[] GetEncoded()
{
MemoryStream bOut = new MemoryStream();
Encode(bOut);
return bOut.ToArray();
}
public void Encode(
Stream outStr)
{
BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr);
bcpgOut.WritePacket(publicPk);
if (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 string)
{
string id = (string) ids[i];
bcpgOut.WritePacket(new UserIdPacket(id));
}
else
{
PgpUserAttributeSubpacketVector v = (PgpUserAttributeSubpacketVector)ids[i];
bcpgOut.WritePacket(new UserAttributePacket(v.ToSubpacketArray()));
}
if (idTrusts[i] != null)
{
bcpgOut.WritePacket((ContainedPacket)idTrusts[i]);
}
foreach (PgpSignature sig in (ArrayList) idSigs[i])
{
sig.Encode(bcpgOut);
}
}
}
else
{
foreach (PgpSignature subSig in subSigs)
{
subSig.Encode(bcpgOut);
}
}
}
/// <summary>Check whether this (sub)key has a revocation signature on it.</summary>
/// <returns>True, if this (sub)key has been revoked.</returns>
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;
}
/// <summary>Add a certification for an id to the given public key.</summary>
/// <param name="key">The key the certification is to be added to.</param>
/// <param name="id">The ID the certification is associated with.</param>
/// <param name="certification">The new certification.</param>
/// <returns>The re-certified key.</returns>
public static PgpPublicKey AddCertification(
PgpPublicKey key,
string id,
PgpSignature certification)
{
PgpPublicKey returnKey = new PgpPublicKey(key);
ArrayList sigList = null;
for (int i = 0; i != returnKey.ids.Count; i++)
{
if (id.Equals(returnKey.ids[i]))
{
sigList = (ArrayList) returnKey.idSigs[i];
}
}
if (sigList != null)
{
sigList.Add(certification);
}
else
{
sigList = new ArrayList();
sigList.Add(certification);
returnKey.ids.Add(id);
returnKey.idTrusts.Add(null);
returnKey.idSigs.Add(sigList);
}
return returnKey;
}
/// <summary>Add a certification for the given UserAttributeSubpackets to the given public key.</summary>
/// <param name="key">The key the certification is to be added to.</param>
/// <param name="userAttributes">The attributes the certification is associated with.</param>
/// <param name="certification">The new certification.</param>
/// <returns>The re-certified key.</returns>
public static PgpPublicKey AddCertification(
PgpPublicKey key,
PgpUserAttributeSubpacketVector userAttributes,
PgpSignature certification)
{
PgpPublicKey returnKey = new PgpPublicKey(key);
IList sigList = null;
for (int i = 0; i != returnKey.ids.Count; i++)
{
if (userAttributes.Equals(returnKey.ids[i]))
{
sigList = (IList) returnKey.idSigs[i];
}
}
if (sigList != null)
{
sigList.Add(certification);
}
else
{
sigList = new ArrayList();
sigList.Add(certification);
returnKey.ids.Add(userAttributes);
returnKey.idTrusts.Add(null);
returnKey.idSigs.Add(sigList);
}
return returnKey;
}
/// <summary>
/// Remove any certifications associated with a user attribute subpacket on a key.
/// </summary>
/// <param name="key">The key the certifications are to be removed from.</param>
/// <param name="userAttributes">The attributes to be removed.</param>
/// <returns>
/// The re-certified key, or null if the user attribute subpacket was not found on the key.
/// </returns>
public static PgpPublicKey RemoveCertification(
PgpPublicKey key,
PgpUserAttributeSubpacketVector userAttributes)
{
PgpPublicKey returnKey = new PgpPublicKey(key);
bool found = false;
for (int i = 0; i < returnKey.ids.Count; i++)
{
if (userAttributes.Equals(returnKey.ids[i]))
{
found = true;
returnKey.ids.RemoveAt(i);
returnKey.idTrusts.RemoveAt(i);
returnKey.idSigs.RemoveAt(i);
}
}
if (!found)
{
return null;
}
return returnKey;
}
/// <summary>Remove any certifications associated with a given ID on a key.</summary>
/// <param name="key">The key the certifications are to be removed from.</param>
/// <param name="id">The ID that is to be removed.</param>
/// <returns>The re-certified key, or null if the ID was not found on the key.</returns>
public static PgpPublicKey RemoveCertification(
PgpPublicKey key,
string 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;
}
/// <summary>Remove any certifications associated with a given ID on a key.</summary>
/// <param name="key">The key the certifications are to be removed from.</param>
/// <param name="id">The ID that the certfication is to be removed from.</param>
/// <param name="certification">The certfication to be removed.</param>
/// <returns>The re-certified key, or null if the certification was not found.</returns>
public static PgpPublicKey RemoveCertification(
PgpPublicKey key,
string 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]))
{
ArrayList certs = (ArrayList) returnKey.idSigs[i];
found = certs.Contains(certification);
if (found)
{
certs.Remove(certification);
}
}
}
return found ? returnKey : null;
}
/// <summary>Add a revocation or some other key certification to a key.</summary>
/// <param name="key">The key the revocation is to be added to.</param>
/// <param name="certification">The key signature to be added.</param>
/// <returns>The new changed public key object.</returns>
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);
if (returnKey.subSigs != null)
{
returnKey.subSigs.Add(certification);
}
else
{
returnKey.keySigs.Add(certification);
}
return returnKey;
}
}
}

View File

@@ -0,0 +1,233 @@
using System;
using System.IO;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.IO;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>A public key encrypted data object.</remarks>
public class PgpPublicKeyEncryptedData
: PgpEncryptedData
{
private PublicKeyEncSessionPacket keyData;
internal PgpPublicKeyEncryptedData(
PublicKeyEncSessionPacket keyData,
InputStreamPacket encData)
: base(encData)
{
this.keyData = keyData;
}
private static IBufferedCipher GetKeyCipher(
PublicKeyAlgorithmTag algorithm)
{
try
{
switch (algorithm)
{
case PublicKeyAlgorithmTag.RsaEncrypt:
case PublicKeyAlgorithmTag.RsaGeneral:
return CipherUtilities.GetCipher("RSA//PKCS1Padding");
case PublicKeyAlgorithmTag.ElGamalEncrypt:
case PublicKeyAlgorithmTag.ElGamalGeneral:
return CipherUtilities.GetCipher("ElGamal/ECB/PKCS1Padding");
default:
throw new PgpException("unknown asymmetric algorithm: " + algorithm);
}
}
catch (PgpException e)
{
throw e;
}
catch (Exception e)
{
throw new PgpException("Exception creating cipher", e);
}
}
private bool ConfirmCheckSum(
byte[] sessionInfo)
{
int check = 0;
for (int i = 1; i != sessionInfo.Length - 2; i++)
{
check += sessionInfo[i] & 0xff;
}
return (sessionInfo[sessionInfo.Length - 2] == (byte)(check >> 8))
&& (sessionInfo[sessionInfo.Length - 1] == (byte)(check));
}
/// <summary>The key ID for the key used to encrypt the data.</summary>
public long KeyId
{
get { return keyData.KeyId; }
}
/// <summary>Return the decrypted data stream for the packet.</summary>
public Stream GetDataStream(
PgpPrivateKey privKey)
{
IBufferedCipher c1 = GetKeyCipher(keyData.Algorithm);
try
{
c1.Init(false, privKey.Key);
}
catch (InvalidKeyException e)
{
throw new PgpException("error setting asymmetric cipher", e);
}
BigInteger[] keyD = keyData.GetEncSessionKey();
if (keyData.Algorithm == PublicKeyAlgorithmTag.RsaEncrypt
|| keyData.Algorithm == PublicKeyAlgorithmTag.RsaGeneral)
{
c1.ProcessBytes(keyD[0].ToByteArrayUnsigned());
}
else
{
ElGamalPrivateKeyParameters k = (ElGamalPrivateKeyParameters)privKey.Key;
int size = (k.Parameters.P.BitLength + 7) / 8;
byte[] bi = keyD[0].ToByteArray();
int diff = bi.Length - size;
if (diff >= 0)
{
c1.ProcessBytes(bi, diff, size);
}
else
{
byte[] zeros = new byte[-diff];
c1.ProcessBytes(zeros);
c1.ProcessBytes(bi);
}
bi = keyD[1].ToByteArray();
diff = bi.Length - size;
if (diff >= 0)
{
c1.ProcessBytes(bi, diff, size);
}
else
{
byte[] zeros = new byte[-diff];
c1.ProcessBytes(zeros);
c1.ProcessBytes(bi);
}
}
byte[] plain;
try
{
plain = c1.DoFinal();
}
catch (Exception e)
{
throw new PgpException("exception decrypting secret key", e);
}
if (!ConfirmCheckSum(plain))
throw new PgpKeyValidationException("key checksum failed");
IBufferedCipher c2;
string cipherName = PgpUtilities.GetSymmetricCipherName((SymmetricKeyAlgorithmTag) plain[0]);
string cName = cipherName;
try
{
if (encData is SymmetricEncIntegrityPacket)
{
cName += "/CFB/NoPadding";
}
else
{
cName += "/OpenPGPCFB/NoPadding";
}
c2 = CipherUtilities.GetCipher(cName);
}
catch (PgpException e)
{
throw e;
}
catch (Exception e)
{
throw new PgpException("exception creating cipher", e);
}
if (c2 == null)
return encData.GetInputStream();
try
{
KeyParameter key = ParameterUtilities.CreateKeyParameter(
cipherName, plain, 1, plain.Length - 3);
byte[] iv = new byte[c2.GetBlockSize()];
c2.Init(false, new ParametersWithIV(key, iv));
encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), c2, null));
if (encData is SymmetricEncIntegrityPacket)
{
truncStream = new TruncatedStream(encStream);
string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1);
IDigest digest = DigestUtilities.GetDigest(digestName);
encStream = new DigestStream(truncStream, digest, null);
}
if (Streams.ReadFully(encStream, iv, 0, iv.Length) < iv.Length)
throw new EndOfStreamException("unexpected end of stream.");
int v1 = encStream.ReadByte();
int v2 = encStream.ReadByte();
if (v1 < 0 || v2 < 0)
throw new EndOfStreamException("unexpected end of stream.");
// Note: the oracle attack on the "quick check" bytes is deemed
// a security risk for typical public key encryption usages,
// therefore we do not perform the check.
// bool repeatCheckPassed =
// iv[iv.Length - 2] == (byte)v1
// && iv[iv.Length - 1] == (byte)v2;
//
// // Note: some versions of PGP appear to produce 0 for the extra
// // bytes rather than repeating the two previous bytes
// bool zeroesCheckPassed =
// v1 == 0
// && v2 == 0;
//
// if (!repeatCheckPassed && !zeroesCheckPassed)
// {
// throw new PgpDataValidationException("quick check failed.");
// }
return encStream;
}
catch (PgpException e)
{
throw e;
}
catch (Exception e)
{
throw new PgpException("Exception starting decryption", e);
}
}
}
}

View File

@@ -0,0 +1,187 @@
using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Utilities.Collections;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>
/// Class to hold a single master public 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>PgpPublicKeyRingBundle</c> class.
/// </p>
/// </remarks>
public class PgpPublicKeyRing
: PgpKeyRing
{
private readonly ArrayList keys;
public PgpPublicKeyRing(
byte[] encoding)
: this(new MemoryStream(encoding, false))
{
}
internal PgpPublicKeyRing(
ArrayList pubKeys)
{
this.keys = pubKeys;
}
public PgpPublicKeyRing(
Stream inputStream)
{
this.keys = new ArrayList();
BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream);
PacketTag initialTag = bcpgInput.NextPacketTag();
if (initialTag != PacketTag.PublicKey && initialTag != PacketTag.PublicSubkey)
{
throw new IOException("public key ring doesn't start with public key tag: "
+ "tag 0x" + ((int)initialTag).ToString("X"));
}
PublicKeyPacket pubPk = (PublicKeyPacket) bcpgInput.ReadPacket();;
TrustPacket trustPk = ReadOptionalTrustPacket(bcpgInput);
// direct signatures and revocations
ArrayList keySigs = ReadSignaturesAndTrust(bcpgInput);
ArrayList ids, idTrusts, idSigs;
ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs);
keys.Add(new PgpPublicKey(pubPk, trustPk, keySigs, ids, idTrusts, idSigs));
// Read subkeys
while (bcpgInput.NextPacketTag() == PacketTag.PublicSubkey)
{
PublicKeyPacket pk = (PublicKeyPacket) bcpgInput.ReadPacket();
TrustPacket kTrust = ReadOptionalTrustPacket(bcpgInput);
// PGP 8 actually leaves out the signature.
ArrayList sigList = ReadSignaturesAndTrust(bcpgInput);
keys.Add(new PgpPublicKey(pk, kTrust, sigList));
}
}
/// <summary>Return the first public key in the ring.</summary>
public PgpPublicKey GetPublicKey()
{
return (PgpPublicKey) keys[0];
}
/// <summary>Return the public key referred to by the passed in key ID if it is present.</summary>
public PgpPublicKey GetPublicKey(
long keyId)
{
foreach (PgpPublicKey k in keys)
{
if (keyId == k.KeyId)
{
return k;
}
}
return null;
}
/// <summary>Allows enumeration of all the public keys.</summary>
/// <returns>An <c>IEnumerable</c> of <c>PgpPublicKey</c> objects.</returns>
public IEnumerable GetPublicKeys()
{
return new EnumerableProxy(keys);
}
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 (PgpPublicKey k in keys)
{
k.Encode(outStr);
}
}
/// <summary>
/// Returns a new key ring with the public key passed in either added or
/// replacing an existing one.
/// </summary>
/// <param name="pubRing">The public key ring to be modified.</param>
/// <param name="pubKey">The public key to be inserted.</param>
/// <returns>A new <c>PgpPublicKeyRing</c></returns>
public static PgpPublicKeyRing InsertPublicKey(
PgpPublicKeyRing pubRing,
PgpPublicKey pubKey)
{
ArrayList keys = new ArrayList(pubRing.keys);
bool found = false;
bool masterFound = false;
for (int i = 0; i != keys.Count; i++)
{
PgpPublicKey key = (PgpPublicKey) keys[i];
if (key.KeyId == pubKey.KeyId)
{
found = true;
keys[i] = pubKey;
}
if (key.IsMasterKey)
{
masterFound = true;
}
}
if (!found)
{
if (pubKey.IsMasterKey && masterFound)
throw new ArgumentException("cannot add a master key to a ring that already has one");
keys.Add(pubKey);
}
return new PgpPublicKeyRing(keys);
}
/// <summary>Returns a new key ring with the public key passed in removed from the key ring.</summary>
/// <param name="pubRing">The public key ring to be modified.</param>
/// <param name="pubKey">The public key to be removed.</param>
/// <returns>A new <c>PgpPublicKeyRing</c>, or null if pubKey is not found.</returns>
public static PgpPublicKeyRing RemovePublicKey(
PgpPublicKeyRing pubRing,
PgpPublicKey pubKey)
{
ArrayList keys = new ArrayList(pubRing.keys);
bool found = false;
for (int i = 0; i < keys.Count; i++)
{
PgpPublicKey key = (PgpPublicKey) keys[i];
if (key.KeyId == pubKey.KeyId)
{
found = true;
keys.RemoveAt(i);
}
}
return found ? new PgpPublicKeyRing(keys) : null;
}
}
}

View File

@@ -0,0 +1,269 @@
using System;
using System.Collections;
using System.Globalization;
using System.IO;
using Org.BouncyCastle.Utilities.Collections;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>
/// Often a PGP key ring file is made up of a succession of master/sub-key key rings.
/// If you want to read an entire public key file in one hit this is the class for you.
/// </remarks>
public class PgpPublicKeyRingBundle
{
private readonly IDictionary pubRings;
private readonly ArrayList order;
private PgpPublicKeyRingBundle(
IDictionary pubRings,
ArrayList order)
{
this.pubRings = pubRings;
this.order = order;
}
public PgpPublicKeyRingBundle(
byte[] encoding)
: this(new MemoryStream(encoding, false))
{
}
/// <summary>Build a PgpPubliKeyRingBundle from the passed in input stream.</summary>
/// <param name="inputStream">Input stream containing data.</param>
/// <exception cref="IOException">If a problem parsing the stream occurs.</exception>
/// <exception cref="PgpException">If an object is encountered which isn't a PgpPublicKeyRing.</exception>
public PgpPublicKeyRingBundle(
Stream inputStream)
: this(new PgpObjectFactory(inputStream).AllPgpObjects())
{
}
public PgpPublicKeyRingBundle(
IEnumerable e)
{
this.pubRings = new Hashtable();
this.order = new ArrayList();
foreach (object obj in e)
{
PgpPublicKeyRing pgpPub = obj as PgpPublicKeyRing;
if (pgpPub == null)
{
throw new PgpException(obj.GetType().FullName + " found where PgpPublicKeyRing expected");
}
long key = pgpPub.GetPublicKey().KeyId;
pubRings.Add(key, pgpPub);
order.Add(key);
}
}
[Obsolete("Use 'Count' property instead")]
public int Size
{
get { return order.Count; }
}
/// <summary>Return the number of rings in this collection.</summary>
public int Count
{
get { return order.Count; }
}
/// <summary>Allow enumeration of the public key rings making up this collection.</summary>
public IEnumerable GetKeyRings()
{
return new EnumerableProxy(pubRings.Values);
}
/// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary>
/// <param name="userId">The user ID to be matched.</param>
/// <returns>An <c>IEnumerable</c> of key rings which matched (possibly none).</returns>
public IEnumerable GetKeyRings(
string userId)
{
return GetKeyRings(userId, false, false);
}
/// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary>
/// <param name="userId">The user ID to be matched.</param>
/// <param name="matchPartial">If true, userId need only be a substring of an actual ID string to match.</param>
/// <returns>An <c>IEnumerable</c> of key rings which matched (possibly none).</returns>
public IEnumerable GetKeyRings(
string userId,
bool matchPartial)
{
return GetKeyRings(userId, matchPartial, false);
}
/// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary>
/// <param name="userId">The user ID to be matched.</param>
/// <param name="matchPartial">If true, userId need only be a substring of an actual ID string to match.</param>
/// <param name="ignoreCase">If true, case is ignored in user ID comparisons.</param>
/// <returns>An <c>IEnumerable</c> of key rings which matched (possibly none).</returns>
public IEnumerable GetKeyRings(
string userId,
bool matchPartial,
bool ignoreCase)
{
IList rings = new ArrayList();
if (ignoreCase)
{
userId = userId.ToLower(CultureInfo.InvariantCulture);
}
foreach (PgpPublicKeyRing pubRing in GetKeyRings())
{
foreach (string nextUserID in pubRing.GetPublicKey().GetUserIds())
{
string next = nextUserID;
if (ignoreCase)
{
next = next.ToLower(CultureInfo.InvariantCulture);
}
if (matchPartial)
{
if (next.IndexOf(userId) > -1)
{
rings.Add(pubRing);
}
}
else
{
if (next.Equals(userId))
{
rings.Add(pubRing);
}
}
}
}
return new EnumerableProxy(rings);
}
/// <summary>Return the PGP public key associated with the given key id.</summary>
/// <param name="keyId">The ID of the public key to return.</param>
public PgpPublicKey GetPublicKey(
long keyId)
{
foreach (PgpPublicKeyRing pubRing in GetKeyRings())
{
PgpPublicKey pub = pubRing.GetPublicKey(keyId);
if (pub != null)
{
return pub;
}
}
return null;
}
/// <summary>Return the public key ring which contains the key referred to by keyId</summary>
/// <param name="keyId">The ID of the public key</param>
public PgpPublicKeyRing GetPublicKeyRing(
long keyId)
{
if (pubRings.Contains(keyId))
{
return (PgpPublicKeyRing)pubRings[keyId];
}
foreach (PgpPublicKeyRing pubRing in GetKeyRings())
{
PgpPublicKey pub = pubRing.GetPublicKey(keyId);
if (pub != null)
{
return pubRing;
}
}
return null;
}
public byte[] GetEncoded()
{
MemoryStream bOut = new MemoryStream();
Encode(bOut);
return bOut.ToArray();
}
public void Encode(
Stream outStr)
{
BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr);
foreach (long key in order)
{
PgpPublicKeyRing sec = (PgpPublicKeyRing) pubRings[key];
sec.Encode(bcpgOut);
}
}
/// <summary>
/// Return a new bundle containing the contents of the passed in bundle and
/// the passed in public key ring.
/// </summary>
/// <param name="bundle">The <c>PgpPublicKeyRingBundle</c> the key ring is to be added to.</param>
/// <param name="publicKeyRing">The key ring to be added.</param>
/// <returns>A new <c>PgpPublicKeyRingBundle</c> merging the current one with the passed in key ring.</returns>
/// <exception cref="ArgumentException">If the keyId for the passed in key ring is already present.</exception>
public static PgpPublicKeyRingBundle AddPublicKeyRing(
PgpPublicKeyRingBundle bundle,
PgpPublicKeyRing publicKeyRing)
{
long key = publicKeyRing.GetPublicKey().KeyId;
if (bundle.pubRings.Contains(key))
{
throw new ArgumentException("Bundle already contains a key with a keyId for the passed in ring.");
}
IDictionary newPubRings = new Hashtable(bundle.pubRings);
ArrayList newOrder = new ArrayList(bundle.order);
newPubRings[key] = publicKeyRing;
newOrder.Add(key);
return new PgpPublicKeyRingBundle(newPubRings, newOrder);
}
/// <summary>
/// Return a new bundle containing the contents of the passed in bundle with
/// the passed in public key ring removed.
/// </summary>
/// <param name="bundle">The <c>PgpPublicKeyRingBundle</c> the key ring is to be removed from.</param>
/// <param name="publicKeyRing">The key ring to be removed.</param>
/// <returns>A new <c>PgpPublicKeyRingBundle</c> not containing the passed in key ring.</returns>
/// <exception cref="ArgumentException">If the keyId for the passed in key ring is not present.</exception>
public static PgpPublicKeyRingBundle RemovePublicKeyRing(
PgpPublicKeyRingBundle bundle,
PgpPublicKeyRing publicKeyRing)
{
long key = publicKeyRing.GetPublicKey().KeyId;
if (!bundle.pubRings.Contains(key))
{
throw new ArgumentException("Bundle does not contain a key with a keyId for the passed in ring.");
}
IDictionary newPubRings = new Hashtable(bundle.pubRings);
ArrayList newOrder = new ArrayList(bundle.order);
newPubRings.Remove(key);
newOrder.Remove(key);
return new PgpPublicKeyRingBundle(newPubRings, newOrder);
}
}
}

View File

@@ -0,0 +1,707 @@
using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>General class to handle a PGP secret key object.</remarks>
public class PgpSecretKey
{
private SecretKeyPacket secret;
private TrustPacket trust;
private ArrayList keySigs;
private ArrayList ids;
private ArrayList idTrusts;
private ArrayList idSigs;
private PgpPublicKey pub;
private ArrayList subSigs;
/// <summary>Copy constructor - master key.</summary>
private PgpSecretKey(
SecretKeyPacket secret,
TrustPacket trust,
ArrayList keySigs,
ArrayList ids,
ArrayList idTrusts,
ArrayList idSigs,
PgpPublicKey pub)
{
this.secret = secret;
this.trust = trust;
this.keySigs = keySigs;
this.ids = ids;
this.idTrusts = idTrusts;
this.idSigs = idSigs;
this.pub = pub;
}
/// <summary>Copy constructor - subkey.</summary>
private PgpSecretKey(
SecretKeyPacket secret,
TrustPacket trust,
ArrayList subSigs,
PgpPublicKey pub)
{
this.secret = secret;
this.trust = trust;
this.subSigs = subSigs;
this.pub = pub;
}
internal PgpSecretKey(
SecretKeyPacket secret,
TrustPacket trust,
ArrayList keySigs,
ArrayList ids,
ArrayList idTrusts,
ArrayList idSigs)
{
this.secret = secret;
this.trust = trust;
this.keySigs = keySigs;
this.ids = ids;
this.idTrusts = idTrusts;
this.idSigs = idSigs;
this.pub = new PgpPublicKey(secret.PublicKeyPacket, trust, keySigs, ids, idTrusts, idSigs);
}
internal PgpSecretKey(
SecretKeyPacket secret,
TrustPacket trust,
ArrayList subSigs)
{
this.secret = secret;
this.trust = trust;
this.subSigs = subSigs;
this.pub = new PgpPublicKey(secret.PublicKeyPacket, trust, subSigs);
}
/// <summary>Create a subkey</summary>
internal PgpSecretKey(
PgpKeyPair keyPair,
TrustPacket trust,
ArrayList subSigs,
SymmetricKeyAlgorithmTag encAlgorithm,
char[] passPhrase,
bool useSHA1,
SecureRandom rand)
: this(keyPair, encAlgorithm, passPhrase, useSHA1, rand)
{
this.secret = new SecretSubkeyPacket(
secret.PublicKeyPacket,
secret.EncAlgorithm,
secret.S2kUsage,
secret.S2k,
secret.GetIV(),
secret.GetSecretKeyData());
this.trust = trust;
this.subSigs = subSigs;
this.pub = new PgpPublicKey(keyPair.PublicKey, trust, subSigs);
}
internal PgpSecretKey(
PgpKeyPair keyPair,
SymmetricKeyAlgorithmTag encAlgorithm,
char[] passPhrase,
bool useSHA1,
SecureRandom rand)
{
PublicKeyPacket pubPk = keyPair.PublicKey.publicPk;
BcpgObject secKey;
switch (keyPair.PublicKey.Algorithm)
{
case PublicKeyAlgorithmTag.RsaEncrypt:
case PublicKeyAlgorithmTag.RsaSign:
case PublicKeyAlgorithmTag.RsaGeneral:
RsaPrivateCrtKeyParameters rsK = (RsaPrivateCrtKeyParameters) keyPair.PrivateKey.Key;
secKey = new RsaSecretBcpgKey(rsK.Exponent, rsK.P, rsK.Q);
break;
case PublicKeyAlgorithmTag.Dsa:
DsaPrivateKeyParameters dsK = (DsaPrivateKeyParameters) keyPair.PrivateKey.Key;
secKey = new DsaSecretBcpgKey(dsK.X);
break;
case PublicKeyAlgorithmTag.ElGamalEncrypt:
case PublicKeyAlgorithmTag.ElGamalGeneral:
ElGamalPrivateKeyParameters esK = (ElGamalPrivateKeyParameters) keyPair.PrivateKey.Key;
secKey = new ElGamalSecretBcpgKey(esK.X);
break;
default:
throw new PgpException("unknown key class");
}
try
{
MemoryStream bOut = new MemoryStream();
BcpgOutputStream pOut = new BcpgOutputStream(bOut);
pOut.WriteObject(secKey);
byte[] keyData = bOut.ToArray();
byte[] checksumBytes = Checksum(useSHA1, keyData, keyData.Length);
pOut.Write(checksumBytes);
byte[] bOutData = bOut.ToArray();
if (encAlgorithm == SymmetricKeyAlgorithmTag.Null)
{
this.secret = new SecretKeyPacket(pubPk, encAlgorithm, null, null, bOutData);
}
else
{
S2k s2k;
byte[] iv;
byte[] encData = EncryptKeyData(bOutData, encAlgorithm, passPhrase, rand, out s2k, out iv);
int usage = useSHA1
? SecretKeyPacket.UsageSha1
: SecretKeyPacket.UsageChecksum;
this.secret = new SecretKeyPacket(pubPk, encAlgorithm, usage, s2k, iv, encData);
}
this.trust = null;
}
catch (PgpException e)
{
throw e;
}
catch (Exception e)
{
throw new PgpException("Exception encrypting key", e);
}
this.keySigs = new ArrayList();
}
public PgpSecretKey(
int certificationLevel,
PgpKeyPair keyPair,
string id,
SymmetricKeyAlgorithmTag encAlgorithm,
char[] passPhrase,
PgpSignatureSubpacketVector hashedPackets,
PgpSignatureSubpacketVector unhashedPackets,
SecureRandom rand)
: this(certificationLevel, keyPair, id, encAlgorithm, passPhrase, false, hashedPackets, unhashedPackets, rand)
{
}
public PgpSecretKey(
int certificationLevel,
PgpKeyPair keyPair,
string id,
SymmetricKeyAlgorithmTag encAlgorithm,
char[] passPhrase,
bool useSHA1,
PgpSignatureSubpacketVector hashedPackets,
PgpSignatureSubpacketVector unhashedPackets,
SecureRandom rand)
: this(keyPair, encAlgorithm, passPhrase, useSHA1, rand)
{
try
{
this.trust = null;
this.ids = new ArrayList();
ids.Add(id);
this.idTrusts = new ArrayList();
idTrusts.Add(null);
this.idSigs = new ArrayList();
PgpSignatureGenerator sGen = new PgpSignatureGenerator(
keyPair.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
//
// Generate the certification
//
sGen.InitSign(certificationLevel, keyPair.PrivateKey);
sGen.SetHashedSubpackets(hashedPackets);
sGen.SetUnhashedSubpackets(unhashedPackets);
PgpSignature certification = sGen.GenerateCertification(id, keyPair.PublicKey);
this.pub = PgpPublicKey.AddCertification(keyPair.PublicKey, id, certification);
ArrayList sigList = new ArrayList();
sigList.Add(certification);
idSigs.Add(sigList);
}
catch (PgpException e)
{
throw e;
}
catch (Exception e)
{
throw new PgpException("Exception encrypting key", e);
}
}
public PgpSecretKey(
int certificationLevel,
PublicKeyAlgorithmTag algorithm,
AsymmetricKeyParameter pubKey,
AsymmetricKeyParameter privKey,
DateTime time,
string id,
SymmetricKeyAlgorithmTag encAlgorithm,
char[] passPhrase,
PgpSignatureSubpacketVector hashedPackets,
PgpSignatureSubpacketVector unhashedPackets,
SecureRandom rand)
: this(certificationLevel,
new PgpKeyPair(algorithm, pubKey, privKey, time),
id, encAlgorithm, passPhrase, hashedPackets, unhashedPackets, rand)
{
}
public PgpSecretKey(
int certificationLevel,
PublicKeyAlgorithmTag algorithm,
AsymmetricKeyParameter pubKey,
AsymmetricKeyParameter privKey,
DateTime time,
string id,
SymmetricKeyAlgorithmTag encAlgorithm,
char[] passPhrase,
bool useSHA1,
PgpSignatureSubpacketVector hashedPackets,
PgpSignatureSubpacketVector unhashedPackets,
SecureRandom rand)
: this(certificationLevel, new PgpKeyPair(algorithm, pubKey, privKey, time), id, encAlgorithm, passPhrase, useSHA1, hashedPackets, unhashedPackets, rand)
{
}
/// <summary>True, if this key is marked as suitable for signature generation.</summary>
public bool IsSigningKey
{
get
{
switch (pub.Algorithm)
{
case PublicKeyAlgorithmTag.RsaGeneral:
case PublicKeyAlgorithmTag.RsaSign:
case PublicKeyAlgorithmTag.Dsa:
case PublicKeyAlgorithmTag.ECDsa:
case PublicKeyAlgorithmTag.ElGamalGeneral:
return true;
default:
return false;
}
}
}
/// <summary>True, if this is a master key.</summary>
public bool IsMasterKey
{
get { return subSigs == null; }
}
/// <summary>The algorithm the key is encrypted with.</summary>
public SymmetricKeyAlgorithmTag KeyEncryptionAlgorithm
{
get { return secret.EncAlgorithm; }
}
/// <summary>The key ID of the public key associated with this key.</summary>
public long KeyId
{
get { return pub.KeyId; }
}
/// <summary>The public key associated with this key.</summary>
public PgpPublicKey PublicKey
{
get { return pub; }
}
/// <summary>Allows enumeration of any user IDs associated with the key.</summary>
/// <returns>An <c>IEnumerable</c> of <c>string</c> objects.</returns>
public IEnumerable UserIds
{
get { return pub.GetUserIds(); }
}
/// <summary>Allows enumeration of any user attribute vectors associated with the key.</summary>
/// <returns>An <c>IEnumerable</c> of <c>string</c> objects.</returns>
public IEnumerable UserAttributes
{
get { return pub.GetUserAttributes(); }
}
private byte[] ExtractKeyData(
char[] passPhrase)
{
SymmetricKeyAlgorithmTag alg = secret.EncAlgorithm;
byte[] encData = secret.GetSecretKeyData();
if (alg == SymmetricKeyAlgorithmTag.Null)
return encData;
byte[] data;
IBufferedCipher c = null;
try
{
string cName = PgpUtilities.GetSymmetricCipherName(alg);
c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding");
}
catch (Exception e)
{
throw new PgpException("Exception creating cipher", e);
}
// TODO Factor this block out as 'encryptData'
try
{
KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(secret.EncAlgorithm, secret.S2k, passPhrase);
byte[] iv = secret.GetIV();
if (secret.PublicKeyPacket.Version == 4)
{
c.Init(false, new ParametersWithIV(key, iv));
data = c.DoFinal(encData);
bool useSHA1 = secret.S2kUsage == SecretKeyPacket.UsageSha1;
byte[] check = Checksum(useSHA1, data, (useSHA1) ? data.Length - 20 : data.Length - 2);
for (int i = 0; i != check.Length; i++)
{
if (check[i] != data[data.Length - check.Length + i])
{
throw new PgpException("Checksum mismatch at " + i + " of " + check.Length);
}
}
}
else // version 2 or 3, RSA only.
{
data = new byte[encData.Length];
//
// read in the four numbers
//
int pos = 0;
for (int i = 0; i != 4; i++)
{
c.Init(false, new ParametersWithIV(key, iv));
int encLen = (((encData[pos] << 8) | (encData[pos + 1] & 0xff)) + 7) / 8;
data[pos] = encData[pos];
data[pos + 1] = encData[pos + 1];
pos += 2;
c.DoFinal(encData, pos, encLen, data, pos);
pos += encLen;
if (i != 3)
{
Array.Copy(encData, pos - iv.Length, iv, 0, iv.Length);
}
}
//
// verify Checksum
//
int cs = ((encData[pos] << 8) & 0xff00) | (encData[pos + 1] & 0xff);
int calcCs = 0;
for (int j=0; j < data.Length-2; j++)
{
calcCs += data[j] & 0xff;
}
calcCs &= 0xffff;
if (calcCs != cs)
{
throw new PgpException("Checksum mismatch: passphrase wrong, expected "
+ cs.ToString("X")
+ " found " + calcCs.ToString("X"));
}
}
return data;
}
catch (PgpException e)
{
throw e;
}
catch (Exception e)
{
throw new PgpException("Exception decrypting key", e);
}
}
/// <summary>Extract a <c>PgpPrivateKey</c> from this secret key's encrypted contents.</summary>
public PgpPrivateKey ExtractPrivateKey(
char[] passPhrase)
{
byte[] secKeyData = secret.GetSecretKeyData();
if (secKeyData == null || secKeyData.Length < 1)
return null;
PublicKeyPacket pubPk = secret.PublicKeyPacket;
try
{
byte[] data = ExtractKeyData(passPhrase);
BcpgInputStream bcpgIn = BcpgInputStream.Wrap(new MemoryStream(data, false));
AsymmetricKeyParameter privateKey;
switch (pubPk.Algorithm)
{
case PublicKeyAlgorithmTag.RsaEncrypt:
case PublicKeyAlgorithmTag.RsaGeneral:
case PublicKeyAlgorithmTag.RsaSign:
RsaPublicBcpgKey rsaPub = (RsaPublicBcpgKey)pubPk.Key;
RsaSecretBcpgKey rsaPriv = new RsaSecretBcpgKey(bcpgIn);
RsaPrivateCrtKeyParameters rsaPrivSpec = new RsaPrivateCrtKeyParameters(
rsaPriv.Modulus,
rsaPub.PublicExponent,
rsaPriv.PrivateExponent,
rsaPriv.PrimeP,
rsaPriv.PrimeQ,
rsaPriv.PrimeExponentP,
rsaPriv.PrimeExponentQ,
rsaPriv.CrtCoefficient);
privateKey = rsaPrivSpec;
break;
case PublicKeyAlgorithmTag.Dsa:
DsaPublicBcpgKey dsaPub = (DsaPublicBcpgKey)pubPk.Key;
DsaSecretBcpgKey dsaPriv = new DsaSecretBcpgKey(bcpgIn);
DsaParameters dsaParams = new DsaParameters(dsaPub.P, dsaPub.Q, dsaPub.G);
privateKey = new DsaPrivateKeyParameters(dsaPriv.X, dsaParams);
break;
case PublicKeyAlgorithmTag.ElGamalEncrypt:
case PublicKeyAlgorithmTag.ElGamalGeneral:
ElGamalPublicBcpgKey elPub = (ElGamalPublicBcpgKey)pubPk.Key;
ElGamalSecretBcpgKey elPriv = new ElGamalSecretBcpgKey(bcpgIn);
ElGamalParameters elParams = new ElGamalParameters(elPub.P, elPub.G);
privateKey = new ElGamalPrivateKeyParameters(elPriv.X, elParams);
break;
default:
throw new PgpException("unknown public key algorithm encountered");
}
return new PgpPrivateKey(privateKey, KeyId);
}
catch (PgpException e)
{
throw e;
}
catch (Exception e)
{
throw new PgpException("Exception constructing key", e);
}
}
private static byte[] Checksum(
bool useSHA1,
byte[] bytes,
int length)
{
if (useSHA1)
{
try
{
IDigest dig = DigestUtilities.GetDigest("SHA1");
dig.BlockUpdate(bytes, 0, length);
return DigestUtilities.DoFinal(dig);
}
//catch (NoSuchAlgorithmException e)
catch (Exception e)
{
throw new PgpException("Can't find SHA-1", e);
}
}
else
{
int Checksum = 0;
for (int i = 0; i != length; i++)
{
Checksum += bytes[i];
}
return new byte[] { (byte)(Checksum >> 8), (byte)Checksum };
}
}
public byte[] GetEncoded()
{
MemoryStream bOut = new MemoryStream();
Encode(bOut);
return bOut.ToArray();
}
public void Encode(
Stream outStr)
{
BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr);
bcpgOut.WritePacket(secret);
if (trust != null)
{
bcpgOut.WritePacket(trust);
}
if (subSigs == null) // is not a sub key
{
foreach (PgpSignature keySig in keySigs)
{
keySig.Encode(bcpgOut);
}
for (int i = 0; i != ids.Count; i++)
{
if (ids[i] is string)
{
string id = (string) ids[i];
bcpgOut.WritePacket(new UserIdPacket(id));
}
else
{
PgpUserAttributeSubpacketVector v = (PgpUserAttributeSubpacketVector)ids[i];
bcpgOut.WritePacket(new UserAttributePacket(v.ToSubpacketArray()));
}
if (idTrusts[i] != null)
{
bcpgOut.WritePacket((ContainedPacket)idTrusts[i]);
}
foreach (PgpSignature sig in (ArrayList) idSigs[i])
{
sig.Encode(bcpgOut);
}
}
}
else
{
foreach (PgpSignature subSig in subSigs)
{
subSig.Encode(bcpgOut);
}
}
// TODO Check that this is right/necessary
//bcpgOut.Finish();
}
/// <summary>
/// Return a copy of the passed in secret key, encrypted using a new password
/// and the passed in algorithm.
/// </summary>
/// <param name="key">The PgpSecretKey to be copied.</param>
/// <param name="oldPassPhrase">The current password for the 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 PgpSecretKey CopyWithNewPassword(
PgpSecretKey key,
char[] oldPassPhrase,
char[] newPassPhrase,
SymmetricKeyAlgorithmTag newEncAlgorithm,
SecureRandom rand)
{
byte[] rawKeyData = key.ExtractKeyData(oldPassPhrase);
int s2kUsage = key.secret.S2kUsage;
byte[] iv = null;
S2k s2k = null;
byte[] keyData;
if (newEncAlgorithm == SymmetricKeyAlgorithmTag.Null)
{
s2kUsage = SecretKeyPacket.UsageNone;
if (key.secret.S2kUsage == SecretKeyPacket.UsageSha1) // SHA-1 hash, need to rewrite Checksum
{
keyData = new byte[rawKeyData.Length - 18];
Array.Copy(rawKeyData, 0, keyData, 0, keyData.Length - 2);
byte[] check = Checksum(false, keyData, keyData.Length - 2);
keyData[keyData.Length - 2] = check[0];
keyData[keyData.Length - 1] = check[1];
}
else
{
keyData = rawKeyData;
}
}
else
{
try
{
keyData = EncryptKeyData(rawKeyData, newEncAlgorithm, newPassPhrase, rand, out s2k, out iv);
}
catch (PgpException e)
{
throw e;
}
catch (Exception e)
{
throw new PgpException("Exception encrypting key", e);
}
}
SecretKeyPacket secret;
if (key.secret is SecretSubkeyPacket)
{
secret = new SecretSubkeyPacket(key.secret.PublicKeyPacket,
newEncAlgorithm, s2kUsage, s2k, iv, keyData);
}
else
{
secret = new SecretKeyPacket(key.secret.PublicKeyPacket,
newEncAlgorithm, s2kUsage, s2k, iv, keyData);
}
if (key.subSigs == null)
{
return new PgpSecretKey(secret, key.trust, key.keySigs, key.ids,
key.idTrusts, key.idSigs, key.pub);
}
return new PgpSecretKey(secret, key.trust, key.subSigs, key.pub);
}
private static byte[] EncryptKeyData(
byte[] rawKeyData,
SymmetricKeyAlgorithmTag encAlgorithm,
char[] passPhrase,
SecureRandom random,
out S2k s2k,
out byte[] iv)
{
IBufferedCipher c;
try
{
string cName = PgpUtilities.GetSymmetricCipherName(encAlgorithm);
c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding");
}
catch (Exception e)
{
throw new PgpException("Exception creating cipher", e);
}
byte[] s2kIV = new byte[8];
random.NextBytes(s2kIV);
s2k = new S2k(HashAlgorithmTag.Sha1, s2kIV, 0x60);
KeyParameter kp = PgpUtilities.MakeKeyFromPassPhrase(encAlgorithm, s2k, passPhrase);
iv = new byte[c.GetBlockSize()];
random.NextBytes(iv);
c.Init(true, new ParametersWithRandom(new ParametersWithIV(kp, iv), random));
return c.DoFinal(rawKeyData);
}
}
}

View File

@@ -0,0 +1,208 @@
using System;
using System.Collections;
using System.IO;
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 ArrayList keys;
internal PgpSecretKeyRing(
ArrayList keys)
{
this.keys = keys;
}
public PgpSecretKeyRing(
byte[] encoding)
: this(new MemoryStream(encoding))
{
}
public PgpSecretKeyRing(
Stream inputStream)
{
this.keys = new ArrayList();
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
ArrayList keySigs = ReadSignaturesAndTrust(bcpgInput);
ArrayList ids, idTrusts, idSigs;
ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs);
keys.Add(new PgpSecretKey(secret, trust, keySigs, ids, idTrusts, idSigs));
// Read subkeys
while (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);
ArrayList sigList = ReadSignaturesAndTrust(bcpgInput);
keys.Add(new PgpSecretKey(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;
}
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 k in keys)
{
k.Encode(outStr);
}
}
/// <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)
{
ArrayList keys = new ArrayList(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 && masterFound)
throw new ArgumentException("cannot add a master key to a ring that already has one");
keys.Add(secKey);
}
return new PgpSecretKeyRing(keys);
}
/// <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)
{
ArrayList keys = new ArrayList(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) : null;
}
}
}

View File

@@ -0,0 +1,270 @@
using System;
using System.Collections;
using System.Globalization;
using System.IO;
using Org.BouncyCastle.Utilities.Collections;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>
/// Often a PGP key ring file is made up of a succession of master/sub-key key rings.
/// If you want to read an entire secret key file in one hit this is the class for you.
/// </remarks>
public class PgpSecretKeyRingBundle
{
private readonly IDictionary secretRings;
private readonly ArrayList order;
private PgpSecretKeyRingBundle(
IDictionary secretRings,
ArrayList order)
{
this.secretRings = secretRings;
this.order = order;
}
public PgpSecretKeyRingBundle(
byte[] encoding)
: this(new MemoryStream(encoding, false))
{
}
/// <summary>Build a PgpSecretKeyRingBundle from the passed in input stream.</summary>
/// <param name="inputStream">Input stream containing data.</param>
/// <exception cref="IOException">If a problem parsing the stream occurs.</exception>
/// <exception cref="PgpException">If an object is encountered which isn't a PgpSecretKeyRing.</exception>
public PgpSecretKeyRingBundle(
Stream inputStream)
: this(new PgpObjectFactory(inputStream).AllPgpObjects())
{
}
public PgpSecretKeyRingBundle(
IEnumerable e)
{
this.secretRings = new Hashtable();
this.order = new ArrayList();
foreach (object obj in e)
{
PgpSecretKeyRing pgpSecret = obj as PgpSecretKeyRing;
if (pgpSecret == null)
{
throw new PgpException(obj.GetType().FullName + " found where PgpSecretKeyRing expected");
}
long key = pgpSecret.GetPublicKey().KeyId;
secretRings.Add(key, pgpSecret);
order.Add(key);
}
}
[Obsolete("Use 'Count' property instead")]
public int Size
{
get { return order.Count; }
}
/// <summary>Return the number of rings in this collection.</summary>
public int Count
{
get { return order.Count; }
}
/// <summary>Allow enumeration of the secret key rings making up this collection.</summary>
public IEnumerable GetKeyRings()
{
return new EnumerableProxy(secretRings.Values);
}
/// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary>
/// <param name="userId">The user ID to be matched.</param>
/// <returns>An <c>IEnumerable</c> of key rings which matched (possibly none).</returns>
public IEnumerable GetKeyRings(
string userId)
{
return GetKeyRings(userId, false, false);
}
/// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary>
/// <param name="userId">The user ID to be matched.</param>
/// <param name="matchPartial">If true, userId need only be a substring of an actual ID string to match.</param>
/// <returns>An <c>IEnumerable</c> of key rings which matched (possibly none).</returns>
public IEnumerable GetKeyRings(
string userId,
bool matchPartial)
{
return GetKeyRings(userId, matchPartial, false);
}
/// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary>
/// <param name="userId">The user ID to be matched.</param>
/// <param name="matchPartial">If true, userId need only be a substring of an actual ID string to match.</param>
/// <param name="ignoreCase">If true, case is ignored in user ID comparisons.</param>
/// <returns>An <c>IEnumerable</c> of key rings which matched (possibly none).</returns>
public IEnumerable GetKeyRings(
string userId,
bool matchPartial,
bool ignoreCase)
{
IList rings = new ArrayList();
if (ignoreCase)
{
userId = userId.ToLower(CultureInfo.InvariantCulture);
}
foreach (PgpSecretKeyRing secRing in GetKeyRings())
{
foreach (string nextUserID in secRing.GetSecretKey().UserIds)
{
string next = nextUserID;
if (ignoreCase)
{
next = next.ToLower(CultureInfo.InvariantCulture);
}
if (matchPartial)
{
if (next.IndexOf(userId) > -1)
{
rings.Add(secRing);
}
}
else
{
if (next.Equals(userId))
{
rings.Add(secRing);
}
}
}
}
return new EnumerableProxy(rings);
}
/// <summary>Return the PGP secret key associated with the given key id.</summary>
/// <param name="keyId">The ID of the secret key to return.</param>
public PgpSecretKey GetSecretKey(
long keyId)
{
foreach (PgpSecretKeyRing secRing in GetKeyRings())
{
PgpSecretKey sec = secRing.GetSecretKey(keyId);
if (sec != null)
{
return sec;
}
}
return null;
}
/// <summary>Return the secret key ring which contains the key referred to by keyId</summary>
/// <param name="keyId">The ID of the secret key</param>
public PgpSecretKeyRing GetSecretKeyRing(
long keyId)
{
long id = keyId;
if (secretRings.Contains(id))
{
return (PgpSecretKeyRing) secretRings[id];
}
foreach (PgpSecretKeyRing secretRing in GetKeyRings())
{
PgpSecretKey secret = secretRing.GetSecretKey(keyId);
if (secret != null)
{
return secretRing;
}
}
return null;
}
public byte[] GetEncoded()
{
MemoryStream bOut = new MemoryStream();
Encode(bOut);
return bOut.ToArray();
}
public void Encode(
Stream outStr)
{
BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr);
foreach (long key in order)
{
PgpSecretKeyRing pub = (PgpSecretKeyRing) secretRings[key];
pub.Encode(bcpgOut);
}
}
/// <summary>
/// Return a new bundle containing the contents of the passed in bundle and
/// the passed in secret key ring.
/// </summary>
/// <param name="bundle">The <c>PgpSecretKeyRingBundle</c> the key ring is to be added to.</param>
/// <param name="secretKeyRing">The key ring to be added.</param>
/// <returns>A new <c>PgpSecretKeyRingBundle</c> merging the current one with the passed in key ring.</returns>
/// <exception cref="ArgumentException">If the keyId for the passed in key ring is already present.</exception>
public static PgpSecretKeyRingBundle AddSecretKeyRing(
PgpSecretKeyRingBundle bundle,
PgpSecretKeyRing secretKeyRing)
{
long key = secretKeyRing.GetPublicKey().KeyId;
if (bundle.secretRings.Contains(key))
{
throw new ArgumentException("Collection already contains a key with a keyId for the passed in ring.");
}
IDictionary newSecretRings = new Hashtable(bundle.secretRings);
ArrayList newOrder = new ArrayList(bundle.order);
newSecretRings[key] = secretKeyRing;
newOrder.Add(key);
return new PgpSecretKeyRingBundle(newSecretRings, newOrder);
}
/// <summary>
/// Return a new bundle containing the contents of the passed in bundle with
/// the passed in secret key ring removed.
/// </summary>
/// <param name="bundle">The <c>PgpSecretKeyRingBundle</c> the key ring is to be removed from.</param>
/// <param name="secretKeyRing">The key ring to be removed.</param>
/// <returns>A new <c>PgpSecretKeyRingBundle</c> not containing the passed in key ring.</returns>
/// <exception cref="ArgumentException">If the keyId for the passed in key ring is not present.</exception>
public static PgpSecretKeyRingBundle RemoveSecretKeyRing(
PgpSecretKeyRingBundle bundle,
PgpSecretKeyRing secretKeyRing)
{
long key = secretKeyRing.GetPublicKey().KeyId;
if (!bundle.secretRings.Contains(key))
{
throw new ArgumentException("Collection does not contain a key with a keyId for the passed in ring.");
}
IDictionary newSecretRings = new Hashtable(bundle.secretRings);
ArrayList newOrder = new ArrayList(bundle.order);
newSecretRings.Remove(key);
newOrder.Remove(key);
return new PgpSecretKeyRingBundle(newSecretRings, newOrder);
}
}
}

View File

@@ -0,0 +1,409 @@
using System;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Date;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>A PGP signature object.</remarks>
public class PgpSignature
{
public const int BinaryDocument = 0x00;
public const int CanonicalTextDocument = 0x01;
public const int StandAlone = 0x02;
public const int DefaultCertification = 0x10;
public const int NoCertification = 0x11;
public const int CasualCertification = 0x12;
public const int PositiveCertification = 0x13;
public const int SubkeyBinding = 0x18;
public const int DirectKey = 0x1f;
public const int KeyRevocation = 0x20;
public const int SubkeyRevocation = 0x28;
public const int CertificationRevocation = 0x30;
public const int Timestamp = 0x40;
private readonly SignaturePacket sigPck;
private readonly int signatureType;
private readonly TrustPacket trustPck;
private ISigner sig;
private byte lastb; // Initial value anything but '\r'
internal PgpSignature(
BcpgInputStream bcpgInput)
: this((SignaturePacket)bcpgInput.ReadPacket())
{
}
internal PgpSignature(
SignaturePacket sigPacket)
: this(sigPacket, null)
{
}
internal PgpSignature(
SignaturePacket sigPacket,
TrustPacket trustPacket)
{
if (sigPacket == null)
throw new ArgumentNullException("sigPacket");
this.sigPck = sigPacket;
this.signatureType = sigPck.SignatureType;
this.trustPck = trustPacket;
}
private void GetSig()
{
this.sig = SignerUtilities.GetSigner(
PgpUtilities.GetSignatureName(sigPck.KeyAlgorithm, sigPck.HashAlgorithm));
}
/// <summary>The OpenPGP version number for this signature.</summary>
public int Version
{
get { return sigPck.Version; }
}
/// <summary>The key algorithm associated with this signature.</summary>
public PublicKeyAlgorithmTag KeyAlgorithm
{
get { return sigPck.KeyAlgorithm; }
}
/// <summary>The hash algorithm associated with this signature.</summary>
public HashAlgorithmTag HashAlgorithm
{
get { return sigPck.HashAlgorithm; }
}
public void InitVerify(
PgpPublicKey pubKey)
{
lastb = 0;
if (sig == null)
{
GetSig();
}
try
{
sig.Init(false, pubKey.GetKey());
}
catch (InvalidKeyException e)
{
throw new PgpException("invalid key.", e);
}
}
public void Update(
byte b)
{
if (signatureType == CanonicalTextDocument)
{
doCanonicalUpdateByte(b);
}
else
{
sig.Update(b);
}
}
private void doCanonicalUpdateByte(
byte b)
{
if (b == '\r')
{
doUpdateCRLF();
}
else if (b == '\n')
{
if (lastb != '\r')
{
doUpdateCRLF();
}
}
else
{
sig.Update(b);
}
lastb = b;
}
private void doUpdateCRLF()
{
sig.Update((byte)'\r');
sig.Update((byte)'\n');
}
public void Update(
params byte[] bytes)
{
Update(bytes, 0, bytes.Length);
}
public void Update(
byte[] bytes,
int off,
int length)
{
if (signatureType == CanonicalTextDocument)
{
int finish = off + length;
for (int i = off; i != finish; i++)
{
doCanonicalUpdateByte(bytes[i]);
}
}
else
{
sig.BlockUpdate(bytes, off, length);
}
}
public bool Verify()
{
byte[] trailer = GetSignatureTrailer();
sig.BlockUpdate(trailer, 0, trailer.Length);
return sig.VerifySignature(GetSignature());
}
private void UpdateWithIdData(
int header,
byte[] idBytes)
{
this.Update(
(byte) header,
(byte)(idBytes.Length >> 24),
(byte)(idBytes.Length >> 16),
(byte)(idBytes.Length >> 8),
(byte)(idBytes.Length));
this.Update(idBytes);
}
private void UpdateWithPublicKey(
PgpPublicKey key)
{
byte[] keyBytes = GetEncodedPublicKey(key);
this.Update(
(byte) 0x99,
(byte)(keyBytes.Length >> 8),
(byte)(keyBytes.Length));
this.Update(keyBytes);
}
/// <summary>
/// Verify the signature as certifying the passed in public key as associated
/// with the passed in user attributes.
/// </summary>
/// <param name="userAttributes">User attributes the key was stored under.</param>
/// <param name="key">The key to be verified.</param>
/// <returns>True, if the signature matches, false otherwise.</returns>
public bool VerifyCertification(
PgpUserAttributeSubpacketVector userAttributes,
PgpPublicKey key)
{
UpdateWithPublicKey(key);
//
// hash in the userAttributes
//
try
{
MemoryStream bOut = new MemoryStream();
foreach (UserAttributeSubpacket packet in userAttributes.ToSubpacketArray())
{
packet.Encode(bOut);
}
UpdateWithIdData(0xd1, bOut.ToArray());
}
catch (IOException e)
{
throw new PgpException("cannot encode subpacket array", e);
}
this.Update(sigPck.GetSignatureTrailer());
return sig.VerifySignature(this.GetSignature());
}
/// <summary>
/// Verify the signature as certifying the passed in public key as associated
/// with the passed in ID.
/// </summary>
/// <param name="id">ID the key was stored under.</param>
/// <param name="key">The key to be verified.</param>
/// <returns>True, if the signature matches, false otherwise.</returns>
public bool VerifyCertification(
string id,
PgpPublicKey key)
{
UpdateWithPublicKey(key);
//
// hash in the id
//
UpdateWithIdData(0xb4, Strings.ToByteArray(id));
Update(sigPck.GetSignatureTrailer());
return sig.VerifySignature(GetSignature());
}
/// <summary>Verify a certification for the passed in key against the passed in master key.</summary>
/// <param name="masterKey">The key we are verifying against.</param>
/// <param name="pubKey">The key we are verifying.</param>
/// <returns>True, if the certification is valid, false otherwise.</returns>
public bool VerifyCertification(
PgpPublicKey masterKey,
PgpPublicKey pubKey)
{
UpdateWithPublicKey(masterKey);
UpdateWithPublicKey(pubKey);
Update(sigPck.GetSignatureTrailer());
return sig.VerifySignature(GetSignature());
}
/// <summary>Verify a key certification, such as revocation, for the passed in key.</summary>
/// <param name="pubKey">The key we are checking.</param>
/// <returns>True, if the certification is valid, false otherwise.</returns>
public bool VerifyCertification(
PgpPublicKey pubKey)
{
if (SignatureType != KeyRevocation
&& SignatureType != SubkeyRevocation)
{
throw new InvalidOperationException("signature is not a key signature");
}
UpdateWithPublicKey(pubKey);
Update(sigPck.GetSignatureTrailer());
return sig.VerifySignature(GetSignature());
}
public int SignatureType
{
get { return sigPck.SignatureType; }
}
/// <summary>The ID of the key that created the signature.</summary>
public long KeyId
{
get { return sigPck.KeyId; }
}
[Obsolete("Use 'CreationTime' property instead")]
public DateTime GetCreationTime()
{
return CreationTime;
}
/// <summary>The creation time of this signature.</summary>
public DateTime CreationTime
{
get { return DateTimeUtilities.UnixMsToDateTime(sigPck.CreationTime); }
}
public byte[] GetSignatureTrailer()
{
return sigPck.GetSignatureTrailer();
}
public PgpSignatureSubpacketVector GetHashedSubPackets()
{
return createSubpacketVector(sigPck.GetHashedSubPackets());
}
public PgpSignatureSubpacketVector GetUnhashedSubPackets()
{
return createSubpacketVector(sigPck.GetUnhashedSubPackets());
}
private PgpSignatureSubpacketVector createSubpacketVector(SignatureSubpacket[] pcks)
{
return pcks == null ? null : new PgpSignatureSubpacketVector(pcks);
}
public byte[] GetSignature()
{
MPInteger[] sigValues = sigPck.GetSignature();
byte[] signature;
if (sigValues != null)
{
if (sigValues.Length == 1) // an RSA signature
{
signature = sigValues[0].Value.ToByteArrayUnsigned();
}
else
{
try
{
signature = new DerSequence(
new DerInteger(sigValues[0].Value),
new DerInteger(sigValues[1].Value)).GetEncoded();
}
catch (IOException e)
{
throw new PgpException("exception encoding DSA sig.", e);
}
}
}
else
{
signature = sigPck.GetSignatureBytes();
}
return signature;
}
// TODO Handle the encoding stuff by subclassing BcpgObject?
public byte[] GetEncoded()
{
MemoryStream bOut = new MemoryStream();
Encode(bOut);
return bOut.ToArray();
}
public void Encode(
Stream outStream)
{
BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStream);
bcpgOut.WritePacket(sigPck);
if (trustPck != null)
{
bcpgOut.WritePacket(trustPck);
}
}
private byte[] GetEncodedPublicKey(
PgpPublicKey pubKey)
{
try
{
return pubKey.publicPk.GetEncodedContents();
}
catch (IOException e)
{
throw new PgpException("exception preparing key.", e);
}
}
}
}

View File

@@ -0,0 +1,393 @@
using System;
using System.IO;
using Org.BouncyCastle.Bcpg.Sig;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>Generator for PGP signatures.</remarks>
// TODO Should be able to implement ISigner?
public class PgpSignatureGenerator
{
private static readonly SignatureSubpacket[] EmptySignatureSubpackets = new SignatureSubpacket[0];
private PublicKeyAlgorithmTag keyAlgorithm;
private HashAlgorithmTag hashAlgorithm;
private PgpPrivateKey privKey;
private ISigner sig;
private IDigest dig;
private int signatureType;
private byte lastb;
private SignatureSubpacket[] unhashed = EmptySignatureSubpackets;
private SignatureSubpacket[] hashed = EmptySignatureSubpackets;
/// <summary>Create a generator for the passed in keyAlgorithm and hashAlgorithm codes.</summary>
public PgpSignatureGenerator(
PublicKeyAlgorithmTag keyAlgorithm,
HashAlgorithmTag hashAlgorithm)
{
this.keyAlgorithm = keyAlgorithm;
this.hashAlgorithm = hashAlgorithm;
dig = DigestUtilities.GetDigest(PgpUtilities.GetDigestName(hashAlgorithm));
sig = SignerUtilities.GetSigner(PgpUtilities.GetSignatureName(keyAlgorithm, hashAlgorithm));
}
/// <summary>Initialise the generator for signing.</summary>
public void InitSign(
int sigType,
PgpPrivateKey key)
{
InitSign(sigType, key, null);
}
/// <summary>Initialise the generator for signing.</summary>
public void InitSign(
int sigType,
PgpPrivateKey key,
SecureRandom random)
{
this.privKey = key;
this.signatureType = sigType;
try
{
ICipherParameters cp = key.Key;
if (random != null)
{
cp = new ParametersWithRandom(key.Key, random);
}
sig.Init(true, cp);
}
catch (InvalidKeyException e)
{
throw new PgpException("invalid key.", e);
}
dig.Reset();
lastb = 0;
}
public void Update(
byte b)
{
if (signatureType == PgpSignature.CanonicalTextDocument)
{
doCanonicalUpdateByte(b);
}
else
{
doUpdateByte(b);
}
}
private void doCanonicalUpdateByte(
byte b)
{
if (b == '\r')
{
doUpdateCRLF();
}
else if (b == '\n')
{
if (lastb != '\r')
{
doUpdateCRLF();
}
}
else
{
doUpdateByte(b);
}
lastb = b;
}
private void doUpdateCRLF()
{
doUpdateByte((byte)'\r');
doUpdateByte((byte)'\n');
}
private void doUpdateByte(
byte b)
{
sig.Update(b);
dig.Update(b);
}
public void Update(
params byte[] b)
{
Update(b, 0, b.Length);
}
public void Update(
byte[] b,
int off,
int len)
{
if (signatureType == PgpSignature.CanonicalTextDocument)
{
int finish = off + len;
for (int i = off; i != finish; i++)
{
doCanonicalUpdateByte(b[i]);
}
}
else
{
sig.BlockUpdate(b, off, len);
dig.BlockUpdate(b, off, len);
}
}
public void SetHashedSubpackets(
PgpSignatureSubpacketVector hashedPackets)
{
hashed = hashedPackets == null
? EmptySignatureSubpackets
: hashedPackets.ToSubpacketArray();
}
public void SetUnhashedSubpackets(
PgpSignatureSubpacketVector unhashedPackets)
{
unhashed = unhashedPackets == null
? EmptySignatureSubpackets
: unhashedPackets.ToSubpacketArray();
}
/// <summary>Return the one pass header associated with the current signature.</summary>
public PgpOnePassSignature GenerateOnePassVersion(
bool isNested)
{
return new PgpOnePassSignature(
new OnePassSignaturePacket(
signatureType, hashAlgorithm, keyAlgorithm, privKey.KeyId, isNested));
}
/// <summary>Return a signature object containing the current signature state.</summary>
public PgpSignature Generate()
{
SignatureSubpacket[] hPkts = hashed, unhPkts = unhashed;
if (!packetPresent(hashed, SignatureSubpacketTag.CreationTime))
{
hPkts = insertSubpacket(hPkts, new SignatureCreationTime(false, DateTime.UtcNow));
}
if (!packetPresent(hashed, SignatureSubpacketTag.IssuerKeyId)
&& !packetPresent(unhashed, SignatureSubpacketTag.IssuerKeyId))
{
unhPkts = insertSubpacket(unhPkts, new IssuerKeyId(false, privKey.KeyId));
}
int version = 4;
byte[] hData;
try
{
MemoryStream hOut = new MemoryStream();
for (int i = 0; i != hPkts.Length; i++)
{
hPkts[i].Encode(hOut);
}
byte[] data = hOut.ToArray();
MemoryStream sOut = new MemoryStream(data.Length + 6);
sOut.WriteByte((byte)version);
sOut.WriteByte((byte)signatureType);
sOut.WriteByte((byte)keyAlgorithm);
sOut.WriteByte((byte)hashAlgorithm);
sOut.WriteByte((byte)(data.Length >> 8));
sOut.WriteByte((byte)data.Length);
sOut.Write(data, 0, data.Length);
hData = sOut.ToArray();
}
catch (IOException e)
{
throw new PgpException("exception encoding hashed data.", e);
}
sig.BlockUpdate(hData, 0, hData.Length);
dig.BlockUpdate(hData, 0, hData.Length);
hData = new byte[]
{
(byte) version,
0xff,
(byte)(hData.Length >> 24),
(byte)(hData.Length >> 16),
(byte)(hData.Length >> 8),
(byte) hData.Length
};
sig.BlockUpdate(hData, 0, hData.Length);
dig.BlockUpdate(hData, 0, hData.Length);
byte[] sigBytes = sig.GenerateSignature();
byte[] digest = DigestUtilities.DoFinal(dig);
byte[] fingerPrint = new byte[] { digest[0], digest[1] };
// an RSA signature
bool isRsa = keyAlgorithm == PublicKeyAlgorithmTag.RsaSign
|| keyAlgorithm == PublicKeyAlgorithmTag.RsaGeneral;
MPInteger[] sigValues = isRsa
? PgpUtilities.RsaSigToMpi(sigBytes)
: PgpUtilities.DsaSigToMpi(sigBytes);
return new PgpSignature(
new SignaturePacket(signatureType, privKey.KeyId, keyAlgorithm,
hashAlgorithm, hPkts, unhPkts, fingerPrint, sigValues));
}
/// <summary>Generate a certification for the passed in ID and key.</summary>
/// <param name="id">The ID we are certifying against the public key.</param>
/// <param name="pubKey">The key we are certifying against the ID.</param>
/// <returns>The certification.</returns>
public PgpSignature GenerateCertification(
string id,
PgpPublicKey pubKey)
{
UpdateWithPublicKey(pubKey);
//
// hash in the id
//
UpdateWithIdData(0xb4, Strings.ToByteArray(id));
return Generate();
}
/// <summary>Generate a certification for the passed in userAttributes.</summary>
/// <param name="userAttributes">The ID we are certifying against the public key.</param>
/// <param name="pubKey">The key we are certifying against the ID.</param>
/// <returns>The certification.</returns>
public PgpSignature GenerateCertification(
PgpUserAttributeSubpacketVector userAttributes,
PgpPublicKey pubKey)
{
UpdateWithPublicKey(pubKey);
//
// hash in the attributes
//
try
{
MemoryStream bOut = new MemoryStream();
foreach (UserAttributeSubpacket packet in userAttributes.ToSubpacketArray())
{
packet.Encode(bOut);
}
UpdateWithIdData(0xd1, bOut.ToArray());
}
catch (IOException e)
{
throw new PgpException("cannot encode subpacket array", e);
}
return this.Generate();
}
/// <summary>Generate a certification for the passed in key against the passed in master key.</summary>
/// <param name="masterKey">The key we are certifying against.</param>
/// <param name="pubKey">The key we are certifying.</param>
/// <returns>The certification.</returns>
public PgpSignature GenerateCertification(
PgpPublicKey masterKey,
PgpPublicKey pubKey)
{
UpdateWithPublicKey(masterKey);
UpdateWithPublicKey(pubKey);
return Generate();
}
/// <summary>Generate a certification, such as a revocation, for the passed in key.</summary>
/// <param name="pubKey">The key we are certifying.</param>
/// <returns>The certification.</returns>
public PgpSignature GenerateCertification(
PgpPublicKey pubKey)
{
UpdateWithPublicKey(pubKey);
return Generate();
}
private byte[] GetEncodedPublicKey(
PgpPublicKey pubKey)
{
try
{
return pubKey.publicPk.GetEncodedContents();
}
catch (IOException e)
{
throw new PgpException("exception preparing key.", e);
}
}
private bool packetPresent(
SignatureSubpacket[] packets,
SignatureSubpacketTag type)
{
for (int i = 0; i != packets.Length; i++)
{
if (packets[i].SubpacketType == type)
{
return true;
}
}
return false;
}
private SignatureSubpacket[] insertSubpacket(
SignatureSubpacket[] packets,
SignatureSubpacket subpacket)
{
SignatureSubpacket[] tmp = new SignatureSubpacket[packets.Length + 1];
tmp[0] = subpacket;
packets.CopyTo(tmp, 1);
return tmp;
}
private void UpdateWithIdData(
int header,
byte[] idBytes)
{
this.Update(
(byte) header,
(byte)(idBytes.Length >> 24),
(byte)(idBytes.Length >> 16),
(byte)(idBytes.Length >> 8),
(byte)(idBytes.Length));
this.Update(idBytes);
}
private void UpdateWithPublicKey(
PgpPublicKey key)
{
byte[] keyBytes = GetEncodedPublicKey(key);
this.Update(
(byte) 0x99,
(byte)(keyBytes.Length >> 8),
(byte)(keyBytes.Length));
this.Update(keyBytes);
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>A list of PGP signatures - normally in the signature block after literal data.</remarks>
public class PgpSignatureList
: PgpObject
{
private PgpSignature[] sigs;
public PgpSignatureList(
PgpSignature[] sigs)
{
this.sigs = (PgpSignature[]) sigs.Clone();
}
public PgpSignatureList(
PgpSignature sig)
{
this.sigs = new PgpSignature[]{ sig };
}
public PgpSignature this[int index]
{
get { return sigs[index]; }
}
[Obsolete("Use 'object[index]' syntax instead")]
public PgpSignature Get(
int index)
{
return this[index];
}
[Obsolete("Use 'Count' property instead")]
public int Size
{
get { return sigs.Length; }
}
public int Count
{
get { return sigs.Length; }
}
public bool IsEmpty
{
get { return (sigs.Length == 0); }
}
}
}

View File

@@ -0,0 +1,141 @@
using System;
using System.Collections;
using Org.BouncyCastle.Bcpg.Sig;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>Generator for signature subpackets.</remarks>
public class PgpSignatureSubpacketGenerator
{
private ArrayList list = new ArrayList();
public void SetRevocable(
bool isCritical,
bool isRevocable)
{
list.Add(new Revocable(isCritical, isRevocable));
}
public void SetExportable(
bool isCritical,
bool isExportable)
{
list.Add(new Exportable(isCritical, isExportable));
}
/// <summary>
/// Add a TrustSignature packet to the signature. The values for depth and trust are largely
/// installation dependent but there are some guidelines in RFC 4880 - 5.2.3.13.
/// </summary>
/// <param name="isCritical">true if the packet is critical.</param>
/// <param name="depth">depth level.</param>
/// <param name="trustAmount">trust amount.</param>
public void SetTrust(
bool isCritical,
int depth,
int trustAmount)
{
list.Add(new TrustSignature(isCritical, depth, trustAmount));
}
/// <summary>
/// Set the number of seconds a key is valid for after the time of its creation.
/// A value of zero means the key never expires.
/// </summary>
/// <param name="isCritical">True, if should be treated as critical, false otherwise.</param>
/// <param name="seconds">The number of seconds the key is valid, or zero if no expiry.</param>
public void SetKeyExpirationTime(
bool isCritical,
long seconds)
{
list.Add(new KeyExpirationTime(isCritical, seconds));
}
/// <summary>
/// Set the number of seconds a signature is valid for after the time of its creation.
/// A value of zero means the signature never expires.
/// </summary>
/// <param name="isCritical">True, if should be treated as critical, false otherwise.</param>
/// <param name="seconds">The number of seconds the signature is valid, or zero if no expiry.</param>
public void SetSignatureExpirationTime(
bool isCritical,
long seconds)
{
list.Add(new SignatureExpirationTime(isCritical, seconds));
}
/// <summary>
/// Set the creation time for the signature.
/// <p>
/// Note: this overrides the generation of a creation time when the signature
/// is generated.</p>
/// </summary>
public void SetSignatureCreationTime(
bool isCritical,
DateTime date)
{
list.Add(new SignatureCreationTime(isCritical, date));
}
public void SetPreferredHashAlgorithms(
bool isCritical,
int[] algorithms)
{
list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredHashAlgorithms, isCritical, algorithms));
}
public void SetPreferredSymmetricAlgorithms(
bool isCritical,
int[] algorithms)
{
list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredSymmetricAlgorithms, isCritical, algorithms));
}
public void SetPreferredCompressionAlgorithms(
bool isCritical,
int[] algorithms)
{
list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredCompressionAlgorithms, isCritical, algorithms));
}
public void SetKeyFlags(
bool isCritical,
int flags)
{
list.Add(new KeyFlags(isCritical, flags));
}
public void SetSignerUserId(
bool isCritical,
string userId)
{
if (userId == null)
throw new ArgumentNullException("userId");
list.Add(new SignerUserId(isCritical, userId));
}
public void SetPrimaryUserId(
bool isCritical,
bool isPrimaryUserId)
{
list.Add(new PrimaryUserId(isCritical, isPrimaryUserId));
}
public void SetNotationData(
bool isCritical,
bool isHumanReadable,
string notationName,
string notationValue)
{
list.Add(new NotationData(isCritical, isHumanReadable, notationName, notationValue));
}
public PgpSignatureSubpacketVector Generate()
{
return new PgpSignatureSubpacketVector(
(SignatureSubpacket[]) list.ToArray(typeof(SignatureSubpacket)));
}
}
}

View File

@@ -0,0 +1,193 @@
using System;
using System.Collections;
using Org.BouncyCastle.Bcpg.Sig;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>Container for a list of signature subpackets.</remarks>
public class PgpSignatureSubpacketVector
{
private readonly SignatureSubpacket[] packets;
internal PgpSignatureSubpacketVector(
SignatureSubpacket[] packets)
{
this.packets = packets;
}
public SignatureSubpacket GetSubpacket(
SignatureSubpacketTag type)
{
for (int i = 0; i != packets.Length; i++)
{
if (packets[i].SubpacketType == type)
{
return packets[i];
}
}
return null;
}
/**
* Return all signature subpackets of the passed in type.
* @param type subpacket type code
* @return an array of zero or more matching subpackets.
*/
public SignatureSubpacket[] GetSubpackets(
SignatureSubpacketTag type)
{
ArrayList list = new ArrayList();
for (int i = 0; i != packets.Length; i++)
{
if (packets[i].SubpacketType == type)
{
list.Add(packets[i]);
}
}
return (SignatureSubpacket[]) list.ToArray(typeof(SignatureSubpacket));
}
public NotationData[] GetNotationDataOccurences()
{
SignatureSubpacket[] notations = GetSubpackets(SignatureSubpacketTag.NotationData);
NotationData[] vals = new NotationData[notations.Length];
for (int i = 0; i < notations.Length; i++)
{
vals[i] = (NotationData) notations[i];
}
return vals;
}
public long GetIssuerKeyId()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.IssuerKeyId);
return p == null ? 0 : ((IssuerKeyId) p).KeyId;
}
public bool HasSignatureCreationTime()
{
return GetSubpacket(SignatureSubpacketTag.CreationTime) != null;
}
public DateTime GetSignatureCreationTime()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.CreationTime);
if (p == null)
{
throw new PgpException("SignatureCreationTime not available");
}
return ((SignatureCreationTime)p).GetTime();
}
/// <summary>
/// Return the number of seconds a signature is valid for after its creation date.
/// A value of zero means the signature never expires.
/// </summary>
/// <returns>Seconds a signature is valid for.</returns>
public long GetSignatureExpirationTime()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.ExpireTime);
return p == null ? 0 : ((SignatureExpirationTime) p).Time;
}
/// <summary>
/// Return the number of seconds a key is valid for after its creation date.
/// A value of zero means the key never expires.
/// </summary>
/// <returns>Seconds a signature is valid for.</returns>
public long GetKeyExpirationTime()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.KeyExpireTime);
return p == null ? 0 : ((KeyExpirationTime) p).Time;
}
public int[] GetPreferredHashAlgorithms()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredHashAlgorithms);
return p == null ? null : ((PreferredAlgorithms) p).GetPreferences();
}
public int[] GetPreferredSymmetricAlgorithms()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredSymmetricAlgorithms);
return p == null ? null : ((PreferredAlgorithms) p).GetPreferences();
}
public int[] GetPreferredCompressionAlgorithms()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredCompressionAlgorithms);
return p == null ? null : ((PreferredAlgorithms) p).GetPreferences();
}
public int GetKeyFlags()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.KeyFlags);
return p == null ? 0 : ((KeyFlags) p).Flags;
}
public string GetSignerUserId()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.SignerUserId);
return p == null ? null : ((SignerUserId) p).GetId();
}
public SignatureSubpacketTag[] GetCriticalTags()
{
int count = 0;
for (int i = 0; i != packets.Length; i++)
{
if (packets[i].IsCritical())
{
count++;
}
}
SignatureSubpacketTag[] list = new SignatureSubpacketTag[count];
count = 0;
for (int i = 0; i != packets.Length; i++)
{
if (packets[i].IsCritical())
{
list[count++] = packets[i].SubpacketType;
}
}
return list;
}
[Obsolete("Use 'Count' property instead")]
public int Size
{
get { return packets.Length; }
}
/// <summary>Return the number of packets this vector contains.</summary>
public int Count
{
get { return packets.Length; }
}
internal SignatureSubpacket[] ToSubpacketArray()
{
return packets;
}
}
}

View File

@@ -0,0 +1,81 @@
using Org.BouncyCastle.Bcpg.Attr;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>Container for a list of user attribute subpackets.</remarks>
public class PgpUserAttributeSubpacketVector
{
private readonly UserAttributeSubpacket[] packets;
internal PgpUserAttributeSubpacketVector(
UserAttributeSubpacket[] packets)
{
this.packets = packets;
}
public UserAttributeSubpacket GetSubpacket(
UserAttributeSubpacketTag type)
{
for (int i = 0; i != packets.Length; i++)
{
if (packets[i].SubpacketType == type)
{
return packets[i];
}
}
return null;
}
public ImageAttrib GetImageAttribute()
{
UserAttributeSubpacket p = GetSubpacket(UserAttributeSubpacketTag.ImageAttribute);
return p == null ? null : (ImageAttrib) p;
}
internal UserAttributeSubpacket[] ToSubpacketArray()
{
return packets;
}
public override bool Equals(
object obj)
{
if (obj == this)
return true;
PgpUserAttributeSubpacketVector other = obj as PgpUserAttributeSubpacketVector;
if (other == null)
return false;
if (other.packets.Length != packets.Length)
{
return false;
}
for (int i = 0; i != packets.Length; i++)
{
if (!other.packets[i].Equals(packets[i]))
{
return false;
}
}
return true;
}
public override int GetHashCode()
{
int code = 0;
foreach (object o in packets)
{
code ^= o.GetHashCode();
}
return code;
}
}
}

View File

@@ -0,0 +1,431 @@
using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.Utilities.IO;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>Basic utility class.</remarks>
public sealed class PgpUtilities
{
private PgpUtilities()
{
}
public static MPInteger[] DsaSigToMpi(
byte[] encoding)
{
DerInteger i1, i2;
try
{
Asn1Sequence s = (Asn1Sequence) Asn1Object.FromByteArray(encoding);
i1 = (DerInteger) s[0];
i2 = (DerInteger) s[1];
}
catch (IOException e)
{
throw new PgpException("exception encoding signature", e);
}
return new MPInteger[]{ new MPInteger(i1.Value), new MPInteger(i2.Value) };
}
public static MPInteger[] RsaSigToMpi(
byte[] encoding)
{
return new MPInteger[]{ new MPInteger(new BigInteger(1, encoding)) };
}
public static string GetDigestName(
HashAlgorithmTag hashAlgorithm)
{
switch (hashAlgorithm)
{
case HashAlgorithmTag.Sha1:
return "SHA1";
case HashAlgorithmTag.MD2:
return "MD2";
case HashAlgorithmTag.MD5:
return "MD5";
case HashAlgorithmTag.RipeMD160:
return "RIPEMD160";
case HashAlgorithmTag.Sha224:
return "SHA224";
case HashAlgorithmTag.Sha256:
return "SHA256";
case HashAlgorithmTag.Sha384:
return "SHA384";
case HashAlgorithmTag.Sha512:
return "SHA512";
default:
throw new PgpException("unknown hash algorithm tag in GetDigestName: " + hashAlgorithm);
}
}
public static string GetSignatureName(
PublicKeyAlgorithmTag keyAlgorithm,
HashAlgorithmTag hashAlgorithm)
{
string encAlg;
switch (keyAlgorithm)
{
case PublicKeyAlgorithmTag.RsaGeneral:
case PublicKeyAlgorithmTag.RsaSign:
encAlg = "RSA";
break;
case PublicKeyAlgorithmTag.Dsa:
encAlg = "DSA";
break;
case PublicKeyAlgorithmTag.ElGamalEncrypt: // in some malformed cases.
case PublicKeyAlgorithmTag.ElGamalGeneral:
encAlg = "ElGamal";
break;
default:
throw new PgpException("unknown algorithm tag in signature:" + keyAlgorithm);
}
return GetDigestName(hashAlgorithm) + "with" + encAlg;
}
public static string GetSymmetricCipherName(
SymmetricKeyAlgorithmTag algorithm)
{
switch (algorithm)
{
case SymmetricKeyAlgorithmTag.Null:
return null;
case SymmetricKeyAlgorithmTag.TripleDes:
return "DESEDE";
case SymmetricKeyAlgorithmTag.Idea:
return "IDEA";
case SymmetricKeyAlgorithmTag.Cast5:
return "CAST5";
case SymmetricKeyAlgorithmTag.Blowfish:
return "Blowfish";
case SymmetricKeyAlgorithmTag.Safer:
return "SAFER";
case SymmetricKeyAlgorithmTag.Des:
return "DES";
case SymmetricKeyAlgorithmTag.Aes128:
return "AES";
case SymmetricKeyAlgorithmTag.Aes192:
return "AES";
case SymmetricKeyAlgorithmTag.Aes256:
return "AES";
case SymmetricKeyAlgorithmTag.Twofish:
return "Twofish";
default:
throw new PgpException("unknown symmetric algorithm: " + algorithm);
}
}
public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm)
{
int keySize;
switch (algorithm)
{
case SymmetricKeyAlgorithmTag.Des:
keySize = 64;
break;
case SymmetricKeyAlgorithmTag.Idea:
case SymmetricKeyAlgorithmTag.Cast5:
case SymmetricKeyAlgorithmTag.Blowfish:
case SymmetricKeyAlgorithmTag.Safer:
case SymmetricKeyAlgorithmTag.Aes128:
keySize = 128;
break;
case SymmetricKeyAlgorithmTag.TripleDes:
case SymmetricKeyAlgorithmTag.Aes192:
keySize = 192;
break;
case SymmetricKeyAlgorithmTag.Aes256:
case SymmetricKeyAlgorithmTag.Twofish:
keySize = 256;
break;
default:
throw new PgpException("unknown symmetric algorithm: " + algorithm);
}
return keySize;
}
public static KeyParameter MakeKey(
SymmetricKeyAlgorithmTag algorithm,
byte[] keyBytes)
{
string algName = GetSymmetricCipherName(algorithm);
return ParameterUtilities.CreateKeyParameter(algName, keyBytes);
}
public static KeyParameter MakeRandomKey(
SymmetricKeyAlgorithmTag algorithm,
SecureRandom random)
{
int keySize = GetKeySize(algorithm);
byte[] keyBytes = new byte[(keySize + 7) / 8];
random.NextBytes(keyBytes);
return MakeKey(algorithm, keyBytes);
}
public static KeyParameter MakeKeyFromPassPhrase(
SymmetricKeyAlgorithmTag algorithm,
S2k s2k,
char[] passPhrase)
{
int keySize = GetKeySize(algorithm);
byte[] pBytes = Strings.ToByteArray(new string(passPhrase));
byte[] keyBytes = new byte[(keySize + 7) / 8];
int generatedBytes = 0;
int loopCount = 0;
while (generatedBytes < keyBytes.Length)
{
IDigest digest;
if (s2k != null)
{
try
{
switch (s2k.HashAlgorithm)
{
case HashAlgorithmTag.Sha1:
digest = DigestUtilities.GetDigest("SHA1");
break;
default:
throw new PgpException("unknown hash algorithm: " + s2k.HashAlgorithm);
}
}
catch (Exception e)
{
throw new PgpException("can't find S2k digest", e);
}
for (int i = 0; i != loopCount; i++)
{
digest.Update(0);
}
byte[] iv = s2k.GetIV();
switch (s2k.Type)
{
case S2k.Simple:
digest.BlockUpdate(pBytes, 0, pBytes.Length);
break;
case S2k.Salted:
digest.BlockUpdate(iv, 0, iv.Length);
digest.BlockUpdate(pBytes, 0, pBytes.Length);
break;
case S2k.SaltedAndIterated:
long count = s2k.IterationCount;
digest.BlockUpdate(iv, 0, iv.Length);
digest.BlockUpdate(pBytes, 0, pBytes.Length);
count -= iv.Length + pBytes.Length;
while (count > 0)
{
if (count < iv.Length)
{
digest.BlockUpdate(iv, 0, (int)count);
break;
}
else
{
digest.BlockUpdate(iv, 0, iv.Length);
count -= iv.Length;
}
if (count < pBytes.Length)
{
digest.BlockUpdate(pBytes, 0, (int)count);
count = 0;
}
else
{
digest.BlockUpdate(pBytes, 0, pBytes.Length);
count -= pBytes.Length;
}
}
break;
default:
throw new PgpException("unknown S2k type: " + s2k.Type);
}
}
else
{
try
{
digest = DigestUtilities.GetDigest("MD5");
for (int i = 0; i != loopCount; i++)
{
digest.Update(0);
}
digest.BlockUpdate(pBytes, 0, pBytes.Length);
}
catch (Exception e)
{
throw new PgpException("can't find MD5 digest", e);
}
}
byte[] dig = DigestUtilities.DoFinal(digest);
if (dig.Length > (keyBytes.Length - generatedBytes))
{
Array.Copy(dig, 0, keyBytes, generatedBytes, keyBytes.Length - generatedBytes);
}
else
{
Array.Copy(dig, 0, keyBytes, generatedBytes, dig.Length);
}
generatedBytes += dig.Length;
loopCount++;
}
Array.Clear(pBytes, 0, pBytes.Length);
return MakeKey(algorithm, keyBytes);
}
/// <summary>Write out the passed in file as a literal data packet.</summary>
public static void WriteFileToLiteralData(
Stream outputStream,
char fileType,
FileInfo file)
{
Stream inStr = file.OpenRead();
Stream outStr = new PgpLiteralDataGenerator().Open(
outputStream, fileType, file.Name, file.Length, file.LastWriteTime);
Streams.PipeAll(inStr, outStr);
inStr.Close();
outStr.Close();
}
/// <summary>Write out the passed in file as a literal data packet in partial packet format.</summary>
public static void WriteFileToLiteralData(
Stream outputStream,
char fileType,
FileInfo file,
byte[] buffer)
{
PgpLiteralDataGenerator lData = new PgpLiteralDataGenerator();
Stream pOut = lData.Open(outputStream, fileType, file.Name, file.LastWriteTime, buffer);
FileStream inputStream = file.OpenRead();
byte[] buf = new byte[buffer.Length];
int len;
while ((len = inputStream.Read(buf, 0, buf.Length)) > 0)
{
pOut.Write(buf, 0, len);
}
lData.Close();
inputStream.Close();
}
private const int ReadAhead = 60;
private static bool IsPossiblyBase64(
int ch)
{
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')
|| (ch >= '0' && ch <= '9') || (ch == '+') || (ch == '/')
|| (ch == '\r') || (ch == '\n');
}
/// <summary>
/// Return either an ArmoredInputStream or a BcpgInputStream based on whether
/// the initial characters of the stream are binary PGP encodings or not.
/// </summary>
public static Stream GetDecoderStream(
Stream inputStream)
{
// TODO Remove this restriction?
if (!inputStream.CanSeek)
throw new ArgumentException("inputStream must be seek-able", "inputStream");
long markedPos = inputStream.Position;
int ch = inputStream.ReadByte();
if ((ch & 0x80) != 0)
{
inputStream.Position = markedPos;
return inputStream;
}
else
{
if (!IsPossiblyBase64(ch))
{
inputStream.Position = markedPos;
return new ArmoredInputStream(inputStream);
}
byte[] buf = new byte[ReadAhead];
int count = 1;
int index = 1;
buf[0] = (byte)ch;
while (count != ReadAhead && (ch = inputStream.ReadByte()) >= 0)
{
if (!IsPossiblyBase64(ch))
{
inputStream.Position = markedPos;
return new ArmoredInputStream(inputStream);
}
if (ch != '\n' && ch != '\r')
{
buf[index++] = (byte)ch;
}
count++;
}
inputStream.Position = markedPos;
//
// nothing but new lines, little else, assume regular armoring
//
if (count < 4)
{
return new ArmoredInputStream(inputStream);
}
//
// test our non-blank data
//
byte[] firstBlock = new byte[8];
Array.Copy(buf, 0, firstBlock, 0, firstBlock.Length);
byte[] decoded = Base64.Decode(firstBlock);
//
// it's a base64 PGP block.
//
bool hasHeaders = (decoded[0] & 0x80) == 0;
return new ArmoredInputStream(inputStream, hasHeaders);
}
}
}
}

View File

@@ -0,0 +1,199 @@
using System;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Date;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>Generator for old style PGP V3 Signatures.</remarks>
// TODO Should be able to implement ISigner?
public class PgpV3SignatureGenerator
{
private PublicKeyAlgorithmTag keyAlgorithm;
private HashAlgorithmTag hashAlgorithm;
private PgpPrivateKey privKey;
private ISigner sig;
private IDigest dig;
private int signatureType;
private byte lastb;
/// <summary>Create a generator for the passed in keyAlgorithm and hashAlgorithm codes.</summary>
public PgpV3SignatureGenerator(
PublicKeyAlgorithmTag keyAlgorithm,
HashAlgorithmTag hashAlgorithm)
{
this.keyAlgorithm = keyAlgorithm;
this.hashAlgorithm = hashAlgorithm;
dig = DigestUtilities.GetDigest(PgpUtilities.GetDigestName(hashAlgorithm));
sig = SignerUtilities.GetSigner(PgpUtilities.GetSignatureName(keyAlgorithm, hashAlgorithm));
}
/// <summary>Initialise the generator for signing.</summary>
public void InitSign(
int sigType,
PgpPrivateKey key)
{
InitSign(sigType, key, null);
}
/// <summary>Initialise the generator for signing.</summary>
public void InitSign(
int sigType,
PgpPrivateKey key,
SecureRandom random)
{
this.privKey = key;
this.signatureType = sigType;
try
{
ICipherParameters cp = key.Key;
if (random != null)
{
cp = new ParametersWithRandom(key.Key, random);
}
sig.Init(true, cp);
}
catch (InvalidKeyException e)
{
throw new PgpException("invalid key.", e);
}
dig.Reset();
lastb = 0;
}
public void Update(
byte b)
{
if (signatureType == PgpSignature.CanonicalTextDocument)
{
doCanonicalUpdateByte(b);
}
else
{
doUpdateByte(b);
}
}
private void doCanonicalUpdateByte(
byte b)
{
if (b == '\r')
{
doUpdateCRLF();
}
else if (b == '\n')
{
if (lastb != '\r')
{
doUpdateCRLF();
}
}
else
{
doUpdateByte(b);
}
lastb = b;
}
private void doUpdateCRLF()
{
doUpdateByte((byte)'\r');
doUpdateByte((byte)'\n');
}
private void doUpdateByte(
byte b)
{
sig.Update(b);
dig.Update(b);
}
public void Update(
byte[] b)
{
if (signatureType == PgpSignature.CanonicalTextDocument)
{
for (int i = 0; i != b.Length; i++)
{
doCanonicalUpdateByte(b[i]);
}
}
else
{
sig.BlockUpdate(b, 0, b.Length);
dig.BlockUpdate(b, 0, b.Length);
}
}
public void Update(
byte[] b,
int off,
int len)
{
if (signatureType == PgpSignature.CanonicalTextDocument)
{
int finish = off + len;
for (int i = off; i != finish; i++)
{
doCanonicalUpdateByte(b[i]);
}
}
else
{
sig.BlockUpdate(b, off, len);
dig.BlockUpdate(b, off, len);
}
}
/// <summary>Return the one pass header associated with the current signature.</summary>
public PgpOnePassSignature GenerateOnePassVersion(
bool isNested)
{
return new PgpOnePassSignature(
new OnePassSignaturePacket(signatureType, hashAlgorithm, keyAlgorithm, privKey.KeyId, isNested));
}
/// <summary>Return a V3 signature object containing the current signature state.</summary>
public PgpSignature Generate()
{
long creationTime = DateTimeUtilities.CurrentUnixMs() / 1000L;
byte[] hData = new byte[]
{
(byte) signatureType,
(byte)(creationTime >> 24),
(byte)(creationTime >> 16),
(byte)(creationTime >> 8),
(byte) creationTime
};
sig.BlockUpdate(hData, 0, hData.Length);
dig.BlockUpdate(hData, 0, hData.Length);
byte[] sigBytes = sig.GenerateSignature();
byte[] digest = DigestUtilities.DoFinal(dig);
byte[] fingerPrint = new byte[]{ digest[0], digest[1] };
// an RSA signature
bool isRsa = keyAlgorithm == PublicKeyAlgorithmTag.RsaSign
|| keyAlgorithm == PublicKeyAlgorithmTag.RsaGeneral;
MPInteger[] sigValues = isRsa
? PgpUtilities.RsaSigToMpi(sigBytes)
: PgpUtilities.DsaSigToMpi(sigBytes);
return new PgpSignature(
new SignaturePacket(3, signatureType, privKey.KeyId, keyAlgorithm,
hashAlgorithm, creationTime * 1000L, fingerPrint, sigValues));
}
}
}

View File

@@ -0,0 +1,25 @@
using System.IO;
using Org.BouncyCastle.Asn1.Utilities;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
public class WrappedGeneratorStream
: FilterStream
{
private readonly IStreamGenerator gen;
public WrappedGeneratorStream(
IStreamGenerator gen,
Stream str)
: base(str)
{
this.gen = gen;
}
public override void Close()
{
gen.Close();
}
}
}