RSA加密解密

X509 文件扩展名

首先我们要理解文件的扩展名代表什么。DER、PEM、CRT和CER这些扩展名经常令人困惑。很多人错误地认为这些扩展名可以互相代替。尽管的确有时候有些扩展名是可以互换的,但是最好你能确定证书是如何编码的,进而正确地标识它们。正确地标识证书有助于证书的管理。

 

编码 (也用于扩展名)

  • .DER = 扩展名DER用于二进制DER编码的证书。这些证书也可以用CER或者CRT作为扩展名。比较合适的说法是“我有一个DER编码的证书”,而不是“我有一个DER证书”。
  • .PEM = 扩展名PEM用于ASCII(Base64)编码的各种X.509 v3 证书文件开始由一行"—– BEGIN …“开始。

常用的扩展名

  • .CRT = 扩展名CRT用于证书。证书可以是DER编码,也可以是PEM编码。扩展名CER和CRT几乎是同义词。这种情况在各种unix/linux系统中很常见。
  • CER = CRT证书的微软型式。可以用微软的工具把CRT文件转换为CER文件(CRT和CER必须是相同编码的,DER或者PEM)。扩展名为CER的文件可以被IE识别并作为命令调用微软的cryptoAPI(具体点就是rudll32.exe cryptext.dll, CyrptExtOpenCER),进而弹出一个对话框来导入并/或查看证书内容。
  • .KEY = 扩展名KEY用于PCSK#8的公钥和私钥。这些公钥和私钥可以是DER编码或者PEM编码。

CRT文件和CER文件只有在使用相同编码的时候才可以安全地相互替代。

 
 
RSA 特点:用私钥加密的字符串只能用公钥进行解密(记住私钥加密的字符串不能用私钥解密)
RSA 特点:用公钥加密的字符串只能用私钥进行解密(记住公钥加密的字符串不能用公钥解密)
 
p12格式:
  • 生成私钥文件
    $ openssl genrsa -out private.pem 1024

    • openssl:是一个自由的软件组织,专注做加密和解密的框架。
    • genrsa:指定了生成了算法使用RSA
    • -out:后面的参数表示生成的key的输入文件
    • 1024:表示的是生成key的长度,单位字节(bits)
  • 创建证书请求
    $ openssl req -new -key private.pem -out rsacert.csr

    • 可以拿着这个文件去数字证书颁发机构(即CA)申请一个数字证书。CA会给你一个新的文件cacert.pem,那才是你的数字证书。(要收费的)
  • 生成证书并签名,有效期10年
    $ openssl x509 -req -days 3650 -in rsacert.csr -signkey private.pem -out rsacert.crt

    • 509是一种非常通用的证书格式。
    • 将用上面生成的密钥privkey.pem和rsacert.csr证书请求文件生成一个数字证书rsacert.crt。这个就是公钥
  • 转换格式 将 PEM 格式文件 转换成 DER 格式
    $ openssl x509 -outform der -in rsacert.crt -out rsacert.der

    • 在 iOS开发中,公钥是不能使用base64编码的,上面的命令是将公钥的base64编码字符串转换成二进制数据
  • 导出 P12 文件

    • 在iOS使用私钥不能直接使用,需要导出一个p12文件。下面命令就是将私钥文件导出为p12文件。

$ openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt

上面的证书iOS端使用p12和der;
 
