我使用bouncy castle库在java中加载公钥,但总是出现错误Invalid point encoding 0x45.

公钥是使用C#CNG API在客户端生成的.

Java方法1:

public PublicKey loadPublicKey(String encodedPublicKey)
            throws NoSuchProvider例外, NoSuch算法rithm例外, InvalidKeySpec例外 {
        
        byte[] keybytes = java.util.Base64.getDecoder().decode(encodedPublicKey);
        
        Security.addProvider(new BouncyCastleProvider());
        
        ECNamedCurveParameterSpec params = ECNamedCurveTable.getParameterSpec("P-256");
        
        ECPublicKeySpec keySpec = new ECPublicKeySpec(params.getCurve().decodePoint(keybytes), params);
        
        return new BCECPublicKey("ECDH", keySpec, BouncyCastleProvider.CONFIGURATION);

    }

方法2

public PublicKey loadPublicKey(String pKey) throws 例外 {
        byte[] keybytes = java.util.Base64.getDecoder().decode(pKey);
        Security.addProvider(new BouncyCastleProvider());
        ECParameterSpec params = ECNamedCurveTable.getParameterSpec("P-256");
        ECPublicKeySpec pubKey = new ECPublicKeySpec(params.getCurve().decodePoint(keybytes), params);
        KeyFactory kf = KeyFactory.getInstance("ECDH", "BC");
        return kf.generatePublic(pubKey);
    }

例外

java.lang.IllegalArgument例外: Invalid point encoding 0x45
    at org.bouncycastle.math.ec.ECCurve.decodePoint(ECCurve.java:443)

下面是创建公钥的方法

public static (byte[] publicKey, byte[] privateKey) CreateKeyPair()
        {
            using (ECDiffieHellmanCng cng = new ECDiffieHellmanCng(
                // need to do this to be able to export private key
                CngKey.Create(
                    Cng算法rithm.ECDiffieHellmanP256,
                    null,
                    new CngKeyCreationParameters
                    { ExportPolicy = CngExportPolicies.AllowPlaintextExport })))
            {
                cng.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
                cng.Hash算法rithm = Cng算法rithm.Sha256;
                // export both private and public keys and return
                var pr = cng.Key.Export(CngKeyBlobFormat.EccPrivateBlob);
                var pub = cng.PublicKey.ToByteArray();
                return (pub, pr);
            }
        }

生成的公钥RUNLMSAAAAHddHI6TOEDG/Ka7naBbLQH0u/DSFfbKJI2w0WSoxrmFkwKm1tktz4wD0rqnwkZp8FwdHJ+8OVrTcpDMmxrwvS6

我在java收到的密钥是72 bytes.但我认为bouncy castle java支持64 bytes键.

我也在调查this人,但没有得到任何帮助

推荐答案

C代码将公钥导出为Base64编码EccPublicBlob,其格式在问题中给出的link中描述:

Java/BC不直接支持导入EccPublicBlob(MS专有),但它支持导入未压缩的公钥.当x和y坐标串联并使用0x04作为前缀时,会产生此结果.然后可以按如下方式使用Java/BC进行导入:

import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.ECPointUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
...
public static PublicKey getPubKeyFromCurve(byte[] uncompRawPubKey, String curveName) throws Exception {
      ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec(curveName);
      ECNamedCurveSpec params = new ECNamedCurveSpec(spec.getName(), spec.getCurve(), spec.getG(), spec.getN());
      ECPoint point = ECPointUtil.decodePoint(params.getCurve(), uncompRawPubKey);
      ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
      KeyFactory kf = KeyFactory.getInstance("ECDH", new BouncyCastleProvider());
      ECPublicKey pubKey = (ECPublicKey) kf.generatePublic(pubKeySpec);
      return pubKey;
}

测试(假设EccPublicBlob是Base64编码的,与发布的一样):

import java.util.Base64;
...
String publicKeyBlob = "RUNLMSAAAAAFzw4IGY4N8PKVt0MGF38SAKU5ixJhptVUdrWzuPhFDOcj/2k4SlGRN1RpRMbar9Iu7Uvcx7Vtm8Wa0HSzWJdE";
byte[] rawPublic = new byte[65];
rawPublic[0] = 0x04;
System.arraycopy(Base64.getDecoder().decode(publicKeyBlob), 8, rawPublic, 1, 64);
PublicKey pub = getPubKeyFromCurve(rawPublic, "P-256");
System.out.println(Base64.getEncoder().encodeToString(pub.getEncoded())); // MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBc8OCBmODfDylbdDBhd/EgClOYsSYabVVHa1s7j4RQznI/9pOEpRkTdUaUTG2q/SLu1L3Me1bZvFmtB0s1iXRA==

测试导入EccPublicBlob并将其导出为X.509/SPKI格式的Base64编码DER密钥.这可以用ASN读取.1个解析器,例如https://lapo.it/asn1js/个,因此需要验证.


注意,C还支持导出其他格式.然而,这取决于版本.E、 g.截至.NET Core 3.0有一种方法ExportSubjectPublicKeyInfo(),它以X.509/SPKI格式导出公钥,DER编码.这种格式和编码可以使用X509EncodedKeySpec直接导入Java(即使没有BouncyCastle)


请记住,P-256的ECDH密钥也可以更简单地通过以下方式创建:

ECDiffieHellmanCng cng = new ECDiffieHellmanCng(ECCurve.NamedCurves.nistP256)

或跨平台

ECDiffieHellman ecdh = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256)

Java相关问答推荐

基于仅存在于父级中的字段查询子文档?

在Java 8之后,HashMap的最坏情况下时间复杂度仍然是O(n)而不是O(log n)?

int Array Stream System. out. print方法在打印Java8时在末尾添加% sign

如何粘合(合并)文件Lucene?

Character::Emoji不支持带数字的字符吗?

如何配置ActiveMQ Artemis以使用AMQP 1.0和其他协议与Java

错误:在Liferay7.4中找不到符号导入com.liferay.portal.kernel.uuid.PortalUUID;";

';com.itextpdf.ext.html.WebColors已弃用

如何创建一个2d自上而下的移动系统,其中移动,同时持有两个关键是可能的处理?

将java.util.Date转换为OffsetDateTime

使用Room Database删除Jetpack合成中的所有项目后,UI未重新合成

在执行流和相关操作时,使用Java泛型为2个方法执行相同的操作,但对象不同

当b是一个字节并且在Java中值为-1时,为什么b>;>;>;1总是等于-1?

如何使用MapStrCut转换双向链接

有没有办法在o(log(N))中以系统的方式将数组中的小块元素复制和移动到新增长的数组中的左侧?

如何在ApacheHttpClient 5中为单个请求设置代理?

在Java中将对象&转换为&q;HashMap(&Q)

我们可以在方法中声明接口吗?

无限递归Java问题

转换为JSON字符串时,日期按天递减-Java