Java 在Android中以AES-ECDH-MAC格式解密来自ATMEL的消息

Java 在Android中以AES-ECDH-MAC格式解密来自ATMEL的消息,java,android,encryption,cryptography,atmel,Java,Android,Encryption,Cryptography,Atmel,我需要读取一条加密信息,它是从一个装有ATMEL芯片的设备上发送的 通信流程附于此处: 我做了所有关于套接字通信和ECDSA签名验证的事情。 现在我被困在解密阶段。 示例解密所需的数据如下所示: // 0 - private key (prv-h, android host) : "308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104206cb80a311272dae8a6bf01273c865a7d289

我需要读取一条加密信息,它是从一个装有ATMEL芯片的设备上发送的 通信流程附于此处:

我做了所有关于套接字通信和ECDSA签名验证的事情。 现在我被困在解密阶段。 示例解密所需的数据如下所示:

// 0 - private key (prv-h, android host) : "308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104206cb80a311272dae8a6bf01273c865a7d2890c16436330b88cb53d2bc55133f23a00a06082a8648ce3d030107a144034200046342f6b12beab7dd829bb95cee5073c8968e1cb406c185ab6dfd8889df0aec1f7ed1a31af57a0df00454cd683d83ee490e6747adbb6d6d68ab345149e7de1366"
// 1 / public ephemereal key (device pub-x): "04F0442494445A67606873A943F228A11FEE28B56502B3753E1FD4B5A369BA62B9DF0965C27E069E4997893CDBCC06B72A39907632FF6E4740597970F3DBEC9A17"
// 2 / encrypted message: "5A92B516C7F6C29168879D913F4D6210EC689419C16B67FB17365CB8C2363D80" "C263D7389158EB0ADF4470FC689CA6726F87EC82C3DCEAE49005C90230034DF7" (2 blocks of 64 bytes. I still don't know if i can collate them or decrypt one by one)
// 3 / iv (generated by device and sent) : "28395B5C43A82E3951B153A33B10B01C"
// 4 / randNum1 (generated by android and previously sent to device): "9f7ed0f97069b01b097f1e6b7e28465678a0b7cd45cfb354de0632b308879f94"
我尝试使用此处找到的流进行解密:

但我发现了一些不同之处,总的来说是先验证后解密,而我需要一个解密后mac的解决方案

此外,该解决方案依赖于我认为我没有的“标签”

而且,该解决方案不使用以前由主机发送的randNum

目前,我可以做到:

public static String DeCrypt(String privateKey_s, String ephemeralPublicKey_s, String message_s, String tag_s, String iv_s, String randNum1_s) throws Exception {
    // premasterkey = ecdh(private, public)
    PrivateKey privateKey = FromHexStringToPrivateKey(privateKey_s);
    PublicKey ephemeralPublicKey = FromCompactStringToPublicKey(ephemeralPublicKey_s);
    KeyAgreement ka = KeyAgreement.getInstance("ECDH");
    ka.init(privateKey);
    ka.doPhase(ephemeralPublicKey, true);
    byte[] preMasterKey = ka.generateSecret();
在这里,我得到一个16字节(32个十六进制字符)的preMasterKey。我认为这是正确的。但是 从这一点上说,我不知道如何做好。我看到参考代码将公钥与共享密钥(preMasterKey)混合以获得会话密钥(加密密钥),但我的流程要求会话密钥从randNum1和preMasterKey派生

    byte[] randNum1 = hexStringToByteArray(randNum1_s);
    byte[] message = hexStringToByteArray(message_s);
    byte[] iv = hexStringToByteArray(iv_s);

    // Deriving encryption and mac keys.
    HKDFBytesGenerator hkdfBytesGenerator = new HKDFBytesGenerator(new SHA256Digest());
    byte[] khdfInput = ByteUtils.concatenate(randNum1, preMasterKey);
    hkdfBytesGenerator.init(new HKDFParameters(khdfInput, null, "Android".getBytes(Charset.forName("UTF-8"))));
    byte[] encryptionKey = new byte[16];
    hkdfBytesGenerator.generateBytes(encryptionKey, 0, 16);
    byte[] macKey = new byte[16];
    hkdfBytesGenerator.generateBytes(macKey, 0, 16);

    // Verifying Message Authentication Code (aka mac/tag)
    Mac macGenerator = Mac.getInstance("HmacSHA256", "BC");
    macGenerator.init(new SecretKeySpec(macKey, "HmacSHA256"));
    byte[] expectedTag = macGenerator.doFinal(messaggio);
    //    if (!isArrayEqual(tag, expectedTag)) {
    //        throw new RuntimeException("Bad Message Authentication Code!");
    //      }

    // Decrypting the message.
    Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
    cipher.init(
        Cipher.DECRYPT_MODE,
        new SecretKeySpec(encryptionKey, "AES"),
    new IvParameterSpec(iv));
    return new String(cipher.doFinal(messaggio));
}

