分类目录归档:sim

位置信息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

主要用来标识虚拟运营商

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)

STK GET INPUT

UICC提示用户应该输入一段信息,用户输入的内容应该由设备透明地传给UICC.
如果UICC提供给你了默认的文字,那么终端在提示用户输入信息时,显示该文字。
用户可以接受,拒绝,编辑文字 作为 响应字符串

提示信息文字可以有3种格式
1. SMS默认字母表的 压缩格式
2. SMS默认字母表的 非压缩格式
3. UCS2 字母表 格式

UICC可以给出 期望输入的字符串的最大和最小长度

用户输入的字符串
1. 只含有数字: 0 到 9, *, # 和 +
2. SMS字符表 或 UCS2

Command Qualifier

bit 1:
0 = digits (0 to 9, *, #, and +) only;
1 = alphabet set.

bit 2:
0 = SMS default alphabet;
1 = UCS2 alphabet.

bit 3:

0 = terminal may echo user input on the display;
1 = user input shall not be revealed in any way (显示**密码).

bit 4:
0 = user input to be in unpacked format;
1 = user input to be in SMS packed format.

bits 5 to 7: = RFU.

bit 8:
0 = no help information available;
1 = help information available.

Command Qualifier 应该设置为 1 或者 3


当终端为 响应 GET INKEY或者GET INPUT命令,发出一个成功的Terminal Response时
它应该提供用户输入的字符串 给UICC

对于GET INPUT, 如果用户输入为空
那么应该设置 Length=01, Value=数据编码(data coding scheme)


真正的null字符串,应该是Length=00, 没有value

数据编码方案 SMS Data coding scheme, 定义在 ETSI TS 123 038 中

推荐这三种编码方案
’00’: GSM default alphabet 7 bits packed;
’04’: GSM default alphabet 8 bits;
’08’: UCS2

Java卡的内存管理

每个实例(intance)放在EEPROM或者RAM里。这依赖于对象(object)的内容
1. 如果对象的内容在卡中从一个session到另一个session都保持不变,它的内容和引用都会保存在EEPROM中,只要它从属的applet 没有被删除。
2. 如果内容会改变,发生下面的事件时会被清零:
CLEAR_ON_RESET
CLEAR_ON_DESELECT


临时内容会存放在RAM中,但它们的引用会放在EEPROM中
内容也保存在EEPROM中的:
所有基本数据类型(byte,short)的全局变量;


实例变量/Instance variables(全局变量/global variables)
在 对象安装时 创建。
局部变量/Local variables:只能在函数内部 或者 代码块 内部访问

可以使用 静态变量时 ,不要用实例变量

但静态变量是类变量, 可能会引起一些安全问题(可能被其他applet访问)


使用new创建出来对象
可以使用 局部的基本类型(byte, short)
但不要使用 局部的 byte[] 和 object
低版本的Java Card没有垃圾回收


常量 加上 static final 关键字
用基本数据类型,而不要对其进行二次封装,因为会带来更多内存消耗

在applet的构造方法里,就把所有的对象都建立好,这样在安装时就能确定自己需要多少资源。


用switch-case代替 if-else

使用复合算术语句而不是单独的赋值

Java卡:编程准则和最佳实践

本文档旨在为Java Card开发人员提供经过验证的技术指南,重点放在applet的设计和优化上。我们希望这些指南将有助于面对Java Card开发的特殊性,即:
•卡资源极为有限。 EEPROM大小通常为32-64Kb,用于保存卡片发行后小应用程序(Applet)和应用程序数据。 RAM大小通常约为4KB,并且必须容纳应用程序栈,瞬态数据和各种运行时缓冲区。你负担不起任何浪费
•CPU功率非常有限。另外,Java Card applet比本机代码慢得多,因此您不想执行不必要的操作
•即使您的小程序在平台上运行良好,您仍然希望节省尽可能多的资源,以便它也可以在更多受限的平台上运行(较少的堆栈,较小的事务缓冲区等)。还记得吗?编写一次,随处运行;-)

Applet设计

Java Card不是Java。花式的面向对象设计会导致小程序变胖和变慢,这是因为:
•CAP文件中的类/接口开销
•运行时的虚拟方法开销

下面介绍的设计准则是基于我们自己的经验。和往常一样,当然还有改进的余地,但是遵循它们肯定可以防止大量常见(以及一些不是那么常见)的错误。

高级设计

通常在设计时会犯下悲剧性的错误。如果发生这种情况,那简直是只有完整的重新设计才会拯救你……为避免这种情况,请非常仔细地阅读本节并确保您遵循这些准则。从合理的面向对象设计开始。这将帮助您了解applet的工作原理,尤其是当applet具有复杂功能(PKCS,EMV等)时。将问题分解为较小的对象仍然是找出问题的最佳方法。不过,您必须避免以下陷阱:

