如何从 ECPublicKey 中找到匹配的曲线名称
How to find the matching curve name from an ECPublicKey
目前我正在更新我的 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 根据曲线或公钥详细信息查找曲线名称的更好方法。您对第一个解决方案的感觉如何,获得错误命中的可能性有多大。
我想我已经找到了使用 EC5Util class 的 jre 类型规范的有效解决方案。所有具有相同名称的双 class 实例使它有点混乱,但是这些功能现在可以访问和使用了。
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)");
}
从您的描述来看,您真正需要的是 OID,而不是名称。如果是这样,那就更容易了,因为曲线 OID 存在于 EC public 密钥的“X.509”编码中,它实际上是 X.509 中的 SubjectPublicKeyInfo
结构(在 PKIX 中复制,参见rfc5280 #4.1 and rfc3279 #2.3.5 but skip the parts about explicit parameters, everybody uses the namedCurve=OID option) which is 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 的地方,除了自签名的唯一你放置 this 密钥算法 OID 的地方。
实际上(至少对于 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();
}
目前我正在更新我的 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 根据曲线或公钥详细信息查找曲线名称的更好方法。您对第一个解决方案的感觉如何,获得错误命中的可能性有多大。
我想我已经找到了使用 EC5Util class 的 jre 类型规范的有效解决方案。所有具有相同名称的双 class 实例使它有点混乱,但是这些功能现在可以访问和使用了。
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)");
}
从您的描述来看,您真正需要的是 OID,而不是名称。如果是这样,那就更容易了,因为曲线 OID 存在于 EC public 密钥的“X.509”编码中,它实际上是 X.509 中的 SubjectPublicKeyInfo
结构(在 PKIX 中复制,参见rfc5280 #4.1 and rfc3279 #2.3.5 but skip the parts about explicit parameters, everybody uses the namedCurve=OID option) which is 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 的地方,除了自签名的唯一你放置 this 密钥算法 OID 的地方。
实际上(至少对于 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();
}