Java 实现 API 签名算法以及 EdDSA 的 Ed25519 签名算法的使用
API 签名
精简版
签名: 指在数字通信或数据交换过程中,用来保证消息完整性和来源可信度的一种特定数据结构。这个“签名”不是传统意义上的手写签名或物理印章,而是一个经过特定数学运算产生的、代表发送者对消息认可的电子印记。
API 签名算法:就是针对 请求数据(请求参数等等) 进行签名。
API签名算法如何保证确保API请求的完整性和来源的可信性呢?
- API 签名算法生成一对密钥(公钥和私钥),私钥签名后的数据通过公钥验签,配对的密钥验签的结果一定为true,除非签名的信息被篡改;
- 其中私钥用来对请求数据进行签名,使用公钥进行验签;
- 公钥可以被传递,但是私钥不能被传递;
概念版
API 签名算法是对请求数据进行签名。具体来说,签名过程是为了确保API请求的完整性和来源的可信性,防止数据在传输过程中被篡改,同时验证请求发起者拥有合法的权限。以下是签名算法对API请求数据进行签名的详细说明:
- 请求参数:
签名算法通常针对 API 请求中包含的所有关键参数进行签名。这些参数可能包括但不限于:访问令牌(Access Token)、请求方法(GET、POST等)、请求路径(URL)、查询参数、请求体(JSON、XML等格式的数据)、时间戳、nonce(一次性随机值,用于防止重放攻击)等。
参数通常按照一定的规则(如字母序、参数重要性)进行排序,确保双方(客户端和服务端)对签名数据的处理方式一致。 - 签名密钥:
签名过程需要用到一个或多个密钥。这些密钥可能是对称密钥(如HMAC-SHA256签名中使用的密钥)或非对称密钥对(如RSA、ECDSA签名中使用的私钥和公钥)。密钥通常由服务提供商分配给API使用者,或者由使用者根据服务提供商的规范自行生成,并在安全通道上传递给服务提供商。 - 签名生成:
客户端(API使用者)将排序后的请求参数拼接成一个字符串或序列化为二进制数据,然后使用指定的签名算法(如HMAC、RSA、ECDSA等)和对应的密钥对这个数据进行签名运算,生成一个固定长度的签名值(通常为一串十六进制或Base64编码的字符串)。 - 签名传递:
客户端将生成的签名值附加到API请求中,通常作为请求头的一个字段(如Authorization、X-Signature、Signature等)发送给服务端。同时,原始请求参数也随请求一同发送。 - 签名验证:
服务端收到请求后,首先提取请求头中的签名值和请求中的所有相关参数。接着,按照与客户端相同的规则重新计算这些参数的签名。如果重新计算得到的签名与接收到的签名值匹配,说明请求数据在传输过程中未被篡改,且请求来自持有正确密钥的合法客户端。
综上所述,API签名算法是对API请求数据(包括请求参数)进行签名,目的是确保请求的完整性和来源的可信性。签名过程涉及到请求参数的规范化、密钥的使用、签名值的生成与传递以及服务端的签名验证。通过签名,服务端可以有效地鉴别请求的真伪,保障API接口的安全性。
Java 使用 Ed25519 算法进行签名和验签
1.引入依赖
使用EdDSA(Edwards-curve Digital Signature Algorithm)进行签名操作,需要借助于BouncyCastle库,因为它提供了对 EdDSA 算法的支持。添加了BouncyCastle作为JCE(Java Cryptography Extension)的提供者。
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>最新版本号</version>
</dependency>
2.编写 EdDSA 签名、Ed25519
步骤:
生成 公钥 和 私钥 并保存在数据库中
- 添加 BouncyCastle 作为 JCE 提供者;
- 生成密钥对;
- 获取私钥和公钥;
- 将 公钥 和 私钥 转换为 byte 数组,经过 Base64 编码之后转换为字符串,方便存储在数据库中
签名和验签:
- 从数据库中取出公钥和私钥;
- 使用私钥对请求数据进行签名,之后转发给其他请求;
- 在另外的服务器中使用公钥验签。
package com.mahua.studytest.encryption;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator;
import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters;
import org.bouncycastle.crypto.para