pem格式生成:
MAC上生成公钥、私钥的方法,及使用
  • 1.打开终端,切换到自己想输出的文件夹下
  • 2.输入指令:openssl(openssl是生成各种秘钥的工具,mac已经嵌入
  • 3.输入指令:genrsa -out rsa_private_key.pem 1024 (生成私钥,java端使用的)
  • 4.输入指令:rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout (生成公钥)
  • 5.输入指令:pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt(私钥转格式,在ios端使用私钥解密时用这个私钥)
    注意:在MAC上生成三个.pem格式的文件,一个公钥,两个私钥,都可以在终端通过指令vim xxx.pem 打开,里面是字符串,第三步生成的私钥是java端用来解密数据的,第五步转换格式的私钥iOS端可以用来调试公钥、私钥解密(因为私钥不留在客户端)
经过测试(iOS端):三个pem格式的密钥,
1.其中rsa_private_key.pemrsa_public_key.pem在iOS中以文件形式加载加密解密时使用。
2.其中pkcs8_rsa_private_key.pemrsa_public_key.pem在iOS中以字符串形式加载进行加密解密时使用
也就是说在私钥在iOS端以字符串加载加密解密时所用的字符串必须进行pkcs8转码。
我自己也是初步了解加密知识,测试的时候是这样,也不知道正确与否
 
从网上找了一些加密方面的Demo,
1.公钥、私钥字符串本地存在的参考:http://www.jianshu.com/p/8fd8306a95d0
2.公钥、私钥文件形式存在本地参考:http://www.cnblogs.com/cocoajin/p/6183443.html
(由于看的博客比较多,上面两个文档找了好久才再次找到)
我把两个结合起来,写到一个工具类里面,可以选择性的根据文件和字符串秘钥进行加密解密:
核心代码:
.h代码:
 
#import <Foundation/Foundation.h>

@interface SecKeyTools : NSObject
/**
 从x509 cer证书中读取公钥
 */
+ (SecKeyRef )publicKeyFromCer:(NSString *)cerFile;


/**
 从 p12 文件中读取私钥,一般p12都有密码
 */
+ (SecKeyRef )privateKeyFromP12:(NSString *)p12File password:(NSString *)pwd;


/**
 iOS 10 上可用如下接口SecKeyCreateWithData 从pem文件中读取私钥或公钥
 */
+ (SecKeyRef )publicKeyFromPem:(NSString *)pemFile keySize:(size_t )size;

+ (SecKeyRef )privaKeyFromPem:(NSString *)pemFile keySize:(size_t )size;
/*
 使用公钥私钥字符串加密解密,注意:私钥字符串需要进行pkcs8转码
 */
+ (SecKeyRef)PublicKey:(NSString *)key;
+ (SecKeyRef)PrivateKey:(NSString *)key;
@end

 


 
 
.m代码:
 
#import "SecKeyTools.h"

@implementation SecKeyTools
/**
 从x509 cer证书中读取公钥
 */
+ (SecKeyRef )publicKeyFromCer:(NSString *)cerFile
{
    OSStatus            err;
    NSData *            certData;
    SecCertificateRef   cert;
    SecPolicyRef        policy;
    SecTrustRef         trust;
    SecTrustResultType  trustResult;
    SecKeyRef           publicKeyRef;
    
    certData = [NSData dataWithContentsOfFile:cerFile];
    cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certData);
    policy = SecPolicyCreateBasicX509();
    err = SecTrustCreateWithCertificates(cert, policy, &trust);
    NSAssert(err==errSecSuccess,@"证书加载失败");
    err = SecTrustEvaluate(trust, &trustResult);
    NSAssert(err==errSecSuccess,@"公钥加载失败");
    publicKeyRef = SecTrustCopyPublicKey(trust);
    
    CFRelease(policy);
    CFRelease(cert);
    return publicKeyRef;
}



/**
 从 p12 文件中读取私钥,一般p12都有密码
 */
+ (SecKeyRef )privateKeyFromP12:(NSString *)p12File password:(NSString *)pwd

{
    NSData *            pkcs12Data;
    CFArrayRef          imported;
    NSDictionary *      importedItem;
    SecIdentityRef      identity;
    OSStatus            err;
    SecKeyRef           privateKeyRef;
    
    pkcs12Data = [NSData dataWithContentsOfFile:p12File];
    err = SecPKCS12Import((__bridge CFDataRef)pkcs12Data,(__bridge CFDictionaryRef) @{(__bridge NSString *)kSecImportExportPassphrase:pwd}, &imported);
    NSAssert(err==errSecSuccess,@"p12加载失败");
    importedItem = (__bridge NSDictionary *) CFArrayGetValueAtIndex(imported, 0);
    identity = (__bridge SecIdentityRef) importedItem[(__bridge NSString *) kSecImportItemIdentity];
    
    err = SecIdentityCopyPrivateKey(identity, &privateKeyRef);
    NSAssert(err==errSecSuccess,@"私钥加载失败");
    CFRelease(imported);
    
    
    return privateKeyRef;
}


+ (SecKeyRef )publicKeyFromPem:(NSString *)pemFile keySize:(size_t )size
{
    SecKeyRef pubkeyref;
    NSError *readFErr = nil;
    CFErrorRef errref = noErr;
    NSString *pemStr = [NSString stringWithContentsOfFile:pemFile encoding:NSASCIIStringEncoding error:&readFErr];
    NSAssert(readFErr==nil, @"pem文件加载失败");
    pemStr = [pemStr stringByReplacingOccurrencesOfString:@"-----BEGIN PUBLIC KEY-----" withString:@""];
    pemStr = [pemStr stringByReplacingOccurrencesOfString:@"\r" withString:@""];
    pemStr = [pemStr stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    pemStr = [pemStr stringByReplacingOccurrencesOfString:@"-----END PUBLIC KEY-----" withString:@""];
    NSData *dataPubKey = [[NSData alloc]initWithBase64EncodedString:pemStr options:0];
    
    NSMutableDictionary *dicPubkey = [[NSMutableDictionary alloc]initWithCapacity:1];
    [dicPubkey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [dicPubkey setObject:(__bridge id) kSecAttrKeyClassPublic forKey:(__bridge id)kSecAttrKeyClass];
    [dicPubkey setObject:@(size) forKey:(__bridge id)kSecAttrKeySizeInBits];
    
    pubkeyref = SecKeyCreateWithData((__bridge CFDataRef)dataPubKey, (__bridge CFDictionaryRef)dicPubkey, &errref);
    
    NSAssert(errref==noErr, @"公钥加载错误");
    
    return pubkeyref;
}

+ (SecKeyRef )privaKeyFromPem:(NSString *)pemFile keySize:(size_t )size
{
    SecKeyRef prikeyRef;
    NSError *readFErr = nil;
    CFErrorRef err = noErr;
    
    NSString *pemStr = [NSString stringWithContentsOfFile:pemFile encoding:NSASCIIStringEncoding error:&readFErr];
    NSAssert(readFErr==nil, @"pem文件加载失败");
    pemStr = [pemStr stringByReplacingOccurrencesOfString:@"-----BEGIN RSA PRIVATE KEY-----" withString:@""];
    pemStr = [pemStr stringByReplacingOccurrencesOfString:@"\r" withString:@""];
    pemStr = [pemStr stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    pemStr = [pemStr stringByReplacingOccurrencesOfString:@"-----END RSA PRIVATE KEY-----" withString:@""];
    NSData *pemData = [[NSData alloc]initWithBase64EncodedString:pemStr options:0];
    
    NSMutableDictionary *dicPrikey = [[NSMutableDictionary alloc]initWithCapacity:1];
    [dicPrikey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [dicPrikey setObject:(__bridge id) kSecAttrKeyClassPrivate forKey:(__bridge id)kSecAttrKeyClass];
    [dicPrikey setObject:@(size) forKey:(__bridge id)kSecAttrKeySizeInBits];
    
    prikeyRef = SecKeyCreateWithData((__bridge CFDataRef)pemData, (__bridge CFDictionaryRef)dicPrikey, &err);
    NSAssert(err==noErr, @"私钥加载错误");
    
    
    return prikeyRef;
}
+ (SecKeyRef)PublicKey:(NSString *)key{
    NSRange spos = [key rangeOfString:@"-----BEGIN RSA PUBLIC KEY-----"];
    NSRange epos = [key rangeOfString:@"-----END RSA PUBLIC KEY-----"];
    if(spos.location != NSNotFound && epos.location != NSNotFound){
        NSUInteger s = spos.location + spos.length;
        NSUInteger e = epos.location;
        NSRange range = NSMakeRange(s, e-s);
        key = [key substringWithRange:range];
    }
    key = [key stringByReplacingOccurrencesOfString:@"\r" withString:@""];
    key = [key stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    key = [key stringByReplacingOccurrencesOfString:@"\t" withString:@""];
    key = [key stringByReplacingOccurrencesOfString:@" "  withString:@""];
//    NSLog(@"key is ...%@\n",key);
    // This will be base64 encoded, decode it.
    NSData *data = [[NSData alloc]initWithBase64EncodedString:key options:NSDataBase64DecodingIgnoreUnknownCharacters];
    data = [SecKeyTools stripPublicKeyHeader:data];
    if(!data){
        return nil;
    }
    
    //a tag to read/write keychain storage
    NSString *tag = @"RSAUtil_PubKey";
    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;
}

+ (SecKeyRef)PrivateKey:(NSString *)key{
    NSRange spos = [key rangeOfString:@"-----BEGIN RSA PRIVATE KEY-----"];
    NSRange epos = [key rangeOfString:@"-----END RSA PRIVATE KEY-----"];
    if(spos.location != NSNotFound && epos.location != NSNotFound){
        NSUInteger s = spos.location + spos.length;
        NSUInteger e = epos.location;
        NSRange range = NSMakeRange(s, e-s);
        key = [key substringWithRange:range];
    }
    key = [key stringByReplacingOccurrencesOfString:@"\r" withString:@""];
    key = [key stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    key = [key stringByReplacingOccurrencesOfString:@"\t" withString:@""];
    key = [key stringByReplacingOccurrencesOfString:@" "  withString:@""];
    
    // This will be base64 encoded, decode it.
    NSData *data = [[NSData alloc]initWithBase64EncodedString:key options:NSDataBase64DecodingIgnoreUnknownCharacters];
    data = [SecKeyTools stripPrivateKeyHeader:data];
    if(!data){
        return nil;
    }
    
    //a tag to read/write keychain storage
    NSString *tag = @"RSAUtil_PrivKey";
    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;
}
+ (NSData *)stripPublicKeyHeader:(NSData *)d_key{
    // Skip ASN.1 public key header
    if (d_key == nil) return(nil);
    
    unsigned long len = [d_key length];
    if (!len) return(nil);
    
    unsigned char *c_key = (unsigned char *)[d_key bytes];
    unsigned int  idx     = 0;
    
    if (c_key[idx++] != 0x30) return(nil);
    
    if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
    else idx++;
    
    // PKCS #1 rsaEncryption szOID_RSA_RSA
    static unsigned char seqiod[] =
    { 0x30,   0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
        0x01, 0x05, 0x00 };
    if (memcmp(&c_key[idx], seqiod, 15)) return(nil);
    
    idx += 15;
    
    if (c_key[idx++] != 0x03) return(nil);
    
    if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
    else idx++;
    
    if (c_key[idx++] != '\0') return(nil);
    
    // Now make a new NSData from this buffer
    return([NSData dataWithBytes:&c_key[idx] length:len - idx]);
}

//credit: http://hg.mozilla.org/services/fx-home/file/tip/Sources/NetworkAndStorage/CryptoUtils.m#l1036
+ (NSData *)stripPrivateKeyHeader:(NSData *)d_key{
    // Skip ASN.1 private key header
    if (d_key == nil) return(nil);
    
    unsigned long len = [d_key length];
    if (!len) return(nil);
    
    unsigned char *c_key = (unsigned char *)[d_key bytes];
    unsigned int  idx     = 22; //magic byte at offset 22
    
    if (0x04 != c_key[idx++]) return nil;
    
    //calculate length of the key
    unsigned int c_len = c_key[idx++];
    int det = c_len & 0x80;
    if (!det) {
        c_len = c_len & 0x7f;
    } else {
        int byteCount = c_len & 0x7f;
        if (byteCount + idx > len) {
            //rsa length field longer than buffer
            return nil;
        }
        unsigned int accum = 0;
        unsigned char *ptr = &c_key[idx];
        idx += byteCount;
        while (byteCount) {
            accum = (accum << 8) + *ptr;
            ptr++;
            byteCount--;
        }
        c_len = accum;
    }
    
    // Now make a new NSData from this buffer
    return [d_key subdataWithRange:NSMakeRange(idx, c_len)];
}
@end

 

对于加密还是一知半解,有写错的还望指正!!
 
posted @ 2017-04-19 14:47  有棱角的圆  阅读(1480)  评论(0编辑  收藏  举报