如何使用 kotlin 从 RSA 私钥获取 ECDSA、DER 和 RPB 格式

How to get the ECDSA, DER and RPB format from a RSA private key using kotlin

提问人:user2517182 提问时间:11/8/2023 最后编辑:user2517182 更新时间:11/8/2023 访问量:60

问:

我有一个 RSA 私钥。我想以下列格式提取公钥:

  1. ECDSA的
  2. DER
  3. RPB公司

我可以使用以下代码从 RSA 格式的私钥获取 RSA 格式的公共密钥。

import java.security.KeyFactory
import java.security.PrivateKey
import java.security.PublicKey
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.RSAPrivateCrtKeySpec
import java.security.interfaces.RSAPrivateCrtKey
import java.security.spec.RSAPublicKeySpec
import java.util.Base64
import java.security.KeyPair
import java.security.KeyPairGenerator

const val ALGORITHM = "RSA"


fun generateKeyPair(): KeyPair {
    val keyPairGenerator = KeyPairGenerator.getInstance("RSA")
    keyPairGenerator.initialize(2048) // You can adjust the key size as needed
    return keyPairGenerator.generateKeyPair()
}

fun generatePublicKey(privateKeyString: String): PublicKey {
    try {
        val kf = KeyFactory.getInstance(ALGORITHM)
        val encodedPrivateKey = Base64.getDecoder().decode(privateKeyString)
        val privateKeySpec = PKCS8EncodedKeySpec(encodedPrivateKey)
        val rsaPrivateKey = kf.generatePrivate(privateKeySpec) as RSAPrivateCrtKey

        val publicKeySpec = RSAPublicKeySpec(rsaPrivateKey.modulus, rsaPrivateKey.publicExponent)
        return kf.generatePublic(publicKeySpec)
    } catch (e: Exception) {
        throw RuntimeException(e)
    }
}

fun main() {
    val keyPair = generateKeyPair()
    // val publicKey: PublicKey = keyPair.public
    val privateKey: PrivateKey = keyPair.private
    // Test with a sample private key string
    val privateKeyString = Base64.getEncoder().encodeToString(privateKey.encoded)
    println("Generated Private Key: $privateKeyString")
    //val privateKeyString = "YOUR_PRIVATE_KEY_STRING_HERE"
    val publicKey = generatePublicKey(privateKeyString)
    println("Generated Public Key: ${Base64.getEncoder().encodeToString(publicKey.encoded)}")
}

既然有,我以为会有一个 、 和 。但是,我错了。不过,还有其他格式类型。java.security.specRSAPublicKeySpecECDSAPublicKeySpecDERPublicKeySpecRPBPublicKeySpecPublicKeySpec

我看了一下充气城堡,但无法从私钥(或从 RSA 格式转换为三种格式)获取三种格式。

我知道 OpenSSL 在带有一些标志的三个命令中完成了我所要求的一切。我不能使用 OpenSSL,我需要在没有 OpenSSL 包装器的情况下用 kotlin 进行。

我相信有 Java Cryptography Extension (JCE) 和 Bouncy Castle(可能会再次检查)扩展了 Java 中加密的一些功能。但是,在我访问/重新访问之前,我将来到我值得信赖的 Stackoverflow 社区。

更新:

以下是我尝试通过 Kotlin(和 Java 库)重新创建的 OpenSSL 中的命令。特别强调代码中具有 --> NEED THIS <-- 的注释

// Private key --> already have this
openssl genrsa -out priv.pem 2048

// Public key --> already have this
openssl rsa -in priv.pem -pubout -out pub.pem

// Public key PEM --> already have this
openssl pkey -inform PEM -pubin -in pub.pem -text -noout

// Public key DER format --> NEED THIS <--
openssl rsa -pubin -inform PEM -in pub.pem -outform DER -out pub.der

// Public key RPB format --> no worries about this
openssl rsa -pubin -inform PEM -in pub.pem -outform RPB -out pub.rpb

// ECDSA
// Private key --> already have this
openssl ecparam -genkey -out ecdsa_priv.pem -name prime256v1

// Public key --> NEED THIS <--
openssl ec -in ecdsa_priv.pem -pubout -out ecdsa_pub.pem

// Public key DER format --> NEED THIS <--
openssl ec -pubin -inform PEM -in ecdsa_pub.pem -outform DER -out ecdsa_pub.der
java kotlin openssl 加密 bouncycastle

评论

