java - 在 Java 中加载原始的 64 字节长 ECDSA 公钥

标签 java cryptography digital-signature public-key ecdsa

我有一个原始 (r,s) 格式的 ECDSA NIST P-256 公钥。似乎没有简单的方法可以将其加载到实现 java.security.interfaces.ECPublicKey 的对象中。

加载 64 字节公钥以用于检查签名的最简洁方法是什么?

最佳答案

EC 功能需要 Java 7,Base 64 编码器/解码器需要 Java 8,无需额外的库 - 只需简单的 Java。请注意,这实际上会在打印时将公钥显示为命名曲线,这是大多数其他解决方案无法做到的。 如果您有最新的运行时,this other answer更干净。

如果我们使用 ECPublicKeySpec 来做这个,这个答案会很困难。所以让我们作弊并使用 X509EncodedKeySpec 代替:

private static byte[] P256_HEAD = Base64.getDecoder().decode("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE");

/**
 * Converts an uncompressed secp256r1 / P-256 public point to the EC public key it is representing.
 * @param w a 64 byte uncompressed EC point consisting of just a 256-bit X and Y
 * @return an <code>ECPublicKey</code> that the point represents 
 */
public static ECPublicKey generateP256PublicKeyFromFlatW(byte[] w) throws InvalidKeySpecException {
    byte[] encodedKey = new byte[P256_HEAD.length + w.length];
    System.arraycopy(P256_HEAD, 0, encodedKey, 0, P256_HEAD.length);
    System.arraycopy(w, 0, encodedKey, P256_HEAD.length, w.length);
    KeyFactory eckf;
    try {
        eckf = KeyFactory.getInstance("EC");
    } catch (NoSuchAlgorithmException e) {
        throw new IllegalStateException("EC key factory not present in runtime");
    }
    X509EncodedKeySpec ecpks = new X509EncodedKeySpec(encodedKey);
    return (ECPublicKey) eckf.generatePublic(ecpks);
}

用法:

ECPublicKey key = generateP256PublicKeyFromFlatW(w);
System.out.println(key);

这背后的想法是创建一个临时的 X509 编码 key ,它以公共(public)点 w 结束。之前的字节包含命名曲线的 OID 和结构开销的 ASN.1 DER 编码,以字节 04 结尾,指示未压缩点。 Here is an example结构是什么样的,对 32 字节的 X 和 Y 使用值 1 和 2。

为创建 header 而删除的未压缩点值的 32 字节 X 和 Y 值。这只有效,因为该点是静态大小的 - 它在末端的位置仅由曲线的大小决定。

现在函数 generateP256PublicKeyFromFlatW 所需要做的就是将接收到的公共(public)点 w 添加到 header 中,并通过为 X509EncodedKeySpec< 实现的解码器运行它.


上面的代码使用原始的、未压缩的公共(public) EC 点——只有 32 字节的 X 和 Y——没有值为 04 的未压缩点指示符。当然支持65字节的压缩点也很容易:

/**
 * Converts an uncompressed secp256r1 / P-256 public point to the EC public key it is representing.
 * @param w a 64 byte uncompressed EC point starting with <code>04</code>
 * @return an <code>ECPublicKey</code> that the point represents 
 */
public static ECPublicKey generateP256PublicKeyFromUncompressedW(byte[] w) throws InvalidKeySpecException {
    if (w[0] != 0x04) {
        throw new InvalidKeySpecException("w is not an uncompressed key");
    }
    return generateP256PublicKeyFromFlatW(Arrays.copyOfRange(w, 1, w.length));
}

最后,我使用 base 64 生成常量 P256_HEAD head 值:

private static byte[] createHeadForNamedCurve(String name, int size)
        throws NoSuchAlgorithmException,
        InvalidAlgorithmParameterException, IOException {
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
    ECGenParameterSpec m = new ECGenParameterSpec(name);
    kpg.initialize(m);
    KeyPair kp = kpg.generateKeyPair();
    byte[] encoded = kp.getPublic().getEncoded();
    return Arrays.copyOf(encoded, encoded.length - 2 * (size / Byte.SIZE));
}

调用者:

String name = "NIST P-256";
int size = 256;
byte[] head = createHeadForNamedCurve(name, size);
System.out.println(Base64.getEncoder().encodeToString(head));

关于java - 在 Java 中加载原始的 64 字节长 ECDSA 公钥,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30445997/

相关文章:

java - 崩溃后,Mongodb java 驱动程序无法重新连接到主节点

Java Servlet - 需要使用 session 跟踪和泛型

algorithm - 什么是最好的低技术协议(protocol)来模拟从帽子里抽出名字并确保保密?

security - 高效的 OAuth2.0 服务器/提供程序如何工作?

c# - PDF 签名 - 嵌入单独签名的哈希

java - 单击时复制子 JFrame

java - 在 Java 8 中尝试通过 JDBC-ODBC 连接到 .accdb 文件时出现 ClassNotFoundException

android - 将 AES 与 AndroidKeyStore 结合使用

asp.net - 加密 key 存储在哪里?

azure - Azure Key Vault 中的 AATL 文档签名证书