•类扩散:一切都不是对象。因此请控制 类/抽象类/接口 的数量
•实用工具 类/接口:将这些方法移至实际实例化的类,而不是定义仅包含静态方法的类。对于仅包含常量的接口,也是如此:将常量移至实际类
•较深的类层次结构:类树越深,继承方法的调用就越昂贵。继承还意味着您将嵌套地调用构造函数,这使栈(stack)处于危险之中。简而言之,继承的必要性必须明确:如果没有必要性,就不要继承。
•设计模式:模式在规范中看起来很棒,但是其实现意味着很多抽象类,接口和深层树
•系统化的get/set方法:同样,这在UML图表中看起来不错,但是对于Java Card应用程序来说,它的负担却很大。增加成员可见性将使您可以删除它们,从而减小代码大小并提高执行速度
•程序包扩散:实际上,除非一个程序包将由另一个applet使用,否则实际上无需将应用程序分解为程序包。看起来更好,但是跨包方法调用有一定的成本:如果不是必要,则避免使用它
•C样式错误处理:请勿在您的方法中使用C样式错误处理,即检查返回代码以查看是否发生了错误。这会产生效率低下的代码(更不用说难看的代码了),因为即使没有发生错误,您也会继续运行与错误相关的代码。您的设计必须使用Java异常:错误仅在发生后才需要处理!

设计完成后,您的应用程序应包括一个或两个软件包,一到十个类和不超过三个层级(level )的类。任何较大的问题都需要进行彻底调查和论证。一旦实现并测试了applet,您就可以确信已正确理解并实现了功能要求。在这个阶段,您可能会开始打破面向对象(OO)设计并进行优化以减小代码大小和/或提高性能。

数据存储

您的设计必须考虑以下约束:

•快速写入RAM
•写入EEPROM的速度非常慢
•分配对象甚至更糟
•垃圾收集不是Java Card 2.1.1的一部分

内存分配

关键的规则是, 在安装时创建所有实例数据,即在javacard.framework.Applet.install()调用的applet构造函数中创建:使用新的临时数组分配,所有显式初始化的对象构造。这样,在applet的使用期间就不会面临内存不足的风险:在安装时所有内容都已保留。此外,内存分配不会降低applet命令的速度。

内存写入

对象存储在EEPROM中。这意味着每次执行
myObject.myByte =(byte)5
时,您都在写入EEPROM。必须尽可能避免这种情况。显然,您需要存储持久性数据,但是在许多情况下,您只需要存储临时数据(中间结果,发送回终端的数据等)。这是APDU缓冲区派上用场的地方!毕竟,它是一个“普通”字节数组,因此请尽可能多地使用它。

安全

如果将小程序提交给安全评估(通用标准等),则可以采取一些特定步骤:
•将所有安全功能(密钥/ PIN处理等)隔离在单独的类中。这样,只需要记录和评估这些类
•尽可能使用Java Card API(PIN,密钥等)。如果平台提供了您需要的安全服务,则applet实施这些服务是无用且不安全的

固定大小的PIN

使用固定大小的PIN,即,将PIN长度存储在PIN本身中,并填充其余字节。这有助于防止定时攻击,还简化了PIN处理。

反DFA RSA签名

在将结果发送到终端之前,请验证所有RSA签名。这将减慢签名操作的速度,但可以防止DFA攻击。

其他需要注意的事项

注意平台错误:它们可能会对您的设计产生影响。
如果小程序必须可互操作,请不要使用任何专有的API。


小程序优化

本节列出了许多实际的(即经过测试和验证的)优化。在应用它们之前,请记住优化的黄金法则:
•专注于最常运行的代码:在这里您将有所作为
•您通常必须在大小和速度之间进行选择
•每次优化通过后运行基准测试
•每次优化通过后运行验证测试

减少代码大小

避免复杂的面向对象设计

如果您阅读了上一节,这应该很明显。简而言之:
•使类/接口的数量最少。组合“相似”类并在实例化时使用构造函数参数对其进行区分
•尽量减少方法数量。尤其要摆脱所有get/set方法:提高数据成员的可见性(例如,从私有到可见的包),以减少get/set方法的数量。由于您保存了方法调用,因此这也将提高性能

权衡:封装被削弱。如果您控制程序包中的所有类,那么这不成问题。

删除无效代码

查找未使用的变量和代码。听起来很明显,但您会感到惊讶…
另外,请尝试使初始化/个性化设置尽可能短。它只需要运行一次,因此非常重。也许个性化系统可以执行更复杂的操作?

分解重复代码

