首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用p7+p8从C#文件中获取PEM

使用p7+p8从C#文件中获取PEM
EN

Stack Overflow用户
提问于 2018-09-22 19:11:28
回答 2查看 2K关注 0票数 9

Intro

我正在将一个Java库转换为.Net。

该图书馆是一个多态化名解密的实现,将在荷兰用于解密欧洲eIDAS电子识别服务领域的“eIDAS”s。

我已经转换了大部分库,并与Java版本的作者一起验证了结果。下一步是让荷兰公司实际使用.Net库,这就是我在过去2周里一直困在这里的地方。

该算法使用PEM文件中的椭圆曲线作为计算的一部分。但是客户端(库的用户)将以p7和p8文件的形式接收该文件,您可以转换/提取/解码(?)到PEM数据。

问题

如何从te p7+p8文件到C#中的PEM字符串?

最好只使用System.Security.Cryptography.Pkcs,但我目前在其他部分使用BouncyCastle (因为Java使用了)。没有在下面列出,但我也尝试使用SignedCms和EnvelopedCms来做这件事,但是除了(对我来说)无法理解的错误之外,什么也没有得到。我在密码学方面没有太多的经验,但是在过去的几周里我学到了很多。

如果我正确地理解了它,我会解释为p7文件是PEM消息的信封,并且信封是使用p8文件中的私钥签名/加密的吗?

代码语言:javascript
复制
public static string ConvertToPem(string p7File, string p8File)
{
    var p7Data = File.ReadAllBytes(p7File);
    var p8Data = File.ReadAllBytes(p8File);

    // Java version gets the private key like this:
    // KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(bytesArray));
    var privateKey = PrivateKeyFactory.CreateKey(p8Data);

    var parser = new CmsEnvelopedDataParser(p7Data);
    var recipients = parser.GetRecipientInfos().GetRecipients().OfType<RecipientInformation>();
    var recipientInformation = recipients.First();

    //Java version gets the message like this:
    //final byte[] message = keyInfo.getContent(new JceKeyTransEnvelopedRecipient(key).setProvider("BC"));

    var keyInfo = (KeyTransRecipientInformation)recipientInformation;
    var message = keyInfo.GetContent(privateKey);

    return Encoding.ASCII.GetString(message);
}

更新8-10-2018在库作者的提示之后,我尝试跳过自动转换到PEM的问题,只使用openssl对其进行解密。不幸的是,openssl命令解密文件也失败了!无论是在Windows上还是在Linux上。奇怪的是,这是使用在Java库中使用时工作非常好的相同文件来完成的。p8是否腐败?它是否只在Java JceKeyTransEnvelopedRecipient中使用时才兼容?

代码语言:javascript
复制
openssl cms -decrypt -inform DER -in dv_keys_ID_D_oin.p7 -inkey privatep8.key -out id.pem

(我也试着用PEM代替DER,但没有用。这些文件在GitHub回购程序中)

Update9-10-2018感谢卡尔,他发现了看似损坏的p8文件的原因。我们必须首先将二进制DER p8转换为base64编码的PEM,而不是使用openssl直接解密它。

代码语言:javascript
复制
openssl pkcs8 -inform der -outform pem -in private.p8 -out private-p8.pem -topk8 -nocrypt

我们还可以在c#中读取p8文件中的字节,将它们转换为Base64,并在其周围添加BEGIN/END私钥头/页脚。

资源

您可以看到此代码在我的项目中作为单元测试被使用并失败。该项目还包括匹配用于测试的p7、p8和PEM文件。

在这里可以找到Java版本:https://github.com/BramvanPelt/PPDecryption

我的工作正在进行中的版本可以在这里找到:https://github.com/MartijnKooij/PolymorphicPseudonymisation

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-10-13 22:14:40

