├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── goodl │ │ └── aes │ │ └── test │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── goodl │ │ │ └── aes │ │ │ └── test │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── goodl │ └── aes │ └── test │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jniLibrary ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── goodl │ │ └── aes │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── cpp │ │ ├── CMakeLists.txt │ │ ├── aes.c │ │ ├── aes.h │ │ ├── aes_utils.c │ │ ├── aes_utils.h │ │ ├── foo_tools.cpp │ │ ├── hex_utils.c │ │ ├── hex_utils.h │ │ ├── junk.h │ │ ├── tools.cpp │ │ └── tools.h │ ├── java │ │ └── com │ │ │ └── goodl │ │ │ └── aes │ │ │ └── FooTools.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── goodl │ └── aes │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | 15 | # Local configuration file (sdk path, etc) 16 | local.properties 17 | 18 | # Windows thumbnail db 19 | Thumbs.db 20 | 21 | # OSX files 22 | .DS_Store 23 | 24 | # Eclipse project files 25 | .classpath 26 | .project 27 | 28 | # Android Studio 29 | *.iml 30 | .idea 31 | #.idea/workspace.xml - remove # and delete .idea if it better suit your needs. 32 | .gradle 33 | build/ 34 | 35 | .settings/ 36 | .vscode/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 客户端数据进行加密保护还是很有必要的。 3 | 4 | 对Android来说,一般的方式有: 5 | - 在 java 代码里进行加密 6 | - 在 native 代码里进行加密 7 | 8 | 对于第一种,安全性不高,应用容易被反编译,看到代码逻辑。当然可以进行加固,但是也有脱壳工具,真是道高一尺,魔高一丈。 9 | 对于第二种,安全性比第一种高。看不到代码。但是 jni 接口是直接暴露的,别人可以直接拿 so 直接使用。可以做签名验证,防止二次打包等。 10 | 11 | 加密方式也有很多,如RSA加密,MD5加密,AES加密,DES加密等等 12 | 这里我们使用的是 AES CBC Pkcs5Padding。具体代码可参考文章最后的源码。 13 | 14 | ---- 15 | 16 | ### 0x01 17 | 首先,我们要创建一个 Android 工程,还有一个 AesUtils.java: 18 | ```java 19 | public class AesUtils { 20 | 21 | static { 22 | System.loadLibrary("aesLib"); 23 | } 24 | 25 | // AES加密, CBC, PKCS5Padding 26 | public static native String encrypt(String str); 27 | 28 | // AES解密, CBC, PKCS5Padding 29 | public static native String decrypt(String str); 30 | } 31 | ``` 32 | 33 | 还有对应的 c++ 文件 aes_lib.cpp: 34 | ```cpp 35 | #include 36 | #include 37 | #include "aes_utils.h" 38 | #include "tools.h" 39 | 40 | 41 | #ifdef __cplusplus 42 | extern "C" { 43 | #endif 44 | 45 | JNIEXPORT jstring JNICALL Java_com_goodl_aes_AesUtils_encrypt(JNIEnv *env, jclass jcls, jstring str_) { 46 | if (str_ == nullptr) return nullptr; 47 | 48 | const char *str = env->GetStringUTFChars(str_, JNI_FALSE); 49 | char *result = AES_128_CBC_PKCS5_Encrypt(str); 50 | 51 | env->ReleaseStringUTFChars(str_, str); 52 | 53 | jstring jResult = getJString(env, result); 54 | free(result); 55 | 56 | return jResult; 57 | } 58 | 59 | JNIEXPORT jstring JNICALL Java_com_goodl_aes_AesUtils_decrypt(JNIEnv *env, jclass jcls, jstring str_) { 60 | if (str_ == nullptr) return nullptr; 61 | 62 | const char *str = env->GetStringUTFChars(str_, JNI_FALSE); 63 | char *result = AES_128_CBC_PKCS5_Decrypt(str); 64 | 65 | env->ReleaseStringUTFChars(str_, str); 66 | 67 | jstring jResult = getJString(env, result); 68 | free(result); 69 | 70 | return jResult; 71 | } 72 | 73 | #ifdef __cplusplus 74 | } 75 | #endif 76 | ``` 77 | 78 | 其中,```getJString``` 函数在 tools 中,负责将 c 字符串转为 java 字符串,最后记得释放内存。其他文件这里省略,可自行参考源码。 79 | 80 | 我们看下运行结果: 81 | ``` 82 | D/aes: text: abc_-=.,123扫地阿姨发现你的代码有Bug 83 | D/aes: text 加密: 9aba6ccf2b80ca251c1186508e019ca52d7e277dc0b4b4420440ed491fb2aeb8635dce02d1bb174363ad919ae261d10f 84 | D/aes: text 解密: abc_-=.,123扫地阿姨发现你的代码有Bug 85 | ``` 86 | 87 | 然后可以在 [http://ctf.ssleye.com/caes.html](http://ctf.ssleye.com/caes.html) 验证: 88 | ![QQ20190917-154909@2x.png](https://upload-images.jianshu.io/upload_images/6349226-cabddb3b4a3e7bad.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700) 89 | 90 | 91 | 结果一致,万事大吉 ??? 92 | 一切才刚刚开始,目前为止,我们已经实现了加解密,但是安全呢? 93 | 对AES加密来说,最重要的就是密钥 key 和偏移量 iv 了。 94 | 我们打开神器 ida,再用神奇的 F5: 95 | ![ida1.png](https://upload-images.jianshu.io/upload_images/6349226-8ed6058dc43fe733.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700) 96 | 97 | 然后再看AES_128_CBC_PKCS5_Encrypt: 98 | ![ida2.png](https://upload-images.jianshu.io/upload_images/6349226-7c7e6acb48258c03.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700) 99 | 100 | 再看看 off_6008 : 101 | ![ida3.png](https://upload-images.jianshu.io/upload_images/6349226-e49b7e246e9d3dbe.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700) 102 | 103 | 104 | 我们的密钥和偏移量就这么暴露了,为什么会这样?因为我们的 key 和 iv 没有任何保护,不管是宏定义还是字符串常量,都很容易被反汇编工具找到 : 105 | ```c 106 | #define AES_KEY "goodl-aes-key123" 107 | #define AES_IV "goodl-aes-iv1234" 108 | 109 | static const char *AES_KEY = "goodl-aes-key123"; 110 | static const char *AES_IV = "goodl-aes-iv1234"; 111 | ``` 112 | 113 | ---- 114 | 115 | ### 0x02 116 | 117 | 那么我们修改一下,对 key 和 iv 进行一些保护: 118 | ```c 119 | static const uint8_t *getKey() { 120 | const int len = 16; 121 | uint8_t *src = malloc(len + 1); 122 | 123 | for (int i = 0; i < len; ++i) { 124 | switch (i) { 125 | case 0: src[i] = 'g'; break; 126 | case 1: src[i] = 'o'; break; 127 | case 2: src[i] = 'o'; break; 128 | case 3: src[i] = 'd'; break; 129 | case 4: src[i] = 'l'; break; 130 | case 5: src[i] = '-'; break; 131 | case 6: src[i] = 'a'; break; 132 | case 7: src[i] = 'e'; break; 133 | case 8: src[i] = 's'; break; 134 | case 9: src[i] = '-'; break; 135 | case 10: src[i] = 'k'; break; 136 | case 11: src[i] = 'e'; break; 137 | case 12: src[i] = 'y'; break; 138 | case 13: src[i] = '1'; break; 139 | case 14: src[i] = '2'; break; 140 | case 15: src[i] = '3'; break; 141 | } 142 | } 143 | src[len] = '\0'; 144 | return src; 145 | } 146 | 147 | static const uint8_t *getIV() { 148 | const int len = 16; 149 | uint8_t *src = malloc(len + 1); 150 | 151 | for (int i = 0; i < len; ++i) { 152 | switch (i) { 153 | case 0: src[i] = 'g'; break; 154 | case 1: src[i] = 'o'; break; 155 | case 2: src[i] = 'o'; break; 156 | case 3: src[i] = 'd'; break; 157 | case 4: src[i] = 'l'; break; 158 | case 5: src[i] = '-'; break; 159 | case 6: src[i] = 'a'; break; 160 | case 7: src[i] = 'e'; break; 161 | case 8: src[i] = 's'; break; 162 | case 9: src[i] = '-'; break; 163 | case 10: src[i] = 'i'; break; 164 | case 11: src[i] = 'v'; break; 165 | case 12: src[i] = '1'; break; 166 | case 13: src[i] = '2'; break; 167 | case 14: src[i] = '3'; break; 168 | case 15: src[i] = '4'; break; 169 | } 170 | } 171 | src[len] = '\0'; 172 | return src; 173 | } 174 | ``` 175 | 176 | 然后再看下ida: 177 | ![ida4.png](https://upload-images.jianshu.io/upload_images/6349226-3a62da3ebffe4164.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700) 178 | 179 | 这样就不能直接看出 key 和 iv 了,起到了一定的保护作用。还可以将 key 和 iv 先 base64 编码,放入数组,再 base64 解码后返回。 180 | 181 | ---- 182 | 183 | ### 0x03 184 | 185 | 我们还可以进一步增强 so 的安全性,比如代码的混淆和加入花指令,以及 so 的加固。 186 | 187 | 花指令是由设计者特别构思,希望使反汇编的时候出错,让破解者无法清楚正确地反汇编程序的内容,迷失方向。有兴趣的话可以自行搜索一下花指令。 188 | 189 | 先说混淆吧,我们可以通过宏定义的方式来混淆。以 aes_utils 为例: 190 | ```c 191 | #define AES_128_CBC_PKCS5_Encrypt ll11l1l1ll 192 | #define AES_128_CBC_PKCS5_Decrypt ll11lll11l 193 | #define getKey ll11lll1l1 194 | #define getIV ll11l1l1l1 195 | #define getPaddingInput ll11l1l11l 196 | #define findPaddingIndex lll1l1l1l1 197 | #define removePadding ll11l1llll 198 | ``` 199 | 200 | 201 | ---- 202 | 203 | ### 0x04 204 | 205 | 然后是花指令,花指令工具类 junk.h: 206 | ```c 207 | #ifndef _JUNK_H_ 208 | #define _JUNK_H_ 209 | 210 | #define JUNK_CODE //是否插入垃圾代码的开关 211 | #ifdef JUNK_CODE 212 | 213 | #define junk_fun0 li11li1o0 214 | #define junk_fun1 li11li1o1 215 | #define junk_fun2 li11li1o2 216 | #define junk_fun3 li11li1o3 217 | 218 | static inline int junk_fun0(void) { 219 | volatile int i = 138, j = 1949; 220 | 221 | if ((i++) % 2 > 0) j *= i; 222 | if (j < 0) i *= 2; 223 | else return 0; 224 | 225 | i = 1; 226 | while (i++ < 2) { 227 | j /= i; 228 | j++; 229 | i++; 230 | } 231 | return i; 232 | } 233 | 234 | static inline int junk_fun1(void) { 235 | volatile int i = 21, j = 75; 236 | 237 | if ((i--) % 3 > 0) j *= i; 238 | if (j > 1) i *= 3; 239 | else return 1; 240 | 241 | i = 1; 242 | while (i++ < 3) { 243 | j /= i; 244 | j--; 245 | i++; 246 | } 247 | return j; 248 | } 249 | 250 | static inline int junk_fun2(void) { 251 | volatile int i = 56, j = 17; 252 | 253 | if ((i--) % 5 > 0) j *= i; 254 | if (j > 2) i *= 5; 255 | else return 0; 256 | 257 | i = 1; 258 | while (i++ < 5) { 259 | j *= i; 260 | j += 3; 261 | i += 3; 262 | } 263 | return i; 264 | } 265 | 266 | static inline int junk_fun3(void) { 267 | volatile int i = 1909, j = 131; 268 | 269 | if ((i--) % 7 > 0) j *= i; 270 | if (j > 3) i *= 7; 271 | else return 1; 272 | 273 | i = 1; 274 | while (i++ < 7) { 275 | j /= i; 276 | j -= 5; 277 | i += 5; 278 | } 279 | return i; 280 | } 281 | 282 | #define _JUNK_FUN_0 {if(junk_fun2())junk_fun1();if(junk_fun0()) junk_fun3();if(junk_fun1()) junk_fun2();if(junk_fun3()) junk_fun1(); \ 283 | if(junk_fun1())junk_fun0();if(junk_fun2()) junk_fun3();if(junk_fun3()) junk_fun1();if(junk_fun1()) junk_fun0();} 284 | #define _JUNK_FUN_1 {if(junk_fun3())junk_fun1();if(junk_fun1()) junk_fun2();if(junk_fun2()) junk_fun0();if(junk_fun0()) junk_fun1(); \ 285 | if(junk_fun2())junk_fun1();if(junk_fun0()) junk_fun3();if(junk_fun1()) junk_fun2();if(junk_fun3()) junk_fun1();} 286 | #define _JUNK_FUN_2 {if(junk_fun1())junk_fun0();if(junk_fun2()) junk_fun3();if(junk_fun3()) junk_fun1();if(junk_fun1()) junk_fun0(); \ 287 | if(junk_fun0())junk_fun2();if(junk_fun3()) junk_fun0();if(junk_fun0()) junk_fun3();if(junk_fun2()) junk_fun3();} 288 | #define _JUNK_FUN_3 {if(junk_fun0())junk_fun2();if(junk_fun3()) junk_fun0();if(junk_fun0()) junk_fun3();if(junk_fun2()) junk_fun3(); \ 289 | if(junk_fun3())junk_fun1();if(junk_fun1()) junk_fun2();if(junk_fun2()) junk_fun0();if(junk_fun0()) junk_fun1();} 290 | 291 | #else 292 | 293 | #define _JUNK_FUN_0 {} 294 | #define _JUNK_FUN_1 {} 295 | #define _JUNK_FUN_2 {} 296 | #define _JUNK_FUN_3 {} 297 | 298 | #endif 299 | #endif 300 | ``` 301 | 302 | 做完这些我们再来看一下效果: 303 | ![ida5.png](https://upload-images.jianshu.io/upload_images/6349226-2bd11603d8c77736.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700) 304 | 305 | 之前可以清晰看到的函数名,现在全变成了 o000OO0O,qqppqp,ll11l1llll,bbbddbdbb 这些没有含义又相似度极高的名称了。总之看起来废眼,头疼。长时间看还容易引起头晕,脑胀,恶心反胃等不良反应。 306 | 307 | ---- 308 | 309 | ### 0x05 310 | 311 | 目前为止,安全性有了一定的提升,但感觉还不够,因为我们的 jni 入口还是可以一眼就看出来的。```Java_com_goodl_aes_AesUtils_encrypt``` 和 ```Java_com_goodl_aes_AesUtils_decrypt ``` 实在是鹤立鸡群,太扎眼。 312 | 313 | 解决方法: 314 | - java 层的类名和方法名就不要那么规范了,人肉混淆 315 | - 改为动态注册(有兴趣的可以搜下 jni 动态注册) 316 | - so 名字也换掉,带个 aes,谁都知道是干什么的了 317 | 318 | AesUtils.java 改为 FooTools.java 319 | ```java 320 | public class FooTools { 321 | 322 | static { 323 | System.loadLibrary("fooLib"); 324 | } 325 | 326 | // AES加密, CBC, PKCS5Padding 327 | public static native String method01(String str); 328 | 329 | // AES解密, CBC, PKCS5Padding 330 | public static native String method02(String str); 331 | } 332 | ``` 333 | 334 | aes_lib.cpp 改为 foo_tools.cpp,使用动态注册: 335 | ```c 336 | #include 337 | #include 338 | #include "aes_utils.h" 339 | #include "tools.h" 340 | #include "junk.h" 341 | 342 | #define JNIREG_CLASS "com/goodl/aes/FooTools" 343 | #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) 344 | 345 | #ifdef __cplusplus 346 | extern "C" { 347 | #endif 348 | 349 | JNIEXPORT jstring JNICALL method01(JNIEnv *env, jclass jcls, jstring str_) { 350 | if (str_ == nullptr) return nullptr; 351 | 352 | const char *str = env->GetStringUTFChars(str_, JNI_FALSE); 353 | char *result = AES_128_CBC_PKCS5_Encrypt(str); 354 | 355 | env->ReleaseStringUTFChars(str_, str); 356 | 357 | jstring jResult = getJString(env, result); 358 | free(result); 359 | 360 | return jResult; 361 | } 362 | 363 | JNIEXPORT jstring JNICALL method02(JNIEnv *env, jclass jcls, jstring str_) { 364 | if (str_ == nullptr) return nullptr; 365 | 366 | const char *str = env->GetStringUTFChars(str_, JNI_FALSE); 367 | char *result = AES_128_CBC_PKCS5_Decrypt(str); 368 | 369 | env->ReleaseStringUTFChars(str_, str); 370 | 371 | jstring jResult = getJString(env, result); 372 | free(result); 373 | 374 | return jResult; 375 | } 376 | 377 | static JNINativeMethod method_table[] = { 378 | {"func0x01", "(Ljava/lang/String;)Ljava/lang/String;", (void *) method01}, 379 | {"func0x02", "(Ljava/lang/String;)Ljava/lang/String;", (void *) method02}, 380 | }; 381 | 382 | static int registerMethods(JNIEnv *env, const char *className, 383 | JNINativeMethod *gMethods, int numMethods) { 384 | jclass clazz = env->FindClass(className); 385 | if (clazz == nullptr) { 386 | return JNI_FALSE; 387 | } 388 | if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { 389 | return JNI_FALSE; 390 | } 391 | return JNI_TRUE; 392 | } 393 | 394 | JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { 395 | _JUNK_FUN_0 396 | 397 | JNIEnv *env = nullptr; 398 | if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { 399 | return JNI_ERR; 400 | } 401 | assert(env != nullptr); 402 | 403 | // 注册native方法 404 | if (!registerMethods(env, JNIREG_CLASS, method_table, NELEM(method_table))) { 405 | return JNI_ERR; 406 | } 407 | 408 | return JNI_VERSION_1_6; 409 | } 410 | 411 | #ifdef __cplusplus 412 | } 413 | #endif 414 | ``` 415 | 416 | 现在 so 的安全性又有提高。我们还可以做什么? 417 | - 验证签名,签名不一致就报错或是返回空 418 | - 防调试 419 | - 防 Xposed 420 | - 对 so 加固 421 | 422 | 423 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.goodl.aes.test" 7 | minSdkVersion 21 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation project(':jniLibrary') 23 | implementation fileTree(dir: 'libs', include: ['*.jar']) 24 | implementation 'androidx.appcompat:appcompat:1.1.0' 25 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 26 | testImplementation 'junit:junit:4.13' 27 | androidTestImplementation 'androidx.test:runner:1.2.0' 28 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 29 | } 30 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/goodl/aes/test/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.goodl.aes.test; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.goodl.aes.test", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/goodl/aes/test/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.goodl.aes.test; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.View; 8 | 9 | import com.goodl.aes.FooTools; 10 | 11 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_main); 17 | 18 | findViewById(R.id.btn_test).setOnClickListener(this); 19 | } 20 | 21 | @Override 22 | public void onClick(View v) { 23 | String text = "abc_-=.,123扫地阿姨发现你的代码有Bug"; 24 | String textEnc = FooTools.method01(text); 25 | String textDec = FooTools.method02(textEnc); 26 | 27 | Log.d("aes", "text: " + text); 28 | Log.d("aes", "text 加码: " + textEnc); 29 | Log.d("aes", "text 解密: " + textDec); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 |