作者归档:softsim

eSIM技术架构

为了实现eSIM的业务需求RSP技术标准定义了一套包含管理平台、终端、eUICC以及相关配套设施的技术体系。

主要实体包括以下6个方面。

①Profile:运营商向用户提供服务所需的卡数据和卡应用的集合,需要通过空中下载的方式安装到eUICC上。

②eUICC:Profile的硬件载体,类似于传统USIM卡的UICC,但软硬件更复杂,可满足动态加载运营商数据的需要。同一张eUICC上可以加载属于不同运营商的多份Profile.但同一时间只有一份能使用。

③SM-DP+:负责生产、存储、提供Profile的网络服务平台。SM-DP+需具备必要的软硬件能力以确保Profile的安全。

④终端:需要接入移动网络的实体。eUICC预置在终端中,终端也负责从SM-DP+下载Profile并写入eUICC。

⑤DS:发现服务器,协助终端寻址SM-DP+。

⑥CA:标准PKI证书权威机构,为体系内的通信各方颁发可信数字证书。


RSP标准的安全机制

RSP标准要求eSIM在生产、管理、安装和使用等任何环节的安全级别都不低于传统可插拔SIM卡。标准要求体系中各实体通过GSMA定义的安全认证:卡商、SM-DP+、DS等需通过SAS认证,eUICC需通过专门定义的Protection Profile认证(GSMA尚未制定该标准)。在技术层面,标准中定义了多种安全机制。

(1)公钥基础设施

公钥基础设施(PKI)是RSP安全机制的基础。系统各个实体需要配置同根的数字证书,用于签名认证和密钥协商。数字证书需支持撤销机制。

为了实现全球互通,GSMA希望根证书统一由经过认证的CA签发。不过CA的使用受各个国家法律的严格约束,可以预见,CA将成为eSIM互通的最大障碍。

(2)安全算法

RSP使用的安全算法包括非对称加密、对称加密、散列算法和HMAC等。其中最核心的非对称加密选用了椭圆曲线算法(ECC)。相对于业界常用的RSA算法,ECC算法秘钥更短,单位安全强度更高,但实现难度较大。

(3)安全通信

安全通信机制用于实现远程操作和数据交换,保障Profile在传输过程中不会出现数据泄露、窃取、失效等问题。实体之间的通信采用基于TLSl.2的安全连接。在TLS之上,RSP还制定了专用安全协议。RSP安全通信遵循下列规则。

①双向认证

任意两个实体之间的通信必须进行双向认证,认证的基础是通信双方拥有同根证书。认证分两步,第一步是请求方(如eUICC)对被请求方(如SM-DP+)进行认证,认证方法是对被请求方的公钥证书进行检验;第二步是被请求方通过同样的方法对请求方进行认证。

②通信保护

通信双方协商一套最小的公共机密工具集,进行端到端的认证、完整性保护和一致性保护,并且尽量采用前向安全机制(Forward Secrecy)。

③信息保密

要求eUICC不得向任何未经认证的服务器提供私密数据;在通过服务器的认证之前,eUICC不对任何资料进行签名。

④授权检查

被请求方必须检查请求方是否具有访问指定信息的权限。


Profile加密

Profile加密是eSIM体系中最重要的安全措施之一,用于确保Profile明文仅出现在生成服务器内部和eUICC内部,在其他任何环节的任何时刻仅以密文形式存在,保障Profile数据不被泄露。RSP标准定义了一种基于SCPlla和SCP03t的安全加密方法。SCP03t是一种面向TLV格式数据、基于对称秘钥的加密方法,SCPlla定义了一种符合前向安全思想的秘钥协商机制。

Profile加密保护分为两种模式:预加密模式和即时加密模式。Profile预加密是指在生成Profile的同时生成一个AES随机秘钥(PPK),使用PPK加密Profile;在下载时,SM-DP+与eUICC协商生成会话秘钥,但会话密钥仅用于加密PPK。即时加密模式是指在下载Profile时.SM-DP+与eUICC协商生成会话秘钥,使用会话秘钥对Profile进行加密。


