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;