java card的rsa运算_Java Card开发指南(二)

标签(空格分隔): Java 智能卡

我们将从原理上理解Java Card开发中一些关键的技术信息。

名词解释

Applet:J2SE中也存在Applet这个概念。这里的Applet可以看做类似于Android中一个个APP。分为ROM Applet、事前发行和事后发行Applet。

AID:应用标识符。又区分为Package AID和Applet AID。相当于MAC地址的构造,前四个字节是IEEE分配的,后四个字节是厂商定义的内部序列号。Applet AID是Package AID的所有部分外加一些字节

CAP:Converted Applet。我们知道,J2SE中也存在Applet,这里沿袭了Applet的定义——小应用程序。由于智能卡的计算能力有限(据说是最小的计算平台),所以需要将二进制文件class进行削减,于是就构成了CAP

ATR:复位应答,卡-机交流的第一句话,包括协议之类的数据

DF:专用文件Dedicated File的缩写,DF的作用可以等同于计算机中的目录文件

MF:在一张卡片里(这里说的是卡片而不是某个应用)有且仅有一个特殊的DF,称为主文件MF,这个MF的FID默认为3F00,相当于计算机中的根目录,而且在任何时候MF都可以被选择

EF:基本信息文件Elementary File,也就是说通常情况下和应用相关的数据都会存放于EF中。

如果某个DF下没有子DF,只有若干EF,那么这个DF也被称作ADF,反之如果某个DF下除了有EF之外,还有子DF,那么这个父级的DF也被称作DDF。为了对文件进行访问,需要给文件分配一个特定的标识。无论是DF还是EF都会有对应的两个字节长ID标识,也就是所谓的FID。而DF还会有5-16个字节长的名字,也叫做AID。EF还会有一个5位长(范围从1到30)的短文件标识,就是SFI。

ADPU

CLA

INS

P1

P2

Lc

数据域

0x0

0xA4

0x4

0x0

AID的长度

AID 字节

APDU的全称是Application Protocol Data Unit,也就是在应用层实现的数据单元。其中读卡器发出的叫Command-APDU,卡回复的叫Response-APDU。

Command-APDU:

CLA

INS

P1

P2

Lc

数据域

0x0

0xA4

0x4

0x0

AID的长度

AID 字节

Le

数据域

SW1

SW2

0x00

Null

0x90

0x00

Response-APDU:

Le

数据域

SW1

SW2

0x00

Null

0x90

0x00

在卡-机通信开始之前,还有个请求和回复的过程。这时候我们可以收到一个ATR如:

ATR = 0x3b 0xf0 0x11 0x00 0xff 0x00

| TS | T0 | TA1,TB1,TC1,TD1 |T1,T2,...,T15 | TCK |

| :----: | :----: | :----: | :----: | :----: | :----: |

| 起始* | 格式*,决定接口和历史 | 接口(由T0决定:b5-TA1,b8-TD1) | 历史 | 校验 |

| 0x3B/0x3F | 0xF0 | 0x11 0x00 0xFF 0x00 | Null | Null |

Applet构成简介

我们以最简单的demo为例:

package hellojavacard;

import javacard.framework.APDU;

import javacard.framework.ISO7816;

import javacard.framework.Applet;

import javacard.framework.ISOException;

public class Appletcard extends Applet {

private Appletcard() {

}

public static void install(byte bArray[], short bOffset, byte bLength) throws ISOException {

new Appletcard().register(bArray, (short) (bOffset + 1), bArray[bOffset]);

}

public void process(APDU apdu) {

if (selectingApplet()) {

return;

}

byte[] buf = apdu.getBuffer();

switch (buf[ISO7816.OFFSET_INS]) {

case (byte) 0x00:

break;

default:

ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);

}

}

}

注册Applet

这一步是通过register()函数实现的,register()函数有两种形式,一种是不带参数的,一种是带参数的,这些参数在Applet的安装期间就进行了传递:

register(byte[] bArray,short bOffset,byte bLength)

#byte[] bArray - 包含安装参数的数组

#short bOffset - 安装参数在 bArray 中的起始位移

#byte Length - bArray 中的参数数据的字节数

这些初始化值是Applet开发者定义的,通常钱包应用可以包括记录数目,钱包ID,卡初始余额等。

选择Applet

CLA

INS

P1

P2

Lc

数据域

0x0

0xA4

0x4

0x0

AID的长度

AID 字节

在未被选择之前,Applet是处于挂起状态的。这一步是通过selectingApplet()实现的,SELECT APDU 命令是在 Java 卡平台上被标准化的唯一 APDU 命令。注意,正常情况下Applet只能通过成功的SELECT APDU命令执行,但有些卡可以通过配置缺省Applet的方式省去这一步骤。

C-APDU:

CLA

INS

P1

P2

Lc

数据域

0x0

0xA4

0x4

0x0

AID的长度

AID 字节

Le

数据域

SW1

SW2

0x00

Null

0x90

0x00

R-APDU:

Le

数据域

SW1

SW2

0x00

Null

0x90

0x00

与之对应的反向操作为deselect(),相当于进程阻塞操作block。在复位和掉电的情况下,取消选择不执行deselect()。

获取APDU

为了处理APDU,我们先要将其获取并存储至字节数组buffer。APDU buffer 是一个字节数组,其长度可通过方法 apdu_buffer.length确定。

