AES/CBC/128/PKCS5Padding加密解密算法(iOS、Android、JavaScript、PHP)
 visitors

最近项目中考虑到用户账户数据的安全性问题,需要对用户账户相关信息进行加密解密。这里我们选择使用AES加解密,至于AES相比其他对称加密算法的优缺点就不再详述,当然加解密过程中还使用了一些其他的算法,比如混合MD5。这边文章主要记录下Objective-C、Java、JavaScript、PHP四种语言的AES/CBC/128/PKCS5Padding的加解密算法,下面直接上代码。

Node.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var crypto = require('crypto');
var data = "test";
var key = '7854156156611111';
//data 是准备加密的字符串,key是你的密钥
function encryption(data, key) {
var iv = "0000000000000000";
var clearEncoding = 'utf8';
var cipherEncoding = 'base64';
var cipherChunks = [];
var cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
cipher.setAutoPadding(true);
cipherChunks.push(cipher.update(data, clearEncoding, cipherEncoding));
cipherChunks.push(cipher.final(cipherEncoding));
return cipherChunks.join('');
}
//data 是你的准备解密的字符串,key是你的密钥
function decryption(data, key) {
var iv = "0000000000000000";
var clearEncoding = 'utf8';
var cipherEncoding = 'base64';
var cipherChunks = [];
var decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
decipher.setAutoPadding(true);
cipherChunks.push(decipher.update(data, cipherEncoding, clearEncoding));
cipherChunks.push(decipher.final(clearEncoding));
return cipherChunks.join('');
}
console.log(encryption(data, key)) ;

PHP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$privateKey = "7854156156611111";
$iv = "0000000000000000";
$data = "test";
echo($privateKey."\n");
//加密
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $privateKey, $data, MCRYPT_MODE_CBC, $iv);
echo(base64_encode($encrypted)."\n");

//解密
$encryptedData = base64_decode("L7AswKt5/t1gND4ct22Odw==");
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $privateKey, $encryptedData, MCRYPT_MODE_CBC, $iv);
echo($decrypted);
?>

这里的PHP版本是7.0.22,需要引入依赖sudo apt-get install php7-mcrypt

Android

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package org.learn;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import java.util.Base64;

public class Encryptor {
public static String encrypt(String key, String initVector, String value) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);

byte[] encrypted = cipher.doFinal(value.getBytes());
System.out.println("encrypted string: " + Base64.getEncoder().encodeToString(encrypted));

return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception ex) {
ex.printStackTrace();
}

return null;
}

public static String decrypt(String key, String initVector, String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);

byte[] original = cipher.doFinal(Base64.getDecoder().decode(encrypted));

return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}

return null;
}

public static void main(String[] args) throws Exception {

String key = "7854156156611111";
String initVector = "0000000000000000"; // 16 bytes IV

System.out.println(decrypt(key, initVector, encrypt(key, initVector, "test")));

}
}

iOS

iOS客户端的加密解密相对复杂一些,使用Objective-C语言编写,一共5个文件。

GTMDefines.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
//
// GTMDefines.h
//
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
//

// ============================================================================

#include <AvailabilityMacros.h>

// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs
#ifndef MAC_OS_X_VERSION_10_5
#define MAC_OS_X_VERSION_10_5 1050
#endif
#ifndef MAC_OS_X_VERSION_10_6
#define MAC_OS_X_VERSION_10_6 1060
#endif

// ----------------------------------------------------------------------------
// CPP symbols that can be overridden in a prefix to control how the toolbox
// is compiled.
// ----------------------------------------------------------------------------


// GTMHTTPFetcher will support logging by default but only hook its input
// stream support for logging when requested. You can control the inclusion of
// the code by providing your own definitions for these w/in a prefix header.
//
#ifndef GTM_HTTPFETCHER_ENABLE_LOGGING
#define GTM_HTTPFETCHER_ENABLE_LOGGING 1
#endif // GTM_HTTPFETCHER_ENABLE_LOGGING
#ifndef GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING
#define GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING 0
#endif // GTM_HTTPFETCHER_ENABLE_INPUTSTREAM_LOGGING

// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and
// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens
// when a validation fails. If you implement your own validators, you may want
// to control their internals using the same macros for consistency.
#ifndef GTM_CONTAINERS_VALIDATION_FAILED_ASSERT
#define GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 0
#endif

