Ошибка при расшифровке MIME-содержимого — встречено неверное значение тега ASN1

Я написал функцию для создания MIME-сообщения и шифрования содержимого с помощью открытого сертификата пользователя, который хранится в хранилище сертификатов, в то время как закрытые ключи хранятся на моей смарт-карте. Моя функция просто сохраняет содержимое pkcs7mime в файл. У меня есть еще одна тестовая функция, которая просто читает файл и пытается расшифровать содержимое. Однако, когда я пытаюсь расшифровать, я получаю сообщение об ошибке: встречено неверное значение тега ASN1.

Мой код функции шифрования:

private static void EncryptMime()
    {
        MimeMessage mm = new MimeMessage();
        var ctx = new WindowsSecureMimeContext();
        mm.From.Add(new MailboxAddress("Bob", "[email protected]"));
        mm.To.Add(new MailboxAddress("Alice", "[email protected]"));

        mm.Subject = "Smime Test";

        mm.Body = new TextPart("plain")
        {
            Text = @"This is a test."
        };

        var signer = mm.From.Mailboxes.FirstOrDefault();
        var recipients = mm.To.Mailboxes.AsEnumerable();
        ApplicationPkcs7Mime p7m = ApplicationPkcs7Mime.SignAndEncrypt(ctx, signer, DigestAlgorithm.Sha1, recipients, mm.Body);

        MemoryStream ms = new MemoryStream();
        p7m.WriteTo(ms, true);
        byte[] bytes = ms.GetBuffer();
        File.WriteAllBytes("smime.p7m", bytes);
    }

Вот моя функция расшифровки:

private static void DecryptMime()
    {

        CryptographyContext.Register(typeof(WindowsSecureMimeContext));

        string messagetext = "";
        // Read the p7m file           
        byte[] bytes = 
        File.ReadAllBytes("smime.p7m");
        MemoryStream ms = new MemoryStream(bytes);
        ApplicationPkcs7Mime p7m = new ApplicationPkcs7Mime(SecureMimeType.EnvelopedData, ms);
        // WindowsSecureMimeContext ctx = new WindowsSecureMimeContext(StoreLocation.CurrentUser);

        if (p7m != null && p7m.SecureMimeType == SecureMimeType.EnvelopedData)
        {
            Console.WriteLine("Decrypting message...");
            try
            {
                p7m = p7m.Decrypt() as ApplicationPkcs7Mime;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error decrypting: " + ex.Message);
            }
        }

        if (p7m != null && p7m.SecureMimeType == SecureMimeType.CompressedData)
        {
            Console.WriteLine("Decompressing message...");
            p7m = p7m.Decompress() as ApplicationPkcs7Mime;
        }

        if (p7m != null && p7m.SecureMimeType == SecureMimeType.SignedData)
        {
            Console.WriteLine("Verifying message...");
            p7m.Verify(out MimeEntity entity);
            MimeMessage mm = new MimeMessage(entity);
            messagetext = mm.GetTextBody(MimeKit.Text.TextFormat.Text);
            Console.WriteLine("Decrypted Message: " + messagetext);
        }           
    }

Изменить: я попытался изолировать проблему, используя следующий код:

MemoryStream ms = new MemoryStream();
        p7m.WriteTo(ms);    
ApplicationPkcs7Mime new_p7m = new ApplicationPkcs7Mime(SecureMimeType.EnvelopedData, ms);  

        if (new_p7m != null && new_p7m.SecureMimeType == SecureMimeType.EnvelopedData)
        {
            Console.WriteLine("Decrypting message...");
            try
            {
                new_p7m = new_p7m.Decrypt() as ApplicationPkcs7Mime;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error decrypting: " + ex.Message);
            }
        }

Таким образом, проблема, кажется, начинается еще с функции WriteTo()...

Обновление № 2: Итак, я сделал toString как для объекта p7m, так и для объекта new_p7m. Видимо содержание разное...

p7m: Content-Type: application/pkcs7-mime; smime-type=enveloped-data;
    name=smime.p7m
Content-Disposition: attachment; filename=smime.p7m
Content-Transfer-Encoding: base64

