Как найти соответствующее имя кривой из ECPublicKey

В настоящее время я обновляю свою библиотеку сертификатов x.509 для поддержки ECC. Большинство реализованных компоновщиков берут общедоступный ключ и получают алгоритм и тому подобное из ключа. В RSA это просто, вы проверяете алгоритм ключа и можете проверить длину в битах. Однако с ECC ключ основан на кривой, и имя кривой (конечно) должно быть указано в сертификате (как OID).

Проблема, над которой я сейчас работаю, заключается в том, чтобы найти способ перейти от java.security.interfaces.ECPublicKey или org.bouncycastle.jce.interfaces.ECPublicKey к имени кривой. (Обе реализации полностью отличаются друг от друга...)

Один из способов, который я могу придумать, - это получить ECPoint ключа и подтвердить, что он находится на заданной кривой. Таким образом, я могу протестировать все поддерживаемые кривые, однако это кажется громоздким во время выполнения и, возможно, подвержено ошибкам, если есть точки, перекрывающие 2 или более кривых.

Другой способ — получить ECCurve (реализация bc) или EllipticCurve (имплементация jre) и сравнить детали кривой с поддерживаемыми реализациями. Это также включает в себя пошаговое выполнение каждой известной кривой.

Кто-нибудь знает лучший способ найти имя кривой на основе данных кривой или публичного ключа, используя только jre (8/9) и bc. И как вы относитесь к первому решению, какова вероятность ложных срабатываний.


person Bas Goossen    schedule 18.04.2018    source источник
comment
Есть ли у вас конкретное стандартное соглашение об именах, которое вы ищете, например, «secpxyz»? Ведь имя — это просто удобная метка для небольшого набора параметров.   -  person President James K. Polk    schedule 18.04.2018
comment
В конце мне нужно преобразовать в определенный OID, представляющий кривую для сертификата x.509. У меня есть таблица тех (тех, которые я могу поддерживать) на основе их имен, которые действительно имеют формат «secpxyz» и «brainpoolxyz» и так далее. Эти имена кажутся довольно стандартизированными и также присутствуют, скажем, в ECNamedCurveTable и других классах в jre и bc.   -  person Bas Goossen    schedule 19.04.2018
comment
Первый метод должен быть адекватным, и было бы почти невозможно, чтобы случайная точка на одной кривой также оказалась случайной точкой на другой кривой, по крайней мере, для кривых, используемых в криптографии. Вероятно, было бы более эффективно искать порядок кривой на карте от BigIntegers до параметров кривой.   -  person President James K. Polk    schedule 19.04.2018
comment
В Bouncy Castle есть несколько классов, содержащих закодированные кривые. Вы можете просто получить параметры из открытых ключей и сравнить их, зациклившись на них. Может быть, вы можете опубликовать зашифрованный открытый ключ? Если вам просто нужно сделать это один раз: распечатайте параметры кривой и используйте Google для поиска одного из параметров, таких как порядок или (что еще лучше) простое число.   -  person Maarten Bodewes    schedule 19.04.2018
comment
@MaartenBodewes спасибо, это действительно то, чем я в конце концов занялся. Преобразование между всеми двойными классами из jce и bc вызвало некоторую головную боль, поскольку обе реализации используют разные имена и типы параметров. Класс EC5Util от BC предоставляет решение. Я поместил решение в ответ ниже. Теперь мне нужно запустить полный UnitTest, чтобы увидеть, все ли кривые и имена подходят.   -  person Bas Goossen    schedule 20.04.2018
comment
Есть ли решения, не использующие Bouncy Castle? Было бы здорово иметь пример без дополнительных зависимостей.   -  person Christopher Schultz    schedule 10.06.2019


Ответы (2)


Из вашего описания видно, что вам действительно нужен OID, а не имя. Если да, то это проще, поскольку OID кривой присутствует в кодировке "X.509" открытого ключа EC, которая на самом деле является структурой SubjectPublicKeyInfo из X.509 (воспроизведенной в PKIX, см. rfc5280 #4.1 и rfc3279 #2.3.5, но пропустите части о явных параметрах, все используют параметр namedCurve=OID), который равен кодировка открытых ключей JCA для реализаций Sun/Oracle/OpenJDK и BC (и всех алгоритмов, не просто ЕСК). Кроме того, 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 алгоритма ключа.

person dave_thompson_085    schedule 21.04.2018
comment
Привет, Дэйв, так как я написал библиотеку DER/ASN.1/X.509, частью которой является этот вопрос. Первое, что я сделал, это проверил содержимое закодированных байтов ключа. Однако все, что я тестировал, использовали явную схему и не содержали OID имени кривой. Я тестировал secp256k1 и curve25519. Кроме того, если вы вводите в сертификат открытый ключ, закодированный java x.509, он не проверяется в Windows. Кроме того, я также использую это имя для сериализации и десериализации ключей, но, поскольку я не сказал, вы не могли знать ;-) - person Bas Goossen; 21.04.2018
comment
Я также запускал ваш образец кода, и при использовании способа генерации ECPublicKey (с использованием формата JCE) OID содержится в закодированном формате. Поскольку вы используете X509EncodedKeySpec из ключа JCE, для реализации BC он по-прежнему содержит OID. Однако ключи, сгенерированные с помощью BC или их деталей кривой, не будут содержать OID кривой, поэтому приведенный выше код не будет работать с этими ключами. - person Bas Goossen; 23.04.2018
comment
Ключи, сгенерированные поставщиком BC с использованием (JCE) ECGenParameterSpec или (BC) ECNamedCurveGenParameterSpec (или перегрузки int), действительно кодируются с OID. Да, если вы вручную/явно указываете все параметры, КРОМЕ имени (почему?), то у вас нет OID. - person dave_thompson_085; 26.04.2018
comment
все зависит от того, откуда вы получаете ключи. Если внешняя сторона предоставляет вам свой открытый ключ, вы не контролируете то, что было сгенерировано. Таким образом, решение, предполагающее, что OID находится в публичном ключе, не поможет. Ни один из образцов ключей, в который я включил OID. - person Bas Goossen; 01.05.2018

Я думаю, что нашел правильное решение, используя класс 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)");
}
person Bas Goossen    schedule 20.04.2018
comment
После некоторых проверок я добавил класс CustomNamedCurves и перебрал их (это можно сделать точно так же, как перебрать ECNamedCurveTable, оба стандарта BC). - person Bas Goossen; 20.04.2018