quinnwencn / blog

Apache License 2.0
0 stars 0 forks source link

RSA-PSS签名算法 #23

Closed quinnwencn closed 3 months ago

quinnwencn commented 3 months ago

RSA签名

RSA签名算法的逻辑是,先对消息计算哈希值,再对哈希进行私钥加密(签名),生成数字签名,如图所示: image 但是RSA有一个致命问题:选择密文攻击。因此,可以考虑在签名前对哈希值进行填充,已达到防止选择密文攻击的目的。目前常见的填充算法有:RSA_PKCS1_PADDING_v1.5RSA_PKCS1_OAEP_PADDINGRSASSA-PKCS1-v1.5其实就是RSA_PKCS1_PADDING_v1.5填充算法。

RSASSA-PSS

采用RSASSA-PSS的签名算法,实际上就是在签名前使用RSA-PSS填充了哈希,然后再签名,过程如图所示: image 其中EM的生成逻辑如下: image

  1. 明文生成的Hash与Padding1、盐值salt拼接构成了M';
  2. 对M‘求哈希生成新的Hash
  3. Padding2和盐值salt拼接构成DB,
  4. 计算2生成的Hash的MGF值,作为DB的mask,dbMask = MGF(H, emLen- hLen - 1)
  5. 根据DB和dbMask计算maskedDB
  6. 将maskDB、H和bc拼接组成EM 最后再用RSA私钥对EM进行加密生成签名。根据上述签名逻辑,验签是一个逆过程,因此RSASSA-PSS算法的校验过程相比于RSAwithSHA256,会多一个解密过程,再校验,实际上的校验过程可以拆分为以下几个步骤:
  7. 公钥解密签名,计算出EM
  8. 检查EM的高位和地位bc
  9. 从EM中提取H
  10. 基于MGF计算H
  11. 通过逆向操作,从maskedDB、H计算DB
  12. 检查DB中的padding2是否是全0
  13. 从DB中提取salt
  14. 将M按照计算方法计算出mHash,并基于mHash,padding1和7中提取的salt计算M'
  15. M'计算出H,对比H与提取出来的H是否相等 在OpenSSL的API中,padding和盐值、H、EM等的计算隐藏了,但是在验证过程中仍然下需要解密出EM,然后再验证。

    C++签名和验签示例:

    
    //
    // Created by Robert on 24-3-18.
    //

ifndef RSASSA_PSS_H

define RSASSA_PSS_H

include

include

include

include <openssl/rsa.h>

include <openssl/pem.h>

include <openssl/err.h>

include <openssl/sha.h>

std::string SHA256_HASH(const std::string& input) { uint8_t hash[SHA256_DIGEST_LENGTH]; SHA256_CTX sha256; SHA256_Init(&sha256); SHA256_Update(&sha256, input.c_str(), input.length()); SHA256_Final(hash, &sha256); std::string result {""}; char buf[2]; for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { sprintf(buf, "%02x", hash[i]); result += buf; }

return result;

}

std::string Base64Encode(const std::string& input) { BIO* bio = BIO_new(BIO_s_mem()); if (bio == nullptr) { std::cerr << "Failed to create bio for base64 encoding" << std::endl; return ""; }

BIO* base64Bio = BIO_push(BIO_new(BIO_f_base64()), bio);
if (base64Bio == nullptr) {
    std::cerr << "BIO_push failed." << std::endl;
    BIO_free_all(bio);
    return "";
}

// 禁用 Base64 编码中的换行符
BIO_set_flags(base64Bio, BIO_FLAGS_BASE64_NO_NL);

// 将数据写入 BIO 进行编码
BIO_write(base64Bio, input.c_str(), static_cast<int>(input.length()));
BIO_flush(base64Bio);

// 获取编码后的数据
BUF_MEM* bioBuffer;
BIO_get_mem_ptr(base64Bio, &bioBuffer);

// 将编码数据拷贝到字符串中
std::string encodedData(bioBuffer->data, bioBuffer->length);

// 释放 BIO 和其他资源
BIO_free_all(base64Bio);

return encodedData;

}

std::string Base64Decode(const std::string& base64) { // 创建一个 BIO 对象,用于进行 Base64 解码 BIO* bio = BIO_new_mem_buf(base64.c_str(), static_cast(base64.length())); if (bio == nullptr) { std::cerr << "Failed to create BIO for base64 decoding" << std::endl; return ""; }

// 创建一个解码 BIO
BIO* base64Bio = BIO_new(BIO_f_base64());
if (base64Bio == nullptr) {
    std::cerr << "Failed to create BIO for base64 decoding" << std::endl;
    BIO_free_all(bio);
    return "";
}

// 禁用 Base64 解码中的换行符
BIO_set_flags(base64Bio, BIO_FLAGS_BASE64_NO_NL);

// 将解码 BIO 与输入 BIO 关联
BIO_push(base64Bio, bio);

// 计算解码后的数据长度
int length = BIO_pending(base64Bio);

// 创建一个缓冲区来存储解码后的数据
std::string decodedData(length, '\0');

// 进行 Base64 解码
length = BIO_read(base64Bio, &decodedData[0], length);

// 释放 BIO 和其他资源
BIO_free_all(base64Bio);

// 调整解码后的数据长度
decodedData.resize(length);

return decodedData;

}

