StringFog
class.
28 | *
29 | * @author Megatron King
30 | * @since 2018/9/20 17:41
31 | */
32 | public final class StringFogClassGenerator {
33 |
34 |
35 | public static void generate(File outputFile, String packageName, String className,
36 | String implementation, StringFogMode mode) throws IOException {
37 | File outputDir = outputFile.getParentFile();
38 | if (!outputDir.exists() && !outputDir.mkdirs()) {
39 | throw new IOException("Can not mkdirs the dir: " + outputDir);
40 | }
41 |
42 | int lastIndexOfDot = implementation.lastIndexOf(".");
43 | String implementationSimpleClassName = lastIndexOfDot == -1 ? implementation :
44 | implementation.substring(implementation.lastIndexOf(".") + 1);
45 |
46 | JavaWriter javaWriter = new JavaWriter(new FileWriter(outputFile));
47 | javaWriter.emitPackage(packageName);
48 | javaWriter.emitEmptyLine();
49 | javaWriter.emitImports(implementation);
50 | javaWriter.emitEmptyLine();
51 | if (mode == StringFogMode.base64) {
52 | javaWriter.emitImports("com.github.megatronking.stringfog.Base64");
53 | }
54 |
55 | javaWriter.emitJavadoc("Generated code from StringFog gradle plugin. Do not modify!");
56 | javaWriter.beginType(className, "class", SetUtils.fromArray(Modifier.PUBLIC,
57 | Modifier.FINAL));
58 |
59 | javaWriter.emitField(implementationSimpleClassName, "IMPL",
60 | SetUtils.fromArray(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL),
61 | "new " + implementationSimpleClassName + "()");
62 |
63 | javaWriter.emitEmptyLine();
64 | if (mode == StringFogMode.base64) {
65 | javaWriter.beginMethod(String.class.getSimpleName(), "decrypt",
66 | SetUtils.fromArray(Modifier.PUBLIC, Modifier.STATIC),
67 | String.class.getSimpleName(), "value",
68 | String.class.getSimpleName(), "key");
69 | javaWriter.emitStatement("return IMPL.decrypt(Base64.decode(value, Base64.DEFAULT), " +
70 | "Base64.decode(key, Base64.DEFAULT))");
71 | javaWriter.endMethod();
72 | } else if (mode == StringFogMode.bytes) {
73 | javaWriter.beginMethod(String.class.getSimpleName(), "decrypt",
74 | SetUtils.fromArray(Modifier.PUBLIC, Modifier.STATIC),
75 | byte[].class.getSimpleName(), "value",
76 | byte[].class.getSimpleName(), "key");
77 | javaWriter.emitStatement("return IMPL.decrypt(value, key)");
78 | javaWriter.endMethod();
79 | }
80 |
81 | javaWriter.emitEmptyLine();
82 | javaWriter.endType();
83 |
84 | javaWriter.close();
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/stringfog-core/src/main/java/com/github/megatronking/stringfog/plugin/StringFogClassVisitor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017, Megatron King
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 |
15 | package com.github.megatronking.stringfog.plugin;
16 |
17 | import com.github.megatronking.stringfog.Base64;
18 | import com.github.megatronking.stringfog.IKeyGenerator;
19 | import com.github.megatronking.stringfog.IStringFog;
20 | import com.github.megatronking.stringfog.plugin.utils.TextUtils;
21 |
22 | import org.objectweb.asm.AnnotationVisitor;
23 | import org.objectweb.asm.ClassVisitor;
24 | import org.objectweb.asm.FieldVisitor;
25 | import org.objectweb.asm.MethodVisitor;
26 | import org.objectweb.asm.Opcodes;
27 | import org.objectweb.asm.Type;
28 |
29 | import java.util.ArrayList;
30 | import java.util.Arrays;
31 | import java.util.List;
32 |
33 | /**
34 | * Visit the class to execute string fog.
35 | *
36 | * @author Megatron King
37 | * @since 2017/3/6 20:37
38 | */
39 |
40 | /* package */ class StringFogClassVisitor extends ClassVisitor {
41 |
42 | private static final String IGNORE_ANNOTATION = "Lcom/github/megatronking/stringfog" +
43 | "/annotation/StringFogIgnore;";
44 |
45 | private boolean isClInitExists;
46 |
47 | private final ListThe padding '=' characters at the end are considered optional, but 99 | * if any are present, there must be the correct number of them. 100 | * 101 | * @param str the input String to decode, which is converted to 102 | * bytes using the default charset 103 | * @param flags controls certain features of the decoded output. 104 | * Pass {@code DEFAULT} to decode standard Base64. 105 | * 106 | * @throws IllegalArgumentException if the input contains 107 | * incorrect padding 108 | */ 109 | public static byte[] decode(String str, int flags) { 110 | return decode(str.getBytes(), flags); 111 | } 112 | 113 | /** 114 | * Decode the Base64-encoded data in input and return the data in 115 | * a new byte array. 116 | * 117 | *
The padding '=' characters at the end are considered optional, but 118 | * if any are present, there must be the correct number of them. 119 | * 120 | * @param input the input array to decode 121 | * @param flags controls certain features of the decoded output. 122 | * Pass {@code DEFAULT} to decode standard Base64. 123 | * 124 | * @throws IllegalArgumentException if the input contains 125 | * incorrect padding 126 | */ 127 | public static byte[] decode(byte[] input, int flags) { 128 | return decode(input, 0, input.length, flags); 129 | } 130 | 131 | /** 132 | * Decode the Base64-encoded data in input and return the data in 133 | * a new byte array. 134 | * 135 | *
The padding '=' characters at the end are considered optional, but 136 | * if any are present, there must be the correct number of them. 137 | * 138 | * @param input the data to decode 139 | * @param offset the position within the input array at which to start 140 | * @param len the number of bytes of input to decode 141 | * @param flags controls certain features of the decoded output. 142 | * Pass {@code DEFAULT} to decode standard Base64. 143 | * 144 | * @throws IllegalArgumentException if the input contains 145 | * incorrect padding 146 | */ 147 | private static byte[] decode(byte[] input, int offset, int len, int flags) { 148 | // Allocate space for the most data the input could represent. 149 | // (It could contain less if it contains whitespace, etc.) 150 | Decoder decoder = new Decoder(flags, new byte[len*3/4]); 151 | 152 | if (!decoder.process(input, offset, len, true)) { 153 | throw new IllegalArgumentException("bad base-64"); 154 | } 155 | 156 | // Maybe we got lucky and allocated exactly enough output space. 157 | if (decoder.op == decoder.output.length) { 158 | return decoder.output; 159 | } 160 | 161 | // Need to shorten the array, so allocate a new one of the 162 | // right size and copy. 163 | byte[] temp = new byte[decoder.op]; 164 | System.arraycopy(decoder.output, 0, temp, 0, decoder.op); 165 | return temp; 166 | } 167 | 168 | private static class Decoder extends Coder { 169 | /** 170 | * Lookup table for turning bytes into their position in the 171 | * Base64 alphabet. 172 | */ 173 | private static final int DECODE[] = { 174 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 175 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 176 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 177 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, 178 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 179 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, 180 | -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 181 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, 182 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 183 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 184 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 186 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 188 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 189 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 190 | }; 191 | 192 | /** 193 | * Decode lookup table for the "web safe" variant (RFC 3548 194 | * sec. 4) where - and _ replace + and /. 195 | */ 196 | private static final int DECODE_WEBSAFE[] = { 197 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 198 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 199 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, 200 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, 201 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 202 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, 203 | -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 204 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, 205 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 206 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 207 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 208 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 209 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 210 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 211 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 212 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 213 | }; 214 | 215 | /** Non-data values in the DECODE arrays. */ 216 | private static final int SKIP = -1; 217 | private static final int EQUALS = -2; 218 | 219 | /** 220 | * States 0-3 are reading through the next input tuple. 221 | * State 4 is having read one '=' and expecting exactly 222 | * one more. 223 | * State 5 is expecting no more data or padding characters 224 | * in the input. 225 | * State 6 is the error state; an error has been detected 226 | * in the input and no future input can "fix" it. 227 | */ 228 | private int state; // state number (0 to 6) 229 | private int value; 230 | 231 | final private int[] alphabet; 232 | 233 | private Decoder(int flags, byte[] output) { 234 | this.output = output; 235 | 236 | alphabet = ((flags & URL_SAFE) == 0) ? DECODE : DECODE_WEBSAFE; 237 | state = 0; 238 | value = 0; 239 | } 240 | 241 | /** 242 | * @return an overestimate for the number of bytes {@code 243 | * len} bytes could decode to. 244 | */ 245 | public int maxOutputSize(int len) { 246 | return len * 3/4 + 10; 247 | } 248 | 249 | /** 250 | * Decode another block of input data. 251 | * 252 | * @return true if the state machine is still healthy. false if 253 | * bad base-64 data has been detected in the input stream. 254 | */ 255 | public boolean process(byte[] input, int offset, int len, boolean finish) { 256 | if (this.state == 6) return false; 257 | 258 | int p = offset; 259 | len += offset; 260 | 261 | // Using local variables makes the decoder about 12% 262 | // faster than if we manipulate the member variables in 263 | // the loop. (Even alphabet makes a measurable 264 | // difference, which is somewhat surprising to me since 265 | // the member variable is final.) 266 | int state = this.state; 267 | int value = this.value; 268 | int op = 0; 269 | final byte[] output = this.output; 270 | final int[] alphabet = this.alphabet; 271 | 272 | while (p < len) { 273 | // Try the fast path: we're starting a new tuple and the 274 | // next four bytes of the input stream are all data 275 | // bytes. This corresponds to going through states 276 | // 0-1-2-3-0. We expect to use this method for most of 277 | // the data. 278 | // 279 | // If any of the next four bytes of input are non-data 280 | // (whitespace, etc.), value will end up negative. (All 281 | // the non-data values in decode are small negative 282 | // numbers, so shifting any of them up and or'ing them 283 | // together will result in a value with its top bit set.) 284 | // 285 | // You can remove this whole block and the output should 286 | // be the same, just slower. 287 | if (state == 0) { 288 | while (p+4 <= len && 289 | (value = ((alphabet[input[p] & 0xff] << 18) | 290 | (alphabet[input[p+1] & 0xff] << 12) | 291 | (alphabet[input[p+2] & 0xff] << 6) | 292 | (alphabet[input[p+3] & 0xff]))) >= 0) { 293 | output[op+2] = (byte) value; 294 | output[op+1] = (byte) (value >> 8); 295 | output[op] = (byte) (value >> 16); 296 | op += 3; 297 | p += 4; 298 | } 299 | if (p >= len) break; 300 | } 301 | 302 | // The fast path isn't available -- either we've read a 303 | // partial tuple, or the next four input bytes aren't all 304 | // data, or whatever. Fall back to the slower state 305 | // machine implementation. 306 | 307 | int d = alphabet[input[p++] & 0xff]; 308 | 309 | switch (state) { 310 | case 0: 311 | if (d >= 0) { 312 | value = d; 313 | ++state; 314 | } else if (d != SKIP) { 315 | this.state = 6; 316 | return false; 317 | } 318 | break; 319 | 320 | case 1: 321 | if (d >= 0) { 322 | value = (value << 6) | d; 323 | ++state; 324 | } else if (d != SKIP) { 325 | this.state = 6; 326 | return false; 327 | } 328 | break; 329 | 330 | case 2: 331 | if (d >= 0) { 332 | value = (value << 6) | d; 333 | ++state; 334 | } else if (d == EQUALS) { 335 | // Emit the last (partial) output tuple; 336 | // expect exactly one more padding character. 337 | output[op++] = (byte) (value >> 4); 338 | state = 4; 339 | } else if (d != SKIP) { 340 | this.state = 6; 341 | return false; 342 | } 343 | break; 344 | 345 | case 3: 346 | if (d >= 0) { 347 | // Emit the output triple and return to state 0. 348 | value = (value << 6) | d; 349 | output[op+2] = (byte) value; 350 | output[op+1] = (byte) (value >> 8); 351 | output[op] = (byte) (value >> 16); 352 | op += 3; 353 | state = 0; 354 | } else if (d == EQUALS) { 355 | // Emit the last (partial) output tuple; 356 | // expect no further data or padding characters. 357 | output[op+1] = (byte) (value >> 2); 358 | output[op] = (byte) (value >> 10); 359 | op += 2; 360 | state = 5; 361 | } else if (d != SKIP) { 362 | this.state = 6; 363 | return false; 364 | } 365 | break; 366 | 367 | case 4: 368 | if (d == EQUALS) { 369 | ++state; 370 | } else if (d != SKIP) { 371 | this.state = 6; 372 | return false; 373 | } 374 | break; 375 | 376 | case 5: 377 | if (d != SKIP) { 378 | this.state = 6; 379 | return false; 380 | } 381 | break; 382 | } 383 | } 384 | 385 | if (!finish) { 386 | // We're out of input, but a future call could provide 387 | // more. 388 | this.state = state; 389 | this.value = value; 390 | this.op = op; 391 | return true; 392 | } 393 | 394 | // Done reading input. Now figure out where we are left in 395 | // the state machine and finish up. 396 | 397 | switch (state) { 398 | case 0: 399 | // Output length is a multiple of three. Fine. 400 | break; 401 | case 1: 402 | // Read one extra input byte, which isn't enough to 403 | // make another output byte. Illegal. 404 | this.state = 6; 405 | return false; 406 | case 2: 407 | // Read two extra input bytes, enough to emit 1 more 408 | // output byte. Fine. 409 | output[op++] = (byte) (value >> 4); 410 | break; 411 | case 3: 412 | // Read three extra input bytes, enough to emit 2 more 413 | // output bytes. Fine. 414 | output[op++] = (byte) (value >> 10); 415 | output[op++] = (byte) (value >> 2); 416 | break; 417 | case 4: 418 | // Read one padding '=' when we expected 2. Illegal. 419 | this.state = 6; 420 | return false; 421 | case 5: 422 | // Read all the padding '='s we expected and no more. 423 | // Fine. 424 | break; 425 | } 426 | 427 | this.state = state; 428 | this.op = op; 429 | return true; 430 | } 431 | } 432 | 433 | /** 434 | * Base64-encode the given data and return a newly allocated 435 | * byte[] with the result. 436 | * 437 | * @param str the string to encode 438 | * @param flags controls certain features of the encoded output. 439 | * Passing {@code DEFAULT} results in output that 440 | * adheres to RFC 2045. 441 | */ 442 | public static byte[] encode(String str, int flags) { 443 | return encode(str.getBytes(), flags); 444 | } 445 | 446 | /** 447 | * Base64-encode the given data and return a newly allocated 448 | * byte[] with the result. 449 | * 450 | * @param input the data to encode 451 | * @param flags controls certain features of the encoded output. 452 | * Passing {@code DEFAULT} results in output that 453 | * adheres to RFC 2045. 454 | */ 455 | public static byte[] encode(byte[] input, int flags) { 456 | return encode(input, 0, input.length, flags); 457 | } 458 | 459 | /** 460 | * Base64-encode the given data and return a newly allocated 461 | * byte[] with the result. 462 | * 463 | * @param input the data to encode 464 | * @param offset the position within the input array at which to 465 | * start 466 | * @param len the number of bytes of input to encode 467 | * @param flags controls certain features of the encoded output. 468 | * Passing {@code DEFAULT} results in output that 469 | * adheres to RFC 2045. 470 | */ 471 | private static byte[] encode(byte[] input, int offset, int len, int flags) { 472 | Encoder encoder = new Encoder(flags, null); 473 | 474 | // Compute the exact length of the array we will produce. 475 | int output_len = len / 3 * 4; 476 | 477 | // Account for the tail of the data and the padding bytes, if any. 478 | if (encoder.do_padding) { 479 | if (len % 3 > 0) { 480 | output_len += 4; 481 | } 482 | } else { 483 | switch (len % 3) { 484 | case 0: break; 485 | case 1: output_len += 2; break; 486 | case 2: output_len += 3; break; 487 | } 488 | } 489 | 490 | // Account for the newlines, if any. 491 | if (encoder.do_newline && len > 0) { 492 | output_len += (((len-1) / (3 * Encoder.LINE_GROUPS)) + 1) * 493 | (encoder.do_cr ? 2 : 1); 494 | } 495 | 496 | encoder.output = new byte[output_len]; 497 | encoder.process(input, offset, len, true); 498 | 499 | return encoder.output; 500 | } 501 | 502 | private static class Encoder extends Coder { 503 | /** 504 | * Emit a new line every this many output tuples. Corresponds to 505 | * a 76-character line length (the maximum allowable according to 506 | * RFC 2045). 507 | */ 508 | static final int LINE_GROUPS = 19; 509 | 510 | /** 511 | * Lookup table for turning Base64 alphabet positions (6 bits) 512 | * into output bytes. 513 | */ 514 | private static final byte ENCODE[] = { 515 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 516 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 517 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 518 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', 519 | }; 520 | 521 | /** 522 | * Lookup table for turning Base64 alphabet positions (6 bits) 523 | * into output bytes. 524 | */ 525 | private static final byte ENCODE_WEBSAFE[] = { 526 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 527 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 528 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 529 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', 530 | }; 531 | 532 | final private byte[] tail; 533 | /* package */ int tailLen; 534 | private int count; 535 | 536 | final boolean do_padding; 537 | final boolean do_newline; 538 | final boolean do_cr; 539 | final private byte[] alphabet; 540 | 541 | private Encoder(int flags, byte[] output) { 542 | this.output = output; 543 | 544 | do_padding = (flags & NO_PADDING) == 0; 545 | do_newline = (flags & NO_WRAP) == 0; 546 | do_cr = (flags & CRLF) != 0; 547 | alphabet = ((flags & URL_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE; 548 | 549 | tail = new byte[2]; 550 | tailLen = 0; 551 | 552 | count = do_newline ? LINE_GROUPS : -1; 553 | } 554 | 555 | /** 556 | * @return an overestimate for the number of bytes {@code 557 | * len} bytes could encode to. 558 | */ 559 | public int maxOutputSize(int len) { 560 | return len * 8/5 + 10; 561 | } 562 | 563 | public boolean process(byte[] input, int offset, int len, boolean finish) { 564 | // Using local variables makes the encoder about 9% faster. 565 | final byte[] alphabet = this.alphabet; 566 | final byte[] output = this.output; 567 | int op = 0; 568 | int count = this.count; 569 | 570 | int p = offset; 571 | len += offset; 572 | int v = -1; 573 | 574 | // First we need to concatenate the tail of the previous call 575 | // with any input bytes available now and see if we can empty 576 | // the tail. 577 | 578 | switch (tailLen) { 579 | case 0: 580 | // There was no tail. 581 | break; 582 | 583 | case 1: 584 | if (p+2 <= len) { 585 | // A 1-byte tail with at least 2 bytes of 586 | // input available now. 587 | v = ((tail[0] & 0xff) << 16) | 588 | ((input[p++] & 0xff) << 8) | 589 | (input[p++] & 0xff); 590 | tailLen = 0; 591 | } 592 | break; 593 | 594 | case 2: 595 | if (p+1 <= len) { 596 | // A 2-byte tail with at least 1 byte of input. 597 | v = ((tail[0] & 0xff) << 16) | 598 | ((tail[1] & 0xff) << 8) | 599 | (input[p++] & 0xff); 600 | tailLen = 0; 601 | } 602 | break; 603 | } 604 | 605 | if (v != -1) { 606 | output[op++] = alphabet[(v >> 18) & 0x3f]; 607 | output[op++] = alphabet[(v >> 12) & 0x3f]; 608 | output[op++] = alphabet[(v >> 6) & 0x3f]; 609 | output[op++] = alphabet[v & 0x3f]; 610 | if (--count == 0) { 611 | if (do_cr) output[op++] = '\r'; 612 | output[op++] = '\n'; 613 | count = LINE_GROUPS; 614 | } 615 | } 616 | 617 | // At this point either there is no tail, or there are fewer 618 | // than 3 bytes of input available. 619 | 620 | // The main loop, turning 3 input bytes into 4 output bytes on 621 | // each iteration. 622 | while (p+3 <= len) { 623 | v = ((input[p] & 0xff) << 16) | 624 | ((input[p+1] & 0xff) << 8) | 625 | (input[p+2] & 0xff); 626 | output[op] = alphabet[(v >> 18) & 0x3f]; 627 | output[op+1] = alphabet[(v >> 12) & 0x3f]; 628 | output[op+2] = alphabet[(v >> 6) & 0x3f]; 629 | output[op+3] = alphabet[v & 0x3f]; 630 | p += 3; 631 | op += 4; 632 | if (--count == 0) { 633 | if (do_cr) output[op++] = '\r'; 634 | output[op++] = '\n'; 635 | count = LINE_GROUPS; 636 | } 637 | } 638 | 639 | if (finish) { 640 | // Finish up the tail of the input. Note that we need to 641 | // consume any bytes in tail before any bytes 642 | // remaining in input; there should be at most two bytes 643 | // total. 644 | 645 | if (p-tailLen == len-1) { 646 | int t = 0; 647 | v = ((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 4; 648 | tailLen -= t; 649 | output[op++] = alphabet[(v >> 6) & 0x3f]; 650 | output[op++] = alphabet[v & 0x3f]; 651 | if (do_padding) { 652 | output[op++] = '='; 653 | output[op++] = '='; 654 | } 655 | if (do_newline) { 656 | if (do_cr) output[op++] = '\r'; 657 | output[op++] = '\n'; 658 | } 659 | } else if (p-tailLen == len-2) { 660 | int t = 0; 661 | v = (((tailLen > 1 ? tail[t++] : input[p++]) & 0xff) << 10) | 662 | (((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 2); 663 | tailLen -= t; 664 | output[op++] = alphabet[(v >> 12) & 0x3f]; 665 | output[op++] = alphabet[(v >> 6) & 0x3f]; 666 | output[op++] = alphabet[v & 0x3f]; 667 | if (do_padding) { 668 | output[op++] = '='; 669 | } 670 | if (do_newline) { 671 | if (do_cr) output[op++] = '\r'; 672 | output[op++] = '\n'; 673 | } 674 | } else if (do_newline && op > 0 && count != LINE_GROUPS) { 675 | if (do_cr) output[op++] = '\r'; 676 | output[op++] = '\n'; 677 | } 678 | } else { 679 | // Save the leftovers in tail to be consumed on the next 680 | // call to encodeInternal. 681 | 682 | if (p == len-1) { 683 | tail[tailLen++] = input[p]; 684 | } else if (p == len-2) { 685 | tail[tailLen++] = input[p]; 686 | tail[tailLen++] = input[p+1]; 687 | } 688 | } 689 | 690 | this.op = op; 691 | this.count = count; 692 | 693 | return true; 694 | } 695 | } 696 | 697 | private Base64() { } // don't instantiate 698 | } 699 | -------------------------------------------------------------------------------- /stringfog-interface/src/main/java/com/github/megatronking/stringfog/IKeyGenerator.java: -------------------------------------------------------------------------------- 1 | package com.github.megatronking.stringfog; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * A generator uses to generate security keys. 7 | * 8 | * @author Megatron King 9 | * @since 2022/1/14 22:15 10 | */ 11 | public interface IKeyGenerator { 12 | 13 | /** 14 | * Generate a security key. 15 | * 16 | * @param text The content text will be encrypted. 17 | * @return A security key for the encryption. 18 | */ 19 | byte[] generate(String text); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /stringfog-interface/src/main/java/com/github/megatronking/stringfog/IStringFog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017, Megatron King 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.github.megatronking.stringfog; 16 | 17 | /** 18 | * Interface of how to encrypt and decrypt a string. 19 | * 20 | * @author Megatron King 21 | * @since 2018/9/20 16:15 22 | */ 23 | public interface IStringFog { 24 | 25 | /** 26 | * Encrypt the data by the special key. 27 | * 28 | * @param data The original data. 29 | * @param key Encrypt key. 30 | * @return The encrypted data. 31 | */ 32 | byte[] encrypt(String data, byte[] key); 33 | 34 | /** 35 | * Decrypt the data to origin by the special key. 36 | * 37 | * @param data The encrypted data. 38 | * @param key Encrypt key. 39 | * @return The original data. 40 | */ 41 | String decrypt(byte[] data, byte[] key); 42 | 43 | /** 44 | * Whether the string should be encrypted. 45 | * 46 | * @param data The original data. 47 | * @return If you want to skip this String, return false. 48 | */ 49 | boolean shouldFog(String data); 50 | 51 | } 52 | -------------------------------------------------------------------------------- /stringfog-interface/src/main/java/com/github/megatronking/stringfog/StringFogWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017, Megatron King 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.github.megatronking.stringfog; 16 | 17 | import java.lang.reflect.InvocationTargetException; 18 | 19 | /** 20 | * A wrapper for the real implementation of fogs. 21 | * 22 | * @author Megatron King 23 | * @since 2018/9/20 16:14 24 | */ 25 | public final class StringFogWrapper implements IStringFog { 26 | 27 | private final IStringFog mStringFogImpl; 28 | 29 | public StringFogWrapper(String impl) { 30 | try { 31 | mStringFogImpl = (IStringFog) Class.forName(impl).getDeclaredConstructor().newInstance(); 32 | } catch (ClassNotFoundException e) { 33 | throw new IllegalArgumentException("Stringfog implementation class not found: " + impl); 34 | } catch (InstantiationException e) { 35 | throw new IllegalArgumentException("Stringfog implementation class new instance failed: " 36 | + e.getMessage()); 37 | } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { 38 | throw new IllegalArgumentException("Stringfog implementation class create instance failed: " 39 | + e.getMessage()); 40 | } 41 | } 42 | 43 | @Override 44 | public byte[] encrypt(String data, byte[] key) { 45 | return mStringFogImpl == null ? data.getBytes() : mStringFogImpl.encrypt(data, key); 46 | } 47 | 48 | @Override 49 | public String decrypt(byte[] data, byte[] key) { 50 | return mStringFogImpl == null ? new String(data) : mStringFogImpl.decrypt(data, key); 51 | } 52 | 53 | @Override 54 | public boolean shouldFog(String data) { 55 | return mStringFogImpl != null && mStringFogImpl.shouldFog(data); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /stringfog-interface/src/main/java/com/github/megatronking/stringfog/annotation/StringFogIgnore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017, Megatron King 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.github.megatronking.stringfog.annotation; 16 | 17 | import java.lang.annotation.Retention; 18 | import java.lang.annotation.RetentionPolicy; 19 | import java.lang.annotation.Target; 20 | 21 | import static java.lang.annotation.ElementType.TYPE; 22 | 23 | /** 24 | * This annotation could keep no fog for string. 25 | * 26 | * @author Megatron King 27 | * @since 2017/3/8 8:41 28 | */ 29 | @Retention(RetentionPolicy.CLASS) 30 | @Target(value={TYPE}) 31 | public @interface StringFogIgnore { 32 | 33 | } 34 | --------------------------------------------------------------------------------