Sunday, 21 June 2009
Old School Acorn Computing
I think anyone who still has fond memories of using Acorn and BBC computers when they were at school will enjoy taking a look at the new series of products. Of course, anyone who is still hankering for a bit of RISC OS action can take a look at Ebay, you can pick up an A7000+ very cheaply these days, some even with a network adapter. Schools are literally chucking these things out with the rubbish nowadays, unfortunately they can't even give them way to charities etc. However, if you really want the latest iteration of RISC OS, currently at version 5, check out Castle's list of products here: http://www.iyonix.com/. Personally, I dont see the need to spend almost £800 on a computer like this, but if you really are an enthusiast you will snap one right up.
Sunday, 7 June 2009
PGP Decryption with C#
Thing is, I just wondered how it would work. I already know that Bouncy Castle works heavily with streams, so I guess the logic would be:
- Open a file as a stream
- Get the encryption keys needed to decrypt the file
- Decrypt the file to a stream
- Write the file(s)
I already have the code that will manage the keys for me and I already have an understanding on how Bouncy Castle's libs work with streams. I had a quick google to see if there was anything going in the way of examples on how to do this, but only managed to find one (someone had pasted the example in the Bouncy Castle source to a forum). Thing is, none of the offered examples seemed to fit my code, which was a shame. However, these things can be overcome once you understand a little more about how Bouncy Castle implements OpenPGP. The code I created to decrypt my archive isnt going to go into production (probably), its just something I wanted to try out, so it's all one big method. Note, these libs work on streams - to decrypt my archive and get to the literal data, I had to manipulate the streams to get there, I think this could put a lot of people off using this. First off, we need to get the file we wish to decrypt into a stream and determine the decoder from it:
input = PgpUtilities.GetDecoderStream(input);
The next thing we want to do is create a PGPObjectFactory. From here we can get a PGPEncryptedDataList and start creating objects from our input stream:
PgpObjectFactory pgpObjF = new PgpObjectFactory(input);
PgpEncryptedDataList enc;
PgpObject obj = pgpObjF.NextPgpObject();
if (obj is PgpEncryptedDataList)
{
enc = (PgpEncryptedDataList)obj;
}
else
{
enc = (PgpEncryptedDataList)pgpObjF.NextPgpObject();
}
Now we are able to get the first encrypted object from our stream so lets decrypt the stream using the private key:
PgpPrivateKey privKey = pgpKeys.PGPPrivateKey;
PgpPublicKeyEncryptedData pbe = null;
foreach (PgpPublicKeyEncryptedData pked in enc.GetEncryptedDataObjects())
{
if (privKey != null)
{
pbe = pked;
break;
}
}
Stream clear = pbe.GetDataStream(privKey);
PgpObjectFactory plainFact = new PgpObjectFactory(clear);
PgpObject message = plainFact.NextPgpObject();
With this done, we are now able to start inspecting the decrypted objects we are creating. We know the file we made earlier was an encrypted zip file, so at this point it is fair to assume message is compressed, which it is:
if (message is PgpCompressedData)
{
PgpCompressedData cData = (PgpCompressedData)message;
Stream compDataIn = cData.GetDataStream();
I knew that message was of the type PGPCompressedData from inspecting the object, what we need to do now is find out when message becomes literal data. To do this, we need to go a bit further, I need to create a new PGPObjectFactory:
PgpObjectFactory o = new PgpObjectFactory(compDataIn);
message = o.NextPgpObject();
if (message is PgpOnePassSignatureList)
{
message = o.NextPgpObject();
PgpLiteralData Ld = null;
Ld = (PgpLiteralData)message;
Stream output = File.Create(outputpath + "\\" + Ld.FileName);
Stream unc = Ld.GetInputStream();
Streams.PipeAll(unc, output);
}
I had thought that the literal data would come next, but it didnt - dont forget when encrypting the data previously I signed it - so I got the signature first. So, I just needed to move on to the next object which was the literal data. As you can see above, all I need to do now is write the file out to the disk and I am done. I did think when I had completed this, "What about decompressing directory structures?". Well, in my humble opinion, there is no need to do this. From what I have seen with encryption over the last few months, files get encrypted, not directories. I wrote a lib the other day that compresses into a zip, I will post it here over the next week, it also decompresses (including the directory structure). So the way I see it, if you wanted to encrypt a set of files and directories, you would create the zip first and then create an encrypted archive from that file. I know it would be two seperate operations, but this sort of suits my situation at the moment: I dont need to decrypt archives programatically, but I can if I need to (now, anyway). So with the stuff I have now, I can create a zip for internal deployment and then create a seperate, encrypted zip from that file for external deployment.
The complete code for decryption is as follows:
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.IO;
using Org.BouncyCastle.Bcpg;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Tools.PGP.Crypto;
namespace Tools.PGP.Crypto
{
public class PGPDecrypt
{
public string _encryptedFilePath;
public string _privKeyPath;
public char[] _password;
public string _outputPath;
public PGPKeys pgpKeys;
public PGPDecrypt(string encryptedFilePath, string privKeyPath, string password, string outputPath, string pubKeyPath, long keyID)
{
_encryptedFilePath = encryptedFilePath;
_outputPath = outputPath;
_password = password.ToCharArray();
_privKeyPath = privKeyPath;
pgpKeys = new PGPKeys(pubKeyPath, privKeyPath, password, keyID);
}
public void decrypt(Stream input, string outputpath)
{
input = PgpUtilities.GetDecoderStream(input);
try
{
PgpObjectFactory pgpObjF = new PgpObjectFactory(input);
PgpEncryptedDataList enc;
PgpObject obj = pgpObjF.NextPgpObject();
if (obj is PgpEncryptedDataList)
{
enc = (PgpEncryptedDataList)obj;
}
else
{
enc = (PgpEncryptedDataList)pgpObjF.NextPgpObject();
}
PgpPrivateKey privKey = pgpKeys.PGPPrivateKey;
PgpPublicKeyEncryptedData pbe = null;
foreach (PgpPublicKeyEncryptedData pked in enc.GetEncryptedDataObjects())
{
if (privKey != null)
{
pbe = pked;
break;
}
}
Stream clear = pbe.GetDataStream(privKey);
PgpObjectFactory plainFact = new PgpObjectFactory(clear);
PgpObject message = plainFact.NextPgpObject();
if (message is PgpCompressedData)
{
PgpCompressedData cData = (PgpCompressedData)message;
Stream compDataIn = cData.GetDataStream();
PgpObjectFactory o = new PgpObjectFactory(compDataIn);
message = o.NextPgpObject();
if (message is PgpOnePassSignatureList)
{
message = o.NextPgpObject();
PgpLiteralData Ld = null;
Ld = (PgpLiteralData)message;
Stream output = File.Create(outputpath + "\\" + Ld.FileName);
Stream unc = Ld.GetInputStream();
Streams.PipeAll(unc, output);
}
else
{
PgpLiteralData Ld = null;
Ld = (PgpLiteralData)message;
Stream output = File.Create(outputpath + "\\" + Ld.FileName);
Stream unc = Ld.GetInputStream();
Streams.PipeAll(unc, output);
}
}
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
}
}
To decrypt a file, I can just call the method like so:
PGPDecrypt test = new PGPDecrypt(@"C:\test\somefile.zip",
@"C:\GnuPG\secring.gpg",
"password",
@"C:\test\test",
@"C:\GnuPG\pubring.gpg",
3699527550217851901);
FileStream fs = File.Open(@"C:\test\somefile.zip", FileMode.Open);
test.decrypt(fs,@"C:\test\test");
Saturday, 6 June 2009
PGP Encryption with C#
Then I was told that there will be some instances that field workers will need to download parts of the app whilst they are out and about doing whatever it is they do. I had already planned to compress the app as an archive, but now I needed a way to secure the archive itself. One of my colleagues suggested that I put a password on the archive, its not a daft suggestion to make, it would imply that only people with the password would be able to extract the data from the archive itself. But, I pointed out that this doesnt prove that the archive was one created by us - all it does it password protect an archive. Plus, we would need to communicate the password to those that needed it in a secure manner, which in turn would mean we couldnt use the same password all the time etc. I asked if we already use some form of secure signature and got told we use PGP - result. I explained to my colleagues that yes, password protecting the archive would mean that only password holders could extract from the archive, but this wouldnt mean they are authorised to hold the password in the first place. I also explained that the password could be cracked, even if it was sufficiently complex, a persistant attacker could spend a lot of time trying to crack it.
With this in mind, I went on to say that as we already encrypt data with PGP, we could do the same thing here with the archives we are planning to create. This way, people using our app (or archives in general) would be able to see with great certainty that they did originate from us and that it hasnt been substituted by a third party.
This is part of a .Net project, so any encryption would ideally take place using the .Net framework. However, right now there is no PGP support in .Net. There is support for cryptograhy in general, for instance RSA is employed as well as DPAPI, but we already use PGP and already have public keys. So it would make more sense to me for us to use these for encryption. To do this, I am going to be using the C# cryptography libraries available from The Legion of the Bouncy Castle: http://www.bouncycastle.org/csharp/ . There are not a lot of good examples of how to use this out on the Internet, the best I could find to outline the concepts involved was here. Unfortunately, the formatting of the page makes it a little hard to understand what is happening - which is the important part
Lets get down to encrypting a file and compressing it to an archive. I am going to assume that if you are searching for PGP encryption methods in C# that you have already generated a set of PGP keys. If you havent, then I can suggest that you nip over to http://www.gnupg.org/ and download a copy of GnuPG to generate some for you (it's what I used to test through my app). The first step we need to take is to understand what we need to get the key values. We need to know the path to the public and private keys, the password for the public key to encrypt with and we need to know the key ID we want to target. The key ID is a 64bit signed integer, using GnuPG you can get this value from the command line using the command:
--list-keys --with-colons <userid>
Simply replace the user ID with the user ID attached to the key (this will probably be the email address used when generating the key itself). You will get something that looks like this back:pub:u:1024:1:33575CB4BB8193FD:2009-06-06:::u:Your Name(RSA Sign only test) <your@email.address>::scESC:
sub:u:1024:1:31B23C6C6681F46B:2009-06-06::::::e:
The key ID is the bit in bold (or should be bold, but not quite so well - it's the fith column along). This is the key ID in hexidecimal, in the code I am about to show you it needs to be in decimal format, so you can either convert it in c# with the following:int decAgain = int.Parse(hexValue, System.Globalization.NumberStyles.HexNumber);
Or you can use a tool to convert it for you like this one: http://www.parkenet.com/apl/HexDecConverter.html, or if you happen to know what it is anyway (like I did), you can just type it in etc.I used RSA encryption when generating my key. I used this as it would appear to be the best method for creating keys right now, there are not many examples of how to use PGP encryption on the Internet at the moment, and the peices of code that are here and there seem to use older standards.
Right, now we know our key ID etc, we can start writing the code. I created a class called PGPKeys to do all my key related work for me. First up, we need to declare all our variables etc:
/// <summary>
/// long representing the key ID we want to target.
/// </summary>
private long _keyID;
/// <summary>
/// string to represent the path to the private key file.
/// </summary>
private string _privKeyPath;
/// <summary>
/// string to represent the path to the public key file.
/// </summary>
private string _pubKeyPath;
/// <summary>
/// string to represent the passphrase used with PGP.
/// </summary>
private string _password;
public PgpPublicKey PGPPublicKey { get; private set; }
public PgpPrivateKey PGPPrivateKey { get; private set; }
public PgpSecretKey PGPSecretKey { get; private set; }
Nice and simple. Next up, I want to be able to validate the values I am planning to use:
/// <summary>
/// Constructor to manage variables for us.
/// </summary>
/// <param name="pubKeyPath">string representing the public key path</param>
/// <param name="privKeyPath">string representing the private key path</param>
/// <param name="password">string representing the passphrase</param>
/// <param name="keyID">long representing the key ID we want to use</param>
public PGPKeys(string pubKeyPath, string privKeyPath, string password, long keyID)
{
if (!File.Exists(pubKeyPath))
{
throw new ArgumentNullException("Could not find the public key at " + pubKeyPath);
}
else
{
_pubKeyPath = pubKeyPath;
}
if (!File.Exists(privKeyPath))
{
throw new ArgumentNullException("Could not find the private key at " + privKeyPath);
}
else
{
_privKeyPath = privKeyPath;
}
if (String.IsNullOrEmpty(password))
{
throw new ArgumentNullException("The password must not be null");
}
else
{
_password = password;
}
if (keyID == 0)
{
throw new ArgumentNullException("The key ID must not be null");
}
else
{
_keyID = keyID;
}
PGPPublicKey = getPublicKey(_pubKeyPath);
PGPSecretKey = getSecretKey(_privKeyPath);
PGPPrivateKey = getPrivateKey(_password);
}
You can see here where the public, private and secret keys are going to be held. Simple so far, but now the work begins. I think one of the tough things to get your head around when using the Bouncy Castle libs is that everything is a set of streams that have things done to them. This is a slight over simplification, but the following bits of code will demonstrate this process. Lets look at getting the public key value:/// <summary>
/// private method to get the public key value.
/// </summary>
/// <param name="_pubKeyPath">string representing the public key path</param>
/// <returns>a PGPPublicKey</returns>
private PgpPublicKey getPublicKey(string _pubKeyPath)
{
PgpPublicKey pubKey;
using (Stream keyin = File.OpenRead(_pubKeyPath))
using (Stream s = PgpUtilities.GetDecoderStream(keyin))
{
PgpPublicKeyRingBundle pubKeyBundle = new PgpPublicKeyRingBundle(s);
pubKey = pubKeyBundle.GetPublicKey(_keyID);
if (pubKey == null)
throw new Exception("The public key value is null!");
}
return pubKey;
}
This is the on ramp with PGP, getting the public key value represented in the code itself. It also shows how Bouncy Castle likes to work with streams, the first stream here opens the file in which the key is stored, the second stream then goes on to get the value from it. With that done, lets get the secret key:/// <summary>
/// private method to get the secret key value.
/// </summary>
/// <param name="_privKeyPath">string representing the private key path</param>
/// <returns>a PGPSecretKey</returns>
private PgpSecretKey getSecretKey(string _privKeyPath)
{
PgpSecretKey secKey;
using (Stream keyin = File.OpenRead(_privKeyPath))
using (Stream s = PgpUtilities.GetDecoderStream(keyin))
{
PgpSecretKeyRingBundle secKeyBundle = new PgpSecretKeyRingBundle(s);
secKey = secKeyBundle.GetSecretKey(_keyID);
if (secKey == null)
throw new Exception("The secret key value is null!");
}
return secKey;
}
Here we pretty much do the same thing as before, but this time we are looking at the private key file path. Dont get confused here, this method gets the secret key value, to do this we point it at the private key file and supply a key ID. To get the private key itself, we use the following:
/// <summary>
/// private method to get the private key value.
/// </summary>
/// <returns>a PGPPrivateKey</returns>
private PgpPrivateKey getPrivateKey(string _password)
{
PgpPrivateKey privKey = PGPSecretKey.ExtractPrivateKey(_password.ToCharArray());
if (privKey == null)
{
return null;
}
else
{
return privKey;
}
}
This one is a lot smaller, but with it we get our private key. And with that, we are done with our PGPKeys class, lets move on to encrypting, signing and compressing using these keys. Remember what I said about streams? Well, this is how it comes into play, lets encrypt some data:private Stream encrypt(Stream output)
{
PgpEncryptedDataGenerator pgpEncDataGen = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Twofish, new SecureRandom());
pgpEncDataGen.AddMethod(_pgpKeys.PGPPublicKey);
Stream encryptedOutput = pgpEncDataGen.Open(output, new byte[BufferSize]);
return encryptedOutput;
}
Thats it done, this will encrypt a stream only. On its own, this isnt so useful, you can see where I am specifying the encryption algorythm (I decided to use TwoFish, apparently this is a very secure algorythm) and where the public key value is applied. If you just hold in your mind for a moment that this represents a stream of encrypted data at its conclusion, we can look at how it is compressed to an archive:private Stream compress(Stream output)
{
PgpCompressedDataGenerator pgpCompDataGen = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);
Stream compressedEncryptedOut = pgpCompDataGen.Open(output);
return compressedEncryptedOut;
}
The stream we just emcrypted is now passed to this method which compresses it to a zip - right now though, this is still a stream, nothing more. The next thing we need to do is apply the literal data to this stream:
private Stream literalOutput(Stream compressedOut, FileInfo file)
{
PgpLiteralDataGenerator pgpLiteralDataGen = new PgpLiteralDataGenerator();
Stream literal = pgpLiteralDataGen.Open(compressedOut, PgpLiteralData.Binary, file);
return literal;
}
Now we have a stream that has been encrypted, compressed and had its literal data applied to it. I am going to point out here that if you dont want to create a PGP encrypted file, but instead just want a PGP encrypted stream, then you could just use the encrypt method. Anyway, now we are ready to write and sign our archive:
private static void writeAndSign(Stream ouput, Stream literalout, FileStream inputFile, PgpSignatureGenerator sigGen)
{
int length = 0;
byte[] buf = new byte[BufferSize];
while ((length = inputFile.Read(buf, 0, buf.Length)) > 0)
{
literalout.Write(buf, 0, length);
sigGen.Update(buf, 0, length);
}
sigGen.Generate().Encode(ouput);
}
The last thing we want to do here, is sign the encrypted data itself:
private PgpSignatureGenerator sigGen(Stream compressedOut)
{
const bool Iscritical = false;
const bool IsNested = false;
PublicKeyAlgorithmTag tag = _pgpKeys.PGPSecretKey.PublicKey.Algorithm;
PgpSignatureGenerator pgpSigGen = new PgpSignatureGenerator(tag, HashAlgorithmTag.Sha1);
pgpSigGen.InitSign(PgpSignature.BinaryDocument, _pgpKeys.PGPPrivateKey);
foreach (string userID in _pgpKeys.PGPSecretKey.PublicKey.GetUserIds())
{
PgpSignatureSubpacketGenerator subPackGen = new PgpSignatureSubpacketGenerator();
subPackGen.SetSignerUserId(Iscritical, userID);
pgpSigGen.SetHashedSubpackets(subPackGen.Generate());
break;
}
pgpSigGen.GenerateOnePassVersion(IsNested).Encode(compressedOut);
return pgpSigGen;
}
Now we are able to encrypt, compress, sign and write our chosen file to an archive. All we need to do now is tie it up nicely:
public void EncryptSignAndZip(Stream output, FileInfo unencryptedinput)
{
if (output == null)
{
throw new ArgumentNullException("The output stream cannot be null");
}
if (unencryptedinput == null)
{
throw new ArgumentNullException("You must supply a filename to encrypt");
}
if (!File.Exists(unencryptedinput.FullName))
{
throw new ArgumentNullException(unencryptedinput + " does not exist");
}
using (Stream encryptedout = encrypt(output))
using (Stream compressedOut = compress(encryptedout))
{
PgpSignatureGenerator signature = sigGen(compressedOut);
using (Stream literalOut = literalOutput(compressedOut, unencryptedinput))
using (FileStream inputfile = unencryptedinput.OpenRead())
{
writeAndSign(compressedOut, literalOut, inputfile, signature);
}
}
}
Again, I am validating the arguments as they go in to make sure that they are not null values. You can also see here how Bouncy Castle works with the streams we are creating. However, thats it - these classes will create an encrypted zip file for you with what ever you want in them. To make use of this class, I used this in testing:
string pubKeyPath = @"C:\GnuPG\pubring.gpg";
string privKeyPath = @"C:\GnuPG\secring.gpg";
string password = "password";
long _keyID = 3699527550217851901;
PGPEncrpyt test = new PGPEncrpyt(pubKeyPath, privKeyPath, password, _keyID);
FileInfo myFile = new FileInfo(@"C:\test\app.config");
FileStream mystream = new FileStream(@"C:\test\somefile.zip", FileMode.Create, FileAccess.Write);
test.EncryptSignAndZip(mystream, myFile);
And thats it, I using the code above I was able to create an encrypted zip, which I was able to decrypt and extract using GnuPG. I have written the code to decrypt it yet as at this time, I dont really need it - the files I am encrypting will normally be sent on to other organisations etc. One thing I may make use of in future is the ability to encrypt just a stream - this may become very useful for me when it comes to sending data over a network to other parts of the business. The complete code is as follows, PGPKeys:
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.IO;
using Org.BouncyCastle.Bcpg;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Tools.PGP.Crypto
{
/// <summary>
/// Nested class to get information on out PGP Keys
/// </summary>
public class PGPKeys
{
/// <summary>
/// long representing the key ID we want to target.
/// </summary>
private long _keyID;
/// <summary>
/// string to represent the path to the private key file.
/// </summary>
private string _privKeyPath;
/// <summary>
/// string to represent the path to the public key file.
/// </summary>
private string _pubKeyPath;
/// <summary>
/// string to represent the passphrase used with PGP.
/// </summary>
private string _password;
public PgpPublicKey PGPPublicKey { get; private set; }
public PgpPrivateKey PGPPrivateKey { get; private set; }
public PgpSecretKey PGPSecretKey { get; private set; }
/// <summary>
/// Constructor to manage variables for us.
/// </summary>
/// <param name="pubKeyPath">string representing the public key path</param>
/// <param name="privKeyPath">string representing the private key path</param>
/// <param name="password">string representing the passphrase</param>
/// <param name="keyID">long representing the key ID we want to use</param>
public PGPKeys(string pubKeyPath, string privKeyPath, string password, long keyID)
{
if (!File.Exists(pubKeyPath))
{
throw new ArgumentNullException("Could not find the public key at " + pubKeyPath);
}
else
{
_pubKeyPath = pubKeyPath;
}
if (!File.Exists(privKeyPath))
{
throw new ArgumentNullException("Could not find the private key at " + privKeyPath);
}
else
{
_privKeyPath = privKeyPath;
}
if (String.IsNullOrEmpty(password))
{
throw new ArgumentNullException("The password must not be null");
}
else
{
_password = password;
}
if (keyID == 0)
{
throw new ArgumentNullException("The key ID must not be null");
}
else
{
_keyID = keyID;
}
PGPPublicKey = getPublicKey(_pubKeyPath);
PGPSecretKey = getSecretKey(_privKeyPath);
PGPPrivateKey = getPrivateKey(_password);
}
/// <summary>
/// private method to get the public key value.
/// </summary>
/// <param name="_pubKeyPath">string representing the public key path</param>
/// <returns>a PGPPublicKey</returns>
private PgpPublicKey getPublicKey(string _pubKeyPath)
{
PgpPublicKey pubKey;
using (Stream keyin = File.OpenRead(_pubKeyPath))
using (Stream s = PgpUtilities.GetDecoderStream(keyin))
{
PgpPublicKeyRingBundle pubKeyBundle = new PgpPublicKeyRingBundle(s);
pubKey = pubKeyBundle.GetPublicKey(_keyID);
if (pubKey == null)
throw new Exception("The public key value is null!");
}
return pubKey;
}
/// <summary>
/// private method to get the secret key value.
/// </summary>
/// <param name="_privKeyPath">string representing the private key path</param>
/// <returns>a PGPSecretKey</returns>
private PgpSecretKey getSecretKey(string _privKeyPath)
{
PgpSecretKey secKey;
using (Stream keyin = File.OpenRead(_privKeyPath))
using (Stream s = PgpUtilities.GetDecoderStream(keyin))
{
PgpSecretKeyRingBundle secKeyBundle = new PgpSecretKeyRingBundle(s);
secKey = secKeyBundle.GetSecretKey(_keyID);
if (secKey == null)
throw new Exception("The secret key value is null!");
}
return secKey;
}
/// <summary>
/// private method to get the private key value.
/// </summary>
/// <returns>a PGPPrivateKey</returns>
private PgpPrivateKey getPrivateKey(string _password)
{
PgpPrivateKey privKey = PGPSecretKey.ExtractPrivateKey(_password.ToCharArray());
if (privKey == null)
{
return null;
}
else
{
return privKey;
}
}
}
}
And PGPEncrypt:
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.IO;
using Org.BouncyCastle.Bcpg;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Tools.PGP.Crypto
{
/// <summary>
/// Public class to encrypt and compress files to a zip archive using PGP
/// </summary>
public class PGPEncrpyt
{
/// <summary>
/// instansiate a PGPKeys object
/// </summary>
private PGPKeys _pgpKeys;
private const int BufferSize = 0x10000;
public PGPEncrpyt(string _pubKeyPath, string _privKeyPath, string _password, long _keyID)
{
_pgpKeys = new PGPKeys(_pubKeyPath, _privKeyPath, _password, _keyID);
}
private static void writeAndSign(Stream ouput, Stream literalout, FileStream inputFile, PgpSignatureGenerator sigGen)
{
int length = 0;
byte[] buf = new byte[BufferSize];
while ((length = inputFile.Read(buf, 0, buf.Length)) > 0)
{
literalout.Write(buf, 0, length);
sigGen.Update(buf, 0, length);
}
sigGen.Generate().Encode(ouput);
}
private Stream encrypt(Stream output)
{
PgpEncryptedDataGenerator pgpEncDataGen = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Twofish, new SecureRandom());
pgpEncDataGen.AddMethod(_pgpKeys.PGPPublicKey);
Stream encryptedOutput = pgpEncDataGen.Open(output, new byte[BufferSize]);
return encryptedOutput;
}
private Stream compress(Stream output)
{
PgpCompressedDataGenerator pgpCompDataGen = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);
Stream compressedEncryptedOut = pgpCompDataGen.Open(output);
return compressedEncryptedOut;
}
private Stream literalOutput(Stream compressedOut, FileInfo file)
{
PgpLiteralDataGenerator pgpLiteralDataGen = new PgpLiteralDataGenerator();
Stream literal = pgpLiteralDataGen.Open(compressedOut, PgpLiteralData.Binary, file);
return literal;
}
private PgpSignatureGenerator sigGen(Stream compressedOut)
{
const bool Iscritical = false;
const bool IsNested = false;
PublicKeyAlgorithmTag tag = _pgpKeys.PGPSecretKey.PublicKey.Algorithm;
PgpSignatureGenerator pgpSigGen = new PgpSignatureGenerator(tag, HashAlgorithmTag.Sha1);
pgpSigGen.InitSign(PgpSignature.BinaryDocument, _pgpKeys.PGPPrivateKey);
foreach (string userID in _pgpKeys.PGPSecretKey.PublicKey.GetUserIds())
{
PgpSignatureSubpacketGenerator subPackGen = new PgpSignatureSubpacketGenerator();
subPackGen.SetSignerUserId(Iscritical, userID);
pgpSigGen.SetHashedSubpackets(subPackGen.Generate());
break;
}
pgpSigGen.GenerateOnePassVersion(IsNested).Encode(compressedOut);
return pgpSigGen;
}
public void EncryptSignAndZip(Stream output, FileInfo unencryptedinput)
{
if (output == null)
{
throw new ArgumentNullException("The output stream cannot be null");
}
if (unencryptedinput == null)
{
throw new ArgumentNullException("You must supply a filename to encrypt");
}
if (!File.Exists(unencryptedinput.FullName))
{
throw new ArgumentNullException(unencryptedinput + " does not exist");
}
using (Stream encryptedout = encrypt(output))
using (Stream compressedOut = compress(encryptedout))
{
PgpSignatureGenerator signature = sigGen(compressedOut);
using (Stream literalOut = literalOutput(compressedOut, unencryptedinput))
using (FileStream inputfile = unencryptedinput.OpenRead())
{
writeAndSign(compressedOut, literalOut, inputfile, signature);
}
}
}
}
}
Thursday, 4 June 2009
Reporting made easy
I initially though this was a joke, afterall this report had been publised each month for the last two years. Once I was told no, we really do do this, I needed to go and get myself a cup of sweet tea. After I had recovered, I started to speak to some of the analysts to try and find out the complete story behind this seemingly terrifying report, once I started to get some of the details behind this I came to the conclusion that the main reason for the length of time it takes to create this report was down to analysts doing the same thing every twenty-five days by hand. A prime opportunity to automate if ever there was one - and I had been after an excuse to create a .Net DAL for the team as well :).
There is nothing outstanding about this project at all, there isnt really any cutting edge tech being employed nor is there any new, fancy way of doing things being discussed. There is rarely anything taxing when it comes to generating reports. The most complicated thing you need to do is validate all the information that you are presenting, once the formula is there you are safe in the knowledge that your report will continue to churn out the required data for ever, the only time you will ever need to test it is when a new requirement is added to the schema of the report itself.
The uber report I am going to tackle just now is intended for publication via printed media. Because of this, I am confident in the knowledge that once the report has been created, no one will need to copy and past the data from it into another application - I know it gets distributed on paper. So, I know that I dont really need to provide a spreadsheet, csv, .doc or peice of XML initially. This is good news for me, as I only need to worry about one format, and I have decided to go for PDF. I had a look through the report a few days ago, when initially this task was given to me. Basically, its just a collection of graphs and tables, very little text, this just gets better as far as I am concerned. One format to worry about and very little wording to go into it - bliss!
So, what do I need for this to work? I need:
- A graphing lib, I am going for ZedGraph. Its open source and does everything I need.
- A PDF generation lob, for this I am going to use PFDJet. This is a commercial product, but there is also an open source version. As I dont need to do anything that fancy, it will do for me.
And thats pretty much it, all I will need to do is get the data I need from the db and use it to create some graphs and charts which go into a PDF report. Couldnt be simpler really, could it?
After a good planning meeting with the team lead and analyst I would be working with, it turns out that there is a similar system already in place for creating the basis for some of the data needed to create this report. After this session, I found that there is a monster Oracle DB somewhere in the depths of the core business that holds all the information we need. With the interesting bits to one side, I was then told a fairly comical story about Oracle, MySql and Access all doing a funny dance...
The only challenging thing really is the design of the report. Now that people have learned that I am automating some report generation, it seems like everyone has a preference as to how it should look. Right now, I am not concerned with fancy graphics or logos, right now the only important thing is getting the correct data into the report in a logical manner...