如何在 javacard 小程序上签名并 return 签名到主机应用程序
How to sign on a javacard applet and return the signature to the host application
我在 javacard 小程序中有以下功能,它应该接收来自主机应用程序的质询,对其进行签名,然后 return 通过命令响应 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(); 以错误的方式检索挑战?还是我的签名全错了?
您正在尝试使用 RSA 密钥进行签名。但是,RSA 生成的签名的签名大小与以最小字节数编码的密钥大小(模数大小)相同。所以例如一个 2048 位的密钥产生大小为 ceil(2028D / 8D) = 256
字节的签名(最大响应大小,除非您使用扩展长度的 APDU)。
您应该永远不要 在Java 中创建字节数组,除非在创建class 或个性化小程序时。使用 new byte[]
在持久内存中创建的任何数组都可能会保留到垃圾收集器 运行,并且它可能会耗尽 EEPROM 或闪存。对于签名,您不需要持久内存。
如果您查看 Signature.sign
方法:
The input and output buffer data may overlap.
因此您可以只将签名生成到 APDU 缓冲区中。否则你可以在 JCSystem.makeTransientByteArray
创建的缓冲区中生成它,但如果你想将它传达给客户端,你必须将它复制到 APDU 缓冲区中。
请永远不要做以下事情:
String s = new String(card_signature);
签名与随机字节几乎没有区别,因此将其打印出来只会产生垃圾。如果您需要文本输出,请尝试对签名进行十六进制或 base 64 编码。或者将其打印为十进制数(但请注意,这可能会导致丢失值为 00
的前导字节)。
我在 javacard 小程序中有以下功能,它应该接收来自主机应用程序的质询,对其进行签名,然后 return 通过命令响应 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(); 以错误的方式检索挑战?还是我的签名全错了?
您正在尝试使用 RSA 密钥进行签名。但是,RSA 生成的签名的签名大小与以最小字节数编码的密钥大小(模数大小)相同。所以例如一个 2048 位的密钥产生大小为 ceil(2028D / 8D) = 256
字节的签名(最大响应大小,除非您使用扩展长度的 APDU)。
您应该永远不要 在Java 中创建字节数组,除非在创建class 或个性化小程序时。使用 new byte[]
在持久内存中创建的任何数组都可能会保留到垃圾收集器 运行,并且它可能会耗尽 EEPROM 或闪存。对于签名,您不需要持久内存。
如果您查看 Signature.sign
方法:
The input and output buffer data may overlap.
因此您可以只将签名生成到 APDU 缓冲区中。否则你可以在 JCSystem.makeTransientByteArray
创建的缓冲区中生成它,但如果你想将它传达给客户端,你必须将它复制到 APDU 缓冲区中。
请永远不要做以下事情:
String s = new String(card_signature);
签名与随机字节几乎没有区别,因此将其打印出来只会产生垃圾。如果您需要文本输出,请尝试对签名进行十六进制或 base 64 编码。或者将其打印为十进制数(但请注意,这可能会导致丢失值为 00
的前导字节)。