【问题标题】:Can I create a reproducible PGP Keypair with a passphrase using Bouncycastle for C#?我可以使用 C# 的 Bouncycastle 创建带有密码的可重现 PGP 密钥对吗?
【发布时间】:2014-03-23 03:23:10
【问题描述】:

编辑:此代码有效。每次关键文本都不一样,但关键材料是一样​​的。我已经更新了下面的代码,以从控制台收集用于创建密钥的数据,而无需回显。

我想通过 livecd 生成 Pgp 密钥对 短语,分发公钥并使用它来加密个人数据 在我将它存储在谷歌驱动器之前,但永远不要将私钥写入 磁盘。

我意识到我可以直接使用 RSA 密钥,但我更愿意 有一个 pgp 密钥以便于使用和灵活。

当我需要解密数据时,我会再次从 livecd 启动,然后 复制私钥。

这是我正在使用的代码...它每次都生成相同的 rsa 密钥 时间,但 pgp 键出来的不同。我错过了什么?

提前致谢,

代码如下:

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

    }
}

【问题讨论】:

  • 换句话说,您需要一个非随机 PGP 密钥生成算法。
  • 是的.. 但显然有足够的熵,它不能比“真正的”随机算法更容易破解。在生产中,我将使用哈希算法的组合来拉伸种子。只有我知道它们的应用数量和顺序。
  • 不知道 BC 是否支持这个,但是创建一对 RSA 密钥然后通过设置密钥材料将它们转换为 OpenPGP 密钥似乎是合乎逻辑的。
  • 正是我想要做的。在 BouncyCastle 的 PGP 实现中必须使用其他一些随机源。但我一直没找到。
  • 您是否尝试过使用这些键来检查它们是否相同,尽管它们看起来是一样的?

标签: c# encryption passwords bouncycastle pgp


【解决方案1】:

谢谢邓肯!

成功了。

即使底层的 RSA 密钥相同,为 PGP 密钥输出的 ASCII 保护文本每次都不同。

但是我能够生成一个密钥对,使用公钥加密文件,然后生成一个新的密钥对并使用新的私钥解密文件。

【讨论】:

    最近更新 更多