簡體   English   中英

如何將 Metamask 連接到 Web3J (java)

[英]How to connect Metamask to Web3J (java)

我正在嘗試將我的 Metamask 錢包連接到我的 Java Spring-Boot 后端。 我試圖按照這里的例子。 我能夠自動生成隨機數並毫無問題地接收錢包 ID。 我正在嘗試驗證服務器上錢包的簽名隨機數,以確保發件人確實是他們所說的人。 但是,我無法在 Web3J 上找到任何文檔來執行此操作。

web3j 不是用於此目的的正確軟件包嗎? 該示例顯示了如何基於 javascript 在 NodeJS 上進行驗證,但我沒有找到任何關於如何在 Java 上執行此操作的示例。

我的理解是,公鑰是錢包 ID 本身,消息是由錢包私鑰簽名的隨機數,出於明顯的原因不共享。 據此,我需要使用公鑰“解密”消息,並查看解密的消息是否與后端發送給 Metamask 簽名的隨機數相同。 這個對嗎?

這是我創建隨機數並將其發送到 UI 的代碼:

public User findUserByPublicAddress(String publicWalletId) {
    User u = userRepository.findByPublicWalletId(publicWalletId);
    if(u == null) {
        u = new User("", "", "", null, publicWalletId, "");
        String nonce = StringUtil.generateRandomAlphaNumericString();
        u.setNonce(nonce);
        userRepository.saveAndFlush(u);
    }
    return u;
}

在這里,我查看用戶是否已經在我的系統中,如果不是,那么我只需創建一個臨時用戶,並生成一個隨機隨機數並將其保存在數據庫中。 這個 nonce 被發送到 UI 以供 Metamask 簽名。 但是,我不確定如何進行驗證部分。

我終於能夠弄清楚這一點。 我最初的理解是錯誤的。 我不應該嘗試解密消息以檢索隨機數。 相反,我需要使用 nonce 來查看是否可以檢索用於簽署消息的私鑰的公鑰,並查看檢索到的公鑰是否與錢包 ID 匹配。

算法:

  1. 從客戶端接收簽名消息和錢包ID
  2. 檢索發送給具有相同錢包 ID 的客戶端的 nonce
  3. 生成隨機數的哈希
  4. 從消息中生成簽名數據。 這基本上檢索了 V、R 和 S 和。 R 和 S 是 ECDSA 簽名的輸出,V 是恢復 ID。
  5. 使用 ECDSA 簽名和 Nonce 的散列,生成可能用於對消息進行簽名的公鑰。 最多可以為該消息生成 4 個可能的公鑰。
  6. 檢查任何生成的密鑰是否與客戶端發送的公共錢包 ID 匹配。 如果它匹配,那么我們有一個肯定的匹配。 生成 JWT 並響應客戶端。 如果不是,我們就知道這個 nonce 沒有被我們預期的 Metamask 錢包簽名。

編碼:

這是 UI(JavaScript 和 HTML)的示例代碼:

web3.eth.sign(
    web3.utils.sha3(nonce),
    window.userWalletAddress)
.then((message) => {
    console.log(message)
    data['message'] = message // BODY
    var xmlReq = new XMLHttpRequest();
    xmlReq.onreadystatechange = function() {
        if(this.readyState == 4 && this.status == 200) {
            response = this.responseText
            console.log(response)
        }
    };
    xmlReq.open("POST", "/api/users/login", true)
    xmlReq.setRequestHeader('Content-Type', 'application/json')
    xmlReq.send(JSON.stringify(data))
})

web3.eth.sign() 獲取要簽名的消息並獲取正在簽名的錢包 ID。 然后將其發送到后端。 在后端:

public User signin(UserLoginDTO loginDetails, HttpServletResponse response) {
    try {
        // Get the wallet ID and signed message from the body stored in the DTO
        String publicWalletId = loginDetails.getPublicWalletId();
        String message = loginDetails.getMessage();

        // Find the nonce from the DB that was used to sign this message
        User user = userRepository.findByPublicWalletId(publicWalletId);
        String nonce = user.getNonce();

        // Generate the HASH of the Nonce
        byte[] nonceHash = Hash.sha3(nonce.getBytes()) // org.web3j.crypto.Hash

        // Generate the Signature Data
        byte[] signatureBytes = Numeric.hexStringToByteArray(message); // org.web3j.utils.Numeric
        
        byte v = (byte) ((signatureBytes[64] < 27) ? (signatureBytes[64] + 27) : signatureBytes[64]);
        byte[] r = Arrays.copyOfRange(signatureBytes, 0, 32);
        byte[] s = Arrays.copyOfRange(signatureBytes, 32, 64);
        
        SignatureData signatureData = new SignatureData(v, r, s); // org.web3j.crypto.Sign.SignatureData

        // Generate the 4 possible Public Keys
        List<String> recoveredKeys = new ArrayList<>();
        for(int i = 0; i < 4; i++) {
            BigInteger r = new BigInteger(1, signatureData.getR());
            BigInteger s = new BigInteger(1, signatureData.getS());
            ECDSASignature ecdsaSignature = new ECDSASignature(r, s);
            BigInteger recoveredKey = Sign.recoverFromSignature((byte)i, ecdsaSignature, nonceHash);
            if(recoveredKey != null) {
                recoveredKeys.add("0x" + Keys.getAddressFromKey(recoveredKey)); // org.web3j.crypto.Keys
            }
        }

        // Check if one of the generated Keys match the public wallet ID.
        for(String recoveredKey : recoveredKeys) {
            if(recoveredKey.equalsIgnoreCase(publicWalletId)) { 
                // Add Code here to create the JWT and add that to your HttpServletResponse. Not shown here.
                return user;
            }
        }
        throw new CustomException("Message Sign Invalid", HttpStatus.UNAUTHORIZED);
    }
    catch (Exception ex) {
         // Custom Error Handling.
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM