在 iOS 上使用 Crypto++ 验证消息时出错

Error verifying message using Crypto++ on iOS

问题

我正在尝试使用签名和 public 密钥验证给定消息。它使用 iOS 提供的安全框架工作正常,但我无法使用 Crypto++ 库(必须使用)使其工作。

我使用 CryptoPP 库按照相同的步骤验证了所有内容 10 次,以不同的方式重写了一些部分,但它仍然抛出相同的异常:

"PK_Signer: key too short for this signature scheme"

上下文

使用的数据

验证所遵循的步骤

  1. 证书

    1. Base64解码证书
    2. 从证书中提取 public 密钥
  2. 签名(JWB 的第三部分)

    1. 用一些“=”将签名填充为4的倍数
    2. URLBase64 解码
  3. 消息验证

    1. 消息 = (JSW Header) + "." +(JWT 负载)。这已经在代码中完成,消息是名为“headerAndPayload.
    2. 的参数
  4. 使用 PKCS1、RSA 验证 SHA256 字节

    1. 消息的 SHA256 摘要
    2. 验证使用:
      1. Public键
      2. 消息的 SHA256 摘要
      3. 签名

iOS 工作代码

(只有重要的部分,因为验证在 iOS 上工作正常)

使用 CryptoPP 库的代码(使用同一组数据)

我 copy/paste 整个代码带有与描述相对应的数字和一些附加注释,例如返回的结构大小。