public void process(APDU apdu){

//索取APDU

buffer byte[] apdu_buffer = apdu.getBuffer();

在调用一个 applet 的 process 方法时,在APDU buffer 中只有命令的前 5 个字节是可用的――前 4

个字节是APDU 头〔CLA,INS,P1,P2〕而第 5 个字节(P3)是一个附加的长度域。P3 的含义是隐含的, 由命令的 case 决定:

对于 case1,P3=0;

对于 case2,P3=Le,表示输出的响应数据的长度;

对于 case3 和 case4,P3=Lc,表示输入的命令数据的长度。

返回状态字

正常状态:返回状态字0x9000。(SW1:0x90;SW2:0x0)

在 命 令 处 理 过 程 中 的 任 何 地 方 , applet 终 止 操 作 并 通 过 调 用 静 态 方 法

ISOException.throwIt(reason) 抛出 ISOException 例外。

如果由底层 Java 卡系统检测到错误, JCRE 的行为是不确定的。例如, JCRE 可能抛出一个例

外 APDUException 或 TransactionException。

智能卡安全

从上面的APDU交互过程我们可以看出,交互的过程完全通过明文传输。这就好比TCP/IP协议实现了端-端的传输,踢进了临门一脚。对于智能卡传输过程中的安全性,我们就要借助应用层协议(如SSL/TLS)。智能卡的传输过程也是一样,我们需要对一些敏感的信息进行加密(比如卡交易金额),同时对消息的可靠性也有认证的需求(还比如卡交易金额)。另外,加密可以作为一个身份认证的标示(比如SIM卡与注册网络),这样可以将其视作一个令牌(token)。

javacard.security 包

类或接口

描述

Key

所有密钥的基接口

SecretKey

用于对称算法的密钥的接口

DESKey

代表 DES 或双密钥 3DES 或 3 密钥 3DES 的 8/16/24 字节密钥

PrivateKey

关于用于非对称算法的私钥的基接口

PublicKey

关于用于非对称算法的公钥的基接口

RSAPrivateKey

用于利用模/指数形式的 RSA 算法签名数据

RSAPrivateCrtKey

用于利用中国余数定理形式的 RSA 算法签名数据

RSAPublicKey

用于验证利用 RSA 算法签名数据时的签名

DSAKey

关于 DSA 算法私钥和公钥实现的基接口

DSAPrivateKey

用于利用 DSA 算法签名数据

DSAPublicKey

用于验证利用 DSA 算法的签名

KeyBuilder

生成一个密钥对象的 Factory 类

MessageDigest

关于 hash 算法的抽象基类

Signature

关于签名算法的抽象基类

RandomData

关于随机数生成的抽象基类

CryptoException

代表一个密码技术相关的例外

类或接口

描述

Cipher

提供关于加密和解密的密码技术 cipher 的功能。类 cipher 是一个抽象基类

KeyEncryption

能使密钥的实现访问加密的密钥数据

javacardx.crypto 包

类或接口

描述

Cipher

提供关于加密和解密的密码技术 cipher 的功能。类 cipher 是一个抽象基类

KeyEncryption

能使密钥的实现访问加密的密钥数据

下面将一些应用进行举例:

计算消息摘要

Public static MessageDigest GetInstance(byte algorithm,boolean externalAccess);

#第一个参数可以为ALG_SHA、ALG_MD5,和 ALG_RIPEMD160

#第二个参数设置的是Applet计算的MD结果可否被外部访问

这里以SHA-1为例,对三个字节数组m1、m2、m3计算摘要:

Public class myApp extends Applet

Private MessageDigest sha;

Public MyApplet()

{

sha = MessageDigest.getInstance(MessageDigest.ALG_SHA,false);

//把字节数组 m1 中的整个数据馈入消息摘要计算

sha.update(m1,(short)0,(short)(m1.length));

//再从字节数组 m2 中位移 0 起馈入 8 字节数据

sha.update(m2,(short)0,(short)8);

//将字节数组 m3 中的全部数据作为最后一批发送给消息摘要计算,并将 hash 值保存于字节数组 digest 中位移 0 开始的地方

sha.doFinal(m3,(short)0,(short)(m3.length),digest,(short)0);

}

产生密钥

类 KeyBuilder 定义了一个供你选择的选择参数集,使你选择密钥的类型和密钥的长度。例如,为了建 立一个长度为 64 字节(64*8 = 512 位)的 RSA 私钥,你调用 buildKey 方法如下:

RSAPrivateKey rsa_private_key;

rsa_private_key = (RSAPrivateKey)KeyBuilder.buildkey(KeyBuilder.TYPE_RSA_PRIVATE, KeyBuilder.LENGTH_RSA_512,false);#这里进行了强制类型转换是便于调用RSAPrivateKey接口中的方法setModulus和setExponent

注意,这里生成的密钥对象未被初始化,还需要设置模数Modulus(r)和指数Exponent(e)。

计算和验证签名

签名就是生成摘要和产生密钥并进行加密的复合过程。所以我们要先构造签名实例:

Signature signature;

Signature = Signature.GetInstance(Signature.ALG_DSA_SHA,false);

#第一个参数支持主流的签名算法

#第二个参数设置的是Applet计算的Signature结果可否被外部访问

因为签名要使用一个密钥,我们需要初始化这个 Signature 对象。为此,我们需要调用两个 init 方法

之一:

public void init(Key theKey,byte theMode);

public void init(Key theKey,byte theMode,byte[] bArray,short bOff,short bLen);

#第二个init方法环允许你在字节数组bArray中规定算法初始化参数。如初始向量IV

在一个非对称算法中,签名和验证不是使用同一个密钥。所以,你应在第二个参数 theMode 中指

出如何使用密钥。有两种模式,如类 Signature 中所定义:

MODE_SIGN--指出签名模式

MODE_VERIFY--指出验证模式

下面的代码计算来自数组 s1, s2,和 s3 的数据的签名:

Public class myApp extends Applet

Private Signature signature;

Public MyApplet()

{

Signature = Signature.GetInstance(Signature.ALG_DSA_SHA,false);

Signnature.init(Key theKey,byte theMode);

//或Signnature.init(Key theKey,byte theMode,byte[] bArray,short bOff,short bLen);

//输入字节数组 s1 中的数据

Signature.update(s1,(short)0,(short)(s1.length));

//输入字节数组 s2 中的数据

Signature.update(s2,(short)0,(short)(s2.length));

//作为最后一批数据输入字节数组 s3 中的数据并生成签名,放到数组 sig_buffer 中,从位移 0 起

Signature.sign(s3,(short)0,(short)(s3.length),sig_buffer,(short)0);

}

下面的例子表明如何验证上一例子中计算出来的签名:

Public class myApp extends Applet

Private Signature signature;

Public MyApplet()

{

Signature = Signature.GetInstance(Signature.ALG_DSA_SHA,false);

Signnature.init(Key theKey,byte theMode);

//或Signnature.init(Key theKey,byte theMode,byte[] bArray,short bOff,short bLen);

//输入字节数组 s1 中的数据

Signature.update(s1,(short)0,(short)(s1.length));

//输入字节数组 s2 中的数据

Signature.update(s2,(short)0,(short)(s2.length));

//作为最后一批数据输入字节数组 s3 中的数据并生成签名,放到数组 sig_buffer 中,从位移 0 起

Signature.sign(s3,(short)0,(short)(s3.length),sig_buffer,(short)0);

//输入数组 s3 中的最后一批数据并用计算出来的签名验证放在 sig_buffer 中的签名

If(Signature.verify(s2,(short)0,(short)(s2.length),sig_buffer,sig_offset,sig_length) != true){ISOException.throwIt(SW_WRONG_SIGNATURE);}

}

数据加密和解密

下面的例子以 CBC 模式 DES 算法建立一个 Cipher 对象。输入数据无 padding。该 Cipher 对象以一个

用于加密的 DES 密钥初始化:

Public class myApp extends Applet

Private Cipher cipher;

Public MyApplet()

{

Cipher = Cipher.getInstance(Cipher.ALG_DES_CBC_NO_PAD,false);

Cipher.init(des_key,Cipher.MODE_ENCRYPT);

//或Signnature.init(Key theKey,byte theMode,byte[] bArray,short bOff,short bLen);

//输入字节数组 s1 中的数据

Signature.update(s1,(short)0,(short)(s1.length));

//输入字节数组 s2 中的数据

Signature.update(s2,(short)0,(short)(s2.length));

//作为最后一批数据输入字节数组 s3 中的数据并生成签名,放到数组 sig_buffer 中,从位移 0 起

Signature.sign(s3,(short)0,(short)(s3.length),sig_buffer,(short)0);

//输入数组 s3 中的最后一批数据并用计算出来的签名验证放在 sig_buffer 中的签名

If(Signature.verify(s2,(short)0,(short)(s2.length),sig_buffer,sig_offset,sig_length) != true){ISOException.throwIt(SW_WRONG_SIGNATURE);}

}

接着,为了加密数据,利用 update 方法和 doFinal 方法:

public short update(byte[] inBuf,short inOffset,short inLength,byte[] outBuff,short outOffset);

public short doFinal(byte[] inBuf,short inOffset,short inLength,byte[] outBuff,short outOffset);

随机数据的生成

随机数是密码技术过程( procedures)经常需要的。为了建立一个随机数发生器,你要调用类javacard.security.randomData中的方法getInstance并指出一种算法。算法选择参数可为RandomData.ALG_PSEDO_RANDOM,它表示伪随机数生成算法实用程序;或者为RandomData.ALG_SECURE_RANDOM,它指的是密码技术上安全性很强的随机数生成算法。

为了获得一个随机数,调用generatedata方法如下:

RandomData random_data = randomdata.getInstance(RandomData.ALG_SECURE_RANDOM);

//种子提供于字节数组 seed 中

random_data.setSeed(seed,seed_offset,seed_length);

//把一个随机数写入字节数组 random_num 中

random_data.generatedata(random_num, random_num_offset, random_num_ length);

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值