【问题标题】:How to initialize a EC key pair with an existing key pair in JavaCard?如何使用 JavaCard 中的现有密钥对初始化 EC 密钥对?
【发布时间】:2016-02-04 07:07:53
【问题描述】:

我已经有一个二进制格式的 EC 密钥对 (secp256r1),它存储在字节数组中,如下所示:

// private key, 32 bytes
byte[] privKey = {0x4c, (byte)0xc7, (byte)0xcf, 0x68, (byte)0x91, 0x18, (byte)0x96, (byte)0xc8, (byte)0xe2, (byte)0xf9, (byte)0xc8, (byte)0xcc, 0x2f, 0x7f, 0x0a, (byte)0xa2, 0x1c, 0x6a, (byte)0xcb, (byte)0xba, 0x38, 0x1c, 0x10, (byte)0x9a, (byte)0xfe, (byte)0x91, 0x18, (byte)0xf6, (byte)0xca, (byte)0xd9, 0x0f, 0x0b};
//public key, 65 bytes, which is contained in a X.509 certificate
byte[] pubKey = {0x04, 0x72, (byte)0x9a, 0x71, (byte)0xd0, (byte)0x81, 0x62, 0x42, (byte)0x84, (byte)0x92, (byte)0xf2, (byte)0xd9, 0x61, (byte)0x92, 0x4d, 0x37, 0x44, 0x3a, 0x4f, 0x1b, (byte)0xda, 0x58, 0x0f, (byte)0x8a, (byte)0xea, 0x29, 0x20, (byte)0xd2, (byte)0x99, 0x7c, (byte)0xbe, (byte)0xa4, 0x39, 0x60, (byte)0xce, 0x72, (byte)0x9e, 0x35, (byte)0xc1, (byte)0xf7, 0x40, (byte)0x92, (byte)0xf2, 0x25, 0x0e, 0x60, 0x74, (byte)0x82, 0x3f, (byte)0xc5, 0x7f, 0x33, 0x60, (byte)0xb7, (byte)0xcd, 0x39, 0x69, (byte)0xc3, (byte)0xc3, 0x12, 0x5e, (byte)0xce, 0x26, 0x5c, 0x29};

此 EC 密钥对由 openssl 生成。我想将此 EC 密钥对存储在我的 javacard 小程序中,以便我每次都可以使用此 EC 私钥对消息进行签名。

但我在 javacard 3 中没有找到任何合适的 API 来设置 EC 密钥对。

我使用这个项目https://github.com/Yubico/ykneo-curves/blob/master/applet/src/com/yubico/ykneo/curves/SecP256r1.java来设置secp256r1中的参数。

更新
我确实在ECPublicKey中设置了参数setW,在ECPrivateKey中设置了setS,其他参数根据https://github.com/Yubico/ykneo-curves/blob/master/applet/src/com/yubico/ykneo/curves/SecP256r1.java设置。像这样:

    privKey.setFieldFP(p, (short) 0, (short) p.length);
    privKey.setA(a, (short) 0, (short) a.length);
    privKey.setB(b, (short) 0, (short) b.length);
    privKey.setG(G, (short) 0, (short) G.length);
    privKey.setR(r, (short) 0, (short) r.length);

    byte[] privData = {(byte)0x25, (byte)0xc9, (byte)0xec, (byte)0xdc, (byte)0x4c, (byte)0x59, (byte)0xa3, (byte)0xe0, (byte)0x4f, (byte)0x01, (byte)0x56, (byte)0x97, (byte)0xf3, (byte)0xcb, (byte)0x60, (byte)0x5b, (byte)0x84, (byte)0x49, (byte)0x45, (byte)0x3a, (byte)0xe2, (byte)0x0e, (byte)0xd1, (byte)0xbd, (byte)0xc0, (byte)0xa7, (byte)0xe1, (byte)0xfa, (byte)0x82, (byte)0xee, (byte)0x3c, (byte)0x73};
    privKey.setS(privData, (short) 0, (short) privData.length);

    pubKey.setFieldFP(p, (short) 0, (short) p.length);
    pubKey.setA(a, (short) 0, (short) a.length);
    pubKey.setB(b, (short) 0, (short) b.length);
    pubKey.setG(G, (short) 0, (short) G.length);
    pubKey.setR(r, (short) 0, (short) r.length);

    byte[] pubData = {0x04, 0x00, (byte)0xb9, (byte)0x8f, (byte)0xcf, (byte)0xc3, (byte)0xc0, (byte)0xae, (byte)0x95, 0x6a, 0x5b, 0x12, 0x6d, (byte)0xbe, 0x43, (byte)0xe4, 0x7f, 0x09, 0x0d, (byte)0xde, 0x02, (byte)0xd2, 0x6b, 0x28, (byte)0x86, (byte)0xed, 0x2b, (byte)0xd7, (byte)0xe2, (byte)0xc2, 0x69, (byte)0xc1, (byte)0x89, (byte)0xb2, 0x53, (byte)0x96, (byte)0xc1, 0x2d, (byte)0xbf, 0x4c, 0x30, (byte)0xae, (byte)0xd5, (byte)0xd5, 0x3c, (byte)0xb5, (byte)0xf9, 0x3b, 0x20, 0x37, (byte)0x83, (byte)0x88, (byte)0x9f, 0x34, 0x74, (byte)0xf5, 0x6c, (byte)0x97, 0x1e, 0x0a, (byte)0xa9, (byte)0xe7, (byte)0xfa, (byte)0xa6, 0x69};
    pubKey.setW(pubData, (short) 0, (short)pubData.length);