bool RSAPSSVerify(const std::string& publicKey, const std::string& message, const std::string& signature) { BIO* publicKeyBio = BIO_new_mem_buf(publicKey.c_str(), publicKey.length()); if (publicKeyBio == nullptr) { std::cerr << "Failed to create bio from publickey string" << std::endl; return false; }

RSA* rsaPublicKey = PEM_read_bio_RSA_PUBKEY(publicKeyBio, nullptr, nullptr, nullptr);
if (rsaPublicKey == nullptr) {
    std::cerr << "Failed to load public key from bio" << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
    return false;
}
RSA_set_method(rsaPublicKey, RSA_PKCS1_OpenSSL());

auto size = RSA_size(rsaPublicKey);
std::vector<uint8_t> decrypt (size, 0);

int status = RSA_public_decrypt(signature.size(),
                                reinterpret_cast<const unsigned char*>(signature.c_str()),
                                decrypt.data(),
                                rsaPublicKey,
                                RSA_NO_PADDING);
if (status == -1) {
    std::cerr << "Decript failed." << std::endl;
    return false;
}

std::string digest = SHA256_HASH(message);
status = RSA_verify_PKCS1_PSS(rsaPublicKey,
                              reinterpret_cast<const unsigned char* >(digest.c_str()),
                              EVP_sha256(),
                              decrypt.data(),
                              -2);
return status == 1;

}

bool RSAPSSSign(const std::string& privateKey, const std::string& message, std::string& signature) { BIO privateKeyBio = BIO_new_mem_buf(privateKey.c_str(), privateKey.length()); RSA rsaPrivateKey = PEM_read_bio_RSAPrivateKey(privateKeyBio, nullptr, nullptr, nullptr);

auto size = RSA_size(rsaPrivateKey);
std::vector<uint8_t> EM (size, 0);
std::string hash = SHA256_HASH(message);
int status = RSA_padding_add_PKCS1_PSS(rsaPrivateKey,
                                        EM.data(),
                                        reinterpret_cast<const unsigned char *>(hash.c_str()),
                                        EVP_sha256(),
                                        -1 /* maximum salt length*/);
if (status == -1) {
    std::cerr << "Add padding failed." << std::endl;
    RSA_free(rsaPrivateKey);
    BIO_free(privateKeyBio);
    return false;
}

signature.resize(size);
status = RSA_private_encrypt(RSA_size(rsaPrivateKey),
                            EM.data(),
                            reinterpret_cast<unsigned char*>(signature.data()),
                            rsaPrivateKey,
                            RSA_NO_PADDING);
if (status == -1) {
    std::cerr << "RSA_private_encrypt failed with error " << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
    RSA_free(rsaPrivateKey);
    BIO_free(privateKeyBio);
    return false;
}

RSA_free(rsaPrivateKey);
BIO_free(privateKeyBio);
return true;

}

bool GenerateRSAKeyPairs(uint16_t size, std::string& publicKey, std::string& privateKey) { RSA rsa = RSA_new(); BIGNUM e = BN_new(); if (!BN_set_word(e, RSA_F4)) { BN_free(e); RSA_free(rsa); return false; }

if (RSA_generate_key_ex(rsa, size, e, nullptr) != 1) {
    BN_free(e);
    RSA_free(rsa);
    return false;
}
BN_free(e);
BIO* bio = BIO_new(BIO_s_mem());
if (!PEM_write_bio_RSAPrivateKey(bio, rsa, nullptr, nullptr, 0, nullptr, nullptr)) {
    BIO_free(bio);
    RSA_free(rsa);
    return false;
}

char* pri_data {nullptr};
auto pri_size = BIO_get_mem_data(bio, &pri_data);
if (pri_size < 0) {
    BIO_free(bio);
    RSA_free(rsa);
    return false;
}

privateKey.assign(pri_data, pri_size);
BIO_free(bio);
bio = BIO_new(BIO_s_mem());
if (!PEM_write_bio_RSA_PUBKEY(bio, rsa)) {
    BIO_free(bio);
    RSA_free(rsa);
    return false;
}

char* pub_data {nullptr};
auto pub_size = BIO_get_mem_data(bio, &pub_data);
publicKey.assign(pub_data, pub_size);

BIO_free(bio);
RSA_free(rsa);
return true;

}

endif //RSASSA_PSS_H