iOS开发中的小伙伴应该是常常用der和p12进行加密解密,并且在一般加密不止一种加密算法,还能够加点儿盐吧~本文章主要阐述的是在iOS中基于openSLL的RSA加密。一共有两种方式,一种是基于p12加密解密的,还有一种是博客园官方提供的公钥字符串加密的,其实两种都差很少,只不过在iOS中支持crt格式的加密,其实也是同样的吧~下面就来看看两种加密的应用。。。html
说在前面的话~本文RSA加密算法并不是笔者本人所做~RSA算法网上有一大堆的demo,不过笔者观察核心的代码也就只有一两个版本~因此,笔者也小小的借鉴了一下~ios
// // CryptorTools.h // 加密/解密工具 // // Created by Erma on 15/4/26. // Copyright (c) 2015年 Erma. All rights reserved. // #import <Foundation/Foundation.h> /// 加密工具类 /// 提供RSA & AES & DES加密方法 @interface CryptorTools : NSObject #pragma mark - DES 加密/解密 /// DES 加密 /// /// @param data 要加密的二进制数据 /// @param keyString 加密密钥 /// @param iv IV向量 /// /// @return 加密后的二进制数据 + (NSData *)DESEncryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv; /// DES 加密字符串 /// /// @param string 要加密的字符串 /// @param keyString 加密密钥 /// @param iv IV向量 /// /// @return 加密后的 BASE64 编码字符串 + (NSString *)DESEncryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv; /// DES 解密 /// /// @param data 要解密的二进制数据 /// @param keyString 解密密钥 /// @param iv IV向量 /// /// @return 解密后的二进制数据 + (NSData *)DESDecryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv; /// DES 解密 /// /// @param string 要解密的 BASE64 编码字符串 /// @param keyString 解密密钥 /// @param iv IV向量 /// /// @return 解密后的二进制数据 + (NSString *)DESDecryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv; #pragma mark - AES 加密/解密 /// AES 加密 /// /// @param data 要加密的二进制数据 /// @param keyString 加密密钥 /// @param iv IV向量 /// /// @return 加密后的二进制数据 + (NSData *)AESEncryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv; /// AES 加密字符串 /// /// @param string 要加密的字符串 /// @param keyString 加密密钥 /// @param iv IV向量 /// /// @return 加密后的 BASE64 编码字符串 + (NSString *)AESEncryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv; /// AES 解密 /// /// @param data 要解密的二进制数据 /// @param keyString 解密密钥 /// @param iv IV向量 /// /// @return 解密后的二进制数据 + (NSData *)AESDecryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv; /// AES 解密 /// /// @param string 要解密的 BASE64 编码字符串 /// @param keyString 解密密钥 /// @param iv IV向量 /// /// @return 解密后的二进制数据 + (NSString *)AESDecryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv; #pragma mark - RSA 加密/解密算法 /// 加载公钥 /// /// @param filePath DER 公钥文件路径 - (void)loadPublicKeyWithFilePath:(NSString *)filePath; /// 加载私钥 /// /// @param filePath P12 私钥文件路径 /// @param password P12 密码 - (void)loadPrivateKey:(NSString *)filePath password:(NSString *)password; /// RSA 加密数据 /// /// @param data 要加密的数据 /// /// @return 加密后的二进制数据 - (NSData *)RSAEncryptData:(NSData *)data; /// RSA 加密字符串 /// /// @param string 要加密的字符串 /// /// @return 加密后的 BASE64 编码字符串 - (NSString *)RSAEncryptString:(NSString *)string; /// RSA 解密数据 /// /// @param data 要解密的数据 /// /// @return 解密后的二进制数据 - (NSData *)RSADecryptData:(NSData *)data; /// RSA 解密字符串 /// /// @param string 要解密的 BASE64 编码字符串 /// /// @return 解密后的字符串 - (NSString *)RSADecryptString:(NSString *)string; @end
// // CryptorTools.m // 加密/解密工具 // // Created by Erma on 15/4/26. // Copyright (c) 2015年 Erma. All rights reserved. // #import "CryptorTools.h" #import <CommonCrypto/CommonCrypto.h> // 填充模式 #define kTypeOfWrapPadding kSecPaddingPKCS1 @interface CryptorTools() { SecKeyRef _publicKeyRef; // 公钥引用 SecKeyRef _privateKeyRef; // 私钥引用 } @end @implementation CryptorTools #pragma mark - DES 加密/解密 #pragma mark 加密 + (NSData *)DESEncryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv { return [self CCCryptData:data algorithm:kCCAlgorithmDES operation:kCCEncrypt keyString:keyString iv:iv]; } + (NSString *)DESEncryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv { NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; NSData *result = [self DESEncryptData:data keyString:keyString iv:iv]; // BASE 64 编码 return [result base64EncodedStringWithOptions:0]; } #pragma mark 解密 + (NSData *)DESDecryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv { return [self CCCryptData:data algorithm:kCCAlgorithmDES operation:kCCDecrypt keyString:keyString iv:iv]; } + (NSString *)DESDecryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv { // BASE 64 解码 NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0]; NSData *result = [self DESDecryptData:data keyString:keyString iv:iv]; return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding]; } #pragma mark - AES 加密/解密 #pragma mark 加密 + (NSData *)AESEncryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv { return [self CCCryptData:data algorithm:kCCAlgorithmAES operation:kCCEncrypt keyString:keyString iv:iv]; } + (NSString *)AESEncryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv { NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; NSData *result = [self AESEncryptData:data keyString:keyString iv:iv]; // BASE 64 编码 return [result base64EncodedStringWithOptions:0]; } #pragma mark 解密 + (NSData *)AESDecryptData:(NSData *)data keyString:(NSString *)keyString iv:(NSData *)iv { return [self CCCryptData:data algorithm:kCCAlgorithmAES operation:kCCDecrypt keyString:keyString iv:iv]; } + (NSString *)AESDecryptString:(NSString *)string keyString:(NSString *)keyString iv:(NSData *)iv { // BASE 64 解码 NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0]; NSData *result = [self AESDecryptData:data keyString:keyString iv:iv]; return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding]; } #pragma mark 对称加密&解密核心方法 /// 对称加密&解密核心方法 /// /// @param data 加密/解密的二进制数据 /// @param algorithm 加密算法 /// @param operation 加密/解密操做 /// @param keyString 密钥字符串 /// @param iv IV 向量 /// /// @return 加密/解密结果 + (NSData *)CCCryptData:(NSData *)data algorithm:(CCAlgorithm)algorithm operation:(CCOperation)operation keyString:(NSString *)keyString iv:(NSData *)iv { int keySize = (algorithm == kCCAlgorithmAES) ? kCCKeySizeAES128 : kCCKeySizeDES; int blockSize = (algorithm == kCCAlgorithmAES) ? kCCBlockSizeAES128: kCCBlockSizeDES; // 设置密钥 NSData *keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding]; uint8_t cKey[keySize]; bzero(cKey, sizeof(cKey)); [keyData getBytes:cKey length:keySize]; // 设置 IV 向量 uint8_t cIv[blockSize]; bzero(cIv, blockSize); int option = kCCOptionPKCS7Padding | kCCOptionECBMode; if (iv) { [iv getBytes:cIv length:blockSize]; option = kCCOptionPKCS7Padding; } // 设置输出缓冲区 size_t bufferSize = [data length] + blockSize; void *buffer = malloc(bufferSize); // 加密或解密 size_t cryptorSize = 0; CCCryptorStatus cryptStatus = CCCrypt(operation, algorithm, option, cKey, keySize, cIv, [data bytes], [data length], buffer, bufferSize, &cryptorSize); NSData *result = nil; if (cryptStatus == kCCSuccess) { result = [NSData dataWithBytesNoCopy:buffer length:cryptorSize]; } else { free(buffer); NSLog(@"[错误] 加密或解密失败 | 状态编码: %d", cryptStatus); } return result; } #pragma mark - RSA 加密/解密算法 - (void)loadPublicKeyWithFilePath:(NSString *)filePath; { NSAssert(filePath.length != 0, @"公钥路径为空"); // 删除当前公钥 if (_publicKeyRef) CFRelease(_publicKeyRef); // 从一个 DER 表示的证书建立一个证书对象 NSData *certificateData = [NSData dataWithContentsOfFile:filePath]; SecCertificateRef certificateRef = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificateData); NSAssert(certificateRef != NULL, @"公钥文件错误"); // 返回一个默认 X509 策略的公钥对象,使用以后须要调用 CFRelease 释放 SecPolicyRef policyRef = SecPolicyCreateBasicX509(); // 包含信任管理信息的结构体 SecTrustRef trustRef; // 基于证书和策略建立一个信任管理对象 OSStatus status = SecTrustCreateWithCertificates(certificateRef, policyRef, &trustRef); NSAssert(status == errSecSuccess, @"建立信任管理对象失败"); // 信任结果 SecTrustResultType trustResult; // 评估指定证书和策略的信任管理是否有效 status = SecTrustEvaluate(trustRef, &trustResult); NSAssert(status == errSecSuccess, @"信任评估失败"); // 评估以后返回公钥子证书 _publicKeyRef = SecTrustCopyPublicKey(trustRef); NSAssert(_publicKeyRef != NULL, @"公钥建立失败"); if (certificateRef) CFRelease(certificateRef); if (policyRef) CFRelease(policyRef); if (trustRef) CFRelease(trustRef); } - (void)loadPrivateKey:(NSString *)filePath password:(NSString *)password { NSAssert(filePath.length != 0, @"私钥路径为空"); // 删除当前私钥 if (_privateKeyRef) CFRelease(_privateKeyRef); NSData *PKCS12Data = [NSData dataWithContentsOfFile:filePath]; CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data; CFStringRef passwordRef = (__bridge CFStringRef)password; // 从 PKCS #12 证书中提取标示和证书 SecIdentityRef myIdentity; SecTrustRef myTrust; const void *keys[] = {kSecImportExportPassphrase}; const void *values[] = {passwordRef}; CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL); CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL); // 返回 PKCS #12 格式数据中的标示和证书 OSStatus status = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items); if (status == noErr) { CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0); myIdentity = (SecIdentityRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity); myTrust = (SecTrustRef)CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust); } if (optionsDictionary) CFRelease(optionsDictionary); NSAssert(status == noErr, @"提取身份和信任失败"); SecTrustResultType trustResult; // 评估指定证书和策略的信任管理是否有效 status = SecTrustEvaluate(myTrust, &trustResult); NSAssert(status == errSecSuccess, @"信任评估失败"); // 提取私钥 status = SecIdentityCopyPrivateKey(myIdentity, &_privateKeyRef); NSAssert(status == errSecSuccess, @"私钥建立失败"); CFRelease(items); } - (NSString *)RSAEncryptString:(NSString *)string { NSData *cipher = [self RSAEncryptData:[string dataUsingEncoding:NSUTF8StringEncoding]]; return [cipher base64EncodedStringWithOptions:0]; } - (NSData *)RSAEncryptData:(NSData *)data { OSStatus sanityCheck = noErr; size_t cipherBufferSize = 0; size_t keyBufferSize = 0; NSAssert(data, @"明文数据为空"); NSAssert(_publicKeyRef, @"公钥为空"); NSData *cipher = nil; uint8_t *cipherBuffer = NULL; // 计算缓冲区大小 cipherBufferSize = SecKeyGetBlockSize(_publicKeyRef); keyBufferSize = data.length; if (kTypeOfWrapPadding == kSecPaddingNone) { NSAssert(keyBufferSize <= cipherBufferSize, @"加密内容太大"); } else { NSAssert(keyBufferSize <= (cipherBufferSize - 11), @"加密内容太大"); } // 分配缓冲区 cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t)); memset((void *)cipherBuffer, 0x0, cipherBufferSize); // 使用公钥加密 sanityCheck = SecKeyEncrypt(_publicKeyRef, kTypeOfWrapPadding, (const uint8_t *)data.bytes, keyBufferSize, cipherBuffer, &cipherBufferSize ); NSAssert(sanityCheck == noErr, @"加密错误,OSStatus == %d", sanityCheck); // 生成密文数据 cipher = [NSData dataWithBytes:(const void *)cipherBuffer length:(NSUInteger)cipherBufferSize]; if (cipherBuffer) free(cipherBuffer); return cipher; } - (NSString *)RSADecryptString:(NSString *)string { NSData *keyData = [self RSADecryptData:[[NSData alloc] initWithBase64EncodedString:string options:0]]; return [[NSString alloc] initWithData:keyData encoding:NSUTF8StringEncoding]; } - (NSData *)RSADecryptData:(NSData *)data { OSStatus sanityCheck = noErr; size_t cipherBufferSize = 0; size_t keyBufferSize = 0; NSData *key = nil; uint8_t *keyBuffer = NULL; SecKeyRef privateKey = _privateKeyRef; NSAssert(privateKey != NULL, @"私钥不存在"); // 计算缓冲区大小 cipherBufferSize = SecKeyGetBlockSize(privateKey); keyBufferSize = data.length; NSAssert(keyBufferSize <= cipherBufferSize, @"解密内容太大"); // 分配缓冲区 keyBuffer = malloc(keyBufferSize * sizeof(uint8_t)); memset((void *)keyBuffer, 0x0, keyBufferSize); // 使用私钥解密 sanityCheck = SecKeyDecrypt(privateKey, kTypeOfWrapPadding, (const uint8_t *)data.bytes, cipherBufferSize, keyBuffer, &keyBufferSize ); NSAssert1(sanityCheck == noErr, @"解密错误,OSStatus == %d", sanityCheck); // 生成明文数据 key = [NSData dataWithBytes:(const void *)keyBuffer length:(NSUInteger)keyBufferSize]; if (keyBuffer) free(keyBuffer); return key; } @end
$ openssl genrsa -out private.pem 1024
执行以代码生成一个私钥,Pem文件,其实Pem文件就是通常的文本格式~看下图~
这是文件:git
选择一个文本编辑器打开次文件能够看到其就是一个普通的文本:
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCfwtWJLpe9QQiBOA/kDVdYGDYko6ieGfaIHiqiHd7Ul13k4gI+
1NgL6SfO/UAhKL6rAwTk9t8/V0bIrbCTBL6hMLc4yJkBFbDK7eLoJNnxaUwl2pLL
BSiTZQQ8vsBC6myUiZDFdCfl2PWvfEMzMYNsCob2Mw4MYWJwNub+MYe7PwIDAQAB
AoGAc8jXy5FKBa5BRK1lzujgWYdKjilSRisY4jPCwDWXzklZkk0+RV0qqw8ye7BN
LvsBnJ0Wif5lc9mEAmLnKtXwdWrHKEi70s69mZZH+ssaP3SGAEug3tY2ojSYixmB
+dWyslVb3dVzxr56fMJLfCBGAhqhmXgy79ruIbnKrDqo6kkCQQDPYCIZRlI0tREa
4y+E2YUqx/x6XPohlJUQoZBJQ3Zt0RQ+afljNxlSOiL4pw9GLwoDhatxzjlMUMnb
b36mP1plAkEAxTib34YEp5nkwpbZ5roAfKRmKgUnezULVCDKS/KiamXURwAUwGGU
aVy9o1akS48C42gsF+NtOe9yq1z9sj6y0wJBAICLZpekL3DcjC3OhbYj35gVPzva
RnJqV7xnabkASHjqEVJe/mexz9BYmTTo2V736Y0lXpC89GeJ7JZJFoiW3MECQDyM
4cZhpiIy7HoVyHa/GpEqBDfYd0OriHveyV1B9D2IYAEgdD6QdvlWQN7aJf0Q vklF
XWxEJe/IpUMZfMZx24MCQDu19hNYYg8863mvGbc7jWAY1Apjx1i/KTXe/6rBjmoS
bxoSEpKNHpW6dgL/6S6WQuB8j3tNUUNj5O99cU6DLsM=
-----END RSA PRIVATE KEY-----算法
$ openssl req -new -key private.pem -out rsacert.csr
Country Name (2 letter code) [AU]:CN State or Province Name (full name) [Some-State]:beijing Locality Name (eg, city) []:beijing Organization Name (eg, company) [Internet Widgits Pty Ltd]:Erma Organizational Unit Name (eg, section) []:com Common Name (e.g. server FQDN or YOUR name) []:Erma Email Address []:mr_wangyaojie@163.com Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
$ openssl x509 -req -days 3650 -in rsacert.csr -signkey private.pem -out rsacert.crt
$ openssl x509 -outform der -in rsacert.crt -out rsacert.der
$ openssl pkcs12 -export -out p.p12 -inkey private.pem -in rsacert.crt
CryptorTools *tool = [[CryptorTools alloc] init];
NSString *pubPath = [[NSBundle mainBundle] pathForResource:@"rsacert.der" ofType:nil]; [tool loadPublicKeyWithFilePath:pubPath];
NSString *result = [tool RSAEncryptString:@"xiaoer"]; NSLog(@"%@",result);
NSString *privatePath = [[NSBundle mainBundle] pathForResource:@"p.p12" ofType:nil]; [tool loadPrivateKey:privatePath password:@"xyz147896321"];
NSLog(@"%@", [tool RSADecryptString:result]);
// // RSA.h // // Created by Erma on 15-2-3. // Copyright (c) 2015年 Erma. All rights reserved. // #import <Foundation/Foundation.h> @interface RSA : NSObject + (NSString *)encryptString:(NSString *)str publicKey:(NSString *)pubKey; + (NSString *)encryptData:(NSData *)data publicKey:(NSString *)pubKey; @end
#import "RSA.h" #import <Security/Security.h> @implementation RSA /* static NSString *base64_encode(NSString *str){ NSData* data = [str dataUsingEncoding:NSUTF8StringEncoding]; if(!data){ return nil; } return base64_encode_data(data); } */ static NSString *base64_encode_data(NSData *data){ data = [data base64EncodedDataWithOptions:0]; NSString *ret = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; return ret; } static NSData *base64_decode(NSString *str){ NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters]; return data; } + (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)]; } + (SecKeyRef)addPublicKey:(NSString *)key{ NSRange spos = [key rangeOfString:@"-----BEGIN PUBLIC KEY-----"]; NSRange epos = [key rangeOfString:@"-----END 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:@""]; // This will be base64 encoded, decode it. NSData *data = base64_decode(key); data = [RSA 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)addPrivateKey:(NSString *)key{ NSRange spos; NSRange epos; spos = [key rangeOfString:@"-----BEGIN RSA PRIVATE KEY-----"]; if(spos.length > 0){ epos = [key rangeOfString:@"-----END RSA PRIVATE KEY-----"]; }else{ spos = [key rangeOfString:@"-----BEGIN PRIVATE KEY-----"]; epos = [key rangeOfString:@"-----END 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 = base64_decode(key); data = [RSA 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; } /* START: Encryption & Decryption with RSA private key */ + (NSData *)encryptData:(NSData *)data withKeyRef:(SecKeyRef) keyRef{ const uint8_t *srcbuf = (const uint8_t *)[data bytes]; size_t srclen = (size_t)data.length; size_t block_size = SecKeyGetBlockSize(keyRef) * sizeof(uint8_t); void *outbuf = malloc(block_size); size_t src_block_size = block_size - 11; NSMutableData *ret = [[NSMutableData alloc] init]; for(int idx=0; idx<srclen; idx+=src_block_size){ //NSLog(@"%d/%d block_size: %d", idx, (int)srclen, (int)block_size); size_t data_len = srclen - idx; if(data_len > src_block_size){ data_len = src_block_size; } size_t outlen = block_size; OSStatus status = noErr; status = SecKeyEncrypt(keyRef, kSecPaddingPKCS1, srcbuf + idx, data_len, outbuf, &outlen ); if (status != 0) { NSLog(@"SecKeyEncrypt fail. Error Code: %d", status); ret = nil; break; }else{ [ret appendBytes:outbuf length:outlen]; } } free(outbuf); CFRelease(keyRef); return ret; } + (NSString *)encryptString:(NSString *)str privateKey:(NSString *)privKey{ NSData *data = [RSA encryptData:[str dataUsingEncoding:NSUTF8StringEncoding] privateKey:privKey]; NSString *ret = base64_encode_data(data); return ret; } + (NSData *)encryptData:(NSData *)data privateKey:(NSString *)privKey{ if(!data || !privKey){ return nil; } SecKeyRef keyRef = [RSA addPrivateKey:privKey]; if(!keyRef){ return nil; } return [RSA encryptData:data withKeyRef:keyRef]; } + (NSData *)decryptData:(NSData *)data withKeyRef:(SecKeyRef) keyRef{ const uint8_t *srcbuf = (const uint8_t *)[data bytes]; size_t srclen = (size_t)data.length; size_t block_size = SecKeyGetBlockSize(keyRef) * sizeof(uint8_t); UInt8 *outbuf = malloc(block_size); size_t src_block_size = block_size; NSMutableData *ret = [[NSMutableData alloc] init]; for(int idx=0; idx<srclen; idx+=src_block_size){ //NSLog(@"%d/%d block_size: %d", idx, (int)srclen, (int)block_size); size_t data_len = srclen - idx; if(data_len > src_block_size){ data_len = src_block_size; } size_t outlen = block_size; OSStatus status = noErr; status = SecKeyDecrypt(keyRef, kSecPaddingNone, srcbuf + idx, data_len, outbuf, &outlen ); if (status != 0) { NSLog(@"SecKeyEncrypt fail. Error Code: %d", status); ret = nil; break; }else{ //the actual decrypted data is in the middle, locate it! int idxFirstZero = -1; int idxNextZero = (int)outlen; for ( int i = 0; i < outlen; i++ ) { if ( outbuf[i] == 0 ) { if ( idxFirstZero < 0 ) { idxFirstZero = i; } else { idxNextZero = i; break; } } } [ret appendBytes:&outbuf[idxFirstZero+1] length:idxNextZero-idxFirstZero-1]; } } free(outbuf); CFRelease(keyRef); return ret; } + (NSString *)decryptString:(NSString *)str privateKey:(NSString *)privKey{ NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters]; data = [RSA decryptData:data privateKey:privKey]; NSString *ret = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; return ret; } + (NSData *)decryptData:(NSData *)data privateKey:(NSString *)privKey{ if(!data || !privKey){ return nil; } SecKeyRef keyRef = [RSA addPrivateKey:privKey]; if(!keyRef){ return nil; } return [RSA decryptData:data withKeyRef:keyRef]; } /* END: Encryption & Decryption with RSA private key */ /* START: Encryption & Decryption with RSA public key */ + (NSString *)encryptString:(NSString *)str publicKey:(NSString *)pubKey{ NSData *data = [RSA encryptData:[str dataUsingEncoding:NSUTF8StringEncoding] publicKey:pubKey]; NSString *ret = base64_encode_data(data); return ret; } + (NSData *)encryptData:(NSData *)data publicKey:(NSString *)pubKey{ if(!data || !pubKey){ return nil; } SecKeyRef keyRef = [RSA addPublicKey:pubKey]; if(!keyRef){ return nil; } return [RSA encryptData:data withKeyRef:keyRef]; } + (NSString *)decryptString:(NSString *)str publicKey:(NSString *)pubKey{ NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters]; data = [RSA decryptData:data publicKey:pubKey]; NSString *ret = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; return ret; } + (NSData *)decryptData:(NSData *)data publicKey:(NSString *)pubKey{ if(!data || !pubKey){ return nil; } SecKeyRef keyRef = [RSA addPublicKey:pubKey]; if(!keyRef){ return nil; } return [RSA decryptData:data withKeyRef:keyRef]; } /* END: Encryption & Decryption with RSA public key */ @end
NSString *publicKey = @"YourPublicKey";
NSString *name = [RSA encryptString:@"你的帐号" publicKey:publicKey]; NSString *password = [RSA encryptString:@"你的密码" publicKey:publicKey];
NSLog(@"%@",name); NSLog(@"%@",password);
<!DOCTYPE html> <html> <head> <title>Base-64 字符数组或字符串的长度无效。</title> <meta name="viewport" content="width=device-width" /> <style> body {font-family:"Verdana";font-weight:normal;font-size: .7em;color:black;} p {font-family:"Verdana";font-weight:normal;color:black;margin-top: -5px} b {font-family:"Verdana";font-weight:bold;color:black;margin-top: -5px} H1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red } H2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maroon } pre {font-family:"Consolas","Lucida Console",Monospace;font-size:11pt;margin:0;padding:0.5em;line-height:14pt} .marker {font-weight: bold; color: black;text-decoration: none;} .version {color: gray;} .error {margin-bottom: 10px;} .expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:hand; } @media screen and (max-width: 639px) { pre { width: 440px; overflow: auto; white-space: pre-wrap; word-wrap: break-word; } } @media screen and (max-width: 479px) { pre { width: 280px; } } </style> </head> <body bgcolor="white"> <span><H1>“/”应用程序中的服务器错误。<hr width=100% size=1 color=silver></H1> <h2> <i>Base-64 字符数组或字符串的长度无效。</i> </h2></span> <font face="Arial, Helvetica, Geneva, SunSans-Regular, sans-serif "> <b> 说明: </b>执行当前 Web 请求期间,出现未经处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中致使错误的出处的详细信息。 <br><br> <b> 异常详细信息: </b>System.FormatException: Base-64 字符数组或字符串的长度无效。<br><br> <b>源错误:</b> <br><br> <table width=100% bgcolor="#ffffcc"> <tr> <td> <code> 执行当前 Web 请求期间生成了未经处理的异常。可使用下面的异常堆栈跟踪信息肯定有关异常缘由和发生位置的信息。</code> </td> </tr> </table> <br> <b>堆栈跟踪:</b> <br><br> <table width=100% bgcolor="#ffffcc"> <tr> <td> <code><pre> [FormatException: Base-64 字符数组或字符串的长度无效。] System.Convert.FromBase64_Decode(Char* startInputPtr, Int32 inputLength, Byte* startDestPtr, Int32 destLength) +307 System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength) +152 System.Convert.FromBase64String(String s) +49 CNBlogs.Infrastructure.Common.RSACryptoService.Decrypt(String cipherText) +40 OpenAPI.Providers.<GrantResourceOwnerCredentials>d__5.MoveNext() +412 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61 Microsoft.Owin.Security.OAuth.<InvokeTokenEndpointResourceOwnerPasswordCredentialsGrantA sync>d__3f.MoveNext() +700
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task) +13848037
Microsoft.Owin.Security.OAuth.<InvokeTokenEndpointAsync>d__22.MoveNext() +1933
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
Microsoft.Owin.Security.OAuth.<InvokeAsync>d__0.MoveNext() +1211
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
Microsoft.Owin.Security.Infrastructure.<Invoke>d__0.MoveNext() +540
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.<RunApp>d__5.MoveNext() +203
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.<DoFinalWork>d__2.MoveNext() +193
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar) +96
System.Web.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +363
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +137
数组
</td> </tr> </table> <br> <hr width=100% size=1 color=silver> <b>版本信息:</b> Microsoft .NET Framework 版本:4.0.30319; ASP.NET 版本:4.6.1055.0 </font> </body> </html>
DES解密时“Base-64字符数组的无效长度”
问题是 在页面传送的时候加密了 ,而后解密出来就抛出异常 跟踪发现是 ++ 在解析REQUEST的时候变成了空格服务器
使用String.Replace("+", "%2B")先将空格编码,而后再做为参数传给另外一页面传递,这样页面在提取参数时才会将“%2B”解码为加号app
下面是一个相关的知识编辑器
在使用Convert.ToBase64String()对字符串进行Base64编码时,注意的几点:
例:string s = "Hello";
byte[] bytes = Convert.FromBase64String(s);
以上代码在运行时会抛出FormatException异常.提示为:Base-64字符数组的无效长度ide
缘由:
当Convert.FromBase64String方法的参数s的长度小于4或不是4的偶数倍时,将会抛出FormatException。工具
例:
Convert.FromBase64String("Hell"); // Normal.
Convert.FromBase64String("Hell "); // Normal.(忽略空格)
Convert.FromBase64String("Hello!"); // throw FormatException.
Convert.FromBase64String("Hello Net"); // Normal.(忽略空格)
name = [name stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"]; password = [password stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"];