From f06f5dde2f1207bb51e3d11bfab2911a0ad398dd Mon Sep 17 00:00:00 2001 From: loki Date: Thu, 23 Jan 2020 16:00:44 +0100 Subject: [PATCH] Generate Private Key and Certificate if none exist --- assets/demoCA/cacert.pem | 22 ------------ assets/demoCA/cakey.pem | 28 --------------- sunshine/config.cpp | 6 ++-- sunshine/crypto.cpp | 65 ++++++++++++++++++++++++++++++++++ sunshine/crypto.h | 7 ++++ sunshine/nvhttp.cpp | 75 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 150 insertions(+), 53 deletions(-) delete mode 100644 assets/demoCA/cacert.pem delete mode 100644 assets/demoCA/cakey.pem diff --git a/assets/demoCA/cacert.pem b/assets/demoCA/cacert.pem deleted file mode 100644 index c4b79cc9..00000000 --- a/assets/demoCA/cacert.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDsTCCApmgAwIBAgIULnRRHDUzdg4a9dwWi0/yV5LADLIwDQYJKoZIhvcNAQEL -BQAwaDELMAkGA1UEBhMCTkwxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM -GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEhMB8GCSqGSIb3DQEJARYSbG9raUBm -YWtlZW1haWwuY29tMB4XDTE5MDYwMzEwMzY0N1oXDTI5MDUzMTEwMzY0N1owaDEL -MAkGA1UEBhMCTkwxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVy -bmV0IFdpZGdpdHMgUHR5IEx0ZDEhMB8GCSqGSIb3DQEJARYSbG9raUBmYWtlZW1h -aWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4cyNQ7uH6tAE -EAD99oxR1CVYlOgRDEUqJCjkpIoVF5uE3/kfuvEJ+l7/WL51lqbq35uL5ta7EbJH -BvVyNH00FN2D/Yl0n/X+TlhRP88MS5F0d0rwyQEAh4wEGcUhdSoW9TfybmLaHeCC -bZlxC3kBWxr0e6YDlV9deM6j+OjCerQwkiiGwgE9dVrVCj1dLyzBhWnYGpYBvY+3 -6kEy0Vmf2spGCB6meCAMrAMz75fUDuk8YRF0umb+SLA44AB/U6d6GXU2EjpTuPww -OMkUr8EmdbAI3l1tmWJTAkhFQ7681AyIWYOspc1biXZdBrvNBTV8kbDGlomNj19V -QhhN44d4ywIDAQABo1MwUTAdBgNVHQ4EFgQUFRktN33zyW4MR9Cy1Vcn+B+EdYAw -HwYDVR0jBBgwFoAUFRktN33zyW4MR9Cy1Vcn+B+EdYAwDwYDVR0TAQH/BAUwAwEB -/zANBgkqhkiG9w0BAQsFAAOCAQEAkpU+ALElNz+5jOnVAPyYXYsJKfevmKK9uK4v -V+l7GrFLIEhC2qr26S8Nd2pLkrgesCD4xfoOONiVOceU5igh9acFA3+NyOSFLdRN -bSdy0jvCuoiK46ieDAagQtdt0G7HGV4u+jWz0jaKUQI9zJqznOHdJV6RZFIqTYLG -KHnGP+mtXjW3E1djU9vFreYcB6UY+Ai1KB33dnBK9Es2fIQhikKZUPTh6BYsRZT6 -U7c6fh+01fRhRPo/SCFmY993857NtoOHMeP0M2V65CG4VjpAPR0msChVQVv7csca -TvBvB23dFRTLbo5PUSWC9bhBrMjzJ7yylt1CNBBHv7ycw8yIPw== ------END CERTIFICATE----- diff --git a/assets/demoCA/cakey.pem b/assets/demoCA/cakey.pem deleted file mode 100644 index b793eb26..00000000 --- a/assets/demoCA/cakey.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDhzI1Du4fq0AQQ -AP32jFHUJViU6BEMRSokKOSkihUXm4Tf+R+68Qn6Xv9YvnWWpurfm4vm1rsRskcG -9XI0fTQU3YP9iXSf9f5OWFE/zwxLkXR3SvDJAQCHjAQZxSF1Khb1N/JuYtod4IJt -mXELeQFbGvR7pgOVX114zqP46MJ6tDCSKIbCAT11WtUKPV0vLMGFadgalgG9j7fq -QTLRWZ/aykYIHqZ4IAysAzPvl9QO6TxhEXS6Zv5IsDjgAH9Tp3oZdTYSOlO4/DA4 -yRSvwSZ1sAjeXW2ZYlMCSEVDvrzUDIhZg6ylzVuJdl0Gu80FNXyRsMaWiY2PX1VC -GE3jh3jLAgMBAAECggEAdnHgoGkc8RXRK7v5fH0653f3sZTSbIdThchVt+IfElUo -LHz4Ig4S191BQQIXmMFSb52ek6aMVsoX7BSQpewPh+pzNGoIXWiiz+IQLNKldnaE -i5cqG6aE6pWOCR6ZYGaFyHhimXkNRaLhiDB3VjdReML5AGujcZWm6Jos9YLTkZ07 -pYXs2S+/5oNbfDdAE8dgdD7vD9lNGrbtmJ9+J+VvgPOYM/4LaEAfZU0ALDm2Hl3n -CqkZCp+eWbQK3MC2+6Y4yS+jo7e5/nKX1De9StkX6KumAEIpxkHPZF2EnWXW64oD -k40tKXT+oMXF1RLb4scnv+J+uR4bdl+Xq/VTzKrtsQKBgQDyoOjrv+g5l0s0KKDJ -qPkLQNRCJpzU7Km3CjPuk8DOlmBCr4KUeVfjbYPxQQMkeNL9zFz/QYBmabTCE6Ih -E8BbzT/RQzVPSOOh//Hh4eN4umrYaOIkY4Bv2R2X3H0ELFq+G7pKdvTAkusGHcWu -OAUb2HO5rTiRYq8DvHy8b+qH7wKBgQDuPjR+DCVtkBXmBNf0FZkl7OW/EUQBq4+T -WoaYleq8Gd7ubJgz2Gud+0+L41VjFD9W3hkJAb/3wkASseQ+2hk3Sv/BzpAIdS9e -tN/xxp/8NK7tJGL63m6tAfX+Hi/kSDEp45Zp3PoOD08MEK0yIrpf7S8uJ++u49K4 -tKXkGCIg5QKBgFujvPW2AQ8nfqcPpVMleBLxBHqLvPaLALr6poy4z7z3fRoS0j4j -6rcimRAZHwe6fu6PLpzWb5m+2R/obHcTz7acujreqJbuj9OTKRfIyrLBrjNYwfk+ -f7c/CPdftvRJkGh3bpBLh7vogc5Ilm5sCDnxMhxyOYhn/nRpz68YkjuPAoGBAOQX -6DfZtyfLcDvV3U/SMdsOkPO6OwsCTya73+tMdP18I2TP0XSpunb5ebIrh7+hTfcE -EqH96+XwM1nyuNy4ALZgdrb95gZC84RP1axsBxX29pcSZDVdKkc3fmW6Tw3XVEKP -o51dNIarf3nEqZ07hIZ81dPx5lbhxgiS49SaimpFAoGBAKamKZFAfUHlaHV/Na1C -3SZji7PaDSj1EFmRkCySK9VqD7Tbh1abrpC2ImdhYHn5TcQQE2eidB+F0Nf6IhKN -upBTofg0ebaslo+BYAqAsRKnUQGDToGIIIdXJ6DnO3wxWu9GY4nKdl3jxqAv2A2x -d8SETw4wqlFFRO33opycuFS5 ------END PRIVATE KEY----- diff --git a/sunshine/config.cpp b/sunshine/config.cpp index 429b88a2..f16d33b3 100644 --- a/sunshine/config.cpp +++ b/sunshine/config.cpp @@ -8,9 +8,9 @@ #include "utility.h" #include "config.h" -#define CA_DIR SUNSHINE_ASSETS_DIR "/demoCA" -#define PRIVATE_KEY_FILE CA_DIR "/cakey.pem" -#define CERTIFICATE_FILE CA_DIR "/cacert.pem" +#define CA_DIR "credentials" +#define PRIVATE_KEY_FILE CA_DIR "/cakey.pem" +#define CERTIFICATE_FILE CA_DIR "/cacert.pem" #define APPS_JSON_PATH SUNSHINE_ASSETS_DIR "/" APPS_JSON namespace config { diff --git a/sunshine/crypto.cpp b/sunshine/crypto.cpp index 7a1ac7b7..70a98289 100644 --- a/sunshine/crypto.cpp +++ b/sunshine/crypto.cpp @@ -5,6 +5,10 @@ #include #include "crypto.h" namespace crypto { +using big_num_t = util::safe_ptr; +//using rsa_t = util::safe_ptr; +using asn1_string_t = util::safe_ptr; + cert_chain_t::cert_chain_t() : _certs {}, _cert_ctx {X509_STORE_CTX_new() } {} void cert_chain_t::add(x509_t &&cert) { x509_store_t x509_store { X509_STORE_new() }; @@ -200,6 +204,25 @@ pkey_t pkey(const std::string_view &k) { return pkey_t { p }; } +std::string pem(x509_t &x509) { + bio_t bio { BIO_new(BIO_s_mem()) }; + + PEM_write_bio_X509(bio.get(), x509.get()); + BUF_MEM *mem_ptr; + BIO_get_mem_ptr(bio.get(), &mem_ptr); + + return { mem_ptr->data, mem_ptr->length }; +} + +std::string pem(pkey_t &pkey) { + bio_t bio { BIO_new(BIO_s_mem()) }; + + PEM_write_bio_PrivateKey(bio.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr); + BUF_MEM *mem_ptr; + BIO_get_mem_ptr(bio.get(), &mem_ptr); + + return { mem_ptr->data, mem_ptr->length }; +} std::string_view signature(const x509_t &x) { // X509_ALGOR *_ = nullptr; @@ -242,6 +265,48 @@ std::vector sign(const pkey_t &pkey, const std::string_view &data, cons return digest; } +creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits) { + x509_t x509 { X509_new() }; + pkey_t pkey { EVP_PKEY_new() }; + + big_num_t big_num { BN_new() }; + BN_set_word(big_num.get(), RSA_F4); + + auto rsa = RSA_new(); + RSA_generate_key_ex(rsa, key_bits, big_num.get(), nullptr); + EVP_PKEY_assign_RSA(pkey.get(), rsa); + + X509_set_version(x509.get(), 2); + ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 0); + + constexpr auto year = 60 * 60 * 24 * 365; +#if OPENSSL_VERSION_NUMBER < 0x10100000L + X509_gmtime_adj(X509_get_notBefore(x509.get()), 0); + X509_gmtime_adj(X509_get_notAfter(x509.get()), 20 * year); +#else + asn1_string_t not_before { ASN1_STRING_dup(X509_get0_notBefore(x509.get())) }; + asn1_string_t not_after { ASN1_STRING_dup(X509_get0_notAfter(x509.get())) }; + + X509_gmtime_adj(not_before.get(), 0); + X509_gmtime_adj(not_after.get(), 20 * year); + + X509_set1_notBefore(x509.get(), not_before.get()); + X509_set1_notAfter(x509.get(), not_after.get()); +#endif + + X509_set_pubkey(x509.get(), pkey.get()); + + auto name = X509_get_subject_name(x509.get()); + X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, + (const std::uint8_t*)cn.data(), cn.size(), + -1, 0); + + X509_set_issuer_name(x509.get(), name); + X509_sign(x509.get(), pkey.get(), EVP_sha256()); + + return { pem(x509), pem(pkey) }; +} + std::vector sign256(const pkey_t &pkey, const std::string_view &data) { return sign(pkey, data, EVP_sha256()); } diff --git a/sunshine/crypto.h b/sunshine/crypto.h index 22dfa42a..faf1fc1f 100644 --- a/sunshine/crypto.h +++ b/sunshine/crypto.h @@ -15,6 +15,10 @@ #include "utility.h" namespace crypto { +struct creds_t { + std::string x509; + std::string pkey; +}; constexpr std::size_t digest_size = 256; void md_ctx_destroy(EVP_MD_CTX *); @@ -35,10 +39,13 @@ aes_t gen_aes_key(const std::array &salt, const std::string_view &p x509_t x509(const std::string_view &x); pkey_t pkey(const std::string_view &k); +std::string pem(x509_t &x509); +std::string pem(pkey_t &pkey); std::vector sign256(const pkey_t &pkey, const std::string_view &data); bool verify256(const x509_t &x509, const std::string_view &data, const std::string_view &signature); +creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits); std::string_view signature(const x509_t &x); diff --git a/sunshine/nvhttp.cpp b/sunshine/nvhttp.cpp index f978519b..2138ab03 100644 --- a/sunshine/nvhttp.cpp +++ b/sunshine/nvhttp.cpp @@ -38,6 +38,7 @@ namespace fs = std::filesystem; namespace pt = boost::property_tree; std::string read_file(const char *path); +int write_file(const char *path, const std::string_view &contents); using https_server_t = SimpleWeb::Server; using http_server_t = SimpleWeb::Server; @@ -652,7 +653,69 @@ void appasset(resp_https_t response, req_https_t request) { response->write(SimpleWeb::StatusCode::success_ok, in); } +int create_creds(const std::string &pkey, const std::string &cert) { + fs::path pkey_path = pkey; + fs::path cert_path = cert; + + auto creds = crypto::gen_creds("Sunshine Gamestream Host"sv, 2048); + + auto pkey_dir = pkey_path; + auto cert_dir = cert_path; + pkey_dir.remove_filename(); + cert_dir.remove_filename(); + + std::error_code err_code{}; + fs::create_directories(pkey_dir, err_code); + if (err_code) { + BOOST_LOG(fatal) << "Couldn't create directory ["sv << pkey_dir << "] :"sv << err_code.message(); + return -1; + } + + fs::create_directories(cert_dir, err_code); + if (err_code) { + BOOST_LOG(fatal) << "Couldn't create directory ["sv << cert_dir << "] :"sv << err_code.message(); + return -1; + } + + if (write_file(pkey.c_str(), creds.pkey)) { + BOOST_LOG(fatal) << "Couldn't open ["sv << config::nvhttp.pkey << ']'; + return -1; + } + + if (write_file(cert.c_str(), creds.x509)) { + BOOST_LOG(fatal) << "Couldn't open ["sv << config::nvhttp.cert << ']'; + return -1; + } + + fs::permissions(pkey_path, + fs::perms::owner_read | fs::perms::owner_write, + fs::perm_options::replace, err_code); + + if (err_code) { + BOOST_LOG(fatal) << "Couldn't change permissions of ["sv << config::nvhttp.pkey << "] :"sv << err_code.message(); + return -1; + } + + fs::permissions(cert_path, + fs::perms::owner_read | fs::perms::group_read | fs::perms::others_read | fs::perms::owner_write, + fs::perm_options::replace, err_code); + + if (err_code) { + BOOST_LOG(fatal) << "Couldn't change permissions of ["sv << config::nvhttp.cert << "] :"sv << err_code.message(); + return -1; + } + + return 0; +} + void start(std::shared_ptr> shutdown_event) { + if(!fs::exists(config::nvhttp.pkey) || !fs::exists(config::nvhttp.cert)) { + if(create_creds(config::nvhttp.pkey, config::nvhttp.cert)) { + shutdown_event->raise(true); + return; + } + } + origin_pin_allowed = net::from_enum_string(config::nvhttp.origin_pin_allowed); load_state(); @@ -748,6 +811,18 @@ void start(std::shared_ptr> shutdown_event) { tcp.join(); } +int write_file(const char *path, const std::string_view &contents) { + std::ofstream out(path); + + if(!out.is_open()) { + return -1; + } + + out << contents; + + return 0; +} + std::string read_file(const char *path) { std::ifstream in(path);