Encryption .Net内核和WebCrypto握手

Encryption .Net内核和WebCrypto握手,encryption,.net-core,rsa,webcrypto-api,Encryption,.net Core,Rsa,Webcrypto Api,我希望通过以下步骤初始化服务器(.NET Core 3.1)和客户端浏览器应用程序(使用WebCryptoAPI的JS)之间的安全通道: 服务器将其公共RSA密钥K发送到客户端 客户机使用K加密其公开RSA密钥L,并将其发送到服务器 服务器解密消息以获取L,使用L加密AES密钥并将其发送回客户端 客户机解密包含AES密钥的消息,然后他们安全地交谈 到目前为止,我成功地完成了第二步: TypeScript客户端请求服务器的公钥 const hubConnection=new signal.HubC

我希望通过以下步骤初始化服务器(.NET Core 3.1)和客户端浏览器应用程序(使用WebCryptoAPI的JS)之间的安全通道:

  • 服务器将其公共RSA密钥
    K
    发送到客户端
  • 客户机使用
    K
    加密其公开RSA密钥
    L
    ,并将其发送到服务器
  • 服务器解密消息以获取
    L
    ,使用
    L
    加密AES密钥并将其发送回客户端
  • 客户机解密包含AES密钥的消息,然后他们安全地交谈
  • 到目前为止,我成功地完成了第二步:

    TypeScript客户端请求服务器的公钥
    const hubConnection=new signal.HubConnectionBuilder().withUrl(“/hub/myhub”).build();
    generateKey({name:“RSA-OAEP”,
    模长:2048,
    publicExponent:new Uint8Array([1,0,1]),
    散列:“SHA-512”},
    是的,
    [“加密”、“解密”])
    。然后(键=>{
    clientRSA=密钥;
    调试(“客户端RSA:”,客户端RSA);
    返回hubConnection.start();
    })
    。然后(()=>hubConnection.invoke(“requestServerKey”);
    
    C#后端创建一个新的键和答案
    公共类MyHub:Hub
    {
    公共覆盖异步任务OnConnectedAsync()
    {
    使用var rsa=rsa.Create(KeySize);
    //将其存储到内存数据库中
    }
    }
    public void RequestServerKey()
    {
    RSA=DB.Get(Context.ConnectionId);
    Clients.Caller.SetServerKey(Convert.ToBase64String(rsa.ExportSubjectPublicKeyInfo());
    }
    
    Typescript客户端接收公钥
    hubConnection.on('setServerKey',(数据:字符串)=>{
    const buffer=Uint8Array.from(atob(数据),c=>c.charCodeAt(0));
    Crypto.importKey(“spki”,缓冲区,{name:“RSA-OAEP”,散列:“SHA-512”},false,['encrypt']))
    .then(rsaPublicKey=>{//由于某种原因,我无法将这个承诺提升到零级
    serverKey=rsaPublicKey;
    返回加密导出密钥(“spki”,clientRSA.publicKey);
    })
    //以下调用失败
    .then(clientPublicKey=>Crypto.encrypt({name:“RSA-OAEP”},serverKey,clientPublicKey))
    。然后(消息=>{
    const str=btoa(String.fromCharCode(…新的Uint8Array(message));
    调用(“requestSymmetricKey”,str);
    })
    });
    
    服务器的公钥似乎已通过
    webcryptoapi
    成功导入,但当我尝试在将其发送到服务器之前使用它加密客户端的公钥时,它失败了。我还尝试了
    wrapKey
    方法,但错误都是一样的。我得到以下信息:

    index.js:1 Uncaught Error: The error you provided does not contain a stack trace.
        at L (index.js:1)
        at Y (index.js:1)
        at index.js:1
        at index.js:1
        at l (index.js:1)
    
    home:1 Uncaught (in promise) DOMException
    Promise.then (async)
    ...
    
    
    因此,我在寻找任何可能是问题所在的线索。我尝试了各种初始化参数的组合,查看了所有中间值,但没有任何东西提示可能的错误。除了我犯了一些愚蠢的复制错误外,我的代码应该遵循以下示例:


    感谢您的帮助

    因此,我在以下帖子的帮助下找到了解决方案:

    由于服务器的公钥是2048位,并且使用SHA-512,因此最大消息长度仅为126字节。然而,我试图传输的消息是294字节。遗憾的是,API没有专门的错误消息,因为它可以很容易地自动检查

    根据对我的问题的评论中的讨论,我已将程序简化为:

  • 服务器在其API中公开其公共RSA密钥,并将其(或其散列)发布到其他独立位置进行验证
  • 客户端生成一个对称AES密钥,通过服务器的公钥RSA对其进行加密,并将其发送到服务器
  • 服务器使用其私有RSA密钥对对称AES密钥进行解密,然后它们安全地通信

  • 现在,我希望JS和C#之间的所有工作都能正常进行。

    为什么要加密客户端的公钥(步骤2)?我可能使用了错误的术语。服务器和客户端都会生成一对非对称密钥(每个会话都是唯一的),它们应该使用这些密钥安全地交换公共对称AES密钥。因此,从技术上讲,只有服务器的“公钥”才是唯一不受保护的公钥。公钥不需要加密,因此使用“公钥”一词(但是,公钥的所有权必须得到证明,通常是通过证书证明的,否则可能会发生中间人攻击)。唯一需要加密的密钥是对称密钥。也许我误解了你。从那以后我一直在思考你的问题。基本上,您建议将其简化为:1)向服务器发送我的公钥2)服务器使用加密对称密钥响应3)我们从此安全地交谈。因此,我现在试图找出简化例程中是否没有安全漏洞……如果服务器使用您的公钥进行加密,则只有您作为相关私钥的所有者才能解密。这是有保证的。当攻击者在交换过程中截获您的公钥并将其自己的公钥发送到服务器(中间人攻击)时,就会出现问题。再看一看混合加密和PKI(公钥基础设施)的概念。