Java Crypto将原始字节转换为PublicKey,反之亦然

问题描述

我正试图从java.security.PublicKey中创建一个byte[],而这仅仅是一个原始密钥,它不在X.509或ASN.1中。在其他语言中,人们使用的是libsodium,它只是给他们一个原始字节[]。我还需要能够将我的java.security.PublicKey转换为byte[]才能使用。似乎java.security竭尽全力使这一挑战变得艰巨,而不是希望我依靠预先编码的X509 SubjectPublicKeyInfo。我在这里错过了一个简单的答案吗?

解决方法

与所有NaCl派生词一样,

Libsodium使用Curve25519进行加密操作,即X25519用于DH密钥交换和Ed25519用于签名。 从version 11开始,Java支持X25519,从version 15开始,支持Ed25519。这两个功能均由SunEC提供商提供。

对于原始密钥的导入/导出,最简单的方法是使用某些BouncyCastle函数。以下代码为X25519(Java 11及更高版本)创建X.509格式的公共测试密钥,并从中导出原始的32个字节的密钥。然后将原始密钥再次导入到X.509密钥中:

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.params.X25519PublicKeyParameters;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.util.encoders.Hex;
...
// Generate public test key in X.509 format
PublicKey publicKey = loadPublicKey("X25519");
byte[] publicKeyBytes = publicKey.getEncoded();
System.out.println(Base64.getEncoder().encodeToString(publicKeyBytes)); // X.509-key,check this in an ASN.1 Parser,e.g. https://lapo.it/asn1js/

// PublicKey to raw key
X25519PublicKeyParameters x25519PublicKeyParameters = (X25519PublicKeyParameters)PublicKeyFactory.createKey(publicKeyBytes);
byte[] rawKey = x25519PublicKeyParameters.getEncoded();
System.out.println(Hex.toHexString(rawKey)); // equals the raw 32 bytes key from the X.509 key

// Raw key to PublicKey
KeyFactory keyFactory = KeyFactory.getInstance("X25519");
SubjectPublicKeyInfo subjectPublicKeyInfo = new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X25519),rawKey);
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(subjectPublicKeyInfo.getEncoded());
publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
publicKeyBytes = publicKey.getEncoded();
System.out.println(Base64.getEncoder().encodeToString(publicKeyBytes)); // equals the X.509 key from above

创建X.509测试密钥的位置:

private static PublicKey loadPublicKey(String algorithm) throws Exception {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
    KeyPair keyPair = keyPairGenerator.generateKeyPair();
    return keyPair.getPublic();
}

和适用于Ed25519(Java 15及更高版本)的模拟。

X25519和Ed25519也可以通过BouncyCastle中的类单独实现。 org.bouncycastle.math.ec.rfc7748.X25519(密钥约定),org.bouncycastle.crypto.generators.X25519KeyPairGenerator(密钥生成),org.bouncycastle.crypto.params.X25519PublicKeyParameters(密钥容器)和Ed25519的模拟类。