Merge pull request #38 from aloucks/parse

Add parsing for pkcs1 and pkcs8 encoded rsa keys
This commit is contained in:
Tony Arcieri 2020-02-25 16:24:44 -10:00 committed by GitHub
commit 54ecc60caa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 605 additions and 2 deletions

View File

@ -21,6 +21,8 @@ rand = "0.7.0"
byteorder = "1.3.1"
failure = "0.1.5"
subtle = "2.0.0"
simple_asn1 = "0.4"
pem = { version = "0.7", optional = true }
[dependencies.zeroize]
version = "1.1.0"
@ -39,7 +41,7 @@ sha2 = "0.8.0"
hex = "0.4.0"
serde_test = "1.0.89"
rand_xorshift = "0.2.0"
pem = "0.7"
[[bench]]
name = "key"
@ -51,7 +53,7 @@ name = "key"
# debug = true
[features]
default = []
default = ["pem"]
nightly = ["subtle/nightly", "num-bigint/nightly"]
serde1 = ["num-bigint/serde", "serde"]
expose-internals = []

View File

@ -29,6 +29,8 @@ pub enum Error {
PublicExponentTooSmall,
#[fail(display = "public exponent too large")]
PublicExponentTooLarge,
#[fail(display = "parse error: {}", reason)]
ParseError { reason: String },
#[fail(display = "internal error")]
Internal,
}

View File

