提取X509证书中的RSA公钥

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

How to extract RSA public key from X509 Certificate

问题

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

  1. --编辑--
  2. 谢谢大家的帮助。我尝试尽量避免依赖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.

  1. let mut certStr: String = &quot;-----BEGIN CERTIFICATE-----\n&quot;.to_owned();
  2. certStr.push_str(&amp;test.certificate);
  3. certStr.push_str(&quot;\n-----END CERTIFICATE-----&quot;);
  4. let rsa_cert = &amp;X509Certificate::from_pem(certStr.as_bytes()).unwrap();
  5. let pKey = &amp;X509Certificate::public_key_data(&amp;rsa_cert); // Invalid format Key
  6. 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() 方法:

  1. use x509_certificate::certificate::X509Certificate;
  2. use pem::{Pem, encode};
  3. use base64::{engine::general_purpose, Engine as _};
  4. fn main() {
  5. let cert_b64_der = "MIIF8TCCA9mgAwIBAgIJAKyFucsUiJogMA0GCSqGSIb3DQEBCwUAMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECAwCREUxEzARBgNVBAcMCldpbG1pbmd0b24xETAPBgNVBAoMCFdoYXRldmVyMRUwEwYDVQQLDAxXaGF0ZXZlck5hbWUxFzAVBgNVBAMMDk15IENvbW1vbi...";
  6. // 将证书转换为PEM格式,导入PEM证书
  7. let mut cert_pem: String = "-----BEGIN CERTIFICATE-----\n".to_owned();
  8. cert_pem.push_str(&cert_b64_der);
  9. cert_pem.push_str("\n-----END CERTIFICATE-----");
  10. let cert = &X509Certificate::from_pem(cert_pem.as_bytes()).unwrap();
  11. // 选择公钥
  12. let public_key = &X509Certificate::public_key_data(&cert);
  13. let public_pkcs1_der = general_purpose::STANDARD.encode(&public_key);
  14. // 导出以PEM编码的PKCS#1格式的公钥
  15. let pem = Pem {
  16. tag: String::from("RSA PUBLIC KEY"),
  17. contents: public_key.to_vec(),
  18. };
  19. let public_pkcs1_pem = encode(&pem);
  20. println!("Base64编码的DER编码的公共PKCS#1密钥:\n{}", public_pkcs1_der);
  21. println!();
  22. println!("PEM编码的公共PKCS#1密钥:\n{}", public_pkcs1_pem);
  23. }