+(bool)verifyBase64EncodedCertificate:(NSString *)certificateString
         base64URLEncodedJWTSignature:(NSString *)urlEncodedSignature
                              message:(NSString *)headerAndPayload
{
    // 1. Certificate
    // 1.1 Decode the certificate
    std::string base64EncodedCertificate = certificateString.UTF8String;
    std::string decodedCertificate;
    CryptoPP::StringSource ss(base64EncodedCertificate,
                              true,
                              new CryptoPP::Base64Decoder(new CryptoPP::StringSink(decodedCertificate))
                              );

    // 1.2 Extract Public Key from certificate
    CryptoPP::ByteQueue certificateByteQueue, publicKeyByteQueue;
    certificateByteQueue.Put((byte *)&decodedCertificate[0], decodedCertificate.size());
    certificateByteQueue.MessageEnd();
    try
    {
        GetPublicKeyFromCert(certificateByteQueue, publicKeyByteQueue);
        // This method comes from CryptoPP docs so I assume it works... certificate gets checked again later on.
    }
    catch(std::exception &)
    {
        std::cerr << "Failed to extract the public key from the CA certificate." << std::endl;
        return nil;
    }
    //publicKeyByteQueue.CurrentSize() = 294


    // 2. Decode Signature
    std::string base64URLEncodedSignature = urlEncodedSignature.UTF8String;
    unsigned long paddingForURLEncodedSignature = 4 - (base64URLEncodedSignature.length() % 4);
    base64URLEncodedSignature.insert(base64URLEncodedSignature.begin(), paddingForURLEncodedSignature, '=');
    std::string decodedSignature;
    CryptoPP::StringSource ss1(base64URLEncodedSignature,
                               true,
                               new CryptoPP::Base64URLDecoder(new CryptoPP::StringSink(decodedSignature))
                               );
    const byte *decodedSignaturePointer = (byte *)&decodedSignature[0];
    size_t decodedSignatureSize = decodedSignature.size();

    // Certificate Signature as Byte Block
    CryptoPP::SecByteBlock certSignature;
    certSignature.Assign(decodedSignaturePointer, decodedSignatureSize);

    // decodedSignatureSize = 256
    // certSignature.size() = 256


    // 3. Message to verify (available already concatenated)
    std::string message = headerAndPayload.UTF8String;
    const byte *messagePointer = (const byte *)message.c_str();
    const size_t messageLength = message.length();        
    // MessageLength = 693

    // 4.1 hash message using SHA256
    byte digest [CryptoPP::SHA256::DIGESTSIZE];
    CryptoPP::SHA256().CalculateDigest(digest, messagePointer, messageLength);

    // 4.2  Create Verifier assigned public key and test
    CryptoPP::AutoSeededRandomPool prng;
    CryptoPP::RSASS<CryptoPP::PKCS1v15, CryptoPP::SHA256>::Verifier verifier;
    verifier.AccessKey().Load(publicKeyByteQueue);
    if (!verifier.AccessKey().Validate(prng, 3))
    {
        throw CryptoPP::Exception(CryptoPP::Exception::OTHER_ERROR, "Failed to validate public key");
    }        

    // verifier.SignatureLength() = 256 = certSignature.size()
    if(certSignature.size() != verifier.SignatureLength())
    {
        std::cerr << "The signature size is does not match the algorithm used for signing." << std::endl;
        return 0;
    }

    // 4. Actual Verification (1st way of doing it)
    CryptoPP::SignatureVerificationFilter vf(verifier);
    try
    {
        vf.Put(digest, CryptoPP::SHA256::DIGESTSIZE);
        vf.Put(certSignature, certSignature.size());
        vf.MessageEnd(); // Throws exception here PK_Signer: key too short for this signature scheme
    }
    catch(std::exception &e)
    {
        std::cerr << "Caught an exception while verifying the signature:" << std::endl;
        std::cerr << "\t" << e.what() << std::endl;
        return 0;
    }
    if(vf.GetLastResult())
    {
        std::cout << "The signature verified." << std::endl;
    }
    else
    {
        std::cout << "Signature verification failed." << std::endl;
    }
    return 1;


    // 4. Actual Verification (2d way of doing it)
    bool verified = verifier.VerifyMessage(digest,                  CryptoPP::SHA256::DIGESTSIZE,
                                           decodedSignaturePointer, decodedSignatureSize);
    // Also throw same exception PK_Signer: key too short for this signature scheme

    return verified;

我能看到纯 iOS 代码和 CryptoPP 代码之间的唯一区别是在验证过程中,iOS 方法需要一个额外的参数 kSecPaddingPKCS1SHA256

SecKeyRawVerify(publicKey,
                kSecPaddingPKCS1SHA256,
                ...)

但除此之外,我觉得我已经使用 CryptoPP 库复制了完全相同的概念。

非常感谢任何帮助,谢谢。

vf.Put(digest, CryptoPP::SHA256::DIGESTSIZE);

签名是而不是哈希大小。签名是模数的大小(或更准确地说,[0,n-1])。协议成帧后,签名可能大于模数大小。另请参阅密码学堆栈交换中的 What is the length of an RSA signature?

至于使用 "Raw Sign" 或 "Raw Encrypt" 创建等效的 iOS 示例,请参阅 Crypto++ wiki 上的 Raw RSA。做模幂运算等低级操作通常不是一个好主意。您应该尝试留在协议和密码系统中,例如 RSASSA_PKCS1v15_SHA_SignerRSASSA_PKCS1v15_SHA_Verifier.

同时检查 RSASS class,它是 RSA 签名方案。我猜你可能想要 RSASS<PKCS1v15, SHA256>::SignerRSASS<PKCS1v15, SHA256>::Verifier:

$ grep -IR Signer * | grep typedef
luc.h:typedef LUCSS<PKCS1v15, SHA>::Signer LUCSSA_PKCS1v15_SHA_Signer;
pubkey.h:   typedef PK_FinalTemplate<TF_SignerImpl<SchemeOptions> > Signer;
pubkey.h:   typedef PK_FinalTemplate<DL_SignerImpl<SchemeOptions> > Signer;
rsa.h:typedef RSASS<PKCS1v15, SHA>::Signer RSASSA_PKCS1v15_SHA_Signer;
rsa.h:typedef RSASS<PKCS1v15, Weak1::MD2>::Signer RSASSA_PKCS1v15_MD2_Signer;
rsa.h:typedef RSASS<PKCS1v15, Weak1::MD5>::Signer RSASSA_PKCS1v15_MD5_Signer;