服务如何生成和使用公共和秘密 API 密钥?

Posted

技术标签:

【中文标题】服务如何生成和使用公共和秘密 API 密钥?【英文标题】:How services generate and use public and secret API keys? 【发布时间】:2019-07-27 07:34:33 【问题描述】:

Google、Stripe 和许多其他公司都有公共 API 密钥和秘密 API 密钥。

生成随机字符串很容易,但我的问题是,我怎样才能生成公钥和私钥,存储它们并正确使用它们?

公共 API 密钥用于告诉用户是谁,秘密用于确认他们的身份。

我的流程如下: - 用户创建一个帐户 - 用户激活服务(内部) - 服务返回一个公开和秘密的 API 密钥 (UARRHAtPtJcLxx5RmmWo9oTrca4gRt2k, C9YS7Mhzichq2vqBuRkNJxkNci5W2Xua) - 用户在他/她的网站上使用公钥,在服务器端使用私钥

我正在使用 nodejs,当用户请求 API 密钥时,公钥是按需生成的:

let public = await crypto.randomBytes(32).toString('base64');

将秘密存储在数据库中就像以明文形式存储密码一样。 我想我们不想要这个,它需要以某种方式散列。例如,我是否生成一个“私有”密钥并使用 argon2 对其进行哈希处理?用户将永远无法再次看到他/她的密钥,需要立即保存,这是一种好的做法吗?

我找不到太多关于这应该如何工作的信息。

【问题讨论】:

您找到解决方案了吗? 不,我没有为此继续研究。如果你有什么想法,请分享。 密钥管理是一个广泛的话题。存储密钥是管理密钥的整体框架中的一个决策点。查看cheatsheetseries.owasp.org/cheatsheets/… 和其中引用的 NIST 文档。 【参考方案1】:

从技术上讲,您所指的只是用户名和密码。唯一重要的区别是这些通常由 API 生成并且非常随机,而不是由用户选择的真实用户名和密码,通常不是非常随机。 (调用这些公钥和私钥有点误导,因为公钥加密是不同的——对于 API 密钥,您通常不需要它,管理 PKI 是一堆蠕虫,而且正确地执行它也非常昂贵。)

由于这些在技术上与用户名和密码相同,因此您希望以类似方式对待它们。我们称这些客户端 ID(“公共”部分)和客户端密钥(“秘密”部分)。

一些想法:

您应该使用加密安全的随机生成器来生成随机字符串。 crypto.randomBytes() 如上就可以了。 您应该考虑熵来为密钥设置适当的长度。熵基本上是密钥的“随机性”,以位为单位。举个例子,如果密钥空间是 1024 个具有相同概率的不同可能密钥,那么您可以说密钥具有 log2(1024) = 10 位熵。熵将密钥的长度与随机源的安全性分离,例如,您可以拥有仍然不安全的非常长的密钥,因为随机源存在缺陷。您希望密钥的熵多少取决于用例,例如是否在任何情况下都可能进行离线攻击或仅在线请求等等(您可能也应该计算离线攻击,这非常快)。根据经验,熵不应低于 128 位,为了更高的安全性,您可能应该达到 256+ 位。如果密钥区分大小写和字母数字,则有 62 个不同的可能字符,长度为 22 的密钥提供约 131 位 (log2(62^22) =~ 130.99)。当然,您总是可以更长,对于 256 位,您需要长度为 43 且区分大小写的字母数字。 正如您正确指出的,存储此类密钥至关重要。至少,您希望将它们存储为散列(使用适当的散列,见下文),就像任何其他密码一样,这样即使攻击者可以访问您的数据库,他们也不会看到您的 api 密钥.任何适当的密钥派生函数(Argon2、bcrypt、PBKDF2 等)都适合此目的,但普通的加密哈希函数(sha1、sha2 等)不是。 你是对的,如果你对这些秘密进行散列,用户将只能在它们生成时才能看到它们,并且永远不会再看到它们。这正是安全在线服务中发生的情况,通常是一种很好的做法。您可以警告您的用户注意该秘密,因为您将无法再次显示它们。 (如果他们忘记了,他们也可以理想地生成一个新的,所以这通常没什么大不了的。) 最好将这些密钥存储在其他地方。考虑到云,存储此类机密的好地方是您的云提供商提供的服务,例如 AWS 中的 Secrets Manager。好处包括使用 KMS 密钥加密、审计,并且您可以确保访问您的密钥的唯一方法是通过适当的 IAM 角色(例如,您无需担心泄露的备份之类的事情)。 虽然客户端 ID 不是机密,但您需要在存储中保护其完整性(以及与客户端机密关联的完整性)。想象一个场景,攻击者可以以某种方式更改您的数据库并将不同的(攻击者已知的)客户端密码分配给现有的客户端 ID。这将意味着与该客户端 ID 关联的数据的完全妥协。因此,您要确保除了保持客户端机密安全之外,攻击者也无法更改这些信息(例如通过对相关组件的访问控制)。

