最近工作中接触到部分网络通信编程,需要将鉴权五元组转换成三元组,故将此涉及的算法记录一下,方便以后回顾。
USIM卡用2G终端,HLR会发送五元组鉴权,同时VLR会启动五元组/三元组转换流程,将五元组变换为三元组,登陆2G网络。
同时,SIM卡用3G终端,HLR会发送三元组鉴权,同时VLR会启动三元组/五元组转换流程,将三元组变换为五元组,登陆3G网络。
背景知识:
【2G/3G鉴权参数的转换】
2G/3G交互操作中的关键是在两个系统中密钥的长度不同。3G鉴权后,有128位密码和完整性密钥CK和IK。在2G鉴权完成后,只有64位密钥Kc服务于网络,故需要一种转换功能,转换3G密钥的长度以符合2G的长度。
当USIM卡接入2G网络时,USIM卡需要支持转换函数,能将XRES’转为SRES’,将CK和IK转为Kc;同时,在不共核心网的情况下,3G HLR/AuC能支持五元组到三元组的转换,从而可以向手机注册的2G MSC/VLR或者SGSN返回三元组认证向量;或者,在共核心网的情况下,HLR/Auc不进行五元组到三元组的转换,而由MSC/VLR与SGSN进行CK+IK到Kc的转换,以支持2G无线网络。
当SIM卡接入3G网络时,其认证过程和2G系统基本相同,只是3G核心网的VLR/SGSN需要通过标准的转换函数将Kc转换为CK和IK,以支持3G无线网络;同时,3G终端从SIM卡得到Kc后,也需要通过同样的转换函数将Kc转换为CK和IK,这样后续终端和3G基站间可以进行加密和完整性保护。
其中 CK+IK到Kc 转换执行c3算法,Xres 到Sres 转换 执行c2算法,以上算法分别通过java 和 pythod 代码实现。
def c2( xres ):
l = len(xres)
if l > 16:
raise PacketError( "xres length:%d error" % l )
elif l < 16:
xres += "\x00" * ( 16 - l )
item = [ xres[pos:pos+4] for pos in range(0, len(xres), 4) ]
res = item[0]
for pos in range( 1, len(item) ):
res = Xor(res, item[pos])
return res
def c3( ck, ik ):
if len(ck) != 16:
raise PacketError( "ck length:%d error" % len(ck) )
if len(ik) != 16:
raise PacketError( "ik length:%d error" % len(ik) )
item = [ ck[0:8], ck[8:16], ik[0:8], ik[8:16] ]
res = item[0]
for pos in range( 1, len(item) ):
res = Xor(res, item[pos])
return res
private String c3(String ck, String ik) {
if (ck.length() != 32) {
log.error("ck length error" + ck.length());
}
if (ik.length() != 32) {
log.error("ik length error" + ik.length());
}
List<String> item = new ArrayList<>();
item.add(ck.substring(0, 16));
item.add(ck.substring(16, 32));
item.add(ik.substring(0, 16));
item.add(ik.substring(16, 32));
return getResult(item);
}
private String c2(String xres) {
int len = xres.length();
if (len > 32) {
log.error("X_RES length error..." + len);
}
if (len < 32) {
StringBuilder sb = new StringBuilder(xres);
for (int i = 0; i < 32 - len; i += 2) {
sb.append("00");
}
xres = sb.toString();
}
String regex = "(.{8})";
String data = xres.replaceAll(regex, "$1,");
List<String> item = Arrays.asList(data.split(","));
return getResult(item);
}
private String getResult(List<String> item) {
String res = item.get(0);
for (int i = 1; i < item.size(); i++) {
res = xor(res, item.get(i));
}
return res;
}
private String xor(String data1, String data2) {
byte[] res = BufferUtilities.hexStringToByteArray(data1);
byte[] data = BufferUtilities.hexStringToByteArray(data2);
byte[] result = new byte[res.length];
for (int i = 0; i < res.length; i++) {
result[i] = (byte) (res[i] ^ data[i]);
}
return BufferUtilities.byteToHexString(result, 0, result.length);
}