@ -192,6 +192,74 @@ impl RSAPublicKey {
Ok(k)
}
/// Parse a `PKCS1` encoded RSA Public Key.
///
/// The `der` data is expected to be the `base64` decoded content
/// following a `-----BEGIN RSA PUBLIC KEY-----` header.
///
/// <https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem>
///
/// # Example
///
/// ```
/// use rsa::RSAPublicKey;
///
/// # // openssl rsa -pubin -in tiny_key.pub.pem -RSAPublicKey_out
/// let file_content = r#"
/// -----BEGIN RSA PUBLIC KEY-----
/// MEgCQQCuWe45NapeQ6rkb5T5hYMgQwr3T0NE9rItqDwFJjcNmf6m9kq/wRAaFqWo
/// BX/BpdSuD0YqSUrnQ5ejf1XW9gmJAgMBAAE=
/// -----END RSA PUBLIC KEY-----
/// "#;
///
/// let der_encoded = file_content
/// .lines()
/// .filter(|line| !line.starts_with("-"))
/// .fold(String::new(), |mut data, line| {
/// data.push_str(&line);
/// data
/// });
/// let der_bytes = base64::decode(&der_encoded).expect("failed to decode base64 content");
/// let public_key = RSAPublicKey::from_pkcs1(&der_bytes).expect("failed to parse key");
/// ```
pub fn from_pkcs1(der: &[u8]) -> Result<RSAPublicKey> {
crate::parse::parse_public_key_pkcs1(der)
}
/// Parse a `PKCS8` encoded RSA Public Key.
///
/// The `der` data is expected to be the `base64` decoded content
/// following a `-----BEGIN PUBLIC KEY-----` header.
///
/// <https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem>
///
/// # Example
///
/// ```
/// use rsa::RSAPublicKey;
///
/// # // openssl rsa -in tiny_key.pem -outform PEM -pubout -out tiny_key.pub.pem
/// let file_content = r#"
/// -----BEGIN PUBLIC KEY-----
/// MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAK5Z7jk1ql5DquRvlPmFgyBDCvdPQ0T2
/// si2oPAUmNw2Z/qb2Sr/BEBoWpagFf8Gl1K4PRipJSudDl6N/Vdb2CYkCAwEAAQ==
/// -----END PUBLIC KEY-----
/// "#;
///
/// let der_encoded = file_content
/// .lines()
/// .filter(|line| !line.starts_with("-"))
/// .fold(String::new(), |mut data, line| {
/// data.push_str(&line);
/// data
/// });
/// let der_bytes = base64::decode(&der_encoded).expect("failed to decode base64 content");
/// let public_key = RSAPublicKey::from_pkcs8(&der_bytes).expect("failed to parse key");
/// ```
pub fn from_pkcs8(der: &[u8]) -> Result<RSAPublicKey> {
crate::parse::parse_public_key_pkcs8(der)
}
}
impl<'a> PublicKey for &'a RSAPublicKey {
@ -301,6 +369,85 @@ impl RSAPrivateKey {
k
}
/// Parse a `PKCS1` encoded RSA Private Key.
///
/// The `der` data is expected to be the `base64` decoded content
/// following a `-----BEGIN RSA PRIVATE KEY-----` header.
///
/// <https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem>
///
/// # Example
///
/// ```
/// use rsa::RSAPrivateKey;
///
/// # // openssl genrsa -out tiny_key.pem 512
/// let file_content = r#"
/// -----BEGIN RSA PRIVATE KEY-----
/// MIIBOwIBAAJBAK5Z7jk1ql5DquRvlPmFgyBDCvdPQ0T2si2oPAUmNw2Z/qb2Sr/B
/// EBoWpagFf8Gl1K4PRipJSudDl6N/Vdb2CYkCAwEAAQJBAI3vWCfqsE8c9zoQPE8F
/// icHx0jOSq0ixLExO8M2gVqESq3SJpWbEbvPPbRb1sIqZHe5wV3Xmj09zvUzfdeB7
/// C6ECIQDjoB/kp7QlRiNhgudhQPct8XUf6Cgp7hBxL2K9Q9UzawIhAMQVvtH1TUOd
/// aSWiqrFx7w+54o58fIpkecI5Kl0TaWfbAiBrnye1Kn2IKhNMZWIUn2y+8izYeyGS
/// QZbQjQD4T3wcJQIgKGgWv2teNZ29ai0AIbrJuaLjhdsvStFzqctf6Hg0k1sCIQCj
/// JdwDGF7Kanex70KAacmOlw3vfx6XWT+2PH6Qh8tLug==
/// -----END RSA PRIVATE KEY-----
/// "#;
///
/// let der_encoded = file_content
/// .lines()
/// .filter(|line| !line.starts_with("-"))
/// .fold(String::new(), |mut data, line| {
/// data.push_str(&line);
/// data
/// });
/// let der_bytes = base64::decode(&der_encoded).expect("failed to decode base64 content");
/// let private_key = RSAPrivateKey::from_pkcs1(&der_bytes).expect("failed to parse key");
/// ```
pub fn from_pkcs1(der: &[u8]) -> Result<RSAPrivateKey> {
crate::parse::parse_private_key_pkcs1(der)
}
/// Parse a `PKCS8` encoded RSA Private Key.
///
/// The `der` data is expected to be the `base64` decoded content
/// following a `-----BEGIN PRIVATE KEY-----` header.
///
/// <https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem>
///
/// # Example
///
/// ```
/// use rsa::RSAPrivateKey;
///
/// # // openssl pkcs8 -topk8 -inform PEM -outform PEM -in tiny_key.pem -out tiny_key.pkcs8.pem -nocrypt
/// let file_content = r#"
/// -----BEGIN PRIVATE KEY-----
/// MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEArlnuOTWqXkOq5G+U
/// +YWDIEMK909DRPayLag8BSY3DZn+pvZKv8EQGhalqAV/waXUrg9GKklK50OXo39V
/// 1vYJiQIDAQABAkEAje9YJ+qwTxz3OhA8TwWJwfHSM5KrSLEsTE7wzaBWoRKrdIml
/// ZsRu889tFvWwipkd7nBXdeaPT3O9TN914HsLoQIhAOOgH+SntCVGI2GC52FA9y3x
/// dR/oKCnuEHEvYr1D1TNrAiEAxBW+0fVNQ51pJaKqsXHvD7nijnx8imR5wjkqXRNp
/// Z9sCIGufJ7UqfYgqE0xlYhSfbL7yLNh7IZJBltCNAPhPfBwlAiAoaBa/a141nb1q
/// LQAhusm5ouOF2y9K0XOpy1/oeDSTWwIhAKMl3AMYXspqd7HvQoBpyY6XDe9/HpdZ
/// P7Y8fpCHy0u6
/// -----END PRIVATE KEY-----
/// "#;
///
/// let der_encoded = file_content
/// .lines()
/// .filter(|line| !line.starts_with("-"))
/// .fold(String::new(), |mut data, line| {
/// data.push_str(&line);
/// data
/// });
/// let der_bytes = base64::decode(&der_encoded).expect("failed to decode base64 content");
/// let private_key = RSAPrivateKey::from_pkcs8(&der_bytes).expect("failed to parse key");
/// ```
pub fn from_pkcs8(der: &[u8]) -> Result<RSAPrivateKey> {
crate::parse::parse_private_key_pkcs8(der)
}
/// Get the public key from the private key, cloning `n` and `e`.
///
/// Generally this is not needed since `RSAPrivateKey` implements the `PublicKey` trait,

View File

@ -60,8 +60,12 @@ pub mod hash;
/// Supported padding schemes.
pub mod padding;
#[cfg(feature="pem")]
pub use pem;
mod key;
mod pkcs1v15;
mod parse;
pub use self::key::{PublicKey, RSAPrivateKey, RSAPublicKey};
pub use self::padding::PaddingScheme;

448
src/parse.rs Normal file
View File

@ -0,0 +1,448 @@
use crate::{
errors::{Error, Result},
RSAPrivateKey, RSAPublicKey,
};
use simple_asn1::{ASN1Block, ASN1DecodeErr, BigUint, OID};
use std::convert::TryFrom;
impl From<ASN1DecodeErr> for Error {
fn from(e: ASN1DecodeErr) -> Error {
Error::ParseError {
reason: format!("{}", e),
}
}
}
#[cfg(feature = "pem")]
impl TryFrom<pem::Pem> for RSAPrivateKey {
type Error = Error;
/// Parses a `PKCS8` or `PKCS1` encoded RSA Private Key.
///
/// Expects one of the following `pem` headers:
/// - `-----BEGIN PRIVATE KEY-----`
/// - `-----BEGIN RSA PRIVATE KEY-----`
///
/// # Example
///
/// ```
/// use std::convert::TryFrom;
/// use rsa::RSAPrivateKey;
///
/// # // openssl genrsa -out tiny_key.pem 512
/// let file_content = r#"
/// -----BEGIN RSA PRIVATE KEY-----
/// MIIBOwIBAAJBAK5Z7jk1ql5DquRvlPmFgyBDCvdPQ0T2si2oPAUmNw2Z/qb2Sr/B
/// EBoWpagFf8Gl1K4PRipJSudDl6N/Vdb2CYkCAwEAAQJBAI3vWCfqsE8c9zoQPE8F
/// icHx0jOSq0ixLExO8M2gVqESq3SJpWbEbvPPbRb1sIqZHe5wV3Xmj09zvUzfdeB7
/// C6ECIQDjoB/kp7QlRiNhgudhQPct8XUf6Cgp7hBxL2K9Q9UzawIhAMQVvtH1TUOd
/// aSWiqrFx7w+54o58fIpkecI5Kl0TaWfbAiBrnye1Kn2IKhNMZWIUn2y+8izYeyGS
/// QZbQjQD4T3wcJQIgKGgWv2teNZ29ai0AIbrJuaLjhdsvStFzqctf6Hg0k1sCIQCj
/// JdwDGF7Kanex70KAacmOlw3vfx6XWT+2PH6Qh8tLug==
/// -----END RSA PRIVATE KEY-----
/// "#;
///
/// let pem = rsa::pem::parse(file_content).expect("failed to parse pem file");
/// let private_key = RSAPrivateKey::try_from(pem).expect("failed to parse key");
/// ```
fn try_from(pem: pem::Pem) -> Result<RSAPrivateKey> {
match &*pem.tag {
"RSA PRIVATE KEY" => parse_private_key_pkcs1(&pem.contents),
"PRIVATE KEY" => parse_private_key_pkcs8(&pem.contents),
_ => Err(Error::ParseError {
reason: format!("unexpected tag: {}", pem.tag),
}),
}
}
}
#[cfg(feature = "pem")]
impl TryFrom<pem::Pem> for RSAPublicKey {
type Error = Error;
/// Parses a `PKCS8` or `PKCS1` encoded RSA Public Key.
///
/// Expects one of the following `pem` headers:
/// - `-----BEGIN PUBLIC KEY-----`
/// - `-----BEGIN RSA PUBLIC KEY-----`
///
/// # Example
///
/// ```
/// use std::convert::TryFrom;
/// use rsa::RSAPublicKey;
///
/// # // openssl rsa -in tiny_key.pem -outform PEM -pubout -out tiny_key.pub.pem
/// let file_content = r#"
/// -----BEGIN PUBLIC KEY-----
/// MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAK5Z7jk1ql5DquRvlPmFgyBDCvdPQ0T2
/// si2oPAUmNw2Z/qb2Sr/BEBoWpagFf8Gl1K4PRipJSudDl6N/Vdb2CYkCAwEAAQ==
/// -----END PUBLIC KEY-----
/// "#;
///
/// let pem = rsa::pem::parse(file_content).expect("failed to parse pem file");
/// let public_key = RSAPublicKey::try_from(pem).expect("failed to parse key");
/// ```
fn try_from(pem: pem::Pem) -> Result<RSAPublicKey> {
match &*pem.tag {
"RSA PUBLIC KEY" => parse_public_key_pkcs1(&pem.contents),
"PUBLIC KEY" => parse_public_key_pkcs8(&pem.contents),
_ => Err(Error::ParseError {
reason: format!("unexpected tag: {}", pem.tag),
}),
}
}
}
fn big_uint(value: &simple_asn1::BigInt) -> Result<crate::BigUint> {
match value.to_biguint() {
Some(value) => {
// TODO: Open simple_asn1 pull request to update the num-bigint crate
Ok(crate::BigUint::from_bytes_le(&value.to_bytes_le()))
}
None => Err(Error::ParseError {
reason: format!("BigInt::to_biguint failed"),
}),
}
}
macro_rules! try_asn1 {
(Sequence($maybe:expr)) => {
if let Some(ASN1Block::Sequence(_, value)) = $maybe {
value
} else {
return Err(Error::ParseError {
reason: format!("expected asn1 sequence"),
});
}
};
(Integer($maybe:expr), $field:expr) => {
if let Some(ASN1Block::Integer(_, value)) = $maybe {
value
} else {
return Err(Error::ParseError {
reason: format!("expected asn1 integer: {}", $field),
});
}
};
(BitString($maybe:expr), $field:expr) => {
if let Some(ASN1Block::BitString(_, _, value)) = $maybe {
value
} else {
return Err(Error::ParseError {
reason: format!("expected asn1 bit string: {}", $field),
});
}
};
(OctetString($maybe:expr), $field:expr) => {
if let Some(ASN1Block::OctetString(_, value)) = $maybe {
value
} else {
return Err(Error::ParseError {
reason: format!("expected asn1 octet string: {}", $field),
});
}
};
(ObjectIdentifier($maybe:expr), $field:expr) => {
if let Some(ASN1Block::ObjectIdentifier(_, value)) = $maybe {
value
} else {
return Err(Error::ParseError {
reason: format!("expected asn1 object identifier: {}", $field),
});
}
};
}
/// Parse a `PKCS1` encoded RSA Private Key.
///
/// The `der` data is expected to be the `base64` decoded content
/// following a `-----BEGIN RSA PRIVATE KEY-----` header.
///
/// <https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem>
pub fn parse_private_key_pkcs1(der: &[u8]) -> Result<RSAPrivateKey> {
let asn1 = simple_asn1::from_der(der)?;
let sequence = try_asn1!(Sequence(asn1.iter().next()));
let mut blocks = sequence.iter();
let _version = big_uint(try_asn1!(Integer(blocks.next()), "version"))?;
let n = big_uint(try_asn1!(Integer(blocks.next()), "modulus (n)"))?;
let e = big_uint(try_asn1!(Integer(blocks.next()), "publicExponent (e)"))?;
let d = big_uint(try_asn1!(Integer(blocks.next()), "privateExponent (d)"))?;
let prime1 = big_uint(try_asn1!(Integer(blocks.next()), "prime1"))?;
let prime2 = big_uint(try_asn1!(Integer(blocks.next()), "prime2"))?;
let primes = vec![prime1, prime2];
Ok(RSAPrivateKey::from_components(n, e, d, primes))
}
/// Parse a `PKCS1` encoded RSA Public Key.
///
/// The `der` data is expected to be the `base64` decoded content
/// following a `-----BEGIN RSA PUBLIC KEY-----` header.
///
/// <https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem>
pub fn parse_public_key_pkcs1(der: &[u8]) -> Result<RSAPublicKey> {
let asn1 = simple_asn1::from_der(der)?;
let sequence = try_asn1!(Sequence(asn1.iter().next()));
let mut blocks = sequence.iter();
let n = big_uint(try_asn1!(Integer(blocks.next()), "modulus (n)"))?;
let e = big_uint(try_asn1!(Integer(blocks.next()), "exponent (e)"))?;
Ok(RSAPublicKey::new(n, e)?)
}
/// Parse a `PKCS8` encoded RSA Public Key.
///
/// The `der` data is expected to be the `base64` decoded content
/// following a `-----BEGIN PUBLIC KEY-----` header.
///
/// <https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem>
pub fn parse_public_key_pkcs8(der: &[u8]) -> Result<RSAPublicKey> {
let asn1 = simple_asn1::from_der(der)?;
let sequence = try_asn1!(Sequence(asn1.iter().next()));
let mut blocks = sequence.iter();
let algorithm = try_asn1!(Sequence(blocks.next()));
let oid = try_asn1!(ObjectIdentifier(algorithm.iter().next()), "oid");
if oid != rsa_oid() {
return Err(Error::ParseError {
reason: format!("oid mismatch: not an rsa key"),
});
}
let bit_string = try_asn1!(BitString(blocks.next()), "PublicKey");
parse_public_key_pkcs1(&bit_string)
}
/// Parse a `PKCS8` encoded RSA Private Key.
///
/// The `der` data is expected to be the `base64` decoded content
/// following a `-----BEGIN PRIVATE KEY-----` header.
///
/// <https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem>
pub fn parse_private_key_pkcs8(der: &[u8]) -> Result<RSAPrivateKey> {
let asn1 = simple_asn1::from_der(der)?;
let sequence = try_asn1!(Sequence(asn1.iter().next()));
let mut blocks = sequence.iter();
let _version = big_uint(try_asn1!(Integer(blocks.next()), "version"))?;
let algorithm = try_asn1!(Sequence(blocks.next()));
let oid = try_asn1!(ObjectIdentifier(algorithm.iter().next()), "oid");
if oid != rsa_oid() {
return Err(Error::ParseError {
reason: format!("oid mismatch: not an rsa key"),
});
}
let octet_string = try_asn1!(OctetString(blocks.next()), "PrivateKey");
parse_private_key_pkcs1(&octet_string)
}
fn rsa_oid() -> OID {
simple_asn1::oid!(1, 2, 840, 113549, 1, 1, 1)
}
#[cfg(test)]
mod tests {
use super::{
parse_private_key_pkcs1, parse_private_key_pkcs8, parse_public_key_pkcs1,
parse_public_key_pkcs8,
};
use crate::{PaddingScheme, PublicKey, RSAPrivateKey, RSAPublicKey};
#[cfg(feature = "pem")]
use std::convert::TryFrom;
/// `openssl genrsa -out test_key.pem`
const PKCS1_PRIVATE_KEY: &str = r#"
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAty3qrJz+cIhdfghtfD+f9CW+H6rn6IYyHQxYuDoie6eeHuRg
m8xAn/l37CcyBh/EkQz5WqD7DD5koK0jsHFWz40WC6USh+W7+1TMFJrp8orF+F8U
PeXlydTo2B7fXvIyi1oOF0HpgcRF/L2Ey/2MLvDmY+uLxil/YJNtF/BI57ycaphQ
LezlRsTpvwAwruLCDeBRefqw39u8nDiOVTr0bLfSzSQtHbX+Fxnko+MzLA3tdWGH
wv3tiPnTyLr+FIzzuJtX7aTuqzMM+u3sH7eKS6M1Lu0qFRw5LFcagLgWsIDUmP+5
hsrzVY8J0wvbOtL9NjCMy6zz1zQ+N7oJ9mdhNwIDAQABAoIBAHtWWF+3KX7d4o18
4TM6p9m9HAG4koO278EtUgNhaVx3JPlJ7l6YrZ7JW1zPm1gSRckgwjiqkb7Rt/GU
AqbH+ZqwNXrLv+lu3x7AHtV05TbhB6FPa5Kt3AYE7G6wgtgsHapEjZ5NTAuK+1DM
zsCHTL9Chu4aaDeaM2D0gw0ORhh5ex4S+NW0utGdkKXP85rxrhqMj58T0HuMPqGK
TyPQzXX58nJ/OniYs4iPJUKV+iiN0FIhQUDn7dcNi3o3nqNdULNMp9t91EePqe4N
XIPVmfYSOK7nsB3n9dK6JRKuXA2PwFfPWaYBYsb2O3MH80u7qTYoxrcoVzkluaEt
IINxF3kCgYEA6f6thnq9+CYvWf8/cYDTs6uTX/zNHravsFUH1SBTUXBMrj26qZ65
tbnWmqZUwbOHb/qvcIBR4gqZW8fMIKMimZoSdqAEMEhOOiak/fqG7asnsFmkryzL
s8Vu7EfZWMTHHpVap5tOBo2l3Z9wFXV+sUY6woMLhcKycctDBjMY9lsCgYEAyGfg
+mqdm91TcOaOJO0WPPNm+S8MKzfRBD6NZcCUn08mg+GLhBysX3hMpdWlnxiU04PR
Z6B2e1EDXrppBVnohXPTSdUpsxU1Bot+GN2sxn3wjvkrLUrbd5K7L463nNvfCbnI
Xnw0YpDBqxVSmllRgadlVE5bNQsY7tyRRhfez1UCgYA08iB3dlx3wsQiHARR/XFp
jSAarwwGsBWO056jFd5kZgGjx2nuKXEh8nvhoFM7RREXQGTkEtT0TaunvcytcdxJ
XzhgSRLJjgLNW6MnqMFRE9I2MAJ4dK1e7wSLSDKgyF25yNerZxO/ndtzCzmEUYKq
QBbZnmdEC+runqx6waMbUwKBgH/yGiemQ8OL9UbCW4Plvenh+B8k018QPqi4Cuwo
nHptPQi7yafp6n71PfIuSZQeTH4RzXKcdqhaW41x87TP5uy0uvOLXRkRdA4epr3X
YQREyX1uRQugnCdmDY3aTw2dLnN2Ih94qrU29/5zGY6jca8WIVJGyGJAGIX/Wdxn
RwAVAoGAbKBEaO52DpjLXc0fdSuYtYCxBdoAhuDFcrHBeStxyndzIbIyVkHLpui9
8Fx/mzEXc+yfT+tqY2q3nFUDKCj9fby62rbnQOs+7tdBx4qGVA1VymAcrfB3vIGt
Kw1Dq0JU0uu70Ivod7tZLZd++lQlBaONOCLXN7csQE7EcviQrxI=
-----END RSA PRIVATE KEY-----
"#;
/// `openssl pkcs8 -topk8 -inform PEM -outform PEM -in test_key.pem -out test_key.pkcs8.pem -nocrypt`
const PKCS8_PRIVATE_KEY: &str = r#"
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC3LeqsnP5wiF1+
CG18P5/0Jb4fqufohjIdDFi4OiJ7p54e5GCbzECf+XfsJzIGH8SRDPlaoPsMPmSg
rSOwcVbPjRYLpRKH5bv7VMwUmunyisX4XxQ95eXJ1OjYHt9e8jKLWg4XQemBxEX8
vYTL/Ywu8OZj64vGKX9gk20X8EjnvJxqmFAt7OVGxOm/ADCu4sIN4FF5+rDf27yc
OI5VOvRst9LNJC0dtf4XGeSj4zMsDe11YYfC/e2I+dPIuv4UjPO4m1ftpO6rMwz6
7ewft4pLozUu7SoVHDksVxqAuBawgNSY/7mGyvNVjwnTC9s60v02MIzLrPPXND43
ugn2Z2E3AgMBAAECggEAe1ZYX7cpft3ijXzhMzqn2b0cAbiSg7bvwS1SA2FpXHck
+UnuXpitnslbXM+bWBJFySDCOKqRvtG38ZQCpsf5mrA1esu/6W7fHsAe1XTlNuEH
oU9rkq3cBgTsbrCC2CwdqkSNnk1MC4r7UMzOwIdMv0KG7hpoN5ozYPSDDQ5GGHl7
HhL41bS60Z2Qpc/zmvGuGoyPnxPQe4w+oYpPI9DNdfnycn86eJiziI8lQpX6KI3Q
UiFBQOft1w2Lejeeo11Qs0yn233UR4+p7g1cg9WZ9hI4ruewHef10rolEq5cDY/A
V89ZpgFixvY7cwfzS7upNijGtyhXOSW5oS0gg3EXeQKBgQDp/q2Ger34Ji9Z/z9x
gNOzq5Nf/M0etq+wVQfVIFNRcEyuPbqpnrm1udaaplTBs4dv+q9wgFHiCplbx8wg
oyKZmhJ2oAQwSE46JqT9+obtqyewWaSvLMuzxW7sR9lYxMcelVqnm04GjaXdn3AV
dX6xRjrCgwuFwrJxy0MGMxj2WwKBgQDIZ+D6ap2b3VNw5o4k7RY882b5LwwrN9EE
Po1lwJSfTyaD4YuEHKxfeEyl1aWfGJTTg9FnoHZ7UQNeumkFWeiFc9NJ1SmzFTUG
i34Y3azGffCO+SstStt3krsvjrec298JuchefDRikMGrFVKaWVGBp2VUTls1Cxju
3JFGF97PVQKBgDTyIHd2XHfCxCIcBFH9cWmNIBqvDAawFY7TnqMV3mRmAaPHae4p
cSHye+GgUztFERdAZOQS1PRNq6e9zK1x3ElfOGBJEsmOAs1boyeowVET0jYwAnh0
rV7vBItIMqDIXbnI16tnE7+d23MLOYRRgqpAFtmeZ0QL6u6erHrBoxtTAoGAf/Ia
J6ZDw4v1RsJbg+W96eH4HyTTXxA+qLgK7Cicem09CLvJp+nqfvU98i5JlB5MfhHN
cpx2qFpbjXHztM/m7LS684tdGRF0Dh6mvddhBETJfW5FC6CcJ2YNjdpPDZ0uc3Yi
H3iqtTb3/nMZjqNxrxYhUkbIYkAYhf9Z3GdHABUCgYBsoERo7nYOmMtdzR91K5i1
gLEF2gCG4MVyscF5K3HKd3MhsjJWQcum6L3wXH+bMRdz7J9P62pjarecVQMoKP19
vLratudA6z7u10HHioZUDVXKYByt8He8ga0rDUOrQlTS67vQi+h3u1ktl376VCUF
o404Itc3tyxATsRy+JCvEg==
-----END PRIVATE KEY-----
"#;
/// `openssl rsa -in test_key.pem -outform PEM -pubout -out test_key.pub.pem`
const PKCS8_PUBLIC_KEY: &str = r#"
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAty3qrJz+cIhdfghtfD+f
9CW+H6rn6IYyHQxYuDoie6eeHuRgm8xAn/l37CcyBh/EkQz5WqD7DD5koK0jsHFW
z40WC6USh+W7+1TMFJrp8orF+F8UPeXlydTo2B7fXvIyi1oOF0HpgcRF/L2Ey/2M
LvDmY+uLxil/YJNtF/BI57ycaphQLezlRsTpvwAwruLCDeBRefqw39u8nDiOVTr0
bLfSzSQtHbX+Fxnko+MzLA3tdWGHwv3tiPnTyLr+FIzzuJtX7aTuqzMM+u3sH7eK
S6M1Lu0qFRw5LFcagLgWsIDUmP+5hsrzVY8J0wvbOtL9NjCMy6zz1zQ+N7oJ9mdh
NwIDAQAB
-----END PUBLIC KEY-----
"#;
/// `ssh-keygen -t rsa -e -m pem -f test_key.pem`
const PKCS1_PUBLIC_KEY: &str = r#"
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAty3qrJz+cIhdfghtfD+f9CW+H6rn6IYyHQxYuDoie6eeHuRgm8xA
n/l37CcyBh/EkQz5WqD7DD5koK0jsHFWz40WC6USh+W7+1TMFJrp8orF+F8UPeXl
ydTo2B7fXvIyi1oOF0HpgcRF/L2Ey/2MLvDmY+uLxil/YJNtF/BI57ycaphQLezl
RsTpvwAwruLCDeBRefqw39u8nDiOVTr0bLfSzSQtHbX+Fxnko+MzLA3tdWGHwv3t
iPnTyLr+FIzzuJtX7aTuqzMM+u3sH7eKS6M1Lu0qFRw5LFcagLgWsIDUmP+5hsrz
VY8J0wvbOtL9NjCMy6zz1zQ+N7oJ9mdhNwIDAQAB
-----END RSA PUBLIC KEY-----
"#;
#[test]
fn parse_pkcs1_private_key() {
let pem = pem::parse(PKCS1_PRIVATE_KEY).expect("pem::parse failed");
parse_private_key_pkcs1(&pem.contents).expect("parse_private_key_pkcs1 failed");
}
#[test]
fn parse_pkcs8_private_key() {
let pem = pem::parse(PKCS8_PRIVATE_KEY).expect("pem::parse failed");
parse_private_key_pkcs8(&pem.contents).expect("parse_private_key_pkcs8 failed");
}
#[test]
fn parse_pkcs1_public_key() {
let pem = pem::parse(PKCS1_PUBLIC_KEY).expect("pem::parse failed");
parse_public_key_pkcs1(&pem.contents).expect("parse_public_key_pkcs1 failed");
}
#[test]
fn parse_pkcs8_public_key() {
let pem = pem::parse(PKCS8_PUBLIC_KEY).expect("pem::parse failed");
parse_public_key_pkcs8(&pem.contents).expect("parse_public_key_pkcs8 failed");
}
#[test]
fn verify_public_keys_are_equal() {
let pem = pem::parse(PKCS1_PUBLIC_KEY).expect("pem::parse failed");
let public_key_pkcs1 =
parse_public_key_pkcs1(&pem.contents).expect("parse_public_key_pkcs1 failed");
let pem = pem::parse(PKCS8_PUBLIC_KEY).expect("pem::parse failed");
let public_key_pkcs8 =
parse_public_key_pkcs8(&pem.contents).expect("parse_public_key_pkcs8 failed");
assert_eq!(public_key_pkcs1, public_key_pkcs8);
}
#[test]
fn verify_private_keys_are_equal() {
let pem = pem::parse(PKCS1_PRIVATE_KEY).expect("pem::parse failed");
let private_key_pkcs1 =
parse_private_key_pkcs1(&pem.contents).expect("parse_private_key_pkcs1 failed");
let pem = pem::parse(PKCS8_PRIVATE_KEY).expect("pem::parse failed");
let private_key_pkcs8 =
parse_private_key_pkcs8(&pem.contents).expect("parse_private_key_pkcs8 failed");
assert_eq!(private_key_pkcs1, private_key_pkcs8);
}
#[test]
fn verify_encrypt_decrypt() {
let pem = pem::parse(PKCS1_PUBLIC_KEY).expect("pem::parse failed");
let public_key =
parse_public_key_pkcs1(&pem.contents).expect("parse_public_key_pkcs1 failed");
let pem = pem::parse(PKCS1_PRIVATE_KEY).expect("pem::parse failed");
let private_key =
parse_private_key_pkcs1(&pem.contents).expect("parse_private_key_pkcs1 failed");
let rng = &mut rand::thread_rng();
let clear_text = "Hello, World!";
let encrypted = public_key
.encrypt(rng, PaddingScheme::PKCS1v15, clear_text.as_bytes())
.expect("encrypt failed");
let decrypted = private_key
.decrypt(PaddingScheme::PKCS1v15, &encrypted)
.expect("decrypt failed");
assert_eq!(
clear_text.as_bytes(),
decrypted.as_slice(),
"clear text did not match decrypted data"
);
}
#[test]
#[cfg(feature = "pem")]
fn rsa_private_key_try_from_pkcs1() {
let pem = pem::parse(PKCS1_PRIVATE_KEY).expect("pem::parse failed");
RSAPrivateKey::try_from(pem).expect("RSAPrivateKey::try_from failed");
}
#[test]
#[cfg(feature = "pem")]
fn rsa_private_key_try_from_pkcs8() {
let pem = pem::parse(PKCS8_PRIVATE_KEY).expect("pem::parse failed");
RSAPrivateKey::try_from(pem).expect("RSAPrivateKey::try_from failed");
}
#[test]
#[cfg(feature = "pem")]
fn rsa_public_key_try_from_pkcs1() {
let pem = pem::parse(PKCS1_PUBLIC_KEY).expect("pem::parse failed");
RSAPublicKey::try_from(pem).expect("RSAPublicKey::try_from failed");
}
#[test]
#[cfg(feature = "pem")]
fn rsa_public_key_try_from_pkcs8() {
let pem = pem::parse(PKCS8_PUBLIC_KEY).expect("pem::parse failed");
RSAPublicKey::try_from(pem).expect("RSAPublicKey::try_from failed");
}
}