附录

也许我的私钥格式不正确

我通过这段代码生成了这对:

public String GenKeysHex() throws Exception {
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
    kpg.initialize(256);
    KeyPair keyPair = kpg.generateKeyPair();
    PrivateKey prv = keyPair.getPrivate();
    PublicKey pub = keyPair.getPublic();

    byte[] prvBytes = prv.getEncoded();
    byte[] pubBytes = pub.getEncoded();

    String result1 = new BigInteger(1, prvBytes).toString(16);
    String result2 = new BigInteger(1, pubBytes).toString(16);

    String result3 = FromLongToCompactPublic(result2);

    return result3 + "," + result1 + "," + result2 ;
}

public static PrivateKey FromHexToPrivateKey(String Hex) throws Exception {
    byte[] hardcodedPrivate = hexStringToByteArray(Hex);
    PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(hardcodedPrivate);
    KeyFactory factory = KeyFactory.getInstance("EC");
    PrivateKey privateKey = factory.generatePrivate(privateSpec);
    return privateKey;
}

public static String FromPrivateKeyToHex(PrivateKey key) throws Exception {
    byte[] prvBytes = key.getEncoded();
    String result1 = new BigInteger(1, prvBytes).toString(16);
    return result1;
}
这是因为我需要创建一个库,并且我需要以字符串格式交换导入/导出数据,所以我检查了私钥的文本/十六进制格式是否通过其他两个例程保持一致

我想知道如何以更紧凑的形式获取私钥



方案中的MAC根本不用于消息身份验证,它只是用作KDF(密钥派生函数)。它派生加密密钥和用于设备实体身份验证的值(
DevMacCalc
)。虽然您可以从链接的代码中借用一些东西,例如算法实例化和初始化,但它不应该作为解密例程的起点。非常感谢。我完全卡住了。芯片是ATEC508A,我找不到任何东西可以告诉我如何从MAC(randNum1,preMasterKey)获取sessionKey。我完全不知道加密技术和Java。我只是一个“算法积分器”。我试着四处搜索,但我找不到任何在两个异构系统之间结合ECDH、MAC和AES通信的东西。你能告诉我什么地方吗?MAC输出的(最左边的)字节直接用作键或值来比较KDF结构。我对从事这类服务的人员不是很了解。我几乎不用思考就可以做到,但我没有所需的公司/时间等。除了其他问题,您的私钥看起来很长……这是因为(经过一点调查)它实际上是一个DER编码的公钥,而不是私钥。因此,即使我们能够计算出密钥派生应该如何工作,这里也没有足够的信息来解密任何东西,我们有两个公钥,而不是私钥和公钥。您方案中的MAC根本不用于消息身份验证,它只是用作KDF(密钥派生函数)。它派生加密密钥和用于设备实体身份验证的值(
DevMacCalc
)。虽然您可以从链接的代码中借用一些东西,例如算法实例化和初始化,但它不应该作为解密例程的起点。非常感谢。我完全卡住了。芯片是ATEC508A,我找不到任何东西可以告诉我如何从MAC(randNum1,preMasterKey)获取sessionKey。我完全不知道加密技术和Java。我只是一个“算法积分器”。我试着四处搜索,但我找不到任何在两个异构系统之间结合ECDH、MAC和AES通信的东西。你能告诉我什么地方吗?MAC输出的(最左边的)字节直接用作键或值来比较KDF结构。我对从事这类服务的人员不是很了解。我几乎不用思考就可以做到,但我没有所需的公司/时间等。除了其他问题,您的私钥看起来很长……这是因为(经过一点调查)它实际上是一个DER编码的公钥,而不是私钥。因此,即使我们能够计算出密钥派生应该如何工作,这里也没有足够的信息来解密任何东西,我们有两个公钥,而不是一个私钥和一个公钥。