最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Encryption using .NET Framework and JavaScript with a self generated certificate - Stack Overflow

programmeradmin4浏览0评论

I'm trying to develop some sort of an end to end encryption using C# .NET Framework on the server side and JavaScript on the client side, this is what I need:

  1. Generating a salt in client side, and sending the salt + unique device number (I have it) to the server.

  2. On the server, Using the device number I need to create a self generated certificate with a private and public key, then encrypt the private key using the salt I sent and send the encrypted private key back to the client.

  3. In client I need to decrypt the key using the same salt and use it to encrypt a certain payload before sending to the server.

  4. Decrypt the payload using the public key (I stored it in the DB for that) and continue as regular.

No matter what I tried, I can't get past the 3, as I'm failing at the client side decryption (Maybe the problem is with the server side encryption, I'm not sure).

Any help will be appreciated.

Just attaching one sample of what I tried (I tried many more):

Server Side:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Web.Http;

[HttpPost]
[Route("api/device/certificate")]
public IHttpActionResult GenerateDeviceCertificate([FromBody] CertificateRequestModel request)
{
    try
    {
        // Create certificate with private key
        var subjectName = $"CN={request.DeviceNumber}";
        var rsa = new RSACryptoServiceProvider(2048);
        var req = new CertificateRequest(subjectName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

        var cert = new X509Certificate2(req.CreateSelfSigned(
            DateTimeOffset.Now,
            DateTimeOffset.Now.AddYears(1)).Export(X509ContentType.Pfx),
            (string)null,
            X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);

        // Get private key
        var privateKey = cert.PrivateKey as RSACryptoServiceProvider;
        if (privateKey == null)
            throw new Exception("Failed to get private key from certificate");

        var privateKeyBytes = privateKey.ExportCspBlob(true);

        // Encrypt the private key using AES with the provided salt
        byte[] saltBytes = Encoding.UTF8.GetBytes(request.Salt);
        using (Aes aes = Aes.Create())
        {
            using (var keyDerivation = new Rfc2898DeriveBytes(request.Salt, saltBytes, 10000))
            {
                aes.Key = keyDerivation.GetBytes(32);
                aes.GenerateIV();

                byte[] encryptedPrivateKey = EncryptAES(privateKeyBytes, aes.Key, aes.IV);

                return Ok(new
                {
                    success = true,
                    encryptedPrivateKey = Convert.ToBase64String(encryptedPrivateKey),
                    iv = Convert.ToBase64String(aes.IV)
                });
            }
        }
    }
    catch (Exception ex)
    {
        return Ok(new
        {
            success = false,
            error = ex.Message
        });
    }
}

// AES Encryption Helper
private byte[] EncryptAES(byte[] data, byte[] key, byte[] iv)
{
    using (Aes aes = Aes.Create())
    {
        aes.Key = key;
        aes.IV = iv;

        using (var encryptor = aes.CreateEncryptor())
        {
            return encryptor.TransformFinalBlock(data, 0, data.Length);
        }
    }
}

Client Side:

async function requestKeyFromServer() {
    // Generate a random salt for key encryption
    const salt = crypto.getRandomValues(new Uint8Array(16))
        .reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');

    // Prepare request data
    const certificateRequest = {
        deviceNumber: _userDevice,
        salt: salt
    };

    // Send to server to generate certificate
    try {
        const response = await fetch(_urlBase + 'api/device/certificate', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(certificateRequest)
        });

        const result = await response.json();
        if (result.success) {
            // Decrypt the private key using the same salt
            _privateKey = await decryptPrivateKey(result.encryptedPrivateKey, salt, result.iv);

            // Store the decrypted private key securely
            //storeKey(response.encryptedPrivateKey, salt, response.Expiry);            
        } else {
            throw new Error(result.error || 'Failed to generate certificate');
        }
    } catch (error) {
        console.error('Certificate generation failed:', error);
        throw error;
    }
}

    async function decryptPrivateKey(encryptedPrivateKey, salt, iv) {
        const aesKey = await deriveKey(salt);
        const encryptedData = Uint8Array.from(atob(encryptedPrivateKey), c => c.charCodeAt(0));
        const ivBytes = Uint8Array.from(atob(iv), c => c.charCodeAt(0));
    
        const decrypted = await crypto.subtle.decrypt(
            { name: "AES-CBC", iv: ivBytes },
            aesKey,
            encryptedData
        );
    
        return new TextDecoder().decode(decrypted);
    }
    
    async function deriveKey(salt) {
        const saltBytes = new TextEncoder().encode(salt); // Convert salt string to Uint8Array
    
        // Import key material (PBKDF2 requires a key material, which is a raw buffer from the salt)
        const keyMaterial = await crypto.subtle.importKey(
            "raw",
            saltBytes, // Corrected: Import the salt as raw key material
            { name: "PBKDF2" },
            false,
            ["deriveBits", "deriveKey"]
        );
    
        // Derive AES key from PBKDF2
        return await crypto.subtle.deriveKey(
            {
                name: "PBKDF2",
                salt: saltBytes,
                iterations: 100000, // Higher is more secure
                hash: "SHA-256"
            },
            keyMaterial,
            { name: "AES-CBC", length: 256 }, // Derive a 256-bit AES key
            true,
            ["encrypt", "decrypt"]
        );
    }
发布评论

评论列表(0)

  1. 暂无评论