简体   繁体   English

使用Bouncy Castle(C#)生成PGP密钥环的结果是密钥ID FFFFFFFF

[英]Generating PGP key ring using Bouncy Castle (C#) results in key ID FFFFFFFF

I try to generate an RSA key pair using Bouncy Castle for C#. 我尝试使用Bouncy Castle for C#生成RSA密钥对。 I followed the tutorial for Java available at http://bouncycastle-pgp-cookbook.blogspot.de/2013_01_01_archive.html and create a master key and a signing key. 我遵循了Java教程, 网址http://bouncycastle-pgp-cookbook.blogspot.de/2013_01_01_archive.html,并创建了一个主密钥和一个签名密钥。 My code looks like 我的代码看起来像

IAsymmetricCipherKeyPairGenerator generator
    = GeneratorUtilities.GetKeyPairGenerator("RSA");
generator.Init(keyRingParams.RsaParams);

/* Create the master (signing-only) key. */
PgpKeyPair masterKeyPair = new PgpKeyPair(
    PublicKeyAlgorithmTag.RsaSign,
    generator.GenerateKeyPair(),
    DateTime.UtcNow);
Debug.WriteLine("Generated master key with ID "
    + masterKeyPair.KeyId.ToString("X"));

PgpSignatureSubpacketGenerator masterSubpckGen
    = new PgpSignatureSubpacketGenerator();
masterSubpckGen.SetKeyFlags(false, PgpKeyFlags.CanSign
    | PgpKeyFlags.CanCertify);
masterSubpckGen.SetPreferredSymmetricAlgorithms(false,
    (from a in keyRingParams.SymmetricAlgorithms
        where a.IsSelected
        select (int) a.Value).ToArray());
masterSubpckGen.SetPreferredHashAlgorithms(false,
    (from a in keyRingParams.HashAlgorithms
        where a.IsSelected
        select (int) a.Value).ToArray());

/* Create a signing and encryption key for daily use. */
PgpKeyPair encKeyPair = new PgpKeyPair(
    PublicKeyAlgorithmTag.RsaGeneral,
    generator.GenerateKeyPair(),
    DateTime.UtcNow);
Debug.WriteLine("Generated encryption key with ID "
    + encKeyPair.KeyId.ToString("X"));

PgpSignatureSubpacketGenerator encSubpckGen
    = new PgpSignatureSubpacketGenerator();
encSubpckGen.SetKeyFlags(false, PgpKeyFlags.CanEncryptCommunications
    | PgpKeyFlags.CanEncryptStorage);
masterSubpckGen.SetPreferredSymmetricAlgorithms(false,
    (from a in keyRingParams.SymmetricAlgorithms
        where a.IsSelected
        select (int) a.Value).ToArray());
masterSubpckGen.SetPreferredHashAlgorithms(false,
    (from a in keyRingParams.HashAlgorithms
        where a.IsSelected
        select (int) a.Value).ToArray());

/* Create the key ring. */
PgpKeyRingGenerator keyRingGen = new PgpKeyRingGenerator(
    PgpSignature.DefaultCertification,
    masterKeyPair,
    keyRingParams.Identity,
    keyRingParams.PrivateKeyEncryptionAlgorithm.Value,
    keyRingParams.GetPassword(),
    true,
    masterSubpckGen.Generate(),
    null,
    new SecureRandom());

/* Add encryption subkey. */
keyRingGen.AddSubKey(encKeyPair, encSubpckGen.Generate(), null);

/* Generate the key ring. */
keyRingGen.GenerateSecretKeyRing();

The code works so far, the debug output also prints meaningful key IDs. 到目前为止,代码可以正常工作,调试输出还可以打印出有意义的密钥ID。 However, if I save the private key to a file and import it to GnuPG, it shows sec 0s/FFFFFFFF 2013-07-30 for the master key. 但是,如果我将私钥保存到文件中并将其导入到GnuPG中,则主密钥显示为sec 0s/FFFFFFFF 2013-07-30 The encryption key is OK. 加密密钥确定。

Edit: gpg --list-packet shows 编辑: gpg --list-packet显示

:secret key packet:
        version 4, algo 3, created 1375205303, expires 0
        unknown algorithm 3

Algorithm 3 is PublicKeyAlgorithmTag.RsaSign , which I assume is OK for a master key (the Java sample does that, too), but GnuPG does not like this... 算法3是PublicKeyAlgorithmTag.RsaSign ,我认为对于主密钥是可以的(Java示例也可以),但是GnuPG不喜欢这样。

What am I doing wrong? 我究竟做错了什么?

Thanks in advance, Christoph 在此先感谢Christoph

In case someone else finds this useful for using BouncyCastle with C# (I just needed to do the same as above, but the code sample above was incomplete so I whipped up my own amateurish edited version). 万一其他人发现这对于将BouncyCastle与C#一起使用很有用(我只需要做与上面相同的操作,但是上面的代码示例不完整,所以我整理了自己的业余编辑版本)。 NOTE: This code was compiled and tested with LinqPad 注意:此代码是使用LinqPad编译和测试的



    void Main()
    {
        String Password = "hello world!";
        String Identity = "Slim Shady";

       PgpKeyRingGenerator krgen = generateKeyRingGenerator(Identity, Password);

       // Generate public key ring, dump to file.
       PgpPublicKeyRing pkr = krgen.GeneratePublicKeyRing();
       BufferedStream pubout = new BufferedStream(new FileStream(@"c:\temp\dummy.pkr", System.IO.FileMode.Create));
       pkr.Encode(pubout);
       pubout.Close();

       // Generate private key, dump to file.
       PgpSecretKeyRing skr = krgen.GenerateSecretKeyRing();
       BufferedStream secout = new BufferedStream(new FileStream(@"c:\temp\dummy.skr", System.IO.FileMode.Create));
       skr.Encode(secout);
       secout.Close();

    }

    public static PgpKeyRingGenerator generateKeyRingGenerator(String identity, String password) {

        KeyRingParams keyRingParams = new KeyRingParams();
        keyRingParams.Password = password;
        keyRingParams.Identity = identity;
        keyRingParams.PrivateKeyEncryptionAlgorithm = SymmetricKeyAlgorithmTag.Aes128;
        keyRingParams.SymmetricAlgorithms = new SymmetricKeyAlgorithmTag[] {
            SymmetricKeyAlgorithmTag.Aes256,
            SymmetricKeyAlgorithmTag.Aes192,
            SymmetricKeyAlgorithmTag.Aes128
        };

        keyRingParams.HashAlgorithms = new HashAlgorithmTag[] {
            HashAlgorithmTag.Sha256,
            HashAlgorithmTag.Sha1,
            HashAlgorithmTag.Sha384,
            HashAlgorithmTag.Sha512,
            HashAlgorithmTag.Sha224,
        };

        IAsymmetricCipherKeyPairGenerator generator
            = GeneratorUtilities.GetKeyPairGenerator("RSA");
        generator.Init(keyRingParams.RsaParams);

        /* Create the master (signing-only) key. */
        PgpKeyPair masterKeyPair = new PgpKeyPair(
            PublicKeyAlgorithmTag.RsaSign,
            generator.GenerateKeyPair(),
            DateTime.UtcNow);
        Debug.WriteLine("Generated master key with ID "
            + masterKeyPair.KeyId.ToString("X"));

        PgpSignatureSubpacketGenerator masterSubpckGen
            = new PgpSignatureSubpacketGenerator();
        masterSubpckGen.SetKeyFlags(false, PgpKeyFlags.CanSign
            | PgpKeyFlags.CanCertify);
        masterSubpckGen.SetPreferredSymmetricAlgorithms(false,
            (from a in keyRingParams.SymmetricAlgorithms
                select (int) a).ToArray());
        masterSubpckGen.SetPreferredHashAlgorithms(false,
            (from a in keyRingParams.HashAlgorithms
                select (int) a).ToArray());

        /* Create a signing and encryption key for daily use. */
        PgpKeyPair encKeyPair = new PgpKeyPair(
            PublicKeyAlgorithmTag.RsaGeneral,
            generator.GenerateKeyPair(),
            DateTime.UtcNow);
        Debug.WriteLine("Generated encryption key with ID "
            + encKeyPair.KeyId.ToString("X"));

        PgpSignatureSubpacketGenerator encSubpckGen = new PgpSignatureSubpacketGenerator();
        encSubpckGen.SetKeyFlags(false, PgpKeyFlags.CanEncryptCommunications | PgpKeyFlags.CanEncryptStorage);

        masterSubpckGen.SetPreferredSymmetricAlgorithms(false,
            (from a in keyRingParams.SymmetricAlgorithms
                select (int) a).ToArray());
        masterSubpckGen.SetPreferredHashAlgorithms(false,
            (from a in keyRingParams.HashAlgorithms
                select (int) a).ToArray());

        /* Create the key ring. */
        PgpKeyRingGenerator keyRingGen = new PgpKeyRingGenerator(
            PgpSignature.DefaultCertification,
            masterKeyPair,
            keyRingParams.Identity,
            keyRingParams.PrivateKeyEncryptionAlgorithm.Value,
            keyRingParams.GetPassword(),
            true,
            masterSubpckGen.Generate(),
            null,
            new SecureRandom());

        /* Add encryption subkey. */
        keyRingGen.AddSubKey(encKeyPair, encSubpckGen.Generate(), null);

        return keyRingGen;

    }

    // Define other methods and classes here
    class KeyRingParams {

        public SymmetricKeyAlgorithmTag? PrivateKeyEncryptionAlgorithm{ get; set; }
        public SymmetricKeyAlgorithmTag[] SymmetricAlgorithms{ get; set; }
        public HashAlgorithmTag[] HashAlgorithms{ get; set; }
        public RsaKeyGenerationParameters RsaParams{ get; set; }
        public string Identity{ get; set; }
        public string Password{ get; set; }
        //= EncryptionAlgorithm.NULL;

        public char[] GetPassword() {
            return Password.ToCharArray();
        }

        public KeyRingParams() {
            //Org.BouncyCastle.Crypto.Tls.EncryptionAlgorithm
            RsaParams = new RsaKeyGenerationParameters(BigInteger.ValueOf(0x10001), new SecureRandom(), 2048, 12);
        }

    }

To test this code, I just used another LinqPad script: 为了测试此代码,我只使用了另一个LinqPad脚本:

void Main()
{

    PGPEncryptDecrypt pgp = new PGPEncryptDecrypt();

    string passPhrase = "hello world!";

    //full path to file to encrypt
    string origFilePath = @"c:\Temp\test.txt"; 
    //folder to store encrypted file
    string encryptedFilePath = @"C:\Temp\Output\";
    //folder to store unencrypted file
    string unencryptedFilePath = @"C:\Temp\Output\";
    //path to public key file 
    string publicKeyFile = @"c:\Temp\dummy.pkr";
    //path to private key file (this file should be kept at client, AND in a secure place, far from prying eyes and tinkering hands)
    string privateKeyFile = @"c:\Temp\dummy.skr";

    pgp.Encrypt(origFilePath, publicKeyFile, encryptedFilePath);
    pgp.Decrypt(encryptedFilePath + "test.txt.asc", privateKeyFile, passPhrase, unencryptedFilePath);

}

// Define other methods and classes here
public class PGPEncryptDecrypt
{

   public PGPEncryptDecrypt()
   {

   }

   /**
   * A simple routine that opens a key ring file and loads the first available key suitable for
   * encryption.
   *
   * @param in
   * @return
   * @m_out
   * @
   */
   private static PgpPublicKey ReadPublicKey(Stream inputStream)
   {

       inputStream = PgpUtilities.GetDecoderStream(inputStream);
       PgpPublicKeyRingBundle pgpPub = new PgpPublicKeyRingBundle(inputStream);
       //
       // we just loop through the collection till we find a key suitable for encryption, in the real
       // world you would probably want to be a bit smarter about this.
       //
       //
       // iterate through the key rings.
       //
       foreach (PgpPublicKeyRing kRing in pgpPub.GetKeyRings())
       {

           foreach (PgpPublicKey k in kRing.GetPublicKeys())
           {

               if (k.IsEncryptionKey)
               {

                   return k;

               }


           }


       }

       throw new ArgumentException("Can't find encryption key in key ring.");

   }

   /**
   * Search a secret key ring collection for a secret key corresponding to
   * keyId if it exists.
   *
   * @param pgpSec a secret key ring collection.
   * @param keyId keyId we want.
   * @param pass passphrase to decrypt secret key with.
   * @return
   */
   private static PgpPrivateKey FindSecretKey(PgpSecretKeyRingBundle pgpSec, long keyId, char[] pass)
   {

       PgpSecretKey pgpSecKey = pgpSec.GetSecretKey(keyId);
       if (pgpSecKey == null)
       {

           return null;

       }

       return pgpSecKey.ExtractPrivateKey(pass);

   }

   /**
   * decrypt the passed in message stream
   */
   private static void DecryptFile(Stream inputStream, Stream keyIn, char[] passwd, string defaultFileName, string pathToSaveFile)
   {

       inputStream = PgpUtilities.GetDecoderStream(inputStream);
       try
       {

           PgpObjectFactory pgpF = new PgpObjectFactory(inputStream);
           PgpEncryptedDataList enc;
           PgpObject o = pgpF.NextPgpObject();
           //
           // the first object might be a PGP marker packet.
           //
           if (o is PgpEncryptedDataList)
           {

               enc = (PgpEncryptedDataList)o;

           }

           else
           {

               enc = (PgpEncryptedDataList)pgpF.NextPgpObject();

           }

           //
           // find the secret key
           //
           PgpPrivateKey sKey = null;
           PgpPublicKeyEncryptedData pbe = null;
           PgpSecretKeyRingBundle pgpSec = new PgpSecretKeyRingBundle(
           PgpUtilities.GetDecoderStream(keyIn));
           foreach (PgpPublicKeyEncryptedData pked in enc.GetEncryptedDataObjects())
           {

               sKey = FindSecretKey(pgpSec, pked.KeyId, passwd);
               if (sKey != null)
               {

                   pbe = pked;
                   break;

               }


           }

           if (sKey == null)
           {

               throw new ArgumentException("secret key for message not found.");

           }

           Stream clear = pbe.GetDataStream(sKey);
           PgpObjectFactory plainFact = new PgpObjectFactory(clear);
           PgpObject message = plainFact.NextPgpObject();
           if (message is PgpCompressedData)
           {

               PgpCompressedData cData = (PgpCompressedData)message;
               PgpObjectFactory pgpFact = new PgpObjectFactory(cData.GetDataStream());
               message = pgpFact.NextPgpObject();

           }

           if (message is PgpLiteralData)
           {

               PgpLiteralData ld = (PgpLiteralData)message;
               string outFileName = ld.FileName;
               if (outFileName.Length == 0)
               {

                   outFileName = defaultFileName;

               }

               Stream fOut = File.Create(pathToSaveFile + outFileName);
               Stream unc = ld.GetInputStream();
               Streams.PipeAll(unc, fOut);
               fOut.Close();

           }

           else if (message is PgpOnePassSignatureList)
           {

               throw new PgpException("encrypted message contains a signed message - not literal data.");

           }

           else
           {

               throw new PgpException("message is not a simple encrypted file - type unknown.");

           }

           if (pbe.IsIntegrityProtected())
           {

               if (!pbe.Verify())
               {

                   Console.Error.WriteLine("message failed integrity check");

               }

               else
               {

                   Console.Error.WriteLine("message integrity check passed");

               }


           }

           else
           {

               Console.Error.WriteLine("no message integrity check");

           }


       }

       catch (PgpException e)
       {

           Console.Error.WriteLine(e);
           Exception underlyingException = e.InnerException;
           if (underlyingException != null)
           {

               Console.Error.WriteLine(underlyingException.Message);
               Console.Error.WriteLine(underlyingException.StackTrace);

           }


       }


   }

   private static void EncryptFile(Stream outputStream, string fileName, PgpPublicKey encKey, bool armor, bool withIntegrityCheck)
   {

       if (armor)
       {

           outputStream = new ArmoredOutputStream(outputStream);

       }

       try
       {

           MemoryStream bOut = new MemoryStream();
           PgpCompressedDataGenerator comData = new PgpCompressedDataGenerator(
           CompressionAlgorithmTag.Zip);
           PgpUtilities.WriteFileToLiteralData(
           comData.Open(bOut),
           PgpLiteralData.Binary,
           new FileInfo(fileName));
           comData.Close();
           PgpEncryptedDataGenerator cPk = new PgpEncryptedDataGenerator(
           SymmetricKeyAlgorithmTag.Cast5, withIntegrityCheck, new SecureRandom());
           cPk.AddMethod(encKey);
           byte[] bytes = bOut.ToArray();
           Stream cOut = cPk.Open(outputStream, bytes.Length);
           cOut.Write(bytes, 0, bytes.Length);
           cOut.Close();
           if (armor)
           {

               outputStream.Close();

           }


       }

       catch (PgpException e)
       {

           Console.Error.WriteLine(e);
           Exception underlyingException = e.InnerException;
           if (underlyingException != null)
           {

               Console.Error.WriteLine(underlyingException.Message);
               Console.Error.WriteLine(underlyingException.StackTrace);

           }


       }


   }

   public void Encrypt(string filePath, string publicKeyFile, string pathToSaveFile)
   {

       Stream keyIn, fos;
       keyIn = File.OpenRead(publicKeyFile);
       string[] fileSplit = filePath.Split('\\');
       string fileName = fileSplit[fileSplit.Length - 1];
       fos = File.Create(pathToSaveFile + fileName + ".asc");
       EncryptFile(fos, filePath, ReadPublicKey(keyIn), true, true);
       keyIn.Close();
       fos.Close();

   }

   public void Decrypt(string filePath, string privateKeyFile, string passPhrase, string pathToSaveFile)
   {

       Stream fin = File.OpenRead(filePath);
       Stream keyIn = File.OpenRead(privateKeyFile);
       DecryptFile(fin, keyIn, passPhrase.ToCharArray(), new FileInfo(filePath).Name + ".out", pathToSaveFile);
       fin.Close();
       keyIn.Close();

   }


}

I still haven't done final tests on this, but it all seems to work (so far). 我仍未对此进行最终测试,但到目前为止一切似乎都奏效。 For LinqPad, add a reference to BouncyCastle.1.7.0\\lib\\Net40-Client\\BouncyCastle.Crypto.dll and my imports are: 对于LinqPad,添加对BouncyCastle.1.7.0 \\ lib \\ Net40-Client \\ BouncyCastle.Crypto.dll的引用 ,我的导入是:

\n\n
 Microsoft.VisualBasic Org.BouncyCastle.Bcpg Org.BouncyCastle.Bcpg.OpenPgp Org.BouncyCastle.Bcpg.Sig Org.BouncyCastle.Crypto Org.BouncyCastle.Crypto.Generators Org.BouncyCastle.Crypto.Parameters Org.BouncyCastle.Math Org.BouncyCastle.Security System System.Collections System.IO 
\n\n

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM