EDIT: This code works. The key text is different every time, but the key material is the same. I have updated the code below to collect the data for key creation from the console, without echo.
I would like to generate a Pgp keypair from a livecd with a pass phrase, distribute the public key and use it to encrypt personal data before I store it at Google drive, but never write the private key to disk.
I realize I could just use the RSA key directly, but I would prefer to have a pgp key for ease of use and flexibility.
When I need to decrypt data I would boot from a livecd again and reproduce the private key.
This is the code I am using... It produces the rsa key the same every time, but the pgp keys come out different. What am I missing?
Thanks in advance,
Code follows:
using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Bcpg;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
namespace PgpKeyFromPassphrase
{
class Program
{
static void Main()
{
string identity = ReadIdentityFromConsole();
DateTime keyDate = ReadDateFromConsole();
string passPhrase = ReadPassphraseFromConsole();
Console.WriteLine("Generating seed and keys. This will take some time");
//Hash the passphrasse 50,000 times
var seed = GenerateSeed(passPhrase);
//Create the RSA keypair from the seed
var keys = GenerateRsaKeys(seed);
//Create PGP secret key from keypair
var secretKey = GeneratePgpKeys(keyDate, identity, keys);
//Write armored secret key
PrintSecretKey(secretKey);
//Write armored public key
PrintPublicKey(secretKey);
Console.WriteLine("Copy the key and press enter to exit the program");
Console.ReadLine();
}
private static string ReadIdentityFromConsole()
{
string retVal = null;
while (retVal == null || retVal.Equals(string.Empty))
{
Console.WriteLine("Type a name to be associated with the Key");
retVal = Console.ReadLine();
}
return retVal;
}
private static DateTime ReadDateFromConsole()
{
DateTime retVal = DateTime.Today;
while (true)
{
Console.WriteLine("Enter the key creation date. Press enter for today");
var line = Console.ReadLine();
if (line == null || line.Equals(string.Empty) || DateTime.TryParse(line, out retVal)) break;
else Console.WriteLine("Failed to parse date, try again");
}
return retVal;
}
static string ReadPassphraseFromConsole()
{
var pass1 = new StringBuilder();
var pass2 = new StringBuilder();
while (pass1.Length == 0 || !pass1.Equals(pass2))
{
if (pass1.Length > 0 && pass2.Length > 0 && !pass1.Equals(pass2))
{
pass1 = new StringBuilder();
pass2 = new StringBuilder();
Console.WriteLine("Passphrases don't match! Try again.");
}
Console.WriteLine("type a strong passphrase and hit enter");
var key = Console.ReadKey(true);
while (!key.Key.Equals(ConsoleKey.Enter))
{
if (key.Key.Equals(ConsoleKey.Backspace))
{
if (pass1.Length > 0) pass1.Remove(pass1.Length - 1, 1);
}
else pass1.Append(key.KeyChar);
key = Console.ReadKey(true);
}
Console.WriteLine("repeat passphrase and hit enter");
key = Console.ReadKey(true);
while (!key.Key.Equals(ConsoleKey.Enter))
{
if (key.Key.Equals(ConsoleKey.Backspace))
{
if (pass2.Length > 0) pass2.Remove(pass2.Length - 1, 1);
}
else pass2.Append(key.KeyChar);
key = Console.ReadKey(true);
}
}
return pass1.ToString();
}
static byte[] GenerateSeed(string passPhrase)
{
//Hash the passphrasse 50,000 times
var passPhraseBytes = new byte[passPhrase.Length * sizeof(char)];
Buffer.BlockCopy(passPhrase.ToCharArray(), 0, passPhraseBytes, 0, passPhraseBytes.Length);
var digester = new Sha256Digest();
var seed = new byte[digester.GetDigestSize()];
digester.BlockUpdate(seed, 0, seed.Length);
digester.DoFinal(seed, 0);
for (var i = 0; i < 49999; i++)
{
digester = new Sha256Digest();
digester.BlockUpdate(seed, 0, seed.Length);
digester.DoFinal(seed, 0);
}
return seed;
}
static AsymmetricCipherKeyPair GenerateRsaKeys(byte[] seed)
{
var kpg = new RsaKeyPairGenerator();
kpg.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(0x13), new SecureRandom(seed), 4096, 8));
AsymmetricCipherKeyPair keys = kpg.GenerateKeyPair();
return keys;
}
static PgpSecretKey GeneratePgpKeys(DateTime keyDate, string identity, AsymmetricCipherKeyPair keys)
{
var secretKey = new PgpSecretKey(PgpSignature.DefaultCertification, PublicKeyAlgorithmTag.RsaGeneral, keys.Public, keys.Private, keyDate, identity, SymmetricKeyAlgorithmTag.Cast5, null, null, null, new SecureRandom());
return secretKey;
}
static void PrintSecretKey(PgpSecretKey secretKey)
{
var secretMemStream = new MemoryStream();
var secretArmoredStream = new ArmoredOutputStream(secretMemStream);
secretKey.Encode(secretArmoredStream);
secretArmoredStream.Close();
var ascPgpSecretKey = Encoding.ASCII.GetString(secretMemStream.ToArray());
Console.WriteLine(ascPgpSecretKey);
}
static void PrintPublicKey(PgpSecretKey secretKey)
{
var pubMemStream = new MemoryStream();
var pubArmoredStream = new ArmoredOutputStream(pubMemStream);
secretKey.PublicKey.Encode(pubArmoredStream);
pubArmoredStream.Close();
var ascPgpPublicKey = Encoding.ASCII.GetString(pubMemStream.ToArray());
Console.WriteLine(ascPgpPublicKey);
}
}
}
Thanks Duncan!
It worked.
The ASCII armored text that comes out for the PGP keys are different every time even though the underlying RSA keys are the same.
However I was able generate a keypair use the public key to encrypt a file then generate a new keypair and use that new private key to decrypt the file.