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

时间:2018-04-18 09:13:23

标签: java cryptography elliptic-curve

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

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

我能想到的一种方法是获取密钥的ECPoint并验证它是否在给定的曲线上。这样我可以测试所有支持的曲线,但是这在运行时感觉很麻烦,如果有两个或多个曲线重叠点,可能会出错。

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

是否有人知道使用jre(8/9)和bc仅基于曲线或publicKey细节查找曲线名称的更好方法。你对第一个解决方案的看法是什么,获得虚假命中的可能性有多大。

2 个答案:

答案 0 :(得分:2)

根据您的描述,您真正需要的是OID,而不是名称。如果是这样,那就更容易了,因为曲线OID出现在EC公钥的“X.509”编码中,这实际上是X.509的SubjectPublicKeyInfo结构(在PKIX中复制,请参阅{{3} }和rfc5280 #4.1但是跳过有关显式参数的部分,每个人都使用named rfc3279 #2.3.5,这是the encoding for JCA public keys,用于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?");

并且即使没有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?");

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

答案 1 :(得分:1)

我认为我已经找到了一个有效的解决方案,使用EC5Util类来实现jre类型规范。所有具有相同名称的双类实例都会使它有点混乱,但现在可以访问和使用这些函数。

public static final String deriveCurveName(org.bouncycastle.jce.spec.ECParameterSpec ecParameterSpec) throws GeneralSecurityException{
    for (@SuppressWarnings("rawtypes")
           Enumeration names = ECNamedCurveTable.getNames(); names.hasMoreElements();){
        final String name = (String)names.nextElement();

        final X9ECParameters params = ECNamedCurveTable.getByName(name);

        if (params.getN().equals(ecParameterSpec.getN())
            && params.getH().equals(ecParameterSpec.getH())
            && params.getCurve().equals(ecParameterSpec.getCurve())
            && params.getG().equals(ecParameterSpec.getG())){
            return name;
        }
    }

    throw new GeneralSecurityException("Could not find name for curve");
}

public static final String deriveCurveName(PublicKey publicKey) throws GeneralSecurityException{
    if(publicKey instanceof java.security.interfaces.ECPublicKey){
        final java.security.interfaces.ECPublicKey pk = (java.security.interfaces.ECPublicKey) publicKey;
        final ECParameterSpec params = pk.getParams();
        return deriveCurveName(EC5Util.convertSpec(params, false));
    } else if(publicKey instanceof org.bouncycastle.jce.interfaces.ECPublicKey){
        final org.bouncycastle.jce.interfaces.ECPublicKey pk = (org.bouncycastle.jce.interfaces.ECPublicKey) publicKey;
        return deriveCurveName(pk.getParameters());
    } else throw new IllegalArgumentException("Can only be used with instances of ECPublicKey (either jce or bc implementation)");
}

public static final String deriveCurveName(PrivateKey privateKey) throws GeneralSecurityException{
    if(privateKey instanceof java.security.interfaces.ECPrivateKey){
        final java.security.interfaces.ECPrivateKey pk = (java.security.interfaces.ECPrivateKey) privateKey;
        final ECParameterSpec params = pk.getParams();
        return deriveCurveName(EC5Util.convertSpec(params, false));
    } else if(privateKey instanceof org.bouncycastle.jce.interfaces.ECPrivateKey){
        final org.bouncycastle.jce.interfaces.ECPrivateKey pk = (org.bouncycastle.jce.interfaces.ECPrivateKey) privateKey;
        return deriveCurveName(pk.getParameters());
    } else throw new IllegalArgumentException("Can only be used with instances of ECPrivateKey (either jce or bc implementation)");
}