Java ECC编码的密钥太大 [英] Java ECC encoded Key too large

查看:70
本文介绍了Java ECC编码的密钥太大的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是EC加密的新手,对此有些挣扎。
我正在使用Java 8和BouncyCatle提供程序。
我现在的问题是:当我生成带有以下代码的EC-KeyPair时,

  ECGenParameterSpec spec =新的ECGenParameterSpec( secp521r1); 
KeyPairGenerator kpg = KeyPairGenerator.getInstance( ECDH,BouncyCastleProvider.PROVIDER_NAME);
kpg.initialize(spec,new SecureRandom());
return kpg.generateKeyPair();

并尝试获取公共密钥的字节数组以将其发送给另一个人,即编码后的密钥长度为158个字节,格式为X.509。但是我期望X9.62格式和65到66字节之间的密钥大小。
为什么公钥这么大,如何用期望的密钥大小对其进行编码? (我期望密钥的大小,因为我期望密钥的长度为521位)

解决方案

ECC公钥在语义上是曲线;如果隐含了您命名的曲线,则X9.62格式的点如果经过压缩,则为67个八位位组(Java字节),如果未经压缩,则为133个八位位组,绝没有其他长度。



如果您的意思是 java.security.PublicKey.getEncoded(),则始终使用Java称为 X.509编码,实际上是ASN.1结构<$ c X.509中定义的$ c> SubjectPublicKeyInfo (SPKI), rfc5280秒4.1 ,编码为DER。该曲线上的这种格式的ECC公钥准确地是90或158个八位位组,用于未压缩或已压缩,并且Java提供程序(至少当前)生成未压缩的形式(尽管它们可以 parse 压缩) 。



听起来您可能想要X9.62压缩格式,正如我所说的那样,它是67字节(不是65或66)。如果是这样,您将无法在标准Java API中控制点压缩,但是考虑到BC提供程序创建了关键对象,BouncyCastle实现类确实支持它。
首先将 keypair.getPublicKey()强制转换为(corr) org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey (在1.47之前为 org.bouncycastle.jce.provider.JCEECPublicKey ),然后 getQ()返回一个 org.bouncycastle.math.ec.ECPoint 具有(重载)的 getEncoded(布尔压缩) 可以产生明显的效果






对于您的其他但不是正式的问题,请重新创建一个 PublicKey 来自已编码对象点(压缩与否),您有两个或三个选项,具体取决于您的计算方式:




  • 构造ASN.1 / DER编码的此曲线和点的SubjectPublicKeyInfo结构(Java称为 X.509格式),将其放在 X509EncodedKeySpec 中,并通过相应的 KeyFactory 。可以使用标准的SunEC提供程序(假定为j7 +,而不是RedHat限制版本)或BC提供程序。通常很难手动构造像SPKI这样的ASN.1编码,但在这种情况下还不错。或者如果您拥有BC,则可以使用其ASN.1功能


  • 直接调用BC例程来执行EC KeyFactory 为上述输入做




创建点然后使用这三种方式的示例代码:

  //除了标准java.security和javax.xml 
之外,还需要导入org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;

KeyPairGenerator kpg = KeyPairGenerator.getInstance( EC, BC);
kpg.initialize(new ECGenParameterSpec( secp521r1));
org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey ku =
(org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey)kpg.generateKeyPair()。getPublic();
byte []编码点= ku.getQ()。getEncoded(true / * compressed * /);

{//手动构建SPKI,此曲线仅
byte [] hdr = DatatypeConverter.parseHexBinary( 3058301006072a8648ce3d020106052b81040023034400);
//也可以写出byte [] hdr = {0x30,0x58,0x30,0x10 ...但是设置为0x80的项目需要转换
if(0x44 / * hdr [0x15] * / -1 != encodepoint.length)抛出新异常( secp521r1的压缩点不好!);
byte [] spki = Arrays.copyOf(hdr,90); System.arraycopy(encodedpoint,0,spki,0x17,0x43);
PublicKey k2 = KeyFactory.getInstance( EC /*,provider?*/).generatePublic(new X509EncodedKeySpec(spki));
Signature.getInstance( ECDSA)。initVerify(k2); // //健全性检查
}
{//用BC构建SPKI
AlgorithmIdentifier algid = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey,SECObjectIdentifiers.secp521r1);
ASN1EncodableVector vec =新的ASN1EncodableVector();
vec.add(algid); vec.add(新的DERBitString(编码点));
byte [] spki = new DERSequence(vec).getEncoded();
PublicKey k2 = KeyFactory.getInstance( EC /*,provider*/).generatePublic(new X509EncodedKeySpec(spki));
Signature.getInstance( ECDSA)。initVerify(k2); //健全性检查
}
{//直接致电BC
ProviderConfiguration配置= BouncyCastleProvider.CONFIGURATION;
X962Parameters参数= X962Parameters.getInstance(org.bouncycastle.asn1.sec.SECObjectIdentifiers.secp521r1);
ECCurve曲线= EC5Util.getCurve(配置,参数);
/ * ECParameterSpec ecSpec = EC5Util.convertToSpec(params,curve); * /
ECPoint point = curve.decodePoint(encodedpoint).normalize();
ECPublicKeyParameters kparams =新的ECPublicKeyParameters(point,ECUtil.getDomainParameters(configuration,params));
PublicKey k2 =新的BCECPublicKey( EC / *或 ECDH等* /,kparams,配置);
Signature.getInstance( ECDSA)。initVerify(k2); //健全性检查
}

