为了尽可能让文章简单,我将上面的代码进行了一定的精简和调整,只实现私钥加密的功能,并对外部使用的变量做了清理操作,减少了原始代码中内存溢出的问题,其他函数实现类似,在此就不多赘述。
考虑到 RSA 加密后的内容可读性不高,于是额外引入了一个简单的 Base64 编码实现。
#include#include #include #include #include #include #include #include #include char privateKey[] = "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" "MIIJrTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQICTqoISmC8M0CAggA\n" ... ... "onL8DKhku9s/5NB+eEVC3v4JubSfph0GEiVemMIQxMI2\n" "-----END ENCRYPTED PRIVATE KEY-----\n"; char *passphrase = "soulteary.com"; int encrypt(unsigned char *data, int data_len, unsigned char *encrypted) { BIO *keybio; keybio = BIO_new_mem_buf(privateKey, -1); RSA *rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, passphrase); int ret_len = RSA_private_encrypt(data_len, data, encrypted, rsa, RSA_PKCS1_PADDING); BIO_free(keybio); RSA_free(rsa); return ret_len; } char *base64(const unsigned char *input, int length) { BIO *bmem, *b64; BUF_MEM *bptr; b64 = BIO_new(BIO_f_base64()); bmem = BIO_new(BIO_s_mem()); b64 = BIO_push(b64, bmem); BIO_write(b64, input, length); BIO_flush(b64); BIO_get_mem_ptr(b64, &bptr); char *buff = (char *)malloc(bptr->length); memcpy(buff, bptr->data, bptr->length - 1); buff[bptr->length - 1] = 0; BIO_free_all(b64); return buff; } int main() { char raw[2048 / 8] = "{\"key\": \"val\"}"; unsigned char encrypted[4098] = {}; int encrypted_length = encrypt(raw, strlen(raw), encrypted); if (encrypted_length == -1) { printf("Encrypt failed"); exit(0); } char *result = base64(encrypted, encrypted_length); printf("%s\n", result); exit(0); }
为了更方便的测试功能,将上面的内容保存为 encrypt.c,然后编写一个 Dockerfile 用于基础计算部分的编译测试(这里需要注意编译顺序,避免编译不通过):
FROM alpine:3.13 AS Builder RUN cat /etc/apk/repositories | sed -e "s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/" | tee /etc/apk/repositories RUN apk add openssl-dev libressl-dev gcc g++ COPY ./encrypt.c / RUN cd / && \ gcc encrypt.c -lssl -lcrypto -o encrypt && \ cp encrypt /bin/
使用 docker build -t test . 构建一个临时的容器,然后使用 docker run –rm -it test encrypt 测试程序,会得到类似下面的结果。
rFKPHoJPR4iExWgx6EFzLVA4uISNyxYDtmOlGT+e6SBy9SPDve4o5YNzSbX1grnj bCFwvp80SwJzs1yaFzChDxo7HTdV3hK3syba+8zHw05FBeuw4/q8zn4e+KAv5QjE KzrQvMlzE1XsPrbI+IjJpqGIrpy57VBVr8CpmT/RajqZ42fy/cgn429i3NJhlckW vVPbY7x3vcXC/5FcRwR9hqPJ2qVXulVH/SxQ422bmLigFHwnWjT0qnDVTvgQFeQd 1edmJzPgbhycmGPvCdjRvN80eEX8lp3Oz92uACXfeReab26R0vhgGysPv3w97vdN TPmt9l1eyeWSnYR/gWU9HSM7FbAJyvKLp5h07X4AYyf2uDl7DxWIJp+ZG/IxzCzt 2IzKN2siq2bqvJEqR7+wDS2ttgCzD2ogmMMiQFgAa1yDruRasSJbV408n7STmE6h Z6klL8C+zXwgOLYnDi7bNVUhz6BkqHrt+utKql6zr0lKdOywwqElOQJeostBknH5 OCcoazuu+ZOOeAT3DoRLRVqHp/v75aIW9nYJmNjCqgonYrw9flKezh04nlSCjDaF DYJKVUSsL0mAhGMxfcVWrEzWOTAIOvg6U3vshSpDKk3KCaRhamWxCMVItfBNeeHL T8ZYF0tHz/cLkBm1wSNOuTxaGQRD/ZH0lSQGP8Aq4x4=
在验证完毕基础功能可行后,我们来看看怎么将它编写为一个 Nginx 可以调用的模块。