查找所有冗余代码并将其分解。如果这意味着分解类并将其替换为私有/静态方法,请执行此操作。

权衡:如果经常调用该代码,则可能会减慢速度。

限制参数的个数

对于虚拟方法,尝试使用不超过3个参数,对于静态方法,尝试使用不超过4个参数。这样,编译器将使用数字或字节码快捷方式,这有助于减小代码大小:aload_x(1字节)而不是aload x(2字节),依此类推。
权衡:如果这意味着从对象实例保存/读取数据,则速度会明显下降。

减少EEPROM消耗

回收所有对象。

Java Card标准不需要垃圾收集。因此,除非您坚持要浪费内存,否则必须跟踪旧对象并重用它们。切记:Java Card虚拟机“永远”运行,因此,如果某个对象变得不可访问,则其内存将”永远”消失(或者至少在删除小程序之前)。即使您的平台提供了专有的垃圾回收,您也最好重用对象。分配新对象很慢,垃圾回收也很慢。你明白了…

仔细分配数组

操作系统以32字节的块(称为clusters)分配内存。一个cluster无法在两个对象之间共享,因此任何对象都将至少吞噬一个群集,无论它多么小。此外,虚拟机将标头附加到所有对象:
•6个字节用于“普通”对象
•8个字节用于基本类型数组
•对象数组12个字节

这意味着您不必创建多个相同类型的小数组,而应将它们组合成一个数组,并使用固定偏移量访问后者。

折衷方案:代码复杂度略有增加。


减少内存消耗

重用局部变量

与其在需要时分配新的局部变量,不如尝试重用先前声明的局部变量。
折衷:滥用此技术会产生无法读取的代码。

仔细分配临时数组 (重复了,上面提到过)

与其创建多个相同类型的小型临时数组,不如将它们组合成一个数组并使用固定偏移量对其进行访问。
折衷方案:代码复杂度略有增加。

限制参数的个数 ( 重复了)

对于虚拟方法,尝试使用不超过3个参数,对于静态方法,尝试使用不超过4个参数。这样,编译器将使用短字节码指令,这有助于减小代码大小:aload_x(1字节)而不是aload x(2字节),依此类推。
权衡:如果这意味着从对象实例保存/读取数据,则速度会明显下降。

避免深层嵌套的方法调用

危险的嵌套调用通常发生在深层类树(基类方法调用)和递归上。后者产生了花哨且紧凑的代码,但它也往往会很快粉碎stack……三遍检查最终条件,并确保测试实际上触发了最坏的情况。
权衡:可能增加代码大小。
当心开关/案例结构中的局部变量

不必在每种情况下分配新的局部变量,而仅在切换之前声明一个。


提高执行速度

开启编译器优化

如果您的编译器提供了优化标志,请使用它们(对于Javac,为-O)。
权衡:可能的代码大小增加。

避免复杂的面向对象设计

有关详细信息,请参见Applet设计和Applet优化。
权衡:封装被削弱。如果您控制程序包中的所有类,那么这不成问题。

支持静态,私有和最终方法

所有公共/受保护的Java方法都是隐式虚拟方法(与C++不同,在C++中,必须将它们声明为虚拟)。这意味着始终进行动态绑定,即,虚拟机在调用公共/受保护的方法之前必须始终确定对象的实际类型。这种查找成本很高,尤其是如果要调用的方法是从基类继承的。这是不欢迎使用深层树的主要原因。

要解决此问题:
1.支持静态方法,因为它们不受动态绑定的约束,这使得它们调用起来更快
2.如果方法不是静态的,请尝试将其设为私有。私有方法不能在派生类中被覆盖,因此虚拟机更容易找到它们
3.如果某个方法不能私有,请尽快将其定型。这将有助于虚拟机

使用本机API

只要平台提供本机代码来执行操作,就使用它!本机方法比您能想到的任何聪明的Java代码要快得多。
尤其是:
•使用
Util.arrayFillNonAtomic()
Util.arrayCopy()
Util.arrayCopyNonAtomic()
初始化或修改数组

•Util.arrayFillNonAtomic()对于在加密操作期间填充缓冲区特别有用
•使用Util.getShort(),Util.setShort()处理byte [] / short转换


避免不必要的初始化

Java Card虚拟机保证将新分配的数据成员和局部变量设置为0 / false / null。

不要将异常用于流程控制

使用异常进行流控制从来都不是一个好主意,Java Card也不例外。异常处理非常慢,除异常处理外,不应将其用于其他任何用途。换句话说,如果您正在考虑使用throw模拟goto,只需加倍努力,寻找更好的解决方案。

使用RAM缓冲区

写入EEPROM的速度大约比写入RAM的速度慢1,000倍,因此,执行的次数越少越好。您可以使用APDU缓冲区或瞬态数组来存储会话数据和临时结果。这对于中间解密解密操作特别有效,并且也更安全。

