如何从 ECPublicKey 中找到匹配的曲线名称

Bas*_*sen 2 java cryptography elliptic-curve

目前我正在更新我的 x.509 证书库以支持 ECC。大多数实现的构建器都采用 publicKey 并从密钥中导出算法等。在 RSA 中,这很简单,您可以检查密钥的算法并验证位长。但是对于 ECC,密钥基于曲线,曲线名称(当然)需要在证书中指定(作为 OID)。

我现在正在处理的问题是找到一种方法,从 java.security.interfaces.ECPublicKey 或 org.bouncycastle.jce.interfaces.ECPublicKey 到曲线名称。(两种实现完全不同......)

我能想到的一种方法是获取密钥的 ECPoint 并验证它是否在给定的曲线上。通过这种方式,我可以测试所有支持的曲线,但是在运行时感觉很麻烦,并且如果点重叠 2 条或更多曲线,则可能容易出错。

另一种方法是获取 ECCurve(bc 实现)或 EllipticCurve(jre 实现)并将曲线细节与支持的实现进行比较。这还涉及逐步遍历每条已知曲线。

有没有人知道仅使用 jre(8/9) 和 bc 根据曲线或公钥详细信息查找曲线名称的更好方法。您对第一个解决方案的感觉如何,获得误报的可能性有多大。

dav*_*085 5

从您的描述看来,您真正需要的是 OID,而不是名称。如果是这样,那就更容易了,因为曲线 OID 存在于 EC 公钥的“X.509”编码中,这实际上是SubjectPublicKeyInfo来自 X.509的结构(在 PKIX 中复制,请参阅rfc5280 #4.1rfc3279 #2.3.5但跳过有关显式参数的部分,每个人都使用 namedCurve=OID 选项),它是JCA 公钥的编码,适用于 Sun/Oracle/OpenJDK 和 BC 实现(以及所有算法,而不仅仅是 ECC)。BC 还为解析此结构提供了良好的支持:

import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;

    KeyPairGenerator gen = KeyPairGenerator.getInstance("EC");
    gen.initialize(new ECGenParameterSpec("secp256r1"));
    ECPublicKey jcekey = (ECPublicKey) gen.generateKeyPair().getPublic();
    //KeyFactory fact = KeyFactory.getInstance("EC", "BC");
    //org.bouncycastle.jce.interfaces.ECPublicKey bckey = (org.bouncycastle.jce.interfaces.ECPublicKey)fact.generatePublic(new X509EncodedKeySpec(jcekey.getEncoded()));

    // with Bouncy
    byte[] enc = jcekey.getEncoded(); //enc = bckey.getEncoded();
    SubjectPublicKeyInfo spki = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(enc));
    AlgorithmIdentifier algid = spki.getAlgorithm();
    if( algid.getAlgorithm().equals(X9ObjectIdentifiers.id_ecPublicKey)){
        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) algid.getParameters();
        System.out.println (oid.toString()); // curve OID, use as needed
    }else System.out.println ("not EC?");
Run Code Online (Sandbox Code Playgroud)

为了完整性,即使没有 Bouncy,如果您不使用最大曲线并且愿意作弊(Java 越来越不鼓励这样做),这并不难:

import sun.security.util.DerInputStream;
import sun.security.util.ObjectIdentifier;

    final ObjectIdentifier x9_id_ec = new ObjectIdentifier("1.2.840.10045.2.1");
    int off = (4+2)+enc[(4+1)];
    if( enc[0]==0x30 && enc[1]>0 && enc[2]==0x30 && enc[4]==6 
        && new ObjectIdentifier(new DerInputStream(enc,4,off-4)).equals((Object)x9_id_ec)
        && enc[off] == 6 ){
        byte[] oidenc = Arrays.copyOfRange(enc,off,off+2+enc[off+1]);
        // that's the DER-encoded OID of the curve
        ObjectIdentifier oid = new ObjectIdentifier(new DerInputStream(oidenc));
        System.out.println (oid.toString()); // and the display form
    }else System.out.println ("not EC or too big?");
Run Code Online (Sandbox Code Playgroud)

我还要注意,如果您正在构建证书,PublicKey.getEncoded()则已经是整个 subjectPublicKeyInfo 字段,这是您唯一需要放置曲线 OID 的地方,除了自签名之外,您唯一需要放置密钥算法 OID 的地方。