我在javacard applet中有以下函数,它应该从宿主应用程序接收一个挑战,签名,然后通过命令响应apdu通信将它返回给主机 .
private void sign(APDU apdu) {
if(!pin.isValidated())ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED);
else{
byte[] buffer = apdu.getBuffer();
byte [] output = new byte [20];
short length = 20;
short x =0;
Signature signature =Signature.getInstance(Signature.ALG_RSA_SHA_PKCS1, false);
signature.init(privKey, Signature.MODE_SIGN);
short sigLength = signature.sign(buffer, offset,length, output, x);
//This sequence of three methods sends the data contained in
//'serial' with offset '0' and length 'serial.length'
//to the host application.
apdu.setOutgoing();
apdu.setOutgoingLength((short)output.length);
apdu.sendBytesLong(output,(short)0,(short)output.length);
}
}
主机按如下方式计算质询并将其发送到javacard小程序进行签名:
//produce challenge
SecureRandom random = SecureRandom . getInstance( "SHA1PRNG" ) ;
byte [ ]bytes = new byte [ 20 ] ;
random . nextBytes ( bytes) ;
CommandAPDU challenge;
ResponseAPDU resp3;
challenge = new CommandAPDU(IDENTITY_CARD_CLA,SIGN_CHALLENGE, 0x00, 0x00,bytes ,20 );
resp3= c.transmit(challenge);
if(resp3.getSW()==0x9000) {
card_signature = resp2.getData();
String s = new String(card_signature);
System.out.println("signature " + s);
}else System.out.println("Challenge signature error: " + resp3.getSW());
正如您所看到的,我检查了成功和不成功的签名,但我打印出以下内容:
Challenge signature error:28416
我到底哪里出错了?是否有可能使用`byte [] buffer = apdu.getBuffer();以错误的方式检索挑战 . 还是我的签名都错了?
1 回答
您正尝试使用RSA密钥进行签名 . 但是,RSA生成的签名的签名大小与以最小字节数编码的密钥大小(模数大小)相同 . 所以例如2048位密钥导致签名的大小为
ceil(2028D / 8D) = 256
字节(最大响应大小,除非您使用扩展长度APDU) .除非在创建类或个性化applet时,否则不应在Java中创建字节数组 . 使用
new byte[]
在持久性存储器中创建的任何阵列可能会一直保留到垃圾收集器运行,并且可能会耗尽EEPROM或闪存 . 对于签名,您不需要持久性内存 .如果你看一下Signature.sign方法:
因此,您只需将签名生成到APDU缓冲区中即可 . 否则,您可以在
JCSystem.makeTransientByteArray
创建的缓冲区中生成它,但如果要将其传送到客户端,则无论如何都必须将其复制到APDU缓冲区中 .请不要做以下事情:
签名几乎与随机字节无法区分,因此将其打印出来只会产生垃圾 . 如果需要文本输出,请尝试十六进制或base 64编码的签名 . 或者将其打印为十进制数字(但请注意,这可能会导致丢失带有值
00
的前导字节) .