草庐IT

iOS RSA加签和验签(SHA1WithRSA)

__Simon__ 2023-09-21 原文

RSA 简介

RSA是一种非对称加密算法,使用公钥加密就可以使用私钥解密,使用私钥加密就可以使用公钥解密。RSA公钥对外公开,私钥自己保留。RSA既能加密、解密,也能加签、验签

加密解密:RSA是公钥加密,私钥私密。数据发送方使用接收方的公钥来对数据进行加密,接收方接收到数据之后用自己的私钥解密

加签验签:RSA私钥加签,公钥验签。数据发送方使用自己的私钥对数据进行签名,数据接收方使用数据发送方的公钥来验签。

我遇到的业务场景是:服务端接口开发人员给了我一套客户端的PKCS1格式的私钥字符串和服务端的公钥字符串。我给服务端发送数据的时候要使用私钥对数据进行签名之后再发送。接收到服务端返回的数据之后要使用服务端的公钥验签。下面接详细讲解在iOS上SHA1WithRSA的实现过程。

加签的流程:
加签流程
验签的流程
验签流程

在数据传输的过程中如果原文是以明文的形式传输,数据还是会被泄漏。所以我这里对原文数据进行了一次AES加密,然后再走加签的流程

下面讲解iOS中使用Security.framework系统框架来实现RSA相关功能,以及使用<CommonCrypto/CommonDiges.h> 下的CC_SHA1实现sha1算法

  1. PKCS1格式的RSA私钥的字符串转SecKeyRef
  2. 公钥字符串转SeckeyRef
  3. SHA1算法的实现
  4. 使用RSA私钥签名的实现
  5. 使用RSA公钥验签的实现

1. PKCS1格式的RSA私钥的字符串转SecKeyRef

PKCS#1格式的RSA私钥转成可供Security.framework框架中使用的SecKeyRef

