1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
use crate::errors::{ErrorKind, Result};
/// Supported PEM files for EC and RSA Public and Private Keys
#[derive(Debug, PartialEq)]
enum PemType {
EcPublic,
EcPrivate,
RsaPublic,
RsaPrivate,
EdPublic,
EdPrivate,
}
#[derive(Debug, PartialEq)]
enum Standard {
// Only for RSA
Pkcs1,
// RSA/EC
Pkcs8,
}
#[derive(Debug, PartialEq)]
enum Classification {
Ec,
Ed,
Rsa,
}
/// The return type of a successful PEM encoded key with `decode_pem`
///
/// This struct gives a way to parse a string to a key for use in jsonwebtoken.
/// A struct is necessary as it provides the lifetime of the key
///
/// PEM public private keys are encoded PKCS#1 or PKCS#8
/// You will find that with PKCS#8 RSA keys that the PKCS#1 content
/// is embedded inside. This is what is provided to ring via `Key::Der`
/// For EC keys, they are always PKCS#8 on the outside but like RSA keys
/// EC keys contain a section within that ultimately has the configuration
/// that ring uses.
/// Documentation about these formats is at
/// PKCS#1: https://tools.ietf.org/html/rfc8017
/// PKCS#8: https://tools.ietf.org/html/rfc5958
#[derive(Debug)]
pub(crate) struct PemEncodedKey {
content: Vec<u8>,
asn1: Vec<simple_asn1::ASN1Block>,
pem_type: PemType,
standard: Standard,
}
impl PemEncodedKey {
/// Read the PEM file for later key use
pub fn new(input: &[u8]) -> Result<PemEncodedKey> {
match pem::parse(input) {
Ok(content) => {
let pem_contents = content.contents;
let asn1_content = match simple_asn1::from_der(pem_contents.as_slice()) {
Ok(asn1) => asn1,
Err(_) => return Err(ErrorKind::InvalidKeyFormat.into()),
};
match content.tag.as_ref() {
// This handles a PKCS#1 RSA Private key
"RSA PRIVATE KEY" => Ok(PemEncodedKey {
content: pem_contents,
asn1: asn1_content,
pem_type: PemType::RsaPrivate,
standard: Standard::Pkcs1,
}),
"RSA PUBLIC KEY" => Ok(PemEncodedKey {
content: pem_contents,
asn1: asn1_content,
pem_type: PemType::RsaPublic,
standard: Standard::Pkcs1,
}),
// No "EC PRIVATE KEY"
// https://security.stackexchange.com/questions/84327/converting-ecc-private-key-to-pkcs1-format
// "there is no such thing as a "PKCS#1 format" for elliptic curve (EC) keys"
// This handles PKCS#8 certificates and public & private keys
tag @ "PRIVATE KEY" | tag @ "PUBLIC KEY" | tag @ "CERTIFICATE" => {
match classify_pem(&asn1_content) {
Some(c) => {
let is_private = tag == "PRIVATE KEY";
let pem_type = match c {
Classification::Ec => {
if is_private {
PemType::EcPrivate
} else {
PemType::EcPublic
}
}
Classification::Ed => {
if is_private {
PemType::EdPrivate
} else {
PemType::EdPublic
}
}
Classification::Rsa => {
if is_private {
PemType::RsaPrivate
} else {
PemType::RsaPublic
}
}
};
Ok(PemEncodedKey {
content: pem_contents,
asn1: asn1_content,
pem_type,
standard: Standard::Pkcs8,
})
}
None => Err(ErrorKind::InvalidKeyFormat.into()),
}
}
// Unknown/unsupported type
_ => Err(ErrorKind::InvalidKeyFormat.into()),
}
}
Err(_) => Err(ErrorKind::InvalidKeyFormat.into()),
}
}
/// Can only be PKCS8
pub fn as_ec_private_key(&self) -> Result<&[u8]> {
match self.standard {
Standard::Pkcs1 => Err(ErrorKind::InvalidKeyFormat.into()),
Standard::Pkcs8 => match self.pem_type {
PemType::EcPrivate => Ok(self.content.as_slice()),
_ => Err(ErrorKind::InvalidKeyFormat.into()),
},
}
}
/// Can only be PKCS8
pub fn as_ec_public_key(&self) -> Result<&[u8]> {
match self.standard {
Standard::Pkcs1 => Err(ErrorKind::InvalidKeyFormat.into()),
Standard::Pkcs8 => match self.pem_type {
PemType::EcPublic => extract_first_bitstring(&self.asn1),
_ => Err(ErrorKind::InvalidKeyFormat.into()),
},
}
}
/// Can only be PKCS8
pub fn as_ed_private_key(&self) -> Result<&[u8]> {
match self.standard {
Standard::Pkcs1 => Err(ErrorKind::InvalidKeyFormat.into()),
Standard::Pkcs8 => match self.pem_type {
PemType::EdPrivate => Ok(self.content.as_slice()),
_ => Err(ErrorKind::InvalidKeyFormat.into()),
},
}
}
/// Can only be PKCS8
pub fn as_ed_public_key(&self) -> Result<&[u8]> {
match self.standard {
Standard::Pkcs1 => Err(ErrorKind::InvalidKeyFormat.into()),
Standard::Pkcs8 => match self.pem_type {
PemType::EdPublic => extract_first_bitstring(&self.asn1),
_ => Err(ErrorKind::InvalidKeyFormat.into()),
},
}
}
/// Can be PKCS1 or PKCS8
pub fn as_rsa_key(&self) -> Result<&[u8]> {
match self.standard {
Standard::Pkcs1 => Ok(self.content.as_slice()),
Standard::Pkcs8 => match self.pem_type {
PemType::RsaPrivate => extract_first_bitstring(&self.asn1),
PemType::RsaPublic => extract_first_bitstring(&self.asn1),
_ => Err(ErrorKind::InvalidKeyFormat.into()),
},
}
}
}
// This really just finds and returns the first bitstring or octet string
// Which is the x coordinate for EC public keys
// And the DER contents of an RSA key
// Though PKCS#11 keys shouldn't have anything else.
// It will get confusing with certificates.
fn extract_first_bitstring(asn1: &[simple_asn1::ASN1Block]) -> Result<&[u8]> {
for asn1_entry in asn1.iter() {
match asn1_entry {
simple_asn1::ASN1Block::Sequence(_, entries) => {
if let Ok(result) = extract_first_bitstring(entries) {
return Ok(result);
}
}
simple_asn1::ASN1Block::BitString(_, _, value) => {
return Ok(value.as_ref());
}
simple_asn1::ASN1Block::OctetString(_, value) => {
return Ok(value.as_ref());
}
_ => (),
}
}
Err(ErrorKind::InvalidEcdsaKey.into())
}
/// Find whether this is EC, RSA, or Ed
fn classify_pem(asn1: &[simple_asn1::ASN1Block]) -> Option<Classification> {
// These should be constant but the macro requires
// #![feature(const_vec_new)]
let ec_public_key_oid = simple_asn1::oid!(1, 2, 840, 10_045, 2, 1);
let rsa_public_key_oid = simple_asn1::oid!(1, 2, 840, 113_549, 1, 1, 1);
let ed25519_oid = simple_asn1::oid!(1, 3, 101, 112);
for asn1_entry in asn1.iter() {
match asn1_entry {
simple_asn1::ASN1Block::Sequence(_, entries) => {
if let Some(classification) = classify_pem(entries) {
return Some(classification);
}
}
simple_asn1::ASN1Block::ObjectIdentifier(_, oid) => {
if oid == ec_public_key_oid {
return Some(Classification::Ec);
}
if oid == rsa_public_key_oid {
return Some(Classification::Rsa);
}
if oid == ed25519_oid {
return Some(Classification::Ed);
}
}
_ => {}
}
}
None
}