Linux 中對升級程式進行數字認證
最近專案中對升級的程式需要做認證,確保升級的資料包是本專案的升級程式包。
數字簽名:對某個資料塊的簽名,就是計算資料塊的Hash值,然後使用私鑰對hash值進行加密,結果就叫數字簽名,Hash值就是資料塊的數字指紋。
簽名驗證:資料接收者拿到原始資料塊與數字簽名後,接受者也會使用相同的Hash演算法得到Hash值,然後使用公鑰解密得到原始的資料指紋,比較2個值,就可以判定資料塊簽名之後有沒有被篡改。
Hash演算法常見的有:MD5,SHA,雜湊演算法也類似摘要演算法,是一個單向的雜湊函式,它解決在某一特定時間內,無法查詢經Hash操作後生成特定HASH值的原資訊塊,雜湊演算法輸入一個長度不固定的資訊塊,返回一個固定長度的結果。
步驟:
1.將生產的程式使用MD5算出一個MD5值,將此值使用RSA私鑰進行加密,得到數字簽名。將此值附加在升級程式後面。
2.升級程式時,將升級程式和數字簽名一起傳送給需要升級的裝置。
3.裝置收到後,對升級程式計算出一個MD5值。
4.將收到的數字簽名使用RSA的公鑰進行解密,得到接收到的MD5值。
5.如果兩個MD5相等,則認證通過(程序升級成功),否則認證失敗(程序升級失敗)。
加密演算法採用RSA,以下是轉載的RSA C語言的實現博文,經過實踐,修改了幾處錯誤。感謝博主的分享。
PEM檔案有以下格式
PEM私鑰檔案格式
-----BEGIN RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----
生成該金鑰的Linux命令:openssl genrsa -out privateKey.pem 1024
讀取該金鑰的Linux Openssl API 函式檔案讀取:
RSA *PEM_read_RSAPrivateKey(FILE *fp, RSA **rsa, pem_password_cb *cb, void *u)
讀取該金鑰的Linux Openssl API 函式記憶體讀取:
RSA *PEM_read_bio_RSAPrivateKey(BIO *bio, RSA **rsa, pem_password_cb *cb, void *u)
PEM私鑰檔案格式(經過口令加密)
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,FCD22C6C17CF034C
-----END RSA PRIVATE KEY-----
生成該金鑰的Linux命令:openssl genrsa -des3 -out privateKey.pem 1024
enter後會要求輸入口令(最少四位)
讀取該金鑰的Linux Openssl API 函式檔案讀取:
RSA *PEM_read_RSAPrivateKey(FILE *fp, RSA **rsa, pem_password_cb *cb, void *u)
讀取該金鑰的Linux Openssl API 函式記憶體讀取:
RSA *PEM_read_bio_RSAPrivateKey(BIO *bio, RSA **rsa, pem_password_cb *cb, void *u)
PEM公鑰檔案格式
-----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----
生成該金鑰的Linux命令:openssl rsa -in privateKey.pem -pubout -out publicKey.pem
讀取該金鑰的Linux Openssl API 函式檔案讀取:
RSA *PEM_read_RSA_PUBKEY(FILE *fp, RSA **rsa, pem_password_cb *cb, void *u)
讀取該金鑰的Linux Openssl API 函式記憶體讀取:
RSA *PEM_read_bio_PUBKEY(BIO *bio, RSA **rsa, pem_password_cb *cb, void *u)
PEM RSAPublicKey公鑰檔案格式
-----BEGIN RSA PUBLIC KEY-----
-----END RSA PUBLIC KEY-----
生成該金鑰的Linux命令:openssl rsa -in privateKey.pem -RSAPublicKey_out -out publicKey.pem
讀取該金鑰的Linux Openssl API 函式檔案讀取:
RSA *PEM_read_RSAPublicKey(FILE *fp, RSA **rsa, pem_password_cb *cb, void *u)
讀取該金鑰的Linux Openssl API 函式記憶體讀取:
RSA *PEM_read_bio_RSAPublicKey(BIO *bio, RSA **rsa, pem_password_cb *cb, void *u)
RSA加密API
int RSA_public_encrypt(int flen, unsigned char *from, unsigned char *to, RSA *rsa, int padding)
引數說明:
flen: 加密資訊的長度
from: 要加密資訊
to: 加密後的資訊
padding: 填充方式( RSA_PKCS1_PADDING ,RSA_PKCS1_OAEP_PADDING,RSA_SSLV23_PADDING,RSA_NO_PADDING)
(注:flen會根據不同的填充方式大小會有變化參考)
RSA解密API
int RSA_private_decrypt(int flen, unsigned char *from, unsigned char *to, RSA *rsa, int padding)
引數說明:
flen: 解密密文的長度
from: 要解密資訊
to: 解密後的資訊
padding: 填充方式( RSA_PKCS1_PADDING ,RSA_PKCS1_OAEP_PADDING,RSA_SSLV23_PADDING,RSA_NO_PADDING)
(注:flen填寫的是金鑰長度可用RSA_size(rsa)函式得到)
RSA程式設計示例(PEM檔案方式)
#include <openssl/rsa.h>
#include <openssl/pem.h>
#define PUBLICKEY "publicKey.pem"
#define PRIVATEKEY "privateKey.pem"
#define PASS "8888" //口令
int main(int argc, char *argv[])
{
FILE *fp = NULL;
RSA *publicRsa = NULL;
RSA *privateRsa = NULL;
if ((fp = fopen(PUBLICKEY, "r")) == NULL)
{
printf("public key path error\n");
return -1;
}
if ((publicRsa = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL)) == NULL)
{
printf("PEM_read_RSA_PUBKEY error\n");
return -1;
}
fclose(fp);
if ((fp = fopen(PRIVATEKEY, "r")) == NULL)
{
printf("private key path error\n");
return -1;
}
OpenSSL_add_all_algorithms();//金鑰有經過口令加密需要這個函式
if ((privateRsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, (char *)PASS)) == NULL)
{
printf("PEM_read_RSAPrivateKey error\n");
return NULL;
}
fclose(fp);
unsigned char *source = (unsigned char *)"123456789";
int rsa_len = RSA_size(publicRsa);
unsigned char *encryptMsg = (unsigned char *)malloc(rsa_len);
memset(encryptMsg, 0, rsa_len);
int len = strlen(source );
if (RSA_public_encrypt(len, source, encryptMsg, publicRsa, RSA_PKCS1_PADDING) < 0)
printf("RSA_public_encrypt error\n");
else
{
rsa_len = RSA_size(privateRsa);
unsigned char *decryptMsg = (unsigned char *)malloc(rsa_len);
memset(decryptMsg, 0, rsa_len);
int mun = RSA_private_decrypt(rsa_len, encryptMsg, decryptMsg, privateRsa, RSA_PKCS1_PADDING);
//解密後的長度 mun
if ( mun < 0)
printf("RSA_private_decrypt error\n");
else
printf("RSA_private_decrypt %s\n", decryptMsg);
}
RSA_free(publicRsa);
RSA_free(privateRsa);
return 0;
}
RSA程式設計示例(PEM記憶體方式)
#include <cstdio>
#include <cstring>
#include <openssl/rsa.h>
#include <openssl/pem.h>
const char *publicKey = "-----BEGIN PUBLIC KEY-----\n\
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDnal1HozHfmZ3B1TITmbjCNKOs\n\
49S+urgJ2P0/T36qN5w+r1jGhZKr54QDI5oXEk+9arlKxDW8kufwGjaTV3i3hyGS\n\
jYv4wNXhPeZAyAQ1vlloLMT6oA0PKe9/l8+mAr1QPEW9fMixAc/0UzPVospjkpfr\n\
YULcrKcH2Oaou5DZ0QIDAQAB\n\
-----END PUBLIC KEY-----";
const char *privateKey = "-----BEGIN RSA PRIVATE KEY-----\n\
Proc-Type: 4,ENCRYPTED\n\
DEK-Info: DES-EDE3-CBC,DF3BD9835CA1186D\n\
\n\
pomWi9/hjscwDDzH5CEpcj8nCumOQpl/2Gk2YynA47qfhxt12glNjgWl5Eaevk2L\n\
bG1t85sPqEvYxAe+ZxZdP6fot+sAg4SUUwSvBMwa7s3XjVhHjf/+hOIjb0skHvp/\n\
p0eOoUgytX7FrNNYEpUFI+eiPob79fgQMq/rypGJ//G6GXLMYixWw2+PyPa1x2PQ\n\
WdBaTpZK3gmDqmu6jR3ieKOahVVO4fEGB5etvB5i1aAh0mT4Wu+ejv2LgIRr2xor\n\
r8LkQZvI/TryZ0sNLe7LlC1bz/Hw8hLBDPprhWaUcSEk6MMgh3LKA2y/pGpFdIYN\n\
Ncj/c+YqEsO+I0KOtPQ1fXlXd1hH1H1rkJxuaNanF0UInUuupV3fP+7cvmfyHM4m\n\
aix8ROt1/Ghau41JDZGYmwk2qgKjUw4zz3eYOMQKl6row3pzhDxbvoMp0Qvfje1J\n\
RYpKMy8skG9pY1l4i1CC98aESC2a7FzjUNcY3f5Jt+QznO15xXxxuJZ8+xNqtIh2\n\
U348rlrQ8OxS1YBJCr+wjesdBdQAiY6X1YB9tljPs7AhlTLo78pHtQac521xOA8j\n\
IcbfkuTIrMIwYBOtM6SJHkB1TgPdPWx+haEy79Ct2yDnvpPqOiFz8i8TG8AQY53l\n\
5xKxxJ9CmPqw+Ua3DAWPaxAMaJFteRbl5Lv/2MvxV9Mu3T0W4B3ij+Gg5aw81v5Y\n\
KTH2KxruYAF5Q70QG8CAR8Vkvdczw940y8nb9pvcixmqYcaaeM9DLaTbycn/AeCt\n\
3UM0R0vvu039Ix5uhXUtVMjhTeUnvNObwEcKM8Grv1oPV3zmTJ5hJg==\n\
-----END RSA PRIVATE KEY-----";
#define PASS "8888" //口令
int main(int argc, char *argv[])
{
BIO *bio = NULL;
RSA *publicRsa = NULL;
RSA *privateRsa = NULL;
if ((bio = BIO_new_mem_buf((void *)publicKey, -1)) == NULL)
{
printf("BIO_new_mem_buf publicKey error\n");
return -1;
}
if ((publicRsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL)) == NULL)
{
printf("PEM_read_bio_RSA_PUBKEY error\n");
return -1;
}
BIO_free_all(bio);
if ((bio = BIO_new_mem_buf((void *)privateKey, -1)) == NULL)
{
printf("BIO_new_mem_buf privateKey error\n");
return -1;
}
OpenSSL_add_all_algorithms();//金鑰有經過口令加密需要這個函式
if ((privateRsa = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, (char *)PASS)) == NULL)
{
printf("PEM_read_RSAPrivateKey error\n");
return NULL;
}
BIO_free_all(bio);
unsigned char *source = (unsigned char *)"123456789";
int rsa_len = RSA_size(publicRsa);
unsigned char *encryptMsg = (unsigned char *)malloc(rsa_len);
memset(encryptMsg, 0, rsa_len);
int len = strlen(source );
if (RSA_public_encrypt(len, source, encryptMsg, publicRsa, RSA_PKCS1_PADDING) < 0)
printf("RSA_public_encrypt error\n");
else
{
rsa_len = RSA_size(privateRsa);
unsigned char *decryptMsg = (unsigned char *)malloc(rsa_len);
memset(decryptMsg, 0, rsa_len);
int mun = RSA_private_decrypt(rsa_len, encryptMsg, decryptMsg, privateRsa, RSA_PKCS1_PADDING);
//mun:解密後的明文的長度
if ( mun < 0)
printf("RSA_private_decrypt error\n");
else
printf("RSA_private_decrypt %s\n", decryptMsg);
}
RSA_free(publicRsa);
RSA_free(privateRsa);
return 0;
}
RSA程式設計示例(PEM檔案方式多執行緒測試樣例)
#include <cstdio>
#include <cstring>
#include <pthread.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#define PRIKEY "prikey.pem"
#define PUBKEY "pubkey.pem"
RSA *publicRsa = NULL;
RSA *privateRsa = NULL;
void* rsaThreadOne(void* param)
{
unsigned char *source = (unsigned char *)param;
int rsa_len = RSA_size(publicRsa);
unsigned char *encryptMsg = (unsigned char *)malloc(rsa_len);
memset(encryptMsg, 0, rsa_len);
int len = rsa_len - 11;
if (RSA_public_encrypt(len, source, encryptMsg, publicRsa, RSA_PKCS1_PADDING) < 0)
{
printf("rsaThreadOne RSA_public_encrypt error\n");
return 0;
}
rsa_len = RSA_size(privateRsa);
unsigned char *decryptMsg = (unsigned char *)malloc(rsa_len);
memset(decryptMsg, 0, rsa_len);
int mun = RSA_private_decrypt(rsa_len, encryptMsg, decryptMsg, privateRsa, RSA_PKCS1_PADDING);
if ( mun < 0)
{
printf("rsaThreadOne RSA_private_decrypt error\n");
return 0;
}
else
{
printf("rsaThreadOne %s\n", decryptMsg);
}
return 0;
}
void* rsaThreadTwo(void* param)
{
unsigned char *source = (unsigned char *)param;
int rsa_len = RSA_size(publicRsa);
unsigned char *encryptMsg = (unsigned char *)malloc(rsa_len);
memset(encryptMsg, 0, rsa_len);
int len = rsa_len - 11;
if (RSA_public_encrypt(len, source, encryptMsg, publicRsa, RSA_PKCS1_PADDING) < 0)
{
printf("rsaThreadTwo RSA_public_encrypt error\n");
return 0;
}
rsa_len = RSA_size(privateRsa);
unsigned char *decryptMsg = (unsigned char *)malloc(rsa_len);
memset(decryptMsg, 0, rsa_len);
int mun = RSA_private_decrypt(rsa_len, encryptMsg, decryptMsg, privateRsa, RSA_PKCS1_PADDING);
if ( mun < 0)
{
printf("rsaThreadTwo RSA_private_decrypt error\n");
return 0;
}
else
{
printf("rsaThreadTwo %s\n", decryptMsg);
}
return 0;
}
int main(int argc, char *argv[])
{
FILE *fp = NULL;
if ((fp = fopen(PUBKEY, "r")) == NULL)
{
printf("pubkey_path error\n");
return -1;
}
if ((publicRsa = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL)) == NULL)
{
printf("PEM_read_RSA_PUBKEY error\n");
return -1;
}
fclose(fp);
if ((fp = fopen(PRIKEY, "r")) == NULL)
{
printf("prikey_path error\n");
return -1;
}
OpenSSL_add_all_algorithms();//金鑰有經過口令加密需要這個函式
if ((privateRsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, (char *)"8888")) == NULL)
{
printf("PEM_read_RSAPrivateKey error\n");
return NULL;
}
fclose(fp);
pthread_t tid1 ;
pthread_t tid2 ;
pthread_create(&tid1, NULL, rsaThreadOne, (void *)"123456789");
pthread_create(&tid2, NULL, rsaThreadTwo, (void *)"987654321");
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}
注:編譯時需加上庫
gcc -I../include rsatest.c -L . -lcrypto -lssl
相應參考文章、文件
---------------------
作者:aqlick12
來源:CSDN
原文:https://blog.csdn.net/aqlick12/article/details/78480505
版權宣告:本文為博主原創文章,轉載請附上博文連結!