如何使用 Bouncy Castle 或其他 C# 库在 C# 中验证此 PGP 消息

问题描述 投票:0回答:4

我需要做的就是验证下面的消息,但我无法让 Bouncy Castle 接收数据并给出公钥验证消息。我很高兴它是免费使用的其他 Lib。这将嵌入到我的应用程序中,该应用程序通过 Internet 接收数据,因此我希望尽可能保留所有托管代码。

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

SCI Version: 1.0
SCI Code: 1
SCI Reason: OK
SCI Balance: 0.00050000

-----BEGIN PGP SIGNATURE-----
Version: GnuPG/MBC v1.0

iQEcBAEBAgAGBQJOGSuYAAoJEJ+5g06lAnqFkdsH/0NoqQbNvR8ZPe8D4gL4gvff
6K1t2LOt0sQGj+RSPeEbag7ZnVNI65LiES/yie1N6cXMkFgb9/ttjxi9/wlbxD/j
gSkuZ6mT9Oc5ExLsRZq9ygytvVs7Ol7uQm6oxDzJX1JMs0ls2EwJbmmpTEOHn8Av
dGlxdZeh+3RlqHJmOdssQCJ0cw5VXuj5vfP35OYz2zO2+sNg0eCXdR5Ml+2S7n3U
n9VHPEECg72LvpxF/y/nApopXoHpwECXoBwHgyd9QIIw1IJgalyRLDmAJ2WXdROV
ln2Mkt/km3KtBS3h4QL407wi/KhgZ4tFohZupt7zq2zUwtHWOhbL2KSUu939OKk=
=mIjM
-----END PGP SIGNATURE-----
c# bouncycastle pgp verify
4个回答
4
投票

对于那些感兴趣的人,我在 BouncyCastle 源代码中发现了这个确切任务的示例。您需要下载源代码而不是二进制文件来获取示例,它似乎有针对所有不同 OpenPGP 用例的示例。


3
投票

按照Seer的建议看这个例子我终于运行了消息验证。

我有方法

VerifyFile
,它接受签名的消息和公钥,如果验证通过则返回消息的内容。例如它可以这样使用:

string key = @"-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BCPG C# v1.6.1.0

mQENBFpc87wBCACK5FG6Z70iovzSzQF7OEB/YbKF7SPS1/GZAQhp/2n2G8x5Lxj5
/CKqR8JLj1+222unuWgfqvfny0fLvttt1r6lAH/kqDYMBg26GTbZy93R5BYatBjd
pzYl/lIyKxc/QwDdZm8zNxeUpDSfoe9jVULOg0MiMDtdQupOf6CanlEioXfyf88F
1BLcJyFSckaYieosBw5hnnI+1cZZ3k+4HpDJJslVzngfTPWRibtN5PKff1CKP55E
ME99XkuPDaNL7XZmu7lZSEUN3jJFVydZQrDkvxddihzV4pTgRI3gDAFoJxxIYZX3
JsQAJItlqq8bBsQ+bKPikgAiMySGcEi+ilI5ABEBAAG0GnNoYWxhbWFub3YubWFy
aW5AZ21haWwuY29tiQEcBBABAgAGBQJaXPO8AAoJEBvHdfmVFHzkvHEH/179VAdH
gWRN5HVprsp4vHP3q1CJV9j+fPlQIZU3JEwrM+INxzpfSqZeN4NwB7yoo2NCdCNP
Ndg8zhiuEYM51hNtqU5cwYBcaAbm1so6TSVo8i4nrfN3+oDYEfYPqglNrd1V233J
oyLriwpGkR6RBYMY2q2Re+EFNR1bxUmeE0wnb8FOodRCSh0Wd3Iy9mvmhv5voHIr
aZzgsuifGw1JilSu9+RoC6b1CHb9jUkWQ/odkTvl5/rxA14TKstgoLoSLHktYQfw
le6B8+lPtmODtagWoDEeR/M0zm/wyCOt5wqjjJCgvaipUaA+oiijIYwCpqUBwfm3
DZ9DStGHGVxQQnc=
=s91O
-----END PGP PUBLIC KEY BLOCK-----
";
string message = @"-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

test tes tes ts tse tse t
-----BEGIN PGP SIGNATURE-----
Version: OpenPGP.js v1.0.1
Comment: http://openpgpjs.org

wsBcBAEBCAAQBQJaXP5WCRAbx3X5lRR85AAAUcoH/jtcyWcpTVyXyz/ptWLo
Hx+g51EeeA0Hpq7kZCXu4FuyhNn/QvnvKyt9qegxQoRSZhT37ln8t80NW6sS
B4XVFziq8TBkjPuaYBI/ijbLigdPMEi81PsOpIXx3BXKCt27TLmUVHpFTWPa
u2NQUQl3k3Xc0H1sy1A+jmjfvCyqWxTOU1IY4rlzRKHdp+D1oCz5iKfyfUko
ktAZgqOMx5pWL975YhM793MS8aYqhOdQpeuW401fm18xxwE4x6svSSys+qq8
MdkL/i7YVjUKr/M8SIrGPb/IjKwClM7jfpN+sHv0p/GcQ7J1kmXYUdA6AJp5
Z0vYk4aPcMSlrPwdRX21I9w=
=zXfe
-----END PGP SIGNATURE-----
"; 