eUICC安全架构

eUICC在接口,物理电气特性等方面保留了传统UICC的技术要求,确保卡外实体无法访问卡内的敏感信息。同时,eUICC还借鉴了全球平台组织(Global Platform)卡规范的技术,通过安全域机制对安装到eUICC中的多个Profile进行严格的隔离,以避免Profile被卡内应用非法访问。安全域技术是防止Profile数据失效的重要手段。


终端透传

由于终端在本质上是非安全的,因此,需要确保终端无法接触敏感数据的明文。RSP标准定义终端负责与SM-DP+建立TLS连接,但在TLS之上由eUICC与SM-DP+直接建立逻辑安全信道进行端到端数据传输。终端仅负责加密数据的透传。


用户确认

RSP标准强制要求用户手动确认,以确保任何对eUICC的操作都体现用户的真实意图。其中定义了用户确认码机制用于防止Profile被非法用户窃取。对于分配给用户的Profile,运营商可以设置一个口令(确认码),在下载之前,用户必须输入正确的口令。


M2M设备的服务对象通常是行业用户,行业用户会选择直接与运营商合作为批量M2M设备提供通信服务。由于M2M设备所处的环境限制且M2M业务通常由平台侧发起,M2M eUICC中必须包含预置号码使得平台能够与eUICC建立初始连接。而消费电子设备比较灵活,可以通过Wifi、蓝牙等方式建立通信连接,因此不是必须包含预置号码,而是在架构中增加了SM-DS网元,设备的LPA模块实现SM-DS的寻址功能,从而通过SM-DS实现与远程管理平台的初始连接。

EF_Keys和EF_KeysPS

EF Keys (Ciphering and Integrity Keys) 移动:07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 电信: 07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
第1个字节是 Key set identifier (KSI ) EF_KeysPS 07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 电信: 07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

位置信息LOCI和PSLOCI

EF_LOCI (Location Information)
6F7E
位置信息由下面几部分组成:
TMSI(Temporary Mobile Subscriber Identity) 1-4字节
LAI(Location Area Information) 5-9 字节
RFU 10字节
Location update status 11字节

LAI编码遵循 TS 24.008
位置更新状态 编码遵循 TS 24.008和TS 31.101

中国移动 FFFFFFFF-64F0000000-FF-01
中国联通 FFFFFFFF-FFFFFFFFFE-00-01
中国电信 FFFFFFFF-FFFFFFFFFE-00-01


EF PSLOCI (Packet Switched location information)
6F73
由以下几部分组成

Packet Temporary Mobile Subscriber Identity (P-TMSI); 1-4字节
Packet Temporary Mobile Subscriber Identity signature value (P-TMSI signature value); 5-7字节
Routing Area Information (RAI); 8-13字节
Routing Area update status. 14字节

中国移动 FFFFFFFF-FFFFFF-64F0000000FF-01
中国联通 FFFFFFFF-FFFFFF-FFFFFFFFFEFF-01
中国电信 00000000-000000-000000000000-00


EF EPSLOCI (EPS location information)
6FE3

Globally Unique Temporary Identifier (GUTI); 1-12字节
Last visited registered Tracking Area Identity (TAI); 13-17字节
EPS update status. 18字节

中国移动 FFFFFFFFFFFFFFFFFFFFFFFF-FFFFFFFFFF-01
中国联通 不存在此文件
中国电信 0BF6FFFFFFFFFFFFFFFFFFFF-FFFFFFFFFE-01


EF 5GS3GPPLOCI (5GS 3GPP location information)
4F01

5G-Globally Unique Temporary Identifier (5G-GUTI); 1-13字节
Last visited registered Tracking Area Identity in 5GS (TAI); 14-19字节
5GS update status. 20字节


LOCI_GPRS
FFFFFFFF-FFFFFF-FFFFFFFFFEFF-01

Location Area Identification

The purpose of the Location Area Identification information element is to provide an unambiguous identification of
location areas within the area covered by the 3GPP system.