相关在Java中加载原始的64字节长的ECDSA公共密钥,该PSD未经压缩。 p>

I am new to the EC-encryption and have some struggle with it. I am using Java 8 and the BouncyCatle provider. The Question I have now is: when I generate an EC-KeyPair with the folloing code:

    ECGenParameterSpec spec = new ECGenParameterSpec("secp521r1");
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECDH", BouncyCastleProvider.PROVIDER_NAME);
    kpg.initialize(spec, new SecureRandom());
    return kpg.generateKeyPair();

and try to get the byte array of the public key to send it to another person, the encoded key is 158 bytes long and in the X.509 format. But I expected the X9.62 format and a keysize between 65 and 66 bytes. Why is the public key this large and how can I encode it with the expected keysize? (I expected the keysize because I expect the key to be 521 bits long)

解决方案

An ECC publickey is semantically a point on a curve; if the curve you name is implied, a point in X9.62 format is either 67 octets (Java bytes) if compressed or 133 octets if uncompressed, never any other length.

If you mean java.security.PublicKey.getEncoded() that is always in what Java calls "X.509" encoding which is actually the ASN.1 structure SubjectPublicKeyInfo (SPKI) defined in X.509 and more conveniently available in rfc5280 sec 4.1, encoded as DER. An ECC publickey on that curve in this format is is 90 or 158 octets, exactly, for uncompressed or compressed, and the Java providers (at least currently) produce the uncompressed form (although they can parse compressed).

It sounds like you may want the X9.62 compressed format, which as I said is 67 bytes (not 65 or 66). If so, you can't control point compression in the standard Java API, but the BouncyCastle implementation classes do support it, given you have key objects created by the BC provider. First cast keypair.getPublicKey() to (corr) org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey (before 1.47 was org.bouncycastle.jce.provider.JCEECPublicKey) and then getQ() returns an org.bouncycastle.math.ec.ECPoint which has an (overloaded) getEncoded(boolean compressed) which produces what you apparently want.


For your additional but not (yet?) official question, to re-create a PublicKey object from an encoded point (compressed or not), you have two or three options depending how you count:

  • construct an ASN.1/DER-encoded SubjectPublicKeyInfo structure (which Java calls "X.509" format) for this curve and point, put it in X509EncodedKeySpec, and run that through an appropriate KeyFactory. Either the standard SunEC provider (assuming j7+, and not a RedHat-crippled version) or the BC provider can be used. Constructing ASN.1 encodings like SPKI by hand is difficult in general but not bad in this specific case; or given you have BC you can use its ASN.1 functionality

  • call the BC routines directly to do what the EC KeyFactory would do for the above input

Example code for creating a point and then using it all three ways:

// as needed in addition to standard java.security and javax.xml 
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;

    KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
    kpg.initialize(new ECGenParameterSpec("secp521r1"));
    org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey ku = 
            (org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey)kpg.generateKeyPair().getPublic();
    byte[] encodedpoint = ku.getQ().getEncoded(true/*compressed*/);

    { // construct SPKI by hand, this curve only
        byte[] hdr = DatatypeConverter.parseHexBinary("3058301006072a8648ce3d020106052b81040023034400");
        // could also write out byte[] hdr = {0x30,0x58,0x30,0x10... but items with 0x80 set need casts
        if( 0x44 /*hdr[0x15]*/ -1 != encodedpoint.length ) throw new Exception ("BAD COMPRESSED POINT FOR secp521r1!");
        byte[] spki = Arrays.copyOf(hdr,90); System.arraycopy(encodedpoint,0, spki,0x17, 0x43);
        PublicKey k2 = KeyFactory.getInstance("EC" /*,provider?*/).generatePublic(new X509EncodedKeySpec(spki));
        Signature.getInstance("ECDSA").initVerify(k2); // sanity check
    }
    { // construct SPKI with BC
        AlgorithmIdentifier algid = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey,SECObjectIdentifiers.secp521r1);
        ASN1EncodableVector vec = new ASN1EncodableVector();
        vec.add(algid); vec.add(new DERBitString(encodedpoint));
        byte[] spki = new DERSequence(vec).getEncoded();
        PublicKey k2 = KeyFactory.getInstance("EC" /*,provider*/).generatePublic(new X509EncodedKeySpec(spki));
        Signature.getInstance("ECDSA").initVerify(k2); // sanity check
    }
    { // call BC directly
        ProviderConfiguration configuration = BouncyCastleProvider.CONFIGURATION;
        X962Parameters params = X962Parameters.getInstance(org.bouncycastle.asn1.sec.SECObjectIdentifiers.secp521r1);
        ECCurve curve = EC5Util.getCurve(configuration, params);
        /*ECParameterSpec ecSpec = EC5Util.convertToSpec(params, curve);*/
        ECPoint point = curve.decodePoint(encodedpoint).normalize();
        ECPublicKeyParameters kparams = new ECPublicKeyParameters(point, ECUtil.getDomainParameters(configuration, params));
        PublicKey k2 = new BCECPublicKey ("EC"/* or "ECDH" etc*/, kparams, configuration);
        Signature.getInstance("ECDSA").initVerify(k2); // sanity check
    }

Related Loading raw 64-byte long ECDSA public key in Java which is for P256 uncompressed.

这篇关于Java ECC编码的密钥太大的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