MemoryStream messageStream = new MemoryStream(Encoding.ASCII.GetBytes(message));
MemoryStream keyStream = new MemoryStream(Encoding.ASCII.GetBytes(key));

try {
    var msg= VerifyFile(messageStream, 
            PgpUtilities.GetDecoderStream(keyStream));
    // verification passes msg is the content of the message
} catch (Exception e) {
    // verification fails
}

这里是实现:

private static String VerifyFile(Stream inputStream, Stream keyIn)
    {
        ArmoredInputStream aIn = new ArmoredInputStream(inputStream);
        MemoryStream outStr = new MemoryStream(); // File.Create(resultName);


        //
        // write out signed section using the local line separator.
        // note: trailing white space needs to be removed from the end of
        // each line RFC 4880 Section 7.1
        //
        MemoryStream lineOut = new MemoryStream();
        int lookAhead = ReadInputLine(lineOut, aIn);
        byte[] lineSep = LineSeparator;

        if (lookAhead != -1 && aIn.IsClearText())
        {
            byte[] line = lineOut.ToArray();
            outStr.Write(line, 0, GetLengthWithoutSeparatorOrTrailingWhitespace(line));
            outStr.Write(lineSep, 0, lineSep.Length);

            while (lookAhead != -1 && aIn.IsClearText())
            {
                lookAhead = ReadInputLine(lineOut, lookAhead, aIn);

                line = lineOut.ToArray();
                outStr.Write(line, 0, GetLengthWithoutSeparatorOrTrailingWhitespace(line));
                outStr.Write(lineSep, 0, lineSep.Length);
            }
        }
        else
        {
            // a single line file
            if (lookAhead != -1)
            {
                byte[] line = lineOut.ToArray();
                outStr.Write(line, 0, GetLengthWithoutSeparatorOrTrailingWhitespace(line));
                outStr.Write(lineSep, 0, lineSep.Length);
            }
        }
        outStr.Flush();
        //outStr.Close();


        PgpPublicKeyRingBundle pgpRings = new PgpPublicKeyRingBundle(keyIn);

        PgpObjectFactory pgpFact = new PgpObjectFactory(aIn);
        PgpSignatureList p3 = (PgpSignatureList)pgpFact.NextPgpObject();
        PgpSignature sig = p3[0];

        var key = pgpRings.GetPublicKey(sig.KeyId);
        if (key == null)
        {
            throw new Exception("Can't verify the message signature.");
        }
        sig.InitVerify(key);

        //
        // read the input, making sure we ignore the last newline.
        //
        outStr.Seek(0, SeekOrigin.Begin);
        StreamReader reader = new StreamReader(outStr);
        string messageContent = reader.ReadToEnd();

        outStr.Seek(0, SeekOrigin.Begin);

        Stream sigIn = outStr; //File.OpenRead(resultName);

        lookAhead = ReadInputLine(lineOut, sigIn);

        ProcessLine(sig, lineOut.ToArray());

        if (lookAhead != -1)
        {
            do
            {
                lookAhead = ReadInputLine(lineOut, lookAhead, sigIn);

                sig.Update((byte)'\r');
                sig.Update((byte)'\n');

                ProcessLine(sig, lineOut.ToArray());
            }
            while (lookAhead != -1);
        }

        sigIn.Close();

        if (sig.Verify()) {
            // signature verified
            return messageContent;
        } else {
            // signature verification failed
            throw new Exception("Can't verify the message signature.");
        }
    }


    private static int ReadInputLine(
            MemoryStream bOut,
            Stream fIn)
    {
        bOut.SetLength(0);

        int lookAhead = -1;
        int ch;

        while ((ch = fIn.ReadByte()) >= 0)
        {
            bOut.WriteByte((byte)ch);
            if (ch == '\r' || ch == '\n')
            {
                lookAhead = ReadPassedEol(bOut, ch, fIn);
                break;
            }
        }

        return lookAhead;
    }

    private static int ReadPassedEol(
        MemoryStream bOut,
        int lastCh,
        Stream fIn)
    {
        int lookAhead = fIn.ReadByte();

        if (lastCh == '\r' && lookAhead == '\n')
        {
            bOut.WriteByte((byte)lookAhead);
            lookAhead = fIn.ReadByte();
        }

        return lookAhead;
    }

    private static void ProcessLine(
            PgpSignature sig,
            byte[] line)
    {
        // note: trailing white space needs to be removed from the end of
        // each line for signature calculation RFC 4880 Section 7.1
        int length = GetLengthWithoutWhiteSpace(line);
        if (length > 0)
        {
            sig.Update(line, 0, length);
        }
    }

    private static void ProcessLine(
        Stream aOut,
        PgpSignatureGenerator sGen,
        byte[] line)
    {
        int length = GetLengthWithoutWhiteSpace(line);
        if (length > 0)
        {
            sGen.Update(line, 0, length);
        }

        aOut.Write(line, 0, line.Length);
    }

    private static int GetLengthWithoutSeparatorOrTrailingWhitespace(byte[] line)
    {
        int end = line.Length - 1;

        while (end >= 0 && IsWhiteSpace(line[end]))
        {
            end--;
        }

        return end + 1;
    }

    private static bool IsLineEnding(
        byte b)
    {
        return b == '\r' || b == '\n';
    }

    private static int GetLengthWithoutWhiteSpace(
        byte[] line)
    {
        int end = line.Length - 1;

        while (end >= 0 && IsWhiteSpace(line[end]))
        {
            end--;
        }

        return end + 1;
    }

    private static bool IsWhiteSpace(
        byte b)
    {
        return IsLineEnding(b) || b == '\t' || b == ' ';
    }

    private static int ReadInputLine(
        MemoryStream bOut,
        int lookAhead,
        Stream fIn)
    {
        bOut.SetLength(0);

        int ch = lookAhead;

        do
        {
            bOut.WriteByte((byte)ch);
            if (ch == '\r' || ch == '\n')
            {
                lookAhead = ReadPassedEol(bOut, ch, fIn);
                break;
            }
        }
        while ((ch = fIn.ReadByte()) >= 0);

        if (ch < 0)
        {
            lookAhead = -1;
        }

        return lookAhead;
    }

    private static byte[] LineSeparator
    {
        get { return Encoding.ASCII.GetBytes(Environment.NewLine); }
    }

