Solidity

如何加密具有多個公共地址的文件?

  • March 30, 2019

我可以使用多個公共地址加密文件,以便每個人都可以使用他們的私鑰解密文件嗎?從而實現了一個將文件安全地儲存在 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);
});

我們將假設您生成了兩個不同的密鑰key1key2使用該方法,現在您想使用key1.publicKeyArmoredand加密一些數據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 文件。

引用自:https://ethereum.stackexchange.com/questions/69058