Curve25519 exchange between BouncyCastle and .NET

171 Views Asked by At

I wrote X25519 curve parameters in .NET and thought to cross-check it between this piece and BouncyCastle. The following is the code. But it fails to export the BouncyCastle key.

There will be an exception

System.Security.Cryptography.CryptographicException : Key is not a valid public or private key.

when I try to import the key between steps 3 and 4 (see code).

I'm a bit at loss what could be the reason for that.

Hints, tips or anything to push towards making this work would be much appreciated. How to make this cross-check work? What do I miss? Is there something with the curve?


public static ECCurve Curve25519 => new()
{
    CurveType = ECCurve.ECCurveType.PrimeMontgomery,

    //In NIST.SP.800-186-draft.pdf: 'A'.
    A = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x07, 0x6d, 0x06 }, // 486662

    //In NIST.SP.800-186-draft.pdf: 'B'.
    B = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },

    G = new ECPoint()
    {
        //In NIST.SP.800-186-draft.pdf: 'Gu'.
        X = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 },

        //Y = Gy2.ToByteArray(true, true),
        //In NIST.SP.800-186-draft.pdf: 'Gu'.
        Y = new byte[] { 0x2f, 0xaf, 0x1f, 0xaf, 0xbf, 0xa0, 0x86, 0xb4, 0xe0, 0x1e, 0xdd, 0x2c, 0x77, 0x48, 0xd1, 0x4c, 0x92, 0x3d, 0x4d, 0x7e, 0x6d, 0x7c, 0x61, 0xb2, 0x29, 0xe9, 0xc5, 0xa2, 0x7e, 0xce, 0xd3, 0xd9 }
    },

    //Prime = p = 2^255 - 19.
    Prime = new byte[] { 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed },

    //Prime = new byte[] { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },

    //In NIST.SP.800-186-draft.pdf: 'n'.
    Order = new byte[] { 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xde, 0xf9, 0xde, 0xa2, 0xf7, 0x9c, 0xd6, 0x58, 0x12, 0x63, 0x1a, 0x5c, 0xf5, 0xd3, 0xed },

    //In NIST.SP.800-186-draft.pdf: 'h'.
    Cofactor = new byte[] { 8 }
};


public void CrossCheck25519UsingBouncyCastleAndDotNet()
{
    // Step 1: Bob generates keys using BouncyCastle
    var randomGenerator = new SecureRandom();
    var bobPrivateKeyBouncy = new X25519PrivateKeyParameters(randomGenerator);
    X25519PublicKeyParameters bobPublicKeyBouncy = bobPrivateKeyBouncy.GeneratePublicKey();

    // Alternatively...
    // var keys = keyGenerator.GenerateKeyPair();
    // var bobPublicKeyBouncy = (X25519PublicKeyParameters)keys.Public;
    // var bobPrivateKeyBouncy = (X25519PrivateKeyParameters)keys.Private;

    // Step 2: Bob sends his public key to Alice            
    SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(bobPublicKeyBouncy);
    byte[] bobPublicKeyBytes = publicKeyInfo.GetDerEncoded();

    // Step 3: Alice receives Bob's public key and generates her own keys using .NET
    using(var alice = ECDiffieHellman.Create(Curve25519))
    {
        // This fails. Is the reason Curve25519 parameters or that BouncyCastle exported keys are in wrong format?
        alice.ImportSubjectPublicKeyInfo(bobPublicKeyBytes, out _);

        // Alice calculates the shared secret
        byte[] aliceSharedSecret = alice.DeriveKeyFromHash(alice.PublicKey, HashAlgorithmName.SHA256);

        // Step 4: Alice sends her public key to Bob
        byte[] alicePublicKeyBytes = alice.PublicKey.ExportSubjectPublicKeyInfo();

        // Step 5: Bob imports Alice's public key using BouncyCastle
        var alicePublicKeyBouncy = new X25519PublicKeyParameters(alicePublicKeyBytes, 0);

        // Step 6: Bob calculates the shared secret using BouncyCastle
        var bobAgreement = new X25519Agreement();
        bobAgreement.Init(bobPrivateKeyBouncy);
        byte[] bobSharedSecret = new byte[bobAgreement.AgreementSize];
        bobAgreement.CalculateAgreement(alicePublicKeyBouncy, bobSharedSecret, 0);

        // Step 7: Compare the shared secrets
        Assert.Equal(bobSharedSecret, aliceSharedSecret);
    }
}

0

There are 0 best solutions below