A-A+

安全 – 秘钥硬编码的解决方案

2020年04月08日 15:47 汪洋大海 暂无评论 阅读 261 views 次

导读
程序中经常用到需要用对称加密算法加解密,通常的做法是在代码中写死,硬编码到代码里。但是通过工具分析代码,是可以看到编码信息的,所以安全的做法是做一次变换,再硬编码进去。

 

秘钥硬编码是常见的安全问题。攻击者拿到编译后的包后,经过分析是可以拿到所有使用到的字符串的(可以使用ida),很容易泄露对称秘钥,导致安全问题。(非对称算法的公钥是可以公开的)
总结下自己平时发现密钥硬编码的主要形式有:
 
1、密钥直接明文存在sharedprefs文件中,这是最不安全的。
 
2、密钥直接硬编码在Java代码中,这很不安全,dex文件很容易被逆向成java代码。
 
3、将密钥分成不同的几段,有的存储在文件中、有的存储在代码中,最后将他们拼接起来,可以将整个操作写的很复杂,这因为还是在java层,逆向者只要花点时间,也很容易被逆向。
 
4、用ndk开发,将密钥放在so文件,加密解密操作都在so文件里,这从一定程度上提高了的安全性,挡住了一些逆向者,但是有经验的逆向者还是会使用IDA破解的。
 
5、在so文件中不存储密钥,so文件中对密钥进行加解密操作,将密钥加密后的密钥命名为其他普通文件,存放在assets目录下或者其他目录下,接着在so文件里面添加无关代码(花指令),虽然可以增加静态分析难度,但是可以使用动态调式的方法,追踪加密解密函数,也可以查找到密钥内容。
 
 
保证密钥的安全确是件难事,涉及到密钥分发,存储,失效回收,APP防反编译和防调试,还有风险评估。可以说在设备上安全存储密钥这个基本无解,只能选择增大攻击者的逆向成本,让攻击者知难而退。而要是普通开发者的话,做妥善保护密钥这些事情这需要耗费很大的心血。
 
 
最安全的解决方案是:对称秘钥按照会话随机生成。通过非对称的(比如RSA)算法,两边交换秘钥。

不过大部分场景还是需要把秘钥写在程序中的,对称秘钥如何存储一直是安全的大难题,目前没有什么方案可以保证安全存储对称秘钥的方案,相关的方案只是增加了破解难度而已

 
 
-----------------下面可看可不看------------
 
 
相对安全的方案是:对秘钥进行变换。

使用的数学方法有:

  • 移位和循环移位
    移位就是将一段数码按照规定的位数整体性地左移或右移。循环右移就是当右移时,把数码的最后的位移到数码的最前头,循环左移正相反。例如,对十进制数码12345678循环右移1位(十进制位)的结果为81234567,而循环左移1位的结果则为23456781。
  • 置换
    就是将数码中的某一位的值根据置换表的规定,用另一位代替。它不像移位操作那样整齐有序,看上去杂乱无章。这正是加密所需,被经常应用。
  • 扩展
    就是将一段数码扩展成比原来位数更长的数码。扩展方法有多种,例如,可以用置换的方法,以扩展置换表来规定扩展后的数码每一位的替代值。
  • 压缩
    就是将一段数码压缩成比原来位数更短的数码。压缩方法有多种,例如,也可以用置换的方法,以表来规定压缩后的数码每一位的替代值。
  • 异或
    这是一种二进制布尔代数运算。异或的数学符号为⊕ ,它的运算法则如下:
    1⊕1 = 0
    0⊕0 = 0
    1⊕0 = 1
    0⊕1 = 1
    也可以简单地理解为,参与异或运算的两数位如相等,则结果为0,不等则为1。
  • 迭代
    迭代就是多次重复相同的运算,这在密码算法中经常使用,以使得形成的密文更加难以破解。

通常是对秘钥进行变形,然后程序里面提供相关接口在运算过程中获取真实的秘钥。下面是几种常见的简单方案:

方案1:使用RSA加密保存

方案很简单,使用RSA的私钥进行加密,RSA的公钥写死在客户端里,使用的时候使用RSA进行一次解密操作。

优点: 简单容易实现,一般项目里都需要使用非对称的加密方法,利用公开的秘钥做一次解密。
缺点: 如果知道了算法很容易破解。

方案2:使用Base64进行编码,再保存

对秘钥进行Base64编码,然后再解码。

方案3:使用AES再次加密

再使用一次对称加密,两次不要使用相同的秘钥

方案4:自定义方案

下面给一个方案,对数据进行变形。算法如下:

  1. 对每一个字节做一次循环右移
  2. 对每一个字节用一个表的数据做位异或操作
  3. 转为16进制数

相关代码:

//
// 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); //公式:循环左移n位: (x>>(N - n) ) | (x<<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); //公式:循环左移n位: (x>>(N - n) ) | (x<<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

布施恩德可便相知重

微信扫一扫打赏

支付宝扫一扫打赏

×

给我留言