├── .gitignore ├── tests ├── data │ ├── gunk.pem │ ├── nistp256curve.pem │ ├── nistp256key.pem │ ├── nistp256key.pkcs8.pem │ ├── spki.pem │ ├── rsa1024.pkcs1.pem │ ├── rsa1024.pkcs8.pem │ ├── crl.pem │ ├── csr.pem │ ├── whitespace-prefix.crt │ ├── certificate.pem │ ├── certificate.chain.pem │ ├── mixed-line-endings.crt │ ├── zen.pem │ └── zen2.pem └── integration.rs ├── .github ├── dependabot.yml └── workflows │ └── build.yml ├── LICENSE ├── benches └── benchmark.rs ├── Cargo.lock ├── Cargo.toml ├── LICENSE-ISC ├── LICENSE-MIT ├── README.md ├── src ├── tests.rs ├── pemfile.rs └── lib.rs └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | -------------------------------------------------------------------------------- /tests/data/gunk.pem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustls/pemfile/HEAD/tests/data/gunk.pem -------------------------------------------------------------------------------- /tests/data/nistp256curve.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BggqhkjOPQMBBw== 3 | -----END EC PARAMETERS----- 4 | -------------------------------------------------------------------------------- /tests/data/nistp256key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIMaA7bFrjDDBSik057bIKo7UQXJZNwLK9AjYZQ7yIWFloAoGCCqGSM49 3 | AwEHoUQDQgAExu0Z/w8nQJZAXeOXOnZun9HiZscY9H/KwYcXpeZHu+f9P9mOUEkH 4 | 5Z0av+JKtzhFspjngNLVgWcjlA1L5AJLdA== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /tests/data/nistp256key.pkcs8.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgxoDtsWuMMMFKKTTn 3 | tsgqjtRBclk3Asr0CNhlDvIhYWWhRANCAATG7Rn/DydAlkBd45c6dm6f0eJmxxj0 4 | f8rBhxel5ke75/0/2Y5QSQflnRq/4kq3OEWymOeA0tWBZyOUDUvkAkt0 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | - package-ecosystem: github-actions 9 | directory: "/" 10 | schedule: 11 | interval: weekly 12 | open-pull-requests-limit: 10 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | rustls-pemfile is distributed under the following three licenses: 2 | 3 | - Apache License version 2.0. 4 | - MIT license. 5 | - ISC license. 6 | 7 | These are included as LICENSE-APACHE, LICENSE-MIT and LICENSE-ISC 8 | respectively. You may use this software under the terms of any 9 | of these licenses, at your option. 10 | 11 | -------------------------------------------------------------------------------- /benches/benchmark.rs: -------------------------------------------------------------------------------- 1 | use std::io::BufReader; 2 | 3 | use bencher::{benchmark_group, benchmark_main, Bencher}; 4 | 5 | fn criterion_benchmark(c: &mut Bencher) { 6 | c.iter(|| { 7 | let data = include_bytes!("../tests/data/certificate.chain.pem"); 8 | let mut reader = BufReader::new(&data[..]); 9 | assert_eq!( 10 | rustls_pemfile::certs(&mut reader) 11 | .collect::, _>>() 12 | .unwrap() 13 | .len(), 14 | 3 15 | ); 16 | }); 17 | } 18 | 19 | benchmark_group!(benches, criterion_benchmark); 20 | benchmark_main!(benches); 21 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "bencher" 7 | version = "0.1.5" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "7dfdb4953a096c551ce9ace855a604d702e6e62d77fac690575ae347571717f5" 10 | 11 | [[package]] 12 | name = "rustls-pemfile" 13 | version = "2.2.0" 14 | dependencies = [ 15 | "bencher", 16 | "rustls-pki-types", 17 | ] 18 | 19 | [[package]] 20 | name = "rustls-pki-types" 21 | version = "1.9.0" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" 24 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustls-pemfile" 3 | version = "2.2.0" 4 | edition = "2018" 5 | license = "Apache-2.0 OR ISC OR MIT" 6 | readme = "README.md" 7 | description = "Basic .pem file parser for keys and certificates" 8 | homepage = "https://github.com/rustls/pemfile" 9 | repository = "https://github.com/rustls/pemfile" 10 | categories = ["network-programming", "cryptography"] 11 | 12 | [dependencies] 13 | pki-types = { package = "rustls-pki-types", version = "1.9" } 14 | 15 | [dev-dependencies] 16 | bencher = "0.1.5" 17 | 18 | [features] 19 | default = ["std"] 20 | std = ["pki-types/std"] 21 | 22 | [[bench]] 23 | name = "benchmark" 24 | harness = false 25 | 26 | [[test]] 27 | name = "integration" 28 | required-features = ["std"] 29 | -------------------------------------------------------------------------------- /LICENSE-ISC: -------------------------------------------------------------------------------- 1 | ISC License (ISC) 2 | Copyright (c) 2016, Joseph Birr-Pixton 3 | 4 | Permission to use, copy, modify, and/or distribute this software for 5 | any purpose with or without fee is hereby granted, provided that the 6 | above copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 9 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 10 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 11 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 12 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 13 | PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 14 | ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 15 | THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /tests/data/spki.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqIh8FTj9DIgI8DAoCBh+ 3 | 6UXOfaWkvNaGZx2GwXl4WDAa/ZSE5/8ofg/6V59bmk9yry57UR4F+blscBvE4g3U 4 | dTvWJOBRD900l21vwpDLKzZguyGOCmKwJu3vCnAQKzBRXW5sDgvO67GeU6kpaic9 5 | LYPYnYaoxCRTYTZu0wy72rW5G0Fe8Gg/duJmUH7vqGIZupTTVzIBMbFVPBMJqprT 6 | MStDhaUL0JiAz0ZgTeNLRIBZWV9mY4PG3rZtbV0BZGR1ipAq9xfgqJcURCcKl/ZT 7 | UMtzvgk8s5hYkIJX0ZL3qsfdM4BMgIFhHq/GisQKbbu9kWldBrxQylOwa6r0m3Jv 8 | KJX2ViDSORndaCz2sppmVx5HDHnj+Bw381yawphnpumP3BJK4iof//uYKvfdc4RC 9 | y2EXL8PYPsT5DMB0jaBt92ytR5sLhn8Sl9Hk0buN4IjrYPISrdhS45xQXUqxcp9O 10 | 9hcU+rSaQyZ45cj+VlWhKq8MDvGvaAONBFSEh01mnUwoJObsAZNVFVtuOkwAli0F 11 | kGouMycQY1BGscpdC516Nya361Hk/ICyby2Y0BJrrVGaSM6poXH9yEjglzAdtSDb 12 | Cvhn/zlAI5ltm4Nv2qTgYBDns5JRGVhBym6RbbZ1C/KfCgn0hOxiw3N7AN4d0K5n 13 | LI6p7U9RnNVbWgbqsuoxBtkCAwEAAQ== 14 | -----END PUBLIC KEY----- 15 | -------------------------------------------------------------------------------- /tests/data/rsa1024.pkcs1.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXAIBAAKBgQC1Dt8tFmGS76ciuNXvk/QRrV8wCcArWxvl7Ku0aSQXgcFBAav6 3 | P5RD8b+dC9DihSu/r+6OOfjsAZ6oKCq3OTUfmoUhLpoBomxPczJgLyyLD+nQkp5q 4 | B1Q3WB6ACL/HJRRjJEIn7lc5u1FVBGbiCAHKMiaP4BDSym8oqimKC6uiaQIDAQAB 5 | AoGAGKmY7sxQqDIqwwkIYyT1Jv9FqwZ4/a7gYvZVATMdLnKHP3KZ2XGVoZepcRvt 6 | 7R0Us3ykcw0kgglKcj9eaizJtnSuoDPPwt53mDypPN2sU3hZgyk2tPgr49DB3MIp 7 | fjoqw4RL/p60ksgGXbDEqBuXqOtH5i61khWlMj+BWL9VDq0CQQDaELWPQGjgs+7X 8 | /QyWMJwOF4FXE4jecH/CcPVDB9K1ukllyC1HqTNe44Sp2bIDuSXXWb8yEixrEWBE 9 | ci2CSSjXAkEA1I4W9IzwEmAeLtL6VBip9ks52O0JKu373/Xv1F2GYdhnQaFw7IC6 10 | 1lSzcYMKGTmDuM8Cj26caldyv19Q0SPmvwJAdRHjZzS9GWWAJJTF3Rvbq/USix0B 11 | renXrRvXkFTy2n1YSjxdkstTuO2Mm2M0HquXlTWpX8hB8HkzpYtmwztjoQJAECKl 12 | LXVReCOhxu4vIJkqtc6qGoSL8J1WRH8X8KgU3nKeDAZkWx++jyyo3pIS/y01iZ71 13 | U8wSxaPTyyFCMk4mYwJBALjg7g8yDy1Lg9GFfOZvAVzPjqD28jZh/VJsDz9IhYoG 14 | z89iHWHkllOisbOm+SeynVC8CoFXmJPc26U65GcjI18= 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /tests/data/rsa1024.pkcs8.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALUO3y0WYZLvpyK4 3 | 1e+T9BGtXzAJwCtbG+Xsq7RpJBeBwUEBq/o/lEPxv50L0OKFK7+v7o45+OwBnqgo 4 | Krc5NR+ahSEumgGibE9zMmAvLIsP6dCSnmoHVDdYHoAIv8clFGMkQifuVzm7UVUE 5 | ZuIIAcoyJo/gENLKbyiqKYoLq6JpAgMBAAECgYAYqZjuzFCoMirDCQhjJPUm/0Wr 6 | Bnj9ruBi9lUBMx0ucoc/cpnZcZWhl6lxG+3tHRSzfKRzDSSCCUpyP15qLMm2dK6g 7 | M8/C3neYPKk83axTeFmDKTa0+Cvj0MHcwil+OirDhEv+nrSSyAZdsMSoG5eo60fm 8 | LrWSFaUyP4FYv1UOrQJBANoQtY9AaOCz7tf9DJYwnA4XgVcTiN5wf8Jw9UMH0rW6 9 | SWXILUepM17jhKnZsgO5JddZvzISLGsRYERyLYJJKNcCQQDUjhb0jPASYB4u0vpU 10 | GKn2SznY7Qkq7fvf9e/UXYZh2GdBoXDsgLrWVLNxgwoZOYO4zwKPbpxqV3K/X1DR 11 | I+a/AkB1EeNnNL0ZZYAklMXdG9ur9RKLHQGt6detG9eQVPLafVhKPF2Sy1O47Yyb 12 | YzQeq5eVNalfyEHweTOli2bDO2OhAkAQIqUtdVF4I6HG7i8gmSq1zqoahIvwnVZE 13 | fxfwqBTecp4MBmRbH76PLKjekhL/LTWJnvVTzBLFo9PLIUIyTiZjAkEAuODuDzIP 14 | LUuD0YV85m8BXM+OoPbyNmH9UmwPP0iFigbPz2IdYeSWU6Kxs6b5J7KdULwKgVeY 15 | k9zbpTrkZyMjXw== 16 | -----END PRIVATE KEY----- 17 | -------------------------------------------------------------------------------- /tests/data/crl.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN X509 CRL----- 2 | MIICiTBzAgEBMA0GCSqGSIb3DQEBCwUAMBoxGDAWBgNVBAMMD3Bvbnl0b3duIFJT 3 | QSBDQRcNMjMwNjI3MDgyODEyWhcNMjMwNzI3MDgyODEyWjAVMBMCAgHIFw0yMzA2 4 | MjcwODI3NTlaoA4wDDAKBgNVHRQEAwIBAjANBgkqhkiG9w0BAQsFAAOCAgEAP6EX 5 | 9+hxjx/AqdBpynZXjGkEqigBcLcJ2PADOXngdQI1jC0WuYnZymUimemeULtt8X+1 6 | ai2KxAuF1m4NEKZsrGKvO+/9s/X1xbGroyHSAMKtZafFopFpoB2aNbYlx7yIyLtD 7 | BBIZIF50g20U+3izqpHutTD10itdk9TLsSceJHpwTkNJtaWMkOfBV28nKzEzVutV 8 | f6WzRpURGzui6nQy7aIqImeanpoBoz323psMfC32U0uMBCZltyHNqsX58/2Uhucx 9 | 0IPnitNuhv4scCPf/jeRfGIWDrTf1/25LDzRxyg1S4z9aa+3GM4O3dqy4igZEhgT 10 | q3pjlJ2hUL5E0oqbZDIQD1SN8UUUv5N2AjwZcxVBNnYeGyuO7YpTBYiu62o73iL2 11 | CjgElfaMq/9hEr9GR9kJozh7VTxtQPbnr4DiucQvhv8o/A1z+zkC0gj8iCLFtDbO 12 | 8bvDowcdle9LKkrLaBe6sO+fSH/I9Wj8vrEJKsuwaEraIdEaq2VrIMUPEWN0/MH9 13 | vTwHyadGSMK4CWtrn9fCAgSLw6NX74D7Cx1IaS8vstMjpeUqOS0dk5ThiW47HceB 14 | DTko7rV5N+RGH2nW1ynLoZKCJQqqZcLilFMyKPui3jifJnQlMFi54jGVgg/D6UQn 15 | 7dA7wb2ux/1hSiaarp+mi7ncVOyByz6/WQP8mfc= 16 | -----END X509 CRL----- 17 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Joseph Birr-Pixton 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /tests/data/csr.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIC+zCCAeMCAQAwfDELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRQwEgYD 3 | VQQHDAtTYW4gQW50b25pbzEdMBsGA1UECgwURXhhbXBsZSBPcmdhbml6YXRpb24x 4 | EjAQBgNVBAsMCU1hcmtldGluZzEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0G 5 | CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwJw0BcbuqZyiABlmSYTi1tcr8DB0D 6 | NcTtzsYe7tlyIKd3mEs+u6Pi3rEQGvOw5eo6CmWII2qmVOqJ2f6gjl2lZJ5DUE6B 7 | I+NNE73zfFMrttUtI8X4ChnE4rrGqqUsSvYz1YVU0KiJ/00YMjEY5XlJYYa9FgfZ 8 | sUrhj4aCFdXS6CU9jueRr+udEBElDcgTS9+pB+LFhVfUMTdxnJ3BcT4ZDDqODH3/ 9 | 5RAgq03dhRpkkaVIg2uVKTBDoM3hs8T1zIxLM7hItaZzMv4uHdfI8y+BdHrePT33 10 | BoTlocvTEZEqqXEdw2kUd4PDgyUTjFE3b9OeLk0Ju5GRvuCW3UcS5gFvAgMBAAGg 11 | OjA4BgkqhkiG9w0BCQ4xKzApMCcGA1UdEQQgMB6CC2V4YW1wbGUuY29tgg9mb28u 12 | ZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBACWsgxPw13QUpoCJOqvp8B1A 13 | EfsxRJITSROmukV3ZQycPT76Y3GVrM9sGjO8p13J/CVw2KcWc9xmgHF0MdvPNhnW 14 | OB6Y07hVpNnJVHb1KglOkNkTy6sVDtnZHg2klqGSyzIbwZ9R3JG8HtRdkceIrm3D 15 | gdiZyLcf1VDCCUGaskEi2CsggCQQJNyGi+8BSQ8MPKm/m0KrSchGQ157eWCCjopz 16 | f5GQe2UGOg5T7g8+S4GdECMwkMlTGUwlAM6LuOG/NZqP528PCAYQv0eOYdSwALQT 17 | GwTyU4AZ9y1uBFuaFxABew9GbDEtNY/XHTF8308edUwGBk6jfD+UuTeEwRZGs9E= 18 | -----END CERTIFICATE REQUEST----- 19 | -------------------------------------------------------------------------------- /tests/data/whitespace-prefix.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDaTCCAlGgAwIBAgIJAOq/zL+84IswMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQIDAJOQzEMMAoGA1UEBwwDUlRQMQ8wDQYDVQQKDAZOZXRB 4 | cHAxDTALBgNVBAsMBEVTSVMxEDAOBgNVBAMMB1NTRk1DQ0EwHhcNMTcxMTAxMjEw 5 | OTQyWhcNMjcxMDMwMjEwOTQyWjBaMQswCQYDVQQGEwJVUzELMAkGA1UECAwCTkMx 6 | DDAKBgNVBAcMA1JUUDEPMA0GA1UECgwGTmV0QXBwMQ0wCwYDVQQLDARFU0lTMRAw 7 | DgYDVQQDDAdTU0ZNQ0NBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA 8 | iaD9Ee0Yrdka0I+9GTJBIW/Fp5JU6kyjaxfOldW/R9lEubegXQFhDD2Xi1HZ+fTM 9 | f224glB9xLJXAHhipRK01C2MgC4kSH75WL1iAiYeOBloExqmK6OCX+sdyO7RXm/H 10 | Ra9tN2INWdvyO2pnmxsSnq56mCMsUZLtrRKp89FWgcxLg5r8QxH7xwfh5k54rxjE 11 | 144TD9yrIiQOgRSIRHUrVJ9l/F/gnwzP8wcNABeXwN71Mzl7mliPA703kONQIAyU 12 | 0E0tLpmy/U8dZdMmTBZGB7jI9f95Hl1RunfwhR371a6z38kgkvwrLzl4qflfsPjw 13 | K9n4omNk9rCH9H9tWkxxjwIDAQABozIwMDAdBgNVHQ4EFgQU/bFyCCnqdDFKlQBJ 14 | ExtV6wcMYkEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAOQMs 15 | Pz2iBD1+3RcSOsahB36WAwPCjgPiXXXpU+Zri11+m6I0Lq+OWtf+YgaQ8ylLmCQd 16 | 0p1wHlYA4qo896SycrhTQfy9GlS/aQqN192k3oBGoJcMIUnGUBGuEvyZ2aDUfkzy 17 | JUqBe+0KaT7pkvvbRL7VUz34I7ouq9fQIRZ26vUDLTY3KM1n/DXBj3e30GHGMV3K 18 | NN2twuLXPNjnryfgpliHU1rwV7r1WvrCVn4StjimP2bO5HGqD/SbiYUL2M9LOuLK 19 | 6mqY4OHumYXq3k7CHrvt0FepsN0L14LYEt1LvpPDFWP3SdN4z4KqT9AGqBaJnhhl 20 | Qiq8GWnAChspdBLxCg== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /tests/data/certificate.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEnzCCAoegAwIBAgIBezANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9wb255 3 | dG93biBSU0EgQ0EwHhcNMTkwNjA5MTcxNTEyWhcNMjkwNjA2MTcxNTEyWjAsMSow 4 | KAYDVQQDDCFwb255dG93biBSU0EgbGV2ZWwgMiBpbnRlcm1lZGlhdGUwggGiMA0G 5 | CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCj/tOFeSW3WB+TtuLCR1L/84lZytFw 6 | zbpzOTGB1kPEKNbrMsv3lHXm5bHa8Bl3k113k7Hi7OAt/nkMm05s8LcUoovhaG5C 7 | G7tjzL+ld1nO74gNS3IQHCzxRdRwIgaDZHyICfBQBfB9/m+9z3yRtOKWJl6i/MT9 8 | HRN6yADW/8gHFlMzRkCKBjIKXehKsu8cbtB+5MukwtXI4rKf9aYXZQOEUn1kEwQJ 9 | ZIKBXR0eyloQiZervUE7meRCTBvzXT9VoSEX49/mempp4hnfdHlRNzre4/tphBf1 10 | fRUdpVXZ3DvmzoHdXRVzxx3X5LvDpf7Eb3ViGkXDFwkSfHEhkRnAl4lIzTH/1F25 11 | stmT8a0PA/lCNMrzJBzkLcuem1G1uMHoQZo1f3OpslJ8gHbE9ZlIbIKmpmJS9oop 12 | Vh1BH+aOy5doCrF8uOLTQ3d5CqA/EZMGahDHy7IkeNYmG/RXUKNltv+r95gwuRP+ 13 | 9UIJ9FTa4REQbIpGWP5XibI6x4LqLTJj+VsCAwEAAaNeMFwwHQYDVR0OBBYEFEKP 14 | y8hHZVazpvIsxFcGo4YrkEkwMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEF 15 | BQcDAjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB/jANBgkqhkiG9w0BAQsFAAOC 16 | AgEAMzTRDLBExVFlw98AuX+pM+/R2Gjw5KFHvSYLKLbMRfuuZK1yNYYaYtNrtF+V 17 | a53OFgaZj56o7tXc2PB8kw4MELD0ViR8Do2bvZieFcEe4DwhdjGCjuLehVLT29qI 18 | 7T3N/JkJ5daemKZcRB6Ne0F4+6QlVVNck28HUKbQThl88RdwLUImmSAfgKSt6uJ5 19 | wlH7wiYQR2vPXwSuEYzwot+L/91eBwuQr4Lovx9+TCKTbwQOKYjX4KfcOOQ1rx0M 20 | IMrvwWqnabc6m1F0O6//ibL0kuFkJYEgOH2uJA12FBHO+/q2tcytejkOWKWMJj6Y 21 | 2etwIHcpzXaEP7fZ75cFGqcE3s7XGsweBIPLjMP1bKxEcFKzygURm/auUuXBCFBl 22 | E16PB6JEAeCKe/8VFeyucvjPuQDWB49aq+r2SbpbI4IeZdz/QgEIOb0MpwStrvhH 23 | 9f/DtGMbjvuAEkRoOorK4m5k4GY3LsWTR2bey27AXk8N7pKarpu2N7ChBPm+EV0Y 24 | H+tAI/OfdZuNUCES00F5UAFdU8zBUZo19ao2ZqfEADimE7Epk2s0bUe4GSqEXJp6 25 | 68oVSMhZmMf/RCSNlr97f34sNiUA1YJ0JbCRZmw8KWNm9H1PARLbrgeRBZ/k31Li 26 | WLDr3fiEVk7SGxj3zo94cS6AT55DyXLiSD/bFmL1QXgZweA= 27 | -----END CERTIFICATE----- 28 | -------------------------------------------------------------------------------- /tests/data/certificate.chain.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBuDCCAWqgAwIBAgICAcgwBQYDK2VwMC4xLDAqBgNVBAMMI3Bvbnl0b3duIEVk 3 | RFNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMB4XDTE5MDgxNjEzMjg1MVoXDTI1MDIw 4 | NTEzMjg1MVowGTEXMBUGA1UEAwwOdGVzdHNlcnZlci5jb20wKjAFBgMrZXADIQAQ 5 | 9M4hrE+Ucw4QUmaKOeKfphklBJi1qsqtX4u+knbseqOBwDCBvTAMBgNVHRMBAf8E 6 | AjAAMAsGA1UdDwQEAwIGwDAdBgNVHQ4EFgQUa/gnV4+a22BUKTouAYX6nfLnPKYw 7 | RAYDVR0jBD0wO4AUFxIwU406tG3CsPWkHWqfuUT48auhIKQeMBwxGjAYBgNVBAMM 8 | EXBvbnl0b3duIEVkRFNBIENBggF7MDsGA1UdEQQ0MDKCDnRlc3RzZXJ2ZXIuY29t 9 | ghVzZWNvbmQudGVzdHNlcnZlci5jb22CCWxvY2FsaG9zdDAFBgMrZXADQQApDiBQ 10 | ns3fuvsWuFpIS+osj2B/gQ0b6eBAZ1UBxRyDlAo5++JZ0PtaEROyGo2t2gqi2Lyz 11 | 47mLyGCvqgVbC6cH 12 | -----END CERTIFICATE----- 13 | -----BEGIN CERTIFICATE----- 14 | MIIBVzCCAQmgAwIBAgIBezAFBgMrZXAwHDEaMBgGA1UEAwwRcG9ueXRvd24gRWRE 15 | U0EgQ0EwHhcNMTkwODE2MTMyODUxWhcNMjkwODEzMTMyODUxWjAuMSwwKgYDVQQD 16 | DCNwb255dG93biBFZERTQSBsZXZlbCAyIGludGVybWVkaWF0ZTAqMAUGAytlcAMh 17 | AD4h3t0UCoMDGgIq4UW4P5zDngsY4vy1pE3wzLPFI4Vdo14wXDAdBgNVHQ4EFgQU 18 | FxIwU406tG3CsPWkHWqfuUT48aswIAYDVR0lAQH/BBYwFAYIKwYBBQUHAwEGCCsG 19 | AQUFBwMCMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgH+MAUGAytlcANBAAZFvMek 20 | Z71I8CXsBmx/0E6Weoaan9mJHgKqgQdK4w4h4dRg6DjNG957IbrLFO3vZduBMnna 21 | qHP3xTFF+11Eyg8= 22 | -----END CERTIFICATE----- 23 | -----BEGIN CERTIFICATE----- 24 | MIIBTDCB/6ADAgECAhRXcvbYynz4+usVvPtJp++sBUih3TAFBgMrZXAwHDEaMBgG 25 | A1UEAwwRcG9ueXRvd24gRWREU0EgQ0EwHhcNMTkwODE2MTMyODUwWhcNMjkwODEz 26 | MTMyODUwWjAcMRowGAYDVQQDDBFwb255dG93biBFZERTQSBDQTAqMAUGAytlcAMh 27 | AIE4tLweIfcBGfhPqyXFp5pjVxjaiKk+9fTbRy46jAFKo1MwUTAdBgNVHQ4EFgQU 28 | z5b9HjkOxffbtCZhWGg+bnxuD6wwHwYDVR0jBBgwFoAUz5b9HjkOxffbtCZhWGg+ 29 | bnxuD6wwDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQBNlt7z4bZ7KhzecxZEe3i5 30 | lH9MRqbpP9Rg4HyzAJfTzFGT183HoJiISdPLbxwMn0KaqSGlVe+9GgNKswoaRAwH 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: rustls-pemfile 2 | 3 | on: 4 | push: 5 | pull_request: 6 | merge_group: 7 | schedule: 8 | - cron: '0 18 * * *' 9 | 10 | jobs: 11 | rustfmt: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: dtolnay/rust-toolchain@stable 15 | with: 16 | components: rustfmt 17 | - uses: actions/checkout@v5 18 | with: 19 | persist-credentials: false 20 | - run: cargo fmt --all -- --check 21 | clippy: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: dtolnay/rust-toolchain@stable 25 | with: 26 | components: clippy 27 | - uses: actions/checkout@v5 28 | with: 29 | persist-credentials: false 30 | - run: cargo clippy --locked --all-features --all-targets 31 | rustdoc: 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: dtolnay/rust-toolchain@stable 35 | - uses: actions/checkout@v5 36 | with: 37 | persist-credentials: false 38 | - run: cargo doc --locked --all-features 39 | build: 40 | name: "Build and test" 41 | runs-on: ${{ matrix.os }} 42 | strategy: 43 | matrix: 44 | # test a bunch of toolchains on ubuntu 45 | rust: 46 | - stable 47 | - beta 48 | - nightly 49 | os: [ubuntu-latest] 50 | # but only stable on macos/windows (slower platforms) 51 | include: 52 | - os: macos-latest 53 | rust: stable 54 | - os: windows-latest 55 | rust: stable 56 | steps: 57 | - name: Checkout sources 58 | uses: actions/checkout@v5 59 | with: 60 | persist-credentials: false 61 | 62 | - name: Install ${{ matrix.rust }} toolchain 63 | uses: dtolnay/rust-toolchain@master 64 | with: 65 | toolchain: ${{ matrix.rust }} 66 | targets: x86_64-unknown-none 67 | 68 | - name: cargo test (debug) 69 | run: cargo test --locked 70 | env: 71 | RUST_BACKTRACE: 1 72 | RUSTFLAGS: "-D warnings" 73 | 74 | - name: cargo test (release) 75 | run: cargo test --locked --release 76 | env: 77 | RUSTFLAGS: "-D warnings" 78 | 79 | # this target does _not_ include the libstd crate in its sysroot 80 | # it will catch unwanted usage of libstd in _dependencies_ 81 | - name: cargo build no-std mode 82 | run: cargo build --locked --no-default-features --target x86_64-unknown-none 83 | env: 84 | RUSTFLAGS: "-D warnings" 85 | 86 | semver: 87 | name: Check semver compatibility 88 | runs-on: ubuntu-latest 89 | steps: 90 | - name: Checkout sources 91 | uses: actions/checkout@v5 92 | with: 93 | persist-credentials: false 94 | 95 | - name: Check semver 96 | uses: obi1kenobi/cargo-semver-checks-action@v2 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rustls-pemfile 2 | This is a basic parser for PEM-encodings commonly used for storing keys and certificates at rest. 3 | 4 | It doesn't support reading encrypted keys: the cryptography standardised for this is typically very 5 | poor and doing so doesn't address a meaningful threat model. 6 | 7 | [![Build Status](https://github.com/rustls/pemfile/workflows/rustls-pemfile/badge.svg)](https://github.com/rustls/pemfile/actions) 8 | [![Crate](https://img.shields.io/crates/v/rustls-pemfile.svg)](https://crates.io/crates/rustls-pemfile) 9 | [![Documentation](https://docs.rs/rustls-pemfile/badge.svg)](https://docs.rs/rustls-pemfile/) 10 | 11 | # See also: rustls-pki-types 12 | 13 | The main function of this crate has been incorporated into 14 | [rustls-pki-types](https://crates.io/crates/rustls-pki-types). 2.2.0 maintains the 15 | existing public API for this crate, on top of this new implementation. This drops 16 | the dependency on the `base64` crate, and allows for constant-time decoding of private keys. 17 | 18 | This crate will continue to exist in its current form, but it is somewhat unlikely that the 19 | API will be extended from its current state. 20 | 21 | Should you wish to migrate to using the new [`rustls-pki-types` PEM APIs](https://docs.rs/rustls-pki-types/latest/rustls_pki_types/pem/trait.PemObject.html) 22 | directly, here is a rough cheat-sheet: 23 | 24 | | *Use case* | *Replace* | 25 | |---|---| 26 | | File stream to `CertificateDer` iterator |`rustls_pemfile::certs(io::BufRead)`
➡️
`CertificateDer::pem_reader_iter(io::Read)` | 27 | | File stream to one `PrivateKeyDer` | `rustls_pemfile::private_key(io::BufRead)`
➡️
`PrivateKeyDer::from_pem_reader(io::Read)` | 28 | | File stream to one `CertificateSigningRequestDer` | `rustls_pemfile::csr(io::BufRead)`
➡️
`CertificateSigningRequestDer::from_pem_reader(io::Read)` | 29 | | File stream to `CertificateRevocationListDer` iterator |`rustls_pemfile::crls(io::BufRead)`
➡️
`CertificateRevocationListDer::pem_reader_iter(io::Read)` | 30 | | File stream to `PrivatePkcs1KeyDer` iterator |`rustls_pemfile::rsa_private_keys(io::BufRead)`
➡️
`PrivatePkcs1KeyDer::pem_reader_iter(io::Read)` | 31 | | File stream to `PrivatePkcs8KeyDer` iterator |`rustls_pemfile::pkcs8_private_keys(io::BufRead)`
➡️
`PrivatePkcs8KeyDer::pem_reader_iter(io::Read)` | 32 | | File stream to `PrivateSec1KeyDer` iterator |`rustls_pemfile::ec_private_keys(io::BufRead)`
➡️
`PrivateSec1KeyDer::pem_reader_iter(io::Read)` | 33 | | File stream to `SubjectPublicKeyInfoDer` iterator |`rustls_pemfile::public_keys(io::BufRead)`
➡️
`SubjectPublicKeyInfoDer::pem_reader_iter(io::Read)` | 34 | 35 | # Changelog 36 | 37 | The detailed list of changes in each release can be found at 38 | https://github.com/rustls/pemfile/releases. 39 | 40 | # License 41 | rustls-pemfile is distributed under the following three licenses: 42 | 43 | - Apache License version 2.0. 44 | - MIT license. 45 | - ISC license. 46 | 47 | These are included as LICENSE-APACHE, LICENSE-MIT and LICENSE-ISC 48 | respectively. You may use this software under the terms of any 49 | of these licenses, at your option. 50 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod unit { 3 | use alloc::{format, vec}; 4 | use std::prelude::v1::*; 5 | 6 | use crate::{Error, Item}; 7 | 8 | #[test] 9 | fn skips_leading_junk() { 10 | assert_eq!( 11 | check_both( 12 | b"junk\n\ 13 | -----BEGIN RSA PRIVATE KEY-----\n\ 14 | qw\n\ 15 | -----END RSA PRIVATE KEY-----\n" 16 | ), 17 | vec![Item::Pkcs1Key(vec![0xab].into())] 18 | ); 19 | } 20 | 21 | #[test] 22 | fn skips_trailing_junk() { 23 | assert_eq!( 24 | check_both( 25 | b"-----BEGIN RSA PRIVATE KEY-----\n\ 26 | qw\n\ 27 | -----END RSA PRIVATE KEY-----\n\ 28 | junk" 29 | ), 30 | vec![Item::Pkcs1Key(vec![0xab].into())] 31 | ); 32 | } 33 | 34 | #[test] 35 | fn skips_non_utf8_junk() { 36 | assert_eq!( 37 | check_both( 38 | b"\x00\x00\n\ 39 | -----BEGIN RSA PRIVATE KEY-----\n\ 40 | qw\n\ 41 | -----END RSA PRIVATE KEY-----\n 42 | \x00\x00" 43 | ), 44 | vec![Item::Pkcs1Key(vec![0xab].into())] 45 | ); 46 | } 47 | 48 | #[test] 49 | fn rejects_invalid_base64() { 50 | let input = b"-----BEGIN RSA PRIVATE KEY-----\n\ 51 | q=w\n\ 52 | -----END RSA PRIVATE KEY-----\n"; 53 | assert_eq!( 54 | format!("{:?}", check_io(input)), 55 | "Err(Custom { kind: InvalidData, error: \"InvalidTrailingPadding\" })" 56 | ); 57 | assert!(matches!(check_slice(input), Err(Error::Base64Decode(_)))); 58 | } 59 | 60 | #[test] 61 | fn rejects_unclosed_start_section() { 62 | let input = b"-----BEGIN RSA PRIVATE KEY-----\n\ 63 | qw\n"; 64 | assert_eq!( 65 | format!("{:?}", 66 | check_io(input)), 67 | "Err(Custom { kind: InvalidData, error: \"section end \\\"-----END RSA PRIVATE KEY-----\\\" missing\" })" 68 | ); 69 | assert_eq!( 70 | check_slice(input), 71 | Err(Error::MissingSectionEnd { 72 | end_marker: b"-----END RSA PRIVATE KEY-----".to_vec() 73 | }) 74 | ) 75 | } 76 | 77 | #[test] 78 | fn rejects_bad_start() { 79 | let input = b"-----BEGIN RSA PRIVATE KEY----\n\ 80 | qw\n\ 81 | -----END RSA PRIVATE KEY-----\n"; 82 | assert_eq!( 83 | format!("{:?}", 84 | check_io(input)), 85 | "Err(Custom { kind: InvalidData, error: \"illegal section start: \\\"-----BEGIN RSA PRIVATE KEY----\\\\n\\\"\" })" 86 | ); 87 | assert_eq!( 88 | check_slice(input), 89 | Err(Error::IllegalSectionStart { 90 | line: b"-----BEGIN RSA PRIVATE KEY----".to_vec() 91 | }) 92 | ) 93 | } 94 | 95 | #[test] 96 | fn skips_unrecognised_section() { 97 | assert_eq!( 98 | check_both( 99 | b"junk\n\ 100 | -----BEGIN BREAKFAST CLUB-----\n\ 101 | qw\n\ 102 | -----END BREAKFAST CLUB-----\n" 103 | ), 104 | vec![] 105 | ); 106 | } 107 | 108 | fn check_both(data: &[u8]) -> Vec { 109 | let mut reader = std::io::BufReader::new(data); 110 | let io_outcome = crate::read_all(&mut reader) 111 | .collect::, _>>() 112 | .unwrap(); 113 | let slice_outcome = check_slice(data).unwrap(); 114 | 115 | assert_eq!(io_outcome, slice_outcome); 116 | 117 | io_outcome 118 | } 119 | 120 | fn check_slice(mut data: &[u8]) -> Result, Error> { 121 | let mut items = vec![]; 122 | while let Some((item, rest)) = crate::read_one_from_slice(data)? { 123 | items.push(item); 124 | data = rest; 125 | } 126 | 127 | Ok(items) 128 | } 129 | 130 | fn check_io(data: &[u8]) -> Result, std::io::Error> { 131 | let mut reader = std::io::BufReader::new(data); 132 | crate::read_all(&mut reader).collect() 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /tests/data/mixed-line-endings.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF 3 | ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 4 | b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL 5 | MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv 6 | b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj 7 | ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM 8 | 9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw 9 | IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 10 | VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L 11 | 93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm 12 | jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC 13 | AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA 14 | A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI 15 | U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs 16 | N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv 17 | o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU 18 | 5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy 19 | rqXRfboQnoZsG4q5WTP468SQvvG5 20 | -----END CERTIFICATE----- 21 | -----BEGIN CERTIFICATE----- MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM 9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L 93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU 5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy rqXRfboQnoZsG4q5WTP468SQvvG5 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- 22 | MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF 23 | ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 24 | b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL 25 | MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv 26 | b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj 27 | ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM 28 | 9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw 29 | IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 30 | VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L 31 | 93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm 32 | jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC 33 | AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA 34 | A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI 35 | U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs 36 | N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv 37 | o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU 38 | 5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy 39 | rqXRfboQnoZsG4q5WTP468SQvvG5 40 | -----END CERTIFICATE----- 41 | -----BEGIN CERTIFICATE----- MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM 9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L 93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU 5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy rqXRfboQnoZsG4q5WTP468SQvvG5 -----END CERTIFICATE----- -------------------------------------------------------------------------------- /src/pemfile.rs: -------------------------------------------------------------------------------- 1 | use alloc::format; 2 | use alloc::string::String; 3 | use alloc::vec::Vec; 4 | #[cfg(feature = "std")] 5 | use core::iter; 6 | #[cfg(feature = "std")] 7 | use std::io::{self, ErrorKind}; 8 | 9 | use pki_types::{ 10 | pem, CertificateDer, CertificateRevocationListDer, CertificateSigningRequestDer, 11 | PrivatePkcs1KeyDer, PrivatePkcs8KeyDer, PrivateSec1KeyDer, SubjectPublicKeyInfoDer, 12 | }; 13 | 14 | /// The contents of a single recognised block in a PEM file. 15 | #[non_exhaustive] 16 | #[derive(Debug, PartialEq)] 17 | pub enum Item { 18 | /// A DER-encoded x509 certificate. 19 | /// 20 | /// Appears as "CERTIFICATE" in PEM files. 21 | X509Certificate(CertificateDer<'static>), 22 | 23 | /// A DER-encoded Subject Public Key Info; as specified in RFC 7468. 24 | /// 25 | /// Appears as "PUBLIC KEY" in PEM files. 26 | SubjectPublicKeyInfo(SubjectPublicKeyInfoDer<'static>), 27 | 28 | /// A DER-encoded plaintext RSA private key; as specified in PKCS #1/RFC 3447 29 | /// 30 | /// Appears as "RSA PRIVATE KEY" in PEM files. 31 | Pkcs1Key(PrivatePkcs1KeyDer<'static>), 32 | 33 | /// A DER-encoded plaintext private key; as specified in PKCS #8/RFC 5958 34 | /// 35 | /// Appears as "PRIVATE KEY" in PEM files. 36 | Pkcs8Key(PrivatePkcs8KeyDer<'static>), 37 | 38 | /// A Sec1-encoded plaintext private key; as specified in RFC 5915 39 | /// 40 | /// Appears as "EC PRIVATE KEY" in PEM files. 41 | Sec1Key(PrivateSec1KeyDer<'static>), 42 | 43 | /// A Certificate Revocation List; as specified in RFC 5280 44 | /// 45 | /// Appears as "X509 CRL" in PEM files. 46 | Crl(CertificateRevocationListDer<'static>), 47 | 48 | /// A Certificate Signing Request; as specified in RFC 2986 49 | /// 50 | /// Appears as "CERTIFICATE REQUEST" in PEM files. 51 | Csr(CertificateSigningRequestDer<'static>), 52 | } 53 | 54 | impl Item { 55 | #[cfg(feature = "std")] 56 | fn from_buf(rd: &mut dyn io::BufRead) -> Result, pem::Error> { 57 | loop { 58 | match pem::from_buf(rd)? { 59 | Some((kind, data)) => match Self::from_kind(kind, data) { 60 | Some(item) => return Ok(Some(item)), 61 | None => continue, 62 | }, 63 | 64 | None => return Ok(None), 65 | } 66 | } 67 | } 68 | 69 | fn from_slice(pem: &[u8]) -> Result, pem::Error> { 70 | let mut iter = <(pem::SectionKind, Vec) as pem::PemObject>::pem_slice_iter(pem); 71 | 72 | for found in iter.by_ref() { 73 | match found { 74 | Ok((kind, data)) => match Self::from_kind(kind, data) { 75 | Some(item) => return Ok(Some((item, iter.remainder()))), 76 | None => continue, 77 | }, 78 | Err(err) => return Err(err), 79 | } 80 | } 81 | 82 | Ok(None) 83 | } 84 | 85 | fn from_kind(kind: pem::SectionKind, data: Vec) -> Option { 86 | use pem::SectionKind::*; 87 | match kind { 88 | Certificate => Some(Self::X509Certificate(data.into())), 89 | PublicKey => Some(Self::SubjectPublicKeyInfo(data.into())), 90 | RsaPrivateKey => Some(Self::Pkcs1Key(data.into())), 91 | PrivateKey => Some(Self::Pkcs8Key(data.into())), 92 | EcPrivateKey => Some(Self::Sec1Key(data.into())), 93 | Crl => Some(Self::Crl(data.into())), 94 | Csr => Some(Self::Csr(data.into())), 95 | _ => None, 96 | } 97 | } 98 | } 99 | 100 | /// Errors that may arise when parsing the contents of a PEM file 101 | /// 102 | /// This differs from [`rustls_pki_types::pem::Error`] because it is `PartialEq`; 103 | /// it is retained for compatibility. 104 | #[derive(Debug, PartialEq)] 105 | pub enum Error { 106 | /// a section is missing its "END marker" line 107 | MissingSectionEnd { 108 | /// the expected "END marker" line that was not found 109 | end_marker: Vec, 110 | }, 111 | 112 | /// syntax error found in the line that starts a new section 113 | IllegalSectionStart { 114 | /// line that contains the syntax error 115 | line: Vec, 116 | }, 117 | 118 | /// base64 decode error 119 | Base64Decode(String), 120 | } 121 | 122 | #[cfg(feature = "std")] 123 | impl From for io::Error { 124 | fn from(error: Error) -> Self { 125 | match error { 126 | Error::MissingSectionEnd { end_marker } => io::Error::new( 127 | ErrorKind::InvalidData, 128 | format!( 129 | "section end {:?} missing", 130 | String::from_utf8_lossy(&end_marker) 131 | ), 132 | ), 133 | 134 | Error::IllegalSectionStart { line } => io::Error::new( 135 | ErrorKind::InvalidData, 136 | format!( 137 | "illegal section start: {:?}", 138 | String::from_utf8_lossy(&line) 139 | ), 140 | ), 141 | 142 | Error::Base64Decode(err) => io::Error::new(ErrorKind::InvalidData, err), 143 | } 144 | } 145 | } 146 | 147 | impl From for Error { 148 | fn from(error: pem::Error) -> Self { 149 | match error { 150 | pem::Error::MissingSectionEnd { end_marker } => Error::MissingSectionEnd { end_marker }, 151 | pem::Error::IllegalSectionStart { line } => Error::IllegalSectionStart { line }, 152 | pem::Error::Base64Decode(str) => Error::Base64Decode(str), 153 | 154 | // this is a necessary bodge to funnel any new errors into our existing type 155 | // (to which we can add no new variants) 156 | other => Error::Base64Decode(format!("{other:?}")), 157 | } 158 | } 159 | } 160 | 161 | /// Extract and decode the next PEM section from `input` 162 | /// 163 | /// - `Ok(None)` is returned if there is no PEM section to read from `input` 164 | /// - Syntax errors and decoding errors produce a `Err(...)` 165 | /// - Otherwise each decoded section is returned with a `Ok(Some((Item::..., remainder)))` where 166 | /// `remainder` is the part of the `input` that follows the returned section 167 | pub fn read_one_from_slice(input: &[u8]) -> Result, Error> { 168 | Item::from_slice(input).map_err(Into::into) 169 | } 170 | 171 | /// Extract and decode the next PEM section from `rd`. 172 | /// 173 | /// - Ok(None) is returned if there is no PEM section read from `rd`. 174 | /// - Underlying IO errors produce a `Err(...)` 175 | /// - Otherwise each decoded section is returned with a `Ok(Some(Item::...))` 176 | /// 177 | /// You can use this function to build an iterator, for example: 178 | /// `for item in iter::from_fn(|| read_one(rd).transpose()) { ... }` 179 | #[cfg(feature = "std")] 180 | pub fn read_one(rd: &mut dyn io::BufRead) -> Result, io::Error> { 181 | Item::from_buf(rd).map_err(|err| match err { 182 | pem::Error::Io(io) => io, 183 | other => Error::from(other).into(), 184 | }) 185 | } 186 | 187 | /// Extract and return all PEM sections by reading `rd`. 188 | #[cfg(feature = "std")] 189 | pub fn read_all(rd: &mut dyn io::BufRead) -> impl Iterator> + '_ { 190 | iter::from_fn(move || read_one(rd).transpose()) 191 | } 192 | -------------------------------------------------------------------------------- /tests/integration.rs: -------------------------------------------------------------------------------- 1 | use std::io::BufReader; 2 | use std::iter; 3 | 4 | #[test] 5 | fn test_rsa_private_keys() { 6 | let data = include_bytes!("data/zen2.pem"); 7 | let mut reader = BufReader::new(&data[..]); 8 | 9 | assert_eq!( 10 | rustls_pemfile::rsa_private_keys(&mut reader) 11 | .collect::, _>>() 12 | .unwrap() 13 | .len(), 14 | 2 15 | ); 16 | } 17 | 18 | #[test] 19 | fn private_key() { 20 | let data = include_bytes!("data/zen2.pem"); 21 | let mut reader = BufReader::new(&data[..]); 22 | rustls_pemfile::private_key(&mut reader).unwrap().unwrap(); 23 | 24 | let data = include_bytes!("data/certificate.chain.pem"); 25 | let mut reader = BufReader::new(&data[..]); 26 | assert!(rustls_pemfile::private_key(&mut reader).unwrap().is_none()); 27 | } 28 | 29 | #[test] 30 | fn public_keys() { 31 | let data = include_bytes!("data/spki.pem"); 32 | let mut reader = BufReader::new(&data[..]); 33 | assert_eq!( 34 | rustls_pemfile::public_keys(&mut reader) 35 | .collect::, _>>() 36 | .unwrap() 37 | .len(), 38 | 1 39 | ); 40 | 41 | let data = include_bytes!("data/zen2.pem"); 42 | let mut reader = BufReader::new(&data[..]); 43 | assert_eq!( 44 | rustls_pemfile::public_keys(&mut reader) 45 | .collect::, _>>() 46 | .unwrap() 47 | .len(), 48 | 2 49 | ); 50 | 51 | let data = include_bytes!("data/certificate.chain.pem"); 52 | let mut reader = BufReader::new(&data[..]); 53 | assert_eq!( 54 | rustls_pemfile::public_keys(&mut reader) 55 | .collect::, _>>() 56 | .unwrap() 57 | .len(), 58 | 0 59 | ); 60 | } 61 | 62 | #[test] 63 | fn test_csr() { 64 | let data = include_bytes!("data/csr.pem"); 65 | let mut reader = BufReader::new(&data[..]); 66 | rustls_pemfile::csr(&mut reader).unwrap().unwrap(); 67 | 68 | let data = include_bytes!("data/certificate.chain.pem"); 69 | let mut reader = BufReader::new(&data[..]); 70 | assert!(rustls_pemfile::private_key(&mut reader).unwrap().is_none()); 71 | } 72 | 73 | #[test] 74 | fn test_certs() { 75 | let data = include_bytes!("data/certificate.chain.pem"); 76 | let mut reader = BufReader::new(&data[..]); 77 | 78 | assert_eq!( 79 | rustls_pemfile::certs(&mut reader) 80 | .collect::, _>>() 81 | .unwrap() 82 | .len(), 83 | 3 84 | ); 85 | } 86 | 87 | #[test] 88 | fn test_certs_with_binary() { 89 | let data = include_bytes!("data/gunk.pem"); 90 | let mut reader = BufReader::new(&data[..]); 91 | assert_eq!( 92 | rustls_pemfile::certs(&mut reader) 93 | .collect::, _>>() 94 | .unwrap() 95 | .len(), 96 | 2 97 | ); 98 | } 99 | 100 | #[test] 101 | fn test_crls() { 102 | let data = include_bytes!("data/crl.pem"); 103 | let mut reader = BufReader::new(&data[..]); 104 | assert_eq!( 105 | rustls_pemfile::crls(&mut reader) 106 | .collect::, _>>() 107 | .unwrap() 108 | .len(), 109 | 1 110 | ); 111 | } 112 | 113 | #[test] 114 | fn test_pkcs8() { 115 | let data = include_bytes!("data/zen.pem"); 116 | let mut reader = BufReader::new(&data[..]); 117 | 118 | assert_eq!( 119 | rustls_pemfile::pkcs8_private_keys(&mut reader) 120 | .collect::, _>>() 121 | .unwrap() 122 | .len(), 123 | 2 124 | ); 125 | } 126 | 127 | #[test] 128 | fn test_sec1() { 129 | let data = include_bytes!("data/nistp256key.pem"); 130 | let mut reader = BufReader::new(&data[..]); 131 | 132 | let items = rustls_pemfile::read_all(&mut reader) 133 | .collect::, _>>() 134 | .unwrap(); 135 | assert_eq!(items.len(), 1); 136 | assert!(matches!(items[0], rustls_pemfile::Item::Sec1Key(_))); 137 | } 138 | 139 | #[test] 140 | fn smoketest_iterate() { 141 | let data = include_bytes!("data/zen2.pem"); 142 | let mut reader = BufReader::new(&data[..]); 143 | 144 | let mut count = 0; 145 | 146 | for item in iter::from_fn(|| rustls_pemfile::read_one(&mut reader).transpose()) { 147 | println!("item {:?}", item); 148 | count += 1; 149 | } 150 | 151 | assert_eq!(count, 18); 152 | } 153 | 154 | #[test] 155 | fn test_sec1_vs_pkcs8() { 156 | { 157 | let data = include_bytes!("data/nistp256key.pem"); 158 | let mut reader = BufReader::new(&data[..]); 159 | 160 | let items = rustls_pemfile::read_all(&mut reader) 161 | .collect::, _>>() 162 | .unwrap(); 163 | assert!(matches!(items[0], rustls_pemfile::Item::Sec1Key(_))); 164 | println!("sec1 {:?}", items); 165 | } 166 | { 167 | let data = include_bytes!("data/nistp256key.pkcs8.pem"); 168 | let mut reader = BufReader::new(&data[..]); 169 | 170 | let items = rustls_pemfile::read_all(&mut reader) 171 | .collect::, _>>() 172 | .unwrap(); 173 | assert!(matches!(items[0], rustls_pemfile::Item::Pkcs8Key(_))); 174 | println!("p8 {:?}", items); 175 | } 176 | } 177 | 178 | #[test] 179 | fn parse_in_order() { 180 | let data = include_bytes!("data/zen.pem"); 181 | let mut reader = BufReader::new(&data[..]); 182 | 183 | let items = rustls_pemfile::read_all(&mut reader) 184 | .collect::, _>>() 185 | .unwrap(); 186 | assert_eq!(items.len(), 10); 187 | assert!(matches!(items[0], rustls_pemfile::Item::X509Certificate(_))); 188 | assert!(matches!(items[1], rustls_pemfile::Item::X509Certificate(_))); 189 | assert!(matches!(items[2], rustls_pemfile::Item::X509Certificate(_))); 190 | assert!(matches!(items[3], rustls_pemfile::Item::X509Certificate(_))); 191 | assert!(matches!(items[4], rustls_pemfile::Item::Sec1Key(_))); 192 | assert!(matches!(items[5], rustls_pemfile::Item::Pkcs8Key(_))); 193 | assert!(matches!(items[6], rustls_pemfile::Item::Pkcs1Key(_))); 194 | assert!(matches!(items[7], rustls_pemfile::Item::Pkcs8Key(_))); 195 | assert!(matches!(items[8], rustls_pemfile::Item::Crl(_))); 196 | assert!(matches!(items[9], rustls_pemfile::Item::Csr(_))); 197 | } 198 | 199 | #[test] 200 | fn different_line_endings() { 201 | let data = include_bytes!("data/mixed-line-endings.crt"); 202 | 203 | // Ensure non-LF line endings are not lost by mistake, causing the test 204 | // to silently regress. 205 | let mut contained_unix_ending = false; 206 | let mut contained_other_ending = false; 207 | for byte in data.iter().copied() { 208 | if contained_other_ending && contained_unix_ending { 209 | break; 210 | } 211 | 212 | if byte == b'\n' { 213 | contained_unix_ending = true; 214 | } else if byte == b'\r' { 215 | contained_other_ending = true; 216 | } 217 | } 218 | assert!(contained_unix_ending); 219 | assert!(contained_other_ending); 220 | 221 | let mut reader = BufReader::new(&data[..]); 222 | 223 | let items = rustls_pemfile::read_all(&mut reader) 224 | .collect::, _>>() 225 | .unwrap(); 226 | 227 | assert_eq!(items.len(), 4); 228 | for cert in items { 229 | assert!(matches!(cert, rustls_pemfile::Item::X509Certificate(_))); 230 | } 231 | } 232 | 233 | #[test] 234 | fn whitespace_prefix() { 235 | let items = rustls_pemfile::read_all(&mut BufReader::new( 236 | &include_bytes!("data/whitespace-prefix.crt")[..], 237 | )) 238 | .collect::, _>>() 239 | .unwrap(); 240 | 241 | assert_eq!(items.len(), 1); 242 | assert!(matches!(items[0], rustls_pemfile::Item::X509Certificate(_))); 243 | } 244 | -------------------------------------------------------------------------------- /tests/data/zen.pem: -------------------------------------------------------------------------------- 1 | one with everything 2 | -----BEGIN CERTIFICATE----- 3 | MIIBuDCCAWqgAwIBAgICAcgwBQYDK2VwMC4xLDAqBgNVBAMMI3Bvbnl0b3duIEVk 4 | RFNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMB4XDTE5MDgxNjEzMjg1MVoXDTI1MDIw 5 | NTEzMjg1MVowGTEXMBUGA1UEAwwOdGVzdHNlcnZlci5jb20wKjAFBgMrZXADIQAQ 6 | 9M4hrE+Ucw4QUmaKOeKfphklBJi1qsqtX4u+knbseqOBwDCBvTAMBgNVHRMBAf8E 7 | AjAAMAsGA1UdDwQEAwIGwDAdBgNVHQ4EFgQUa/gnV4+a22BUKTouAYX6nfLnPKYw 8 | RAYDVR0jBD0wO4AUFxIwU406tG3CsPWkHWqfuUT48auhIKQeMBwxGjAYBgNVBAMM 9 | EXBvbnl0b3duIEVkRFNBIENBggF7MDsGA1UdEQQ0MDKCDnRlc3RzZXJ2ZXIuY29t 10 | ghVzZWNvbmQudGVzdHNlcnZlci5jb22CCWxvY2FsaG9zdDAFBgMrZXADQQApDiBQ 11 | ns3fuvsWuFpIS+osj2B/gQ0b6eBAZ1UBxRyDlAo5++JZ0PtaEROyGo2t2gqi2Lyz 12 | 47mLyGCvqgVbC6cH 13 | -----END CERTIFICATE----- 14 | -----BEGIN CERTIFICATE----- 15 | MIIBVzCCAQmgAwIBAgIBezAFBgMrZXAwHDEaMBgGA1UEAwwRcG9ueXRvd24gRWRE 16 | U0EgQ0EwHhcNMTkwODE2MTMyODUxWhcNMjkwODEzMTMyODUxWjAuMSwwKgYDVQQD 17 | DCNwb255dG93biBFZERTQSBsZXZlbCAyIGludGVybWVkaWF0ZTAqMAUGAytlcAMh 18 | AD4h3t0UCoMDGgIq4UW4P5zDngsY4vy1pE3wzLPFI4Vdo14wXDAdBgNVHQ4EFgQU 19 | FxIwU406tG3CsPWkHWqfuUT48aswIAYDVR0lAQH/BBYwFAYIKwYBBQUHAwEGCCsG 20 | AQUFBwMCMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgH+MAUGAytlcANBAAZFvMek 21 | Z71I8CXsBmx/0E6Weoaan9mJHgKqgQdK4w4h4dRg6DjNG957IbrLFO3vZduBMnna 22 | qHP3xTFF+11Eyg8= 23 | -----END CERTIFICATE----- 24 | -----BEGIN CERTIFICATE----- 25 | MIIBTDCB/6ADAgECAhRXcvbYynz4+usVvPtJp++sBUih3TAFBgMrZXAwHDEaMBgG 26 | A1UEAwwRcG9ueXRvd24gRWREU0EgQ0EwHhcNMTkwODE2MTMyODUwWhcNMjkwODEz 27 | MTMyODUwWjAcMRowGAYDVQQDDBFwb255dG93biBFZERTQSBDQTAqMAUGAytlcAMh 28 | AIE4tLweIfcBGfhPqyXFp5pjVxjaiKk+9fTbRy46jAFKo1MwUTAdBgNVHQ4EFgQU 29 | z5b9HjkOxffbtCZhWGg+bnxuD6wwHwYDVR0jBBgwFoAUz5b9HjkOxffbtCZhWGg+ 30 | bnxuD6wwDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQBNlt7z4bZ7KhzecxZEe3i5 31 | lH9MRqbpP9Rg4HyzAJfTzFGT183HoJiISdPLbxwMn0KaqSGlVe+9GgNKswoaRAwH 32 | -----END CERTIFICATE----- 33 | -----BEGIN CERTIFICATE----- 34 | MIIEnzCCAoegAwIBAgIBezANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9wb255 35 | dG93biBSU0EgQ0EwHhcNMTkwNjA5MTcxNTEyWhcNMjkwNjA2MTcxNTEyWjAsMSow 36 | KAYDVQQDDCFwb255dG93biBSU0EgbGV2ZWwgMiBpbnRlcm1lZGlhdGUwggGiMA0G 37 | CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCj/tOFeSW3WB+TtuLCR1L/84lZytFw 38 | zbpzOTGB1kPEKNbrMsv3lHXm5bHa8Bl3k113k7Hi7OAt/nkMm05s8LcUoovhaG5C 39 | G7tjzL+ld1nO74gNS3IQHCzxRdRwIgaDZHyICfBQBfB9/m+9z3yRtOKWJl6i/MT9 40 | HRN6yADW/8gHFlMzRkCKBjIKXehKsu8cbtB+5MukwtXI4rKf9aYXZQOEUn1kEwQJ 41 | ZIKBXR0eyloQiZervUE7meRCTBvzXT9VoSEX49/mempp4hnfdHlRNzre4/tphBf1 42 | fRUdpVXZ3DvmzoHdXRVzxx3X5LvDpf7Eb3ViGkXDFwkSfHEhkRnAl4lIzTH/1F25 43 | stmT8a0PA/lCNMrzJBzkLcuem1G1uMHoQZo1f3OpslJ8gHbE9ZlIbIKmpmJS9oop 44 | Vh1BH+aOy5doCrF8uOLTQ3d5CqA/EZMGahDHy7IkeNYmG/RXUKNltv+r95gwuRP+ 45 | 9UIJ9FTa4REQbIpGWP5XibI6x4LqLTJj+VsCAwEAAaNeMFwwHQYDVR0OBBYEFEKP 46 | y8hHZVazpvIsxFcGo4YrkEkwMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEF 47 | BQcDAjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB/jANBgkqhkiG9w0BAQsFAAOC 48 | AgEAMzTRDLBExVFlw98AuX+pM+/R2Gjw5KFHvSYLKLbMRfuuZK1yNYYaYtNrtF+V 49 | a53OFgaZj56o7tXc2PB8kw4MELD0ViR8Do2bvZieFcEe4DwhdjGCjuLehVLT29qI 50 | 7T3N/JkJ5daemKZcRB6Ne0F4+6QlVVNck28HUKbQThl88RdwLUImmSAfgKSt6uJ5 51 | wlH7wiYQR2vPXwSuEYzwot+L/91eBwuQr4Lovx9+TCKTbwQOKYjX4KfcOOQ1rx0M 52 | IMrvwWqnabc6m1F0O6//ibL0kuFkJYEgOH2uJA12FBHO+/q2tcytejkOWKWMJj6Y 53 | 2etwIHcpzXaEP7fZ75cFGqcE3s7XGsweBIPLjMP1bKxEcFKzygURm/auUuXBCFBl 54 | E16PB6JEAeCKe/8VFeyucvjPuQDWB49aq+r2SbpbI4IeZdz/QgEIOb0MpwStrvhH 55 | 9f/DtGMbjvuAEkRoOorK4m5k4GY3LsWTR2bey27AXk8N7pKarpu2N7ChBPm+EV0Y 56 | H+tAI/OfdZuNUCES00F5UAFdU8zBUZo19ao2ZqfEADimE7Epk2s0bUe4GSqEXJp6 57 | 68oVSMhZmMf/RCSNlr97f34sNiUA1YJ0JbCRZmw8KWNm9H1PARLbrgeRBZ/k31Li 58 | WLDr3fiEVk7SGxj3zo94cS6AT55DyXLiSD/bFmL1QXgZweA= 59 | -----END CERTIFICATE----- 60 | -----BEGIN EC PARAMETERS----- 61 | BggqhkjOPQMBBw== 62 | -----END EC PARAMETERS----- 63 | -----BEGIN EC PRIVATE KEY----- 64 | MHcCAQEEIMaA7bFrjDDBSik057bIKo7UQXJZNwLK9AjYZQ7yIWFloAoGCCqGSM49 65 | AwEHoUQDQgAExu0Z/w8nQJZAXeOXOnZun9HiZscY9H/KwYcXpeZHu+f9P9mOUEkH 66 | 5Z0av+JKtzhFspjngNLVgWcjlA1L5AJLdA== 67 | -----END EC PRIVATE KEY----- 68 | -----BEGIN PRIVATE KEY----- 69 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgxoDtsWuMMMFKKTTn 70 | tsgqjtRBclk3Asr0CNhlDvIhYWWhRANCAATG7Rn/DydAlkBd45c6dm6f0eJmxxj0 71 | f8rBhxel5ke75/0/2Y5QSQflnRq/4kq3OEWymOeA0tWBZyOUDUvkAkt0 72 | -----END PRIVATE KEY----- 73 | -----BEGIN RSA PRIVATE KEY----- 74 | MIICXAIBAAKBgQC1Dt8tFmGS76ciuNXvk/QRrV8wCcArWxvl7Ku0aSQXgcFBAav6 75 | P5RD8b+dC9DihSu/r+6OOfjsAZ6oKCq3OTUfmoUhLpoBomxPczJgLyyLD+nQkp5q 76 | B1Q3WB6ACL/HJRRjJEIn7lc5u1FVBGbiCAHKMiaP4BDSym8oqimKC6uiaQIDAQAB 77 | AoGAGKmY7sxQqDIqwwkIYyT1Jv9FqwZ4/a7gYvZVATMdLnKHP3KZ2XGVoZepcRvt 78 | 7R0Us3ykcw0kgglKcj9eaizJtnSuoDPPwt53mDypPN2sU3hZgyk2tPgr49DB3MIp 79 | fjoqw4RL/p60ksgGXbDEqBuXqOtH5i61khWlMj+BWL9VDq0CQQDaELWPQGjgs+7X 80 | /QyWMJwOF4FXE4jecH/CcPVDB9K1ukllyC1HqTNe44Sp2bIDuSXXWb8yEixrEWBE 81 | ci2CSSjXAkEA1I4W9IzwEmAeLtL6VBip9ks52O0JKu373/Xv1F2GYdhnQaFw7IC6 82 | 1lSzcYMKGTmDuM8Cj26caldyv19Q0SPmvwJAdRHjZzS9GWWAJJTF3Rvbq/USix0B 83 | renXrRvXkFTy2n1YSjxdkstTuO2Mm2M0HquXlTWpX8hB8HkzpYtmwztjoQJAECKl 84 | LXVReCOhxu4vIJkqtc6qGoSL8J1WRH8X8KgU3nKeDAZkWx++jyyo3pIS/y01iZ71 85 | U8wSxaPTyyFCMk4mYwJBALjg7g8yDy1Lg9GFfOZvAVzPjqD28jZh/VJsDz9IhYoG 86 | z89iHWHkllOisbOm+SeynVC8CoFXmJPc26U65GcjI18= 87 | -----END RSA PRIVATE KEY----- 88 | -----BEGIN PRIVATE KEY----- 89 | MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALUO3y0WYZLvpyK4 90 | 1e+T9BGtXzAJwCtbG+Xsq7RpJBeBwUEBq/o/lEPxv50L0OKFK7+v7o45+OwBnqgo 91 | Krc5NR+ahSEumgGibE9zMmAvLIsP6dCSnmoHVDdYHoAIv8clFGMkQifuVzm7UVUE 92 | ZuIIAcoyJo/gENLKbyiqKYoLq6JpAgMBAAECgYAYqZjuzFCoMirDCQhjJPUm/0Wr 93 | Bnj9ruBi9lUBMx0ucoc/cpnZcZWhl6lxG+3tHRSzfKRzDSSCCUpyP15qLMm2dK6g 94 | M8/C3neYPKk83axTeFmDKTa0+Cvj0MHcwil+OirDhEv+nrSSyAZdsMSoG5eo60fm 95 | LrWSFaUyP4FYv1UOrQJBANoQtY9AaOCz7tf9DJYwnA4XgVcTiN5wf8Jw9UMH0rW6 96 | SWXILUepM17jhKnZsgO5JddZvzISLGsRYERyLYJJKNcCQQDUjhb0jPASYB4u0vpU 97 | GKn2SznY7Qkq7fvf9e/UXYZh2GdBoXDsgLrWVLNxgwoZOYO4zwKPbpxqV3K/X1DR 98 | I+a/AkB1EeNnNL0ZZYAklMXdG9ur9RKLHQGt6detG9eQVPLafVhKPF2Sy1O47Yyb 99 | YzQeq5eVNalfyEHweTOli2bDO2OhAkAQIqUtdVF4I6HG7i8gmSq1zqoahIvwnVZE 100 | fxfwqBTecp4MBmRbH76PLKjekhL/LTWJnvVTzBLFo9PLIUIyTiZjAkEAuODuDzIP 101 | LUuD0YV85m8BXM+OoPbyNmH9UmwPP0iFigbPz2IdYeSWU6Kxs6b5J7KdULwKgVeY 102 | k9zbpTrkZyMjXw== 103 | -----END PRIVATE KEY----- 104 | -----BEGIN X509 CRL----- 105 | MIICiTBzAgEBMA0GCSqGSIb3DQEBCwUAMBoxGDAWBgNVBAMMD3Bvbnl0b3duIFJT 106 | QSBDQRcNMjMwNjI3MDgyODEyWhcNMjMwNzI3MDgyODEyWjAVMBMCAgHIFw0yMzA2 107 | MjcwODI3NTlaoA4wDDAKBgNVHRQEAwIBAjANBgkqhkiG9w0BAQsFAAOCAgEAP6EX 108 | 9+hxjx/AqdBpynZXjGkEqigBcLcJ2PADOXngdQI1jC0WuYnZymUimemeULtt8X+1 109 | ai2KxAuF1m4NEKZsrGKvO+/9s/X1xbGroyHSAMKtZafFopFpoB2aNbYlx7yIyLtD 110 | BBIZIF50g20U+3izqpHutTD10itdk9TLsSceJHpwTkNJtaWMkOfBV28nKzEzVutV 111 | f6WzRpURGzui6nQy7aIqImeanpoBoz323psMfC32U0uMBCZltyHNqsX58/2Uhucx 112 | 0IPnitNuhv4scCPf/jeRfGIWDrTf1/25LDzRxyg1S4z9aa+3GM4O3dqy4igZEhgT 113 | q3pjlJ2hUL5E0oqbZDIQD1SN8UUUv5N2AjwZcxVBNnYeGyuO7YpTBYiu62o73iL2 114 | CjgElfaMq/9hEr9GR9kJozh7VTxtQPbnr4DiucQvhv8o/A1z+zkC0gj8iCLFtDbO 115 | 8bvDowcdle9LKkrLaBe6sO+fSH/I9Wj8vrEJKsuwaEraIdEaq2VrIMUPEWN0/MH9 116 | vTwHyadGSMK4CWtrn9fCAgSLw6NX74D7Cx1IaS8vstMjpeUqOS0dk5ThiW47HceB 117 | DTko7rV5N+RGH2nW1ynLoZKCJQqqZcLilFMyKPui3jifJnQlMFi54jGVgg/D6UQn 118 | 7dA7wb2ux/1hSiaarp+mi7ncVOyByz6/WQP8mfc= 119 | -----END X509 CRL----- 120 | -----BEGIN CERTIFICATE REQUEST----- 121 | MIIC+zCCAeMCAQAwfDELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRQwEgYD 122 | VQQHDAtTYW4gQW50b25pbzEdMBsGA1UECgwURXhhbXBsZSBPcmdhbml6YXRpb24x 123 | EjAQBgNVBAsMCU1hcmtldGluZzEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0G 124 | CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwJw0BcbuqZyiABlmSYTi1tcr8DB0D 125 | NcTtzsYe7tlyIKd3mEs+u6Pi3rEQGvOw5eo6CmWII2qmVOqJ2f6gjl2lZJ5DUE6B 126 | I+NNE73zfFMrttUtI8X4ChnE4rrGqqUsSvYz1YVU0KiJ/00YMjEY5XlJYYa9FgfZ 127 | sUrhj4aCFdXS6CU9jueRr+udEBElDcgTS9+pB+LFhVfUMTdxnJ3BcT4ZDDqODH3/ 128 | 5RAgq03dhRpkkaVIg2uVKTBDoM3hs8T1zIxLM7hItaZzMv4uHdfI8y+BdHrePT33 129 | BoTlocvTEZEqqXEdw2kUd4PDgyUTjFE3b9OeLk0Ju5GRvuCW3UcS5gFvAgMBAAGg 130 | OjA4BgkqhkiG9w0BCQ4xKzApMCcGA1UdEQQgMB6CC2V4YW1wbGUuY29tgg9mb28u 131 | ZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBACWsgxPw13QUpoCJOqvp8B1A 132 | EfsxRJITSROmukV3ZQycPT76Y3GVrM9sGjO8p13J/CVw2KcWc9xmgHF0MdvPNhnW 133 | OB6Y07hVpNnJVHb1KglOkNkTy6sVDtnZHg2klqGSyzIbwZ9R3JG8HtRdkceIrm3D 134 | gdiZyLcf1VDCCUGaskEi2CsggCQQJNyGi+8BSQ8MPKm/m0KrSchGQ157eWCCjopz 135 | f5GQe2UGOg5T7g8+S4GdECMwkMlTGUwlAM6LuOG/NZqP528PCAYQv0eOYdSwALQT 136 | GwTyU4AZ9y1uBFuaFxABew9GbDEtNY/XHTF8308edUwGBk6jfD+UuTeEwRZGs9E= 137 | -----END CERTIFICATE REQUEST----- 138 | ... that's all folks! 139 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # rustls-pemfile 2 | //! A basic parser for .pem files containing cryptographic keys and certificates. 3 | //! 4 | //! The input to this crate is a .pem file containing potentially many sections, 5 | //! and the output is those sections as alleged DER-encodings. This crate does 6 | //! not decode the actual DER-encoded keys/certificates. 7 | //! 8 | //! ## Quick start 9 | //! Starting with an `io::BufRead` containing the file to be read: 10 | //! - Use `read_all()` to ingest the whole file, then work through the contents in-memory, or, 11 | //! - Use `read_one()` to stream through the file, processing the items as found, or, 12 | //! - Use `certs()` to extract just the certificates (silently discarding other sections), and 13 | //! similarly for `rsa_private_keys()` and `pkcs8_private_keys()`. 14 | //! 15 | //! # no-std support 16 | //! 17 | //! The opt-out "std" Cargo feature can be disabled to put this crate in no-std mode. 18 | //! 19 | //! In no-std mode, the `read_one_from_slice` API can be used to parse a .pem file that has already 20 | //! been loaded into memory. 21 | //! 22 | //! ## Example code 23 | #![cfg_attr(feature = "std", doc = "```")] 24 | #![cfg_attr(not(feature = "std"), doc = "```ignore")] 25 | //! use std::iter; 26 | //! use rustls_pemfile::{Item, read_one}; 27 | //! # let mut reader = std::io::BufReader::new(&b"junk\n-----BEGIN RSA PRIVATE KEY-----\nqw\n-----END RSA PRIVATE KEY-----\n"[..]); 28 | //! // Assume `reader` is any std::io::BufRead implementor 29 | //! for item in iter::from_fn(|| read_one(&mut reader).transpose()) { 30 | //! match item.unwrap() { 31 | //! Item::X509Certificate(cert) => println!("certificate {:?}", cert), 32 | //! Item::Crl(crl) => println!("certificate revocation list: {:?}", crl), 33 | //! Item::Csr(csr) => println!("certificate signing request: {:?}", csr), 34 | //! Item::Pkcs1Key(key) => println!("rsa pkcs1 key {:?}", key), 35 | //! Item::Pkcs8Key(key) => println!("pkcs8 key {:?}", key), 36 | //! Item::Sec1Key(key) => println!("sec1 ec key {:?}", key), 37 | //! _ => println!("unhandled item"), 38 | //! } 39 | //! } 40 | //! ``` 41 | 42 | // Require docs for public APIs, deny unsafe code, etc. 43 | #![forbid(unsafe_code, unused_must_use, unstable_features)] 44 | #![deny( 45 | trivial_casts, 46 | trivial_numeric_casts, 47 | missing_docs, 48 | unused_import_braces, 49 | unused_extern_crates, 50 | unused_qualifications 51 | )] 52 | #![no_std] 53 | 54 | extern crate alloc; 55 | #[cfg(any(feature = "std", test))] 56 | extern crate std; 57 | 58 | #[cfg(test)] 59 | #[cfg(feature = "std")] 60 | mod tests; 61 | 62 | mod pemfile; 63 | #[cfg(feature = "std")] 64 | use core::iter; 65 | /// --- Legacy APIs: 66 | #[cfg(feature = "std")] 67 | use std::io; 68 | 69 | #[cfg(feature = "std")] 70 | pub use pemfile::{read_all, read_one}; 71 | pub use pemfile::{read_one_from_slice, Error, Item}; 72 | #[cfg(feature = "std")] 73 | use pki_types::PrivateKeyDer; 74 | #[cfg(feature = "std")] 75 | use pki_types::{ 76 | CertificateDer, CertificateRevocationListDer, CertificateSigningRequestDer, PrivatePkcs1KeyDer, 77 | PrivatePkcs8KeyDer, PrivateSec1KeyDer, SubjectPublicKeyInfoDer, 78 | }; 79 | 80 | /// Return an iterator over certificates from `rd`. 81 | /// 82 | /// Filters out any PEM sections that are not certificates and yields errors if a problem 83 | /// occurs while trying to extract a certificate. 84 | #[cfg(feature = "std")] 85 | pub fn certs( 86 | rd: &mut dyn io::BufRead, 87 | ) -> impl Iterator, io::Error>> + '_ { 88 | iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item { 89 | Ok(Item::X509Certificate(cert)) => Some(Ok(cert)), 90 | Err(err) => Some(Err(err)), 91 | _ => None, 92 | }) 93 | } 94 | 95 | /// Return the first private key found in `rd`. 96 | /// 97 | /// Yields the first PEM section describing a private key (of any type), or an error if a 98 | /// problem occurs while trying to read PEM sections. 99 | #[cfg(feature = "std")] 100 | pub fn private_key(rd: &mut dyn io::BufRead) -> Result>, io::Error> { 101 | for result in iter::from_fn(move || read_one(rd).transpose()) { 102 | match result? { 103 | Item::Pkcs1Key(key) => return Ok(Some(key.into())), 104 | Item::Pkcs8Key(key) => return Ok(Some(key.into())), 105 | Item::Sec1Key(key) => return Ok(Some(key.into())), 106 | Item::X509Certificate(_) 107 | | Item::SubjectPublicKeyInfo(_) 108 | | Item::Crl(_) 109 | | Item::Csr(_) => continue, 110 | } 111 | } 112 | 113 | Ok(None) 114 | } 115 | 116 | /// Return the first certificate signing request (CSR) found in `rd`. 117 | /// 118 | /// Yields the first PEM section describing a certificate signing request, or an error if a 119 | /// problem occurs while trying to read PEM sections. 120 | #[cfg(feature = "std")] 121 | pub fn csr( 122 | rd: &mut dyn io::BufRead, 123 | ) -> Result>, io::Error> { 124 | for result in iter::from_fn(move || read_one(rd).transpose()) { 125 | match result? { 126 | Item::Csr(csr) => return Ok(Some(csr)), 127 | Item::Pkcs1Key(_) 128 | | Item::Pkcs8Key(_) 129 | | Item::Sec1Key(_) 130 | | Item::X509Certificate(_) 131 | | Item::SubjectPublicKeyInfo(_) 132 | | Item::Crl(_) => continue, 133 | } 134 | } 135 | 136 | Ok(None) 137 | } 138 | 139 | /// Return an iterator certificate revocation lists (CRLs) from `rd`. 140 | /// 141 | /// Filters out any PEM sections that are not CRLs and yields errors if a problem occurs 142 | /// while trying to extract a CRL. 143 | #[cfg(feature = "std")] 144 | pub fn crls( 145 | rd: &mut dyn io::BufRead, 146 | ) -> impl Iterator, io::Error>> + '_ { 147 | iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item { 148 | Ok(Item::Crl(crl)) => Some(Ok(crl)), 149 | Err(err) => Some(Err(err)), 150 | _ => None, 151 | }) 152 | } 153 | 154 | /// Return an iterator over RSA private keys from `rd`. 155 | /// 156 | /// Filters out any PEM sections that are not RSA private keys and yields errors if a problem 157 | /// occurs while trying to extract an RSA private key. 158 | #[cfg(feature = "std")] 159 | pub fn rsa_private_keys( 160 | rd: &mut dyn io::BufRead, 161 | ) -> impl Iterator, io::Error>> + '_ { 162 | iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item { 163 | Ok(Item::Pkcs1Key(key)) => Some(Ok(key)), 164 | Err(err) => Some(Err(err)), 165 | _ => None, 166 | }) 167 | } 168 | 169 | /// Return an iterator over PKCS8-encoded private keys from `rd`. 170 | /// 171 | /// Filters out any PEM sections that are not PKCS8-encoded private keys and yields errors if a 172 | /// problem occurs while trying to extract an RSA private key. 173 | #[cfg(feature = "std")] 174 | pub fn pkcs8_private_keys( 175 | rd: &mut dyn io::BufRead, 176 | ) -> impl Iterator, io::Error>> + '_ { 177 | iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item { 178 | Ok(Item::Pkcs8Key(key)) => Some(Ok(key)), 179 | Err(err) => Some(Err(err)), 180 | _ => None, 181 | }) 182 | } 183 | 184 | /// Return an iterator over SEC1-encoded EC private keys from `rd`. 185 | /// 186 | /// Filters out any PEM sections that are not SEC1-encoded EC private keys and yields errors if a 187 | /// problem occurs while trying to extract a SEC1-encoded EC private key. 188 | #[cfg(feature = "std")] 189 | pub fn ec_private_keys( 190 | rd: &mut dyn io::BufRead, 191 | ) -> impl Iterator, io::Error>> + '_ { 192 | iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item { 193 | Ok(Item::Sec1Key(key)) => Some(Ok(key)), 194 | Err(err) => Some(Err(err)), 195 | _ => None, 196 | }) 197 | } 198 | 199 | /// Return an iterator over SPKI-encoded keys from `rd`. 200 | /// 201 | /// Filters out any PEM sections that are not SPKI-encoded public keys and yields errors if a 202 | /// problem occurs while trying to extract a SPKI-encoded public key. 203 | #[cfg(feature = "std")] 204 | pub fn public_keys( 205 | rd: &mut dyn io::BufRead, 206 | ) -> impl Iterator, io::Error>> + '_ { 207 | iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item { 208 | Ok(Item::SubjectPublicKeyInfo(key)) => Some(Ok(key)), 209 | Err(err) => Some(Err(err)), 210 | _ => None, 211 | }) 212 | } 213 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /tests/data/zen2.pem: -------------------------------------------------------------------------------- 1 | one with everything 2 | -----BEGIN CERTIFICATE----- 3 | MIIBuDCCAWqgAwIBAgICAcgwBQYDK2VwMC4xLDAqBgNVBAMMI3Bvbnl0b3duIEVk 4 | RFNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMB4XDTE5MDgxNjEzMjg1MVoXDTI1MDIw 5 | NTEzMjg1MVowGTEXMBUGA1UEAwwOdGVzdHNlcnZlci5jb20wKjAFBgMrZXADIQAQ 6 | 9M4hrE+Ucw4QUmaKOeKfphklBJi1qsqtX4u+knbseqOBwDCBvTAMBgNVHRMBAf8E 7 | AjAAMAsGA1UdDwQEAwIGwDAdBgNVHQ4EFgQUa/gnV4+a22BUKTouAYX6nfLnPKYw 8 | RAYDVR0jBD0wO4AUFxIwU406tG3CsPWkHWqfuUT48auhIKQeMBwxGjAYBgNVBAMM 9 | EXBvbnl0b3duIEVkRFNBIENBggF7MDsGA1UdEQQ0MDKCDnRlc3RzZXJ2ZXIuY29t 10 | ghVzZWNvbmQudGVzdHNlcnZlci5jb22CCWxvY2FsaG9zdDAFBgMrZXADQQApDiBQ 11 | ns3fuvsWuFpIS+osj2B/gQ0b6eBAZ1UBxRyDlAo5++JZ0PtaEROyGo2t2gqi2Lyz 12 | 47mLyGCvqgVbC6cH 13 | -----END CERTIFICATE----- 14 | -----BEGIN CERTIFICATE----- 15 | MIIBVzCCAQmgAwIBAgIBezAFBgMrZXAwHDEaMBgGA1UEAwwRcG9ueXRvd24gRWRE 16 | U0EgQ0EwHhcNMTkwODE2MTMyODUxWhcNMjkwODEzMTMyODUxWjAuMSwwKgYDVQQD 17 | DCNwb255dG93biBFZERTQSBsZXZlbCAyIGludGVybWVkaWF0ZTAqMAUGAytlcAMh 18 | AD4h3t0UCoMDGgIq4UW4P5zDngsY4vy1pE3wzLPFI4Vdo14wXDAdBgNVHQ4EFgQU 19 | FxIwU406tG3CsPWkHWqfuUT48aswIAYDVR0lAQH/BBYwFAYIKwYBBQUHAwEGCCsG 20 | AQUFBwMCMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgH+MAUGAytlcANBAAZFvMek 21 | Z71I8CXsBmx/0E6Weoaan9mJHgKqgQdK4w4h4dRg6DjNG957IbrLFO3vZduBMnna 22 | qHP3xTFF+11Eyg8= 23 | -----END CERTIFICATE----- 24 | -----BEGIN CERTIFICATE----- 25 | MIIBTDCB/6ADAgECAhRXcvbYynz4+usVvPtJp++sBUih3TAFBgMrZXAwHDEaMBgG 26 | A1UEAwwRcG9ueXRvd24gRWREU0EgQ0EwHhcNMTkwODE2MTMyODUwWhcNMjkwODEz 27 | MTMyODUwWjAcMRowGAYDVQQDDBFwb255dG93biBFZERTQSBDQTAqMAUGAytlcAMh 28 | AIE4tLweIfcBGfhPqyXFp5pjVxjaiKk+9fTbRy46jAFKo1MwUTAdBgNVHQ4EFgQU 29 | z5b9HjkOxffbtCZhWGg+bnxuD6wwHwYDVR0jBBgwFoAUz5b9HjkOxffbtCZhWGg+ 30 | bnxuD6wwDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQBNlt7z4bZ7KhzecxZEe3i5 31 | lH9MRqbpP9Rg4HyzAJfTzFGT183HoJiISdPLbxwMn0KaqSGlVe+9GgNKswoaRAwH 32 | -----END CERTIFICATE----- 33 | -----BEGIN CERTIFICATE----- 34 | MIIEnzCCAoegAwIBAgIBezANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9wb255 35 | dG93biBSU0EgQ0EwHhcNMTkwNjA5MTcxNTEyWhcNMjkwNjA2MTcxNTEyWjAsMSow 36 | KAYDVQQDDCFwb255dG93biBSU0EgbGV2ZWwgMiBpbnRlcm1lZGlhdGUwggGiMA0G 37 | CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCj/tOFeSW3WB+TtuLCR1L/84lZytFw 38 | zbpzOTGB1kPEKNbrMsv3lHXm5bHa8Bl3k113k7Hi7OAt/nkMm05s8LcUoovhaG5C 39 | G7tjzL+ld1nO74gNS3IQHCzxRdRwIgaDZHyICfBQBfB9/m+9z3yRtOKWJl6i/MT9 40 | HRN6yADW/8gHFlMzRkCKBjIKXehKsu8cbtB+5MukwtXI4rKf9aYXZQOEUn1kEwQJ 41 | ZIKBXR0eyloQiZervUE7meRCTBvzXT9VoSEX49/mempp4hnfdHlRNzre4/tphBf1 42 | fRUdpVXZ3DvmzoHdXRVzxx3X5LvDpf7Eb3ViGkXDFwkSfHEhkRnAl4lIzTH/1F25 43 | stmT8a0PA/lCNMrzJBzkLcuem1G1uMHoQZo1f3OpslJ8gHbE9ZlIbIKmpmJS9oop 44 | Vh1BH+aOy5doCrF8uOLTQ3d5CqA/EZMGahDHy7IkeNYmG/RXUKNltv+r95gwuRP+ 45 | 9UIJ9FTa4REQbIpGWP5XibI6x4LqLTJj+VsCAwEAAaNeMFwwHQYDVR0OBBYEFEKP 46 | y8hHZVazpvIsxFcGo4YrkEkwMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEF 47 | BQcDAjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB/jANBgkqhkiG9w0BAQsFAAOC 48 | AgEAMzTRDLBExVFlw98AuX+pM+/R2Gjw5KFHvSYLKLbMRfuuZK1yNYYaYtNrtF+V 49 | a53OFgaZj56o7tXc2PB8kw4MELD0ViR8Do2bvZieFcEe4DwhdjGCjuLehVLT29qI 50 | 7T3N/JkJ5daemKZcRB6Ne0F4+6QlVVNck28HUKbQThl88RdwLUImmSAfgKSt6uJ5 51 | wlH7wiYQR2vPXwSuEYzwot+L/91eBwuQr4Lovx9+TCKTbwQOKYjX4KfcOOQ1rx0M 52 | IMrvwWqnabc6m1F0O6//ibL0kuFkJYEgOH2uJA12FBHO+/q2tcytejkOWKWMJj6Y 53 | 2etwIHcpzXaEP7fZ75cFGqcE3s7XGsweBIPLjMP1bKxEcFKzygURm/auUuXBCFBl 54 | E16PB6JEAeCKe/8VFeyucvjPuQDWB49aq+r2SbpbI4IeZdz/QgEIOb0MpwStrvhH 55 | 9f/DtGMbjvuAEkRoOorK4m5k4GY3LsWTR2bey27AXk8N7pKarpu2N7ChBPm+EV0Y 56 | H+tAI/OfdZuNUCES00F5UAFdU8zBUZo19ao2ZqfEADimE7Epk2s0bUe4GSqEXJp6 57 | 68oVSMhZmMf/RCSNlr97f34sNiUA1YJ0JbCRZmw8KWNm9H1PARLbrgeRBZ/k31Li 58 | WLDr3fiEVk7SGxj3zo94cS6AT55DyXLiSD/bFmL1QXgZweA= 59 | -----END CERTIFICATE----- 60 | -----BEGIN EC PARAMETERS----- 61 | BggqhkjOPQMBBw== 62 | -----END EC PARAMETERS----- 63 | -----BEGIN EC PRIVATE KEY----- 64 | MHcCAQEEIMaA7bFrjDDBSik057bIKo7UQXJZNwLK9AjYZQ7yIWFloAoGCCqGSM49 65 | AwEHoUQDQgAExu0Z/w8nQJZAXeOXOnZun9HiZscY9H/KwYcXpeZHu+f9P9mOUEkH 66 | 5Z0av+JKtzhFspjngNLVgWcjlA1L5AJLdA== 67 | -----END EC PRIVATE KEY----- 68 | -----BEGIN PRIVATE KEY----- 69 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgxoDtsWuMMMFKKTTn 70 | tsgqjtRBclk3Asr0CNhlDvIhYWWhRANCAATG7Rn/DydAlkBd45c6dm6f0eJmxxj0 71 | f8rBhxel5ke75/0/2Y5QSQflnRq/4kq3OEWymOeA0tWBZyOUDUvkAkt0 72 | -----END PRIVATE KEY----- 73 | -----BEGIN RSA PRIVATE KEY----- 74 | MIICXAIBAAKBgQC1Dt8tFmGS76ciuNXvk/QRrV8wCcArWxvl7Ku0aSQXgcFBAav6 75 | P5RD8b+dC9DihSu/r+6OOfjsAZ6oKCq3OTUfmoUhLpoBomxPczJgLyyLD+nQkp5q 76 | B1Q3WB6ACL/HJRRjJEIn7lc5u1FVBGbiCAHKMiaP4BDSym8oqimKC6uiaQIDAQAB 77 | AoGAGKmY7sxQqDIqwwkIYyT1Jv9FqwZ4/a7gYvZVATMdLnKHP3KZ2XGVoZepcRvt 78 | 7R0Us3ykcw0kgglKcj9eaizJtnSuoDPPwt53mDypPN2sU3hZgyk2tPgr49DB3MIp 79 | fjoqw4RL/p60ksgGXbDEqBuXqOtH5i61khWlMj+BWL9VDq0CQQDaELWPQGjgs+7X 80 | /QyWMJwOF4FXE4jecH/CcPVDB9K1ukllyC1HqTNe44Sp2bIDuSXXWb8yEixrEWBE 81 | ci2CSSjXAkEA1I4W9IzwEmAeLtL6VBip9ks52O0JKu373/Xv1F2GYdhnQaFw7IC6 82 | 1lSzcYMKGTmDuM8Cj26caldyv19Q0SPmvwJAdRHjZzS9GWWAJJTF3Rvbq/USix0B 83 | renXrRvXkFTy2n1YSjxdkstTuO2Mm2M0HquXlTWpX8hB8HkzpYtmwztjoQJAECKl 84 | LXVReCOhxu4vIJkqtc6qGoSL8J1WRH8X8KgU3nKeDAZkWx++jyyo3pIS/y01iZ71 85 | U8wSxaPTyyFCMk4mYwJBALjg7g8yDy1Lg9GFfOZvAVzPjqD28jZh/VJsDz9IhYoG 86 | z89iHWHkllOisbOm+SeynVC8CoFXmJPc26U65GcjI18= 87 | -----END RSA PRIVATE KEY----- 88 | -----BEGIN PRIVATE KEY----- 89 | MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALUO3y0WYZLvpyK4 90 | 1e+T9BGtXzAJwCtbG+Xsq7RpJBeBwUEBq/o/lEPxv50L0OKFK7+v7o45+OwBnqgo 91 | Krc5NR+ahSEumgGibE9zMmAvLIsP6dCSnmoHVDdYHoAIv8clFGMkQifuVzm7UVUE 92 | ZuIIAcoyJo/gENLKbyiqKYoLq6JpAgMBAAECgYAYqZjuzFCoMirDCQhjJPUm/0Wr 93 | Bnj9ruBi9lUBMx0ucoc/cpnZcZWhl6lxG+3tHRSzfKRzDSSCCUpyP15qLMm2dK6g 94 | M8/C3neYPKk83axTeFmDKTa0+Cvj0MHcwil+OirDhEv+nrSSyAZdsMSoG5eo60fm 95 | LrWSFaUyP4FYv1UOrQJBANoQtY9AaOCz7tf9DJYwnA4XgVcTiN5wf8Jw9UMH0rW6 96 | SWXILUepM17jhKnZsgO5JddZvzISLGsRYERyLYJJKNcCQQDUjhb0jPASYB4u0vpU 97 | GKn2SznY7Qkq7fvf9e/UXYZh2GdBoXDsgLrWVLNxgwoZOYO4zwKPbpxqV3K/X1DR 98 | I+a/AkB1EeNnNL0ZZYAklMXdG9ur9RKLHQGt6detG9eQVPLafVhKPF2Sy1O47Yyb 99 | YzQeq5eVNalfyEHweTOli2bDO2OhAkAQIqUtdVF4I6HG7i8gmSq1zqoahIvwnVZE 100 | fxfwqBTecp4MBmRbH76PLKjekhL/LTWJnvVTzBLFo9PLIUIyTiZjAkEAuODuDzIP 101 | LUuD0YV85m8BXM+OoPbyNmH9UmwPP0iFigbPz2IdYeSWU6Kxs6b5J7KdULwKgVeY 102 | k9zbpTrkZyMjXw== 103 | -----END PRIVATE KEY----- 104 | ... that's all folks! 105 | one with everything 106 | -----BEGIN CERTIFICATE----- 107 | MIIBuDCCAWqgAwIBAgICAcgwBQYDK2VwMC4xLDAqBgNVBAMMI3Bvbnl0b3duIEVk 108 | RFNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMB4XDTE5MDgxNjEzMjg1MVoXDTI1MDIw 109 | NTEzMjg1MVowGTEXMBUGA1UEAwwOdGVzdHNlcnZlci5jb20wKjAFBgMrZXADIQAQ 110 | 9M4hrE+Ucw4QUmaKOeKfphklBJi1qsqtX4u+knbseqOBwDCBvTAMBgNVHRMBAf8E 111 | AjAAMAsGA1UdDwQEAwIGwDAdBgNVHQ4EFgQUa/gnV4+a22BUKTouAYX6nfLnPKYw 112 | RAYDVR0jBD0wO4AUFxIwU406tG3CsPWkHWqfuUT48auhIKQeMBwxGjAYBgNVBAMM 113 | EXBvbnl0b3duIEVkRFNBIENBggF7MDsGA1UdEQQ0MDKCDnRlc3RzZXJ2ZXIuY29t 114 | ghVzZWNvbmQudGVzdHNlcnZlci5jb22CCWxvY2FsaG9zdDAFBgMrZXADQQApDiBQ 115 | ns3fuvsWuFpIS+osj2B/gQ0b6eBAZ1UBxRyDlAo5++JZ0PtaEROyGo2t2gqi2Lyz 116 | 47mLyGCvqgVbC6cH 117 | -----END CERTIFICATE----- 118 | -----BEGIN CERTIFICATE----- 119 | MIIBVzCCAQmgAwIBAgIBezAFBgMrZXAwHDEaMBgGA1UEAwwRcG9ueXRvd24gRWRE 120 | U0EgQ0EwHhcNMTkwODE2MTMyODUxWhcNMjkwODEzMTMyODUxWjAuMSwwKgYDVQQD 121 | DCNwb255dG93biBFZERTQSBsZXZlbCAyIGludGVybWVkaWF0ZTAqMAUGAytlcAMh 122 | AD4h3t0UCoMDGgIq4UW4P5zDngsY4vy1pE3wzLPFI4Vdo14wXDAdBgNVHQ4EFgQU 123 | FxIwU406tG3CsPWkHWqfuUT48aswIAYDVR0lAQH/BBYwFAYIKwYBBQUHAwEGCCsG 124 | AQUFBwMCMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgH+MAUGAytlcANBAAZFvMek 125 | Z71I8CXsBmx/0E6Weoaan9mJHgKqgQdK4w4h4dRg6DjNG957IbrLFO3vZduBMnna 126 | qHP3xTFF+11Eyg8= 127 | -----END CERTIFICATE----- 128 | -----BEGIN CERTIFICATE----- 129 | MIIBTDCB/6ADAgECAhRXcvbYynz4+usVvPtJp++sBUih3TAFBgMrZXAwHDEaMBgG 130 | A1UEAwwRcG9ueXRvd24gRWREU0EgQ0EwHhcNMTkwODE2MTMyODUwWhcNMjkwODEz 131 | MTMyODUwWjAcMRowGAYDVQQDDBFwb255dG93biBFZERTQSBDQTAqMAUGAytlcAMh 132 | AIE4tLweIfcBGfhPqyXFp5pjVxjaiKk+9fTbRy46jAFKo1MwUTAdBgNVHQ4EFgQU 133 | z5b9HjkOxffbtCZhWGg+bnxuD6wwHwYDVR0jBBgwFoAUz5b9HjkOxffbtCZhWGg+ 134 | bnxuD6wwDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQBNlt7z4bZ7KhzecxZEe3i5 135 | lH9MRqbpP9Rg4HyzAJfTzFGT183HoJiISdPLbxwMn0KaqSGlVe+9GgNKswoaRAwH 136 | -----END CERTIFICATE----- 137 | -----BEGIN CERTIFICATE----- 138 | MIIEnzCCAoegAwIBAgIBezANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9wb255 139 | dG93biBSU0EgQ0EwHhcNMTkwNjA5MTcxNTEyWhcNMjkwNjA2MTcxNTEyWjAsMSow 140 | KAYDVQQDDCFwb255dG93biBSU0EgbGV2ZWwgMiBpbnRlcm1lZGlhdGUwggGiMA0G 141 | CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCj/tOFeSW3WB+TtuLCR1L/84lZytFw 142 | zbpzOTGB1kPEKNbrMsv3lHXm5bHa8Bl3k113k7Hi7OAt/nkMm05s8LcUoovhaG5C 143 | G7tjzL+ld1nO74gNS3IQHCzxRdRwIgaDZHyICfBQBfB9/m+9z3yRtOKWJl6i/MT9 144 | HRN6yADW/8gHFlMzRkCKBjIKXehKsu8cbtB+5MukwtXI4rKf9aYXZQOEUn1kEwQJ 145 | ZIKBXR0eyloQiZervUE7meRCTBvzXT9VoSEX49/mempp4hnfdHlRNzre4/tphBf1 146 | fRUdpVXZ3DvmzoHdXRVzxx3X5LvDpf7Eb3ViGkXDFwkSfHEhkRnAl4lIzTH/1F25 147 | stmT8a0PA/lCNMrzJBzkLcuem1G1uMHoQZo1f3OpslJ8gHbE9ZlIbIKmpmJS9oop 148 | Vh1BH+aOy5doCrF8uOLTQ3d5CqA/EZMGahDHy7IkeNYmG/RXUKNltv+r95gwuRP+ 149 | 9UIJ9FTa4REQbIpGWP5XibI6x4LqLTJj+VsCAwEAAaNeMFwwHQYDVR0OBBYEFEKP 150 | y8hHZVazpvIsxFcGo4YrkEkwMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEF 151 | BQcDAjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB/jANBgkqhkiG9w0BAQsFAAOC 152 | AgEAMzTRDLBExVFlw98AuX+pM+/R2Gjw5KFHvSYLKLbMRfuuZK1yNYYaYtNrtF+V 153 | a53OFgaZj56o7tXc2PB8kw4MELD0ViR8Do2bvZieFcEe4DwhdjGCjuLehVLT29qI 154 | 7T3N/JkJ5daemKZcRB6Ne0F4+6QlVVNck28HUKbQThl88RdwLUImmSAfgKSt6uJ5 155 | wlH7wiYQR2vPXwSuEYzwot+L/91eBwuQr4Lovx9+TCKTbwQOKYjX4KfcOOQ1rx0M 156 | IMrvwWqnabc6m1F0O6//ibL0kuFkJYEgOH2uJA12FBHO+/q2tcytejkOWKWMJj6Y 157 | 2etwIHcpzXaEP7fZ75cFGqcE3s7XGsweBIPLjMP1bKxEcFKzygURm/auUuXBCFBl 158 | E16PB6JEAeCKe/8VFeyucvjPuQDWB49aq+r2SbpbI4IeZdz/QgEIOb0MpwStrvhH 159 | 9f/DtGMbjvuAEkRoOorK4m5k4GY3LsWTR2bey27AXk8N7pKarpu2N7ChBPm+EV0Y 160 | H+tAI/OfdZuNUCES00F5UAFdU8zBUZo19ao2ZqfEADimE7Epk2s0bUe4GSqEXJp6 161 | 68oVSMhZmMf/RCSNlr97f34sNiUA1YJ0JbCRZmw8KWNm9H1PARLbrgeRBZ/k31Li 162 | WLDr3fiEVk7SGxj3zo94cS6AT55DyXLiSD/bFmL1QXgZweA= 163 | -----END CERTIFICATE----- 164 | -----BEGIN EC PARAMETERS----- 165 | BggqhkjOPQMBBw== 166 | -----END EC PARAMETERS----- 167 | -----BEGIN EC PRIVATE KEY----- 168 | MHcCAQEEIMaA7bFrjDDBSik057bIKo7UQXJZNwLK9AjYZQ7yIWFloAoGCCqGSM49 169 | AwEHoUQDQgAExu0Z/w8nQJZAXeOXOnZun9HiZscY9H/KwYcXpeZHu+f9P9mOUEkH 170 | 5Z0av+JKtzhFspjngNLVgWcjlA1L5AJLdA== 171 | -----END EC PRIVATE KEY----- 172 | -----BEGIN PRIVATE KEY----- 173 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgxoDtsWuMMMFKKTTn 174 | tsgqjtRBclk3Asr0CNhlDvIhYWWhRANCAATG7Rn/DydAlkBd45c6dm6f0eJmxxj0 175 | f8rBhxel5ke75/0/2Y5QSQflnRq/4kq3OEWymOeA0tWBZyOUDUvkAkt0 176 | -----END PRIVATE KEY----- 177 | -----BEGIN RSA PRIVATE KEY----- 178 | MIICXAIBAAKBgQC1Dt8tFmGS76ciuNXvk/QRrV8wCcArWxvl7Ku0aSQXgcFBAav6 179 | P5RD8b+dC9DihSu/r+6OOfjsAZ6oKCq3OTUfmoUhLpoBomxPczJgLyyLD+nQkp5q 180 | B1Q3WB6ACL/HJRRjJEIn7lc5u1FVBGbiCAHKMiaP4BDSym8oqimKC6uiaQIDAQAB 181 | AoGAGKmY7sxQqDIqwwkIYyT1Jv9FqwZ4/a7gYvZVATMdLnKHP3KZ2XGVoZepcRvt 182 | 7R0Us3ykcw0kgglKcj9eaizJtnSuoDPPwt53mDypPN2sU3hZgyk2tPgr49DB3MIp 183 | fjoqw4RL/p60ksgGXbDEqBuXqOtH5i61khWlMj+BWL9VDq0CQQDaELWPQGjgs+7X 184 | /QyWMJwOF4FXE4jecH/CcPVDB9K1ukllyC1HqTNe44Sp2bIDuSXXWb8yEixrEWBE 185 | ci2CSSjXAkEA1I4W9IzwEmAeLtL6VBip9ks52O0JKu373/Xv1F2GYdhnQaFw7IC6 186 | 1lSzcYMKGTmDuM8Cj26caldyv19Q0SPmvwJAdRHjZzS9GWWAJJTF3Rvbq/USix0B 187 | renXrRvXkFTy2n1YSjxdkstTuO2Mm2M0HquXlTWpX8hB8HkzpYtmwztjoQJAECKl 188 | LXVReCOhxu4vIJkqtc6qGoSL8J1WRH8X8KgU3nKeDAZkWx++jyyo3pIS/y01iZ71 189 | U8wSxaPTyyFCMk4mYwJBALjg7g8yDy1Lg9GFfOZvAVzPjqD28jZh/VJsDz9IhYoG 190 | z89iHWHkllOisbOm+SeynVC8CoFXmJPc26U65GcjI18= 191 | -----END RSA PRIVATE KEY----- 192 | -----BEGIN PRIVATE KEY----- 193 | MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALUO3y0WYZLvpyK4 194 | 1e+T9BGtXzAJwCtbG+Xsq7RpJBeBwUEBq/o/lEPxv50L0OKFK7+v7o45+OwBnqgo 195 | Krc5NR+ahSEumgGibE9zMmAvLIsP6dCSnmoHVDdYHoAIv8clFGMkQifuVzm7UVUE 196 | ZuIIAcoyJo/gENLKbyiqKYoLq6JpAgMBAAECgYAYqZjuzFCoMirDCQhjJPUm/0Wr 197 | Bnj9ruBi9lUBMx0ucoc/cpnZcZWhl6lxG+3tHRSzfKRzDSSCCUpyP15qLMm2dK6g 198 | M8/C3neYPKk83axTeFmDKTa0+Cvj0MHcwil+OirDhEv+nrSSyAZdsMSoG5eo60fm 199 | LrWSFaUyP4FYv1UOrQJBANoQtY9AaOCz7tf9DJYwnA4XgVcTiN5wf8Jw9UMH0rW6 200 | SWXILUepM17jhKnZsgO5JddZvzISLGsRYERyLYJJKNcCQQDUjhb0jPASYB4u0vpU 201 | GKn2SznY7Qkq7fvf9e/UXYZh2GdBoXDsgLrWVLNxgwoZOYO4zwKPbpxqV3K/X1DR 202 | I+a/AkB1EeNnNL0ZZYAklMXdG9ur9RKLHQGt6detG9eQVPLafVhKPF2Sy1O47Yyb 203 | YzQeq5eVNalfyEHweTOli2bDO2OhAkAQIqUtdVF4I6HG7i8gmSq1zqoahIvwnVZE 204 | fxfwqBTecp4MBmRbH76PLKjekhL/LTWJnvVTzBLFo9PLIUIyTiZjAkEAuODuDzIP 205 | LUuD0YV85m8BXM+OoPbyNmH9UmwPP0iFigbPz2IdYeSWU6Kxs6b5J7KdULwKgVeY 206 | k9zbpTrkZyMjXw== 207 | -----END PRIVATE KEY----- 208 | -----BEGIN PUBLIC KEY----- 209 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqIh8FTj9DIgI8DAoCBh+ 210 | 6UXOfaWkvNaGZx2GwXl4WDAa/ZSE5/8ofg/6V59bmk9yry57UR4F+blscBvE4g3U 211 | dTvWJOBRD900l21vwpDLKzZguyGOCmKwJu3vCnAQKzBRXW5sDgvO67GeU6kpaic9 212 | LYPYnYaoxCRTYTZu0wy72rW5G0Fe8Gg/duJmUH7vqGIZupTTVzIBMbFVPBMJqprT 213 | MStDhaUL0JiAz0ZgTeNLRIBZWV9mY4PG3rZtbV0BZGR1ipAq9xfgqJcURCcKl/ZT 214 | UMtzvgk8s5hYkIJX0ZL3qsfdM4BMgIFhHq/GisQKbbu9kWldBrxQylOwa6r0m3Jv 215 | KJX2ViDSORndaCz2sppmVx5HDHnj+Bw381yawphnpumP3BJK4iof//uYKvfdc4RC 216 | y2EXL8PYPsT5DMB0jaBt92ytR5sLhn8Sl9Hk0buN4IjrYPISrdhS45xQXUqxcp9O 217 | 9hcU+rSaQyZ45cj+VlWhKq8MDvGvaAONBFSEh01mnUwoJObsAZNVFVtuOkwAli0F 218 | kGouMycQY1BGscpdC516Nya361Hk/ICyby2Y0BJrrVGaSM6poXH9yEjglzAdtSDb 219 | Cvhn/zlAI5ltm4Nv2qTgYBDns5JRGVhBym6RbbZ1C/KfCgn0hOxiw3N7AN4d0K5n 220 | LI6p7U9RnNVbWgbqsuoxBtkCAwEAAQ== 221 | -----END PUBLIC KEY----- 222 | -----BEGIN PUBLIC KEY----- 223 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqIh8FTj9DIgI8DAoCBh+ 224 | 6UXOfaWkvNaGZx2GwXl4WDAa/ZSE5/8ofg/6V59bmk9yry57UR4F+blscBvE4g3U 225 | dTvWJOBRD900l21vwpDLKzZguyGOCmKwJu3vCnAQKzBRXW5sDgvO67GeU6kpaic9 226 | LYPYnYaoxCRTYTZu0wy72rW5G0Fe8Gg/duJmUH7vqGIZupTTVzIBMbFVPBMJqprT 227 | MStDhaUL0JiAz0ZgTeNLRIBZWV9mY4PG3rZtbV0BZGR1ipAq9xfgqJcURCcKl/ZT 228 | UMtzvgk8s5hYkIJX0ZL3qsfdM4BMgIFhHq/GisQKbbu9kWldBrxQylOwa6r0m3Jv 229 | KJX2ViDSORndaCz2sppmVx5HDHnj+Bw381yawphnpumP3BJK4iof//uYKvfdc4RC 230 | y2EXL8PYPsT5DMB0jaBt92ytR5sLhn8Sl9Hk0buN4IjrYPISrdhS45xQXUqxcp9O 231 | 9hcU+rSaQyZ45cj+VlWhKq8MDvGvaAONBFSEh01mnUwoJObsAZNVFVtuOkwAli0F 232 | kGouMycQY1BGscpdC516Nya361Hk/ICyby2Y0BJrrVGaSM6poXH9yEjglzAdtSDb 233 | Cvhn/zlAI5ltm4Nv2qTgYBDns5JRGVhBym6RbbZ1C/KfCgn0hOxiw3N7AN4d0K5n 234 | LI6p7U9RnNVbWgbqsuoxBtkCAwEAAQ== 235 | -----END PUBLIC KEY----- 236 | ... that's all folks! 237 | --------------------------------------------------------------------------------