RSA签名和验签说明

最近遇到一个项目,需要进行RSA的验签,RSA的密钥为2048位即256个字节长。

上游是先对一个文件进行SHA256做hash,得到32字节的摘要,然后进行填充,填充规则为前面两个字节为0x00,0x01,然后填充全FF,接下来是32字节的摘要数据,这样填充之后的总数据长度为256字节,然后使用RSA的私钥对这256字节进行签名,下发下来,下游需要对这个签名进行处理,得到原始摘要,然后跟自己计算的摘要比对,验证通过则合法。

涉及的知识点如下:

一、RSA签名算法的填充方式,常用的有如下三种

1、RSA/ECB/PKCS1Padding 填充模式,最常用的模式

要求:
输入:必须 比 RSA 密钥长度 短至少11个字节, 否则会报错,数据不足时由接口自动进行随机填充
如果输入的明文过长,必须切割, 然后填充

输出:和RSA密钥长度一样长

根据这个要求,对于2048bit的密钥, block length = 2048/8 – 11 = 245字节

注意:加密的时候会在你的明文中随机填充一些数据,所以会导致对同样的明文每次加密后的结果都不一样。

对加密后的密文,服务器使用相同的填充方式都能解密。解密后的明文也就是之前加密的明文。

如果是私钥签名,那么填充的前面两个字节为0x00和0x01(表示私钥签名),如果是公钥签名,那么填充的签名的两个字节为0x00和0x02(表示公钥签名),然后填充全FF,在填充最后的有效数据之前,会填充一个00,然后后面就全部是有效数据,所以使用RSA签名的数据再解密之后得到的数据在真是数据之前一定有00,否则就是输入没有严格遵守RSA/ECB/PKCS1Padding填充方式

2.RSA_PKCS1_OAEP_PADDING
输入:必须 比 RSA 密钥长度 短至少41个字节, 否则会报错,数据不足时由接口自动进行随机填充

输出:和RSA密钥长度一样长

3.RSA/ECB/NoPadding  不填充

输入:可以和RSA钥模长一样长,如果输入的明文过长,必须切割,然后填充,这种方式需要自己手动进行填充好输入数据

输出:和RSA密钥长度一样长

跟DES,AES一样, RSA也是一个块加密算法( block cipher algorithm),总是在一个固定长度的块上进行操作。

但跟AES等不同的是, block length是跟key length有关的。

每次RSA加密的明文的长度是受RSA填充模式限制的,但是RSA每次加密的块长度就是密钥长度

 

具体代码片段如下:

/**
     * RSA签名
     * 
     * @param key RSA的密钥 公钥用X.509编码;私钥用PKCS#8编码
     * @param data 输入数据
     * @param mode 0-加密,1-解密
     * @param type 0-私钥加密,公钥解密 1-公钥加密,私钥解密
     * @return 签名后的数据 为null表示操作失败
     */
    public static String generateRSA(String key, String data, int mode, int type) {
        try {
            // 判断加密还是解密
            int opmode = (mode == 0) ? Cipher.ENCRYPT_MODE
                    : Cipher.DECRYPT_MODE;

            KeyFactory keyFactory = KeyFactory.getInstance("RSA");

            Key strkey = null;
            if (mode != type) { // 生成公钥
                KeySpec keySpec = new X509EncodedKeySpec(
                        Base64.decodeBase64(key)); // X.509编码
                strkey = keyFactory.generatePublic(keySpec);
            } else { // 生成私钥
                KeySpec keySpec = new PKCS8EncodedKeySpec(
                        Base64.decodeBase64(key)); // PKCS#8编码
                strkey = keyFactory.generatePrivate(keySpec);
            }

            // 获得一个RSA的Cipher类,使用私钥加密
            // RSA/ECB/NoPadding表示无填充,待加密数据长度必须跟密钥长度一致,填充需要自己完成
            // RSA/ECB/PKCS1Padding表示PKCS#1填充,前面填充0x00,0x01(私钥加密,公钥加密是02),然后填充0xFF,在数据之前再填充一个0x00,接下来是真正传入的数据,待加密数据需要小于密钥数据-11,这个是自动填充
            Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding"); // ,
            // 初始化
            cipher.init(opmode, strkey);
            byte[] byteData = str2bytes(data);
            System.out.println("generateRSA doFinal字节长度为: " + byteData.length);
            // 返回加解密结果
            return (bytesToHexString(cipher.doFinal(byteData)))
                    .toUpperCase(Locale.getDefault());// 开始计算
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
        return null;
    }

 

 

 

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
RSA数字签名的大致流程如下: 1. 首先需要生成RSA密钥对,包括公钥和私钥。 2. 对要签名的数据进行哈希处理。 3. 用私钥对哈希值进行加密,得到签名值。 4. 将签名值和原始数据一起发送给接收方。 5. 接收方用公钥对签名值进行解密,得到哈希值。 6. 对接收到的原始数据进行哈希处理,得到接收方计算出的哈希值。 7. 比较接收方计算出的哈希值和接收到的哈希值是否一致,如果一致,则表示签名证成功。 下面是一个简单的RSA数字签名的C++代码示例: ```c++ #include <iostream> #include <openssl/rsa.h> #include <openssl/pem.h> #include <openssl/err.h> #include <openssl/md5.h> using namespace std; // 生成RSA密钥对 RSA* generateRSAKey() { RSA* rsa = RSA_new(); BIGNUM* bne = BN_new(); BN_set_word(bne, RSA_F4); RSA_generate_key_ex(rsa, 2048, bne, NULL); return rsa; } // RSA数字签名 bool rsaSign(const char* data, size_t dataLen, RSA* rsa, unsigned char* sig, unsigned int* sigLen) { unsigned char md[MD5_DIGEST_LENGTH]; MD5((unsigned char*)data, dataLen, md); int ret = RSA_sign(NID_md5, md, MD5_DIGEST_LENGTH, sig, sigLen, rsa); return (ret == 1); } // RSA bool rsaVerify(const char* data, size_t dataLen, RSA* rsa, unsigned char* sig, unsigned int sigLen) { unsigned char md[MD5_DIGEST_LENGTH]; MD5((unsigned char*)data, dataLen, md); int ret = RSA_verify(NID_md5, md, MD5_DIGEST_LENGTH, sig, sigLen, rsa); return (ret == 1); } int main() { // 生成RSA密钥对 RSA* rsa = generateRSAKey(); // 要签名的数据 char data[] = "Hello World!"; // RSA数字签名 unsigned char sig[1024]; unsigned int sigLen = 0; rsaSign(data, sizeof(data), rsa, sig, &sigLen); // RSA bool result = rsaVerify(data, sizeof(data), rsa, sig, sigLen); if (result) { cout << "RSA verify success!" << endl; } else { cout << "RSA verify failed!" << endl; } RSA_free(rsa); return 0; } ``` 需要注意的是,本示例使用了OpenSSL库来实现RSA签名,使用前需要先安装OpenSSL库并链接到项目中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晒干的老咸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值