How to sign a txt file with a PGP key in C# using Bouncy Castle library

前端 未结 2 573
小鲜肉
小鲜肉 2021-01-03 10:49

Does anyone have an example of how I can sign a txt file, using a PGP key in C# and Bouncy Castle library. Not to encrypt the file, only to add a signature.

2条回答
  •  挽巷
    挽巷 (楼主)
    2021-01-03 11:28

    DidiSoft's code in a working fashion:

    using Org.BouncyCastle.Bcpg;
    using Org.BouncyCastle.Bcpg.OpenPgp;
    using Org.BouncyCastle.Security;
    
    
    namespace BouncyCastleTest.PGP
    {
    
        // http://www.programcreek.com/java-api-examples/index.php?api=org.bouncycastle.bcpg.HashAlgorithmTags
        // http://stackoverflow.com/questions/6337985/how-to-sign-a-txt-file-with-a-pgp-key-in-c-sharp-using-bouncy-castle-library
        class SignOnly
        {
    
    
            public void SignFile(string hashAlgorithm, string fileName, System.IO.Stream privateKeyStream
                , string privateKeyPassword, System.IO.Stream outStream)
            {
    
                PgpSecretKey pgpSec = ReadSigningSecretKey(privateKeyStream);
                PgpPrivateKey pgpPrivKey = null;
    
                pgpPrivKey = pgpSec.ExtractPrivateKey(privateKeyPassword.ToCharArray());
    
    
    
    
                PgpSignatureGenerator sGen = new PgpSignatureGenerator(pgpSec.PublicKey.Algorithm, ParseHashAlgorithm(hashAlgorithm));
    
                sGen.InitSign(PgpSignature.BinaryDocument, pgpPrivKey);
    
                foreach (string userId in pgpSec.PublicKey.GetUserIds())
                {
                    PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();
    
                    spGen.SetSignerUserId(false, userId);
                    sGen.SetHashedSubpackets(spGen.Generate());
                }
    
                CompressionAlgorithmTag compression = PreferredCompression(pgpSec.PublicKey);
                PgpCompressedDataGenerator cGen = new PgpCompressedDataGenerator(compression);
    
                BcpgOutputStream bOut = new BcpgOutputStream(cGen.Open(outStream));
                sGen.GenerateOnePassVersion(false).Encode(bOut);
    
                System.IO.FileInfo file = new System.IO.FileInfo(fileName);
                System.IO.FileStream fIn = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read);
                PgpLiteralDataGenerator lGen = new PgpLiteralDataGenerator();
                System.IO.Stream lOut = lGen.Open(bOut, PgpLiteralData.Binary, file);
    
                int ch = 0;
                while ((ch = fIn.ReadByte()) >= 0)
                {
                    lOut.WriteByte((byte)ch);
                    sGen.Update((byte)ch);
                }
    
                fIn.Close();
                sGen.Generate().Encode(bOut);
                lGen.Close();
                cGen.Close();
                outStream.Close();
            }
    
    
            public static PgpSecretKeyRingBundle CreatePgpSecretKeyRingBundle(System.IO.Stream keyInStream)
            {
                return new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(keyInStream));
            }
    
    
            public PgpSecretKey ReadSigningSecretKey(System.IO.Stream keyInStream) 
            {
                PgpSecretKeyRingBundle pgpSec = CreatePgpSecretKeyRingBundle(keyInStream);
                PgpSecretKey key = null;
                System.Collections.IEnumerator rIt = pgpSec.GetKeyRings().GetEnumerator();
                while (key == null && rIt.MoveNext())
                {
                    PgpSecretKeyRing kRing = (PgpSecretKeyRing)rIt.Current;
                    System.Collections.IEnumerator kIt = kRing.GetSecretKeys().GetEnumerator();
                    while (key == null && kIt.MoveNext())
                    {
                        PgpSecretKey k = (PgpSecretKey)kIt.Current;
                        if (k.IsSigningKey)
                            key = k;
                    }
                }
    
                if (key == null)
                    throw new System.Exception("Wrong private key - Can't find signing key in key ring.");
                else
                    return key;
            }
    
    
    
            public static string GetDigestName(int hashAlgorithm)
            {
                switch ((Org.BouncyCastle.Bcpg.HashAlgorithmTag)hashAlgorithm)
                {
                    case Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha1:
                        return "SHA1";
                    case Org.BouncyCastle.Bcpg.HashAlgorithmTag.MD2:
                        return "MD2";
                    case Org.BouncyCastle.Bcpg.HashAlgorithmTag.MD5:
                        return "MD5";
                    case Org.BouncyCastle.Bcpg.HashAlgorithmTag.RipeMD160:
                        return "RIPEMD160";
                    case Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha256:
                        return "SHA256";
                    case Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha384:
                        return "SHA384";
                    case Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha512:
                        return "SHA512";
                    case Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha224:
                        return "SHA224";
                    case Org.BouncyCastle.Bcpg.HashAlgorithmTag.Tiger192:
                        return "TIGER";
                    default:
                        throw new Org.BouncyCastle.Bcpg.OpenPgp.PgpException("unknown hash algorithm tag in GetDigestName: " + hashAlgorithm);
                }
            }
    
    
            public static Org.BouncyCastle.Bcpg.HashAlgorithmTag ParseHashAlgorithm(string hashAlgorithm)
            {
                switch (hashAlgorithm.ToUpper())
                {
                    case "SHA1":
                        return Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha1;
                    case "MD2":
                        return Org.BouncyCastle.Bcpg.HashAlgorithmTag.MD2;
                    case "MD5":
                        return Org.BouncyCastle.Bcpg.HashAlgorithmTag.MD5;
                    case "RIPEMD160":
                        return Org.BouncyCastle.Bcpg.HashAlgorithmTag.RipeMD160;
                    case "SHA256":
                        return Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha256;
                    case "SHA384":
                        return Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha384;
                    case "SHA512":
                        return Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha512;
                    case "SHA224":
                        return Org.BouncyCastle.Bcpg.HashAlgorithmTag.Sha224;
                    case "TIGER":
                        return Org.BouncyCastle.Bcpg.HashAlgorithmTag.Tiger192;
                    default:
                        throw new Org.BouncyCastle.Bcpg.OpenPgp.PgpException("unknown hash algorithm name in ParseHashAlgorithm: " + hashAlgorithm);
                }
            }
    
    
            public static CompressionAlgorithmTag PreferredCompression(PgpPublicKey publicKey)
            {
                return CompressionAlgorithmTag.BZip2;
            }
    
        }
    
    
    }
    

    There is also an example in the BouncyCastle source-code (including how to verify):

    using System;
    using System.Collections;
    using System.IO;
    
    
    using Org.BouncyCastle.Bcpg.OpenPgp;
    
    namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples
    {
        /**
        * A simple utility class that signs and verifies files.
        * 

    * To sign a file: SignedFileProcessor -s [-a] fileName secretKey passPhrase.
    * If -a is specified the output file will be "ascii-armored".

    *

    * To decrypt: SignedFileProcessor -v fileName publicKeyFile.

    *

    * Note: this example will silently overwrite files, nor does it pay any attention to * the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase * will have been used.

    *

    * Note: the example also makes use of PGP compression. If you are having difficulty Getting it * to interoperate with other PGP programs try removing the use of compression first.

    */ public sealed class SignedFileProcessor { private SignedFileProcessor() {} /** * verify the passed in file as being correctly signed. */ private static void VerifyFile( Stream inputStream, Stream keyIn) { inputStream = PgpUtilities.GetDecoderStream(inputStream); PgpObjectFactory pgpFact = new PgpObjectFactory(inputStream); PgpCompressedData c1 = (PgpCompressedData) pgpFact.NextPgpObject(); pgpFact = new PgpObjectFactory(c1.GetDataStream()); PgpOnePassSignatureList p1 = (PgpOnePassSignatureList) pgpFact.NextPgpObject(); PgpOnePassSignature ops = p1[0]; PgpLiteralData p2 = (PgpLiteralData) pgpFact.NextPgpObject(); Stream dIn = p2.GetInputStream(); PgpPublicKeyRingBundle pgpRing = new PgpPublicKeyRingBundle(PgpUtilities.GetDecoderStream(keyIn)); PgpPublicKey key = pgpRing.GetPublicKey(ops.KeyId); Stream fos = File.Create(p2.FileName); ops.InitVerify(key); int ch; while ((ch = dIn.ReadByte()) >= 0) { ops.Update((byte)ch); fos.WriteByte((byte) ch); } fos.Close(); PgpSignatureList p3 = (PgpSignatureList)pgpFact.NextPgpObject(); PgpSignature firstSig = p3[0]; if (ops.Verify(firstSig)) { Console.Out.WriteLine("signature verified."); } else { Console.Out.WriteLine("signature verification failed."); } } /** * Generate an encapsulated signed file. * * @param fileName * @param keyIn * @param outputStream * @param pass * @param armor */ private static void SignFile( string fileName, Stream keyIn, Stream outputStream, char[] pass, bool armor, bool compress) { if (armor) { outputStream = new ArmoredOutputStream(outputStream); } PgpSecretKey pgpSec = PgpExampleUtilities.ReadSecretKey(keyIn); PgpPrivateKey pgpPrivKey = pgpSec.ExtractPrivateKey(pass); PgpSignatureGenerator sGen = new PgpSignatureGenerator(pgpSec.PublicKey.Algorithm, HashAlgorithmTag.Sha1); sGen.InitSign(PgpSignature.BinaryDocument, pgpPrivKey); foreach (string userId in pgpSec.PublicKey.GetUserIds()) { PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator(); spGen.SetSignerUserId(false, userId); sGen.SetHashedSubpackets(spGen.Generate()); // Just the first one! break; } Stream cOut = outputStream; PgpCompressedDataGenerator cGen = null; if (compress) { cGen = new PgpCompressedDataGenerator(CompressionAlgorithmTag.ZLib); cOut = cGen.Open(cOut); } BcpgOutputStream bOut = new BcpgOutputStream(cOut); sGen.GenerateOnePassVersion(false).Encode(bOut); FileInfo file = new FileInfo(fileName); PgpLiteralDataGenerator lGen = new PgpLiteralDataGenerator(); Stream lOut = lGen.Open(bOut, PgpLiteralData.Binary, file); FileStream fIn = file.OpenRead(); int ch = 0; while ((ch = fIn.ReadByte()) >= 0) { lOut.WriteByte((byte) ch); sGen.Update((byte)ch); } fIn.Close(); lGen.Close(); sGen.Generate().Encode(bOut); if (cGen != null) { cGen.Close(); } if (armor) { outputStream.Close(); } } public static void Main( string[] args) { // TODO provide command-line option to determine whether to use compression in SignFile if (args[0].Equals("-s")) { Stream keyIn, fos; if (args[1].Equals("-a")) { keyIn = File.OpenRead(args[3]); fos = File.Create(args[2] + ".asc"); SignFile(args[2], keyIn, fos, args[4].ToCharArray(), true, true); } else { keyIn = File.OpenRead(args[2]); fos = File.Create(args[1] + ".bpg"); SignFile(args[1], keyIn, fos, args[3].ToCharArray(), false, true); } keyIn.Close(); fos.Close(); } else if (args[0].Equals("-v")) { using (Stream fis = File.OpenRead(args[1]), keyIn = File.OpenRead(args[2])) { VerifyFile(fis, keyIn); } } else { Console.Error.WriteLine("usage: SignedFileProcessor -v|-s [-a] file keyfile [passPhrase]"); } } } }

提交回复
热议问题