
文章目录 [隐藏]
一、背景
公司有一个需求需要在windows平台上实现一个web小程序,对该web小程序有如下要求:
- 体积小,10M以下
- 无需安装,下载下来,鼠标双击即可使用
- 实现https加密和浏览器进行通信
二、技术选型
为了实现这个小程序,公司对技术做了如下选型:
- 使用C语言编写
- 使用mongoose框架
- 使用openssl库实现https加密
- ssl证书保存在字符串中,不存储在文件中。
注: ssl证书不存储在文件中,而存储在字符串中,是因为该小程序不需要安装,只有一个exe文件,不能有其他额外的文件。
三、技术实现
公司使用mongoose框架做webserver,mongoose非常轻量,github地址是:https://github.com/cesanta/mongoose,目前有5k多个星,比较稳定。
实现上比较麻烦的是ssl证书不存储在文件中,而是存储在字符串中。mongoose关于https的实现,都是基于证书为文件实现的,所以需要对相关代码进行修改。
下面直接上干货,修改的函数主要是mongoose.c文件中的mg_use_cert函数。
3.1 mg_user_cert函数默认加载ssl文件代码
static enum mg_ssl_if_result mg_use_cert(SSL_CTX *ctx, const char *cert,
const char *key,
const char **err_msg) {
if (key == NULL) key = cert;
if (cert == NULL || cert[0] == '\0' || key == NULL || key[0] == '\0') {
return MG_SSL_OK;
} else if (SSL_CTX_use_certificate_file(ctx, cert, 1) == 0) {
MG_SET_PTRPTR(err_msg, "Invalid SSL cert");
return MG_SSL_ERROR;
} else if (SSL_CTX_use_PrivateKey_file(ctx, key, 1) == 0) {
MG_SET_PTRPTR(err_msg, "Invalid SSL key");
return MG_SSL_ERROR;
} else if (SSL_CTX_use_certificate_chain_file(ctx, cert) == 0) {
MG_SET_PTRPTR(err_msg, "Invalid CA bundle");
return MG_SSL_ERROR;
} else {
SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
#if !MG_DISABLE_PFS && !defined(KR_VERSION)
BIO *bio = NULL;
DH *dh = NULL;
/* Try to read DH parameters from the cert/key file. */
bio = BIO_new_file(cert, "r");
if (bio != NULL) {
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
BIO_free(bio);
}
/*
* If there are no DH params in the file, fall back to hard-coded ones.
* Not ideal, but better than nothing.
*/
if (dh == NULL) {
bio = BIO_new_mem_buf((void *) mg_s_default_dh_params, -1);
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
BIO_free(bio);
}
if (dh != NULL) {
SSL_CTX_set_tmp_dh(ctx, dh);
SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
DH_free(dh);
}
#if OPENSSL_VERSION_NUMBER > 0x10002000L
SSL_CTX_set_ecdh_auto(ctx, 1);
#endif
#endif
}
return MG_SSL_OK;
}
C
3.2 修改mg_user_cert函数从字符串中加载ssl证书
直接上修改好的代码,主要思路是:
- 使用函数SSL_CTX_use_certificate 替换SSL_CTX_use_certificate_file
- 使用PEM_read_bio_RSAPrivateKey 替换SSL_CTX_use_PrivateKey_file.
- 使用X509_STORE_add_cert函数替换SSL_CTX_use_certificate_chain_file来添加cert证书
具体代码如下:
static int mg_use_cert(SSL_CTX *ctx, const char *cert, const char *key) {
if (ctx == NULL) {
return -1;
} else {
int ret = 0;
#if 0
if (SSL_CTX_use_certificate_file(ctx, cert, 1) == 0) {
return -2;
}
if (SSL_CTX_use_PrivateKey_file(ctx, key, 1) == 0) {
return -2;
}
#endif
/* 使用函数SSL_CTX_use_certificate 替换SSL_CTX_use_certificate_file
* 创建BIO结构,存储pem字符串内容,pem字符串内容存储在fangstarHelperPem字符数组中
* fangstarHelperPem的存储见第四节:字符串存储ssl证书
*/
BIO *cbio = BIO_new(BIO_s_mem());
BIO_puts(cbio, fangstarHelperPem);
X509* xCert = NULL;
xCert = PEM_read_bio_X509(cbio, NULL, 0, NULL);
ret = SSL_CTX_use_certificate(ctx, xCert);
BIO_free(cbio);
if (ret != 1) {
printf("Failed to use cretificate\n");
return -2;
}
/* 使用PEM_read_bio_RSAPrivateKey 替换SSL_CTX_use_PrivateKey_file.
* 创建BIO结构,存储key字符串内容,key字符串内容存储在fangstarHelperKey字符数组中
* fangstarHelperKey的存储见第下一节:字符串存储ssl证书
*/
BIO *kbio = BIO_new(BIO_s_mem());
BIO_puts(kbio, fangstarHelperKey);
RSA *rsa = PEM_read_bio_RSAPrivateKey(kbio, NULL, 0, NULL);
BIO_free(kbio);
ret = SSL_CTX_use_RSAPrivateKey(ctx, rsa);
if (ret != 1) {
printf("Failed to use RSA Private Key\n");
return -2;
}
if (!SSL_CTX_check_private_key(ctx)) {
printf("Failed to check RSA Private Key\n");
return -2;
}
#ifndef MG_DISABLE_PFS
BIO *bio = NULL;
DH *dh = NULL;
/* Try to read DH parameters from the cert/key file. */
//bio = BIO_new_file(cert, "r");
bio = BIO_new(BIO_s_mem());
BIO_puts(bio, fangstarHelperPem);
//bio = BIO_new_mem_buf(fangstarHelperPem, sizeof(fangstarHelperPem));
if (bio != NULL) {
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
BIO_free(bio);
}
/*
* If there are no DH params in the file, fall back to hard-coded ones.
* Not ideal, but better than nothing.
*/
if (dh == NULL) {
bio = BIO_new_mem_buf((void *) mg_s_default_dh_params, -1);
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
BIO_free(bio);
}
if (dh != NULL) {
SSL_CTX_set_tmp_dh(ctx, dh);
SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
DH_free(dh);
}
#endif
SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
//SSL_CTX_use_certificate_chain_file(ctx, cert);
/* 使用X509_STORE_add_cert函数替换SSL_CTX_use_certificate_chain_file来添加cert证书 */
BIO *chainBio = BIO_new(BIO_s_mem());
BIO_puts(chainBio, fangstarHelperPem);
X509* chainCert = NULL;
while (chainCert = PEM_read_bio_X509(chainBio, NULL, 0, NULL)) {
X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), chainCert);
}
BIO_free(chainBio);
return 0;
}
}
C
3.3 字符串存储ssl证书
本文只给出了ssl证书的一部分,详情如下
3.3.1 字符存储key
const char fangstarHelperKey[] =
"-----BEGIN RSA PRIVATE KEY-----\n"
"MIIEowIBAAKCAQEAnNRp0Ii1Rarc+0BIi5LPzVYbDd0lLoYLgjveTOfFq+0mH2/I\n"
"kLfqDG2rzEZ1NiVXvmtwsiqzejvmeWwraWpNDJVzyytCwevGiUZAlsZ4gXvUsdEO\n"
"2fL7QrOi+ve/AVaqEL7UuudVzh+pyVhZ1+fRRn4Ep2jLiEfJTyXwc6XWgSR5iqHg\n"
"gKHNNeYwirUPdCbIzcyOWEMghVe6O5Vyw6K8TlCuSd4YrxTcIJ6i4xO8jbly3y6N\n"
"XrRlp128ZmF2SO8PFDvF5y64k12031Zf+qhbJisdU7TJfqz+15CjAxBAK9ve33ff\n"
"PMMg/ple/Yxt5YdPl1cGp7G3k3DwfkvZPW+jlwIDAQABAoIBAEr5lZi3Mooa1Ehd\n"
"hBEN38BXsNlg5tymAqyBOJZlm2/FERuoUic1dXP5nk8rQ+/dzGPhc2AJ4AHIzzcv\n"
"SlW8Fxez/0aa6PBSanjIGCq8uXGVpKpZupLhgdNyk7ENbcgTCXy17ndoDvJa2s3C\n"
"-----END RSA PRIVATE KEY-----\n";
C
3.3.2 字符存储key
const char fangstarHelperPem[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIFjzCCBHegAwIBAgIQAQQoucU55ET7eK62KAW67DANBgkqhkiG9w0BAQsFADBu\n"
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
"d3cuZGlnaWNlcnQuY29tMS0wKwYDVQQDEyRFbmNyeXB0aW9uIEV2ZXJ5d2hlcmUg\n"
"RFYgVExTIENBIC0gRzEwHhcNMTgwOTA2MDAwMDAwWhcNMTkwOTA2MTIwMDAwWjAe\n"
"MRwwGgYDVQQDExNyZmlkLmUuZmFuZ3N0YXIubmV0MIIBIjANBgkqhkiG9w0BAQEF\n"
"AAOCAQ8AMIIBCgKCAQEAnNRp0Ii1Rarc+0BIi5LPzVYbDd0lLoYLgjveTOfFq+0m\n"
"H2/IkLfqDG2rzEZ1NiVXvmtwsiqzejvmeWwraWpNDJVzyytCwevGiUZAlsZ4gXvU\n"
"sdEO2fL7QrOi+ve/AVaqEL7UuudVzh+pyVhZ1+fRRn4Ep2jLiEfJTyXwc6XWgSR5\n"
"iqHggKHNNeYwirUPdCbIzcyOWEMghVe6O5Vyw6K8TlCuSd4YrxTcIJ6i4xO8jbly\n"
"QSr4yaGavtbuS2Ezievk0jBZ7v24VNtyiOIeFxoU7WDjM2g=\n"
"-----END CERTIFICATE-----\n"
"-----BEGIN CERTIFICATE-----\n"
"MIIEqjCCA5KgAwIBAgIQAnmsRYvBskWr+YBTzSybsTANBgkqhkiG9w0BAQsFADBh\n"
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n"
"QTAeFw0xNzExMjcxMjQ2MTBaFw0yNzExMjcxMjQ2MTBaMG4xCzAJBgNVBAYTAlVT\n"
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n"
"b20xLTArBgNVBAMTJEVuY3J5cHRpb24gRXZlcnl3aGVyZSBEViBUTFMgQ0EgLSBH\n"
"MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALPeP6wkab41dyQh6mKc\n"
"-----END CERTIFICATE-----\n";
C