// Give ourselves a consistent way to do inlines. Apple's macros even use
// a few different actual definitions, so we're based off of the foundation
// one.
#if !defined(GTM_INLINE)
#if defined (__GNUC__) && (__GNUC__ == 4)
#define GTM_INLINE static __inline__ __attribute__((always_inline))
#else
#define GTM_INLINE static __inline__
#endif
#endif

// Give ourselves a consistent way of doing externs that links up nicely
// when mixing objc and objc++
#if !defined (GTM_EXTERN)
#if defined __cplusplus
#define GTM_EXTERN extern "C"
#else
#define GTM_EXTERN extern
#endif
#endif

// Give ourselves a consistent way of exporting things if we have visibility
// set to hidden.
#if !defined (GTM_EXPORT)
#define GTM_EXPORT __attribute__((visibility("default")))
#endif

// _GTMDevLog & _GTMDevAssert
//
// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for
// developer level errors. This implementation simply macros to NSLog/NSAssert.
// It is not intended to be a general logging/reporting system.
//
// Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert
// for a little more background on the usage of these macros.
//
// _GTMDevLog log some error/problem in debug builds
// _GTMDevAssert assert if conditon isn't met w/in a method/function
// in all builds.
//
// To replace this system, just provide different macro definitions in your
// prefix header. Remember, any implementation you provide *must* be thread
// safe since this could be called by anything in what ever situtation it has
// been placed in.
//

// We only define the simple macros if nothing else has defined this.
#ifndef _GTMDevLog

#ifdef DEBUG
#define _GTMDevLog(...) NSLog(__VA_ARGS__)
#else
#define _GTMDevLog(...) do { } while (0)
#endif

#endif // _GTMDevLog

// Declared here so that it can easily be used for logging tracking if
// necessary. See GTMUnitTestDevLog.h for details.
@class NSString;
GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);

#ifndef _GTMDevAssert
// we directly invoke the NSAssert handler so we can pass on the varargs
// (NSAssert doesn't have a macro we can use that takes varargs)
#if !defined(NS_BLOCK_ASSERTIONS)
#define _GTMDevAssert(condition, ...) \
do { \
if (!(condition)) { \
[[NSAssertionHandler currentHandler] \
handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \
file:[NSString stringWithUTF8String:__FILE__] \
lineNumber:__LINE__ \
description:__VA_ARGS__]; \
} \
} while(0)
#else // !defined(NS_BLOCK_ASSERTIONS)
#define _GTMDevAssert(condition, ...) do { } while (0)
#endif // !defined(NS_BLOCK_ASSERTIONS)

#endif // _GTMDevAssert

// _GTMCompileAssert
// _GTMCompileAssert is an assert that is meant to fire at compile time if you
// want to check things at compile instead of runtime. For example if you
// want to check that a wchar is 4 bytes instead of 2 you would use
// _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X)
// Note that the second "arg" is not in quotes, and must be a valid processor
// symbol in it's own right (no spaces, punctuation etc).

// Wrapping this in an #ifndef allows external groups to define their own
// compile time assert scheme.
#ifndef _GTMCompileAssert
// We got this technique from here:
// http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html

#define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg
#define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg)
#define _GTMCompileAssert(test, msg) \
typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
#endif // _GTMCompileAssert