MIIOOwYJKoZIhvcNAQcDoIIOLDCCDigCAQIxggEwMIIBLAIBAoAU2NQqDvYHJuMeC27IpyiV
....

New p7m: Content-Type: application/pkcs7-mime; smime-type=enveloped-data;
    name=smime.p7m
Content-Disposition: attachment; filename=smime.p7m
Content-Transfer-Encoding: base64

Q29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9wa2NzNy1taW1lOyBzbWltZS10eXBlPWVudmVs
....

То, как-то реальное содержание меняется... очень странно...


person Kelvin L    schedule 20.12.2018    source источник


Ответы (1)


Держу пари, что ваш код для сохранения содержимого на диск записывает поврежденный файл:

MemoryStream ms = new MemoryStream();
p7m.WriteTo(ms, true);
byte[] bytes = ms.GetBuffer();
File.WriteAllBytes("smime.p7m", bytes);

Проблема с приведенным выше кодом заключается в том, что GetBuffer() в большинстве случаев возвращает больше байтов, чем фактически используется потоком памяти. Приведенная выше логика также сохранит содержимое в base64, что, вероятно, не то, что вам нужно. Следующий фрагмент кода декодирует содержимое base64 в файл smime.p7m:

using (var stream = File.Create ("smime.p7m"))
    p7m.Content.DecodeTo (stream);

Затем, чтобы восстановить часть MIME обратно для расшифровки, сделайте следующее:

var bytes = File.ReadAllBytes ("smime.p7m");
var ms = new MemoryStream (bytes);
var p7m = new ApplicationPkcs7Mime (SecureMimeType.EnvelopedData, ms);

Проблема, с которой вы столкнулись, заключается в том, что контент ранее был закодирован в base64, а этого не должно было быть.

Конечно, если на самом деле нет необходимости сохранять только необработанный контент, а затем перестраивать часть application/pkcs7-mime сложным путем, почему бы просто не сохранить всю часть MIME вот так?

using (var stream = File.Create ("smime.p7m"))
    p7m.WriteTo (stream);

И затем перезагрузите его следующим образом:

using (var stream = File.OpenRead ("smime.p7m"))
    p7m = (ApplicationPkcs7Mime) MimeEntity.Load (stream);

Тогда вам не придется беспокоиться о том, что что-то не так.

person jstedfast    schedule 20.12.2018
comment
Я пробовал и ToArray(), и код, который вы написали, оба по-прежнему дают мне ту же ошибку. Но чтобы проверить ваши подозрения, я попытался расшифровать p7m сразу после шифрования, и, похоже, он смог правильно расшифровать. Так что, вероятно, я неправильно записываю буфер в файл. - person Kelvin L; 21.12.2018
comment
Я обновил свой ответ. Контент, который вы сохраняли, был закодирован в base64, и вы этого не хотите. - person jstedfast; 21.12.2018
comment
Круто, это сработало. Значит, контент изначально был закодирован в base64? Есть ли способ установить кодировку? - person Kelvin L; 21.12.2018
comment
Да: p7m.ContentTransferEncoding = ContentEncoding.Base64; - person jstedfast; 21.12.2018
comment
Итак, если я установлю для ContentEncoding значение по умолчанию, функция WriteTo() будет работать так, как ожидалось? - person Kelvin L; 21.12.2018
comment
Должен, да, но в целом, не делайте этого. Используйте part.Content.DecodeTo() вместо того, чтобы пытаться перехитрить MimeKit. Также: почему вы все равно сохраняете только контент? Почему не вся часть MIME? Было бы куда проще. - person jstedfast; 21.12.2018
comment
По сути, этот код, который я пишу, является частью более крупного проекта. Я создаю плагин, который извлекает только вложение файла p7m из веб-почты (например, OWA) для расшифровки. Точно так же, когда я шифрую тело сообщения, я хочу создать только содержимое и прикрепить его как вложение p7m. Это довольно странное требование... Но почтовый клиент, которому я отправляю это письмо, может распознавать вложение .p7m. - person Kelvin L; 21.12.2018
comment
Ладно, думаю, тогда это имеет смысл. Тем не менее, я все же рекомендую использовать метод p7m.Content.DecodeTo(stream);. - person jstedfast; 21.12.2018