#java #javacard #ecdsa
#java #javacard #ecdsa
Вопрос:
Я реализую код подписи с использованием ECDSA в Javacard.
Мой код выводит 0x0003 (NO_SUCH_ALGORITHM) в исключительной части, что означает, что эта карта не поддерживает алгоритм. Я этого не понимаю, потому что мой поставщик сказал мне, что он поддерживает ECC. Я пришел к выводу, что не знаю, как подписать контракт с ECDSA, и я хочу это знать.
Вот мой полный исходный код
package MyECDSA;
import javacard.framework.*;
import javacard.security.*;
import javacardx.crypto.*;
public class MyECDSA extends Applet{
private byte[] PLAINTEXT ;
private ECPrivateKey objECDSAPriKey=null; // Object for ECDSA Private Key
private ECPublicKey objECDSAPubKey=null; // Object for ECDSA Public Key
private KeyPair objECDSAKeyPair=null; // Object for ECDSA Key Pair
private Signature objECDSASign=null; // Object for ECDSA Signature
final static short BAS = 0;
public static void install(byte[] bArray, short bOffset, byte bLength){
new MyECDSA(bArray, bOffset, bLength);
}
private MyECDSA(byte bArray[], short bOffset, byte bLength){
PLAINTEXT = new byte[0x100] ; // Data file
Util.arrayFillNonAtomic(PLAINTEXT, BAS, (short)0x100, (byte)0);
register();
}
//======================================================================================
public void process(APDU apdu){
byte buf[] = apdu.getBuffer();
switch(buf[1])
{
//--------------------------------------------------------
case (byte)0xA4: break;
case (byte)0x46:
// Create ECDSA Keys and Pair
try {
// <<<<<<<<<<<<<<<< Here is the problem >>>>>>>>>>>>>>>>>
objECDSAKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_192);
//objECDSAKeyPair = new KeyPair(KeyPair.ALG_EC_F2M, KeyBuilder.LENGTH_EC_F2M_193);
}
catch(CryptoException c)
{
short reason = c.getReason();
ISOException.throwIt(reason);
}
ISOException.throwIt((short)0x9999); // for check
// Generate Key pair
objECDSAKeyPair.genKeyPair();
// Create Signature Object
objECDSASign = Signature.getInstance(Signature.ALG_ECDSA_SHA, false);
objECDSAPriKey = (ECPrivateKey)objECDSAKeyPair.getPrivate();
objECDSAPubKey = (ECPublicKey)objECDSAKeyPair.getPublic();
break;
case (byte)0x2E:
short Le = apdu.setOutgoing();
short sSignLen=0 ;
// Init with Private Key
objECDSASign.init(objECDSAPriKey, Signature.MODE_SIGN);
// Sign Data
sSignLen = objECDSASign.sign(PLAINTEXT, BAS, Le, buf, BAS);
apdu.setOutgoingLength(sSignLen);
apdu.sendBytes(BAS, sSignLen);
break;
//--------------------------------------------------------
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
return;
}
}
И команда APDU выглядит следующим образом
[ Card ] <== 00A4040007D4106509900090
[ Card ] ==> 9000
[ Card ] <== 0046000000
[ Card ] ==> 0003
Моя среда разработки выглядит следующим образом.
- ОС: Windows 7
- JCDK Версия 2.2.1
- Версия JDK 1.4.2
- Чип: NXP
- Терминал: бесконтактный считыватель смарт-карт ACR122 NFC
Я изменил свой код, чтобы задать параметры домена. Но карта по-прежнему выдает тот же результат (0x0003). Вот мой полный исходный код.
package MyECDSA;
import javacard.framework.*;
import javacard.security.*;
import javacardx.crypto.*;
public class MyECDSA extends Applet{
private byte[] PLAINTEXT ;
private ECPrivateKey objECDSAPriKey=null; // Object for ECDSA Private Key
private ECPublicKey objECDSAPubKey=null; // Object for ECDSA Public Key
private KeyPair objECDSAKeyPair=null; // Object for ECDSA Key Pair
private Signature objECDSASign=null; // Object for ECDSA Signature
final static short BAS = 0;
final static byte[] SecP192r1_P = { // 24
(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
(byte)0xFE,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF};
final static byte[] SecP192r1_A = { // 24
(byte)0xFC,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
(byte)0xFE,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF};
final static byte[] SecP192r1_B = { // 24
(byte)0xB1,(byte)0xB9,(byte)0x46,(byte)0xC1,(byte)0xEC,(byte)0xDE,(byte)0xB8,(byte)0xFE,
(byte)0x49,(byte)0x30,(byte)0x24,(byte)0x72,(byte)0xAB,(byte)0xE9,(byte)0xA7,(byte)0x0F,
(byte)0xE7,(byte)0x80,(byte)0x9C,(byte)0xE5,(byte)0x19,(byte)0x05,(byte)0x21,(byte)0x64};
final static byte[] SecP192r1_S = { // 20
(byte)0xD5,(byte)0x96,(byte)0x21,(byte)0xE1,(byte)0xEA,(byte)0x20,(byte)0x81,(byte)0xD3,
(byte)0x28,(byte)0x95,(byte)0x57,(byte)0xED,(byte)0x64,(byte)0x2F,(byte)0x42,(byte)0xC8,
(byte)0x6F,(byte)0xAE,(byte)0x45,(byte)0x30};
final static byte[] SecP192r1_G = { // 25
(byte)0x12,(byte)0x10,(byte)0xFF,(byte)0x82,(byte)0xFD,(byte)0x0A,(byte)0xFF,(byte)0xF4,
(byte)0x00,(byte)0x88,(byte)0xA1,(byte)0x43,(byte)0xEB,(byte)0x20,(byte)0xBF,(byte)0x7C,
(byte)0xF6,(byte)0x90,(byte)0x30,(byte)0xB0,(byte)0x0E,(byte)0xA8,(byte)0x8D,(byte)0x18,(byte)0x03};
final static byte[] SecP192r1_N = { // 24
(byte)0x31,(byte)0x28,(byte)0xD2,(byte)0xB4,(byte)0xB1,(byte)0xC9,(byte)0x6B,(byte)0x14,
(byte)0x36,(byte)0xF8,(byte)0xDE,(byte)0x99,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF};
final static short SecP192r1_H = 1;
//======================================================================================
public static void install(byte[] bArray, short bOffset, byte bLength){
new MyECDSA(bArray, bOffset, bLength);
}
private MyECDSA(byte bArray[], short bOffset, byte bLength){
PLAINTEXT = new byte[0x100] ; // Data file
Util.arrayFillNonAtomic(PLAINTEXT, BAS, (short)0x100, (byte)0);
register();
}
//======================================================================================
public void process(APDU apdu){
byte buf[] = apdu.getBuffer();
switch(buf[1])
{
//--------------------------------------------------------
case (byte)0xA4: break;
case (byte)0x46:
// Create ECDSA Keys and Pair
try {
// <<<<<<<<<<<<<<<< Here is the problem >>>>>>>>>>>>>>>>>
objECDSAPriKey = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_192, false);
ISOException.throwIt((short)0x8888); // for check
objECDSAPubKey = (ECPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, KeyBuilder.LENGTH_EC_FP_192, false);
// set EC Domain Parameters
objECDSAPubKey.setFieldFP(SecP192r1_P, BAS, (short)24);
objECDSAPubKey.setA(SecP192r1_A, BAS, (short)24);
objECDSAPubKey.setB(SecP192r1_B, BAS, (short)24);
objECDSAPubKey.setG(SecP192r1_G, BAS, (short)25);
objECDSAPubKey.setK(SecP192r1_H);
objECDSAPubKey.setR(SecP192r1_N, BAS, (short)24);
objECDSAKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_192);
}
catch(CryptoException c)
{
short reason = c.getReason();
ISOException.throwIt(reason); // for check
}
// On-Card Key Generation Process
objECDSAKeyPair.genKeyPair();
// Obtain Key References
objECDSAPriKey = (ECPrivateKey)objECDSAKeyPair.getPrivate();
objECDSAPubKey = (ECPublicKey)objECDSAKeyPair.getPublic();
// Create Signature Object
objECDSASign = Signature.getInstance(Signature.ALG_ECDSA_SHA, false);
break;
case (byte)0x2E:
short Le = apdu.setOutgoing();
short sSignLen=0 ;
// Init with Private Key
objECDSASign.init(objECDSAPriKey, Signature.MODE_SIGN);
// Sign Data
sSignLen = objECDSASign.sign(PLAINTEXT, BAS, Le, buf, BAS);
apdu.setOutgoingLength(sSignLen);
apdu.sendBytes(BAS, sSignLen);
break;
//--------------------------------------------------------
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
return;
}
}
Комментарии:
1. В чем ваша проблема / Какую часть вы не понимаете? Если у вас возникли проблемы, пожалуйста, укажите конкретные проблемы. Если вы хотите, чтобы кто-то взглянул на ваш код и высказал свое мнение, разместите его здесь: codereview.stackexchange.com
2. @VinceEmigh Вопрос на самом деле достаточно ясен для меня.
3. Помимо проблемы с поддержкой ЕС: обратите внимание, что ваши параметры имеют неправильный размер. Кажется, вы смешиваете параметры 224 и 192, и точка G, я думаю, должна быть несжатой точкой примерно в два раза больше размера ключа, начиная с
04
,4. @owlstead Теперь я сотрудничаю с поставщиком по поводу ECC. Похоже, что в карте есть какая-то проблема. В любом случае, спасибо.
Ответ №1:
В Java Card нет параметров домена EC по умолчанию. Требуется создать с KeyPair
помощью an ECPublicKey
и a ECPrivateKey
, для которых заданы параметры домена (так что точка W и секрет S могут быть оставлены пустыми). После этого можно вызвать genKeyPair()
, по крайней мере, если карта поддерживает криптографию с эллиптической кривой F (2m) или F (p) и указанный размер ключа.
ДОБАВЛЕНО
Обратите внимание, что чипы NXP JCOP могут требовать установки этих параметров для открытого и закрытого ключей. Параметры должны иметь либо размер ключа (для отдельных значений), либо несжатую точку эллиптической кривой. Значение G в вопросе, по-видимому, является сжатой точкой. Только кофактор (for setH
) должен иметь значение 1.
Обратите внимание, что только микросхемы с асимметричным сопроцессором могут поддерживать эллиптические кривые; не все карты создаются / настраиваются одинаково. Обратитесь к своему поставщику за подробной информацией.
Ответ №2:
Если попытка создать экземпляр определенного алгоритма (KeyPair.ALG_EC_FP и KeyBuilder.LENGTH_EC_FP_192 в вашем случае) завершается с ошибкой NO_SUCH_ALGORITHM, она либо не поддерживается вашей картой полностью (например, старое оборудование), либо отключена.
Проект JCAlgTester позволяет получить полный список поддерживаемых алгоритмов для вашей конкретной карты. Также доступна база данных результатов для нескольких разных карт (но лучше проверить вашу конкретную карту напрямую, загрузив апплет JCAlgTester).