安全-秘鑰硬編碼解決
發(fā)表于 2017-06-26 | 分類于 安全
導(dǎo)讀 程序中經(jīng)常用到需要用對稱加密算法加解密,通常的做法是在代碼中寫死,硬編碼到代碼里。但是通過工具分析代碼,是可以看到編碼信息的,所以安全的做法是做一次變換,再硬編碼進去。
秘鑰硬編碼是常見的安全問題。攻擊者拿到編譯后的包后,經(jīng)過分析是可以拿到所有使用到的字符串的(可以使用ida),很容易泄露對稱秘鑰,導(dǎo)致安全問題。(非對稱算法的公鑰是可以公開的)
最安全的解決方案是:對稱秘鑰按照會話隨機生成。通過非對稱的(比如RSA)算法,兩邊交換秘鑰。
不過大部分場景還是需要把秘鑰寫在程序中的,對稱秘鑰如何存儲一直是安全的大難題,目前沒有什么方案可以保證安全存儲對稱秘鑰的方案,相關(guān)的方案只是增加了破解難度而已。
相對安全的方案是:對秘鑰進行變換。
使用的數(shù)學(xué)方法有:
移位和循環(huán)移位 移位就是將一段數(shù)碼按照規(guī)定的位數(shù)整體性地左移或右移。循環(huán)右移就是當(dāng)右移時,把數(shù)碼的最后的位移到數(shù)碼的最前頭,循環(huán)左移正相反。例如,對十進制數(shù)碼12345678循環(huán)右移1位(十進制位)的結(jié)果為81234567,而循環(huán)左移1位的結(jié)果則為23456781。
置換 就是將數(shù)碼中的某一位的值根據(jù)置換表的規(guī)定,用另一位代替。它不像移位操作那樣整齊有序,看上去雜亂無章。這正是加密所需,被經(jīng)常應(yīng)用。
擴展 就是將一段數(shù)碼擴展成比原來位數(shù)更長的數(shù)碼。擴展方法有多種,例如,可以用置換的方法,以擴展置換表來規(guī)定擴展后的數(shù)碼每一位的替代值。
壓縮 就是將一段數(shù)碼壓縮成比原來位數(shù)更短的數(shù)碼。壓縮方法有多種,例如,也可以用置換的方法,以表來規(guī)定壓縮后的數(shù)碼每一位的替代值。
異或 這是一種二進制布爾代數(shù)運算。異或的數(shù)學(xué)符號為⊕ ,它的運算法則如下: 1⊕1 = 0 0⊕0 = 0 1⊕0 = 1 0⊕1 = 1 也可以簡單地理解為,參與異或運算的兩數(shù)位如相等,則結(jié)果為0,不等則為1。
迭代 迭代就是多次重復(fù)相同的運算,這在密碼算法中經(jīng)常使用,以使得形成的密文更加難以破解。
通常是對秘鑰進行變形,然后程序里面提供相關(guān)接口在運算過程中獲取真實的秘鑰。下面是幾種常見的簡單方案:
方案1:使用RSA加密保存
方案很簡單,使用RSA的私鑰進行加密,RSA的公鑰寫死在客戶端里,使用的時候使用RSA進行一次解密操作。
優(yōu)點: 簡單容易實現(xiàn),一般項目里都需要使用非對稱的加密方法,利用公開的秘鑰做一次解密。 缺點: 如果知道了算法很容易破解。
方案2:使用Base64進行編碼,再保存
對秘鑰進行Base64編碼,然后再解碼。
方案3:使用AES再次加密
再使用一次對稱加密,兩次不要使用相同的秘鑰
方案4:自定義方案
下面給一個方案,對數(shù)據(jù)進行變形。算法如下:
對每一個字節(jié)做一次循環(huán)右移
對每一個字節(jié)用一個表的數(shù)據(jù)做位異或操作
轉(zhuǎn)為16進制數(shù)
相關(guān)代碼:
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n44" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">//
// Utils.m
// Safe
//
// Created by bolei on 2017/7/18.
// Copyright ? 2017年 bolei. All rights reserved.
//
import "Utils.h"
import "NSData+HexString.h"
//使用自己的映射表
char changeMap[16] = {'w','o','s','h','i','b','o','l','e','i','s','h','a','i','g','e'};
@implementation Utils
- (NSString *)hardKeyEncode:(NSString *)key {
if ([key length] == 0) {
return @"";
}
NSData *data = [key dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger i, len;
unsigned char *bytes;
len = data.length;
bytes = (unsigned char*)data.bytes;
for (i = 0; i < len; i++) {
NSUInteger index = i % 16;
unsigned char p = bytes[i];
p = (p << 7 || p >> 1); //公式:循環(huán)左移n位: (x>>(N - n) ) | (x<<n);循環(huán)右移n位: (x<<(N - n) ) | (x>>n)。
p ^= changeMap[index];
}
NSData *changeData = [NSData dataWithBytes:bytes length:len];
NSString *keyHex = [data hexStringFromData:changeData];
return keyHex;
}
- (NSString *)hardKeyDecode:(NSString *)key {
if ([key length] == 0) {
return @"";
}
NSData *data = [NSData dataFromHexString:key];
if ([data length] == 0) {
return @"";
}
NSUInteger i, len;
unsigned char *bytes;
len = data.length;
bytes = (unsigned char*)data.bytes;
for (i = 0; i < len; i++) {
NSUInteger index = i % 16;
unsigned char p = bytes[i];
p = (p >> 7 || p << 1); //公式:循環(huán)左移n位: (x>>(N - n) ) | (x<<n);循環(huán)右移n位: (x<<(N - n) ) | (x>>n)。
p ^= changeMap[index];
}
NSData *changeData = [NSData dataWithBytes:bytes length:len];
NSString *result = [NSString stringWithUTF8String:changeData.bytes];
return result;
}
@end</pre>
推薦方案5: 隨機+固定
本地固定使用一段隨機數(shù)16字節(jié),使用上面的方法或自定義方法做一層變換后再存儲。
啟動后隨機生成16字節(jié)的數(shù)據(jù),持久化到本地(可以保存到keychain里)。后面如果有保存就直接使用。
兩個做拼接后生成32字節(jié)作為對稱秘鑰。
好處:
秘鑰是每個設(shè)備隨機生成的,所以拿到固定秘鑰和算法不會影響其他設(shè)備。
兩段進行拼接加大了相關(guān)破解的難度
缺點:
首次讀取持久化的秘鑰有一定耗時
能破解一定能破解
最后
目前沒有安全的方法保存秘鑰,除非使用硬件來解決(后臺的加密機使用的就是硬件,秘鑰放在芯片里),所以本地保存數(shù)據(jù)需要遵循:
需要長久更安全的保存,使用keychain的方式保存
不要保存敏感信息,如果要保存需要先做脫敏
任何時候都不要保存密碼