无法从 Java 读取 OpenSSL 生成的 ECDSA 密钥:InvalidKeySpecException

Posted

技术标签:

【中文标题】无法从 Java 读取 OpenSSL 生成的 ECDSA 密钥:InvalidKeySpecException【英文标题】:Can't read OpenSSL-generated ECDSA key from Java: InvalidKeySpecException 【发布时间】:2017-02-14 19:34:30 【问题描述】:

我创建了一个 ECDSA 密钥对并使用 OpenSSL 导出了公钥,但我无法从 Java 中读取公钥。

OpenSSL 中的密钥生成:

$ openssl ecparam -name secp256k1 -genkey -noout -outform DER | openssl ec -inform DER -pubout -outform DER > secp256k1.public.der

从文件中读取密钥的 Clojure 代码,将 [org.bouncycastle/bcprov-jdk15on "1.56"] 作为依赖项:

(ns adhoc.ecdsa-mismatch
  (:import (java.nio.file Files Paths)
           (java.security Security KeyFactory)
           (java.security.spec X509EncodedKeySpec)))

(when (nil? (Security/getProvider "BC"))
  (Security/addProvider (org.bouncycastle.jce.provider.BouncyCastleProvider.)))

(defn read-bytes
  "Read file from path as byte array."
  [^String path]
  (Files/readAllBytes (Paths/get path (into-array String []))))

(defn read-pubkey
  [^String path]
  (.generatePublic (KeyFactory/getInstance "ECDSA" "BC")
                   (X509EncodedKeySpec. (read-bytes path))))
(require '[adhoc.ecdsa-mismatch :as mm])

(mm/read-pubkey "secp256k1.public.der")
;; InvalidKeySpecException java.lang.NullPointerException  org.bouncycastle.jce.provider.JDKKeyFactory$EC.engineGeneratePublic (:-1)

(.printStackTrace *e)
;; java.security.spec.InvalidKeySpecException: java.lang.NullPointerException
;;  at org.bouncycastle.jce.provider.JDKKeyFactory$EC.engineGeneratePublic(Unknown Source)
;;  at java.security.KeyFactory.generatePublic(KeyFactory.java:328)
;;  at adhoc.ecdsa_mismatch$read_pubkey.invokeStatic(ecdsa_mismatch.clj:16)
;;  at adhoc.ecdsa_mismatch$read_pubkey.invoke(ecdsa_mismatch.clj:14)

其他信息

使用 NIST P-256 曲线生成密钥:

$ openssl ecparam -name prime256v1 -genkey -noout -outform DER | openssl ec -inform DER -pubout -outform DER > prime256v1.public.der

从磁盘加载prime256v1

(println (mm/read-pubkey "prime256v1.public.der"))
;; #object[org.bouncycastle.jce.provider.JCEECPublicKey 0x72369051 EC Public Key
;;             X: 5a690a647dc4b7e80f71f15212b08686c1f717ada7198fa8a0b8c93cec16ecb
;;             Y: 30be46137f328766ef8686675f118760ab0c96c52275bf00cf56097333ea6487
;; ]

公钥的内容:

$ base64 secp256k1.public.der 
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEsipNgzBpQ0CLgZNw+LhASmh4KmGXnJsOC2dfzq5cVwMn
HfsfHGphaGvZTNoc/lUVrr+ICFK/2D/9up9hHHHHWg==
$ base64 prime256v1.public.der 
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBaaQpkfcS36A9x8VISsIaGwfcXracZj6iguMk87B
bsswvkYTfzKHZu+GhmdfEYdgqwyWxSJ1vwDPVglzM+pkhw==

【问题讨论】:

另见How to create ECDSA keypair (256bit) for bitcoin curve (secp256k1) using spongy castle?和C# ecdsa signature - Which key specification can i choose? 【参考方案1】:

解决方案是Java——或者这个版本的BouncyCastle——不支持secp256k1椭圆曲线!在 openssl 调用中使用更常见的曲线 prime256v1 (NIST P-256) 会生成一个我可以读取的密钥。

示例成功输出:

#<JCEECPublicKey EC Public Key
            X: 89cfd7dfb455e6ea51e578e9ea3505ba1e44a0a5f126e3b280611cf6bb467653
            Y: 565eb189ef15b8ff1b72824a7f0418a7df205797a86c8f30961e9bf0deaaa0c3
>

【讨论】:

那是同一条曲线下的另一个名字,BC应该支持任一标识符。你能在问答中公布公钥吗?也许还有 Bouncy 的版本?这将使我们能够进一步研究这个问题。顺便说一句,感谢您提供的信息。 当然,添加了确切的公钥和 BC 版本。稍微改变了 openssl 命令,效果相同。 另外...我确定这些是不同的曲线,但我是 EC 新手。我错了 NIST P-256 = prime256v1secp256k1 不同吗? (你有没有把它误读为secp256r1?我刚刚知道这是prime256v1的同义词......)

以上是关于无法从 Java 读取 OpenSSL 生成的 ECDSA 密钥:InvalidKeySpecException的主要内容,如果未能解决你的问题,请参考以下文章

openssl与java(读取加密码的密钥)

为啥我使用 OpenSSL 和 Java 生成的 RSA-SHA256 签名不同?

Window下生成OpenSSL自签证书

Java 密钥库可以导入 OpenSSL 生成的密钥对吗?

openssl pem证书 转换为p12类型证书

为啥 openssl 无法读取 OSX 上由 openssh 创建的 ssh 私钥