清理循环

访问实例成员比访问局部变量要昂贵。如果必须执行重复访问,则在第一次访问时将实例成员存储在本地变量中,然后仅使用该本地变量。数组访问(array [i],array.length)和方法调用也是如此。这将大大提高性能。

使用逻辑表达式的求值顺序

逻辑表达式(测试等)从左到右评估。如果条件失败,则不会评估其余测试。如果其中一种情况几乎总是错误,请首先进行检查:您将避免执行不必要的代码。
尽可能经常使用返回值

许多API的返回值都是有意义的:使用它可以节省不必要的操作。

谨慎使用事务(transactions)

由于许多本机API已经进行事务写操作,因此请确保必须在applet中明确使用事务。特别是,如果您不需要交易,请不要使用Util.arrayCopy(),请改用Util.arrayCopyNonAtomic()。

同时检查APDU的类(CLA)和指令(INS)

当使用OP Secure Messaging时,这特别有效,因为所有安全命令将使用两个不同的类(CLA)值。

同时检查P1和P2

尽可能同时检查P1和P2。您的代码将更快,更紧凑。

块加密的几种padding方案

PKCS5Padding和PKCS7Padding唯一不同的地方在于块的大小

在PKCS5Padding中, 明确定义Block的大小是8个字节
而在PKCS7Padding定义中,块的大小是不确定的,可以在1-255字节之间

PKCS5, PKCS7 有如下相同的特点:
1)填充的字节都是一个相同的字节
2)该字节的值,就是要填充的字节的个数

如果要填充8个字节,那么填充的字节的值就是0×8;
要填充7个字节,那么填入的值就是0×7;

如果只填充1个字节,那么填入的值就是0×1;

正是这种即使恰好是8个字节也需要再填充字节的规定,可以让解密的数据很确定无误的移除多余的字节。
因为解密恢复的明文的最后一个字节,就能告诉你 存在多少个填充字节

举例:假定块长度为 8,数据长度为 9,

数据: FF FF FF FF FF FF FF FF FF

PKCS7 填充后: FF FF FF FF FF FF FF FF FF 07 07 07 07 07 07 07

一种python 实现

    pad_len = 8 - (len(data) % self.block_size)
    if _pythonMajorVersion < 3:
        data += pad_len * chr(pad_len)
    else:
        data += bytes([pad_len] * pad_len)



    if _pythonMajorVersion < 3:
        pad_len = ord(data[-1])
    else:
        pad_len = data[-1]
    data = data[:-pad_len]

另外一种实现

BS = 8
pad = lambda s: s +  (BS - len(s) % BS) * bytes([BS - len(s) % BS])
unpad = lambda s : s[0:-(s[-1])]
#############
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-ord(s[-1])]

PKCS5 pad/unpad的 PHP实现

function pkcs5_pad ($text, $blocksize)
{
    $pad = $blocksize - (strlen($text) % $blocksize);
    return $text . str_repeat(chr($pad), $pad);
}

function pkcs5_unpad($text)
{
    $pad = ord($text{strlen($text)-1});

    if ($pad > strlen($text))
       return false;
    if (strspn($text, chr($pad), strlen($text) - $pad) != $pad)
       return false;
    return substr($text, 0, -1 * $pad);
}

还有一点需要说明的是:
块大小是 加密方案(加密算法)的一种固有属性,对于AES来说,块大小是128位(16字节)

填充是为了 让加密的输入, 匹配 输入块(一般是块加密算法中矩阵)的长度, 而不是 Key的长度。 输入块是 64位, Key可以是128位,256位。

严格来讲, PKCS5Padding 是不能用于 AES的,因为
PKCS5Padding的块大小,是8字节
AES的块大小,是16字节。
如果有人说 AES算法,使用 PKCS5Padding填充时, 实践上,他使用的是 PKCS7Padding

SSL3Padding跟PKCS5Padding只是稍有不同:PKCS5 Padding填充的值是 要填充的字节数, 而SSL 3 Padding 是要填充的字节数 减去1

所以,很容易得到 ssl3padding的php实现

function ssl3_pad ($text, $blocksize)
{
     $pad = $blocksize - (strlen($text) % $blocksize);
     return $text . str_repeat(chr($pad-1), $pad);
}


function ssl3_unpad($text)
{
     $text_len = strlen($text);
     $pad = ord($text{$text_len-1});
     $pad_len = $pad + 1;

     if ($pad_len > $text_len) {
         return false;
     }

     if (strspn($text, chr($pad), $text_len - $pad_len) != $pad_len) {
          return false;
     }

     return substr($text, 0, -1 * $pad_len);
}