Javacard 中的 ECDSA 签名

Posted

技术标签:

【中文标题】Javacard 中的 ECDSA 签名【英文标题】:ECDSA Signature in Javacard 【发布时间】:2014-06-28 14:07:22 【问题描述】:

我正在使用 Javacard 中的 ECDSA 实现签名代码。

我的代码在异常部分输出 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 芯片:恩智浦 终端: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; 



【问题讨论】:

你有什么问题/你不明白哪一部分?如果您遇到问题,请说明具体问题。如果您希望有人查看您的代码以发表意见,请在此处发布:codereview.stackexchange.com @VinceEmigh 这个问题对我来说其实已经很清楚了。 除了 EC 支持的问题:请注意,您的参数大小不正确。你似乎混合了 224 和 192 参数,我认为 G 点应该是一个未压缩点,大约是密钥大小的两倍,从 04 开始, @owlstead 现在,我正在与供应商合作讨论 ECC。卡好像有问题。无论如何,谢谢。 【参考方案1】:

Java Card 中没有默认的 EC 域参数。需要使用已设置域参数的ECPublicKeyECPrivateKey 创建KeyPair(因此点W 和秘密S 可以保持为空)。之后,可以调用genKeyPair(),至少如果卡支持 F(2m) 或 F(p) 椭圆曲线加密和指定的密钥大小。


添加

请注意,NXP JCOP 芯片可能需要为公共和私人密钥设置这些参数。参数应具有密钥大小(用于单独的值)或未压缩椭圆曲线点。问题中 G 的值似乎是一个压缩点。只有辅因子(setH)应该只有值 1。

请注意,只有带有非对称协处理器的芯片才能支持椭圆曲线;并非所有卡都是平等创建/配置的。请联系您的供应商了解详情。

【讨论】:

【参考方案2】:

如果尝试创建特定算法的实例(在您的情况下为 KeyPair.ALG_EC_FP 和 KeyBuilder.LENGTH_EC_FP_192)失败并显示 NO_SUCH_ALGORITHM,则您的卡完全不支持(例如,旧硬件)或禁用它。

Project JCAlgTester 允许您获取特定卡支持的算法的完整列表。多个不同卡片的结果数据库也可用(但最好通过上传 JCALgTester 小程序直接检查您的特定卡片)。

【讨论】:

以上是关于Javacard 中的 ECDSA 签名的主要内容,如果未能解决你的问题,请参考以下文章

Javascript(椭圆)上的 ECDSA 签名验证失败 [关闭]

如何从 Crypto++ ECDSA 中的签名体中获取签名长度

创建由其他 ECDSA 证书签名的 ECDSA 证书

sCrypt 中的 ECDSA 签名验证

如何使用证书中的公钥检查 ECDSA(在 p-256 上)签名

验证在 JavaCard 上签名的数据时出现 Java BadPaddingException