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
extern crate yasna;
extern crate base64;
extern crate num;
extern crate byteorder;
const RSA_ENCRYPTION: [u64; 7] = [1, 2, 840, 113549, 1, 1, 1];
const SSH_RSA_TEXT: &str = "ssh-rsa";
use std::io::{Read, BufRead};
use std::io::{self, BufReader};
use num::bigint::{BigInt, Sign};
use base64::{decode, encode, DecodeError};
use yasna::{parse_der, ASN1Error};
use yasna::models::ObjectIdentifier;
use byteorder::BigEndian;
use byteorder::WriteBytesExt;
#[derive(Debug)]
pub enum Error {
IoError(io::Error),
DecodeError(DecodeError),
ASN1Error(ASN1Error),
InvalidSshaRsa,
}
#[derive(Debug)]
struct PublicDer {
object_identifier: ObjectIdentifier,
exponent: BigInt,
modulus: BigInt,
}
pub fn pem_to_der<R: Read>(pem: &mut R) -> Result<Vec<u8>, Error> {
let buff = BufReader::new(pem);
let mut pem = String::new();
for line in buff.lines() {
let line = line.map_err(Error::IoError)?;
if !line.starts_with('-') {
pem.extend(line.chars());
}
}
Ok(decode(&pem).map_err(Error::DecodeError)?)
}
pub fn der_to_openssh(der: &[u8]) -> Result<String, Error> {
let public_der = parse_der(der, |reader| {
reader.read_sequence(|reader| {
let oid = reader.next().read_sequence(|reader| {
let oid = reader.next().read_oid()?;
let _ = reader.next().read_null()?;
Ok(oid)
})?;
let bs = reader.next().read_bitvec()?.to_bytes();
let (n, e) = parse_der(&bs, |reader| {
reader.read_sequence(|reader| {
let n = reader.next().read_bigint()?;
let e = reader.next().read_bigint()?;
Ok((n, e))
})
})?;
Ok(PublicDer {
object_identifier: oid,
exponent: e,
modulus: n,
})
})
}).map_err(Error::ASN1Error)?;
let iod = public_der.object_identifier.components().as_slice();
if iod != RSA_ENCRYPTION {
return Err(Error::InvalidSshaRsa);
}
let mut openssh_key = Vec::new();
let ssh_rsa_text_len = SSH_RSA_TEXT.len() as u32;
openssh_key.write_u32::<BigEndian>(ssh_rsa_text_len).map_err(Error::IoError)?;
openssh_key.extend_from_slice(SSH_RSA_TEXT.as_bytes());
let exp_bits_size = (public_der.exponent.bits() / 8 + 1) as u32;
openssh_key.write_u32::<BigEndian>(exp_bits_size).map_err(Error::IoError)?;
let (sign, mut bytes) = public_der.exponent.to_bytes_be();
if bytes.len() < exp_bits_size as usize {
bytes.insert(0, 0);
}
bytes[0] |= if sign == Sign::Minus { 0b1000_0000 } else { 0 };
openssh_key.extend_from_slice(&bytes);
let mod_bits_size = (public_der.modulus.bits() / 8 + 1) as u32;
openssh_key.write_u32::<BigEndian>(mod_bits_size).map_err(Error::IoError)?;
let (sign, mut bytes) = public_der.modulus.to_bytes_be();
if bytes.len() < mod_bits_size as usize {
bytes.insert(0, 0);
}
bytes[0] |= if sign == Sign::Minus { 0b1000_0000 } else { 0 };
openssh_key.extend_from_slice(&bytes);
let mut openssh_key = encode(&openssh_key);
openssh_key.insert_str(0, "ssh-rsa ");
Ok(openssh_key)
}