I am attempting to obtain a shared secret using BouncyCastle's ECDH methods in C#, however I am receiving a shared key which is much shorter than the 48 bytes I expect it to be.

I have a trusted external source providing me (Side A) with a public key and a private key, these were generated for P192, and are of length 48 for the public and 24 for the private.

Also, I'm receiving a public key for the B side to calculate the agreement with.

I am expecting the shared key to be of length 48, but ending up with length 24.

    string P192publicA = ""; // 48 length key
    string P192privateA = ""; // 24 length key
    string P192publicB = ""; // 48 length key

    DerObjectIdentifier p192Der = Org.BouncyCastle.Asn1.Sec.SecObjectIdentifiers.SecP192r1;        
    ECKeyGenerationParameters ecparams = new ECKeyGenerationParameters(p192Der, new SecureRandom());        
    ECCurve curveP192 = ecparams.DomainParameters.Curve;

    ECPrivateKeyParameters ecPrivateA = new ECPrivateKeyParameters("ECDSA", new BigInteger(P192privateA, 16), p192Der);        
    ECPublicKeyParameters ecPublicA = new ECPublicKeyParameters("ECDSA", curveP192.DecodePoint(Hex.Decode("04" + P192publicA)), p192Der);
    ECPublicKeyParameters ecPublicB = new ECPublicKeyParameters("ECDSA", curveP192.DecodePoint(Hex.Decode("04" + P192publicB)), p192Der);

    IBasicAgreement aKeyAgreeBasic = AgreementUtilities.GetBasicAgreement("ECDHC");

    BigInteger k = aKeyAgreeBasic.CalculateAgreement(ecPublicB);

My result in hex is only length 24 while I expected 48.

It there something wrong with my understanding of the key agreement mechanism? I wasn't sure about the decoding of a point, I saw in the bouncy examples they add "04" before decoding a point, and every other prefix I tried threw an exception.

Any tips will be greatly appreciated !


An elliptic curve is a point in a bi-dimensional space, hence it has two coordinates (usually called X and Y) which are values in some field. In the case of P-192, the field consists in integers modulo a prime p of length 192 bits (p lies between 2191 and 2192). The curve points are the (X,Y) pairs that fulfil the curve equation (Y2 = X3 + aX + b for two constants a and b that define the curve). It so happens that the number of points on the curve (the curve "order") is an integer n whose value is also close to (but somewhat lower than) 2192. The specific curve (the constants a and b) were chosen so that n is prime.

A private DH key is an integer modulo n. Thus, it can always be encoded as 192 bits (24 bytes) since n itself is a 192-bit integer.

A public DH key is a point; it is what the two parties send to each other. For private key u, the corresponding public key is the point uG (multiplication of a conventional curve point G, which is part of the curve definition).

When two parties U and V do an ECDH, they use (or generate randomly) their private keys u and v, and send to each other the corresponding public key uG and vG. The resulting shared secret is then the point uvG that they can both compute (but that eavesdroppers cannot compute, which is the point of ECDH).

It so happens that ECDH is defined by the ANSI X9.63 standard and that standard says that the actual shared secret is not the complete point uvG, but only the X coordinate of the point uvG. That coordinate is an element of the field in which we are working, i.e. an integer modulo p. Thus, it can be encoded over 192 bits, which is why you get only 24 bytes. Note that the value is an integer modulo p (the field modulus), while private keys are modulo n (the curve order); p and n are distinct (but both are 192-bit values in the P-192 curve).

You may ask how comes that ECDH is defined that way. The reason is mostly that point coordinates are redundant: since a point (X,Y) must fulfil the curve equation, from X alone you can compute X3 + aX + b = Y2, thus get the square of the Y coordinate. In a field, a given value can have at most two square roots, so if you have the X coordinate then you can recover Y and -Y, i.e. almost the full point. In that sense, with the X coordinate only, you almost have all the secrecy that you can expect from ECDH.

(This implies that ECDH is still possible when the two parties send to each other only the X coordinates of their respective public keys uG and vG, provided that they both recompute the missing Y. Not all implementations support that, though, since it involves some extra code for square roots, and there is some uncertainty about possible patents on the subject. In all generality, this is called point compression.)

| improve this answer | |
  • Thanks for the answer, your explanation cleared out some stuff for me. I believe now that my problem was that I needed to apply some KDF on the X in order to get to the shared 48 bytes. Do you know why that "04" is added to the decoding of a point? – g3trans Jul 11 '15 at 0:11
  • 2
    @g3trans: prefix byte value 4 means the point is uncompressed; 2 or 3 would mean compressed as noted in the answer Thomas linked to. (To be completist, your code uses hexadecimal strings for this data, so the string "04" decodes to the byte value 4.) – dave_thompson_085 Jul 11 '15 at 11:07
  • @dave_thompson_085 Do you know the meaning of the P521 X/Y prefixes 00 or 01? I can't seem to find a good page explaining this. – g3trans Jul 15 '15 at 14:36
  • 1
    Assuming you mean the first byte of each (noncompressed) coordinate, not the first byte of the whole point (which is 2,3,4 as above), that's just the first byte of the X or Y value. A 521-bit number occupies 115 full bytes plus one (leftmost) byte which contains only 1 used bit and the rest left-0-padding, and thus has a byte value 0 or 1. – dave_thompson_085 Jul 16 '15 at 13:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.