提取X509证书中的RSA公钥

huangapple go评论73阅读模式
英文:

How to extract RSA public key from X509 Certificate

问题

我是Rust的新手,我试图从X509证书中提取RSA公钥。基本上,我有一个证书字符串<-----BEGIN CERTIFICATE-----MII......-----END CERTIFICATE----->。我想从证书字符串中提取 - <----BEGIN RSA PUBLIC KEY----MIII......----END RSA PUBLIC KEY>

我尝试使用pem库和x509_certificate库,但是密钥的格式是无效的。

let mut certStr: String = "-----BEGIN CERTIFICATE-----\n".to_owned();
certStr.push_str(&test.certificate);
certStr.push_str("\n-----END CERTIFICATE-----");

let rsa_cert = &X509Certificate::from_pem(certStr.as_bytes()).unwrap();
let pKey = &X509Certificate::public_key_data(&rsa_cert); // 无效格式的密钥
let pem = parse(&certStr).unwrap(); // 这有两个属性 -> 标签和内容。不确定如何提取p



--编辑--
谢谢大家的帮助。我尝试尽量避免依赖openssl。我所需要的只是从证书中获取公钥,以验证由相应私钥加密的JWT令牌。在Rust中做到这一点的安全和直接的方法是什么?
英文:

I am new to Rust and I trying to extract RSA public key from a X509 Certificate. Basically, I have a cert string <-----BEGIN CERTIFICATE-----MII......-----END CERTIFICATE----->. I want to extract - <----BEGIN RSA PUBLIC KEY----MIII......----END RSA PUBLIC KEY> from the cert string.

I tried using the pem crate and x509_certificate crate but the key is coming out in an invalid format.

let mut certStr: String = &quot;-----BEGIN CERTIFICATE-----\n&quot;.to_owned();
certStr.push_str(&amp;test.certificate);
certStr.push_str(&quot;\n-----END CERTIFICATE-----&quot;);

let rsa_cert = &amp;X509Certificate::from_pem(certStr.as_bytes()).unwrap();
let pKey = &amp;X509Certificate::public_key_data(&amp;rsa_cert); // Invalid format Key
let pem = parse(&amp;certStr).unwrap(); // This has 2 properties -&gt; tag and contents. Not sure how to extract p

--Edit--
Thanks everyone for your help. I am trying to avoid taking dependency on openssl if possible. All I need is to get the public key out of the certificate to validate a JWT token which is encrypted by a corresponding private key. What would be the secure and straightforward way of doing that in Rust?

答案1

得分: 2

代码中导出密钥时使用了错误的方法:public_key_data()返回的密钥是以DER编码的PKCS#1格式的公钥。要将其转换为以PEM编码的密钥,应该使用 encode() 方法,而不是 parse() 方法:

use x509_certificate::certificate::X509Certificate;
use pem::{Pem, encode};
use base64::{engine::general_purpose, Engine as _};

fn main() {
    let cert_b64_der = "MIIF8TCCA9mgAwIBAgIJAKyFucsUiJogMA0GCSqGSIb3DQEBCwUAMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECAwCREUxEzARBgNVBAcMCldpbG1pbmd0b24xETAPBgNVBAoMCFdoYXRldmVyMRUwEwYDVQQLDAxXaGF0ZXZlck5hbWUxFzAVBgNVBAMMDk15IENvbW1vbi...";

    // 将证书转换为PEM格式,导入PEM证书
    let mut cert_pem: String = "-----BEGIN CERTIFICATE-----\n".to_owned();
    cert_pem.push_str(&cert_b64_der);
    cert_pem.push_str("\n-----END CERTIFICATE-----");
    let cert = &X509Certificate::from_pem(cert_pem.as_bytes()).unwrap();

    // 选择公钥
    let public_key = &X509Certificate::public_key_data(&cert); 
    let public_pkcs1_der = general_purpose::STANDARD.encode(&public_key);

    // 导出以PEM编码的PKCS#1格式的公钥
    let pem = Pem {
        tag: String::from("RSA PUBLIC KEY"),
        contents: public_key.to_vec(),
    };
    let public_pkcs1_pem = encode(&pem);

    println!("Base64编码的DER编码的公共PKCS#1密钥:\n{}", public_pkcs1_der);
    println!();
    println!("PEM编码的公共PKCS#1密钥:\n{}", public_pkcs1_pem);
}

这段代码的输出是:

Base64编码的DER编码的公共PKCS#1密钥:
MIICCgKCAgEAvWYJ7W5XBA5lwDCbgQX2kbZZaHSuMD+50ln7lRhuhu5Zzn7C1Iz3SmntF7vzrrtYHt8v5Cr7S4uh8GWwLr5k3Bq9xowHKnMM91DFUVCW1U0x2JavRtU79QLzLZDdh/bVcv0VSA3ZiM2yPgwY2HzJmgiLu8OZB40Lk80mf3VPfdBMkD35IlQzJC1/KV8fVP+eWOXPq8fTX/w3mNpANVnIAYGe7WsoygUMXuivi9DXAgUzDK5y5gdOmhta9EK+4wMrdFF7tk7vEL8r/cy+6rA/mRHNKRhxRKzoRQZfchpPvFk1+hJrt214/78Uf65E3NJlZKXWl2JwpmOmqpnUrGiGnotqzNX/RHgZVqwUgZqHuHS+m6wivfDMxjJg1861xOHGFMk2wpJhWReaQxPAwyatJrbCo7BsgXLlGGa4wdLq2/IMBONCw7W6xfC4QzW3pmCV5E0z6PGT1+s4YAE32qKjbCO6Xff5DOmKp8qQKykM++Rd7MHTgOiYdoWt4X+Us2YJMWZ1YG1coYapFVLW9TuO3R+Mn0MxuwomqChd3bsvEtTxG/cI06uBLrz5DthwtbE35NlDKJhoeDnMHRc1Z+knrCFFIM4U3GSS/CxfugMD7kgb1vvrMnquP23EEFtWfZndSsKkCh6VyqOTLYEIYEKeE+MEkWL3AXFUvinfb1gpBD8CAwEAAQ==

PEM编码的公共PKCS#1密钥:
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEAvWYJ7W5XBA5lwDCbgQX2kbZZaHSuMD+50ln7lRhuhu5Zzn7C1Iz3
SmntF7vzrrtYHt8v5Cr7S4uh8GWwLr5k3Bq9xowHKnMM91DFUVCW1U0x2JavRtU7
9QLzLZDdh/bVcv0VSA3ZiM2yPgwY2HzJmgiLu8OZB40Lk80mf3VPfdBMkD35IlQz
JC1/KV8fVP+eWOXPq8fTX/w3mNpANVnIAYGe7WsoygUMXuivi9DXAgUzDK5y5gdO
mhta9EK+4wMrdFF7tk7vEL8r/cy+6rA/mRHNKRhxRKzoRQZfchpPvFk1+hJrt214
/78Uf65E3NJlZKXWl2JwpmOmqpnUrGiGnotqzNX/RHgZVqwUgZqHuHS+m6wivfDM
xjJg1861xOHGFMk2wpJhWReaQxPAwyatJrbCo7BsgXLlGGa4wdLq2/IMBONCw7W6
xfC4QzW3pmCV5E0z6PGT1+s4YAE32qKjbCO6Xff5DOmKp8qQKykM++Rd7MHTgOiY
doWt4X+Us2YJMWZ1YG1coYapFVLW9TuO3R+Mn0MxuwomqChd3bsvEtTxG/cI06uB
Lrz5DthwtbE35NlDKJhoeDnMHRc1Z+knrCFF

<details>
<summary>英文:</summary>

In the code the wrong method is used when exporting the key: The key returned by [`public_key_data()`][0] is a DER encoded public key in PKCS#1 format. To convert this to a PEM encoded key, the [`encode()`][2] method must be applied instead of the [`parse()`][1] method:

