我正在尝试使用其签名和公钥验证给定邮件。它使用iOS提供的安全框架工作正常,但我无法使用Crypto ++库(必须使用)使其工作。
我使用CryptoPP库执行了相同的步骤,并对所有内容进行了10次验证,重写了一些不同的部分,但它仍然引发了相同的异常:
“PK_Signer:此签名方案的密钥太短”
证书
签名(JWB的第三段)
要验证的消息
使用PKCS1,RSA
验证SHA256字节(只有部分重要,因为验证在iOS上运行正常)
证书
NSData *certificateData = [[NSData alloc] initWithBase64EncodedString:certificateString options:0];
SecKeyRef getPublicKeyFromCertificate(certificateData)
在网上找到,效果很好。
使用PKCS1,RSA
验证SHA256字节BOOL PKCSVerifyBytesSHA256withRSA(NSData* message, NSData* signature, SecKeyRef publicKey)
{
size_t signedHashBytesSize = SecKeyGetBlockSize(publicKey);
const void* signedHashBytes = [signature bytes];
size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH;
void* hashBytes = malloc(hashBytesSize);
if (!CC_SHA256([message bytes], (CC_LONG)[message length], hashBytes)) {
return NULL;
}
OSStatus status = SecKeyRawVerify(publicKey,
kSecPaddingPKCS1SHA256,
hashBytes,
hashBytesSize,
signedHashBytes,
signedHashBytesSize);
return status == errSecSuccess;
}
我使用与描述相对应的数字和一些额外的注释复制/粘贴整个代码,例如返回的结构大小。
+(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库复制了完全相同的概念。
非常感谢任何帮助,谢谢。
答案 0 :(得分:0)
vf.Put(摘要,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_Signer
和RSASSA_PKCS1v15_SHA_Verifier
。
同时结帐RSASS
class,即RSA签名方案。我猜你可能想要一个RSASS<PKCS1v15, SHA256>::Signer
和RSASS<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;