###前提・実現したいこと
iOSアプリでwebサーバにPOST送信するアプリを作成しております。
POSTの際にAES256により暗号化(その後、base64でエンコード)を行なっているのですが、特定のデータで復元に失敗します。
(特定のデータで復元ができているため、暗号化キー、初期化ベクトルの設定は問題ないと思われる。)
確証はないのですが、文字コードの設定などが怪しいのではないかと考えております。
###発生している問題・エラーメッセージ
サーバ側(C# ASP.net、IIS8.0) Base-64 文字配列または文字列の長さが無効です。
###クライアント側(iOS)のソースコード
Objective
1- (void)postInputData:(NSData *)query 2{ 3 NSString *url = @"URL"; 4 5 //リクエストの種類、ヘッダを設定する 6 NSMutableURLRequest *request = [NSMutableURLRequest 7 requestWithURL: [NSURL URLWithString: url] 8 cachePolicy: NSURLRequestUseProtocolCachePolicy 9 timeoutInterval: TIMEOUT_SECOND]; 10 11 [request setHTTPMethod: @"POST"]; 12 [request setValue: @"application/x-www-form-urlencoded" forHTTPHeaderField: @"Content-Type"]; 13 [request setValue: [NSString stringWithFormat: @"%lu", (unsigned long)[query length]] forHTTPHeaderField: @"Content-Length"]; 14 [request setHTTPBody: query]; 15 16 //共有セッションを取得し、サーバにリクエストを行う 17 NSURLSession *session = [NSURLSession sharedSession]; 18 19 [[session dataTaskWithRequest: request completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error) { 20 21 //レスポンスを確認 22 NSString *responseString = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding]; 23 24 }] resume]; 25} 26 27- (NSData *) buildQuery:(NSData *)imageData 28{ 29 NSString *const FIRST_POST_FORMAT = @"=%@="; 30 NSString *const POST_FORMAT = @"%@="; 31 32 //項目名を追加 33 NSMutableArray *paramCodeArray = [NSMutableArray array]; 34 [paramCodeArray addObject:[NSString stringWithFormat:FIRST_POST_FORMAT, @“No”]]; 35 [paramCodeArray addObject:[NSString stringWithFormat:POST_FORMAT, @“name”]]; 36 37 //値を追加 38 NSMutableArray *paramValueArray = [NSMutableArray array]; 39 [paramValueArray addObject:@“1234567”]]; 40 [paramValueArray addObject:@“佐藤 一郎”]; 41 42 NSMutableData *query = [NSMutableData data]; 43 for (int paramNo = 0; paramNo < paramCodeArray.count; paramNo++) 44 { 45 //項目名(NSData)を追加 46 [query appendData:[self convertPostData:paramCodeArray[paramNo] 47 encyiptFlg:NO]]; 48 49 //値を追加 50 [query appendData:[self convertPostData:paramValueArray[paramNo] 51 encyiptFlg:YES]]; 52 53 if (paramNo != paramCodeArray.count - 1) 54 { 55 [query appendData:[self convertPostData:@"," encyiptFlg:NO]]; 56 } 57 } 58 59 return query; 60} 61 62- (NSData *)convertPostData: (NSString *)str encyiptFlg:(BOOL)encyiptFlg 63{ 64 NSData *query; 65 66 if (encyiptFlg == YES) 67 { 68 query = [str dataUsingEncoding: NSShiftJISStringEncoding]; 69 //①queryをAES256で暗号化 70 query = [query AES256EncryptWithKey:AES256KEY]; 71 72 //②queryをbase64に変換 73 query = [query base64EncodedDataWithOptions:NSDataBase64Encoding64CharacterLineLength]; 74 } 75 else 76 { 77 query = [str dataUsingEncoding: NSShiftJISStringEncoding]; 78 } 79 return query; 80} 81 82以下、NSDataへ追加しています 83- (NSData *)AES256EncryptWithKey:(NSString *)key { 84 // 'key' should be 32 bytes for AES256, will be null-padded otherwise 85 char keyPtr[kCCKeySizeAES256+1]; 86 bzero(keyPtr, sizeof(keyPtr)); 87 88 [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; 89 90 char ivPtr[kCCKeySizeAES128+1]; 91 bzero(ivPtr, sizeof(ivPtr)); 92 [IV getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding]; 93 94 95 NSUInteger dataLength = [self length]; 96 97 size_t bufferSize = dataLength + kCCBlockSizeAES128; 98 void *buffer = malloc(bufferSize); 99 100 size_t numBytesEncrypted = 0; 101 CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, 102 keyPtr, kCCKeySizeAES256,//kCCKeySizeAES256, 103 ivPtr /* initialization vector (optional) */, //NULL 104 [self bytes], dataLength, /* input */ 105 buffer, bufferSize, /* output */ 106 &numBytesEncrypted); 107 if (cryptStatus == kCCSuccess) { 108 return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted]; 109 } 110 111 free(buffer); //free the buffer; 112 return nil; 113}
###サーバ側(C# APS.net)のソースコード
C#
1 //ブロックサイズ 2 private const int aesBlockSize = 128; 3 //鍵長 4 private const int aesKeySize = 256; 5 //エンコーディング 6 private static Encoding aesEncoding = Encoding.GetEncoding("shift_jis"); 7 8 //POST 9 public string Post([FromBody]string value) 10 { 11 try 12 { 13 string text = string.Empty; 14 //バイト型配列に戻す 15 if (value != null && value != string.Empty) 16 { 17 // 取得した日付を表示する 18 string strDate = DateTime.Now.ToString("yyyyMMddHHmmss"); 19 string pathStr = @"D:\postTest" + strDate + ".txt"; 20 21 //テキストファイルに書き込み 22 using (StreamWriter writer = new StreamWriter(pathStr, true, System.Text.Encoding.GetEncoding("shift_jis"))) 23 { 24 //カンマ区切りで分割して配列に格納する 25 string[] postData = value.Split(','); 26 27 for (int i = 0; i < postData.Length; i++) 28 { 29 /*---------------POSTデータ分解処理---------------*/ 30 //「No=1234567」の形に取り出す 31 string oneReq = postData[i]; 32 //key(例:No)の部分を取り出す 33 string[] oneReqArray = oneReq.Split('='); 34 string key = oneReqArray[0]; 35 //keyを削除する 36 string reqValue = oneReq.Remove(0, key.Length + 1); 37 /*---------------POSTデータ分解処理---------------*/ 38 39 /*--------------aes256復元処理(base64のデコードも)--------------*/ 40 string hukugouStr = ValuesController.AesDecrypt(reqValue, Aes256Key); 41 /*--------------aes256復元処理--------------*/ 42 43 //復元した値を書込み 44 writer.WriteLine(key + " : " + hukugouStr); 45 } 46 } 47 return "OK"; 48 } 49 //値が空の場合、エラーとする。 50 else 51 { 52 // 取得した日付を表示する 53 string strDate2 = DateTime.Now.ToString("yyyyMMddHHmmss"); 54 string pathStr2 = @"D:\postTest" + strDate2 + ".txt"; 55 using (StreamWriter writer = new StreamWriter(pathStr2, true, System.Text.Encoding.UTF8)) 56 { 57 writer.WriteLine("空でした。"); 58 } 59 return "NG" 60 } 61 } 62 } 63 64 /// <summary> 65 /// 指定した鍵で復号化 66 /// </summary> 67 /// <param name="source">復号対象文字列</param> 68 /// <param name="key">鍵</param> 69 public static string AesDecrypt(string source, string key) 70 { 71 using (var provider = CreateAesProvider(key)) 72 { 73 var sourceBytes = Convert.FromBase64String(source); 74 using (var decryptor = provider.CreateDecryptor()) 75 { 76 var retBytes = decryptor.TransformFinalBlock(sourceBytes, 0, sourceBytes.Length); 77 return aesEncoding.GetString(retBytes); 78 } 79 } 80 } 81 82 /// <summary> 83 /// AESプロバイダー生成 84 /// </summary> 85 /// <param name="key">鍵</param> 86 private static AesCryptoServiceProvider CreateAesProvider(string key) 87 { 88 var provider = new AesCryptoServiceProvider(); 89 provider.BlockSize = aesBlockSize; 90 provider.KeySize = aesKeySize; 91 provider.IV = aesEncoding.GetBytes(aes256IV); 92 provider.Key = aesEncoding.GetBytes(key); 93 provider.Mode = CipherMode.CBC; 94 provider.Padding = PaddingMode.PKCS7; 95 return provider; 96 }
###試したこと
下記の事項は検証いたしました。
①暗号化なしでは正常に送信できること。
②Base64のエンコードのみでは正常に送信できること。
③AES256で暗号化、その後base64でエンコードした際、特定のデータでは送信できること。
※エラーはBase64から復元できないと書いてあります
###補足情報(言語/FW/ツール等のバージョンなど)
言語:クライアント側:Objective-c
サーバ側:C#、ASP.net
OS:クライアント側:iOS10.2
サーバ側:Windows Server 2012
クライアント側、サーバ側と両方関係があると思われるため、原因の特定ができずにいます。
些細な情報でも構いませんので、アドバイスを頂ければ幸いです。
よろしくお願いいたします。

回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/01/23 06:18
2017/01/23 07:10
2017/01/23 07:13