```rust
use x509_certificate::certificate::X509Certificate;
use pem::{Pem, encode};
use base64::{engine::general_purpose, Engine as _};

fn main() {
    let cert_b64_der = &quot;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&quot;;

    // Convert certificate to PEM, import PEM certifciate
    let mut cert_pem: String = &quot;-----BEGIN CERTIFICATE-----\n&quot;.to_owned();
    cert_pem.push_str(&amp;cert_b64_der);
    cert_pem.push_str(&quot;\n-----END CERTIFICATE-----&quot;);
    let cert = &amp;X509Certificate::from_pem(cert_pem.as_bytes()).unwrap();

    // Pick public key
    let public_key = &amp;X509Certificate::public_key_data(&amp;cert); 
    let public_pkcs1_der = general_purpose::STANDARD.encode(&amp;public_key);
  
    // Export public key as PEM encoded public key in PKCS#1 format
    let pem = Pem {
        tag: String::from(&quot;RSA PUBLIC KEY&quot;),
        contents: public_key.to_vec(),
    };
    let public_pkcs1_pem = encode(&amp;pem);
  
    println!(&quot;Base64 encoded DER encoded public PKCS#1 key:\n{}&quot;, public_pkcs1_der);
    println!();
    println!(&quot;PEM encoded public PKCS#1 key:\n{}&quot;, public_pkcs1_pem);
}

The output of this code is:

Base64 encoded DER encoded public PKCS#1 key:
MIICCgKCAgEAvWYJ7W5XBA5lwDCbgQX2kbZZaHSuMD+50ln7lRhuhu5Zzn7C1Iz3SmntF7vzrrtYHt8v5Cr7S4uh8GWwLr5k3Bq9xowHKnMM91DFUVCW1U0x2JavRtU79QLzLZDdh/bVcv0VSA3ZiM2yPgwY2HzJmgiLu8OZB40Lk80mf3VPfdBMkD35IlQzJC1/KV8fVP+eWOXPq8fTX/w3mNpANVnIAYGe7WsoygUMXuivi9DXAgUzDK5y5gdOmhta9EK+4wMrdFF7tk7vEL8r/cy+6rA/mRHNKRhxRKzoRQZfchpPvFk1+hJrt214/78Uf65E3NJlZKXWl2JwpmOmqpnUrGiGnotqzNX/RHgZVqwUgZqHuHS+m6wivfDMxjJg1861xOHGFMk2wpJhWReaQxPAwyatJrbCo7BsgXLlGGa4wdLq2/IMBONCw7W6xfC4QzW3pmCV5E0z6PGT1+s4YAE32qKjbCO6Xff5DOmKp8qQKykM++Rd7MHTgOiYdoWt4X+Us2YJMWZ1YG1coYapFVLW9TuO3R+Mn0MxuwomqChd3bsvEtTxG/cI06uBLrz5DthwtbE35NlDKJhoeDnMHRc1Z+knrCFFIM4U3GSS/CxfugMD7kgb1vvrMnquP23EEFtWfZndSsKkCh6VyqOTLYEIYEKeE+MEkWL3AXFUvinfb1gpBD8CAwEAAQ==

PEM encoded public PKCS#1 key:
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEAvWYJ7W5XBA5lwDCbgQX2kbZZaHSuMD+50ln7lRhuhu5Zzn7C1Iz3
SmntF7vzrrtYHt8v5Cr7S4uh8GWwLr5k3Bq9xowHKnMM91DFUVCW1U0x2JavRtU7
9QLzLZDdh/bVcv0VSA3ZiM2yPgwY2HzJmgiLu8OZB40Lk80mf3VPfdBMkD35IlQz
JC1/KV8fVP+eWOXPq8fTX/w3mNpANVnIAYGe7WsoygUMXuivi9DXAgUzDK5y5gdO
mhta9EK+4wMrdFF7tk7vEL8r/cy+6rA/mRHNKRhxRKzoRQZfchpPvFk1+hJrt214
/78Uf65E3NJlZKXWl2JwpmOmqpnUrGiGnotqzNX/RHgZVqwUgZqHuHS+m6wivfDM
xjJg1861xOHGFMk2wpJhWReaQxPAwyatJrbCo7BsgXLlGGa4wdLq2/IMBONCw7W6
xfC4QzW3pmCV5E0z6PGT1+s4YAE32qKjbCO6Xff5DOmKp8qQKykM++Rd7MHTgOiY
doWt4X+Us2YJMWZ1YG1coYapFVLW9TuO3R+Mn0MxuwomqChd3bsvEtTxG/cI06uB
Lrz5DthwtbE35NlDKJhoeDnMHRc1Z+knrCFFIM4U3GSS/CxfugMD7kgb1vvrMnqu
P23EEFtWfZndSsKkCh6VyqOTLYEIYEKeE+MEkWL3AXFUvinfb1gpBD8CAwEAAQ==
-----END RSA PUBLIC KEY-----

That the exported public key really corresponds to the key from the certificate and that the key is correctly formatted can be checked most easily with an ASN.1 parser, e.g. https://lapo.it/asn1js/.

As it seems that your certificate is available as a Base64 encoded ASN.1/DER encoded certificate, it is probably more convenient to import this directly as DER using from_der() and spare the detour via the PEM encoding. For this, simply replace the Convert certificate to PEM code block with:

// Convert certificate to DER, import DER certificate
let cert_der = general_purpose::STANDARD.decode(&amp;cert_b64_der).unwrap();
let cert = &amp;X509Certificate::from_der(cert_der.as_slice()).unwrap();

You can alternatively use the openssl crate that supports all the tasks you need in a single crate: The import of the PEM certificate, the extraction of the public key and the export as PEM encoded public key in PKCS#1 format (as well as in various other formats and encodings):

use openssl::x509::X509;
use std::str;

fn main() {

    let cert_b64_der = &quot;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&quot;;

    // Convert certificate to PEM, import PEM certificate
    let mut cert_pem: String = &quot;-----BEGIN CERTIFICATE-----\n&quot;.to_owned();
    cert_pem.push_str(&amp;cert_b64_der);
    cert_pem.push_str(&quot;\n-----END CERTIFICATE-----&quot;);
    let cert = &amp;X509::from_pem(cert_pem.as_bytes()).unwrap();

    // Pick public key
    let public_pkey = cert.public_key().unwrap();

    // Export public key as PEM encoded public key in PKCS#1 format
    let public_pkcs1_pem = public_pkey.rsa().unwrap().public_key_to_pem_pkcs1().unwrap();

    println!(&quot;PEM encoded public PKCS#1 key:\n{}&quot;, str::from_utf8(public_pkcs1_pem.as_slice()).unwrap());
}

Also the OpenSSL library allows to import the ASN.1/DER encoded certificate directly (here) and to export the key DER encoded (here).

huangapple
  • 本文由 发表于 2023年2月18日 09:34:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/75490641.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定