如何使用Azure Key Vault管理以太坊钱包
对于每个以太坊钱包所有者来说,最重要的信息是私钥。因为丢失私钥将导致所有者无法访问与该钱包关联的令牌,而无法进行任何恢复。
在某些基于区块链的场景中,需要一个集中且安全的钱包管理解决方案,通常允许用户在系统边界内使用专用的钱包,而不会冒其私人钱包的风险。
Azure密钥保管库及其硬件安全模块(HSM)产品甚至更是如此,它允许管理和使用加密密钥对,而私钥永远不会离开保管库,除非它用于备份和还原操作,这意味着丢失的风险或减少私钥的暴露。
基础架构设置
在这里,我们将使用Terraform部署我们简单的以太坊钱包管理器所需的所有资源,其中包括:
功能应用程序所需的存储帐户。
用于存储钱包密钥的密钥库。
最后是一个功能应用程序,它将充当保管库的网关。
为简便起见,我将仅遍历Terraform脚本中专门处理库的创建,功能应用程序和允许应用程序访问库的策略的区域。
我们将首先添加azurerm_key_vault带有premiumSKU的资源,因为HSM仅在该层可用:
resource "azurerm_key_vault" "this" { name = "kv-$" location = azurerm_resource_group.this.location resource_group_name = azurerm_resource_group.this.name tenant_id = data.azurerm_client_config.current.tenant_id soft_delete_enabled = true sku_name = "premium"}
下一个是功能应用程序,其中我们专门启用了托管身份(使用identity块),因为在分配允许该应用程序访问保管库的策略时需要身份标识:
resource "azurerm_function_app" "this" { name = "func-$" location = azurerm_resource_group.this.location resource_group_name = azurerm_resource_group.this.name app_service_plan_id = azurerm_app_service_plan.this.id storage_account_name = azurerm_storage_account.this.name storage_account_access_key = azurerm_storage_account.this.primary_access_key version = "~3" app_settings = { "KEY_VAULT_URL" = azurerm_key_vault.this.vault_uri } identity { type = "SystemAssigned" }}
最后,我们添加将azurerm_key_vault_access_policy功能应用程序与密钥保险库相关联的块,并定义它将拥有的权限。请注意,在这个例子中,我们将使用密钥保管库(的密钥机制key_permissions块),并且只允许功能应用创建,得到大众和标志操作(create,get,sign):
resource "azurerm_key_vault_access_policy" "this" { key_vault_id = azurerm_key_vault.this.id tenant_id = azurerm_key_vault.this.tenant_id object_id = azurerm_function_app.this.identity[0].principal_id key_permissions = [ "create", "sign", "get" ]}
功能应用
现在我们已经完成了基础架构的工作,我们可以继续编写功能应用程序,该应用程序将与钱包相关的操作公开给外界,另一方面,访问受保护的密钥库。
在此,我们将使用C#并创建一个新的空Azure Functions项目,我们可以使用Visual Studio的项目模板或Azure Functions Core Tools。无论哪种方式,一旦我们有了空项目,我们都将添加基于HTTP触发器的函数。第一个CreateWallet功能是访问密钥库并生成secp256k1公钥和私钥对的函数,并从创建的公钥中生成以太坊地址:
public static class CreateWallet { [FunctionName("CreateWallet")] public static async TaskRun( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "wallets")] HttpRequest req, ILogger log) { string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); var model = JsonConvert.DeserializeObject(requestBody); string KeyVaultUrl = Environment.GetEnvironmentVariable("KEY_VAULT_URL"); var keyClient = new KeyClient(new Uri(KeyVaultUrl), new DefaultAzureCredential()); var response = await keyClient.CreateEcKeyAsync(new CreateEcKeyOptions(model.Name, true) { CurveName = KeyCurveName.P256K }); var sha3 = new Sha3Keccack(); var addressUtil = new AddressUtil(); byte[] publicKey = response.Value.Key.ToECDsa().ExportSubjectPublicKeyInfo(); byte[] hash = sha3.CalculateHash(publicKey); byte[] addressBuffer = new byte[hash.Length - 12]; Array.Copy(hash, 12, addressBuffer, 0, hash.Length - 12); string address = addressUtil.ConvertToChecksumAddress(addressBuffer.ToHex()); log.LogInformation($"Generate address: "); return new OkObjectResult(new CreateWalletResultModel { Address = address }); } } public class CreateWalletModel { public string Name { get; set; } } public class CreateWalletResultModel { public string Address { get; set; } }
第二个Sign函数接受哈希有效载荷的base64编码的字符串(需要为256位哈希)和用于签名的钱包名称,并返回生成的签名的base64编码的字符串:
public static class Sign { [FunctionName("Sign")] public static async TaskRun( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "sign")] HttpRequest req, ILogger log) { string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); var model = JsonConvert.DeserializeObject(requestBody); string KeyVaultUrl = Environment.GetEnvironmentVariable("KEY_VAULT_URL"); var keyClient = new KeyClient(new Uri(KeyVaultUrl), new DefaultAzureCredential()); var response = await keyClient.GetKeyAsync(model.WalletName); var cryptoClient = new CryptographyClient(response.Value.Id, new DefaultAzureCredential()); var signature = await cryptoClient.SignAsync(SignatureAlgorithm.ES256K, Convert.FromBase64String(model.Payload)); return new OkObjectResult(new SignResultModel { Signature = Convert.ToBase64String(signature.Signature) }); } } public class SignModel { public string WalletName { get; set; } public string Payload { get; set; } } public class SignResultModel { public string Signature { get; set; } }
请注意,这两个函数都使用名为的环境变量KEY_VAULT_URL,该变量在azurerm_function_app块内的Terraform脚本中设置为密钥保险库的URI,从而无需在函数应用程序中进行任何手动配置。
测试解决方案
要测试解决方案,我们可以使用Azure门户,方法是导航到要测试的功能并使用所需的参数执行该功能。为了生成可以发送给Sign函数的base64编码的哈希,我们可以使用:
https://emn178.github.io/online-tools/sha3_256.html要生成hex我们想要签名的任何值的字符串。
https://base64.guru/converter/encode/hex将生成的hex字符串转换为base64编码的字符串。
结论
本文介绍了基于以太坊的网络上如何使用的Azure Key Vault创建密钥对的基础知识。在生产级环境中,用于管理钱包的解决方案将更具限制性和弹性,例如通过向功能添加身份验证和授权或公开用于备份和还原密钥的操作,这将为钱包更好的保护私钥,并在丢失时允许恢复此类密钥。
微信掃描關注公眾號,及時掌握新動向
2.本文版權歸屬原作所有,僅代表作者本人觀點,不代表比特範的觀點或立場
2.本文版權歸屬原作所有,僅代表作者本人觀點,不代表比特範的觀點或立場