rustls / rcgen Goto Github PK
View Code? Open in Web Editor NEWGenerate X.509 certificates
License: Other
Generate X.509 certificates
License: Other
I want to generate self signed certificate for mitm proxy. I used openssl to generate key
openssl genrsa -out cert.key 2048
openssl req -new -x509 -key cert.key -out cert.crt
But as it is mitm proxy I need to adjust certificate subject line ON feild according to request. If request comes for xyz.com i need to change ON=xyz.com and if it comes for jsonip.com I need to change certificate subject to ON=jsonip.com
so I wont see tls error.
How do generate x509 key in such case where i just need to change ON and nothing else?
I basically wanted to create root cert and key using open ssl command add it to trusted root certificate . And using rcgen wanted to generate x509 certificate with the change of subject line having OU=xyz.com
.
I hope there is easy way to accomplish this with rcgen.
Thanks.
It would be great to have additional methods to interact with a specified or generated keypair.
Right now, I am missing two features:
KeyPair
and Certificate
, in DER format (example: serialize_public_key_der(&self) -> Vec<u8>
)KeyPair
compatibility with a given algorithm manually (making KeyPair::is_compatible(...)
public should do the trick)Right now there is no way to map a deserialized KeyPair
back to a usable SignatureAlgorithm
(apart from using is_compatible()
repeatedly for all known algorithms, and that is bugged, see #18 ).
It would improve usability a lot - and also achieve a proper separation of concerns - by having a separate, exposed enum for the type of KeyPair
, having variants such as RSA(bitlen)
, EC_P256
, EC_P384
and Ed25519
and being able to query this type for any given KeyPair
instance. Library users may then choose an appropriate compatible SignatureAlgorithm
.
Hey there!
I'm currently playing around with your package. I try to create a CA that can receive a CSR and sign the CSR. While I saw that I cannot use RSA (because of ring as far as I understand), it seems that I cannot create a CSR in code as well, since I cannot extract the public key from a KeyPair.
Do you have any advice on how I can manage to create such a PKI, or am I bound to other languages such as go?
Nevertheless, nice work in this lib!
Regards
I think it would be really neat if you offered a bunch of prebuilt binaries for OSX, Linux, Windows so that you don't need OpenSSL on those systems in order to generate certificates.
Since you're already using travis, you could have a peek at how I do it for some inspiration. :)
Currently users of rcgen can set many fields in CertificateParams that will be silently ignored when serializing a CSR.
One example is:
use rcgen;
let mut params = CertificateParams::default();
// https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.3
params.key_usages.push(KeyUsagePurpose::DigitalSignature);
params.key_usages.push(KeyUsagePurpose::KeyEncipherment);
params.key_usages.push(KeyUsagePurpose::KeyAgreement);
// ServerAuth may be consistent with DigitalSignature, KeyEncipherment, and KeyAgreement per rfc5280 #4.2.1.12
// https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.12
params
.extended_key_usages
.push(ExtendedKeyUsagePurpose::ServerAuth);
params.key_pair = Some(KeyPair::from_der(pkcs8_bytes)?);
let certificate = Certificate::from_params(params)?;
certificate.serialize_request_pem()?
In this example the key_usages
and extended_key_usages
fields will not be present in the serialized CSR.
Per rfc2985 5.4.2 the extensionRequest
attribute type may be used to carry information about certificate extensions the requester wishes to be included in a certificate.
It seems to me that subject_alt_names
, key_usages
, extended_key_usages
, name_constraints
, custom_extensions
, and the use_authority_key_identifier_extension
fields are all valid things to include in the extensionRequest
attribute. Currently it seems like subject_alt_names
is the only thing that is included.
Would you be open to merging a PR that adds key_usages
, extended_key_usages
, name_constraints
, custom_extensions
, and the Authority Key Identifier
extensions to the extension request section during CSR serialization?
There are two functions to parse Ed25519
keys in ring, Ed25519KeyPair::from_pkcs8
and from_pkcs8_maybe_unchecked
. Currently, we are using the former but it requires the keys to be in PKCS#8 v2
format. The command openssl genpkey -algorithm ED25519
does not generate such keys, so they are being rejected. If we change the parsing function to from_pkcs8_maybe_unchecked
, they are being accepted.
Right now I'm wondering how to make openssl generate PKCS#8 v2
keys or whether there is a way convert v1 keys to v2 ones. If there is a way to do the conversion with openssl cli tools, I'm inclined to keeping from_pkcs8
, otherwise I think we should switch to from_pkcs8_maybe_unchecked
.
It would be nice to take advantage of the serialization part of the library without being tied specifically to ring. I believe RemoteKeypair
could be leveraged to provide your own implementation of signing (perhaps with the Openssl crate)?
See this comment listing the things that rcgen needs to add in order to be used by certainly:
serverAuth
/ clientAuth
(3b0e2e6)pathLenConstraint
BasicConstraint (already supported in rcgen: 08abee4)cc @passcod .
libp2p doesn’t need them, and they bloat the handshake.
I'm missing a way to zero a KeyPair
after use.
Using the crate zeroise
, one can zeroize the string return by Certificate::serialize_private_key_pem
but not the KeyPair
internally hold by that Certificate
,
because some Into<Zeroizing>
trait implementation is missing.
Could rcgen
provide such zeroize feature? With some explicit call or implicitly in Drop
?
Formatting with cargo fmt
has been widely adopted by now. I was wondering if you were open to introduce formatting with rustfmt?
You could introduce a .rustfmt.toml
, which almost maintains the current formatting config.
The only mention of signing new certificates I see is serialize_*_with_signer
functions.
I am expected to serialize, then immediately deserialize certificate even though there is no intention to save it to file or transmit.
Documentation may be more clear about why signing and serialization are one step. I expected there be something like rcgen::Certificate::from_params_with_signer(params: CertificateParams, ca: &Certificate)
, so I can sign certificate, then serialize it if needed (or use somehow directly).
It appears that write_dt_utc_or_generalized()
is not stripping nanoseconds for chrono::DateTime<Utc>
.
Simple example is as follows:
// create certificate_params
...
let current_time = Utc::now();
// set not_before and not_after
...
// generate certificate
let cert = Certificate::from_params(certificate_params)?;
libs.rs
:
fn write_dt_utc_or_generalized(writer: DERWriter, dt: &DateTime<Utc>) -> Result<(), RcgenError> {
// RFC 5280 requires CAs to write certificate validity dates
// below 2050 as UTCTime, and anything starting from 2050
// as GeneralizedTime [1]. The RFC doesn't say anything
// about dates before 1950, but as UTCTime can't represent
// them, we have to use GeneralizedTime if we want to or not.
// [1]: https://tools.ietf.org/html/rfc5280#section-4.1.2.5
if (1950..2050).contains(&dt.year()) {
let ut = UTCTime::from_datetime::<Utc>(dt); // fails here
writer.write_utctime(&ut);
} else {
let gt = dt_to_generalized(dt)?;
writer.write_generalized_time(>);
}
Ok(())
}
Cause webpki refuse it https://github.com/briansmith/webpki/blob/17d9189981a618120fd8217a913828e7418e2484/src/cert.rs#L98-L107
It's lead to:
Rustls: InvalidCertificateData("invalid peer certificate: MissingOrMalformedExtensions")
(not a very explicit error).
I suggest at the very least that generate_simple_self_signed
refuse empty vec.
I need to generate a certificate with a subject line containing multiple OU key-value pairs:
Subject: OU=certtype:instance, OU=compartment:compartment, OU=instance:instance
The problem is subsequent calls to DistinguishedName::push
overwrite the previous value:
params.distinguished_name.push(DnType::OrganizationalUnitName, format!("certtype:{}", certtype));
params.distinguished_name.push(DnType::OrganizationalUnitName, format!("compartment:{}", compartment));
params.distinguished_name.push(DnType::OrganizationalUnitName, format!("instance:{}", instance));
This leaves me with a subject containing only the last value:
Subject: OU=instance:instance
I am using rcgen to generate a certificate, then I'm sharing the fingerprint of the certificate with a peer, and then I'm using a library to communicate with that peer. The library requires me to provide the certificate & private key in PEM format.
I've been debugging for a while why the fingerprint I generate, like this:
let fingerprint = ring::digest::digest(&ring::digest::SHA256, &cert.serialize_der()?);
... does not match the fingerprint the peer expects to receive, nor the fingerprint OpenSSL reports when I supply it with the PEM-encoded certificate obtained with serialize_pem()
.
After reading the discussion in #28, and looking at the code, I think I understand what's going on — serialize_der()
and serialize_pem()
are not actually just serializing; they are in fact generating the certificate each time they are called, so the random components are different between the DER serialization and the PEM serialization.
Ideally the certificate would be generated when the Certificate
struct is created, so that it can be repeatedly serialized — perhaps into different formats — without regenerating it each time.
If that's not possible, the functions should either be renamed or at least the mis-naming documented clearly to save the time of anyone else who encounters this.
Right now, from_params()
is documented as "Generates a new certificate" and serialize_der()
is documented as "Serializes the certificate to the binary DER format", when in reality from_params()
just stores the params (and does keygen if needed), and serialize_der()
etc actually do the certificate generation.
There should be a way to specify custom extensions at certficate generation time.
Add example to demonstrate how to create a CA and a certificate signed with that CA.
Basically I want to be able to generate the ca.crt/ca.key and redis.crt/redis.key files that this script generates https://github.com/redis/redis/blob/unstable/utils/gen-test-certs.sh#L5+L6
But I cant figure out how to from looking at the API, it seems like it should be possible though?
Hi,
The IP Address SAN is being encoded as an octet string of length 8 when it should be of length 4. This first four bytes are [48, 6, 135, 4]
(decimal), irrelevant of the actual IP address. The last four bytes are the actual IP address. This seems to be related to #25, but that was fixed more than two years ago.
I noticed that the CertificateParams::write_extension
method seems to be adding the [135, 4]
bytes to the octet string. I've done very little work with ASN.1, so I might be completely missing something, especially considering no one else has recently had this issue.
Right now DistinguishedName
can be written, but not read from outside the crate. Adding methods to get and iterate DN elements would be helpful.
pub fn get(&self, ty: DnType) -> Option<&str>
iter
- and the corresponding Iterator - publicWould be nice to have a release out that doesn't (transitively) depend on base64-0.10.
I am currently investigating this crate for potential use within the Parsec project, since it provides the ideal facilities for creating certs and CSRs from a private key that is managed in hardware. The RemoteKeyPair
trait provides a nice extension point where I can call out to key functions that are implemented in Parsec (which in turn can then be back-ended into a variety of TPMs, HSMs or secure elements).
One small interface wrinkle is in the error handling. The RemoteKeyPair::sign()
function is contracted to return RcgenError
on a failure. But this is an enum with a fixed set of variants, and none of the existing variants is directly applicable if the signing function fails in a third-party component. It would be good to have a RcgenError::RemoteKeyError
or something similar to flag when the remote implementation has encountered an error that is not explicitly modelled in the existing variants.
KeyPair::is_compatible()
yields true when:
PKCS_ECDSA_P256_SHA256
PKCS_ECDSA_P384_SHA384
This has the consequence that when CertificateParams
have such a combination of algorithm & KeyPair
a Certificate
will be generated without error, but will have a mismatch between public key type and public key data, rendering it ill-formed.
Being able to clone()
KeyPair
, CertificateParams
and Certificate
would help a great deal in some situations.
Right now the only solution seems to serialize and then deserialize again
Hi! I don't know if this is the best place for questions. If not feel free to close this.
I am implementing TLS authentication between two peers. On each peer I generate a Certificate
and serialize it to disk with cert.serialize_private_key_pem()
.
Later I exchange peers' certificates cert.serialize_der()
and then use rustls
for establishing TLS session.
Everything works out well. The problem I am having is that I want to generate a hash (unique peer ID) from public certificate and keep it the same for each peer on each run. I've noticed that every time I call cert.serialize_der()
it generates a slightly different output.
It seems that underlying key pair's public key remains the same so I can consistently generate a hash on each peer but then when TLS session is established I would like each peer to generate hash again from rustls::Session::get_peer_certificates()
.
What am I missing? What should I use as an input for hash generation? Is KeyPair::public_key_der()
a good candidate? If yes, how do get it from rustls::Session
?
My issues are most likely due to the lack of knowledge about certificates and encryption in general.
Just an FYI the logic in #42 is incorrect, Botan supports macOS (aka x86_64-apple-darwin
) just fine, and has for years. It does not support the old MacOS "Classic" which nobody uses anymore. I think the confusion came about because of the recent rebranding of OS X to macOS, plus the fact that the docs even bothered mentioning Classic, which has been dead for 20 years. I've amended Botan's documentation so the situation is more clear.
Importing a Certificate
via from_ca_cert_pem
or from_ca_cert_der
then running Certificate::serialize_der
on it yields invalid certificates, at least according to webpki (BadDer).
My work around was to keep a copy of the imported certificate as a backup and yield that backup when the certificate is requested.
It is stated in the doc that the public_key should returned in DER format
https://docs.rs/rcgen/0.9.2/rcgen/trait.RemoteKeyPair.html#tymethod.public_key
However it should be the raw bytes of the public key as this is what provided from ring
https://docs.rs/rcgen/0.9.2/src/rcgen/lib.rs.html#1781
I can't find in the docs of thering library but here it says the output from ring is in raw format
https://docs.rs/rcgen/0.9.2/rcgen/struct.KeyPair.html#method.public_key_raw
I'm trying to develop a P2P application where clients can register with a server, which will sign their certificates, so that they can later talk to each other without the involvement of the server.
I've been trying to load the CA cert using CertificateParams::from_ca_cert_pem
and sign the client certs with Certificate::serialize_der_with_signer
. However the "issuer" attached to the resulting signed cert doesn't match the original cert (specifically the "subject" is 1 byte different).
I don't really know enough about TLS to figure out why this is happening, so let me know if I can provide any additional details.
My (testing) code looks like this:
let ca_key = rcgen::KeyPair::from_der(debug_certs::CA_KEY_PK8).unwrap();
let params = rcgen::CertificateParams::from_ca_cert_pem(
debug_certs::CA_CERT,
ca_key,
)
.unwrap();
let ca_cert = rcgen::Certificate::from_params(params).unwrap();
let gen_cert = rcgen::generate_simple_self_signed(vec![hostname]).unwrap();
gen_cert.serialize_der_with_signer(&ca_cert).unwrap();
The issuer "subject" lines I get during certificate verification look like this (as byte arrays, note byte index 9)
Certificate "Issuer":
[49, 11, 48, 9, 6, 3, 85, 4, 6, 12, 2, 85, 83, 49, 17, 48, 15, 6, 3, 85, 4, 8, 12, 8, 86, 105, 114, 103, 105, 110, 105, 97, 49, 33, 48, 31, 6, 3, 85, 4, 10, 12, 24, 73, 110, 116, 101, 114, 110, 101, 116, 32, 87, 105, 100, 103, 105, 116, 115, 32, 80, 116, 121, 32, 76, 116, 100]
Trust Anchor "Subject":
[49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 17, 48, 15, 6, 3, 85, 4, 8, 12, 8, 86, 105, 114, 103, 105, 110, 105, 97, 49, 33, 48, 31, 6, 3, 85, 4, 10, 12, 24, 73, 110, 116, 101, 114, 110, 101, 116, 32, 87, 105, 100, 103, 105, 116, 115, 32, 80, 116, 121, 32, 76, 116, 100]
Hey, have you decided what you want to do with the chrono issues?
https://rustsec.org/advisories/RUSTSEC-2020-0159
Maybe it makes sense for rcgen to just adopt time 0.3 instead?
Go has a TLS stack that is becoming increasingly important as increasingly more projects are written in Go. The test suite should be extended to check Go's TLS stack as well. Going via a binary that does the validation would probably be easiest.
There are a bunch of unwraps in the code. It'd be great to use proper error handling, aka with enums over the possible cases.
First of all, thanks for this great little library. Works as advertised and is very straight-forward.
I hit a bit of a snag after attempting to generate and use a CA+CA signed certificate (code below). Verification fails on macOS (LibreSSL 2.8.3) with a format error in certificate's notBefore field
. Interestingly, it appears to work just fine on Linux with the same certificates.
Some digging revealed that this probably has something to do with the date format used by rcgen. See e.g. here and here. Apparently, depending on the version of OpenSSL/LibreSSL, dates in the 'GeneralizedTime' format (YYYYMMDDHHMMSSZ) are not supported for years <2050. For these dates, the 'UTCTime' format should be used (YYMMDDHHMMSSZ). My ca.pem
does appear to use the GeneralizedTime format (see here).
Suggested fix: use UTCTime format when date is before 2050, following RFC5280.
catest % openssl verify -CAfile ./ca.pem child.pem
child.pem: CN = MyCA
error 13 at 1 depth lookup:format error in certificate's notBefore field
CN = MyCA
error 13 at 1 depth lookup:format error in certificate's notBefore field
CN = MyCA
error 13 at 1 depth lookup:format error in certificate's notBefore field
$ openssl verify -CAfile ./ca.pem child.pem
child.pem: OK
openssl x509 -in ./ca.pem -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1 (0x1)
Signature Algorithm: ecdsa-with-SHA256
Issuer: CN=MyCA
Validity
Not Before: Jun 4 20:15:46 2020 GMT
Not After : May 30 20:15:46 2040 GMT
Subject: CN=MyCA
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:75:0c:8a:f4:f5:76:e7:fb:22:56:9c:dc:a7:6b:
9b:52:75:7a:d5:cd:e0:fd:52:3f:fc:99:58:dc:9a:
af:51:46:11:bc:66:8d:99:ac:71:59:ff:0c:9b:b6:
19:64:c3:80:ee:49:e0:87:38:35:4e:cd:5c:a5:52:
db:f9:fc:ba:15
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Subject Key Identifier:
93:5D:F8:17:62:41:59:76:96:2A:ED:42:D8:46:F4:4C:19:F0:30:BD
X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: ecdsa-with-SHA256
30:45:02:21:00:9d:c4:5f:18:0b:a4:32:1c:b1:9a:4f:1a:5d:
8f:1d:98:94:a8:07:d8:1d:97:29:0f:0b:aa:09:1a:1a:e6:d6:
87:02:20:31:9a:2c:73:3a:2a:02:67:60:58:98:8e:65:d4:67:
77:f8:38:6f:cc:f1:5e:3e:e1:9b:21:6d:ad:c9:95:39:ee
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 2 (0x2)
Signature Algorithm: ecdsa-with-SHA256
Issuer: CN=MyCA
Validity
Not Before: Jun 4 20:15:46 2020 GMT
Not After : Jun 14 20:15:46 2020 GMT
Subject: CN=MyCA user
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:11:3c:17:b6:62:0b:07:cd:b8:91:ee:7d:55:d9:
88:7d:d0:da:44:ef:3c:d6:84:5a:75:8f:79:a5:9e:
d3:a4:37:35:53:af:e3:1e:11:27:90:11:c2:89:34:
91:1e:b4:75:de:5d:02:87:91:30:76:fe:50:91:0d:
97:6b:d6:4d:5f
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Extended Key Usage:
TLS Web Client Authentication, TLS Web Server Authentication
Signature Algorithm: ecdsa-with-SHA256
30:46:02:21:00:97:4d:87:2e:0d:f4:9a:bc:5f:ca:ae:03:a8:
ff:d4:52:22:ab:08:f6:48:ac:50:55:a5:5d:10:fb:f3:b7:d2:
c3:02:21:00:f5:c6:48:30:6c:a8:4a:1a:a0:2b:91:59:3a:8e:
14:8f:61:06:9a:79:23:05:6e:ab:26:dc:30:32:55:10:83:04
use rcgen::{Certificate, CertificateParams, IsCa, BasicConstraints, DistinguishedName, DnType, ExtendedKeyUsagePurpose};
use std::result::Result;
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::time::SystemTime;
use chrono::{DateTime, Utc};
fn main() -> Result<(), Box<dyn Error>> {
let ca_validity = std::time::Duration::from_secs(86400 * 365 * 20);
let child_validity = std::time::Duration::from_secs(86400 * 10);
let ca_expiry_date = DateTime::<Utc>::from(SystemTime::now().checked_add(ca_validity).unwrap());
println!("expiry_date={:?}", ca_expiry_date);
// Create CA
let mut ca_dn = DistinguishedName::new();
ca_dn.push(DnType::CommonName, "MyCA");
let mut params = CertificateParams::new(vec![]);
params.distinguished_name = ca_dn;
params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
params.not_before = DateTime::<Utc>::from(SystemTime::now());
params.not_after = ca_expiry_date;
params.serial_number = Some(1);
let ca = Certificate::from_params(params).unwrap();
let ca_pem = ca.serialize_pem().unwrap();
let mut file = File::create("ca.pem")?;
file.write_all(ca_pem.as_bytes())?;
let ca_key = ca.serialize_private_key_pem();
let mut key_file = File::create("ca.key.pem")?;
key_file.write_all(ca_key.as_bytes())?;
// Create signed cert
let child_expiry_date = DateTime::<Utc>::from(SystemTime::now().checked_add(child_validity).unwrap());
let mut child_dn = DistinguishedName::new();
child_dn.push(DnType::CommonName, "MyCA user");
let mut child_params = CertificateParams::new(vec![]);
child_params.distinguished_name = child_dn;
child_params.not_before = DateTime::<Utc>::from(SystemTime::now());
child_params.not_after = child_expiry_date;
child_params.is_ca = IsCa::SelfSignedOnly;
child_params.serial_number = Some(2);
child_params.extended_key_usages.push(ExtendedKeyUsagePurpose::ClientAuth);
child_params.extended_key_usages.push(ExtendedKeyUsagePurpose::ServerAuth);
let child_crt = Certificate::from_params(child_params).unwrap();
let child_pem = child_crt.serialize_pem_with_signer(&ca).unwrap();
let mut child_file = File::create("child.pem")?;
child_file.write_all(child_pem.as_bytes())?;
let child_key_pem = child_crt.serialize_private_key_pem();
let mut child_key = File::create("child.key.pem")?;
child_key.write_all(child_key_pem.as_bytes())?;
Ok(())
}
For tests, we should use the openssl crate to ensure that the certs are formatted correctly.
For Basic Constraints certificate extension currently rcgen support two options:
pub enum IsCa {
SelfSignedOnly,
Ca(BasicConstraints)
}
And if we look into spec we will see exactly these two options:
id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 }
BasicConstraints ::= SEQUENCE {
cA BOOLEAN DEFAULT FALSE,
pathLenConstraint INTEGER (0..MAX) OPTIONAL }
and extension identifies two aspects:
Subject type: is the certificate a CA certificate or it is an end entity certificate;
[Optionally] How many CAs are allowed in the chain below current CA certificate. This setting has no meaning for end entity certificates.
And for sure Ca(x)
case generate something like:
SubjectType=CA
Path length Constraint = x
But in case of SelfSignedOnly it didn't generate anything.
Empty
Despite spec said: "If Basic Constraints extension is not included in certificate, it is automatically treated as end entity certificate."
Is it possible to force writing this field?
SubjectType=End Entity
Path length Constraint = None
Currently there is no way to generate a full X.509 certificate from a raw private and public key.
Additionally support for a "remote" raw private key would be amazing too, with remote meaning that no direct access is available, but a callback to signing is.
At the moment my ad-hoc solution is to copy some parts of rcgen.
I would propose to add an enum
of sorts to rcgen::CertificateParams::key_pair
that has the ability to take in a Box
to a trait similar to rustls::sign::SigningKey
and the public key in it's raw form.
Intending to make a PR myself if it's decided that this falls into the scope of rcgen.
My use case:
I'm currently writing a library that enables cross-platform access to the OS certificate vault and store. This includes generation, storage and some level of editing.
The goal is to be able to generate a key pair inside the OS vault and use that for example in rustls. When done that way, there is no access to the private key directly. This works great with rustls because it offers SigningKey
, which can call the OS vault signing function to sign a message without direct access to the private key.
The missing part is X.509 certificate generation. All three OS's (Linux, MacOS and Windows) generate only raw keys. Which is a great option for the future: rustls/rustls#423.
But right now and in some other use cases, for example using it on a server with a HSM for example, you need to generate a X.509 certificate from the already generated raw keys in addition to the private key not being directly accessible.
At the moment the usual solution is to use tools or OS API's to generate certificates, it would be great to allow that to be replaced by rcgen.
Not super sure why this is writing CIDRs?
The OID description expects only an octet string under the tag IPAddress
:
IPAddress [7] OCTET STRING,
and OpenSSL marks rcgen-generated IP fields as invalid:
X509v3 Subject Alternative Name:
IP Address:<invalid>
Generating IPAddress SANs with OpenSSL makes [u8; 4]
and [u8; 16]
for IP v4 and v6 respectively, and not CIDRs with the mask.
TaggedDerValue {
tag: Tag {
tag_class: ContextSpecific,
tag_number: 7,
},
pcbit: Primitive,
value: [
1,
2,
3,
4,
],
}
I was able to create CertificateParam
from existing certificate. But I only want to change OU field of certificate.
What i found is internally it is stored in CerficateParam's distinguished_name
field.
The unfortunate situation is DistinguishedName
has two field which are not public . And DistinguishedName
only provides new get push and iter which doesn't give ability to modify the internal value inside hashmap?
Here was my initial thought .
let mut param =
CertificateParams::from_ca_cert_pem(&cert_buf[..], key_pair).expect("Invalid pem key");
if let Some(x) = param.distinguished_name.entries.get_mut(rcgen::DnType::CustomDnType(u64::from_ne_bytes(b"OU"))) {
*x = "yahoo.com"; // yahoo.com is what i want to set of course dynamically
}
But unfortunately param.distinguished_name.entries is private :(
Let me know if i am doing something wrong?
Thanks :)
Contrast:
https://docs.rs/rcgen/0.8.11/rcgen/struct.Certificate.html
with:
https://docs.rs/rcgen/0.8.12/rcgen/struct.Certificate.html
I'd suggest yanking 0.8.12 and either restoring Send+Sync in 0.8.13, or instead releasing 0.9.0 with them removed
I wasted a lot of time trying to figure out that I needed to enable the optional extra "x509-parser" feature flag, in order to use the CertificateParams::from_ca_cert_{pem,der}
functions, which I found from the API docs on docs.rs
. This is not mentioned in the documentation or the readme. docs.rs
API documentation is generated with the feature enabled, giving the impression that the functions are available.
It would be nice if a mention about this is added somewhere relevant.
0.9 raised the MSRV to 1.56 by virtue of setting the edition to 2021. IMO it would be nice to avoid that for a while longer; for Quinn, for example, we have a policy of sticking to 6-month old versions for the MSRV.
This crate is hard to find on Crates.io, as it lacks categories and keywords.
I suggest to add them. Apart from obvious keywords like certificate
, tls
, ca
and X.509
, I suggest to also include mkcert
keyword. It is a popular CLI tool that does some things that this library also does, so it may serve as a way to discover this library.
What is the dependency of Openssl
for this lib? Is it just for verification of generated certs ?
Hi everyone,
I am having trouble creating a self signed certificate with ECDSA that will be usable (on macOS) with native-tls as a client identity.
This is the code I have so far:
fn get_identity() -> Result<native_tls::Identity, String>
let mut dn = DistinguishedName::new();
dn.push(rcgen::DnType::OrganizationName, "Demo");
dn.push(rcgen::DnType::CountryName, "DE");
dn.push(rcgen::DnType::CommonName, "Demo");
let mut cert_params = CertificateParams::default();
cert_params.distinguished_name = dn;
cert_params.serial_number = Option::Some(1);
cert_params.alg = &rcgen::PKCS_ECDSA_P256_SHA256;
cert_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
let certificate = match Certificate::from_params(cert_params) {
Ok(certificate) => certificate,
Err(e) => return Err(e.to_string()),
};
let certificate = match Certificate::from_params(cert_params) {
Ok(certificate) => certificate,
Err(e) => return Err(e.to_string()),
};
let cert = match certificate.serialize_pem() {
Ok(cert) => cert,
Err(err) => return Err(format!("Error in serializing the cert pem: {}", err)),
};
let key = certificate.serialize_private_key_pem();
match native_tls::Identity::from_pkcs8(cert.as_bytes(), key.as_bytes()) {
Ok(identity) => return Ok(identity),
Err(err) => {
println!("Error in creating identity: {}", err);
},
};
}
The error I am getting from macOS security framework is:
Error in creating identity: Unknown format in import.
I got as far as identifying the issue being with the private key.
Is there anything I am doing completely wrong?
Thanks
Andreas
In tests/openssl.rs, invocation of the verify_csr(&cert);
function is commented out for ed25519 certificates because it fails. I'm not sure what the problem is.
This is the output if you remove the //
:
---- test_openssl_25519_given stdout ----
-----BEGIN CERTIFICATE-----
MIIBLzCB4qADAgECAgEqMAUGAytlcDAsMSowFgYDVQQKDA9DcmFiIHdpZGdpdHMg
U0UwEAYDVQQDDAlNYXN0ZXIgQ0EwIhgPMTk3NTAxMDEwMDAwMDBaGA80MDk2MDEw
MTAwMDAwMFowLDEqMBYGA1UECgwPQ3JhYiB3aWRnaXRzIFNFMBAGA1UEAwwJTWFz
dGVyIENBMCowBQYDK2VwAyEA67x/8fDcfbp7bLnOhE/nHt8Oz0ri2PAS1nB3Vwxr
5ECjJTAjMCEGA1UdEQQaMBiCC2NyYWJzLmNyYWJzgglsb2NhbGhvc3QwBQYDK2Vw
A0EANUf5PpvKy0FIAHybycyZZViPYXndWjFMPSLMH9qNmKo3VnsCwjWlboetGT6i
j3popEMaJ8nhDP4AAqo+/c/vDA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE REQUEST-----
MIHiMIGVAgEAMC4xGDAWBgNVBAoMD0NyYWIgd2lkZ2l0cyBTRTESMBAGA1UEAwwJ
TWFzdGVyIENBMCowBQYDK2VwAyEA67x/8fDcfbp7bLnOhE/nHt8Oz0ri2PAS1nB3
Vwxr5ECgNDAyBgkqhkiG9w0BCQ4xJTAjMCEGA1UdEQQaMBiCC2NyYWJzLmNyYWJz
gglsb2NhbGhvc3QwBQYDK2VwA0EAbXjjM1CwFLPPCMRDNtt0SnVLvD7gv70Hj9TA
UWb1oFnKmIu9qQOXOW7xb5YHGcwPyo0zTpBU5vukDFkraojhDg==
-----END CERTIFICATE REQUEST-----
thread 'test_openssl_25519_given' panicked at 'called `Result::unwrap()` on an `Err` value: ErrorStack([Error { code: 218529960, library: "asn1 encoding routines", function: "asn1_check_tlen", reason: "wrong tag", file: "../crypto/asn1/tasn_dec.c", line: 1130 }, Error { code: 218546234, library: "asn1 encoding routines", function: "asn1_d2i_ex_primitive", reason: "nested asn1 error", file: "../crypto/asn1/tasn_dec.c", line: 694 }, Error { code: 218640442, library: "asn1 encoding routines", function: "asn1_template_noexp_d2i", reason: "nested asn1 error", file: "../crypto/asn1/tasn_dec.c", line: 627, data: "Field=privateKey, Type=EC_PRIVATEKEY" }, Error { code: 269033488, library: "elliptic curve routines", function: "d2i_ECPrivateKey", reason: "EC lib", file: "../crypto/ec/ec_asn1.c", line: 899 }, Error { code: 269344910, library: "elliptic curve routines", function: "old_ec_priv_decode", reason: "decode error", file: "../crypto/ec/ec_ameth.c", line: 447 }, Error { code: 218529960, library: "asn1 encoding routines", function: "asn1_check_tlen", reason: "wrong tag", file: "../crypto/asn1/tasn_dec.c", line: 1130 }, Error { code: 218640442, library: "asn1 encoding routines", function: "asn1_template_noexp_d2i", reason: "nested asn1 error", file: "../crypto/asn1/tasn_dec.c", line: 553, data: "Field=attributes, Type=PKCS8_PRIV_KEY_INFO" }])', src/libcore/result.rs:997:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
cc @djc
It'd be cool if you could specify the keypairs used for signing yourself.
See https://tools.ietf.org/id/draft-ietf-tls-tls13-21.html#rfc.section.4.2.3 for a full list of signature algos.
So far missing:
Similar to 6d85389, we should also add a webpki based test to the testsuite.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.