// Macro to allow fast enumeration when building for 10.5 or later, and
// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration
// does keys, so pick the right thing, nothing is done on the FastEnumeration
// side to be sure you're getting what you wanted.
#ifndef GTM_FOREACH_OBJECT
#if defined(TARGET_OS_IPHONE) || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
#define GTM_FOREACH_OBJECT(element, collection) \
for (element in collection)
#define GTM_FOREACH_KEY(element, collection) \
for (element in collection)
#else
#define GTM_FOREACH_OBJECT(element, collection) \
for (NSEnumerator * _ ## element ## _enum = [collection objectEnumerator]; \
(element = [_ ## element ## _enum nextObject]) != nil; )
#define GTM_FOREACH_KEY(element, collection) \
for (NSEnumerator * _ ## element ## _enum = [collection keyEnumerator]; \
(element = [_ ## element ## _enum nextObject]) != nil; )
#endif
#endif

// ============================================================================

// ----------------------------------------------------------------------------
// CPP symbols defined based on the project settings so the GTM code has
// simple things to test against w/o scattering the knowledge of project
// setting through all the code.
// ----------------------------------------------------------------------------

// Provide a single constant CPP symbol that all of GTM uses for ifdefing
// iPhone code.
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE // iPhone SDK
// For iPhone specific stuff
#define GTM_IPHONE_SDK 1
#if TARGET_IPHONE_SIMULATOR
#define GTM_IPHONE_SIMULATOR 1
#else
#define GTM_IPHONE_DEVICE 1
#endif // TARGET_IPHONE_SIMULATOR
#else
// For MacOS specific stuff
#define GTM_MACOS_SDK 1
#endif

// Provide a symbol to include/exclude extra code for GC support. (This mainly
// just controls the inclusion of finalize methods).
#ifndef GTM_SUPPORT_GC
#if GTM_IPHONE_SDK
// iPhone never needs GC
#define GTM_SUPPORT_GC 0
#else
// We can't find a symbol to tell if GC is supported/required, so best we
// do on Mac targets is include it if we're on 10.5 or later.
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
#define GTM_SUPPORT_GC 0
#else
#define GTM_SUPPORT_GC 1
#endif
#endif
#endif

// To simplify support for 64bit (and Leopard in general), we provide the type
// defines for non Leopard SDKs
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
// NSInteger/NSUInteger and Max/Mins
#ifndef NSINTEGER_DEFINED
#if __LP64__ || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif
#define NSIntegerMax LONG_MAX
#define NSIntegerMin LONG_MIN
#define NSUIntegerMax ULONG_MAX
#define NSINTEGER_DEFINED 1
#endif // NSINTEGER_DEFINED
// CGFloat
#ifndef CGFLOAT_DEFINED
#if defined(__LP64__) && __LP64__
// This really is an untested path (64bit on Tiger?)
typedef double CGFloat;
#define CGFLOAT_MIN DBL_MIN
#define CGFLOAT_MAX DBL_MAX
#define CGFLOAT_IS_DOUBLE 1
#else /* !defined(__LP64__) || !__LP64__ */
typedef float CGFloat;
#define CGFLOAT_MIN FLT_MIN
#define CGFLOAT_MAX FLT_MAX
#define CGFLOAT_IS_DOUBLE 0
#endif /* !defined(__LP64__) || !__LP64__ */
#define CGFLOAT_DEFINED 1
#endif // CGFLOAT_DEFINED
#endif // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4

GTMBase64.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
//
// GTMBase64.h
//
// Copyright 2006-2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
//

#import <Foundation/Foundation.h>
#import "GTMDefines.h"

// GTMBase64
//
/// Helper for handling Base64 and WebSafeBase64 encodings
//
/// The webSafe methods use different character set and also the results aren't
/// always padded to a multiple of 4 characters. This is done so the resulting
/// data can be used in urls and url query arguments without needing any
/// encoding. You must use the webSafe* methods together, the data does not
/// interop with the RFC methods.
//
@interface GTMBase64 : NSObject

//
// Standard Base64 (RFC) handling
//

// encodeData:
//
/// Base64 encodes contents of the NSData object.
//
/// Returns:
/// A new autoreleased NSData with the encoded payload. nil for any error.
//
+(NSData *)encodeData:(NSData *)data;

// decodeData:
//
/// Base64 decodes contents of the NSData object.
//
/// Returns:
/// A new autoreleased NSData with the decoded payload. nil for any error.
//
+(NSData *)decodeData:(NSData *)data;

// encodeBytes:length:
//
/// Base64 encodes the data pointed at by |bytes|.
//
/// Returns:
/// A new autoreleased NSData with the encoded payload. nil for any error.
//
+(NSData *)encodeBytes:(const void *)bytes length:(NSUInteger)length;

// decodeBytes:length:
//
/// Base64 decodes the data pointed at by |bytes|.
//
/// Returns:
/// A new autoreleased NSData with the encoded payload. nil for any error.
//
+(NSData *)decodeBytes:(const void *)bytes length:(NSUInteger)length;

// stringByEncodingData:
//
/// Base64 encodes contents of the NSData object.
//
/// Returns:
/// A new autoreleased NSString with the encoded payload. nil for any error.
//
+(NSString *)stringByEncodingData:(NSData *)data;

// stringByEncodingBytes:length:
//
/// Base64 encodes the data pointed at by |bytes|.
//
/// Returns:
/// A new autoreleased NSString with the encoded payload. nil for any error.
//
+(NSString *)stringByEncodingBytes:(const void *)bytes length:(NSUInteger)length;

// decodeString:
//
/// Base64 decodes contents of the NSString.
//
/// Returns:
/// A new autoreleased NSData with the decoded payload. nil for any error.
//
+(NSData *)decodeString:(NSString *)string;

//
// Modified Base64 encoding so the results can go onto urls.
//
// The changes are in the characters generated and also allows the result to
// not be padded to a multiple of 4.
// Must use the matching call to encode/decode, won't interop with the
// RFC versions.
//

// webSafeEncodeData:padded:
//
/// WebSafe Base64 encodes contents of the NSData object. If |padded| is YES
/// then padding characters are added so the result length is a multiple of 4.
//
/// Returns:
/// A new autoreleased NSData with the encoded payload. nil for any error.
//
+(NSData *)webSafeEncodeData:(NSData *)data
padded:(BOOL)padded;

// webSafeDecodeData:
//
/// WebSafe Base64 decodes contents of the NSData object.
//
/// Returns:
/// A new autoreleased NSData with the decoded payload. nil for any error.
//
+(NSData *)webSafeDecodeData:(NSData *)data;

// webSafeEncodeBytes:length:padded:
//
/// WebSafe Base64 encodes the data pointed at by |bytes|. If |padded| is YES
/// then padding characters are added so the result length is a multiple of 4.
//
/// Returns:
/// A new autoreleased NSData with the encoded payload. nil for any error.
//
+(NSData *)webSafeEncodeBytes:(const void *)bytes
length:(NSUInteger)length
padded:(BOOL)padded;

// webSafeDecodeBytes:length:
//
/// WebSafe Base64 decodes the data pointed at by |bytes|.
//
/// Returns:
/// A new autoreleased NSData with the encoded payload. nil for any error.
//
+(NSData *)webSafeDecodeBytes:(const void *)bytes length:(NSUInteger)length;

// stringByWebSafeEncodingData:padded:
//
/// WebSafe Base64 encodes contents of the NSData object. If |padded| is YES
/// then padding characters are added so the result length is a multiple of 4.
//
/// Returns:
/// A new autoreleased NSString with the encoded payload. nil for any error.
//
+(NSString *)stringByWebSafeEncodingData:(NSData *)data
padded:(BOOL)padded;

// stringByWebSafeEncodingBytes:length:padded:
//
/// WebSafe Base64 encodes the data pointed at by |bytes|. If |padded| is YES
/// then padding characters are added so the result length is a multiple of 4.
//
/// Returns:
/// A new autoreleased NSString with the encoded payload. nil for any error.
//
+(NSString *)stringByWebSafeEncodingBytes:(const void *)bytes
length:(NSUInteger)length
padded:(BOOL)padded;

// webSafeDecodeString:
//
/// WebSafe Base64 decodes contents of the NSString.
//
/// Returns:
/// A new autoreleased NSData with the decoded payload. nil for any error.
//
+(NSData *)webSafeDecodeString:(NSString *)string;

@end

GTMBase64.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695

//
// GTMBase64.m
//
// Copyright 2006-2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
//

#import "GTMBase64.h"
#import "GTMDefines.h"

static const char *kBase64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char *kWebSafeBase64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
static const char kBase64PaddingChar = '=';
static const char kBase64InvalidChar = 99;

static const char kBase64DecodeChars[] = {
// This array was generated by the following code:
// #include <sys/time.h>
// #include <stdlib.h>
// #include <string.h>
// main()
// {
// static const char Base64[] =
// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// char *pos;
// int idx, i, j;
// printf(" ");
// for (i = 0; i < 255; i += 8) {
// for (j = i; j < i + 8; j++) {
// pos = strchr(Base64, j);
// if ((pos == NULL) || (j == 0))
// idx = 99;
// else
// idx = pos - Base64;
// if (idx == 99)
// printf(" %2d, ", idx);
// else
// printf(" %2d/*%c*/,", idx, j);
// }
// printf("\n ");
// }
// }
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 62/*+*/, 99, 99, 99, 63/*/ */,
52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
60/*8*/, 61/*9*/, 99, 99, 99, 99, 99, 99,
99, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/,
7/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
23/*X*/, 24/*Y*/, 25/*Z*/, 99, 99, 99, 99, 99,
99, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
49/*x*/, 50/*y*/, 51/*z*/, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99
};

static const char kWebSafeBase64DecodeChars[] = {
// This array was generated by the following code:
// #include <sys/time.h>
// #include <stdlib.h>
// #include <string.h>
// main()
// {
// static const char Base64[] =
// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
// char *pos;
// int idx, i, j;
// printf(" ");
// for (i = 0; i < 255; i += 8) {
// for (j = i; j < i + 8; j++) {
// pos = strchr(Base64, j);
// if ((pos == NULL) || (j == 0))
// idx = 99;
// else
// idx = pos - Base64;
// if (idx == 99)
// printf(" %2d, ", idx);
// else
// printf(" %2d/*%c*/,", idx, j);
// }
// printf("\n ");
// }
// }
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 62/*-*/, 99, 99,
52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
60/*8*/, 61/*9*/, 99, 99, 99, 99, 99, 99,
99, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/,
7/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
23/*X*/, 24/*Y*/, 25/*Z*/, 99, 99, 99, 99, 63/*_*/,
99, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
49/*x*/, 50/*y*/, 51/*z*/, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99
};


// Tests a character to see if it's a whitespace character.
//
// Returns:
// YES if the character is a whitespace character.
// NO if the character is not a whitespace character.
//
GTM_INLINE BOOL IsSpace(unsigned char c) {
// we use our own mapping here because we don't want anything w/ locale
// support.
static BOOL kSpaces[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // 0-9
1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 10-19
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20-29
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 30-39
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40-49
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 50-59
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60-69
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 70-79
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80-89
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90-99
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 100-109
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 110-119
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 120-129
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 130-139
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 140-149
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 150-159
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160-169
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 170-179
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 180-189
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 190-199
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 200-209
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 210-219
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 220-229
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 230-239
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 240-249
0, 0, 0, 0, 0, 1, // 250-255
};
return kSpaces[c];
}

// Calculate how long the data will be once it's base64 encoded.
//
// Returns:
// The guessed encoded length for a source length
//
GTM_INLINE NSUInteger CalcEncodedLength(NSUInteger srcLen, BOOL padded) {
NSUInteger intermediate_result = 8 * srcLen + 5;
NSUInteger len = intermediate_result / 6;
if (padded) {
len = ((len + 3) / 4) * 4;
}
return len;
}

// Tries to calculate how long the data will be once it's base64 decoded.
// Unlike the above, this is always an upperbound, since the source data
// could have spaces and might end with the padding characters on them.
//
// Returns:
// The guessed decoded length for a source length
//
GTM_INLINE NSUInteger GuessDecodedLength(NSUInteger srcLen) {
return (srcLen + 3) / 4 * 3;
}


@interface GTMBase64 (PrivateMethods)

+(NSData *)baseEncode:(const void *)bytes
length:(NSUInteger)length
charset:(const char *)charset
padded:(BOOL)padded;

+(NSData *)baseDecode:(const void *)bytes
length:(NSUInteger)length
charset:(const char*)charset
requirePadding:(BOOL)requirePadding;

+(NSUInteger)baseEncode:(const char *)srcBytes
srcLen:(NSUInteger)srcLen
destBytes:(char *)destBytes
destLen:(NSUInteger)destLen
charset:(const char *)charset
padded:(BOOL)padded;

+(NSUInteger)baseDecode:(const char *)srcBytes
srcLen:(NSUInteger)srcLen
destBytes:(char *)destBytes
destLen:(NSUInteger)destLen
charset:(const char *)charset
requirePadding:(BOOL)requirePadding;

@end


@implementation GTMBase64

//
// Standard Base64 (RFC) handling
//

+(NSData *)encodeData:(NSData *)data {
return [self baseEncode:[data bytes]
length:[data length]
charset:kBase64EncodeChars
padded:YES];
}

+(NSData *)decodeData:(NSData *)data {
return [self baseDecode:[data bytes]
length:[data length]
charset:kBase64DecodeChars
requirePadding:YES];
}

+(NSData *)encodeBytes:(const void *)bytes length:(NSUInteger)length {
return [self baseEncode:bytes
length:length
charset:kBase64EncodeChars
padded:YES];
}

+(NSData *)decodeBytes:(const void *)bytes length:(NSUInteger)length {
return [self baseDecode:bytes
length:length
charset:kBase64DecodeChars
requirePadding:YES];
}

+(NSString *)stringByEncodingData:(NSData *)data {
NSString *result = nil;
NSData *converted = [self baseEncode:[data bytes]
length:[data length]
charset:kBase64EncodeChars
padded:YES];
if (converted) {
result = [[NSString alloc] initWithData:converted
encoding:NSASCIIStringEncoding];
}
return result;
}

+(NSString *)stringByEncodingBytes:(const void *)bytes length:(NSUInteger)length {
NSString *result = nil;
NSData *converted = [self baseEncode:bytes
length:length
charset:kBase64EncodeChars
padded:YES];
if (converted) {
result = [[NSString alloc] initWithData:converted
encoding:NSASCIIStringEncoding];
}
return result;
}

+(NSData *)decodeString:(NSString *)string {
NSData *result = nil;
NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding];
if (data) {
result = [self baseDecode:[data bytes]
length:[data length]
charset:kBase64DecodeChars
requirePadding:YES];
}
return result;
}

//
// Modified Base64 encoding so the results can go onto urls.
//
// The changes are in the characters generated and also the result isn't
// padded to a multiple of 4.
// Must use the matching call to encode/decode, won't interop with the
// RFC versions.
//

+(NSData *)webSafeEncodeData:(NSData *)data
padded:(BOOL)padded {
return [self baseEncode:[data bytes]
length:[data length]
charset:kWebSafeBase64EncodeChars
padded:padded];
}

+(NSData *)webSafeDecodeData:(NSData *)data {
return [self baseDecode:[data bytes]
length:[data length]
charset:kWebSafeBase64DecodeChars
requirePadding:NO];
}

+(NSData *)webSafeEncodeBytes:(const void *)bytes
length:(NSUInteger)length
padded:(BOOL)padded {
return [self baseEncode:bytes
length:length
charset:kWebSafeBase64EncodeChars
padded:padded];
}

+(NSData *)webSafeDecodeBytes:(const void *)bytes length:(NSUInteger)length {
return [self baseDecode:bytes
length:length
charset:kWebSafeBase64DecodeChars
requirePadding:NO];
}

+(NSString *)stringByWebSafeEncodingData:(NSData *)data
padded:(BOOL)padded {
NSString *result = nil;
NSData *converted = [self baseEncode:[data bytes]
length:[data length]
charset:kWebSafeBase64EncodeChars
padded:padded];
if (converted) {
result = [[NSString alloc] initWithData:converted
encoding:NSASCIIStringEncoding];
}
return result;
}

+(NSString *)stringByWebSafeEncodingBytes:(const void *)bytes
length:(NSUInteger)length
padded:(BOOL)padded {
NSString *result = nil;
NSData *converted = [self baseEncode:bytes
length:length
charset:kWebSafeBase64EncodeChars
padded:padded];
if (converted) {
result = [[NSString alloc] initWithData:converted
encoding:NSASCIIStringEncoding];
}
return result;
}

+(NSData *)webSafeDecodeString:(NSString *)string {
NSData *result = nil;
NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding];
if (data) {
result = [self baseDecode:[data bytes]
length:[data length]
charset:kWebSafeBase64DecodeChars
requirePadding:NO];
}
return result;
}

@end

@implementation GTMBase64 (PrivateMethods)

//
// baseEncode:length:charset:padded:
//
// Does the common lifting of creating the dest NSData. it creates & sizes the
// data for the results. |charset| is the characters to use for the encoding
// of the data. |padding| controls if the encoded data should be padded to a
// multiple of 4.
//
// Returns:
// an autorelease NSData with the encoded data, nil if any error.
//
+(NSData *)baseEncode:(const void *)bytes
length:(NSUInteger)length
charset:(const char *)charset
padded:(BOOL)padded {
// how big could it be?
NSUInteger maxLength = CalcEncodedLength(length, padded);
// make space
NSMutableData *result = [NSMutableData data];
[result setLength:maxLength];
// do it
NSUInteger finalLength = [self baseEncode:bytes
srcLen:length
destBytes:[result mutableBytes]
destLen:[result length]
charset:charset
padded:padded];
if (finalLength) {
_GTMDevAssert(finalLength == maxLength, @"how did we calc the length wrong?");
} else {
// shouldn't happen, this means we ran out of space
result = nil;
}
return result;
}

//
// baseDecode:length:charset:requirePadding:
//
// Does the common lifting of creating the dest NSData. it creates & sizes the
// data for the results. |charset| is the characters to use for the decoding
// of the data.
//
// Returns:
// an autorelease NSData with the decoded data, nil if any error.
//
//
+(NSData *)baseDecode:(const void *)bytes
length:(NSUInteger)length
charset:(const char *)charset
requirePadding:(BOOL)requirePadding {
// could try to calculate what it will end up as
NSUInteger maxLength = GuessDecodedLength(length);
// make space
NSMutableData *result = [NSMutableData data];
[result setLength:maxLength];
// do it
NSUInteger finalLength = [self baseDecode:bytes
srcLen:length
destBytes:[result mutableBytes]
destLen:[result length]
charset:charset
requirePadding:requirePadding];
if (finalLength) {
if (finalLength != maxLength) {
// resize down to how big it was
[result setLength:finalLength];
}
} else {
// either an error in the args, or we ran out of space
result = nil;
}
return result;
}

//
// baseEncode:srcLen:destBytes:destLen:charset:padded:
//
// Encodes the buffer into the larger. returns the length of the encoded
// data, or zero for an error.
// |charset| is the characters to use for the encoding
// |padded| tells if the result should be padded to a multiple of 4.
//
// Returns:
// the length of the encoded data. zero if any error.
//
+(NSUInteger)baseEncode:(const char *)srcBytes
srcLen:(NSUInteger)srcLen
destBytes:(char *)destBytes
destLen:(NSUInteger)destLen
charset:(const char *)charset
padded:(BOOL)padded {
if (!srcLen || !destLen || !srcBytes || !destBytes) {
return 0;
}

char *curDest = destBytes;
const unsigned char *curSrc = (const unsigned char *)(srcBytes);

// Three bytes of data encodes to four characters of cyphertext.
// So we can pump through three-byte chunks atomically.
while (srcLen > 2) {
// space?
_GTMDevAssert(destLen >= 4, @"our calc for encoded length was wrong");
curDest[0] = charset[curSrc[0] >> 2];
curDest[1] = charset[((curSrc[0] & 0x03) << 4) + (curSrc[1] >> 4)];
curDest[2] = charset[((curSrc[1] & 0x0f) << 2) + (curSrc[2] >> 6)];
curDest[3] = charset[curSrc[2] & 0x3f];

curDest += 4;
curSrc += 3;
srcLen -= 3;
destLen -= 4;
}

// now deal with the tail (<=2 bytes)
switch (srcLen) {
case 0:
// Nothing left; nothing more to do.
break;
case 1:
// One byte left: this encodes to two characters, and (optionally)
// two pad characters to round out the four-character cypherblock.
_GTMDevAssert(destLen >= 2, @"our calc for encoded length was wrong");
curDest[0] = charset[curSrc[0] >> 2];
curDest[1] = charset[(curSrc[0] & 0x03) << 4];
curDest += 2;
destLen -= 2;
if (padded) {
_GTMDevAssert(destLen >= 2, @"our calc for encoded length was wrong");
curDest[0] = kBase64PaddingChar;
curDest[1] = kBase64PaddingChar;
curDest += 2;
}
break;
case 2:
// Two bytes left: this encodes to three characters, and (optionally)
// one pad character to round out the four-character cypherblock.
_GTMDevAssert(destLen >= 3, @"our calc for encoded length was wrong");
curDest[0] = charset[curSrc[0] >> 2];
curDest[1] = charset[((curSrc[0] & 0x03) << 4) + (curSrc[1] >> 4)];
curDest[2] = charset[(curSrc[1] & 0x0f) << 2];
curDest += 3;
destLen -= 3;
if (padded) {
_GTMDevAssert(destLen >= 1, @"our calc for encoded length was wrong");
curDest[0] = kBase64PaddingChar;
curDest += 1;
}
break;
}
// return the length
return (curDest - destBytes);
}

//
// baseDecode:srcLen:destBytes:destLen:charset:requirePadding:
//
// Decodes the buffer into the larger. returns the length of the decoded
// data, or zero for an error.
// |charset| is the character decoding buffer to use
//
// Returns:
// the length of the encoded data. zero if any error.
//
+(NSUInteger)baseDecode:(const char *)srcBytes
srcLen:(NSUInteger)srcLen
destBytes:(char *)destBytes
destLen:(NSUInteger)destLen
charset:(const char *)charset
requirePadding:(BOOL)requirePadding {
if (!srcLen || !destLen || !srcBytes || !destBytes) {
return 0;
}

int decode;
NSUInteger destIndex = 0;
int state = 0;
char ch = 0;
while (srcLen-- && (ch = *srcBytes++) != 0) {
if (IsSpace(ch)) // Skip whitespace
continue;

if (ch == kBase64PaddingChar)
break;

decode = charset[(unsigned int)ch];
if (decode == kBase64InvalidChar)
return 0;

// Four cyphertext characters decode to three bytes.
// Therefore we can be in one of four states.
switch (state) {
case 0:
// We're at the beginning of a four-character cyphertext block.
// This sets the high six bits of the first byte of the
// plaintext block.
_GTMDevAssert(destIndex < destLen, @"our calc for decoded length was wrong");
destBytes[destIndex] = decode << 2;
state = 1;
break;
case 1:
// We're one character into a four-character cyphertext block.
// This sets the low two bits of the first plaintext byte,
// and the high four bits of the second plaintext byte.
_GTMDevAssert((destIndex+1) < destLen, @"our calc for decoded length was wrong");
destBytes[destIndex] |= decode >> 4;
destBytes[destIndex+1] = (decode & 0x0f) << 4;
destIndex++;
state = 2;
break;
case 2:
// We're two characters into a four-character cyphertext block.
// This sets the low four bits of the second plaintext
// byte, and the high two bits of the third plaintext byte.
// However, if this is the end of data, and those two
// bits are zero, it could be that those two bits are
// leftovers from the encoding of data that had a length
// of two mod three.
_GTMDevAssert((destIndex+1) < destLen, @"our calc for decoded length was wrong");
destBytes[destIndex] |= decode >> 2;
destBytes[destIndex+1] = (decode & 0x03) << 6;
destIndex++;
state = 3;
break;
case 3:
// We're at the last character of a four-character cyphertext block.
// This sets the low six bits of the third plaintext byte.
_GTMDevAssert(destIndex < destLen, @"our calc for decoded length was wrong");
destBytes[destIndex] |= decode;
destIndex++;
state = 0;
break;
}
}

// We are done decoding Base-64 chars. Let's see if we ended
// on a byte boundary, and/or with erroneous trailing characters.
if (ch == kBase64PaddingChar) { // We got a pad char
if ((state == 0) || (state == 1)) {
return 0; // Invalid '=' in first or second position
}
if (srcLen == 0) {
if (state == 2) { // We run out of input but we still need another '='
return 0;
}
// Otherwise, we are in state 3 and only need this '='
} else {
if (state == 2) { // need another '='
while ((ch = *srcBytes++) && (srcLen-- > 0)) {
if (!IsSpace(ch))
break;
}
if (ch != kBase64PaddingChar) {
return 0;
}
}
// state = 1 or 2, check if all remain padding is space
while ((ch = *srcBytes++) && (srcLen-- > 0)) {
if (!IsSpace(ch)) {
return 0;
}
}
}
} else {
// We ended by seeing the end of the string.

if (requirePadding) {
// If we require padding, then anything but state 0 is an error.
if (state != 0) {
return 0;
}
} else {
// Make sure we have no partial bytes lying around. Note that we do not
// require trailing '=', so states 2 and 3 are okay too.
if (state == 1) {
return 0;
}
}
}

// If then next piece of output was valid and got written to it means we got a
// very carefully crafted input that appeared valid but contains some trailing
// bits past the real length, so just toss the thing.
if ((destIndex < destLen) &&
(destBytes[destIndex] != 0)) {
return 0;
}

return destIndex;
}

@end

NSData+AES.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//
// NSData+AES.h
// iOS_AES
//
// Created by FM-13 on 16/6/8.
// Copyright © 2016年 cong. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface NSData (AES)


//加密
- (NSData *)AES128EncryptWithKey:(NSString *)key iv:(NSString *)iv;


//解密
- (NSData *)AES128DecryptWithKey:(NSString *)key iv:(NSString *)iv;



@end

NSData+AES.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//
// NSData+AES.m
// iOS_AES
//
// Created by FM-13 on 16/6/8.
// Copyright © 2016年 cong. All rights reserved.
//

#import "NSData+AES.h"
#import <CommonCrypto/CommonCryptor.h>

@implementation NSData (AES)

//加密
- (NSData *)AES128EncryptWithKey:(NSString *)key iv:(NSString *)iv
{
return [self AES128operation:kCCEncrypt key:key iv:iv];
}


//解密
- (NSData *)AES128DecryptWithKey:(NSString *)key iv:(NSString *)iv
{
return [self AES128operation:kCCDecrypt key:key iv:iv];
}

- (NSData *)AES128operation:(CCOperation)operation key:(NSString *)key iv:(NSString *)iv
{
char keyPtr[kCCKeySizeAES128 + 1];
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

// IV
char ivPtr[kCCBlockSizeAES128 + 1];
bzero(ivPtr, sizeof(ivPtr));
[iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];

size_t bufferSize = [self length] + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;


CCCryptorStatus cryptorStatus = CCCrypt(operation, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128,
ivPtr,
[self bytes], [self length],
buffer, bufferSize,
&numBytesEncrypted);

if(cryptorStatus == kCCSuccess){
NSLog(@"Success");
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];

}else{
NSLog(@"Error");
}

free(buffer);
return nil;
}

@end

使用方法如下:

1
2
3
4
5
6
7
8
9
NSString *aesKey = @"7854156156611111";
NSString *aesIV = @"0000000000000000";

NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
//加密
data = [data AES128EncryptWithKey:aesKey iv:aesIV];
data = [GTMBase64 encodeData:data];

NSString *encodeStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];