从 x509certificate2 对象导出 pem 格式的公钥

编程入门 行业动态 更新时间:2024-10-19 04:26:57
本文介绍了从 x509certificate2 对象导出 pem 格式的公钥的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我是这个主题的新手,我对 PEM 格式和 CER 格式的公钥之间的区别感到困惑.

I'm new to this subject, and I got confused of the differences between a public key in PEM format vs CER format.

我正在尝试从 C# 代码中 PEM 格式的 x509certificate2 对象导出公钥.

I'm trying to export a public key from a x509certificate2 object in PEM format in c# code.

据我了解,cer 格式的证书与 pem 格式的证书的区别仅在于页眉和页脚(如果我理解正确,base 64 中 .cer 格式的证书应该是 someBase64String 而在 pem 格式中它是相同的字符串,包括开始和结束页眉和页脚.

As far as I understand, the difference between a certificate in cer format vs pem format, is only the header and footer (if I understand correctly, a certificate in .cer format in base 64 should be someBase64String and in pem format it's the same string including the begin and end header and footer).

但我的问题是关于公钥的.让 pubKey 成为从 x509certificate2 对象以 .cer 格式导出的公钥,是这个key的pem格式,将是:

but my question is for the public key. let pubKey be a public key exported in .cer format from an x509certificate2 object, is the pem format of this key, will be:

------BEGIN PUBLIC KEY----- pubKey... ------END PUBLIC KEY------

以 base 64 编码?

encoded in base 64?

谢谢:)

推荐答案

用于公钥.让 pubKey 成为从 x509certificate2 对象以 .cer 格式导出的公钥

for the public key. let pubKey be a public key exported in .cer format from an x509certificate2 object

谈论.cer 格式"仅适用于您拥有完整证书的情况;这就是 X509Certificate2 将导出的全部内容.(好吧,或者一组证书,或者一组带有关联私钥的证书).

Talking about a ".cer format" only applies when you have the whole certificate; and that's all that an X509Certificate2 will export as. (Well, or a collection of certificates, or a collection of certificates with associated private keys).

.NET 中的任何内容都不会为您提供证书的 DER 编码的 SubjectPublicKeyInfo 块,这就是 PEM 编码下的PUBLIC KEY".

Nothing built in to .NET will give you the DER-encoded SubjectPublicKeyInfo block of the certificate, which is what becomes "PUBLIC KEY" under a PEM encoding.

如果需要,您可以自己构建数据.对于 RSA 来说,这还不算太糟糕,但并不完全令人愉快.数据格式定义在 tools.ietf/html/rfc3280#第 4.1 节:

You can build the data yourself, if you want. For RSA it's not too bad, though not entirely pleasant. The data format is defined in tools.ietf/html/rfc3280#section-4.1:

SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING } AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL }

tools.ietf/html/rfc3279#section-2.3.1 描述了如何对 RSA 密钥进行编码,尤其是:

tools.ietf/html/rfc3279#section-2.3.1 describes how RSA keys, in particular are to be encoded:

rsaEncryption OID 旨在用于算法领域AlgorithmIdentifier 类型的值.参数字段必须此算法标识符的 ASN.1 类型为 NULL.

The rsaEncryption OID is intended to be used in the algorithm field of a value of type AlgorithmIdentifier. The parameters field MUST have ASN.1 type NULL for this algorithm identifier.

RSA 公钥必须使用 ASN.1 类型 RSAPublicKey 进行编码:

The RSA public key MUST be encoded using the ASN.1 type RSAPublicKey:

RSAPublicKey ::= SEQUENCE { modulus INTEGER, -- n publicExponent INTEGER } -- e

这些结构背后的语言是 ASN.1,由 ITU X.680,它们被编码为字节的方式包含在 ITU X.690.

The language behind these structures is ASN.1, defined by ITU X.680, and the way they get encoded to bytes is covered by the Distinguished Encoding Rules (DER) ruleset of ITU X.690.

.NET 实际上给了你很多这样的部分,但你必须组装它们:

.NET actually gives you back a lot of these pieces, but you have to assemble them:

private static string BuildPublicKeyPem(X509Certificate2 cert) { byte[] algOid; switch (cert.GetKeyAlgorithm()) { case "1.2.840.113549.1.1.1": algOid = new byte[] { 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; break; default: throw new ArgumentOutOfRangeException(nameof(cert), $"Need an OID lookup for {cert.GetKeyAlgorithm()}"); } byte[] algParams = cert.GetKeyAlgorithmParameters(); byte[] publicKey = WrapAsBitString(cert.GetPublicKey()); byte[] algId = BuildSimpleDerSequence(algOid, algParams); byte[] spki = BuildSimpleDerSequence(algId, publicKey); return PemEncode(spki, "PUBLIC KEY"); } private static string PemEncode(byte[] berData, string pemLabel) { StringBuilder builder = new StringBuilder(); builder.Append("-----BEGIN "); builder.Append(pemLabel); builder.AppendLine("-----"); builder.AppendLine(Convert.ToBase64String(berData, Base64FormattingOptions.InsertLineBreaks)); builder.Append("-----END "); builder.Append(pemLabel); builder.AppendLine("-----"); return builder.ToString(); } private static byte[] BuildSimpleDerSequence(params byte[][] values) { int totalLength = values.Sum(v => v.Length); byte[] len = EncodeDerLength(totalLength); int offset = 1; byte[] seq = new byte[totalLength + len.Length + 1]; seq[0] = 0x30; Buffer.BlockCopy(len, 0, seq, offset, len.Length); offset += len.Length; foreach (byte[] value in values) { Buffer.BlockCopy(value, 0, seq, offset, value.Length); offset += value.Length; } return seq; } private static byte[] WrapAsBitString(byte[] value) { byte[] len = EncodeDerLength(value.Length + 1); byte[] bitString = new byte[value.Length + len.Length + 2]; bitString[0] = 0x03; Buffer.BlockCopy(len, 0, bitString, 1, len.Length); bitString[len.Length + 1] = 0x00; Buffer.BlockCopy(value, 0, bitString, len.Length + 2, value.Length); return bitString; } private static byte[] EncodeDerLength(int length) { if (length <= 0x7F) { return new byte[] { (byte)length }; } if (length <= 0xFF) { return new byte[] { 0x81, (byte)length }; } if (length <= 0xFFFF) { return new byte[] { 0x82, (byte)(length >> 8), (byte)length, }; } if (length <= 0xFFFFFF) { return new byte[] { 0x83, (byte)(length >> 16), (byte)(length >> 8), (byte)length, }; } return new byte[] { 0x84, (byte)(length >> 24), (byte)(length >> 16), (byte)(length >> 8), (byte)length, }; }

DSA 和 ECDSA 密钥具有更复杂的 AlgorithmIdentifier.parameters 值,但 X509Certificate 的 GetKeyAlgorithmParameters() 恰好将它们返回正确格式,因此您只需要记下它们的 OID(字符串)查找键和它们的 OID(字节[]) switch 语句中的编码值.

DSA and ECDSA keys have more complex values for AlgorithmIdentifier.parameters, but X509Certificate's GetKeyAlgorithmParameters() happens to give them back correctly formatted, so you would just need to write down their OID (string) lookup key and their OID (byte[]) encoded value in the switch statement.

我的 SEQUENCE 和 BIT STRING 构建器肯定可以更高效(哦,看看那些糟糕的数组),但这对于性能不重要的东西就足够了.

My SEQUENCE and BIT STRING builders can definitely be more efficient (oh, look at all those poor arrays), but this would suffice for something that isn't perf-critical.

要检查您的结果,您可以将输出粘贴到 openssl rsa -pubin -text -noout,如果它打印出除错误之外的任何内容,您已制作了合法编码的公钥"RSA 密钥的编码.

To check your results, you can paste the output to openssl rsa -pubin -text -noout, and if it prints anything other than an error you've made a legally encoded "PUBLIC KEY" encoding for an RSA key.

更多推荐

从 x509certificate2 对象导出 pem 格式的公钥

本文发布于:2023-11-13 02:08:29,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1583123.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:对象   格式   公钥   pem

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!