为了遵循挪威银行业的新 Peppol 要求,我必须使用非对称加密。我需要发送的文件是pkcs#7文件并且密钥的加密需要是aes-256-gcm。
请注意,我正在 Windows 计算机上工作,并且希望能够在 C# 中执行此操作。
加密本身效果很好
仅使用 System.Security.Cryptography 我可以做到这一点:
X509Certificate2 cert = new X509Certificate2(certificateAsByteArray);
byte[] encryptedData = new byte[data.Length];
byte[] tag = new byte[16]; // GCM tag length is 16 bytes
byte[] nonce = new byte[12]; // GCM standard nonce length is 12 bytes
RandomNumberGenerator.Fill(nonce); // Generate a random nonce
byte[] symmetricKey = new byte[32]; // 256 bits for AES-256-GCM
RandomNumberGenerator.Fill(symmetricKey);
using (AesGcm aesGcm = new AesGcm(symmetricKey))
{
aesGcm.Encrypt(nonce, indreAsice, encryptedData, tag);
}
但我不知道如何将其放入 pkcs#7 文件中。
MimeKit 制作 pkcs#7 文件
MimeKit 是一个可以帮助解决此问题的工具。
var pkcs7AsStream = new MemoryStream();
using (var memoryStream = new MemoryStream(indreAsice))
using (var ctx = new AesGcmContextContext())
{
ctx.Import(cert);
var recipients = new MimeKit.Cryptography.CmsRecipientCollection
{
new MimeKit.Cryptography.CmsRecipient(cert)
};
var mimePart = new MimePart(MimeKit.ContentType.Parse("application/pkcs7-mime"))
{
Content = new MimeContent(memoryStream)
};
var mime = ApplicationPkcs7Mime.Encrypt(ctx, recipients, mimePart);
mime.WriteTo(pkcs7AsStream);
}
pkcs7AsStream.Seek(0, SeekOrigin.Begin);
上下文被我覆盖了:
public class AesGcmContextContext : TemporarySecureMimeContext
{
protected override EncryptionAlgorithm GetPreferredEncryptionAlgorithm(MimeKit.Cryptography.CmsRecipientCollection recipients)
{
return EncryptionAlgorithm.Aes256;
//return base.GetPreferredEncryptionAlgorithm(recipients);
}
}
这给出了一个 pkcs#7 文件,其中使用的加密是 aes-256-cbc,而不是 aes-256-gcm。事实证明,你无法在 MimeKit 中选择 aes-256-gcm,并且
.Aes256
给出了 aes-256-cbc。
OpenSSL
生成 pkcs#7 的另一种方法是使用
cms
的 OpenSSL,如下所示:
codeopenssl cms -encrypt -in plaintext.txt -outform DER -out cms-enveloped-data.p7m -aes-256-gcm recipient-cert.pem
但这不起作用。不过,aes-256-cbc 的类似方法效果很好。
充气城堡
这也许是最好的机会,但我似乎无法让它发挥作用:
Org.BouncyCastle.Cms.CmsEnvelopedDataGenerator envData = new CmsEnvelopedDataGenerator();
envData.AddKeyTransRecipient(sertifikater.GetPublicCertificate());
var enveloped = envData.Generate(new CmsProcessableByteArray(data), "2.16.840.1.101.3.4.1.46");
var kryptencoded = enveloped.GetEncoded();
它接受
2.16.840.1.101.3.4.1.42
(aes-256-cbc),但不接受 2.16.840.1.101.3.4.1.46
(aes-256-gcm)。
我最近的尝试是使用
CryptoConfig.AddAlgorithm
添加算法,但没有成功。
最后一个问题:
似乎 aes-256-gcm 和 pkcs#7 互不喜欢。我听说这在 Java 世界中运行良好,但我们希望继续使用 .Net,因为我们所做的其他一切都是 .Net。我怎样才能让它发挥作用?
同事的朋友终于找到了解决这个问题的方法。为他喝点啤酒吧!事实证明,在.Net世界里有两种不同的BouncyCastle,Portable.BouncyCastle和另一种(抱歉,不知道它有没有名字)。对于解密,常规 BouncyCastle 包可以工作,但对于加密,我们必须使用 Portable.BouncyCastle 并进行一些调整。
请注意,由于我们必须使用这两个包,因此我们必须使用别名来区分,因此当您阅读
Port.BouncyCastle
时,它意味着 Portable.BouncyCastle,但否则它是另一个。
首先
CmsEnvelopedDataGenerator
必须扩展并覆盖 GetAlgorithmIdentifier
:
public class SmarterCmsEnveloped : Port.Org.BouncyCastle.Cms.CmsEnvelopedDataGenerator
{
protected override Port.Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier GetAlgorithmIdentifier(string encryptionOid, Port.Org.BouncyCastle.Crypto.Parameters.KeyParameter encKey, Port.Org.BouncyCastle.Asn1.Asn1Encodable asn1Params,
out Port.Org.BouncyCastle.Crypto.ICipherParameters cipherParameters)
{
if (encryptionOid == NistObjectIdentifiers.IdAes256Gcm.Id)
{
var random = new Port.Org.BouncyCastle.Security.SecureRandom();
var nonce = Port.Org.BouncyCastle.Security.SecureRandom.GetNextBytes(random, 12);
cipherParameters = new Port.Org.BouncyCastle.Crypto.Parameters.AeadParameters(encKey, 12 * 8, nonce);
return new Port.Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier(Port.Org.BouncyCastle.Asn1.Nist.NistObjectIdentifiers.IdAes256Gcm, new Port.Org.BouncyCastle.Asn1.DerSequence(new Port.Org.BouncyCastle.Asn1.DerOctetString(nonce)));
}
return base.GetAlgorithmIdentifier(encryptionOid, encKey, asn1Params, out cipherParameters);
}
}
然后这将为我们提供带有使用 aes-256-gcm 加密的对称密钥的 plcs#7:
var cmsGenerator = new SmarterCmsEnveloped();
var parser = new Port.Org.BouncyCastle.X509.X509CertificateParser();
var certificate = parser.ReadCertificate(certificateAsByteArray);
var sha256 = new Port.Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier(Port.Org.BouncyCastle.Asn1.Nist.NistObjectIdentifiers.IdSha256);
cmsGenerator.AddRecipientInfoGenerator(new Port.Org.BouncyCastle.Operators.CmsKeyTransRecipientInfoGenerator(
certificate,
new Port.Org.BouncyCastle.Crypto.Operators.Asn1KeyWrapper(Port.Org.BouncyCastle.Asn1.Pkcs.PkcsObjectIdentifiers.IdRsaesOaep,
new Port.Org.BouncyCastle.Asn1.Pkcs.RsaesOaepParameters(sha256,
new Port.Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier(Port.Org.BouncyCastle.Asn1.Pkcs.PkcsObjectIdentifiers.IdMgf1, sha256)) , certificate.GetPublicKey())));
var content = new Port.Org.BouncyCastle.Cms.CmsProcessableByteArray(dataToBeEncryptedWithAes256Gcm);
var generator = cmsGenerator.Generate(content, Port.Org.BouncyCastle.Asn1.Nist.NistObjectIdentifiers.IdAes256Gcm.Id);
我希望这对其他人有帮助,因为合作起来很令人沮丧!