OpenSSL: 椭圆曲线签名与校验 (ECDSA)(2005-12-11 21:20:06)
/*
目录: -------------------- 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; } |
|