2
投票
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
投票

如果做SignFile,生成签名和消息结合的封装签名文件,之后消息如何取回?

-----BEGIN PGP SIGNATURE-----
Version: BCPG v1.61

iQFTBAABAgA9BQJkLR6cNhxzZWJfcHVibGljX3Rlc3Rfc2lnbl9lbmNyeXB0XzIw
NDggPEJJU1N1cHBvcnRAc2ViLnNlPgAKCRD0wi65TXX6U9cfCACOf8rJMhw7Toj6
QGH1tapMoBWqlq54UGxzzC8QM/kbxEKmzWvIUZlUr9sH6S8nxaTsZ7QMGPFqhXep
JutXLrTg/W1tssXFLNYC8YLOxOxF/F/Fxu1uaDkc9LgClNei2f3XHjvV0myKnbH/
k9sGGKRPWLCj1qjwUbdg9dfp/AWHrweL6IlmrX3MM1gvbJqy9w1rfwjyuaomCXZS
etSCFsRDt8P5VXoNTvSTWigDSP2dns9u38nAIoIwKs7ZY4/1exnwGiRlxsE1Wxdv
1ZXcg+9JCIWZP3SN35LH4CjeyOKveme3N3+vKXJ5LFiQy2oOL05VGR0aoz/qbiB2
2K0ECQ89y8JHYiRjNmVlNjQyMC1kMzgwLTExZWQtOWE4ZC00ODBiMGExMDE4NWRk
LR6cPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48RG9jdW1l
bnQgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1p
bnN0YW5jZSIgeHNpOnNjaGVtYUxvY2F0aW9uPSJ1cm46aXNvOnN0ZDppc286MjAw
MjI6dGVjaDp4c2Q6cGFpbi4wMDIuMDAxLjAzIHBhaW4uMDAyLjAwMS4wMy54c2Qi
IHhtbG5zPSJ1cm46aXNvOnN0ZDppc286MjAwMjI6dGVjaDp4c2Q6cGFpbi4wMDIu
MDAxLjAzIj48Q3N0bXJQbXRTdHNScHQ+PEdycEhkcj48TXNnSWQ+MjAyOTcyMDwv
TXNnSWQ+PENyZUR0VG0+MjAyMy0wNC0wNVQwOTowOToxNiswMTowMDwvQ3JlRHRU
bT48SW5pdGdQdHk+PElkPjxPcmdJZD48QklDT3JCRUk+RVNTRVNFU1NYWFg8L0JJ
Q09yQkVJPjxPdGhyPjxJZD4wMDMzMjk0NTQ3MDAwNTwvSWQ+PFNjaG1lTm0+PENk
PkJBTks8L0NkPjwvU2NobWVObT48L090aHI+PC9PcmdJZD48L0lkPjwvSW5pdGdQ
dHk+PC9HcnBIZHI+PE9yZ25sR3JwSW5mQW5kU3RzPjxPcmdubE1zZ0lkPjIwMjMx
MTM2MjwvT3JnbmxNc2dJZD48T3JnbmxNc2dObUlkPnBhaW4uMDAxLjAwMS4wMzwv
T3JnbmxNc2dObUlkPjxPcmdubENyZUR0VG0+MjAyMy0wMy0xN1QxNToyMTo1MTwv
T3JnbmxDcmVEdFRtPjxPcmdubE5iT2ZUeHM+MTwvT3JnbmxOYk9mVHhzPjxHcnBT
dHM+QUNUQzwvR3JwU3RzPjwvT3JnbmxHcnBJbmZBbmRTdHM+PC9Dc3RtclBtdFN0
c1JwdD48L0RvY3VtZW50Pg==
=U3wm
-----END PGP SIGNATURE-----
© www.soinside.com 2019 - 2024. All rights reserved.