+ (SecKeyRef)addPrivateKey:(NSString *)key{
    
    // This is a base64 encoded key. so, decode it.
    NSData *data = [[NSData alloc] initWithBase64EncodedString:key options:NSDataBase64DecodingIgnoreUnknownCharacters];
    
    if(!data){ return nil; }
    //a tag to read/write keychain storage
    NSString *tag = @"RSA_PRIVATE_KEY";
    NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];
    
    // Delete any old lingering key with the same tag
    NSMutableDictionary *privateKey = [[NSMutableDictionary alloc] init];
    [privateKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass];
    [privateKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [privateKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag];
    SecItemDelete((__bridge CFDictionaryRef)privateKey);
    
    // Add persistent version of the key to system keychain
    [privateKey setObject:data forKey:(__bridge id)kSecValueData];
    [privateKey setObject:(__bridge id) kSecAttrKeyClassPrivate forKey:(__bridge id)kSecAttrKeyClass];
    [privateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnPersistentRef];
    
    CFTypeRef persistKey = nil;
    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)privateKey, &persistKey);
    if (persistKey != nil){ CFRelease(persistKey); }
    if ((status != noErr) && (status != errSecDuplicateItem)) { return nil; }
    
    [privateKey removeObjectForKey:(__bridge id)kSecValueData];
    [privateKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef];
    [privateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
    [privateKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    
    // Now fetch the SecKeyRef version of the key
    SecKeyRef keyRef = nil;
    status = SecItemCopyMatching((__bridge CFDictionaryRef)privateKey, (CFTypeRef *)&keyRef);
    if(status != noErr){
        return nil;
    }
    return keyRef;
}

2. 公钥字符串转SeckeyRef

+ (SecKeyRef)addPublicKey:(NSString *)pubKey
{
    NSData *data = [[NSData alloc] initWithBase64EncodedString:pubKey options:NSDataBase64DecodingIgnoreUnknownCharacters];
    
    //a tag to read/write keychain storage
    NSString *tag = @"RSA_PUBLIC_KEY";
    NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];
    
    // Delete any old lingering key with the same tag
    NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init];
    [publicKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass];
    [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [publicKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag];
    SecItemDelete((__bridge CFDictionaryRef)publicKey);
    
    // Add persistent version of the key to system keychain
    [publicKey setObject:data forKey:(__bridge id)kSecValueData];
    [publicKey setObject:(__bridge id) kSecAttrKeyClassPublic forKey:(__bridge id)kSecAttrKeyClass];
    [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnPersistentRef];
    
    CFTypeRef persistKey = nil;
    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)publicKey, &persistKey);
    if (persistKey != nil){
        CFRelease(persistKey);
    }
    
    if ((status != noErr) && (status != errSecDuplicateItem)) { return nil; }
    
    [publicKey removeObjectForKey:(__bridge id)kSecValueData];
    [publicKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef];
    [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
    [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    
    // Now fetch the SecKeyRef version of the key
    SecKeyRef keyRef = nil;
    status = SecItemCopyMatching((__bridge CFDictionaryRef)publicKey, (CFTypeRef *)&keyRef);
    if(status != noErr){
        return nil;
    }
    return keyRef;
}

3. SHA1算法的实现

// digest message with sha1
+ (NSData *)sha1:(NSString *)str
{
    const void *data = [str cStringUsingEncoding:NSUTF8StringEncoding];
    CC_LONG len = (CC_LONG)strlen(data);
    uint8_t * md = malloc( CC_SHA1_DIGEST_LENGTH * sizeof(uint8_t) );;
    CC_SHA1(data, len, md);
    return [NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH];
}

4. 使用RSA私钥签名的实现

// Using the RSA private key to sign the specified message
+ (NSString *)sign:(NSString *)content withPriKey:(NSString *)priKey
{
    SecKeyRef privateKeyRef = [self addPrivateKey:priKey];
    if (!privateKeyRef) { NSLog(@"添加私钥失败"); return  nil; }
    NSData *sha1Data = [self sha1:content];
    unsigned char *sig = (unsigned char *)malloc(256);
    size_t sig_len;
    OSStatus status = SecKeyRawSign(privateKeyRef, kSecPaddingPKCS1SHA1, [sha1Data bytes], CC_SHA1_DIGEST_LENGTH, sig, &sig_len);
    
    if (status != noErr) { NSLog(@"加签失败:%d",status); return nil; }
    
    NSData *outData = [NSData dataWithBytes:sig length:sig_len];
    return [outData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
}

5. 使用RSA公钥验签的实现

// verify Signature
+ (BOOL)verify:(NSString *)content signature:(NSString *)signature withPublivKey:(NSString *)publicKey {
    
    SecKeyRef publicKeyRef = [self addPublicKey:publicKey];
    if (!publicKeyRef) { NSLog(@"添加公钥失败"); return NO; }
    NSData *originData = [self sha1:content];
    NSData *signatureData = [[NSData alloc] initWithBase64EncodedString:signature options:NSDataBase64DecodingIgnoreUnknownCharacters];
    if (!originData || !signatureData) { return NO; }
    OSStatus status =  SecKeyRawVerify(publicKeyRef, kSecPaddingPKCS1SHA1, [originData bytes], originData.length, [signatureData bytes], signatureData.length);
    
    if (status ==noErr) { return  YES; }
    else{ NSLog(@"验签失败:%d",status); return NO; }
}

完整的示例代码可以在GitHub上查看

附:

本文介绍的加签是基于RSA的PKCS#1格式的私钥来完成。如实你使用的是PKCS#8格式的私钥请参考另一个库Encrypt。关于RSA私钥的格式请自行百度了解。关于PKCS1转PKCS8请看这篇文章RSA私钥PKCS1 转 PKCS8

有关iOS RSA加签和验签(SHA1WithRSA)的更多相关文章

  1. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  2. ruby - 使用 ruby​​ 生成格式化为/etc/shadow 的 SHA512 crypt-style 哈希? - 2

    我想生成SHA512散列密码以直接包含在/etc/shadow文件中以与chef'suserresource一起使用.通常我会为此访问stdlib的Digest库,但它不会以正确的格式生成哈希:ruby-1.9.2-p136:001>require'digest/sha2'=>trueruby-1.9.2-p136:002>Digest::SHA512.hexdigest('test')=>"ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b1437

  3. ruby-on-rails - LoadError : dlopen(digest/sha1. bundle):找不到符号:_rb_Digest_SHA1_Finish - 2

    Ruby最近停止在我兄弟的机器上工作。gem命令rails服务器rails控制台全部失败并出现以下错误:$irbirb(main):001:0>require'digest/sha1'LoadError:dlopen(~/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/x86_64-darwin12.2.0/digest/sha1.bundle,9):Symbolnotfound:_rb_Digest_SHA1_FinishReferencedfrom:~/.rbenv/versions/2.0.0-p0/lib/ruby/2.0.0/x86_64-da

  4. c# - ruby 中的 hmac-sha1 不同于 C# HMACSHA1 - 2

    我正在尝试测试来自ankoder.com的APIauthenticationtoken的摘要计算有问题.当我尝试从C#调用时,示例是ruby。当我比较HMAC-SHA1中的摘要结果时,我发现key结果有问题。为了便于测试这里是代码:require'hmac-sha1'require'digest/sha1'require'base64'token="-Sat,14Nov200909:47:53GMT-GET-/video.xml-"private_key="whatever"salt=Digest::SHA1.hexdigest(token)[0..19]passkey=Base64.

  5. ruby - 使用 SHA256 摘要签名的 OpenSSL RSA - 2

    我正在尝试找出与我拥有的小型ruby​​脚本等效的shell脚本。这是ruby脚本:require'openssl'require'base64'k=OpenSSL::PKey::RSA.new(File.read("key.pem"))res=File.read("res.tmp")digest=OpenSSL::Digest::SHA256.newsignature=k.sign(digest,res)File.write("foo1.txt",Base64.strict_encode64(signature))就是这样。它需要一些数据,获取它的SHA256哈希值,然后用我拥有的私

  6. ruby - 使用 ruby​​ 中的 SHA256 哈希算法计算符合 RFC 2104 的 HMAC - 2

    我在浏览亚马逊产品广告APIRESTsignaturedocs时卡在了#8CalculateanRFC2104-compliantHMACwiththeSHA256hashalgorithmusingthestringabovewithour"dummy"SecretAccessKey:1234567890.Formoreinformationaboutthisstep,seedocumentationandcodesamplesforyourprogramminglanguage.没关系,在CalculatingaSHAhashwithastring+secretkeyinpytho

  7. ruby-on-rails - 在 Ruby 中使用 HMAC SHA256 - 2

    我正在尝试应用HMAC-SHA256为RestAPI生成key。我正在做这样的事情:defgenerateTransactionHash(stringToHash)key='123'data='stringToHash'digest=OpenSSL::Digest.new('sha256')hmac=OpenSSL::HMAC.digest(digest,key,data)putshmacend它的输出始终是这样的:(如果我将“12345”作为参数或“HUSYED815X”,我会得到相同的结果)ۯw/{o���p�T����:��a�h��E|qAPI因此无法正常工作...有人可以帮我

  8. ruby-on-rails - SHA1 哈希值在我的 Rails 和 Cocoa 应用程序之间不匹配 - 2

    我有一个Cocoa应用程序将一些数据连同该数据的SHA1哈希值一起发送到Rails应用程序,该应用程序验证数据和哈希值是否匹配,但它们不匹配。为了确保我已经记录了在Rails和Cocoa端散列到控制台的数据的十六进制版本,并且它们完全匹配。这是Cocoa部分:#import//...-(NSData*)dataOfSHA1Hash{unsignedcharhashBytes[CC_SHA1_DIGEST_LENGTH];CC_SHA1([selfbytes],CC_SHA1_DIGEST_LENGTH,hashBytes);return[NSDatadataWithBytes:hash

  9. 为 OAuth 生成 HMAC-SHA1 签名的 Ruby 方法 - 2

    我正在编写一个小的ruby​​程序来通过OAuth与Twitter一起玩,但还没有找到一个正确的方法来进行HMAC-SHA1签名。到目前为止,我搞砸了Base64.encode64(OpenSSL::HMAC.hexdigest(digest,key,stuff)).chomp但这会输出Twitter拒绝的内容,而不是有效签名。我实际上以更糟糕的方式解决了它,请不要打我耳光:php-r"echorawurlencode(base64_encode(hash_hmac('sha1','#{@signature}','#{llave}',true)));"最后一个确实有效,我可以四处做我的

  10. ruby-on-rails - 如何使用 capistrano deploy 定位特定的提交 SHA - 2

    我想知道如何使用Capistrano在Git中针对特定的提交SHA进行部署?应该是这样的capdeploy--version=经过大量搜索似乎无法找到这个问题的答案。 最佳答案 对于Capistrano2.9到3.0:cap-Srevision=80655da8d80aaaf92ce5357e7828dc09adb00993deploy对于旧版本的Capistrano,您可以通过执行以下操作来部署特定的gitcommit/tree/branch/tag:cap-sbranch=80655da8d80aaaf92ce5357e7828

随机推荐