1

I'm trying to generate a public key with BouncyCastle (because I'm using Unity and do not have access to ECDiffieHellmanCng), and then I transfer the public key to the server which is using ECDiffieHellmanCng for its key handling. The server is rejecting my key, for what appears to be because of its small length. ECDiffieHellmanCng generates a public key that is much larger in size compared to that of what Bouncy castle generates.

Is there a way to generate a larger key in bouncy castle?

I tried changing the keybit size, but get an error saying: InvalidParameterException: unknown key size.

Key that BouncyCastle generates:

3059301306072A8648CE3D020106082A8648CE3D03010703420004272F71C1D8B3DC0A7FCB1E9650EEF64EA8F639BEC97D49F8848455C2F5869F7324332D188129C84727F834EE7EE7D8EB7DFC8D40CD4ED219A4FBCEF6C15200F3

Key that ECDiffieHellmanCng generates:

45434B35420000000055CC8665A66A7CDF2E9BF7C69A25B322C72CDBDB1EA8F348050B0A7CF32F9AAD890EA513583367977D5157B2F7FBF55661C9AE2DBAF09B1DC1EA8F193688C3C09501BEE326867ABCB41CA1029F66AF888649F0A6C0674D19670CF32461BA7B3867C1623D68829A7A9A7F1CFC6F5DB99E13C8D960AEF6F5CDAB5B3B62ED6CBEC7222C9F

Here is the code thats generating the bouncy castle key:

    const string Algorithm = "ECDH";
    const int KeyBitSize = 256;
    const int NonceBitSize = 128;
    const int MacBitSize = 128;
    const int DefaultPrimeProbability = 30;

    IAsymmetricCipherKeyPairGenerator aliceKeyGen = GeneratorUtilities.GetKeyPairGenerator(Algorithm);
    DHParametersGenerator aliceGenerator = new DHParametersGenerator();
    aliceGenerator.Init(KeyBitSize, DefaultPrimeProbability, new SecureRandom());
    DHParameters aliceParameters = aliceGenerator.GenerateParameters();
    KeyGenerationParameters aliceKGP = new DHKeyGenerationParameters(new SecureRandom(), aliceParameters);
    aliceKeyGen.Init(aliceKGP);

    AsymmetricCipherKeyPair aliceKeyPair = aliceKeyGen.GenerateKeyPair();
    IBasicAgreement aliceKeyAgree = AgreementUtilities.GetBasicAgreement(Algorithm);
    aliceKeyAgree.Init(aliceKeyPair.Private);

    SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(aliceKeyPair.Public);
    byte[] serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded();
    string serializedPublic = AsString(serializedPublicBytes);

public static string AsString(byte[] bytes, bool keepDashes = false)
    {
        string hex = BitConverter.ToString(bytes);
        return (keepDashes ? hex : hex.Replace("-", ""));
    }

I also tried the Mentalis.org DH library, which gives me a larger key, but still just a hair too short.

// create a new DH instance
DiffieHellman dh1 = new DiffieHellmanManaged();
// generate the public key of the first DH instance        
byte[] ke1 = dh1.CreateKeyExchange();
string publicKeyString = AsString(ke1);

Key from mentalis.org library:

5F4542F9A8F5636ECCBBAC38238C97ABE757B8F65E25B181BCF41C58985E699EFD6B9606B99F7074717E83F7AC1B5E97DFF6DBA94876F74645F25F0D7FAA1528898C1BD0BB568DF15A98724093766B213769893A05B47E40410B0F395C834F68F57B2EE01852895D912C1D56675A7D8C5367B5E06DE08AAA18CBB4C69F3AE142

Snipe3000
  • 321
  • 3
  • 15
  • 2
    It not so much about size but about format. Bouncycastle returns a standardardized encoding of a public key but your ECDiffieHellmanCng is a Microsoft-proprietary blob. The Mentalis result is *not* an ECDH value at all. – President James K. Polk Feb 24 '17 at 22:15
  • Hmmmm, is there anyway to get the Microsoft-proprietary blob using something other than ECDiffieHellmanCng? I do not have access to it. – Snipe3000 Feb 27 '17 at 17:46
  • 1
    Oh, and part of the size difference is you have a NIST P-521 key in CNG, and a NIST P-256 key from BouncyCastle. (0x354B4345 == BCRYPT_ECDH_PUBLIC_P521_MAGIC) – bartonjs Feb 27 '17 at 19:31
  • Snipe3000: dunno, but you might find some of this code useful: https://gist.github.com/larsw/2c45147a242ceb079f5d – larsw Mar 10 '17 at 12:14
  • Also, check out the answers to this question http://stackoverflow.com/questions/24251336/import-a-public-key-from-somewhere-else-to-cngkey – larsw Mar 10 '17 at 12:43
  • Thanks for those links larsw. Unfortunately both solutions hinge on having CngKey.Import available, which is not accessible in the latest version of mono that Unity uses, so its unavailable to me. – Snipe3000 Mar 15 '17 at 17:51

1 Answers1

2

If you were to decode the BouncyCastle version you'd see that it is

30 59
SEQUENCE
  30 13
  SEQUENCE
    06 07 2A 86 48 CE 3D 02 01
    OBJECT IDENTIFIER 1.2.840.10045.2.1 (id-ecPublicKey)
    06 08 2A 86 48 CE 3D 03 01 07
    OBJECT IDENTIFIER 1.2.840.10045.3.1.7 (id-secp256r1)
  03 42 00
  BIT STRING
    04 27 2F 71 C1 D8 B3 DC 0A 7F CB 1E 96 50 EE F6
    4E A8 F6 39 BE C9 7D 49 F8 84 84 55 C2 F5 86 9F
    73 24 33 2D 18 81 29 C8 47 27 F8 34 EE 7E E7 D8
    EB 7D FC 8D 40 CD 4E D2 19 A4 FB CE F6 C1 52 00
    F3

The BIT STRING's payload is the encoded value of an ecPublicKey whose curve is secp256r1.

Then, following 2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion from the SEC-1 paper we see that it's encoded as

04
Uncompressed Point
  X = 27 2F 71 C1 D8 B3 DC 0A 7F CB 1E 96 50 EE F6 4E
      A8 F6 39 BE C9 7D 49 F8 84 84 55 C2 F5 86 9F 73
  Y = 24 33 2D 18 81 29 C8 47 27 F8 34 EE 7E E7 D8 EB
      7D FC 8D 40 CD 4E D2 19 A4 FB CE F6 C1 52 00 F3

Following the logic from the .NET Core import/export ECC feature we see that the equivalent CNG blob is

// BCRYPT_ECDH_PUBLIC_P256_MAGIC (little-endian)
45 43 B4 31
// cbKey=(DWORD)32 (little-endian)
20 00 00 00
// The X bytes (big-endian):
27 2F 71 C1 D8 B3 DC 0A 7F CB 1E 96 50 EE F6 4E
A8 F6 39 BE C9 7D 49 F8 84 84 55 C2 F5 86 9F 73
// The Y bytes (big-endian):
27 2F 71 C1 D8 B3 DC 0A 7F CB 1E 96 50 EE F6 4E
A8 F6 39 BE C9 7D 49 F8 84 84 55 C2 F5 86 9F 73
bartonjs
  • 30,352
  • 2
  • 71
  • 111
  • So it sounds like there might be a way to convert BouncyCastle's public key blob to the ECDiffieHellmanCng public key blob? – Snipe3000 Feb 27 '17 at 20:21