The Location Area Identification information element is coded as shown in figure 10.5.3/3GPP TS 24.008 and table 10.5.3/3GPP TS 24.008.

The Location Area Identification is a type 3 information element with 6 octets length.

iPhone切换运营商

iPhone:/var/mobile/Library root# ls -ld Carrier*
lrwxr-xr-x 1 mobile mobile  44 Apr 15 16:17 Carrier\ Bundle.bundle -> /System/Library/Carrier\ Bundles/iPhone/46000/
drwxr-xr-x 5 mobile mobile 160 Apr 15 16:13 Carrier\ Bundles/
lrwxr-xr-x 1 mobile mobile  44 Apr 15 16:17 Carrier1Bundle.bundle -> /System/Library/Carrier\ Bundles/iPhone/46000/
lrwxr-xr-x 1 mobile mobile  53 Feb 27 08:50 CarrierDefault.bundle -> /System/Library/Carrier\ Bundles/iPhone/Default.bundle/

ICCID

/var/mobile/Library/Carrier Bundles/ICCIDCache/ICCIDCache.plist

插入电信卡

iPhone:/var/mobile/Library root# ls -ld Carrier*
lrwxr-xr-x 1 mobile mobile  44 Apr 15 17:25 Carrier\ Bundle.bundle -> /System/Library/Carrier\ Bundles/iPhone/46011/
drwxr-xr-x 5 mobile mobile 160 Apr 15 16:13 Carrier\ Bundles/
lrwxr-xr-x 1 mobile mobile  44 Apr 15 17:25 Carrier1Bundle.bundle -> /System/Library/Carrier\ Bundles/iPhone/46011/
lrwxr-xr-x 1 mobile mobile  53 Feb 27 08:50 CarrierDefault.bundle -> /System/Library/Carrier\ Bundles/iPhone/Default.bundle/

插入联通卡

iPhone:/var/mobile/Library root# ls -ld Carrier*
lrwxr-xr-x 1 mobile mobile  48 Apr 15 17:33 Carrier\ Bundle.bundle -> /var/mobile/Library/Carrier\ Bundles/iPhone/46001/
drwxr-xr-x 5 mobile mobile 160 Apr 15 16:13 Carrier\ Bundles/
lrwxr-xr-x 1 mobile mobile  48 Apr 15 17:33 Carrier1Bundle.bundle -> /var/mobile/Library/Carrier\ Bundles/iPhone/46001/
lrwxr-xr-x 1 mobile mobile  53 Feb 27 08:50 CarrierDefault.bundle -> /System/Library/Carrier\ Bundles/iPhone/Default.bundle/

中国

lrwxr-xr-x 1 root wheel 14 Nov  3 10:59 46000 -> CMCC_cn.bundle/
lrwxr-xr-x 1 root wheel 16 Nov  3 10:59 46001 -> Unicom_cn.bundle/
lrwxr-xr-x 1 root wheel 14 Nov  3 10:59 46002 -> CMCC_cn.bundle/
lrwxr-xr-x 1 root wheel 22 Nov  3 10:59 46003 -> ChinaTelecom_cn.bundle/
lrwxr-xr-x 1 root wheel 14 Nov  3 10:59 46007 -> CMCC_cn.bundle/
lrwxr-xr-x 1 root wheel 14 Nov  3 10:59 46008 -> CMCC_cn.bundle/
lrwxr-xr-x 1 root wheel 16 Nov  3 10:59 46009 -> Unicom_cn.bundle/
lrwxr-xr-x 1 root wheel 27 Nov  3 10:59 46011 -> ChinaTelecom_USIM_cn.bundle/

RC4流式对称加密

RC 4生成一个伪随机流(pseduorandom stream of bits) 来同 明文 进行 按位异或(bit-wise exclusive-or) 来 实现加密和解密

KSA(key-scheduling algorithm)

初始化数组S. 键长 1 ≤ keylength ≤ 256, 典型值为 5~ 16, 也就是 40bits ~ 128bits

