/*
目录:
--------------------
1. 简介
2. 生成 ECDSA 密钥对
3. 签名
4. 校验
*/
/*
1. 简介
--------------------
对 PE 文件做 ECDSA 签名. 签名写入 PE 头部 DOS Stub代码后边. 使用的椭圆
曲线是 FIPS 186-2 中的 P-192. 签名长度不超过 56 字节,强度和 RSA 1024 相当.
*/
/*
2. 生成 ECDSA 密钥对
生成的私钥用 RC4 加密. 生成形式为 c 语言可用的形式.
*/
#include <stdio.h>
#include <openssl/crypto.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/ecdsa.h>
#include <openssl/engine.h>
#include <openssl/err.h>
#include <openssl/rc4.h>
#include <conio.h>
#define MAXPASS 64
#define PRIVKEY "static unsigned char privkey[%d] = {"
#define PUBKEY "static const unsigned char pubkey[%d] = {"
#define ENDKEY "/n};/n"
int main(int argc, char* argv[])
{
RC4_KEY rc4;
EC_KEY *ecdsa;
char passbuf[MAXPASS + 3] = {MAXPASS};
char *pass;
byte buf[1024];
byte *pp;
int i,len;
printf("Input password: ");
pass = _cgets(passbuf);
if (*pass == '/0' || passbuf[1] == 0)
{
printf("NULL password/n");
return -1;
}
RAND_seed(passbuf,sizeof(passbuf));
if ( (ecdsa = EC_KEY_new()) == NULL)
{
printf("EC_KEY_new/n");
return 0;
}
if ((ecdsa->group = EC_GROUP_new_by_nid(NID_X9_62_prime192v1)) == NULL)
{
printf("EC_GROUP_new_by_nid/n");
goto err;
}
if (!EC_KEY_generate_key(ecdsa))
{
printf("EC_KEY_generate_key error/n");
goto err;
}
/*
* 生成 RC4 加密的ECDSA私钥
*/
pp = buf;
len = i2d_ECPrivateKey(ecdsa,&pp);
if (!len)
{
goto err;
}
RC4_set_key(&rc4,strlen(pass),(byte*)pass);
RC4(&rc4,len,buf,buf);
printf(PRIVKEY,len);
for (i=0; i<len; i++)
{
if ( !(i % 8) )
printf("/n");
printf("0x%02X , ",buf[i]);
}
printf(ENDKEY);
/*
* 生成ECDSA公钥
*/
pp = buf;
len = ECPublicKey_get_octet_string(ecdsa,&pp);
if (!len)
{
goto err;
}
printf(PUBKEY,len);
for (i=0; i<len; i++)
{
if ( !(i % 8) )
printf("/n");
printf("0x%02X , ",buf[i]);
}
printf(ENDKEY);
err:
EC_KEY_free(ecdsa);
return 0;
}
/*
3. 签名
-------------------
签名时需要输入私钥密码 (最长 512 位).
*/
#include <openssl/crypto.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/ecdsa.h>
#include <openssl/engine.h>
#include <openssl/err.h>
#include <openssl/sha.h>
#include <openssl/rc4.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <io.h>
#include <windows.h>
#include <commdlg.h>
#include "resource.h"
typedef unsigned char byte;
#define MAXSIGLEN 128
#define MAXPASS 64
#define MAGIC_CHAR 0x27
static char password[MAXPASS];
static RC4_KEY key;
/*
* RC4 加密过的ECDSA私钥 ()
*/
static unsigned char privkey[] = {
0xBE , 0x5E , 0x1D , 0x8F , 0x79 , 0xBA , 0xC7 , 0x2B ,
0x32 , 0x9D , 0x4A , 0xFC , 0xE7 , 0x62 , 0x24 , 0x8C ,
0x84 , 0x88 , 0x32 , 0x6F , 0x4C , 0x5B , 0x32 , 0xEA ,
0x46 , 0x97 , 0x71 , 0x83 , 0x84 , 0x3D , 0x91 , 0x79 ,
0x4D , 0x17 , 0xB1 , 0x28 , 0xED , 0x39 , 0xBF , 0xDF ,
0xDD , 0xF2 , 0x93 , 0x88 , 0xBF , 0xFD , 0x4D , 0x02 ,
0xC7 , 0x1A , 0x22 , 0xAB , 0x41 , 0x41 , 0x9F , 0x63 ,
0x26 , 0x7E , 0x49 , 0x08 , 0x7F , 0xB0 , 0xE3 , 0xB9 ,
0xDB , 0xC4 , 0x04 , 0xE9 , 0x01 , 0xB8 , 0xC1 , 0x71 ,
0x93 , 0xC6 , 0xD4 , 0x83 , 0xB3 , 0x08 , 0x28 , 0x18 ,
0xE1 , 0x4C , 0xE1 , 0xD7 , 0x0C , 0x66 , 0x77 , 0x3E ,
0x65 , 0x2F , 0x12 , 0xD3 , 0x2F , 0xD4 , 0xC3 , 0xB4 ,
0x99 , 0xA2 , 0xF5 , 0x57 , 0x59 , 0xA1 , 0x20 , 0x81 ,
0xEB , 0x3F , 0xCF , 0x46 , 0xD2 , 0x45 , 0x71 , 0x06 ,
0x9C , 0x5F , 0x60 , 0x2E , 0x68 , 0xFB , 0x6C , 0xA4 ,
0xC8 , 0x94 , 0x72 , 0xA3 , 0x3B , 0xE0 , 0x10 , 0xB9 ,
0x1B , 0xAE , 0xFE , 0xD4 , 0x58 , 0x1C , 0xA9 , 0x80 ,
0x0F , 0xF0 , 0x55 , 0xDC , 0xC6 , 0x58 , 0xE0 , 0x5D ,
0x83 , 0xBB , 0x27 , 0x82 , 0x7E , 0xA1 , 0xF5 , 0x10 ,
0x2E , 0x18 , 0x9A , 0xDA , 0x61 , 0x3B , 0xD2 , 0xA2 ,
0xAB , 0x45 , 0x63 , 0xBD , 0x89 , 0xAD , 0xFB , 0x4E ,
0x2E , 0x6D , 0x23 , 0x4A , 0x55 , 0x57 , 0x28 , 0x1A ,
0xE5 , 0x5B , 0xC3 , 0x62 , 0x3B , 0x6F , 0xA5 , 0x96 ,
0x72 , 0xBD , 0x16 , 0x36 , 0xC4 , 0xB9 , 0x2B , 0x31 ,
0xE6 , 0x3B , 0x78 , 0xF0 , 0x2C , 0xBE , 0x4A , 0x70 ,
0xA0 , 0xA1 , 0x5E , 0x38 , 0x4A , 0x3F , 0xFE , 0x79 ,
0xD4 , 0xCF , 0x0C , 0x70 , 0x1B , 0xD5 , 0xE5 , 0x54 ,
0x52 , 0x44 , 0x1A , 0x47 , 0xFD , 0x59 , 0x81 , 0xBF ,
0xAE , 0x0C , 0xF4 , 0xDB , 0xC2 , 0x19 , 0xAC , 0xA9 ,
0x2F , 0x90 , 0x31 , 0xEC , 0xEA , 0xDD , 0xCE , 0x5F ,
0xA8 , 0xA7 , 0xEE , 0x79 , 0x1C , 0x20 , 0x95 , 0x93 ,
0x22 , 0x61 , 0xA2 , 0x41 , 0x40 , 0xCF , 0x26 , 0xF4 ,
0x73 , 0xFE , 0xD8 , 0x23 , 0x12 , 0x81 , 0x42 , 0x6D ,
0x32 , 0xC2 , 0xD0 , 0x64 , 0xF2 , 0x02 , 0x0A , 0x22 ,
0xFE , 0x20 , 0xB3 , 0x25 , 0x29 , 0x7B , 0x78 , 0xFA ,
0x0D , 0x9B , 0x95 , 0x61 , 0xF5 , 0x40 , 0x1B , 0x43 ,
0xEC , 0x7D , 0xEB , 0x61 ,
};
static const unsigned char dos[] = {
0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD,
0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x69, 0x50,
0x42, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x65, 0x64,
0x2E, 0x20, 0x0D, 0x0D, 0x0A, 0x24, MAGIC_CHAR,
};
/*
* ECDSA 签名
*/
static unsigned int sign(byte *sig,const byte *buf,int len)
{
unsigned int siglen = MAXSIGLEN;
EC_KEY *ecdsa = NULL;
unsigned char *pp = (unsigned char*)privkey;
RAND_seed(buf, len);
/*
* 解密私钥
*/
RC4(&key,sizeof(privkey),privkey,privkey);
ecdsa = d2i_ECPrivateKey(&ecdsa, (const unsigned char**)&pp, sizeof(privkey));
ZeroMemory(privkey,sizeof(privkey));
if ( ecdsa == NULL)
{
MessageBox(NULL,"Can't restore private key","Sign",MB_ICONERROR);
return 0;
}
if (!ECDSA_sign(0,buf, len, sig,&siglen,ecdsa))
{
MessageBox(NULL,"Sign error","Sign",MB_ICONERROR);
EC_KEY_free(ecdsa);
return 0;
}
EC_KEY_free(ecdsa);
return siglen;
}
static BOOL GetFileName(char *sFile)
{
OPENFILENAME ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.lpstrFile = sFile;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFilter = "EXE/DLL/OCX files/0*.exe;*.dll;*.ocx/0All/0*.*/0";
ofn.lpstrTitle = TEXT("Please Select a File to sign");
ofn.lpstrInitialDir = ".";
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_HIDEREADONLY;
return GetOpenFileName(&ofn);
}
INT_PTR CALLBACK DlgProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if(uMsg == WM_INITDIALOG)
{
SetFocus(GetDlgItem(hwndDlg,IDC_PASSWORD));
}
else if(uMsg == WM_COMMAND)
{
if( wParam == IDOK)
{
GetDlgItemText(hwndDlg,IDC_PASSWORD,password,MAXPASS);
if (password[0] == '/0')
{
MessageBeep(MB_ICONASTERISK);
SetFocus(GetDlgItem(hwndDlg,IDC_PASSWORD));
return TRUE;
}
else
{
EndDialog(hwndDlg,0);
}
}
}
else if( uMsg == WM_CLOSE )
{
EndDialog(hwndDlg,0);
}
return 0;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
int len;
byte sig[MAXSIGLEN];
unsigned int siglen;
int fd;
byte md[SHA_DIGEST_LENGTH];
char buf[1024];
SHA_CTX ctx;
char sFile[MAX_PATH] = {0};
/*
* 得到要签名的文件名
*/
if (!GetFileName(sFile))
{
return -1;
}
if ((fd = open(sFile,O_RDWR | O_BINARY)) < 0)
{
MessageBox(NULL,"Can't open file for read-write","Open",MB_ICONERROR);
return -1;
}
/*
* 检验文件
*/
if ( (len = read(fd,buf,0x40)) != 0x40 )
{
MessageBox(NULL,"Read error","Read",MB_ICONSTOP);
goto out;
}
if (buf[0] != 'M' || buf[1] != 'Z')
{
MessageBox(NULL,"Not a valid PE file!","Sign",MB_ICONSTOP);
goto out;
}
if ( *(unsigned int *)(buf + 0x3C) < 0xA0)
{
MessageBox(NULL,"No enough file space to write signature","Sign",MB_ICONSTOP);
goto out;
}
/*
* 计算 SHA1
*/
SHA1_Init(&ctx);
SHA1_Update(&ctx,buf,len);
lseek(fd,0xA0L,SEEK_SET);
while ( (len = read(fd,buf,sizeof(buf))) > 0)
{
SHA1_Update(&ctx,buf,len);
}
if (len == -1)
{
MessageBox(NULL,"Read error","Error",MB_ICONERROR);
goto out;
}
ZeroMemory(md,sizeof(md));
SHA1_Final(md,&ctx);
/*
* 输入解密口令
*/
ZeroMemory(password,MAXPASS);
DialogBox(hInstance,MAKEINTRESOURCE(IDD_PASSWORD),NULL,(DLGPROC)DlgProc);
if (password[0] == '/0')
{
MessageBox(NULL,"You must input password!","Error",MB_ICONSTOP);
goto out;
}
RC4_set_key(&key,strlen(password),(const byte*)password);
ZeroMemory(password,MAXPASS);
/*
* ECDSA sign
*/
{
siglen = sign(sig,md,SHA_DIGEST_LENGTH);
if (siglen)
{
/*
* Write
*/
lseek(fd,0x40L,SEEK_SET);
write(fd,dos,sizeof(dos));
write(fd,(byte*)&siglen,1);
write(fd,sig,siglen);
MessageBox(NULL,"File signed","Ok",MB_OK);
}
}
out:
close(fd);
return 0;
}
/*
4. 校验
-------------------
校验不需要输入密码.
*/
#include <openssl/crypto.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/ecdsa.h>
#include <openssl/engine.h>
#include <openssl/err.h>
#include <openssl/sha.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <io.h>
#include <windows.h>
#include <commdlg.h>
typedef unsigned char byte;
#define MAXSIGLEN 64
#define MAGIC_CHAR 0x27
/*
* ECDSA 公钥
*/
static const unsigned char pubkey[] = {
0x04 , 0xDA , 0x4C , 0x77 , 0xEB , 0x7A , 0x8C , 0x12 ,
0x87 , 0xF3 , 0x6C , 0x10 , 0x87 , 0xA1 , 0xB1 , 0x2D ,
0x96 , 0xE6 , 0x85 , 0xE0 , 0x40 , 0xD0 , 0x19 , 0x14 ,
0xE3 , 0x1F , 0xF0 , 0x1A , 0x76 , 0x8D , 0x3A , 0x5D ,
0x57 , 0x33 , 0xE6 , 0xD5 , 0x5D , 0x23 , 0x75 , 0x1E ,
0x54 , 0x60 , 0x67 , 0xC9 , 0xA9 , 0x60 , 0x74 , 0xC5 ,
0x3F ,
};
/*
* DOS stub 代码
*/
static const unsigned char dos[] = {
0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD,
0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x69, 0x50,
0x42, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x65, 0x64,
0x2E, 0x20, 0x0D, 0x0D, 0x0A, 0x24, MAGIC_CHAR,
};
/*
* 校验 ECDSA 签名
*/
static int verify(const byte *sig,int siglen,const byte *buf,int buflen)
{
int ret;
EC_KEY *pub = NULL;
byte *pp = (byte*)pubkey;
if ( (pub = EC_KEY_new()) == NULL)
{
return 0;
}
if ((pub->group = EC_GROUP_new_by_nid(NID_X9_62_prime192v1)) == NULL)
{
EC_KEY_free(pub);
return 0;
}
pub = ECPublicKey_set_octet_string(&pub,(const byte**)&pp,sizeof(pubkey));
if (pub == NULL)
{
EC_KEY_free(pub);
return 0;
}
ret = ECDSA_verify(0,(const byte*)buf, buflen, sig, siglen,pub);
EC_KEY_free(pub);
return ret == 1 ? 1 : 0;
}
static BOOL GetFileName(char *sFile)
{
OPENFILENAME ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.lpstrFile = sFile;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFilter = "EXE/DLL/OCX files/0*.exe;*.dll;*.ocx/0All/0*.*/0";
ofn.lpstrTitle = TEXT("Please Select a File to verify");
ofn.lpstrInitialDir = ".";
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_READONLY;
return GetOpenFileName(&ofn);
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
int len;
byte sig[MAXSIGLEN];
unsigned int siglen = 0xFF;
int fd;
byte md[SHA_DIGEST_LENGTH];
char buf[1024];
SHA_CTX ctx;
char sFile[MAX_PATH] = {0};
if (!GetFileName(sFile))
{
return -1;
}
if ((fd = open(sFile,O_RDONLY | O_BINARY)) < 0)
{
MessageBox(NULL,"Can't open file for read","Open",MB_ICONERROR);
return -1;
}
/*
* 读入签名并检查
*/
lseek(fd,0x40L,SEEK_SET);
if ( (len = read(fd,buf,0x60)) != 0x60)
{
MessageBox(NULL,"Read error","Read",MB_ICONSTOP);
close(fd);
return -1;
}
if (memcmp(buf,dos,sizeof(dos)) ||
(byte)buf[sizeof(dos) - 1] != MAGIC_CHAR ||
buf[sizeof(dos)] > MAXSIGLEN)
{
MessageBox(NULL,"Not signed or incorrect!","Verify",MB_ICONSTOP);
close(fd);
return -1;
}
siglen = buf[sizeof(dos)];
memcpy(sig,buf + sizeof(dos) + 1,siglen);
/*
* 计算 SHA1
*/
SHA1_Init(&ctx);
lseek(fd,0L,SEEK_SET);
if ( (len = read(fd,buf,0x40)) != 0x40)
{
MessageBox(NULL,"Read error","Read",MB_ICONSTOP);
close(fd);
return -1;
}
SHA1_Update(&ctx,buf,len);
lseek(fd,0xA0L,SEEK_SET);
while ( (len = read(fd,buf,sizeof(buf))) > 0)
{
SHA1_Update(&ctx,buf,len);
}
if (len == -1)
{
MessageBox(NULL,"Read error","Error",MB_ICONERROR);
close(fd);
return -1;
}
close(fd);
memset(md,0,sizeof(md));
SHA1_Final(md,&ctx);
if ( verify(sig,siglen,md,SHA_DIGEST_LENGTH) )
MessageBox(NULL,"Verify Ok!","Verify",MB_OK);
else
MessageBox(NULL,"Verify Error!","Verify",MB_ICONERROR);
return 0;
}