这段代码的输出是:

  1. Base64编码的DER编码的公共PKCS#1密钥:
  2. MIICCgKCAgEAvWYJ7W5XBA5lwDCbgQX2kbZZaHSuMD+50ln7lRhuhu5Zzn7C1Iz3SmntF7vzrrtYHt8v5Cr7S4uh8GWwLr5k3Bq9xowHKnMM91DFUVCW1U0x2JavRtU79QLzLZDdh/bVcv0VSA3ZiM2yPgwY2HzJmgiLu8OZB40Lk80mf3VPfdBMkD35IlQzJC1/KV8fVP+eWOXPq8fTX/w3mNpANVnIAYGe7WsoygUMXuivi9DXAgUzDK5y5gdOmhta9EK+4wMrdFF7tk7vEL8r/cy+6rA/mRHNKRhxRKzoRQZfchpPvFk1+hJrt214/78Uf65E3NJlZKXWl2JwpmOmqpnUrGiGnotqzNX/RHgZVqwUgZqHuHS+m6wivfDMxjJg1861xOHGFMk2wpJhWReaQxPAwyatJrbCo7BsgXLlGGa4wdLq2/IMBONCw7W6xfC4QzW3pmCV5E0z6PGT1+s4YAE32qKjbCO6Xff5DOmKp8qQKykM++Rd7MHTgOiYdoWt4X+Us2YJMWZ1YG1coYapFVLW9TuO3R+Mn0MxuwomqChd3bsvEtTxG/cI06uBLrz5DthwtbE35NlDKJhoeDnMHRc1Z+knrCFFIM4U3GSS/CxfugMD7kgb1vvrMnquP23EEFtWfZndSsKkCh6VyqOTLYEIYEKeE+MEkWL3AXFUvinfb1gpBD8CAwEAAQ==
  3. PEM编码的公共PKCS#1密钥:
  4. -----BEGIN RSA PUBLIC KEY-----
  5. MIICCgKCAgEAvWYJ7W5XBA5lwDCbgQX2kbZZaHSuMD+50ln7lRhuhu5Zzn7C1Iz3
  6. SmntF7vzrrtYHt8v5Cr7S4uh8GWwLr5k3Bq9xowHKnMM91DFUVCW1U0x2JavRtU7
  7. 9QLzLZDdh/bVcv0VSA3ZiM2yPgwY2HzJmgiLu8OZB40Lk80mf3VPfdBMkD35IlQz
  8. JC1/KV8fVP+eWOXPq8fTX/w3mNpANVnIAYGe7WsoygUMXuivi9DXAgUzDK5y5gdO
  9. mhta9EK+4wMrdFF7tk7vEL8r/cy+6rA/mRHNKRhxRKzoRQZfchpPvFk1+hJrt214
  10. /78Uf65E3NJlZKXWl2JwpmOmqpnUrGiGnotqzNX/RHgZVqwUgZqHuHS+m6wivfDM
  11. xjJg1861xOHGFMk2wpJhWReaQxPAwyatJrbCo7BsgXLlGGa4wdLq2/IMBONCw7W6
  12. xfC4QzW3pmCV5E0z6PGT1+s4YAE32qKjbCO6Xff5DOmKp8qQKykM++Rd7MHTgOiY
  13. doWt4X+Us2YJMWZ1YG1coYapFVLW9TuO3R+Mn0MxuwomqChd3bsvEtTxG/cI06uB
  14. Lrz5DthwtbE35NlDKJhoeDnMHRc1Z+knrCFF
  15. <details>
  16. <summary>英文:</summary>
  17. 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:
  18. ```rust
  19. use x509_certificate::certificate::X509Certificate;
  20. use pem::{Pem, encode};
  21. use base64::{engine::general_purpose, Engine as _};
  22. fn main() {
  23. let cert_b64_der = &quot;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&quot;;
  24. // Convert certificate to PEM, import PEM certifciate
  25. let mut cert_pem: String = &quot;-----BEGIN CERTIFICATE-----\n&quot;.to_owned();
  26. cert_pem.push_str(&amp;cert_b64_der);
  27. cert_pem.push_str(&quot;\n-----END CERTIFICATE-----&quot;);
  28. let cert = &amp;X509Certificate::from_pem(cert_pem.as_bytes()).unwrap();
  29. // Pick public key
  30. let public_key = &amp;X509Certificate::public_key_data(&amp;cert);
  31. let public_pkcs1_der = general_purpose::STANDARD.encode(&amp;public_key);
  32. // Export public key as PEM encoded public key in PKCS#1 format
  33. let pem = Pem {
  34. tag: String::from(&quot;RSA PUBLIC KEY&quot;),
  35. contents: public_key.to_vec(),
  36. };
  37. let public_pkcs1_pem = encode(&amp;pem);
  38. println!(&quot;Base64 encoded DER encoded public PKCS#1 key:\n{}&quot;, public_pkcs1_der);
  39. println!();
  40. println!(&quot;PEM encoded public PKCS#1 key:\n{}&quot;, public_pkcs1_pem);
  41. }

The output of this code is:

  1. Base64 encoded DER encoded public PKCS#1 key:
  2. MIICCgKCAgEAvWYJ7W5XBA5lwDCbgQX2kbZZaHSuMD+50ln7lRhuhu5Zzn7C1Iz3SmntF7vzrrtYHt8v5Cr7S4uh8GWwLr5k3Bq9xowHKnMM91DFUVCW1U0x2JavRtU79QLzLZDdh/bVcv0VSA3ZiM2yPgwY2HzJmgiLu8OZB40Lk80mf3VPfdBMkD35IlQzJC1/KV8fVP+eWOXPq8fTX/w3mNpANVnIAYGe7WsoygUMXuivi9DXAgUzDK5y5gdOmhta9EK+4wMrdFF7tk7vEL8r/cy+6rA/mRHNKRhxRKzoRQZfchpPvFk1+hJrt214/78Uf65E3NJlZKXWl2JwpmOmqpnUrGiGnotqzNX/RHgZVqwUgZqHuHS+m6wivfDMxjJg1861xOHGFMk2wpJhWReaQxPAwyatJrbCo7BsgXLlGGa4wdLq2/IMBONCw7W6xfC4QzW3pmCV5E0z6PGT1+s4YAE32qKjbCO6Xff5DOmKp8qQKykM++Rd7MHTgOiYdoWt4X+Us2YJMWZ1YG1coYapFVLW9TuO3R+Mn0MxuwomqChd3bsvEtTxG/cI06uBLrz5DthwtbE35NlDKJhoeDnMHRc1Z+knrCFFIM4U3GSS/CxfugMD7kgb1vvrMnquP23EEFtWfZndSsKkCh6VyqOTLYEIYEKeE+MEkWL3AXFUvinfb1gpBD8CAwEAAQ==
  3. PEM encoded public PKCS#1 key:
  4. -----BEGIN RSA PUBLIC KEY-----
  5. MIICCgKCAgEAvWYJ7W5XBA5lwDCbgQX2kbZZaHSuMD+50ln7lRhuhu5Zzn7C1Iz3
  6. SmntF7vzrrtYHt8v5Cr7S4uh8GWwLr5k3Bq9xowHKnMM91DFUVCW1U0x2JavRtU7
  7. 9QLzLZDdh/bVcv0VSA3ZiM2yPgwY2HzJmgiLu8OZB40Lk80mf3VPfdBMkD35IlQz
  8. JC1/KV8fVP+eWOXPq8fTX/w3mNpANVnIAYGe7WsoygUMXuivi9DXAgUzDK5y5gdO
  9. mhta9EK+4wMrdFF7tk7vEL8r/cy+6rA/mRHNKRhxRKzoRQZfchpPvFk1+hJrt214
  10. /78Uf65E3NJlZKXWl2JwpmOmqpnUrGiGnotqzNX/RHgZVqwUgZqHuHS+m6wivfDM
  11. xjJg1861xOHGFMk2wpJhWReaQxPAwyatJrbCo7BsgXLlGGa4wdLq2/IMBONCw7W6
  12. xfC4QzW3pmCV5E0z6PGT1+s4YAE32qKjbCO6Xff5DOmKp8qQKykM++Rd7MHTgOiY
  13. doWt4X+Us2YJMWZ1YG1coYapFVLW9TuO3R+Mn0MxuwomqChd3bsvEtTxG/cI06uB
  14. Lrz5DthwtbE35NlDKJhoeDnMHRc1Z+knrCFFIM4U3GSS/CxfugMD7kgb1vvrMnqu
  15. P23EEFtWfZndSsKkCh6VyqOTLYEIYEKeE+MEkWL3AXFUvinfb1gpBD8CAwEAAQ==
  16. -----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:

  1. // Convert certificate to DER, import DER certificate
  2. let cert_der = general_purpose::STANDARD.decode(&amp;cert_b64_der).unwrap();
  3. 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):

  1. use openssl::x509::X509;
  2. use std::str;
  3. fn main() {
  4. let cert_b64_der = &quot;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&quot;;
  5. // Convert certificate to PEM, import PEM certificate
  6. let mut cert_pem: String = &quot;-----BEGIN CERTIFICATE-----\n&quot;.to_owned();
  7. cert_pem.push_str(&amp;cert_b64_der);
  8. cert_pem.push_str(&quot;\n-----END CERTIFICATE-----&quot;);
  9. let cert = &amp;X509::from_pem(cert_pem.as_bytes()).unwrap();
  10. // Pick public key
  11. let public_pkey = cert.public_key().unwrap();
  12. // Export public key as PEM encoded public key in PKCS#1 format
  13. let public_pkcs1_pem = public_pkey.rsa().unwrap().public_key_to_pem_pkcs1().unwrap();
  14. println!(&quot;PEM encoded public PKCS#1 key:\n{}&quot;, str::from_utf8(public_pkcs1_pem.as_slice()).unwrap());
  15. }

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:

确定