Java ECC encoded Key too large

喜你入骨 提交于 2020-01-02 16:14:12

问题


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)


回答1:


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.



来源:https://stackoverflow.com/questions/51347513/java-ecc-encoded-key-too-large

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!