rsa_export/
lib.rs

1#![deny(missing_docs)]
2#![warn(clippy::all, clippy::pedantic)]
3#![allow(clippy::missing_errors_doc, clippy::must_use_candidate)]
4
5//! `rsa_export` allows you to export your RSA key, generated via the [rsa crate](https://crates.io/crates/rsa), with PKCS#1 or PKCS#8 encoding  
6//!
7//! **Note**: Multi-prime keys are not supported
8//!
9//! The keys can also be exported into the PEM format by enabling the feature `pem`
10//!
11//! Example:  
12//!
13//! ```rust
14//! use rsa::RSAPrivateKey;
15//! use rand::rngs::OsRng;
16//! use rsa_export::Encode;
17//!
18//! let mut rng = OsRng;
19//! let private_key = RSAPrivateKey::new(&mut rng, 2048).unwrap();
20//! let public_key = private_key.to_public_key();
21//!
22//! let pkcs1_encoded_private = private_key.as_pkcs1().unwrap();
23//! let pkcs1_encoded_public = public_key.as_pkcs1().unwrap();
24//!
25//! let pkcs8_encoded_private = private_key.as_pkcs8().unwrap();
26//! let pkcs8_encoded_public = public_key.as_pkcs8().unwrap();
27//! ```
28//!
29//! Encode PKCS#1 or PKCS#8 encoded keys into the PEM format
30//!
31//! ```rust
32//! use rsa::RSAPrivateKey;
33//! use rand::rngs::OsRng;
34//! use rsa_export::PemEncode;
35//!
36//! let mut rng = OsRng;
37//! let private_key = RSAPrivateKey::new(&mut rng, 2048).unwrap();
38//! let public_key = private_key.to_public_key();
39//!
40//! let pkcs1_encoded_private_pem = private_key.as_pkcs1_pem().unwrap();
41//! let pkcs1_encoded_public_pem = public_key.as_pkcs1_pem().unwrap();
42//!
43//! let pkcs8_encoded_private_pem = private_key.as_pkcs8_pem().unwrap();
44//! let pkcs8_encoded_public_pem = public_key.as_pkcs8_pem().unwrap();
45//! ```
46//!
47//! Encode PKCS#1 or PKCS#8 encoded keys into the PEM format with a custom line ending
48//!
49//! ```rust
50//! use rsa::RSAPrivateKey;
51//! use rsa_export::{PemEncode, LineEnding};
52//! use rand::rngs::OsRng;
53//!
54//! let mut rng = OsRng;
55//! let private_key = RSAPrivateKey::new(&mut rng, 2048).unwrap();
56//! let public_key = private_key.to_public_key();
57//!
58//! let pkcs1_encoded_private_pem = private_key
59//!     .as_pkcs1_pem_custom_ending(LineEnding::CRLF)
60//!     .unwrap();
61//! let pkcs1_encoded_public_pem = public_key
62//!     .as_pkcs1_pem_custom_ending(LineEnding::CRLF)
63//!     .unwrap();
64//!
65//! let pkcs8_encoded_private_pem = private_key
66//!     .as_pkcs1_pem_custom_ending(LineEnding::CRLF)
67//!     .unwrap();
68//! let pkcs8_encoded_public_pem = public_key
69//!    .as_pkcs1_pem_custom_ending(LineEnding::CRLF)
70//!    .unwrap();
71//! ```
72//!
73
74#[cfg(feature = "pem")]
75extern crate pem as pem_crate;
76
77use {
78    crate::error::Error,
79    num_bigint_dig::ToBigInt,
80    rsa::{RSAPrivateKey, RSAPublicKey},
81};
82
83fn rsa_oid() -> simple_asn1::OID {
84    use simple_asn1::{BigUint, OID};
85
86    simple_asn1::oid!(1, 2, 840, 113_549, 1, 1, 1)
87}
88
89fn to_bigint(biguint: &rsa::BigUint) -> simple_asn1::BigInt {
90    simple_asn1::BigInt::from_signed_bytes_le(
91        biguint.to_bigint().unwrap().to_signed_bytes_le().as_slice(),
92    )
93}
94
95/// Trait for encoding the keys
96pub trait Encode: sealed::Sealed {
97    /// Encode in the PKCS#1 format
98    fn as_pkcs1(&self) -> Result<Vec<u8>, Error>;
99    /// Encode in the PKCS#8 format
100    fn as_pkcs8(&self) -> Result<Vec<u8>, Error>;
101}
102
103impl Encode for RSAPrivateKey {
104    fn as_pkcs1(&self) -> Result<Vec<u8>, Error> {
105        pkcs1::private_key(self)
106    }
107
108    fn as_pkcs8(&self) -> Result<Vec<u8>, Error> {
109        pkcs8::private_key(self)
110    }
111}
112
113impl Encode for RSAPublicKey {
114    fn as_pkcs1(&self) -> Result<Vec<u8>, Error> {
115        pkcs1::public_key(self)
116    }
117
118    fn as_pkcs8(&self) -> Result<Vec<u8>, Error> {
119        pkcs8::public_key(self)
120    }
121}
122
123#[cfg(feature = "pem")]
124/// Trait for encoding the keys and wrapping them in the PEM format  
125pub trait PemEncode: sealed::Sealed + Encode {
126    /// Encode in the PKCS#1 format, PEM encoded
127    fn as_pkcs1_pem(&self) -> Result<String, Error>;
128    /// Encode in the PKCS#8 format, PEM encoded
129    fn as_pkcs8_pem(&self) -> Result<String, Error>;
130
131    /// Encode in the PKCS#1 format, PEM encoded with a custom line ending
132    fn as_pkcs1_pem_custom_ending(&self, line_ending: LineEnding) -> Result<String, Error>;
133    /// Encode in the PKCS#8 format, PEM encoded with a custom line ending
134    fn as_pkcs8_pem_custom_ending(&self, line_ending: LineEnding) -> Result<String, Error>;
135}
136
137#[cfg(feature = "pem")]
138impl PemEncode for RSAPrivateKey {
139    /// Line endings default to Unix-style LF  
140    fn as_pkcs1_pem(&self) -> Result<String, Error> {
141        self.as_pkcs1_pem_custom_ending(LineEnding::LF)
142    }
143
144    /// Line endings default to Unix-style LF
145    fn as_pkcs8_pem(&self) -> Result<String, Error> {
146        self.as_pkcs8_pem_custom_ending(LineEnding::LF)
147    }
148
149    /// Encode with a different line ending
150    fn as_pkcs1_pem_custom_ending(&self, line_ending: LineEnding) -> Result<String, Error> {
151        let pkcs1_der = self.as_pkcs1()?;
152
153        Ok(pem::encode(
154            pem::EncodingScheme::PKCS1,
155            pem::KeyType::Private,
156            line_ending,
157            pkcs1_der,
158        ))
159    }
160
161    /// Encode with a different line ending
162    fn as_pkcs8_pem_custom_ending(&self, line_ending: LineEnding) -> Result<String, Error> {
163        let pkcs8_der = self.as_pkcs8()?;
164
165        Ok(pem::encode(
166            pem::EncodingScheme::PKCS8,
167            pem::KeyType::Private,
168            line_ending,
169            pkcs8_der,
170        ))
171    }
172}
173
174#[cfg(feature = "pem")]
175impl PemEncode for RSAPublicKey {
176    /// Line endings default to Unix-style LF  
177    fn as_pkcs1_pem(&self) -> Result<String, Error> {
178        self.as_pkcs1_pem_custom_ending(LineEnding::LF)
179    }
180
181    /// Line endings default to Unix-style LF
182    fn as_pkcs8_pem(&self) -> Result<String, Error> {
183        self.as_pkcs8_pem_custom_ending(LineEnding::LF)
184    }
185
186    /// Encode with a different line ending
187    fn as_pkcs1_pem_custom_ending(&self, line_ending: LineEnding) -> Result<String, Error> {
188        let pkcs1_der = self.as_pkcs1()?;
189
190        Ok(pem::encode(
191            pem::EncodingScheme::PKCS1,
192            pem::KeyType::Public,
193            line_ending,
194            pkcs1_der,
195        ))
196    }
197
198    /// Encode with a different line ending
199    fn as_pkcs8_pem_custom_ending(&self, line_ending: LineEnding) -> Result<String, Error> {
200        let pkcs8_der = self.as_pkcs8()?;
201
202        Ok(pem::encode(
203            pem::EncodingScheme::PKCS8,
204            pem::KeyType::Public,
205            line_ending,
206            pkcs8_der,
207        ))
208    }
209}
210
211#[cfg(feature = "pem")]
212pub use pem_crate::LineEnding;
213
214mod sealed {
215    pub trait Sealed {}
216
217    impl Sealed for rsa::RSAPrivateKey {}
218    impl Sealed for rsa::RSAPublicKey {}
219}
220
221/// Error type
222pub mod error;
223
224/// Encode a public/private key with PKCS#1
225mod pkcs1;
226/// Encode a public/private key with PKCS#8
227mod pkcs8;
228
229#[cfg(feature = "pem")]
230/// Encode a public/private key encoded with PKCS#1 or PKCS#8 into the PEM format
231mod pem;
232
233#[cfg(test)]
234mod test;