rasn
rasn copied to clipboard
Decoding Subject, public key info to be able to print
I like rasn but I am have a problem decoding tbs_certificate fields. subject, subject_public_key_info, ...
When I print the subject I get RdnSequence([{AttributeTypeAndValue { type: ObjectIdentifier([2, 5, 4, 3]), value: Any { contents: [12, 8, 84, 80, 32, 83, 83, 32, 67, 65] } }}, {AttributeTypeAndValue { type: ObjectIdentifier([2, 5, 4, 6]), value: Any { contents: [19, 2, 67, 82] } }}, {AttributeTypeAndValue { type: ObjectIdentifier([2, 5, 4, 7]), value: Any { contents: [12, 8, 80, 97, 108, 109, 97, 114, 101, 115] } }}])
CN = TP SS CA, C = CR, L = Palmares
and subject public key info. SubjectPublicKeyInfo { algorithm: AlgorithmIdentifier { algorithm: ObjectIdentifier([1, 3, 101, 112]), parameters: None }, subject_public_key: BitVec<u8, bitvec::order::Msb0> { addr: 0x557c4f6c8a10, head: 000, bits: 256, capacity: 256 } [0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0] }
I would like to print out something like this Public Key Algorithm: ED25519 ED25519 Public-Key: pub: 4b:02:d2:8c:eb:99:32:e9:bb:92:80:83:1d:56:9e: 79:75:c7:e4:2f:4a:99:51:70:a9:41:94:ca:53:27: 73:6e
If you could show a code snippet that I could use to decode and print the cert.
specifically,
BitVec -> u8,
from OID ObjectIdentifier([1, 3, 101, 112]) to Ed25519
ObjectIdentifier([2, 5, 4, 3]), -> CN
value: Any { contents: [12, 8, 84, 80, 32, 83, 83, 32, 67, 65] } } -> TP SS CA
I hope to make a PR that would add these features
Thank you for your issue! I would definitely like to improve both the debug printing and the API for these types. Here's how I think this could be added to the project.
- Add manual
Debug
implementation for OID/ObjectIdentifier.- This should output both the raw identifier or the human readable version (if possible).
- The raw identifer should be in
{ 2 999 }
format. - The human readable version should be
{joint-iso-itu-t(2) example(999)}
.
- The raw identifer should be in
- This should output both the raw identifier or the human readable version (if possible).
- Add a manual
Debug
implementation toAlgorithimIdentifier
. - Add a manual
Debug
implementation toSubjectPublicKeyInfo
.
We could also change the AlgorithimIdentifier
to an enum of known and unknown variants, so you'd need one less step to decode it.
enum AlgorithimIdentifier {
Ed25519,
Ecdsa(EcpkParameters),
Unknown {
algorithm: ObjectIdentifier,
parameters: Option<Any>,
}
}
Then you'd add a manual decode and encode implementation that matches it into the original representation.
Thanks for the quick answer.
I have specific questions:
What function do I use to convert a BitVec to Vec
I have been looking at x509_parser for ideas, but rasn is abstracted more.
What function do I use to convert a BitVec to Vec?
If you mean just to get a slice it's bitvec.as_raw_slice()
, but you don't want to do that generically, you should want to convert it to a human readable format in the Debug
implementation.
How do I look up an OID and/or add a OID to lookup?
This currently does not exist, but you can see we do have table of OIDs already in the source code. So what's required is modifying this macro definition to generate a lookup table.
https://github.com/XAMPPRocky/rasn/blob/9fed0d7e61a9ecbd9f073eb365c8d85dda035404/src/types/oid.rs#L245-L264
what function do I use to convert/parse a RdnSequence to Vec
You should be calling RdnSequence
's Debug
implementation.
A value has a type, length, data, how do you lookup the value type?
You can't rely on the tag statically as a type can override the static tag (for example; fields or newtypes). You want to match by OID.
I am working on creating a search for OIDs, The idea that I am working on is creating a constant array of ConstOid and name const tbl: [(str, ConstOid)] = [($name, ConstOid), ($name, ConstOid), .... ]; I get errors of course. I can't make pub const $name: ... and create a table, I would have to wrap the oids macro to create the table and that would hide the const need by rasn. any ideas on how to proceed?
any ideas on how to proceed?
You’re going to need to move this macro from being declarative, to being a procedural macro in rasn-macros
to create the structure you want at compile time.
I have written a Cert Request decode and encode but there is a problem with the public key in CSR. here is the decode and the openssl output.
https://webencrypt.org/asn1js/ SEQUENCE (3 elem) SEQUENCE (3 elem) INTEGER 0 SEQUENCE (1 elem) SET (1 elem) SEQUENCE (2 elem) OBJECT IDENTIFIER 2.5.4.3 commonName (X.520 DN component) IA5String Ed25519 Test SEQUENCE (2 elem) SEQUENCE (2 elem) OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm) NULL BIT STRING (256 bit) 0101111101110010110011111010011001011011101010010000110001000110000111… SEQUENCE (2 elem) OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm) NULL BIT STRING (512 bit) 0011011100100101110110011000001100101010110010111001010100010011110111…
here is the openssl output
openssl req -in tst.csr -text Certificate Request: Data: Version: 1 (0x0) Subject: CN = Ed25519 Test Subject Public Key Info: Public Key Algorithm: ED25519 Unable to load Public Key 40F72525827F0000:error:03000072:digital envelope routines:X509_PUBKEY_get0:decode error:../crypto/x509/x_pubkey.c:458: Attributes: Requested Extensions: Signature Algorithm: ED25519 Signature Value: 37:25:d9:83:2a:cb:95:13:de:e1:ff:b2:d0:94:af:6b:a1:6f: 35:bc:46:55:eb:45:83:44:a6:37:45:ef:cc:69:90:9c:85:4f: ef:17:53:64:53:fa:db:7a:72:f1:e9:85:bf:44:3b:08:d0:60: df:d8:74:e7:35:35:9d:ed:c8:0d -----BEGIN CERTIFICATE REQUEST----- MIGYMEoCAQAwFzEVMBMGA1UEAxYMRWQyNTUxOSBUZXN0MCwwBwYDK2VwBQADIQBf cs+mW6kMRhwQziU4+Zt4F+PoHCdI24Vce3LlTpwbEjAHBgMrZXAFAANBADcl2YMq y5UT3uH/stCUr2uhbzW8RlXrRYNEpjdF78xpkJyFT+8XU2RT+tt6cvHphb9EOwjQ YN/YdOc1NZ3tyA0= -----END CERTIFICATE REQUEST-----
I have written a Cert Request decode and encode but there is a problem with the public key in CSR.
Could you provide a bit more detail, what's the problem specifically?
I have written a Certificate signing request generation and decode. It works self contained, but I want to verify it using openssl. openssl ed25519 public key does not have any parameters so it expects the key material directly after the key_alg. In rasn and x509-certificate libraries uses a NULL in the parameter field because it is an option.
I have placed a bug report for openssl Ed25519 pubkey decode fails because parameters value is NULL https://github.com/openssl/openssl/issues/20045
But I was wondering if rasn could not include a None option?
In rasn and x509-certificate libraries uses a NULL in the parameter field because it is an option.
Hmm, that doesn't seem right. In ASN.1 you don't set NULL when an Option is None
, you don't set anything. Can you try encoding it with the per
branch of the library and see what happens?
same. But I switch from Ia5String to Utf8String because Ia5String was giving me conversion issues
use argparse::{ArgumentParser, StoreTrue, Store, StoreOption}; use std::io; use std::io::prelude::; use std::fs::File; use std::process::exit; use pem::{parse, Pem}; //use crate::error::Error; extern crate alloc; use rasn::prelude::; use rasn::{types::, Decode, Encode, der::{decode, encode}}; use rasn_pkix::;
use ring::{ rand, signature::{self, KeyPair, Ed25519KeyPair}, }; use std::ops::Deref; use bitvec::prelude::*;
#[derive(AsnType, Clone, Copy, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash)] #[rasn(delegate)] pub struct Version(u64);
impl Version { pub const V1: Self = Self(0); pub const V2: Self = Self(1);
/// Returns the raw value of the version. Note that the version is
/// zero-indexed (v1 is 0, v2 is 1, etc).
pub fn raw_value(self) -> u64 {
self.0
}
}
impl Default for Version { fn default() -> Self { Self::V1 } }
impl core::fmt::Display for Version { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { core::write!(f, "{}", self.0.saturating_add(1)) } }
/// Attributes.
///
/// asn.1 /// Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } ///
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialOrd, Ord, PartialEq, Eq, Hash)] pub struct Attributes(Vec<Attribute>);
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct CertificateRequestInfo { //#[rasn(tag(explicit(0)), default)] pub version: Version, pub subject: Name, pub subject_public_key_info: SubjectPublicKeyInfo, #[rasn(tag(0))] pub attributes: Option<Attributes> }
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CertificateRequest {
pub certification_request_info: CertificateRequestInfo,
pub signature_algorithm: AlgorithmIdentifier,
pub signature_value: BitString
}
/*
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[rasn(choice)]
pub enum Name {
RdnSequence(RdnSequence),
}
*/
fn rd_file(fname: String) -> Vec
// read pem to der
//let (pem, sz) = Pem::read(&mut buffer).expect("Can'tread file");
//pem
data
}
fn rdprivkey(file: String) -> Ed25519KeyPair { // pkcs8 = InMemorySigningKeyPair::from_pkcs8_pem(rd_file(file)).expect("can't read pkcs8pem"); // println!("{:?}", pkcs8.verification_algorithm()); let byts = parse(rd_file(file)).expect("Read or der failed"); //private key println!("{:?} {:?}", byts.tag, byts.contents); let kp25519 = signature::Ed25519KeyPair::from_pkcs8_maybe_unchecked(&byts.contents).expect("Can't read key"); //let pubkey = kp25519.public_key().clone(); //println!("{:?}", &pubkey);
println!("{:?}", kp25519);
println!("{:?}", kp25519.public_key());
kp25519
}
pub fn mkAttribute(oidvec: Vec
pub fn mkName(names: &Vec<(Vec
}
// CertificateRequestInfo pub fn bld_cri(name: &Vec<Utf8String>, attrs: &Vec<Utf8String>, kp: &Ed25519KeyPair ) -> CertificateRequestInfo {
println!("{:?}", kp);
let version: Version = Version::V1;
let tst = mkAttribute(vec!(2,5,4,3), &attrs[0]);
let cn = AttributeTypeAndValue {
r#type: ObjectIdentifier::new_unchecked((&[2,5,4,3][..]).into()),
value: Any::new(
rasn::der::encode(&Utf8String::from(
"Ed25519"
)).unwrap())
};
println!("{:?} {:?}", cn, tst);
let par = vec!((vec!(2,5,4,3), Utf8String::from("test")), (vec!(2,5,4,4), Utf8String::from("other")));
let tst1 = mkName(&par);
let subject = Name::RdnSequence(vec![
{
let mut set = rasn::types::SetOf::new();
set.insert(cn);
set
}
]);
println!("{:?}", subject);
let subject_public_key_info = SubjectPublicKeyInfo {
algorithm: AlgorithmIdentifier {
algorithm: ObjectIdentifier::new_unchecked(
(&[1,3,101,112][..]).into(),
),
parameters: Some(Any::new(rasn::der::encode(&()).unwrap())),
},
subject_public_key: BitString::from_slice(
kp.public_key().as_ref().deref()
),
};
let attrs: Option<Attributes> = None;
let cri = CertificateRequestInfo {
version: version,
subject: subject,
subject_public_key_info: subject_public_key_info,
attributes: None,
};
cri
//println!("{:?}", cri);
//let cder = encode(&cri);
//println!("{:?}", cder);
}
fn main() { println!("Hello, world!"); let mut verbose = false; let mut file = "".to_string(); let mut privkey: Option<String> = std::option::Option::None; //let args =vec!(); { let mut ap = ArgumentParser::new(); ap.set_description("Greet somebody."); ap.stop_on_first_argument(true); ap.refer(&mut verbose) .add_option(&["-v", "--verbose"], StoreTrue, "Be verbose"); ap.refer(&mut privkey) // sign private key .key .add_option(&["--key"], StoreOption, "Ket set"); ap.refer(&mut file) // certificate .crt .add_argument("file", Store, "File name"); match ap.parse_args() { Ok(()) => { } Err(e) => { exit(e); } } } //let crtname = file.clone() + &".crt".to_string(); let keyname = file.clone() + &".crt".to_string(); let mut pkcs8 = "".to_string(); match privkey { Some(key) => { pkcs8 = key.clone(); }, None => { pkcs8 = file.clone() + &".pkcs8".to_string(); } } let signkey = rdprivkey(pkcs8);
//let crtdata = rd_file(crtname);
//let crtder = parse(crtdata).expect("cant pem to der");
//let crt: Certificate = rasn::der::decode(&crtder.contents).expect("no decode");
//println!("{:?}", crt);
let csrname = file.clone() + &".csr".to_string();
let csrdata = rd_file(csrname);
let csrder = parse(csrdata).expect("cant pem to der");
//println!("{:?}", csrder);
let csr: CertificateRequest = rasn::der::decode(&csrder.contents).expect("no decode");
//println!("{:?}", csr);
let cristruct = bld_cri(&vec!(Utf8String::from("test")), &vec!(Utf8String::from("bob")), &signkey);
let crider = encode(&cristruct).unwrap();
//let csr1: CertificateRequestInfo = rasn::der::decode(&crider).expect("no decode");
//println!("{:?} {:?} {:?}", &cristruct, &crider, csr1);
let sig = signkey.sign(&crider);
let alg = AlgorithmIdentifier {
algorithm: ObjectIdentifier::new_unchecked(
(&[1,3,101,112][..]).into(),
),
parameters: Some(Any::new(rasn::der::encode(&()).unwrap())),
};
let csr = CertificateRequest {
certification_request_info: cristruct,
signature_algorithm: alg,
signature_value: BitString::from_slice(sig.as_ref()),
};
let csrder = encode(&csr).unwrap();
println!("{:?}", &csr);
let peer_public_key_bytes = signkey.public_key().as_ref();
let peer_public_key =
signature::UnparsedPublicKey::new(&signature::ED25519, peer_public_key_bytes);
peer_public_key.verify(&crider, sig.as_ref()).expect("Verify failed");
let pem = Pem {
tag:String::from("CERTIFICATE REQUEST"),
contents: csrder
};
println!("{:?}", pem::encode(&pem));
}
It looks like if it is none it should just return
/// Encode the absent value of an optional field. fn encode_none<E: Encode>(&mut self) -> Result<Self::Ok, Self::Error>;
/// Encode the absent value with `tag` of an optional field.
fn encode_none_with_tag(&mut self, tag: Tag) -> Result<Self::Ok, Self::Error>;
I found problem with ed25519 parameters. my fault I am using pkcs8 crate for reading in keys.
@greenpdx Can I ask if there was a reason you were using pkcs8? Is there functionality in that crate that you'd like to see in the rasn PKIX crate?
I would like to use polymorphic keys, There are three main algorithms I use ed26619, es256 and RSA. in the Ring crate the sub variante needs to be specified. In rasm the okcs8 just returns a Document. I would like a "KeyPair" struct pub struct OneAsymmetricKey { pub algorithm: AlgorithmIdentifier, private_key: [u8], pub public_key: [u8], } impl OneAsymmetricKey { pub fn sign() {} pub fn verify() {} pub fn read_key() If version one, reads or creates the public key pub fmt () {}
Then create macros to use the correct keys.
On Tue, Jan 17, 2023 at 9:37 AM XAMPPRocky @.***> wrote:
@greenpdx https://github.com/greenpdx Can I ask if there was a reason you were using pkcs8? Is there functionality in that crate that you'd like to see in the rasn PKIX crate?
— Reply to this email directly, view it on GitHub https://github.com/XAMPPRocky/rasn/issues/112#issuecomment-1385614935, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABDEGBVAJSAGCDTBK67DOBDWS24CLANCNFSM6AAAAAAS3E7CMU . You are receiving this because you were mentioned.Message ID: @.***>
I have created a PKCS8 reader and debug display. But when I do it for ES256 and RSA the pub and key material are ASN1 sequences
How do I create a "switch" for the three different OIDs? Is there a ASN1 raw decoder?
#[derive(AsnType, Clone, Copy, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash)] #[rasn(delegate)] pub struct Version(u64);
impl Version { pub const V1: Self = Self(0); pub const V2: Self = Self(1);
/// Returns the raw value of the version. Note that the version is
/// zero-indexed (v1 is 0, v2 is 1, etc).
pub fn raw_value(self) -> u64 {
self.0
}
}
impl Default for Version { fn default() -> Self { Self::V1 } }
impl core::fmt::Display for Version { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { core::write!(f, "{}", self.0.saturating_add(1)) } }
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialOrd, Ord, PartialEq, Eq, Hash)] pub struct PKCS8 { //#[rasn(tag(explicit(0)), default)] pub version: Version, pub algorithm: AlgorithmIdentifier, pub private_key: OctetString, #[rasn(tag(0))] pub attributes: Option<Vec<Attributes>>, #[rasn(tag(1))] pub public_key: Option<BitString>, }
impl PKCS8 { pub fn print_pkcs8_info(&self) { println!("PKCS8 key"); println!("Version; {}", self.version); println!("AlgorithmIdentifier"); println!("\talgorithm: {:?}",self.algorithm.algorithm); match self.algorithm.parameters.clone() { None => {}, Some(parameters) => { println!("\tparameters: {:?}", ¶meters )} }; println!("Key: {:?}", format_serial(&self.private_key)); let tst = self.public_key.clone(); let tst1 = tst.unwrap(); println!("Pub: {:?}", format_serial(&tst1.as_raw_slice()));
}
}
pub fn rd_keys(file: &String) -> PKCS8 { let pkcs8name = file.clone() + &".pkcs8".to_string(); let pubname = file.clone() + &".pub".to_string();
let pemkey = read_to_string(&pkcs8name).expect("can't read key file");
let pempub = read_to_string(&pubname).expect("can't read pub file");
let derpub= parse(pempub).expect("Not Valid pub Pem");
let derkey = parse(pemkey).expect("Not valid key Pem");
let mut privkey: PKCS8 = rasn::der::decode(&derkey.contents).expect("no decode pkcs8");
let pubkey: SubjectPublicKeyInfo = rasn::der::decode(&derpub.contents).expect("no decode pkcs8");
privkey.public_key = Some(pubkey.subject_public_key);
privkey
} The output for ed25519
PKCS8 key Version; 1 AlgorithmIdentifier algorithm: ObjectIdentifier([1, 3, 101, 112]) Key: "04:20:ed:55:ba:91:91:a5:7a:73:4a:2c:57:63:28:65:bd:78:e3:7c:65:e8:51:ca:30:db:b8:1d:be:9e:25:c5:17:72" Pub: "5f:72:cf:a6:5b:a9:0c:46:1c:10:ce:25:38:f9:9b:78:17:e3:e8:1c:27:48:db:85:5c:7b:72:e5:4e:9c:1b:12"
I am working on CSR attributes. It is an option and there needs to be a [0] in the ASN1
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct CertificateRequestInfo { //#[rasn(tag(explicit(0)), default)] pub version: Version, pub subject: Name, pub subject_public_key_info: SubjectPublicKeyInfo, #[rasn(tag(0))] <- this does nothing pub attributes: Option<X509Attributes> } How do I add a [0] in front of attributes:
SEQUENCE (2 elem) SubjectPublicKey SEQUENCE (1 elem) OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm) BIT STRING (256 bit) 0101111101110010110011111010011001011011101010010000110001000110000111… [0] (1 elem) SEQUENCE (2 elem) attributes OBJECT IDENTIFIER 1.2.840.113549.1.9.14 extensionRequest (PKCS #9 via CRMF)
@greenpdx It would be very helpful if you could use code blocks and formatting in your comments as it would make them easier for me to read.
#[rasn(tag(0))] <- this does nothing
Are you sure? That's how it's done everywhere else in the codebase. Could you run cargo expand
(you may need to install it) on the module to see what the generated code looks like?