13

我正在尝试在 Java 应用程序中实现基于 Yubikey NEO OpenPGP 智能卡小程序的 PGP 加密。这似乎是一门黑暗的艺术,用谷歌搜索这些东西并不容易,但这是我到目前为止的地方:

  1. 初始化卡,使用 gpg 工具生成密钥。它通常有效。我有.asc格式的公钥并设法将其加载到org.bouncycastle.openpgp

  2. javax.smartcardio使用API连接到 USB 加密狗中的智能卡。

  3. 选择 OpenPGP 小程序

    val pgpAID = bytes(0xD2, 0x76, 0x00, 0x01, 0x24, 0x01)
    val answer = cardChannel.transmit(CommandAPDU(0x00, 0xA4, 0x04, 0x00, pgpAID))
    
  4. 成功向卡出示正确的 PIN 码

    val pin = "123456"
    return bytes(0x00, 0x20, 0x00, 0x82, pin.length) + pin.toByteArray(Charsets.UTF_8)
    
  5. 发送准成功(见下文)decipher命令

    bytes(0x00, 0x2a, 0x80, 0x86, data.size) + data + bytes(0x00)
    

    当 时data = "xxxx".toByteArray(),结果为SW=9000(= success) 但不返回任何数据。这是一个幼稚的测试,因为第 52 页的OpenPGP 小程序文档提到

    命令输入(填充指示字节除外)应在加密前根据 PKCS#1 进行格式化。

我不知道如何加密数据并将其转换为 PKCS#1 格式。

我还尝试阅读Yubico OpenPGP 卡实现测试,但它只提供了另一个“失败”示例(第 196 行)。我尝试运行它,但结果不同:测试预期SW=0050(指示异常?)而我得到的是SW=6f00(根据本文档,没有精确的诊断)。

我用整个代码创建了一个GitHub 存储库。它是用 Kotlin 编写的,但应该易于阅读。

4

1 回答 1

6

您的问题有些困惑,但我很确定您想使用与智能卡上的 RSA 私钥相对应的 RSA 公钥创建 PGP 加密消息,然后使用智能卡上的 RSA 私钥(帮助)解密它们。PGP(几乎与其他所有内容一样)使用混合加密,因此相关部分的 PGP 加密消息包括:

  • 使用随机生成的工作密钥使用适当的对称算法(如 TDES 或 AES)加密的实际消息,称为 K
  • 工作密钥 K 加上一些由 RSA 使用接收者的公钥加密的元数据和原始 PKCS#1 标准定义的填充,现在正式称为 RSAES-PKCS1-v1_5,但仍被广泛称为 PKCS1,有些不准确。

您不需要执行加密步骤,因为任何实现该标准的软件都可以这样做,包括 GnuPG 或 BouncyCastle 的bcpg库。如果您想自己做,也许对于使用伪造的 K 而没有真实消息的测试数据,您需要进行填充RSA 模幂运算;在 Java 中,至少是带有标准加密提供程序的 Oracle 或 openjdk Java,您可以以通常的方式使用javax.crypto.Cipher获得的 with .getInstance("RSA/ECB/PKCS1Padding")

“PKCS1”加密填充(用于 RSA)如该文档第 52 页底部和第 53 页顶部所述,其内容相同,但格式与当前 OpenPGP 规范(及更早版本)相同,该规范指的是实际上与当前的 PKCS#1 规范(和更早的规范)相同,所有这些都说它是:

  • 一个字节 00
  • 一个字节 02
  • 足够的非零随机字节使结果具有正确的长度并且是安全的
  • 一个字节 00
  • “明文”,对于 PGP 加密,它实际上是按 PGP 规范中指定的格式格式化的工作对称密钥K。

注意段落开头

在AES算法的情况下

似乎用于不同的选项,而不是 PGP AFAICS,在上一页中描述为

通过选项(在扩展功能中宣布)该卡支持使用存储在特殊 DO (D5) 中的 AES 密钥对纯文本进行解密。如果不存在证书或公钥并且外部世界与卡有共同的秘密,这将很有用。

所以忽略它。

于 2015-11-22T20:28:18.550 回答