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"];