for i from 0 to 255
S[i] := i
endfor

j := 0
for i from 0 to 255
j := (j + S[i] + key[i mod keylength]) mod 256
swap(&S[i],&S[j])
endfor

PRGA

使用上一步生成的S 数组来生成 伪随机流

i := 0
j := 0
while GeneratingOutput:
i := (i + 1) mod 256
j := (j + S[i]) mod 256
swap(&S[i],&S[j])
output S[(S[i] + S[j]) mod 256]
endwhile

一个C实现

unsigned char S[256];
unsigned int i, j;

void swap(unsigned char *s, unsigned int i, unsigned int j) {
    unsigned char temp = s[i];
    s[i] = s[j];
    s[j] = temp;
}

/* KSA */
void rc4_init(unsigned char *key, unsigned int key_length) {
    for (i = 0; i < 256; i++)
        S[i] = i;

    for (i = j = 0; i < 256; i++) {
        j = (j + key[i % key_length] + S[i]) & 255;
        swap(S, i, j);
    }

    i = j = 0;
}

/* PRGA */
unsigned char rc4_output() {
    i = (i + 1) & 255;
    j = (j + S[i]) & 255;

    swap(S, i, j);

    return S[(S[i] + S[j]) & 255];
}

#include < stdio.h >
#include < string.h >
#include < stdlib.h >

#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))

int main() {
    unsigned char *test_vectors[][2] =
    {
        {"Key", "Plaintext"},
        {"Wiki", "pedia"},
        {"Secret", "Attack at dawn"}
    };

    int x;
    for (x = 0; x < ARRAY_SIZE(test_vectors); x++) {
        int y;
        rc4_init(test_vectors[x][0], strlen((char*)test_vectors[x][0]));

        for (y = 0; y < strlen((char*)test_vectors[x][1]); y++)
           printf("%02X", test_vectors[x][1][y] ^ rc4_output());
        printf("n");
    }
    return 0;
}

Python实现

def initialize(key):
    """Produce a 256-entry list based on `key` (a sequence of numbers)
    as the first step in RC4.
    Note: indices in key greater than 255 will be ignored.
    """
    k = range(256)
    j = 0
    for i in range(256):
        j = (j + k[i] + key[i % len(key)]) % 256
        k[i], k[j] = k[j], k[i]
    return k

def gen_random_bytes(k):
    """Yield a pseudo-random stream of bytes based on 256-byte array `k`."""
    i = 0
    j = 0
    while True:
        i = (i + 1) % 256
        j = (j + k[i]) % 256
        k[i], k[j] = k[j], k[i]
        yield k[(k[i] + k[j]) % 256]

def run_rc4(k, text):
    cipher_chars = []
    random_byte_gen = gen_random_bytes(k)
    for char in text:
        byte = ord(char)
        cipher_byte = byte ^ random_byte_gen.next()
        cipher_chars.append(chr(cipher_byte))
    return ''.join(cipher_chars)

以上是一种实现方式, run_rc4(k, text)  输入 key 和 待加密的 text, 返回 已经加密的串

class RC4:
   def __init__(self, key = ""):
       if key:
           self._rc4_init(key)

   def _rc4_init(self, key):
       (self.x,self.y) = (0,0)
       key_len = len(key)
       if key_len > 256 or key_len < 1:
           raise IndexError, 'Invalid key length' + key_len
       self.state_array = [i for i in xrange(0,256)] #self.stat_array = range(0,256)
       for i in xrange(0,256):
           self.x = ((ord(key[i%key_len]) & 0xff) + self.state_array[i] + self.x) & 0xff
           self.state_array[i], self.state_array[self.x] = self.state_array[self.x], self.state_array[i]
       self.x = 0

   def engine_crypt(self, input):
       self.out = []
       for i in xrange(0,len(input)):
           self.x = (self.x + 1) & 0xff
           self.y = (self.state_array[self.x] + self.y) & 0xff
           self.state_array[self.x], self.state_array[self.y] = self.state_array[self.y], self.state_array[self.x]
           self.out.append(chr((ord(input[i]) ^ self.state_array[(self.state_array[self.x] + self.state_array[self.y]) & 0xff])))
       return "".join(self.out)

