sha256算法细节详解
最近在項目中遇到sha256算法加密的需求,于是看了一些相關的資料,最后也整理出一些東西,并且參考文檔自己實現了一遍:
#define S(a, b) (((a) >> (b)) | ((a) << (32 - b))) /* a循環右移b位 *//* sha256邏輯函數 */ #define CH(x, y, z) ((x & y) ^ ((~x) & z)) #define MA(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) #define Σ0(x) (S(x, 2)^S(x, 13)^S(x, 22)) #define Σ1(x) (S(x, 6)^S(x, 11)^S(x, 25)) #define σ0(x) (S(x, 7)^S(x, 18)^(x >> 3)) #define σ1(x) (S(x, 17)^S(x, 19)^(x >> 10))/* 功能:大小端判斷返回: 1小端 0大端 */ int Endian() {int a = 1;int *p = &a;return (*(char *)p == 1); } /* 功能:sha256編碼入參:char *Src 源數據 int SrcLen 源數據長度返回值: sha256數據 */ char* MyShaA256Encode(const char *Src, long long SrcLen) {unsigned long i = 0, T1 = 0, T2 = 0, A = 0, B = 0, C = 0, D = 0, E = 0, F = 0, G = 0, H = 0;unsigned long *p = NULL;unsigned long W[64] = {0}; /* 64個字 */static char shadata[256] = {0}; /* sha256數據緩存 */unsigned long Key[64] = /* 秘鑰表 */{0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,};unsigned long Hash[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; /* 初始哈希值 */long long reallen = (SrcLen * 8) % 512 >= 448 ? ((SrcLen * 8) / 512 + 2) * 64 : ((SrcLen * 8) / 512 + 1) * 64; /* 補位后長度 */char * processsrc = (char *)calloc(reallen, sizeof(char)); /* 源數據補位后緩存 */if (NULL == processsrc) {return NULL;}/* 預處理 */if (Endian()) {for (i = 0; i < SrcLen; processsrc[i + 3 - 2 * (i % 4)] = Src[i], i++); /* 小端存法 */processsrc[i + 3 - 2 * (i % 4)] = 1 << 7; /* 補一個1 */} else {memcpy(processsrc, Src, SrcLen); /* 大端存法 */*(processsrc + SrcLen) = 1 << 7; /* 補一個1 */}*((long*)(processsrc + reallen - 4)) = SrcLen << 3; /* 最后64位代表源數據長度 */*((long*)(processsrc + reallen - 8)) = SrcLen >> 29;for (p = (unsigned long *)processsrc; p < (unsigned long *)(processsrc + reallen); p += 16) {/* 初始化64個字(W) */for (i = 0; i < 16; ++i) {W[i] = *(p + i);}for (i = 16; i < 64; ++i) {W[i] = σ1(W[i - 2]) + W[i - 7] + σ0(W[i - 15]) + W[i - 16];}/* sha256摘要迭代 */A = Hash[0]; B = Hash[1]; C = Hash[2]; D = Hash[3]; E = Hash[4]; F = Hash[5]; G = Hash[6]; H = Hash[7]; for (i = 0; i < 64; ++i) {T1 = H + Σ1(E) + CH(E, F, G) + Key[i] + W[i];T2 = Σ0(A) + MA(A, B, C);H = G, G = F, F = E, E = D + T1, D = C, C = B, B = A, A = T1 + T2;}Hash[0] += A, Hash[1] += B, Hash[2] += C, Hash[3] += D, Hash[4] += E, Hash[5] += F, Hash[6] += G, Hash[7] += H;}sprintf(shadata, "%08X%08X%08X%08X%08X%08X%08X%08X", Hash[0], Hash[1], Hash[2], Hash[3], Hash[4], Hash[5], Hash[6], Hash[7]);free(processsrc);printf("sha256: %s\r\n", shadata);return shadata; }源碼詳解:
1、sha256常數
sha256最終輸出的是64位字符串,故需要一串64字符的初始哈希值,即源碼中的Hash[8],剛好是8*8個字符,其次還需要64個4byte的秘鑰,用于摘要迭代,即源碼中的Key[64]:
unsigned long Key[64] = /* 秘鑰表 */{0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,};unsigned long Hash[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; /* 初始哈希值 */2、補位后長度計算
補位后長度計算遵循這樣的原則:在源數據后先添加一位1,再在1后面添加若干個0,知道數據總位數對512求余為512-64=448,最后64位需要填寫源數據長度,用于入參SrcLen表示的是原字符串長度(單位是byte),1byte=8bit,所以在計算時需要有*8的操作,具體如下:
long long reallen = (SrcLen * 8) % 512 >= 448 ? ((SrcLen * 8) / 512 + 2) * 64 : ((SrcLen * 8) / 512 + 1) * 64; /* 補位后長度 */3、預處理(補位)
將原始數據Src擴展成512位對齊的processsrc數據,此處需要關注的是Src數據默認是低地址->高地址排列傳入的,假設需要加密的數據是字符串"12345",傳入的是字符串首地址,長度為5byte,這個方法內部看到的是0x31,0x32,0x33,0x34,0x35這些數據,接著以4字節為單位進行加工,首先是0x31323334,將這個數據看成是一個4byte的整體,從低地址到高地址依次存入processsrc,小端機器按0x34,0x33,0x32,0x31存入,大端機器按0x31,0x32,0x33,0x34存入(此步其他數文章里沒有提出,本人也不確定是不是這樣??),小端最后就是這行代碼:
for (i = 0; i < SrcLen; processsrc[i + 3 - 2 * (i % 4)] = Src[i], i++);大端是這行代碼:
memcpy(processsrc, Src, SrcLen);將源數據存入后,再進行1+n個0的補齊,calloc初始值就是0,所以只需要補齊一個1:,小端補1:
processsrc[i + 3 - 2 * (i % 4)] = 1 << 7;大端補1:
*(processsrc + SrcLen) = 1 << 7;最后64位代表源數據長度,繼續補齊:
*((long*)(processsrc + reallen - 4)) = SrcLen << 3; *((long*)(processsrc + reallen - 8)) = SrcLen >> 29;4、初始化64個字
將每個512位的數據塊擴充成64byte,前16個byte將512位數據完全拷貝即可,后面48byte參考文檔里面這個公式擴充:
相應的代碼是這段:
5、摘要迭代
根據官方手冊上提供的邏輯圖進行操作即可:
代碼是這一段:
接下來就是重復4、5步操作,直到將processsrc 的數據完全處理完,得到一串Hash即sha256的加密數據。
總結
以上是生活随笔為你收集整理的sha256算法细节详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 俄罗斯:自由软件在这里生根
- 下一篇: Hadoop学习笔记