|
1 | 1 | extern crate schannel;
|
2 | 2 |
|
3 |
| -use self::schannel::cert_context::{CertContext, HashAlgorithm}; |
| 3 | +use self::schannel::cert_context::{CertContext, HashAlgorithm, KeySpec}; |
4 | 4 | use self::schannel::cert_store::{CertAdd, CertStore, Memory, PfxImportOptions};
|
| 5 | +use self::schannel::crypt_prov::{AcquireOptions, ProviderType}; |
5 | 6 | use self::schannel::schannel_cred::{Direction, Protocol, SchannelCred};
|
6 | 7 | use self::schannel::tls_stream;
|
7 | 8 | use std::error;
|
@@ -93,6 +94,59 @@ impl Identity {
|
93 | 94 |
|
94 | 95 | Ok(Identity { cert: identity })
|
95 | 96 | }
|
| 97 | + |
| 98 | + pub fn from_pkcs8(pem: &[u8], key: &[u8]) -> Result<Identity, Error> { |
| 99 | + if !key.starts_with(b"-----BEGIN PRIVATE KEY-----") { |
| 100 | + return Err(io::Error::new(io::ErrorKind::InvalidInput, "not a PKCS#8 key").into()); |
| 101 | + } |
| 102 | + |
| 103 | + let mut store = Memory::new()?.into_store(); |
| 104 | + let mut cert_iter = pem::PemBlock::new(pem).into_iter(); |
| 105 | + let leaf = cert_iter.next().ok_or_else(|| { |
| 106 | + io::Error::new( |
| 107 | + io::ErrorKind::InvalidInput, |
| 108 | + "at least one certificate must be provided to create an identity", |
| 109 | + ) |
| 110 | + })?; |
| 111 | + let cert = CertContext::from_pem(std::str::from_utf8(leaf).map_err(|_| { |
| 112 | + io::Error::new( |
| 113 | + io::ErrorKind::InvalidInput, |
| 114 | + "leaf cert contains invalid utf8", |
| 115 | + ) |
| 116 | + })?)?; |
| 117 | + |
| 118 | + let name = gen_container_name(); |
| 119 | + let mut options = AcquireOptions::new(); |
| 120 | + options.container(&name); |
| 121 | + let type_ = ProviderType::rsa_full(); |
| 122 | + |
| 123 | + let mut container = match options.acquire(type_) { |
| 124 | + Ok(container) => container, |
| 125 | + Err(_) => options.new_keyset(true).acquire(type_)?, |
| 126 | + }; |
| 127 | + container.import().import_pkcs8_pem(&key)?; |
| 128 | + |
| 129 | + cert.set_key_prov_info() |
| 130 | + .container(&name) |
| 131 | + .type_(type_) |
| 132 | + .keep_open(true) |
| 133 | + .key_spec(KeySpec::key_exchange()) |
| 134 | + .set()?; |
| 135 | + let mut context = store.add_cert(&cert, CertAdd::Always)?; |
| 136 | + |
| 137 | + for int_cert in cert_iter { |
| 138 | + let certificate = Certificate::from_pem(int_cert)?; |
| 139 | + context = store.add_cert(&certificate.0, CertAdd::Always)?; |
| 140 | + } |
| 141 | + Ok(Identity { cert: context }) |
| 142 | + } |
| 143 | +} |
| 144 | + |
| 145 | +// The name of the container must be unique to have multiple active keys. |
| 146 | +fn gen_container_name() -> String { |
| 147 | + use std::sync::atomic::{AtomicUsize, Ordering}; |
| 148 | + static COUNTER: AtomicUsize = AtomicUsize::new(0); |
| 149 | + format!("native-tls-{}", COUNTER.fetch_add(1, Ordering::Relaxed)) |
96 | 150 | }
|
97 | 151 |
|
98 | 152 | #[derive(Clone)]
|
@@ -384,3 +438,125 @@ impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
|
384 | 438 | self.0.flush()
|
385 | 439 | }
|
386 | 440 | }
|
| 441 | + |
| 442 | +mod pem { |
| 443 | + /// Split data by PEM guard lines |
| 444 | + pub struct PemBlock<'a> { |
| 445 | + pem_block: &'a str, |
| 446 | + cur_end: usize, |
| 447 | + } |
| 448 | + |
| 449 | + impl<'a> PemBlock<'a> { |
| 450 | + pub fn new(data: &'a [u8]) -> PemBlock<'a> { |
| 451 | + let s = ::std::str::from_utf8(data).unwrap(); |
| 452 | + PemBlock { |
| 453 | + pem_block: s, |
| 454 | + cur_end: s.find("-----BEGIN").unwrap_or(s.len()), |
| 455 | + } |
| 456 | + } |
| 457 | + } |
| 458 | + |
| 459 | + impl<'a> Iterator for PemBlock<'a> { |
| 460 | + type Item = &'a [u8]; |
| 461 | + fn next(&mut self) -> Option<Self::Item> { |
| 462 | + let last = self.pem_block.len(); |
| 463 | + if self.cur_end >= last { |
| 464 | + return None; |
| 465 | + } |
| 466 | + let begin = self.cur_end; |
| 467 | + let pos = self.pem_block[begin + 1..].find("-----BEGIN"); |
| 468 | + self.cur_end = match pos { |
| 469 | + Some(end) => end + begin + 1, |
| 470 | + None => last, |
| 471 | + }; |
| 472 | + return Some(&self.pem_block[begin..self.cur_end].as_bytes()); |
| 473 | + } |
| 474 | + } |
| 475 | + |
| 476 | + #[test] |
| 477 | + fn test_split() { |
| 478 | + // Split three certs, CRLF line terminators. |
| 479 | + assert_eq!( |
| 480 | + PemBlock::new( |
| 481 | + b"-----BEGIN FIRST-----\r\n-----END FIRST-----\r\n\ |
| 482 | + -----BEGIN SECOND-----\r\n-----END SECOND\r\n\ |
| 483 | + -----BEGIN THIRD-----\r\n-----END THIRD\r\n" |
| 484 | + ) |
| 485 | + .collect::<Vec<&[u8]>>(), |
| 486 | + vec![ |
| 487 | + b"-----BEGIN FIRST-----\r\n-----END FIRST-----\r\n" as &[u8], |
| 488 | + b"-----BEGIN SECOND-----\r\n-----END SECOND\r\n", |
| 489 | + b"-----BEGIN THIRD-----\r\n-----END THIRD\r\n" |
| 490 | + ] |
| 491 | + ); |
| 492 | + // Split three certs, CRLF line terminators except at EOF. |
| 493 | + assert_eq!( |
| 494 | + PemBlock::new( |
| 495 | + b"-----BEGIN FIRST-----\r\n-----END FIRST-----\r\n\ |
| 496 | + -----BEGIN SECOND-----\r\n-----END SECOND-----\r\n\ |
| 497 | + -----BEGIN THIRD-----\r\n-----END THIRD-----" |
| 498 | + ) |
| 499 | + .collect::<Vec<&[u8]>>(), |
| 500 | + vec![ |
| 501 | + b"-----BEGIN FIRST-----\r\n-----END FIRST-----\r\n" as &[u8], |
| 502 | + b"-----BEGIN SECOND-----\r\n-----END SECOND-----\r\n", |
| 503 | + b"-----BEGIN THIRD-----\r\n-----END THIRD-----" |
| 504 | + ] |
| 505 | + ); |
| 506 | + // Split two certs, LF line terminators. |
| 507 | + assert_eq!( |
| 508 | + PemBlock::new( |
| 509 | + b"-----BEGIN FIRST-----\n-----END FIRST-----\n\ |
| 510 | + -----BEGIN SECOND-----\n-----END SECOND\n" |
| 511 | + ) |
| 512 | + .collect::<Vec<&[u8]>>(), |
| 513 | + vec![ |
| 514 | + b"-----BEGIN FIRST-----\n-----END FIRST-----\n" as &[u8], |
| 515 | + b"-----BEGIN SECOND-----\n-----END SECOND\n" |
| 516 | + ] |
| 517 | + ); |
| 518 | + // Split two certs, CR line terminators. |
| 519 | + assert_eq!( |
| 520 | + PemBlock::new( |
| 521 | + b"-----BEGIN FIRST-----\r-----END FIRST-----\r\ |
| 522 | + -----BEGIN SECOND-----\r-----END SECOND\r" |
| 523 | + ) |
| 524 | + .collect::<Vec<&[u8]>>(), |
| 525 | + vec![ |
| 526 | + b"-----BEGIN FIRST-----\r-----END FIRST-----\r" as &[u8], |
| 527 | + b"-----BEGIN SECOND-----\r-----END SECOND\r" |
| 528 | + ] |
| 529 | + ); |
| 530 | + // Split two certs, LF line terminators except at EOF. |
| 531 | + assert_eq!( |
| 532 | + PemBlock::new( |
| 533 | + b"-----BEGIN FIRST-----\n-----END FIRST-----\n\ |
| 534 | + -----BEGIN SECOND-----\n-----END SECOND" |
| 535 | + ) |
| 536 | + .collect::<Vec<&[u8]>>(), |
| 537 | + vec![ |
| 538 | + b"-----BEGIN FIRST-----\n-----END FIRST-----\n" as &[u8], |
| 539 | + b"-----BEGIN SECOND-----\n-----END SECOND" |
| 540 | + ] |
| 541 | + ); |
| 542 | + // Split a single cert, LF line terminators. |
| 543 | + assert_eq!( |
| 544 | + PemBlock::new(b"-----BEGIN FIRST-----\n-----END FIRST-----\n").collect::<Vec<&[u8]>>(), |
| 545 | + vec![b"-----BEGIN FIRST-----\n-----END FIRST-----\n" as &[u8]] |
| 546 | + ); |
| 547 | + // Split a single cert, LF line terminators except at EOF. |
| 548 | + assert_eq!( |
| 549 | + PemBlock::new(b"-----BEGIN FIRST-----\n-----END FIRST-----").collect::<Vec<&[u8]>>(), |
| 550 | + vec![b"-----BEGIN FIRST-----\n-----END FIRST-----" as &[u8]] |
| 551 | + ); |
| 552 | + // (Don't) split garbage. |
| 553 | + assert_eq!( |
| 554 | + PemBlock::new(b"junk").collect::<Vec<&[u8]>>(), |
| 555 | + Vec::<&[u8]>::new() |
| 556 | + ); |
| 557 | + assert_eq!( |
| 558 | + PemBlock::new(b"junk-----BEGIN garbage").collect::<Vec<&[u8]>>(), |
| 559 | + vec![b"-----BEGIN garbage" as &[u8]] |
| 560 | + ); |
| 561 | + } |
| 562 | +} |
0 commit comments