以上是第2种实现,使用了class
使用方法  RC4(key).engine_crypt(text)  ,返回的是加密后的串

def rc4crypt(data, key):
    x = 0
    box = range(256)
    for i in range(256):
        x = (x + box[i] + ord(key[i % len(key)])) % 256
        box[i], box[x] = box[x], box[i]
    x = 0
    y = 0
    out = []
    for char in data:
        x = (x + 1) % 256
        y = (y + box[x]) % 256
        box[x], box[y] = box[y], box[x]
        out.append(chr(ord(char) ^ box[(box[x] + box[y]) % 256]))

    return ''.join(out)

Python3版本


def rc4crypt(data: bytes, key: bytes) -> bytes:
    x = 0
    box = list(range(256))
    for i in range(256):
        x = (x + box[i] + key[i % len(key)]) % 256
        box[i], box[x] = box[x], box[i]
    x = y = 0
    out = []
    for char in data:
        x = (x + 1) % 256
        y = (y + box[x]) % 256
        box[x], box[y] = box[y], box[x]
        out.append((char ^ box[(box[x] + box[y]) % 256]))

    return bytes(out)
   
    
mykey = b'softs.im!'    
mydata = b'android/telephony/TelephonyManager'

x = rc4crypt(mydata, mykey)

import binascii
print(x)
print(binascii.hexlify(x))

y =  rc4crypt(x, mykey)
print(y)
print(binascii.hexlify(y))


以上是一个单个函数形式的实现

PHP实现

function Encrypt($key, $pt) {
    $s = array();
    for ($i=0; $i<256; $i++) {
        $s[$i] = $i;
    }

    $j = 0;
    $key_len = strlen($key);
    for ($i=0; $i<256; $i++) {
        $j = ($j + $s[$i] + ord($key[$i % $key_len])) % 256;
        //swap
        $x = $s[$i];
        $s[$i] = $s[$j];
        $s[$j] = $x;
    }
    $i = 0;
    $j = 0;
    $ct = '';
    $data_len = strlen($pt);
    for ($y=0; $y< $data_len; $y++) {
        $i = ($i + 1) % 256;
        $j = ($j + $s[$i]) % 256;
        //swap
        $x = $s[$i];
        $s[$i] = $s[$j];
        $s[$j] = $x;
        $ct .= $pt[$y] ^ chr($s[($s[$i] + $s[$j]) % 256]);
    }
    return $ct;
}

Java也可以实现

   private static byte[] rc4_encrypt(byte[] key, byte[] plaintext) {
        byte[] S = new byte[256];
        byte[] T = new byte[256];
        int keylen = key.length;

        for (int i = 0; i < 256; i++) {
            S[i] = (byte) i;
            T[i] = key[i % keylen];
        }
        int j = 0;
        for (int i = 0; i < 256; i++) {
            j = (j + S[i] + T[i]) & 0xFF;
            S[i] ^= S[j];
            S[j] ^= S[i];
            S[i] ^= S[j];
        }

        byte[] ciphertext = new byte[plaintext.length];
        int i = 0, k, t;
        j = 0;
        for (int counter = 0; counter < plaintext.length; counter++) {
            i = (i + 1) & 0xFF;
            j = (j + S[i]) & 0xFF;
            S[i] ^= S[j];
            S[j] ^= S[i];
            S[i] ^= S[j];
            t = (S[i] + S[j]) & 0xFF;
            k = S[t];
            ciphertext[counter] = (byte) (plaintext[counter] ^ k);
        }
        return ciphertext;
    }

GSM GID1和GID2

The GID1 and GID2 elementary files on the SIM are specified in GSM 11.11 (ETS 300 977)

启用它,须在SIM Service Talbe中做出合适的标记