【讨论】:

为什么对 128 位以上的随机性使用“硬”散列函数?暴力破解密码很容易,因为即使是“好的”密码也只有 40 位左右,但这与 128 位相差甚远。如果你能在 1 纳秒内破解 40 位密码,那么破解 128 位密码需要 100 亿年。位随机生成的密钥。 你说的其实是对的,我没有想过这个,谢谢!使用像 sha2 这样的普通散列函数不会显着增加风险,但我认为它会增加一点,这取决于我们接受与否的先决条件。我明天会编辑我的答案(不在电话上时)。【参考方案2】:

我认为我们可以使用下面的代码生成一对公钥和私钥(私钥)..您可以参考链接link to generate key pair here doc

var pk="";
var sk="";
var string= payload;
const  generateKeyPair  = require('crypto');
generateKeyPair('rsa', 
      modulusLength: 4096,
      publicKeyEncoding: 
        type: 'spki',
        format: 'pem'
      ,
      privateKeyEncoding: 
        type: 'pkcs8',
        format: 'pem',
        cipher: 'aes-256-cbc',
        passphrase: 'top secret'
      
    , (err, publicKey, privateKey) => 
      try 
          pk=publicKey;
          sk=privateKey;

       catch (error) 
          console.log(err)

      
    );

现在我们有了密钥和公钥....所以我们可以实现 HMAC 身份验证...参考..go for hmac authentication doc


var hmac = crypto.createHmac('sha384', sk).update(string).digest('hex');

request.post(uri:..., json:  hmac, pk, string , function(err, response, body) 
   console.log(body);
);


【讨论】:

写得很好,但不能回答这个问题:“如何处理私钥以及如何存储它们”。 @GeckoIT 在请求中明确公钥、hmac、有效负载已在上面传递。我们还将在我们的数据库中存储公钥和私钥。因此,当请求(公钥,hmac.payload)到来时,我们将不得不使用请求中存在的公钥从数据库中找到密钥。现在使用该密钥,我们将不得不再次计算 hmac 以及是否匹配完成验证。我想它会帮助你。 我不认为解释我们将如何存储到我们的数据库中是很好的......可能已经有问题了。我们可以轻松地存储这两个密钥。此外,如果我们愿意,我们可以以加密形式存储我们的私钥,但我认为在 hmac 身份验证中不需要这样做。

以上是关于服务如何生成和使用公共和秘密 API 密钥?的主要内容,如果未能解决你的问题,请参考以下文章

使用 WSO2 作为移动应用程序的 API 网关时,如何安全地处理用户密钥和秘密

在数据库中存储 API 密钥和秘密

API 密钥和秘密密钥如何工作?如果我必须将我的 API 和密钥传递给另一个应用程序,它会安全吗?

带有使用者密钥和秘密的简单 python oAuth 1.0 示例

带有使用者密钥和秘密的简单 python oAuth 1.0 示例

如何导出私有/秘密 ASC 密钥以解密 GPG 文件