Solidity
如何加密具有多個公共地址的文件?
我可以使用多個公共地址加密文件,以便每個人都可以使用他們的私鑰解密文件嗎?從而實現了一個將文件安全地儲存在 IPFS 上的系統,該文件只能由選定數量的個人訪問。我究竟如何才能做到這一點?如何在 Solidity 和 JS 中實現這一點。
此腳本允許您使用乙太坊私鑰生成PGP密鑰對:
npm install openpgp
const openpgp = require('openpgp'); const ethereumPrivateKey = 'e9ee206196eb26b92b937b83b7a44974b1c3d995b9e61ca65d2c5760a97250af'; const buffKey = Buffer.from(ethereumPrivateKey); const privateKey = openpgp.util.Uint8Array_to_str(buffKey); const options = { curve: 'secp256k1', userIds: {name: 'Tiago', email: 'github@tloriato.com'}, numBits: 2048, material: { key: privateKey, } }; openpgp.generateKey(options).then(function(key) { console.log(key.privateKeyArmored); }).catch(function(error) { console.log(error); });
我們將假設您生成了兩個不同的密鑰
key1
並key2
使用該方法,現在您想使用key1.publicKeyArmored
and加密一些數據key2.publicKeyArmored
(堆棧語法突出顯示現在非常糟糕,但它有效,我保證):const openpgp = require('openpgp'); openpgp.initWorker({ path: 'openpgp.worker.js' }); const keys = [`-----BEGIN PGP PUBLIC KEY BLOCK----- Version: OpenPGP.js v4.4.10 Comment: https://openpgpjs.org xk8EXJ8SUxMFK4EEAAoCAwQRvMNE18QPFsBLIw0yWHfNZUVtOyPVXplzqhPy DslTKxjj3PE2s15vbVDNNOpmmzgIlRUEbyYFcDfhdCCXXscxzRtUaWFnbyA8 Z2l0aHViQHRsb3JpYXRvLmNvbT7CdwQQEwgAHwUCXJ8SUwYLCQcIAwIEFQgK AgMWAgECGQECGwMCHgEACgkQnCIDAkHMdM8czwEA2JOPG6zaNsa1D9M2a3U8 gl5R8S1F1lOELJtlVSH1tWAA/RRPREctK8pxkCKs5NuICnx9FIQ1WUsyulG+ 08xZY5CZzlMEXJ8SUxIFK4EEAAoCAwQ9aBjd/ntB+uqOUZgpzaNdtE6O2cQd l3DnZ3k2tEngIWKve+5d7woMd6idEdk2AkAvyhmqYMeiaCkI3LuNzjr3AwEI B8JhBBgTCAAJBQJcnxJTAhsMAAoJEJwiAwJBzHTP1IABAMNLXoEyrW7+5/ty bG8u5uIGP/0M+EDnELpDpUWMBa8dAP9MtynJ0qHbov6so3+9jA5Nug0s7Ywq 8NDcfsq8u367Tg== =67V7 -----END PGP PUBLIC KEY BLOCK-----`, `-----BEGIN PGP PUBLIC KEY BLOCK----- Version: OpenPGP.js v4.4.10 Comment: https://openpgpjs.org xk8EXJ8SKRMFK4EEAAoCAwTlZPr0xaQXWs3+2Q2at+OgE1VevOeyq0TXOMw2 BhFUTOF49sY3mJ9tA0jtHYeRG/BwXJlO7ycC6pc8a04LHeYWzRtUaWFnbyA8 Z2l0aHViQHRsb3JpYXRvLmNvbT7CdwQQEwgAHwUCXJ8SKQYLCQcIAwIEFQgK AgMWAgECGQECGwMCHgEACgkQ0ZFQ34vxIBgpCgD9Hbz/W+mQDLFAUulQ0pgO UBKuNdPraXpU419kIXZzl3QA/RTaX8cbQTHiuDy63tiwRZqZSiiuBiYZaZ0G S9p+k2UWzlMEXJ8SKRIFK4EEAAoCAwQu4w+JVtCeXxuaZIWg+r6FvjURLMqN gK9czwBYZlsWHZgXiiAI7aVgDtoPVcm1wYByMV7Ut6eUvzaDOdTwS/KrAwEI B8JhBBgTCAAJBQJcnxIpAhsMAAoJENGRUN+L8SAYsoYA/ilPvNJmlbpC187c LJXqKxdppUlb6DISS8gq4Moin/BdAQClCGwqtyovA7Et7MjkCxiW0P5xiaTn qENz9YJFSRBEAQ== =bUeW -----END PGP PUBLIC KEY BLOCK-----` ]; openpgp.key.readArmored(keys[0]) .then((firstKey) => { openpgp.key.readArmored(keys[1]) .then((secondKey) => { const options = { message: openpgp.message.fromText(`I just discovered the keys to Satoshi's Wallet! Meet @ Liberty Sq., 20:30pm`), // You can also call fromFile()! publicKeys: [firstKey.keys[0], secondKey.keys[0]] }; openpgp.encrypt(options).then(ciphertext => { encrypted = ciphertext.data; console.log(encrypted); }); }); });
現在我假設乙太坊開始發揮作用?您想在 Solidity 中與乙太坊持有者驗證 PGP 公鑰。你可以用兩個“鑰匙”來做,你可以讓他們簽名:
- PGP指紋(小得多,但你必須在某處獲得完整的密鑰)
- PGP 公鑰(更大,更廣泛)
首先,您需要您的參與者簽署上述密鑰之一:
const ethers = require('ethers'); // The message... const message = PGP_PUBLIC_KEY||PG_FINGERPRINT; // Sign the message (this could also come from eth_signMessage) const wallet = new ethers.Wallet(privateKey); const signature = wallet.signMessage(message) // Split the signature into its r, s and v (Solidity's format) const sig = ethers.utils.splitSignature(signature); // Call the contract with the message and signature const promise = contract.verifyMessage(message, sig.v, sig.r, sig.s); promise.then(function(signer) { // Check the computed signer matches the actual signer console.log(signer === wallet.address); });
現在你只需要創建你的 Solidity 合約來驗證某個消息:
contract Verifier { // Returns the address that signed a given string message function verifyString(string message, uint8 v, bytes32 r, bytes32 s) public pure returns (address signer) { // The message header; we will fill in the length next string memory header = "\x19Ethereum Signed Message:\n000000"; uint256 lengthOffset; uint256 length; assembly { // The first word of a string is its length length := mload(message) // The beginning of the base-10 message length in the prefix lengthOffset := add(header, 57) } // Maximum length we support require(length <= 999999); // The length of the message's length in base-10 uint256 lengthLength = 0; // The divisor to get the next left-most message length digit uint256 divisor = 100000; // Move one digit of the message length to the right at a time while (divisor != 0) { // The place value at the divisor uint256 digit = length / divisor; if (digit == 0) { // Skip leading zeros if (lengthLength == 0) { divisor /= 10; continue; } } // Found a non-zero digit or non-leading zero digit lengthLength++; // Remove this digit from the message length's current value length -= digit * divisor; // Shift our base-10 divisor over divisor /= 10; // Convert the digit to its ASCII representation (man ascii) digit += 0x30; // Move to the next character and write the digit lengthOffset++; assembly { mstore8(lengthOffset, digit) } } // The null string requires exactly 1 zero (unskip 1 leading 0) if (lengthLength == 0) { lengthLength = 1 + 0x19 + 1; } else { lengthLength += 1 + 0x19; } // Truncate the tailing zeros from the header assembly { mstore(header, lengthLength) } // Perform the elliptic curve recover operation bytes32 check = keccak256(header, message); return ecrecover(check, v, r, s); } }
在 RicMoo 的幫助下,Solidity 的部分
您應該創建一個用於加密的對稱密鑰並用它加密 IPFS 文件,然後用不同的公鑰加密這個對稱密鑰,以便每個接收者都可以解密對稱密鑰,進而解密 IPFS 文件。