.NET Core 5.0到Javascript DFH密钥交换不工作

.NET Core 5.0到Javascript DFH密钥交换不工作,javascript,c#,diffie-hellman,Javascript,C#,Diffie Hellman,我们正试图获得一个基于浏览器的应用程序,该应用程序使用JS与使用Eliptical Curve Diffie Hellman的.Net Core 5.0服务器交换密钥。 我们的应用程序需要在两端共享一个秘密,用于我们所做的某些特定处理(不是加密,而是我们自己的处理),我们希望该秘密能够被派生出来 而不是出于安全目的传输。 我们已经四处寻找合适的解决方案,并将各种解决方案拼凑在一起,我们已经找到了这个解决方案,但它失败了,C#端出现了一个例外。 基本上,我们的伪代码如下(到目前为止,我们只进行了步

我们正试图获得一个基于浏览器的应用程序,该应用程序使用JS与使用Eliptical Curve Diffie Hellman的.Net Core 5.0服务器交换密钥。 我们的应用程序需要在两端共享一个秘密,用于我们所做的某些特定处理(不是加密,而是我们自己的处理),我们希望该秘密能够被派生出来 而不是出于安全目的传输。 我们已经四处寻找合适的解决方案,并将各种解决方案拼凑在一起,我们已经找到了这个解决方案,但它失败了,C#端出现了一个例外。 基本上,我们的伪代码如下(到目前为止,我们只进行了步骤3):

  • 客户端(Bob)使用window.crypto.minute库在客户端生成ECDH/P-256密钥对
  • Bob从此对导出公钥,并向服务器(Alice)发出GET以获取她的公钥(将BobPubliceByB64作为查询参数传递)
  • Alice接受传入的请求并调用一个C#方法来使用Bob的公钥创建一个共享密钥
  • Alice将此共享秘密存储在memcache中,并将其公钥返回给Bob
  • 然后,Bob使用Alice的公钥获取自己的共享密钥,并将其存储在浏览器的“会话存储”中
  • 这里没有显示将Bob的公钥发送给Alice的代码,它是一个标准的XMLHttpRequest。但是,它确实可以工作,并且服务器被调用,正如C#代码中的故障所见证的那样。我们已经验证了Bob发送BobPubliceB64时与Alice收到BobPubliceB64时完全相同

    一旦这项工作开始,我们将加强两端的存储方法,但首先我们需要让exchange正常工作。 Alice(C#)代码块中的注释显示了它失败的地方。 getDerivedKey方法(目前已注释掉)来自以下帖子- 它失败了,但出现了一个不同的异常(我确信这两个失败都是由于JS库和.Net实现之间的不匹配造成的,但我们无法处理它)。 我们非常感谢所有的帮助——基本问题是“当Bob是JS,Alice是C#时,我们如何让Bob和Alice说话?”

    以下是Bob上的JS代码:

    async function setUpDFHKeys() {
    let bobPublicKeyB64;
    let bobPrivateKeyB64;
    try {
    
        const bobKey = await window.crypto.subtle.generateKey(
            { name: 'ECDH', namedCurve: 'P-256' },
            true,
            ["deriveKey"]
        );
                
        const publicKeyData = await window.crypto.subtle.exportKey("raw", bobKey.publicKey);
        const publicKeyBytes = new Uint8Array(publicKeyData);
        const publicKeyB64 = btoa(publicKeyBytes);        
        bobPublicKeyB64 = publicKeyB64;
    
        const privateKeyData = await window.crypto.subtle.exportKey("pkcs8", bobKey.privateKey);
        const privateKeyBytes = new Uint8Array(privateKeyData);
        const privateKeyB64 = btoa(String.fromCharCode.apply(null, privateKeyBytes));
        bobPrivateKeyB64 = privateKeyB64;
    }
    catch (error) {
        console.log("Could not setup DFH Keys " + error);
    }};
    
    以下是Alice的C#代码:

      private string WorkWithJSPublicKey(string bobPublicKeyB64, out string alicePublicKey)
        {
            try
            {
                //
                // Alice is this server.
                // Bob is a browser that uses the window.crypto.subtle.generateKey with 'ECDH' and 'P-256' as parameters
                // and window.crypto.subtle.exportKey of the key.publicKey in "raw" format (this is then converted to a B64 string using btoa)
                //
                using (ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256))
                {
                    //
                    // Get the public key info from Bob and convert to B64 string to return to Alice
                    //
                    Span<byte> exported = new byte[alice.KeySize];
                    int len = 0;
                    alice.TryExportSubjectPublicKeyInfo(exported, out len);
                    alicePublicKey = Convert.ToBase64String(exported.Slice(0, len));
                    //
                    // Get Alice's private key to use to generate a shared secret
                    //
                    byte[] alicePrivateKey = alice.ExportECPrivateKey();
                    //
                    // Import Bob's public key after converting it to bytes
                    //
                    var bobPubKeyBytes = Convert.FromBase64String(bobPublicKeyB64);
                    //
                    // TRY THIS... (Bombs with "The specified curve 'nistP256' or its parameters are not valid for this platform").
                    //
                    // getDerivedKey(bobPubKeyBytes, alice);
                    //
                    // This throws exception ("The provided data is tagged with 'Universal' class value '20', but it should have been 'Universal' class value '16'.")
                    //
                    alice.ImportSubjectPublicKeyInfo(bobPubKeyBytes, out len);
                    //
                    // Once Alice knows about Bob, create a shared secret and return it.
                    //
                    byte[] sharedSecret = alice.DeriveKeyMaterial(alice.PublicKey);
                    return Convert.ToBase64String(sharedSecret);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, ex.Message);
                alicePublicKey = string.Empty;
                return string.Empty;
            }
        }
    

    如果Web加密端的公钥以X.509/SPKI格式导出,而不是作为原始密钥导出,则解决方案更简单,因为.NET 5对此格式有一个专用的导入方法。此外,这与C#代码一致,其中公钥也以X.509/SPKI格式导出。在下面的示例中,Web加密代码以X.509/SPKI格式导出公钥

    步骤1-Web加密端(Bob):生成EC密钥对

    以下Web加密代码创建ECDH密钥对,并导出X.509/SPKI格式的公钥(以及PKCS8格式的私钥)。请注意,由于存在错误,以PKCS8格式导出在Firefox下不起作用,另请参见:

    setUpDFHKeys()。然后(()=>{});
    异步函数setUpDFHKeys(){
    var bobKey=wait window.crypto.minute.generateKey(
    {name:'ECDH',namedCurve:'P-256'},
    是的,
    [“衍生密钥”]
    );
    var publicKeyData=await window.crypto.minute.exportKey(“spki”,bobKey.publicKey);
    var publicKeyBytes=新的Uint8Array(publicKeyData);
    var publicKeyB64=btoa(String.fromCharCode.apply(null,publicKeyBytes));
    log(“Bob的public:\n”+publicKeyB64.replace(/({56})/g,$1\n');
    var privateKeyData=await window.crypto.minute.exportKey(“pkcs8”,bobKey.privateKey);
    var privateKeyBytes=新的Uint8Array(privateKeyData);
    var privateKeyB64=btoa(String.fromCharCode.apply(null,privateKeyBytes));
    log(“Bob的private:\n”+privateKeyB64.replace(/({56})/g,$1\n');
    
    };只是“.NET5.0”,而不是“.NETCore5.0”。谢谢,这很有效。注意,在将BobPublicey发送给Alice之前,必须使用EncodeuriComponent(BobPublice64)对其进行编码,因为它嵌入了“+”和“=”字符。
    static byte[] getDerivedKey(byte[] key1, ECDiffieHellman alice)
        {
            byte[] keyX = new byte[key1.Length / 2];
            byte[] keyY = new byte[keyX.Length];
            Buffer.BlockCopy(key1, 1, keyX, 0, keyX.Length);
            Buffer.BlockCopy(key1, 1 + keyX.Length, keyY, 0, keyY.Length);
            ECParameters parameters = new ECParameters
            {
                Curve = ECCurve.NamedCurves.nistP256,
                Q = {
                        X = keyX,
                        Y = keyY,
                    },
            };
            byte[] derivedKey;
            using (ECDiffieHellman bob = ECDiffieHellman.Create(parameters))
            using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
            {
                return derivedKey = alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256);
            }
        }