现在我在消息上签名:

    // the class Secp256r1 can be found in above link
    pair = SecP256r1.newKeyPair();

    ECPublicKey pubKey = (ECPublicKey) pair.getPublic();
    ECPrivateKey privKey = (ECPrivateKey) pair.getPrivate();


    byte[] data = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};


    signature = Signature.getInstance(Signature.ALG_ECDSA_SHA, false);
    signature.init(pair.getPrivate(), Signature.MODE_SIGN);

    byte[] signData = new byte[127];
    short sendLen = signature.sign(data, (short) 0, (short) data.length, buffer, (short) 0);
    apdu.setOutgoingAndSend((short) 0, sendLen);

我发送了几个 APDU 来调用这个代码片段。但每次我收到不同的标志信息。为什么会发生这种情况?

【问题讨论】:

    标签: java key javacard


    【解决方案1】:

    ECPublicKey 上有方法setW(...) 和在ECPrivateKey 上的setS(...)

    棘手的部分是您的keyPair.getPublic()keyPair.getPrivate() 返回通用接口。你必须施放它们:

    KeyPair keyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256);
    ECPublicKey pub = (ECPublicKey) keyPair.getPublic();
    ECPrivateKey priv = (ECPrivateKey) keyPair.getPrivate();
    pub.setW(pubBytes, (short) 0, (short) pubBytes.length);
    priv.setS(privBytes, (short) 0, (short) privBytes.length);
    //do not forget to set parameters of your curve to both private and public key HERE!!!
    

    https://docs.oracle.com/javacard/3.0.5/api/javacard/security/ECPublicKey.html

    void setW(byte[] 缓冲区, 短偏移, 长度短) 抛出 CryptoException

    设置包含公钥的曲线点。重点 应按照 ANSI X9.62 指定为八位字节字符串。一个特定的 实现不需要支持压缩形式,但必须支持 点的未压缩形式。纯文本数据格式为 大端和右对齐(最低有效位是最少的 最后一个字节的有效位)。输入参数数据被复制到 内部表示。

    https://docs.oracle.com/javacard/3.0.5/api/javacard/security/ECPrivateKey.html

    void setS(byte[] 缓冲区, 短偏移, 长度短) 抛出 CryptoException

    设置密钥的值。纯文本数据格式为 大端和右对齐(最低有效位是最少的 最后一个字节的有效位)。输入参数数据被复制到 内部表示。

    更新答案:

    基于椭圆曲线的签名包含一个随机数,这就是为什么每次计算签名都会得到不同结果的原因。这是一项功能,而不是错误。

    【讨论】:

    • 听从您的建议后,我更新了我的问题。但是我遇到了一个新问题:签名信息每次都不一样。这应该是每次签名使用的EC密钥对不同造成的吧?
    • 我会将签名和EC公钥发送到卡外端(如Android手机的应用程序)。由于卡外方不知道随机数,能否用公钥验证这个签名?谢谢你的帮助@vojta
    • @YangZhou 是的,当然。随机数写在您的输出签名字节中。卡外方只需要公钥和签名,不需要其他额外信息。
    猜你喜欢
    • 2012-04-28
    • 2017-03-02
    • 2018-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多