GID1 (Group Identifier Level 1) – an identifier for a particular SIM and handset association, which can be used to identify a group of SIMs involved in a particular application.

GID2 (Group Identifier Level 2) – a GID1-like identifier

主要用来标识虚拟运营商

XCode11中使用StoryBoard创建Scroll View无需任何代码

在XCode11中,跟”XCode8中使用StoryBoard创建Scroll View”有所区别。
虽然,也可以手动编辑 Main.stroyboard, 删除 Safe Area之后,采用跟XCode8一样的方法。

在 XCode11, Object Library默认隐藏, 可以用 Command + Shift + L 快捷键打开
或者 View / ShowLibrary打开

  1. 创建 Scroll View, 并添加约束
  2. 添加View到Scroll View 上
  3. 在树形图中,选中View, 按住Ctrl键盘, 拖向 Content Layout Guide

  4. 在弹出的约束菜单里,按住Shift, 同时选中leading / top / trailing / bottom 4个约束

  5. 选中View, 拖向 Frame Layout Guide, 在弹出的菜单里,选择Equal Width

  6. 为ContentView 设置高度
  7. 为Scroll View和Content View 检查约束常量  Constant 是否为0, Multiplier是否为1

XCode8中使用StoryBoard创建Scroll View

  1. XCode创建一个 Single View Application
  2. 在左侧的Project navigator中选中 Main.storyboard, 中间会出现 View Controller Scene的树型列表, 选中 View
  3. 在右下侧的 object libary 筛选出 Scroll View, 拖到预览图中的View上面, 或者 拖到 树型图的 View 下面。
  4. 在树形图中选中 Scroll View, 为其添加约束(Add New Constraints)
  5. 上下左右,都为0, 共4个

  6. 然后在 Object Libaray中筛选uiview,  将View拖到预览图的ScrollView之上(也就是树形图的ScrollView之下)
  7. 树形图中选中 新建立的View, 右侧选 Identity inspector, 在Document里为新View设置Label ,名为 ContentView.
  8. (这一步可以不要)

  9. 然后为新的ContentView,添加约束
  10. 在树形图中选中ContentView,  按住Ctrl键, 拖向  Scroll View. 同时松开鼠标左键和键盘Ctrl键, 出现菜单,选 Equal Widths
  11. 所有的Constraints 如下图
  12. 再在ContentView上添加的东西,就可以滚动了

Java SIM OTA

[Acess Domain] specifying the applets rights on file system

[Minimum Security Level] specifying minimum security that incoming OTA
message has to present.

当STK应用被安装时,一些参数可以设定 ME和SIM卡的哪些资源可以被applet使用。

访问域(Access Domain): 指定了被授予STK访问GSM文件,在这些文件上按照访问条件执行操作的 实体(identities)或 访问权限(access rights,比如CHV和ADM)
1. 如果所有的访问权限都被授予,那么所有的行为都允许,除了那些NEVEr访问条件的
2. 如果没有任何条件被授予,那么任何行为都不被允许
3. 如果某些条件被允许,只允许那些已定义的实体才允许执行。

STK的访问权限 跟 用户,OTA等实体 的访问权限是无关的,相互独立。
修改用户的CHV1,并不影响STK的权限。

权限级别(priority level):多个stk applet注册同样的事件时,激活的优先级

定时器个数

菜单条目个数

TS123.048中介绍:
访问域由 Access Domain Parameter (ADP) 加上 Access Domain Data (ADD)构成

ADP = 00 时,表示可以完全访问GSM文件系统, ADD为空
ADP = 01 时,表示 APDU级的访问机制, ADD长度为2
ADP = 02 时,表示 3GPP访问机制

参考资料:

Digital cellular telecommunications system (Phase 2+);
Subscriber Identity Module Application Programming
Interface (SIM API) for Java Card;
Stage 2 (ETSI TS 143 019)

Digital cellular telecommunications system (Phase 2+);
Universal Mobile Telecommunications System (UMTS);
Security mechanisms for the (U)SIM application toolkit;
Stage 2 (ETSI TS 123 048)