2赞 Maarten Bodewes 11/8/2023
ECDSA 是一种使用椭圆曲线执行 DSA 的方法;显然,它不是一种格式,它与 RSA 不兼容。DER 是可分辨编码规则;它是一种对 ASN.1 定义的数据结构进行编码的方法,但它并不特定于任何格式。我试图查找RPB,但一无所获。
0赞 Maarten Bodewes 11/9/2023
我没有投反对票,但我可以想象人们因为对格式的误解而投了反对票。虽然很烦人,但请记住,投票不是个人的。当我还是新用户时,我问过同样的问题,但请理解,下行者通常不会遵循这个问题。
0赞 user2517182 11/13/2023
是的,根本不是个人的:-),只是好奇,所以会尽量不要犯同样的错误。谢谢

答:

3赞 dave_thompson_085 11/8/2023 #1

公钥 PEM -->已经有这个
openssl pkey -inform PEM -pubin -in pub.pem -text -noout

没有。Java - 或者只是在 Kotlin 中 - 是 X.509-SPKI 格式的公钥,它与使用的格式相同。当你编码为 base64 并每隔 64 添加换行符并在之前和之后添加一行时,则它与 OpenSSL PEM 相同,这也是标准的;见第 2 节和第 13 节RFC7468。根据您将如何使用此文件或数据,您可能会在没有 base64 中的换行符的情况下逃脱;并非所有程序都需要这样做。但大多数使用 PEM 的程序都会强制执行 dash-BEGIN 和 dash-END 线。PublicKey.getEncoded().encodedopenssl-----BEGIN PUBLIC KEY----------END PUBLIC KEY-----

公钥 DER 格式 --> 需要这个<--
openssl rsa -pubin -inform PEM -in pub.pem -outform DER -out pub.der

这是微不足道的。只需跳过 base64 编码;返回的正是这种格式(作为)。PublicKey.getEncoded()byte[]

公钥 RPB 格式 --> 不用担心这个
openssl rsa -pubin -inform PEM -in pub.pem -outform RPB -out pub.rpb

您正在使用的这个openssl是什么?你从哪里/怎么得到的?没有“真正的”OpenSSL(即来自 www.openssl.org 的 OpenSSL 项目)具有这样的选项。我也不认为 LibreSSL 会这样做,尽管我没有那么密切地跟踪它。-outform

ECDSA
// 私钥 --> 已经有这个 openssl ecparam -genkey -out ecdsa_priv.pem -name prime256v1 // 公钥 --> 需要这个<-- openssl ec -in ecdsa_priv.pem -pubout -out ecdsa_pub.pem /
/ 公钥 DER 格式 --> 需要这个
<--

openssl ec -pubin -inform PEM -in ecdsa_pub.pem
-outform DER -out ecdsa_pub.der

这些是从 EC 密钥(或真正的密钥对)生成的,而不是 RSA 密钥。请注意,尽管您使用了文件名 ecdsa,但实际上用于 ECDSA 和 ECDH 以及可能的 EQMQV 使用了相同的密钥;因此,在 Java 中,您使用 but 和 .KeyPairGenerator|KeyFactory.getInstance("EC")Signature.getInstance("ECDSA")KeyAgreement.getInstance("ECDH")

如果您有其中一个关键对象,即一个带有 interface not 的对象,以及您的帖子建议但实际上没有说明的 BouncyCastle,您可以调整我最近在 Extract public key 中写的方法 PrivateKeyInfo with Bouncy CastleECPrivateKeyRSAPrivateCrtKey

    // import org.bouncycastle.asn1.pkcs.PrivateKeyInfo and org.bouncycastle.asn1.ASN1BitString
    PrivateKeyInfo privinfo = PrivateKeyInfo.getInstance( ecprivkeyobject.getEncoded() );
    ASN1BitString wrappt = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(privinfo.getPrivateKey().getOctets()).getPublicKey();
    // use full name to avoid conflict with 'standard' java.security.interfaces.ECPrivateKey
    byte[] spki = new SubjectPublicKeyInfo(privinfo.getPrivateKeyAlgorithm(), wrappt).getEncoded() ));

这是 EC 的 openssl DER 格式,类似于 RSA,如果您进行 base64 编码、添加换行符并添加 BEGIN/END 行,即 openssl(和标准 = RFC7468)PEM 格式。byte[] spki

补充:此外,您当前的 RSA 方法是不必要的迂回交叉路口。你不需要为RSA创建一个,采用它的编码,通过一个 with 运行该编码来产生你投射到的第二个;相反,您可以直接转换原始内容并使用它来获取 和 放入 .这个案例也在我链接的上一个Q中。PrivateKeyKeyFactoryPKCS8EncodedKeySpecPrivateKeyRSAPrivateCrtKeyPrivateKeymoduluspublicExponentRSAPublicKeySpec