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

前端 未结 2 574
小鲜肉
小鲜肉 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.
        * <p>
        * To sign a file: SignedFileProcessor -s [-a] fileName secretKey passPhrase.<br/>
        * If -a is specified the output file will be "ascii-armored".</p>
        * <p>
        * To decrypt: SignedFileProcessor -v fileName publicKeyFile.</p>
        * <p>
        * <b>Note</b>: 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.</p>
        * <p>
        * <b>Note</b>: 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.</p>
        */
        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]");
                }
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-03 11:39
    public void SignFile(String fileName, 
                         Stream privateKeyStream,
                         String privateKeyPassword,                      
                         Stream outStream)
    {
    
        PgpSecretKey pgpSec = ReadSigningSecretKey(privateKeyStream);
        PgpPrivateKey pgpPrivKey = null;
    
        pgpPrivKey = pgpSec.ExtractPrivateKey(privateKeyPassword.ToCharArray());
        PgpSignatureGenerator sGen = new PgpSignatureGenerator(pgpSec.PublicKey.Algorithm, KeyStore.ParseHashAlgorithm(this.hash.ToString()));
    
        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);
    
        FileInfo file = new FileInfo(fileName);
        FileStream fIn = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
        PgpLiteralDataGenerator lGen = new PgpLiteralDataGenerator();
        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 PgpSecretKey ReadSigningSecretKey(Stream inStream)
    // throws IOException, PGPException, WrongPrivateKeyException
    {        
        PgpSecretKeyRingBundle pgpSec = CreatePgpSecretKeyRingBundle(inStream);
        PgpSecretKey key = null;
        IEnumerator rIt = pgpSec.GetKeyRings().GetEnumerator();
        while (key == null && rIt.MoveNext())
        {
            PgpSecretKeyRing kRing = (PgpSecretKeyRing)rIt.Current;
            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 WrongPrivateKeyException("Can't find signing key in key ring.");
        else
            return key;
    }
    
    0 讨论(0)
提交回复
热议问题