最后,我成功地解密了消息;看起来,BouncyCastle API忽略了SHA-256 OAEP指令,并坚持使用SHA-1 OAEP,这将导致填充异常。此外,据我所知,Microsoft利用了仅支持SHA-1 OAEP支持的X509Certificate2。对于SHA-256 OAEP支持,需要更新的RsaCng。我想我们需要用corefx (https://github.com/dotnet/corefx)和bc-csharp (https://github.com/bcgit/bc-csharp)来开罚单。

以下c#代码将解密消息;使用Microsoft:

代码语言:javascript
复制
// Read the RSA private key:
var p8Data = File.ReadAllBytes(@"resources\private.p8");    
CngKey key = CngKey.Import(p8Data, CngKeyBlobFormat.Pkcs8PrivateBlob);
var rsaprovider = new RSACng(key);

// Process the enveloped CMS structure:
var p7Data = File.ReadAllBytes(@"resources\p7\ID-4.p7");
var envelopedCms = new System.Security.Cryptography.Pkcs.EnvelopedCms();
envelopedCms.Decode(p7Data);
var recipients = envelopedCms.RecipientInfos;
var firstRecipient = recipients[0];

// Decrypt the AES-256 CBC session key; take note of enforcing OAEP SHA-256:
var result = rsaprovider.Decrypt(firstRecipient.EncryptedKey, RSAEncryptionPadding.OaepSHA256);

// Build out the AES-256 CBC decryption:
RijndaelManaged alg = new RijndaelManaged();
alg.KeySize = 256;
alg.BlockSize = 128;
alg.Key = result;

// I used an ASN.1 parser (https://lapo.it/asn1js/) to grab the AES IV from the PKCS#7 file.
// I could not find an API call to get this from the enveloped CMS object:
string hexstring = "919D287AAB62B672D6912E72D5DA29CD"; 
var iv = StringToByteArray(hexstring);
alg.IV = iv;
alg.Mode = CipherMode.CBC;
alg.Padding = PaddingMode.PKCS7;

// Strangely both BouncyCastle as well as the Microsoft API report 406 bytes;
// whereas https://lapo.it/asn1js/ reports only 400 bytes. 
// The 406 bytes version results in an System.Security.Cryptography.CryptographicException 
// with the message "Length of the data to decrypt is invalid.", so we strip it to 400 bytes:
byte[] content = new byte[400];
Array.Copy(envelopedCms.ContentInfo.Content, content, 400);
string decrypted = null;
ICryptoTransform decryptor = alg.CreateDecryptor(alg.Key, alg.IV);
using (var memoryStream = new MemoryStream(content)) {
    using (var cryptoStream = new CryptoStream(memoryStream, alg.CreateDecryptor(alg.Key, alg.IV), CryptoStreamMode.Read)) {
        decrypted = new StreamReader(cryptoStream).ReadToEnd();
    }
}

StringToByteArray的实现如下:

代码语言:javascript
复制
public static byte[] StringToByteArray(String hex) {
    NumberChars = hex.Length;
    byte[] bytes = new byte[NumberChars / 2];
    for (int i = 0; i < NumberChars; i += 2)
        bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
    return bytes;
}
票数 1
EN

Stack Overflow用户

发布于 2018-10-19 16:43:26

您应该能够使用.NET 4.7.2实现您的目标:

代码语言:javascript
复制
using (CngKey key = CngKey.Import(p8Data, CngKeyBlobFormat.Pkcs8PrivateBlob))
{
    // The export policy needs to be redefined because CopyWithPrivateKey
    // needs to export/re-import ephemeral keys
    key.SetProperty(
        new CngProperty(
            "Export Policy",
            BitConverter.GetBytes((int)CngExportPolicies.AllowPlaintextExport),
            CngPropertyOptions.Persist));

    using (RSA rsa = new RSACng(key))
    using (X509Certificate2 cert = new X509Certificate2(certData))
    using (X509Certificate2 certWithKey = cert.CopyWithPrivateKey(rsa))
    {
        EnvelopedCms cms = new EnvelopedCms();
        cms.Decode(p7Data);
        cms.Decrypt(new X509Certificate2Collection(certWithKey));

        // I get here reliably with your reference documents
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52460001

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档