【问题标题】:How to find the matching curve name from an ECPublicKey如何从 ECPublicKey 中找到匹配的曲线名称
【发布时间】:2018-09-28 11:02:24
【问题描述】:

目前我正在更新我的 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 根据曲线或公钥详细信息查找曲线名称的更好方法。以及您对第一个解决方案的感受是什么,得到错误点击的可能性有多大。

【问题讨论】:

  • 您是否有特定的标准命名约定,例如“secpxyz”?毕竟,名称只是一小组参数的方便标签。
  • 最后我需要转换为代表 x.509 证书曲线的特定 OID。我有一个基于他们的名字的表格(我可以支持的那些),其格式确实是“secpxyz”和“brainpoolxyz”等等。这些名称似乎非常标准化,并且也出现在 ECNamedCurveTable 和 jre an bc 中的其他类中。
  • 第一种方法应该足够了,一条曲线上的随机点几乎不可能变成另一条曲线上的随机点,至少对于密码学中使用的曲线是这样。在从 BigIntegers 到曲线参数的映射中查找曲线顺序可能会更有效。
  • 充气城堡有一些包含编码曲线的类。您可以从公钥中检索参数并通过循环进行比较。也许您可以发布编码的公钥?如果您只需要这样做一次:打印出曲线参数,然后使用 google 搜索其中一个参数,例如阶数或(甚至更好)素数。
  • @MaartenBodewes 谢谢这确实是我最终最终做的。来自 jce 和 bc 的所有双精度类之间的转换引起了一些麻烦,因为两种实现都使用不同的参数名称和类型。 BC 的 EC5Util 类提供了一个解决方案。我已将解决方案放在下面的答案中。现在我必须运行一个完整的 UnitTest 来查看所有曲线和名称是否正确。

标签: java cryptography elliptic-curve


【解决方案1】:

实际上(至少对于SunEC 提供者生成的密钥)OID 可以比其他答案建议的更容易提取:

import java.security.Key;
import java.security.AlgorithmParameters;
import java.security.interfaces.ECKey;
import java.security.spec.ECGenParameterSpec;

public static String curveOid(Key key) throws Exception {
    AlgorithmParameters params = AlgorithmParameters.getInstance("EC");
    params.init(((ECKey) key).getParams());
    return params.getParameterSpec(ECGenParameterSpec.class).getName();
}

【讨论】:

    【解决方案2】:

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

    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。

    【讨论】:

    • 嗨戴夫,因为我已经编写了一个 DER/ASN.1/X.509 库,这个问题是其中的一部分。我做的第一件事是检查密钥的“编码”字节的内容。然而,我测试的每个人都使用显式方案,并且不包含曲线名称 OID。我用 secp256k1 和 curve25519 进行了测试。此外,如果您将 java x.509 编码的公钥输入到证书中,它不会在 Windows 中验证。除此之外,我还使用该名称来对密钥进行序列化和反序列化,但因为我没有告诉你不知道 ;-)
    • 我也运行了您的代码示例,并且当使用您生成 ECPublicKey 的方式(使用 JCE 格式)时,OID 包含在编码格式中。由于您使用的是 JCE 密钥中的 X509EncodedKeySpec,因此对于 BC 实现,它仍然包含 OID。但是,使用 BC 或其曲线详细信息生成的键不包含 Curve OID,因此上面的代码不适用于这些键。
    • 由 BC provider 使用 (JCE) ECGenParameterSpec 或 (BC) ECNamedCurveGenParameterSpec(或 int 重载)生成的密钥使用 OID 进行编码。是的,如果您手动/明确提供除名称之外的所有参数(为什么?),那么您没有 OID。
    • 这一切都取决于您从哪里获取密钥。如果外部方向您提供了他们的公钥,您将无法控制生成的内容。因此,假设 OID 在公钥中的解决方案不会有帮助。我没有包含 OID 的关键样本。
    【解决方案3】:

    我想我已经找到了一个使用 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)");
    }
    

    【讨论】:

    • 经过一些检查后,我最终添加了 CustomNamedCurves 类并循环遍历这些类(这可以通过与循环遍历 ECNamedCurveTable 完全相同的方式完成,两者都是 BC 标准)。
    猜你喜欢
    • 1970-01-01
    • 2011-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-30
    • 2012-02-24
    • 1970-01-01
    相关资源
    最近更新 更多