From c759a291fe7304fe3e5483c08a02b633550fbd85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A2=96=E9=80=B8?= <49649786+Zuoqiu-Yingyi@users.noreply.github.com> Date: Tue, 9 May 2023 11:20:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20`DES`,=20`AES`,=20`MD5`,=20`SHA1`,=20`R?= =?UTF-8?q?SA`=20=E5=8E=9F=E7=94=9F=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assignment/release/2022-11-20 23-12-49.rsakey | 8 + assignment/release/2022-11-20 23-15-28.rsakey | 8 + assignment/release/Cipher.exe | Bin 0 -> 409088 bytes assignment/release/Cipher.jar | Bin 0 -> 95229 bytes assignment/src/2022-11-20 23-12-49.rsakey | 8 + assignment/src/2022-11-20 23-15-28.rsakey | 8 + .../cryptography/ByteArrayAccess.java | 466 +++++ .../src/assignment/cryptography/Cipher.java | 393 ++++ .../cryptography/CipherConstants.java | 43 + .../cryptography/EncryptAndDecryptDialog.java | 470 +++++ .../src/assignment/cryptography/GUI.java | 1837 +++++++++++++++++ .../src/assignment/cryptography/Utils.java | 390 ++++ .../assignment/cryptography/crypto/AES.java | 985 +++++++++ .../cryptography/crypto/CryptoInterface.java | 14 + .../assignment/cryptography/crypto/DES.java | 367 ++++ .../cryptography/hash/DigestBase.java | 262 +++ .../src/assignment/cryptography/hash/MD5.java | 235 +++ .../cryptography/hash/MessageDigestSpi.java | 208 ++ .../src/assignment/cryptography/hash/SHA.java | 181 ++ .../cryptography/rsa/RSABaseInterface.java | 14 + .../cryptography/rsa/RSACompleteKey.java | 359 ++++ .../cryptography/rsa/RSAConstants.java | 21 + .../assignment/cryptography/rsa/RSAKey.java | 313 +++ .../cryptography/rsa/RSAKeyGenerator.java | 201 ++ .../cryptography/rsa/RSAPrivateKey.java | 125 ++ .../cryptography/rsa/RSAPublicKey.java | 116 ++ .../assignment/cryptography/rsa/RSATest.java | 136 ++ 27 files changed, 7168 insertions(+) create mode 100644 assignment/release/2022-11-20 23-12-49.rsakey create mode 100644 assignment/release/2022-11-20 23-15-28.rsakey create mode 100644 assignment/release/Cipher.exe create mode 100644 assignment/release/Cipher.jar create mode 100644 assignment/src/2022-11-20 23-12-49.rsakey create mode 100644 assignment/src/2022-11-20 23-15-28.rsakey create mode 100644 assignment/src/assignment/cryptography/ByteArrayAccess.java create mode 100644 assignment/src/assignment/cryptography/Cipher.java create mode 100644 assignment/src/assignment/cryptography/CipherConstants.java create mode 100644 assignment/src/assignment/cryptography/EncryptAndDecryptDialog.java create mode 100644 assignment/src/assignment/cryptography/GUI.java create mode 100644 assignment/src/assignment/cryptography/Utils.java create mode 100644 assignment/src/assignment/cryptography/crypto/AES.java create mode 100644 assignment/src/assignment/cryptography/crypto/CryptoInterface.java create mode 100644 assignment/src/assignment/cryptography/crypto/DES.java create mode 100644 assignment/src/assignment/cryptography/hash/DigestBase.java create mode 100644 assignment/src/assignment/cryptography/hash/MD5.java create mode 100644 assignment/src/assignment/cryptography/hash/MessageDigestSpi.java create mode 100644 assignment/src/assignment/cryptography/hash/SHA.java create mode 100644 assignment/src/assignment/cryptography/rsa/RSABaseInterface.java create mode 100644 assignment/src/assignment/cryptography/rsa/RSACompleteKey.java create mode 100644 assignment/src/assignment/cryptography/rsa/RSAConstants.java create mode 100644 assignment/src/assignment/cryptography/rsa/RSAKey.java create mode 100644 assignment/src/assignment/cryptography/rsa/RSAKeyGenerator.java create mode 100644 assignment/src/assignment/cryptography/rsa/RSAPrivateKey.java create mode 100644 assignment/src/assignment/cryptography/rsa/RSAPublicKey.java create mode 100644 assignment/src/assignment/cryptography/rsa/RSATest.java diff --git a/assignment/release/2022-11-20 23-12-49.rsakey b/assignment/release/2022-11-20 23-12-49.rsakey new file mode 100644 index 0000000..68a204c --- /dev/null +++ b/assignment/release/2022-11-20 23-12-49.rsakey @@ -0,0 +1,8 @@ +92228292ac71fe2742592f8f90d7f459b9ca5f2619fbf0e46dd37ea976feeb851 +2661157 +828b59ff49697ac06ca78dca00ea75be8abe913d95cc9f7d98d0eb09e5ce563a3 +4efe75b89afd94c8b1bf278c97f56c7ce61cf3bf00304f8bb +1d9963a3a32831863 +43119c92569b0b40585a9d2a12edea7640163f4c61bde7b61 +23589e5e36bec053 +11c815120642a89b447676a3d9fbf93b8a6861096d590252 diff --git a/assignment/release/2022-11-20 23-15-28.rsakey b/assignment/release/2022-11-20 23-15-28.rsakey new file mode 100644 index 0000000..9cb8dba --- /dev/null +++ b/assignment/release/2022-11-20 23-15-28.rsakey @@ -0,0 +1,8 @@ +87d154d72075570fe049ff0b042a6bf5edc4b7859b9222f9b0ed46da6754b7fd5 +20172fb +4b598c003278d979ab9e92257dd768d14eb82674b3b232bcea1fbb3368d9ec53b +786cd40c65bcd0f4cc263dfe6fcb15df96d2e00f029fb349b +120b8bb35ba5d0c4f +2cc9cb31b7ab58e57f72ab8887fa611812671eec52bedfc33 +21c640fb840b20ad +46cbe3f4be6605ee84602c58f2674aca753d144afccc0fe48 diff --git a/assignment/release/Cipher.exe b/assignment/release/Cipher.exe new file mode 100644 index 0000000000000000000000000000000000000000..0b3a20bba4236baaa387d373eb52000d2628f37e GIT binary patch literal 409088 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%0DLUX6 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 literal 0 HcmV?d00001 diff --git a/assignment/release/Cipher.jar b/assignment/release/Cipher.jar new file mode 100644 index 0000000000000000000000000000000000000000..491ad7a950268d8a561403cde8d8da08f9bfd7e3 GIT binary patch literal 95229 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⁢C5zB3AmnLoU;{H3p*BFs(cHiWHY`FX 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 literal 0 HcmV?d00001 diff --git a/assignment/src/2022-11-20 23-12-49.rsakey b/assignment/src/2022-11-20 23-12-49.rsakey new file mode 100644 index 0000000..68a204c --- /dev/null +++ b/assignment/src/2022-11-20 23-12-49.rsakey @@ -0,0 +1,8 @@ +92228292ac71fe2742592f8f90d7f459b9ca5f2619fbf0e46dd37ea976feeb851 +2661157 +828b59ff49697ac06ca78dca00ea75be8abe913d95cc9f7d98d0eb09e5ce563a3 +4efe75b89afd94c8b1bf278c97f56c7ce61cf3bf00304f8bb +1d9963a3a32831863 +43119c92569b0b40585a9d2a12edea7640163f4c61bde7b61 +23589e5e36bec053 +11c815120642a89b447676a3d9fbf93b8a6861096d590252 diff --git a/assignment/src/2022-11-20 23-15-28.rsakey b/assignment/src/2022-11-20 23-15-28.rsakey new file mode 100644 index 0000000..9cb8dba --- /dev/null +++ b/assignment/src/2022-11-20 23-15-28.rsakey @@ -0,0 +1,8 @@ +87d154d72075570fe049ff0b042a6bf5edc4b7859b9222f9b0ed46da6754b7fd5 +20172fb +4b598c003278d979ab9e92257dd768d14eb82674b3b232bcea1fbb3368d9ec53b +786cd40c65bcd0f4cc263dfe6fcb15df96d2e00f029fb349b +120b8bb35ba5d0c4f +2cc9cb31b7ab58e57f72ab8887fa611812671eec52bedfc33 +21c640fb840b20ad +46cbe3f4be6605ee84602c58f2674aca753d144afccc0fe48 diff --git a/assignment/src/assignment/cryptography/ByteArrayAccess.java b/assignment/src/assignment/cryptography/ByteArrayAccess.java new file mode 100644 index 0000000..e8af992 --- /dev/null +++ b/assignment/src/assignment/cryptography/ByteArrayAccess.java @@ -0,0 +1,466 @@ +// TODO 分析 +/* + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package assignment.cryptography; + +import static java.lang.Integer.reverseBytes; +import static java.lang.Long.reverseBytes; + +import java.lang.reflect.Field; +import java.nio.ByteOrder; + +import sun.misc.Unsafe; + +/** + * Optimized methods for converting between byte[] and int[]/long[], both for + * big endian and little endian byte orders. + * + * Currently, it includes a default code path plus two optimized code paths. + * One is for little endian architectures that support full speed int/long + * access at unaligned addresses (i.e. x86/amd64). The second is for big endian + * architectures (that only support correctly aligned access), such as SPARC. + * These are the only platforms we currently support, but other optimized + * variants could be added as needed. + * + * NOTE that ArrayIndexOutOfBoundsException will be thrown if the bounds checks + * failed. + * + * This class may also be helpful in improving the performance of the + * crypto code in the SunJCE provider. However, for now it is only accessible by + * the message digest implementation in the SUN provider. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +public final class ByteArrayAccess +{ + + private ByteArrayAccess() + { + // empty + } + + private static final Unsafe unsafe = getUnsafe(); + + // whether to use the optimized path for little endian platforms that + // support full speed unaligned memory access. + private static final boolean littleEndianUnaligned; + + // whether to use the optimzied path for big endian platforms that + // support only correctly aligned full speed memory access. + // (Note that on SPARC unaligned memory access is possible, but it is + // implemented using a software trap and therefore very slow) + private static final boolean bigEndian; + + private final static int byteArrayOfs = unsafe.arrayBaseOffset(byte[].class); + + static { + boolean scaleOK = ((unsafe.arrayIndexScale(byte[].class) == 1) && (unsafe.arrayIndexScale(int[].class) == 4) && (unsafe.arrayIndexScale(long[].class) == 8) && ((byteArrayOfs & 3) == 0)); + + ByteOrder byteOrder = ByteOrder.nativeOrder(); + littleEndianUnaligned = scaleOK && unaligned() && (byteOrder == ByteOrder.LITTLE_ENDIAN); + bigEndian = scaleOK && (byteOrder == ByteOrder.BIG_ENDIAN); + } + + // REF [Java双刃剑之Unsafe类详解 - 知乎](https://zhuanlan.zhihu.com/p/368970745) + private static final Unsafe getUnsafe() + { + Field unsafeField; + try { + unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); + // Field unsafeField = Unsafe.class.getDeclaredFields()[0]; //也可以这样,作用相同 + unsafeField.setAccessible(true); + Unsafe unsafe = (Unsafe) unsafeField.get(null); + return unsafe; + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return null; + } + + // Return whether this platform supports full speed int/long memory access + // at unaligned addresses. + // This code was copied from java.nio.Bits because there is no equivalent + // public API. + private static boolean unaligned() + { + // String arch = java.security.AccessController.doPrivileged(new sun.security.action.GetPropertyAction("os.arch", "")); + // return arch.equals("i386") || arch.equals("x86") || arch.equals("amd64") || arch.equals("x86_64"); + return true; + } + + /** + * byte[] 转换为 int[] 小字节序 + */ + public static void b2iLittle(byte[] in, int inOfs, int[] out, int outOfs, int len) + { + if ((inOfs < 0) || ((in.length - inOfs) < len) || (outOfs < 0) || ((out.length - outOfs) < len / 4)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + inOfs += byteArrayOfs; + len += inOfs; + while (inOfs < len) { + out[outOfs++] = unsafe.getInt(in, (long) inOfs); + inOfs += 4; + } + } else if (bigEndian && ((inOfs & 3) == 0)) { + inOfs += byteArrayOfs; + len += inOfs; + while (inOfs < len) { + out[outOfs++] = reverseBytes(unsafe.getInt(in, (long) inOfs)); + inOfs += 4; + } + } else { + len += inOfs; + while (inOfs < len) { + out[outOfs++] = ((in[inOfs] & 0xff)) | ((in[inOfs + 1] & 0xff) << 8) | ((in[inOfs + 2] & 0xff) << 16) | ((in[inOfs + 3]) << 24); + inOfs += 4; + } + } + } + + // 64 字节 {@link b2iLittle} + public static void b2iLittle64(byte[] in, int inOfs, int[] out) + { + if ((inOfs < 0) || ((in.length - inOfs) < 64) || (out.length < 16)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + inOfs += byteArrayOfs; + out[0] = unsafe.getInt(in, (long) (inOfs)); + out[1] = unsafe.getInt(in, (long) (inOfs + 4)); + out[2] = unsafe.getInt(in, (long) (inOfs + 8)); + out[3] = unsafe.getInt(in, (long) (inOfs + 12)); + out[4] = unsafe.getInt(in, (long) (inOfs + 16)); + out[5] = unsafe.getInt(in, (long) (inOfs + 20)); + out[6] = unsafe.getInt(in, (long) (inOfs + 24)); + out[7] = unsafe.getInt(in, (long) (inOfs + 28)); + out[8] = unsafe.getInt(in, (long) (inOfs + 32)); + out[9] = unsafe.getInt(in, (long) (inOfs + 36)); + out[10] = unsafe.getInt(in, (long) (inOfs + 40)); + out[11] = unsafe.getInt(in, (long) (inOfs + 44)); + out[12] = unsafe.getInt(in, (long) (inOfs + 48)); + out[13] = unsafe.getInt(in, (long) (inOfs + 52)); + out[14] = unsafe.getInt(in, (long) (inOfs + 56)); + out[15] = unsafe.getInt(in, (long) (inOfs + 60)); + } else if (bigEndian && ((inOfs & 3) == 0)) { + inOfs += byteArrayOfs; + out[0] = reverseBytes(unsafe.getInt(in, (long) (inOfs))); + out[1] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 4))); + out[2] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 8))); + out[3] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 12))); + out[4] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 16))); + out[5] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 20))); + out[6] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 24))); + out[7] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 28))); + out[8] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 32))); + out[9] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 36))); + out[10] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 40))); + out[11] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 44))); + out[12] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 48))); + out[13] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 52))); + out[14] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 56))); + out[15] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 60))); + } else { + b2iLittle(in, inOfs, out, 0, 64); + } + } + + /** + * int[] 转换为 byte[], 小字节序 + */ + public static void i2bLittle(int[] in, int inOfs, byte[] out, int outOfs, int len) + { + if ((inOfs < 0) || ((in.length - inOfs) < len / 4) || (outOfs < 0) || ((out.length - outOfs) < len)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + outOfs += byteArrayOfs; + len += outOfs; + while (outOfs < len) { + unsafe.putInt(out, (long) outOfs, in[inOfs++]); + outOfs += 4; + } + } else if (bigEndian && ((outOfs & 3) == 0)) { + outOfs += byteArrayOfs; + len += outOfs; + while (outOfs < len) { + unsafe.putInt(out, (long) outOfs, reverseBytes(in[inOfs++])); + outOfs += 4; + } + } else { + len += outOfs; + while (outOfs < len) { + int i = in[inOfs++]; + out[outOfs++] = (byte) (i); + out[outOfs++] = (byte) (i >> 8); + out[outOfs++] = (byte) (i >> 16); + out[outOfs++] = (byte) (i >> 24); + } + } + } + + // 将一个 32 位整数以小字节序保存在 out[outOfs..outOfs+3] + public static void i2bLittle4(int val, byte[] out, int outOfs) + { + if ((outOfs < 0) || ((out.length - outOfs) < 4)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + unsafe.putInt(out, (long) (byteArrayOfs + outOfs), val); + } else if (bigEndian && ((outOfs & 3) == 0)) { + unsafe.putInt(out, (long) (byteArrayOfs + outOfs), reverseBytes(val)); + } else { + out[outOfs] = (byte) (val); + out[outOfs + 1] = (byte) (val >> 8); + out[outOfs + 2] = (byte) (val >> 16); + out[outOfs + 3] = (byte) (val >> 24); + } + } + + /** + * byte[] to int[] conversion, big endian byte order. + */ + public static void b2iBig(byte[] in, int inOfs, int[] out, int outOfs, int len) + { + if ((inOfs < 0) || ((in.length - inOfs) < len) || (outOfs < 0) || ((out.length - outOfs) < len / 4)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + inOfs += byteArrayOfs; + len += inOfs; + while (inOfs < len) { + out[outOfs++] = reverseBytes(unsafe.getInt(in, (long) inOfs)); + inOfs += 4; + } + } else if (bigEndian && ((inOfs & 3) == 0)) { + inOfs += byteArrayOfs; + len += inOfs; + while (inOfs < len) { + out[outOfs++] = unsafe.getInt(in, (long) inOfs); + inOfs += 4; + } + } else { + len += inOfs; + while (inOfs < len) { + out[outOfs++] = ((in[inOfs + 3] & 0xff)) | ((in[inOfs + 2] & 0xff) << 8) | ((in[inOfs + 1] & 0xff) << 16) | ((in[inOfs]) << 24); + inOfs += 4; + } + } + } + + // Special optimization of b2iBig(in, inOfs, out, 0, 64) + public static void b2iBig64(byte[] in, int inOfs, int[] out) + { + if ((inOfs < 0) || ((in.length - inOfs) < 64) || (out.length < 16)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + inOfs += byteArrayOfs; + out[0] = reverseBytes(unsafe.getInt(in, (long) (inOfs))); + out[1] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 4))); + out[2] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 8))); + out[3] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 12))); + out[4] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 16))); + out[5] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 20))); + out[6] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 24))); + out[7] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 28))); + out[8] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 32))); + out[9] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 36))); + out[10] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 40))); + out[11] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 44))); + out[12] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 48))); + out[13] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 52))); + out[14] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 56))); + out[15] = reverseBytes(unsafe.getInt(in, (long) (inOfs + 60))); + } else if (bigEndian && ((inOfs & 3) == 0)) { + inOfs += byteArrayOfs; + out[0] = unsafe.getInt(in, (long) (inOfs)); + out[1] = unsafe.getInt(in, (long) (inOfs + 4)); + out[2] = unsafe.getInt(in, (long) (inOfs + 8)); + out[3] = unsafe.getInt(in, (long) (inOfs + 12)); + out[4] = unsafe.getInt(in, (long) (inOfs + 16)); + out[5] = unsafe.getInt(in, (long) (inOfs + 20)); + out[6] = unsafe.getInt(in, (long) (inOfs + 24)); + out[7] = unsafe.getInt(in, (long) (inOfs + 28)); + out[8] = unsafe.getInt(in, (long) (inOfs + 32)); + out[9] = unsafe.getInt(in, (long) (inOfs + 36)); + out[10] = unsafe.getInt(in, (long) (inOfs + 40)); + out[11] = unsafe.getInt(in, (long) (inOfs + 44)); + out[12] = unsafe.getInt(in, (long) (inOfs + 48)); + out[13] = unsafe.getInt(in, (long) (inOfs + 52)); + out[14] = unsafe.getInt(in, (long) (inOfs + 56)); + out[15] = unsafe.getInt(in, (long) (inOfs + 60)); + } else { + b2iBig(in, inOfs, out, 0, 64); + } + } + + /** + * int[] 转换为 byte[], 大字节序 + */ + public static void i2bBig(int[] in, int inOfs, byte[] out, int outOfs, int len) + { + if ((inOfs < 0) || ((in.length - inOfs) < len / 4) || (outOfs < 0) || ((out.length - outOfs) < len)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + outOfs += byteArrayOfs; + len += outOfs; + while (outOfs < len) { + unsafe.putInt(out, (long) outOfs, reverseBytes(in[inOfs++])); + outOfs += 4; + } + } else if (bigEndian && ((outOfs & 3) == 0)) { + outOfs += byteArrayOfs; + len += outOfs; + while (outOfs < len) { + unsafe.putInt(out, (long) outOfs, in[inOfs++]); + outOfs += 4; + } + } else { + len += outOfs; + while (outOfs < len) { + int i = in[inOfs++]; + out[outOfs++] = (byte) (i >> 24); + out[outOfs++] = (byte) (i >> 16); + out[outOfs++] = (byte) (i >> 8); + out[outOfs++] = (byte) (i); + } + } + } + + // Store one 32-bit value into out[outOfs..outOfs+3] in big endian order. + public static void i2bBig4(int val, byte[] out, int outOfs) + { + if ((outOfs < 0) || ((out.length - outOfs) < 4)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + unsafe.putInt(out, (long) (byteArrayOfs + outOfs), reverseBytes(val)); + } else if (bigEndian && ((outOfs & 3) == 0)) { + unsafe.putInt(out, (long) (byteArrayOfs + outOfs), val); + } else { + out[outOfs] = (byte) (val >> 24); + out[outOfs + 1] = (byte) (val >> 16); + out[outOfs + 2] = (byte) (val >> 8); + out[outOfs + 3] = (byte) (val); + } + } + + /** + * byte[] to long[] conversion, big endian byte order. + */ + public static void b2lBig(byte[] in, int inOfs, long[] out, int outOfs, int len) + { + if ((inOfs < 0) || ((in.length - inOfs) < len) || (outOfs < 0) || ((out.length - outOfs) < len / 8)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + inOfs += byteArrayOfs; + len += inOfs; + while (inOfs < len) { + out[outOfs++] = reverseBytes(unsafe.getLong(in, (long) inOfs)); + inOfs += 8; + } + } else if (bigEndian && ((inOfs & 3) == 0)) { + // In the current HotSpot memory layout, the first element of a + // byte[] is only 32-bit aligned, not 64-bit. + // That means we could use getLong() only for offset 4, 12, etc., + // which would rarely occur in practice. Instead, we use an + // optimization that uses getInt() so that it works for offset 0. + inOfs += byteArrayOfs; + len += inOfs; + while (inOfs < len) { + out[outOfs++] = ((long) unsafe.getInt(in, (long) inOfs) << 32) | (unsafe.getInt(in, (long) (inOfs + 4)) & 0xffffffffL); + inOfs += 8; + } + } else { + len += inOfs; + while (inOfs < len) { + int i1 = ((in[inOfs + 3] & 0xff)) | ((in[inOfs + 2] & 0xff) << 8) | ((in[inOfs + 1] & 0xff) << 16) | ((in[inOfs]) << 24); + inOfs += 4; + int i2 = ((in[inOfs + 3] & 0xff)) | ((in[inOfs + 2] & 0xff) << 8) | ((in[inOfs + 1] & 0xff) << 16) | ((in[inOfs]) << 24); + out[outOfs++] = ((long) i1 << 32) | (i2 & 0xffffffffL); + inOfs += 4; + } + } + } + + // Special optimization of b2lBig(in, inOfs, out, 0, 128) + public static void b2lBig128(byte[] in, int inOfs, long[] out) + { + if ((inOfs < 0) || ((in.length - inOfs) < 128) || (out.length < 16)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + inOfs += byteArrayOfs; + out[0] = reverseBytes(unsafe.getLong(in, (long) (inOfs))); + out[1] = reverseBytes(unsafe.getLong(in, (long) (inOfs + 8))); + out[2] = reverseBytes(unsafe.getLong(in, (long) (inOfs + 16))); + out[3] = reverseBytes(unsafe.getLong(in, (long) (inOfs + 24))); + out[4] = reverseBytes(unsafe.getLong(in, (long) (inOfs + 32))); + out[5] = reverseBytes(unsafe.getLong(in, (long) (inOfs + 40))); + out[6] = reverseBytes(unsafe.getLong(in, (long) (inOfs + 48))); + out[7] = reverseBytes(unsafe.getLong(in, (long) (inOfs + 56))); + out[8] = reverseBytes(unsafe.getLong(in, (long) (inOfs + 64))); + out[9] = reverseBytes(unsafe.getLong(in, (long) (inOfs + 72))); + out[10] = reverseBytes(unsafe.getLong(in, (long) (inOfs + 80))); + out[11] = reverseBytes(unsafe.getLong(in, (long) (inOfs + 88))); + out[12] = reverseBytes(unsafe.getLong(in, (long) (inOfs + 96))); + out[13] = reverseBytes(unsafe.getLong(in, (long) (inOfs + 104))); + out[14] = reverseBytes(unsafe.getLong(in, (long) (inOfs + 112))); + out[15] = reverseBytes(unsafe.getLong(in, (long) (inOfs + 120))); + } else { + // no optimization for big endian, see comments in b2lBig + b2lBig(in, inOfs, out, 0, 128); + } + } + + /** + * long[] to byte[] conversion, big endian byte order. + */ + public static void l2bBig(long[] in, int inOfs, byte[] out, int outOfs, int len) + { + if ((inOfs < 0) || ((in.length - inOfs) < len / 8) || (outOfs < 0) || ((out.length - outOfs) < len)) { + throw new ArrayIndexOutOfBoundsException(); + } + len += outOfs; + while (outOfs < len) { + long i = in[inOfs++]; + out[outOfs++] = (byte) (i >> 56); + out[outOfs++] = (byte) (i >> 48); + out[outOfs++] = (byte) (i >> 40); + out[outOfs++] = (byte) (i >> 32); + out[outOfs++] = (byte) (i >> 24); + out[outOfs++] = (byte) (i >> 16); + out[outOfs++] = (byte) (i >> 8); + out[outOfs++] = (byte) (i); + } + } +} diff --git a/assignment/src/assignment/cryptography/Cipher.java b/assignment/src/assignment/cryptography/Cipher.java new file mode 100644 index 0000000..39bfc5f --- /dev/null +++ b/assignment/src/assignment/cryptography/Cipher.java @@ -0,0 +1,393 @@ +package assignment.cryptography; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Arrays; + +import static assignment.cryptography.ByteArrayAccess.*; +import assignment.cryptography.crypto.CryptoInterface; +import assignment.cryptography.crypto.DES; +import assignment.cryptography.crypto.AES; +import assignment.cryptography.hash.DigestBase; +import assignment.cryptography.hash.MD5; +import assignment.cryptography.hash.SHA; +import assignment.cryptography.rsa.RSACompleteKey; +import assignment.cryptography.rsa.RSAKeyGenerator; +import assignment.cryptography.rsa.RSAPublicKey; + +public class Cipher implements CipherConstants +{ + protected byte[] key; // 对称密钥 + protected byte[] cipher_key; // 加密后的对称密钥 + + // protected int digest_algorithm; // 摘要算法 + // protected int signature_algorithm; // 公钥密码算法 + // protected int encrypt_algorithm; // 对称密码算法 + // protected int encrypt_key_length; // 对称密码密钥长度 + // protected int data_format; // 数据格式 + protected int encrypt_mode; // 分组密码模式 + + protected byte[] message; // 消息 + protected byte[] digest; // 消息摘要 + protected byte[] _digest; // 解密后的摘要 + protected byte[] signature; // 签名 + protected byte[] plain_datagram; // 明文数据报 + protected byte[] IV; // 初始化向量 + protected byte[] cipher_datagram; // 密文数据报 + protected byte[] packet; // 数据包 + + private static final int INDEX_PACKET_HEAD = 0; // 包头长度字段索引 + private static final int INDEX_IV = 1; // 包头长度字段索引 + private static final int INDEX_DATAGRAM = 2; // 包头长度字段索引 + private static final int INDEX_MESSAGE = 3; // 包头长度字段索引 + private static final int INDEX_SIGNATURE = 4; // 包头长度字段索引 + private static final int INDEX_KEY = 5; // 包头长度字段索引 + + protected int[] packet_head = new int[] { // 数据包头 + 24, // + 包头长度, 单位: Byte + -1, // + IV 长度 + -1, // + 填充后数据报长度 + -1, // | + 明文消息长度 + -1, // | + 明文签名长度 + -1, // + 加密密钥长度 + }; + + public Cipher( // + // int digestAlgorithm, // 摘要算法 + // int signatureAlgorithm, // 公钥密码算法 + // int encryptAlgorithm, // 对称密码算法 + // int encryptKeyLength, // 对称密码密钥长度 + // int dataFormat, // 数据格式 + int encryptMode // 分组密码模式 + ) + { + // this.digest_algorithm = digestAlgorithm; + // this.signature_algorithm = signatureAlgorithm; + // this.encrypt_algorithm = encryptAlgorithm; + // this.encrypt_key_length = encryptKeyLength; + // this.data_format = dataFormat; + this.encrypt_mode = encryptMode; + } + + /* 设置安全参数 */ + public Cipher setParams( // + // int digestAlgorithm, // 摘要算法 + // int signatureAlgorithm, // 公钥密码算法 + // int encryptAlgorithm, // 对称密码算法 + // int encryptKeyLength, // 对称密码密钥长度 + // int dataFormat, // 数据格式 + int encryptMode // 分组密码模式 + ) + { + // this.digest_algorithm = digestAlgorithm; + // this.signature_algorithm = signatureAlgorithm; + // this.encrypt_algorithm = encryptAlgorithm; + // this.encrypt_key_length = encryptKeyLength; + // this.data_format = dataFormat; + this.encrypt_mode = encryptMode; + return this; + } + + public byte[] getMessage() + { + return this.message; + } + + public byte[] getPacket() + { + return this.packet; + } + + public byte[] getKey() + { + return this.key; + } + + /* 设置消息 */ + public Cipher setMessage(byte[] message) + { + this.message = message; + return this; + } + + /* 设置消息 */ + public Cipher setPacket(byte[] packet) + { + this.packet = packet; + return this; + } + + /* 生成摘要 */ + public Cipher buildDigest(DigestBase digestBase) + { + digestBase.update(this.message); + this.digest = digestBase.digest(); + return this; + } + + /* 生成签名 */ + public Cipher buildSignature(RSACompleteKey completeKey) + { + this.signature = completeKey.decrypt(new BigInteger(Utils.paddingStart(this.digest, this.digest.length + 1))).toByteArray(); + this.packet_head[INDEX_SIGNATURE] = this.signature.length; + return this; + } + + /* 校验签名 */ + public Cipher verifySignature(RSAPublicKey publicKey) + { + this._digest = publicKey.encrypt(new BigInteger(Utils.paddingStart(this.signature, this.signature.length + 1))).toByteArray(); + if (this._digest.length % 2 == 1 && this._digest[0] == 0x00) this._digest = Arrays.copyOfRange(this._digest, 1, this._digest.length); + return this; + } + + /* 校验摘要 */ + public boolean isVerify() + { + if (this._digest.length < this.digest.length) Utils.paddingStart(this._digest, this.digest.length); + return Arrays.equals(this.digest, this._digest); + } + + /* 构造明文报文 */ + public Cipher constructDatagram() + { + this.plain_datagram = new byte[this.message.length + this.signature.length]; // 明文数据报 + System.arraycopy(this.message, 0, this.plain_datagram, 0, this.message.length); // 明文消息 + System.arraycopy(this.signature, 0, this.plain_datagram, this.message.length, this.signature.length); // 明文签名 + this.packet_head[INDEX_MESSAGE] = this.message.length; // 明文消息长度 + this.packet_head[INDEX_SIGNATURE] = this.signature.length; // 密文消息长度 + return this; + } + + /* 解构明文报文 */ + public Cipher deconstructDatagram() + { + this.message = new byte[(int) (this.packet_head[INDEX_MESSAGE])]; // 明文消息 + this.signature = new byte[(int) (this.packet_head[INDEX_SIGNATURE])]; // 明文签名 + System.arraycopy(this.plain_datagram, 0, this.message, 0, this.message.length); // 明文消息 + System.arraycopy(this.plain_datagram, this.message.length, this.signature, 0, this.signature.length); // 明文签名 + return this; + } + + /* 加密报文 */ + public Cipher encryptDatagram(CryptoInterface crypto, byte[] key) + { + /* 设置加密密钥 */ + this.key = key; + assert this.key.length % 4 == 0 : "密钥长度必须为 4 的倍数"; + int[] key_int = new int[this.key.length / 4]; + b2iBig(key, 0, key_int, 0, key.length); + crypto.setKey(key_int); // 设置密钥 + + /* 计算分组长度 */ + final int blocksize = crypto.getBlockSize(); // 分组长度 (字节) + final int blocksize_word = blocksize / 4; // 分组长度(32 位字) + int[] block = new int[blocksize_word]; // 临时分组 + + /* 构造一个随机初始化向量 */ + this.IV = new byte[blocksize]; // 初始化向量 + SecureRandom rand = new SecureRandom(); + rand.nextBytes(this.IV); + + /* 明文报文填充 */ + final int padding_length = blocksize - this.plain_datagram.length % blocksize; // 填充长度 + byte[] padding_content = new byte[padding_length]; // 填充内容 + byte[] padding_datagram = new byte[this.plain_datagram.length + padding_length]; // 填充后的数据报 + rand.nextBytes(padding_content); // 随机生成填充内容 + System.arraycopy(this.plain_datagram, 0, padding_datagram, 0, this.plain_datagram.length); + System.arraycopy(padding_content, 0, padding_datagram, this.plain_datagram.length, padding_length); + this.packet_head[INDEX_IV] = IV.length; // IV 长度 + this.packet_head[INDEX_DATAGRAM] = padding_datagram.length; // 填充后数据报长度 + + /* 构造密文数据报 */ + this.cipher_datagram = new byte[padding_datagram.length]; + + switch (this.encrypt_mode) { + case ENCRYPT_MODE_ECB: // ECB 模式 + for (int i = 0; i < this.cipher_datagram.length; i += blocksize) { + b2iBig(padding_datagram, i, block, 0, blocksize); // 待加密分组 + i2bBig(crypto.encrypt(block), 0, this.cipher_datagram, i, blocksize); // 加密分组 + } + break; + + case ENCRYPT_MODE_CBC: // CBC 模式 + int[] temp = new int[blocksize_word]; // 临时分组 + b2iBig(this.IV, 0, temp, 0, blocksize); // 使用初始化向量初始化临时分组 + + for (int i = 0; i < this.cipher_datagram.length; i += blocksize) { + b2iBig(padding_datagram, i, block, 0, blocksize); // 待加密分组 + for (int j = 0; j < blocksize_word; ++j) { + block[j] ^= temp[j]; // 异或运算 + } + temp = crypto.encrypt(block); // 加密分组 + i2bBig(temp, 0, this.cipher_datagram, i, blocksize); // 追加加密分组 + } + break; + + default: + break; + } + return this; + } + + /* 解密报文 */ + public Cipher decryptDatagram(CryptoInterface crypto) + { + /* 设置解密密钥 */ + final int key_length = crypto.getKeySize(); // 密钥长度 + assert key_length >= this.key.length || (key_length == this.key.length - 1 && this.key[0] == 0x00) : "对称密钥长度过长"; + if (key_length > this.key.length) { + this.key = Utils.paddingStart(this.key, key_length); + } else if (key_length == this.key.length - 1 && this.key[0] == 0x00) { + this.key = Arrays.copyOfRange(this.key, 1, key_length); + } + + int[] key_int = new int[key_length / 4]; + b2iBig(this.key, 0, key_int, 0, this.key.length); + crypto.setKey(key_int); // 设置密钥 + + /* 计算分组长度 */ + final int blocksize = crypto.getBlockSize(); // 分组长度 (字节) + final int blocksize_word = blocksize / 4; // 分组长度(32 位字) + int[] block = new int[blocksize_word]; // 临时分组 + + /* 构造数据报明文 */ + byte[] padding_datagram = new byte[this.cipher_datagram.length]; // 填充后的数据报 + this.plain_datagram = new byte[(int) (this.packet_head[INDEX_DATAGRAM])]; // 移除填充后的数据报 + + assert this.cipher_datagram.length % blocksize == 0 : "密文分组长度不是分组长度的整数倍"; + + switch (this.encrypt_mode) { + case ENCRYPT_MODE_ECB: // ECB 模式 + for (int i = 0; i < this.cipher_datagram.length; i += blocksize) { + b2iBig(this.cipher_datagram, i, block, 0, blocksize); // 待加密分组 + i2bBig(crypto.decrypt(block), 0, padding_datagram, i, blocksize); // 加密分组 + } + break; + + case ENCRYPT_MODE_CBC: // CBC 模式 + int[] temp = new int[blocksize_word]; // 临时分组 + int[] temp1 = new int[blocksize_word]; // 临时分组 + b2iBig(this.IV, 0, temp, 0, blocksize); // 使用初始化向量初始化临时分组 + + for (int i = 0; i < this.cipher_datagram.length; i += blocksize) { + b2iBig(this.cipher_datagram, i, temp1, 0, blocksize); // 待解密分组 + block = crypto.decrypt(temp1); // 解密 + for (int j = 0; j < blocksize_word; ++j) { + block[j] ^= temp[j]; // 异或运算 + } + i2bBig(block, 0, padding_datagram, i, blocksize); // 追加解密分组 + System.arraycopy(temp1, 0, temp, 0, blocksize_word); // 更新临时分组 + } + break; + + default: + break; + } + System.arraycopy(padding_datagram, 0, this.plain_datagram, 0, this.plain_datagram.length); // 移除填充内容 + return this; + } + + /* 加密对称密钥 */ + public Cipher encryptKey(RSAPublicKey publicKey) + { + this.cipher_key = publicKey.encrypt(new BigInteger(Utils.paddingStart(this.key, this.key.length + 1))).toByteArray(); + this.packet_head[INDEX_KEY] = this.cipher_key.length; // 密钥长度 + return this; + } + + /* 解密对称密钥 */ + public Cipher decryptKey(RSACompleteKey completeKey) + { + this.key = completeKey.decrypt(new BigInteger(Utils.paddingStart(this.cipher_key, this.cipher_key.length + 1))).toByteArray(); + if (this.key.length % 2 == 1 && this.key[0] == 0x00) this.key = Arrays.copyOfRange(this.key, 1, this.key.length); + return this; + } + + /* 构造数据包 */ + public Cipher constructDataPacket() + { + int ofs = 0; + this.packet = new byte[(int) (ofs // + + this.packet_head[INDEX_PACKET_HEAD] // + + this.packet_head[INDEX_IV] // + + this.packet_head[INDEX_DATAGRAM] // + + this.packet_head[INDEX_KEY] // + )]; + + i2bBig(this.packet_head, 0, this.packet, ofs, (int) (this.packet_head[INDEX_PACKET_HEAD])); + ofs += this.packet_head[INDEX_PACKET_HEAD]; + System.arraycopy(this.IV, 0, this.packet, ofs, (int) (this.packet_head[INDEX_IV])); + ofs += this.packet_head[INDEX_IV]; + System.arraycopy(this.cipher_datagram, 0, this.packet, ofs, (int) (this.packet_head[INDEX_DATAGRAM])); + ofs += this.packet_head[INDEX_DATAGRAM]; + System.arraycopy(this.cipher_key, 0, this.packet, ofs, (int) (this.packet_head[INDEX_KEY])); + return this; + } + + /* 解构数据包 */ + public Cipher deconstructDataPacket() + { + int ofs = 0; + b2iBig(this.packet, ofs, this.packet_head, 0, this.packet_head.length * 4); + + ofs += this.packet_head[INDEX_PACKET_HEAD]; + this.IV = new byte[this.packet_head[INDEX_IV]]; + System.arraycopy(this.packet, ofs, this.IV, 0, this.packet_head[INDEX_IV]); + + ofs += this.packet_head[INDEX_IV]; + this.cipher_datagram = new byte[this.packet_head[INDEX_DATAGRAM]]; + System.arraycopy(this.packet, ofs, this.cipher_datagram, 0, this.packet_head[INDEX_DATAGRAM]); + + ofs += this.packet_head[INDEX_DATAGRAM]; + this.cipher_key = new byte[this.packet_head[INDEX_KEY]]; + System.arraycopy(this.packet, ofs, this.cipher_key, 0, this.packet_head[INDEX_KEY]); + return this; + } + + public static void main(String[] args) + { + // byte[] key = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; + byte[] key = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }; + byte[] message = new byte[] { 0x00, 0x01, 0x02, 0x03 }; + // DigestBase digest = new MD5(); + DigestBase digest = new SHA(); + // CryptoInterface crypto = new DES(); + CryptoInterface crypto = new AES(); + RSAKeyGenerator key_generator_A = new RSAKeyGenerator(256); + RSAKeyGenerator key_generator_B = new RSAKeyGenerator(256); + RSACompleteKey complete_key_A = (RSACompleteKey) (key_generator_A.makeKey(RSAKeyGenerator.COMPLETE_KEY)); + RSACompleteKey complete_key_B = (RSACompleteKey) (key_generator_B.makeKey(RSAKeyGenerator.COMPLETE_KEY)); + RSAPublicKey public_key_A = (RSAPublicKey) (key_generator_A.makeKey(RSAKeyGenerator.PUBLIC_KEY)); + RSAPublicKey public_key_B = (RSAPublicKey) (key_generator_B.makeKey(RSAKeyGenerator.PUBLIC_KEY)); + Cipher cipher1 = new Cipher(ENCRYPT_MODE_CBC); + Cipher cipher2 = new Cipher(ENCRYPT_MODE_CBC); + + cipher1 // + .setMessage(message) // + .buildDigest(digest) // + .buildSignature(complete_key_A) // + .constructDatagram() // + .encryptDatagram(crypto, key) // + .encryptKey(public_key_B) // + .constructDataPacket() // + ; + // Utils.print(cipher1.message); + // Utils.print(cipher1.packet); + Utils.print(cipher1.digest); + + cipher2 // + .setPacket(cipher1.getPacket()) // + .deconstructDataPacket() // + .decryptKey(complete_key_B) // + .decryptDatagram(crypto) // + .deconstructDatagram() // + .verifySignature(public_key_A) // + .buildDigest(digest) // + ; + // Utils.print(cipher2.packet); + // Utils.print(cipher2.message); + Utils.print(cipher2.digest); + Utils.print(cipher2._digest); + } +} diff --git a/assignment/src/assignment/cryptography/CipherConstants.java b/assignment/src/assignment/cryptography/CipherConstants.java new file mode 100644 index 0000000..c393f93 --- /dev/null +++ b/assignment/src/assignment/cryptography/CipherConstants.java @@ -0,0 +1,43 @@ +package assignment.cryptography; + +public interface CipherConstants +{ + public static final int KEY_LENGTH = 32; // 密钥长度(单位: Byte) + public static final int DES_KEY_LENGTH = 8; // DES 密钥长度(单位: Byte) + public static final int AES_KEY_LENGTH_128 = 16; // DES 密钥长度(单位: Byte) + public static final int AES_KEY_LENGTH_192 = 24; // DES 密钥长度(单位: Byte) + public static final int AES_KEY_LENGTH_256 = 32; // DES 密钥长度(单位: Byte) + + public static final int COMPLETE_KEY_MIN_LENGTH = 260; // 公钥密码密钥最小长度(单位: bit) + public static final int COMPLETE_KEY_MAX_LENGTH = 4096; // 公钥密码密钥最大长度(单位: bit) + public static final int CIPHERTEXT_BLOCK_LENGTH = 16; // 密文数据分组长度(单位: Byte) + // public static final int CIPHERTEXT_INIT_VECTOR_LENGTH = 16; // 密文数据初始向量长度(单位: Byte) + // public static final int CIPHERTEXT_MODE_OFFSET = 0; // 密文数据中 摘要算法的偏移量(单位: Byte) + // public static final int CIPHERTEXT_MODE_OFFSET = 0; // 密文数据中 签名算法的偏移量(单位: Byte) + // public static final int CIPHERTEXT_MODE_OFFSET = 0; // 密文数据中 加密算法的偏移量(单位: Byte) + public static final int CIPHERTEXT_MODE_OFFSET = 0; // 密文数据中 分组模式的偏移量(单位: Byte) + public static final int CIPHERTEXT_EFFECTIVE_OFFSET = 1; // 密文数据中 末尾16字节有效位数的偏移量(单位: Byte) + public static final int CIPHERTEXT_INIT_VECTOR_OFFSET = 2; // 密文数据中 初始向量的的偏移量(单位: Byte) + public static final int CIPHERTEXT_DATA_OFFSET = 18; // 密文数据中 加密数据的的偏移量(单位: Byte) + + public static final byte CIPHERTEXT_FULL_VALUE = 0; // 密文数据中 末尾16字节填充值 + + public static final int DEGEST_ALGORITHM_MD5 = 0; // 下拉选项卡选项, 摘要算法 MD5 + public static final int DEGEST_ALGORITHM_SHA = 1; // 下拉选项卡选项, 摘要算法 SHA + + public static final int SIGNATURE_ALGORITHM_RSA = 0; // 下拉选项卡选项, 签名算法 RSA + + public static final int ENCRYPT_ALGORITHM_DES = 0; // 下拉选项卡选项, 加密算法 DES + public static final int ENCRYPT_ALGORITHM_AES = 1; // 下拉选项卡选项, 加密算法 AES + + public static final int ENCRYPT_KEY_LENGTH_064 = 0; // 下拉选项卡选项, 对称加密密钥长度 + public static final int ENCRYPT_KEY_LENGTH_128 = 1; // 下拉选项卡选项, 对称加密密钥长度 + public static final int ENCRYPT_KEY_LENGTH_192 = 2; // 下拉选项卡选项, 对称加密密钥长度 + public static final int ENCRYPT_KEY_LENGTH_256 = 3; // 下拉选项卡选项, 对称加密密钥长度 + + public static final int ENCRYPT_MODE_ECB = 0; // 分组加密模式:ECB + public static final int ENCRYPT_MODE_CBC = 1; // 分组加密模式:CBC + + public static final int DATA_FORMAT_BYTE = 0; // 数据格式: 字节数组 + public static final int DATA_FORMAT_FILE = 1; // 数据格式: 文件 +} diff --git a/assignment/src/assignment/cryptography/EncryptAndDecryptDialog.java b/assignment/src/assignment/cryptography/EncryptAndDecryptDialog.java new file mode 100644 index 0000000..8fb4883 --- /dev/null +++ b/assignment/src/assignment/cryptography/EncryptAndDecryptDialog.java @@ -0,0 +1,470 @@ +package assignment.cryptography; + +import javax.swing.*; + +import java.awt.event.*; +import java.awt.*; + +import java.io.*; + +import java.util.concurrent.CancellationException; + +public class EncryptAndDecryptDialog // 加解密对话框 +{ + private static final int DIALOG_WIDTH = 900; // 窗口宽度 + private static final int DIALOG_HEIGHT = 160; // 窗口高度 + + private static final int MIN_PROGRESS_BAR = 0; // 进度条最小值 + private static final int MAX_PROGRESS_BAR = 1000; // 进度条最大值 + + public static final int MODE_TEST = 0; // 模式-对话框测试 + public static final int MODE_ENCRYPT = 1; // 模式-加密 + public static final int MODE_DECRYPT = 2; // 模式-解密 + + private static final long STATUS_UPDATE_INTERVAL = 100000000; // 更新状态信息的间隔(单位ns) + + private static final String FORMAT_STATUS_NOT_STARTED = "%s未开始"; // 状态-未开始 格式化文本 + private static final String FORMAT_STATUS_ONGOING = "正在进行%s"; // 状态-正在进行 格式化文本 + private static final String FORMAT_STATUS_COMPLETE = "%s完成"; // 状态-完成 格式化文本 + + private static final String FORMAT_SPEED_REAL_TIME = "%s实时速度: %d byte/s"; // 实时速度格式化文本 + private static final String FORMAT_SPEED_AVERAGE = "%s平均速度: %d byte/s"; // 平均速度格式化文本 + + private static final String FORMAT_TIME_ELAPSED = "已用时间: %.9f s"; // 已用时间格式化文本 + private static final String FORMAT_TIME_ESTIMATED = "预计剩余时间: %.9f s"; // 预计剩余时间格式化文本 + + private JDialog dialog; // 对话框 + private JProgressBar bar_progress; // 进度条 + private JLabel label_status; // 状态 + private JLabel label_speed; // 速度 + private JLabel label_time_elapsed; // 已用时间 + private JLabel label_time_estimated; // 预计剩余时间 + private JButton button_confirm; // 确定按钮 + + private int function; // 功能 + private int mode; // 模式 + private byte[] key; // 加解密密钥 + private File file_in; // 输入的文件 + private File dir_out; // 输出的目录 + + private long time_elapsed_nano = 0; // 已用时间(单位ns) + private long time_estimated_nano = 0; // 预计剩余时间(单位ns) + private long byte_num_processed = 0; // 已处理比特数 + private long byte_num_total = 0; // 总比特数 + private long byte_num_per_second = 0; // 处理速度 + + /** 构造函数 + * @param owner JFrame 依赖的上层窗口 + * @param parentComponent Component 用于定位的面板 + * @param title String 对话框的标题 + * @param callFunction int 调用的功能 0: 测试; 1: 加密; 2: 解密 + * @param encryptMode int 加密模式, 解密时可为任意值 + * @param secretKey byte[] 加解密密钥 + * @param fileInput File 输入的待加密/待解密文件(不进行合法性检查) + * @param dirOutput File 加密/解密文件的输出目录(不进行合法性检查) + */ + EncryptAndDecryptDialog( // + JFrame owner, // + Component parentComponent, // + String title, // + int callFunction, // + int encryptMode, // + byte[] secretKey, // + File fileInput, // + File dirOutput // + ) + { + this.function = callFunction; + this.mode = encryptMode; + this.key = secretKey; + this.file_in = fileInput; + this.dir_out = dirOutput; + + this.dialog = new JDialog(owner, title, false); // 所有者, 标题, 是否为模态对话框 + this.dialog.setSize(EncryptAndDecryptDialog.DIALOG_WIDTH, EncryptAndDecryptDialog.DIALOG_HEIGHT); + this.dialog.setResizable(false); // 禁止改变大小 + this.dialog.setLocationRelativeTo(parentComponent); // 设置相对显示位置 + this.dialog.setLayout(new GridBagLayout()); // 网格包布局 + + this.bar_progress = new JProgressBar(JProgressBar.HORIZONTAL, EncryptAndDecryptDialog.MIN_PROGRESS_BAR, EncryptAndDecryptDialog.MAX_PROGRESS_BAR); // 水平方向进度条 + this.bar_progress.setStringPainted(true); // 显示百分比文本 + + this.label_status = new JLabel(String.format(EncryptAndDecryptDialog.FORMAT_STATUS_NOT_STARTED, title)); + this.label_speed = new JLabel(String.format(EncryptAndDecryptDialog.FORMAT_SPEED_REAL_TIME, title, 0)); + this.label_time_elapsed = new JLabel(String.format(EncryptAndDecryptDialog.FORMAT_TIME_ELAPSED, 0.0)); + this.label_time_estimated = new JLabel(String.format(EncryptAndDecryptDialog.FORMAT_TIME_ESTIMATED, 0.0)); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.CENTER; // 组件在显示区域居中对齐 + gbc.fill = GridBagConstraints.HORIZONTAL; // 设置组件填充区域 + gbc.insets = new Insets(5, 5, 5, 5); // 设置与显示区域边缘的位置 + + SwingWorker task = new SwingWorker() + { + @Override + protected Long doInBackground() throws Exception + { + bar_progress.setValue(EncryptAndDecryptDialog.MIN_PROGRESS_BAR); + label_status.setText(String.format(EncryptAndDecryptDialog.FORMAT_STATUS_ONGOING, title)); + + long time_begin = System.nanoTime(); + long timer_begin = time_begin; + long timer_now = timer_begin; + + /** + * 0: 需要处理的总比特数 + * 1: 已经处理的比特数 + * 2: 当前已用时间 + * 3: 当前周期处理的比特数 + * 4: 当前周期所用时间 + */ + Long progress_value[] = new Long[5]; + for (int i = 0; i < progress_value.length; i++) { + progress_value[i] = Long.valueOf(0); + } + + switch (function) { + case EncryptAndDecryptDialog.MODE_TEST: // 测试 + + break; + case EncryptAndDecryptDialog.MODE_ENCRYPT: // 加密 + try { + String filename = file_in.getName(); + String dir = dir_out.getPath(); + long plaintext_length = file_in.length(); + progress_value[0] = plaintext_length; + + FileInputStream plaintext_stream = new FileInputStream(file_in); + FileOutputStream ciphertext_stream = new FileOutputStream(String.format(GUI.FORMAT_TARGET_FILE_PATH, dir, filename + GUI.ENCRYPTED_FILENAME_EXTENSION)); + + long effective = plaintext_length % GUI.CIPHERTEXT_BLOCK_LENGTH; + long grout_num = plaintext_length / GUI.CIPHERTEXT_BLOCK_LENGTH; + grout_num += ((effective != 0) ? 1 : 0); // 填充后的分组数 + + /** + * 1字节表示加密模式 + * 1字节表示最后16字节有效字节数 + * 16字节表示初始化向量 + */ + byte data1[] = new byte[GUI.CIPHERTEXT_BLOCK_LENGTH]; + + switch (mode) { + case GUI.ENCRYPT_MODE_ECB: // ECB 模式 + ciphertext_stream.write((byte) GUI.ENCRYPT_MODE_ECB); + ciphertext_stream.write((byte) effective); + ciphertext_stream.write(Utils.getHashByteString(Utils.getRandomString(32), GUI.CIPHERTEXT_BLOCK_LENGTH)); + + for (long i = 0; i < plaintext_length - effective; i += GUI.CIPHERTEXT_BLOCK_LENGTH) { + timer_now = System.nanoTime(); + if (timer_now - timer_begin >= EncryptAndDecryptDialog.STATUS_UPDATE_INTERVAL) { // 定时更新一次状态 + progress_value[3] = i - progress_value[1]; // 当前周期处理的比特数 + progress_value[1] = i; // 已经处理的比特数 + + progress_value[4] = timer_now - timer_begin; // 当前周期所用时间 + progress_value[2] = timer_now - time_begin; // 当前已用时间 + timer_begin = timer_now; + // 通过 SwingWorker 内部机制传递数据到事件调度线程 + publish(progress_value); + } + + plaintext_stream.read(data1); + // GUI.encrypt(GUI.ENCRYPT_MODE_ECB, key, data1, null); + ciphertext_stream.write(data1); + } + if (effective != 0) { // 加密最后的16字节 + data1 = Utils.getHashByteString(Utils.getRandomString(32), GUI.CIPHERTEXT_BLOCK_LENGTH); + plaintext_stream.read(data1, 0, (int) effective); + // GUI.encrypt(GUI.ENCRYPT_MODE_ECB, key, data1, null); + ciphertext_stream.write(data1); + } + + break; + + case GUI.ENCRYPT_MODE_CBC: // CBC 模式 + // 为初始向量赋值 + byte data2[] = new byte[GUI.CIPHERTEXT_BLOCK_LENGTH]; + System.arraycopy(Utils.getHashByteString(Utils.getRandomString(32), GUI.CIPHERTEXT_BLOCK_LENGTH), 0, data2, 0, GUI.CIPHERTEXT_BLOCK_LENGTH); + + ciphertext_stream.write((byte) GUI.ENCRYPT_MODE_CBC); + ciphertext_stream.write((byte) effective); + ciphertext_stream.write(data2); + + for (long i = 0; i < plaintext_length - effective; i += GUI.CIPHERTEXT_BLOCK_LENGTH) { + timer_now = System.nanoTime(); + if (timer_now - timer_begin >= EncryptAndDecryptDialog.STATUS_UPDATE_INTERVAL) { // 定时更新一次状态 + progress_value[3] = i - progress_value[1]; // 当前周期处理的比特数 + progress_value[1] = i; // 已经处理的比特数 + + progress_value[4] = timer_now - timer_begin; // 当前周期所用时间 + progress_value[2] = timer_now - time_begin; // 当前已用时间 + timer_begin = timer_now; + // 通过 SwingWorker 内部机制传递数据到事件调度线程 + publish(progress_value); + } + + if (((i / GUI.CIPHERTEXT_BLOCK_LENGTH + 1) & 1) == 1) { + plaintext_stream.read(data1); + // GUI.encrypt(GUI.ENCRYPT_MODE_CBC, key, data1, data2); + ciphertext_stream.write(data1); + } else { + plaintext_stream.read(data2); + // GUI.encrypt(GUI.ENCRYPT_MODE_CBC, key, data2, data1); + ciphertext_stream.write(data2); + } + } + if (effective != 0) { // 加密最后的16字节 + if ((grout_num & 1) == 1) { + data1 = Utils.getHashByteString(Utils.getRandomString(32), GUI.CIPHERTEXT_BLOCK_LENGTH); + plaintext_stream.read(data1, 0, (int) effective); + // GUI.encrypt(GUI.ENCRYPT_MODE_CBC, key, data1, data2); + ciphertext_stream.write(data1); + } else { + data2 = Utils.getHashByteString(Utils.getRandomString(32), GUI.CIPHERTEXT_BLOCK_LENGTH); + plaintext_stream.read(data2, 0, (int) effective); + // GUI.encrypt(GUI.ENCRYPT_MODE_CBC, key, data2, data1); + ciphertext_stream.write(data2); + } + } + + break; + + default: + break; + } + + plaintext_stream.close(); + ciphertext_stream.close(); + progress_value[1] = progress_value[0]; + + } catch (IOException e) { + Utils.showExceptionDialog("文件读写异常", e); + this.cancel(true); + } + break; + case EncryptAndDecryptDialog.MODE_DECRYPT: // 解密 + try { + String filename = file_in.getName(); + String dir = dir_out.getPath(); + long plaintext_length = file_in.length() - GUI.CIPHERTEXT_DATA_OFFSET; + progress_value[0] = plaintext_length; + + FileInputStream ciphertext_stream = new FileInputStream(file_in); + + FileOutputStream plaintext_stream = null; + if (filename.endsWith(GUI.ENCRYPTED_FILENAME_EXTENSION)) { // 若待解密文件拓展名为设置的拓展名, 则移除该拓展名 + plaintext_stream = new FileOutputStream(String.format(GUI.FORMAT_TARGET_FILE_PATH, dir, filename.substring(0, filename.lastIndexOf(GUI.ENCRYPTED_FILENAME_EXTENSION)))); + } else { // 若待解密文件拓展名不为设置的拓展名, 则为其添加表示已解密的拓展名 + plaintext_stream = new FileOutputStream(String.format(GUI.FORMAT_TARGET_FILE_PATH, dir, filename + GUI.DECRYPTED_FILENAME_EXTENSION)); + } + + int mode = ciphertext_stream.read(); + long effective = ciphertext_stream.read(); + long grout_num = plaintext_length / GUI.CIPHERTEXT_BLOCK_LENGTH; + grout_num += ((effective != 0) ? 1 : 0); // 填充后的分组数 + + /** + * 1字节表示加密模式 + * 1字节表示最后16字节有效字节数 + * 16字节表示初始化向量 + */ + byte data1[] = new byte[GUI.CIPHERTEXT_BLOCK_LENGTH]; + + switch (mode) { + case GUI.ENCRYPT_MODE_ECB: // ECB 模式 + ciphertext_stream.skip(GUI.CIPHERTEXT_BLOCK_LENGTH); // 舍弃初始向量 + + for (long i = 0; i < plaintext_length - effective; i += GUI.CIPHERTEXT_BLOCK_LENGTH) { + timer_now = System.nanoTime(); + if (timer_now - timer_begin >= EncryptAndDecryptDialog.STATUS_UPDATE_INTERVAL) { // 定时更新一次状态 + progress_value[3] = i - progress_value[1]; // 当前周期处理的比特数 + progress_value[1] = i; // 已经处理的比特数 + + progress_value[4] = timer_now - timer_begin; // 当前周期所用时间 + progress_value[2] = timer_now - time_begin; // 当前已用时间 + timer_begin = timer_now; + // 通过 SwingWorker 内部机制传递数据到事件调度线程 + publish(progress_value); + } + + ciphertext_stream.read(data1); + // GUI.decrypt(GUI.ENCRYPT_MODE_ECB, key, data1, null); + plaintext_stream.write(data1); + } + if (effective != 0) { // 解密最后的16字节 + ciphertext_stream.read(data1); + // GUI.decrypt(GUI.ENCRYPT_MODE_ECB, key, data1, null); + plaintext_stream.write(data1, 0, (int) effective); + } + progress_value[1] = plaintext_length; + + break; + + case GUI.ENCRYPT_MODE_CBC: // CBC 模式 + // 为初始向量赋值 + byte data2[] = new byte[GUI.CIPHERTEXT_BLOCK_LENGTH]; + byte data3[] = new byte[GUI.CIPHERTEXT_BLOCK_LENGTH]; + ciphertext_stream.read(data2); + + for (long i = 0; i < plaintext_length - effective; i += GUI.CIPHERTEXT_BLOCK_LENGTH) { + timer_now = System.nanoTime(); + if (timer_now - timer_begin >= EncryptAndDecryptDialog.STATUS_UPDATE_INTERVAL) { // 定时更新一次状态 + progress_value[3] = i - progress_value[1]; // 当前周期处理的比特数 + progress_value[1] = i; // 已经处理的比特数 + + progress_value[4] = timer_now - timer_begin; // 当前周期所用时间 + progress_value[2] = timer_now - time_begin; // 当前已用时间 + timer_begin = timer_now; + // 通过 SwingWorker 内部机制传递数据到事件调度线程 + publish(progress_value); + } + + if (((i / GUI.CIPHERTEXT_BLOCK_LENGTH + 1) & 1) == 1) { + ciphertext_stream.read(data1); + System.arraycopy(data1, 0, data3, 0, GUI.CIPHERTEXT_BLOCK_LENGTH); + // GUI.decrypt(GUI.ENCRYPT_MODE_CBC, key, data1, data2); + plaintext_stream.write(data1); + } else { + ciphertext_stream.read(data1); + System.arraycopy(data1, 0, data2, 0, GUI.CIPHERTEXT_BLOCK_LENGTH); + // GUI.decrypt(GUI.ENCRYPT_MODE_CBC, key, data1, data3); + plaintext_stream.write(data1); + } + } + if (effective != 0) { // 解密最后的16字节 + if ((grout_num & 1) == 1) { + ciphertext_stream.read(data1); + // GUI.decrypt(GUI.ENCRYPT_MODE_CBC, key, data1, data2); + plaintext_stream.write(data1, 0, (int) effective); + } else { + ciphertext_stream.read(data1); + // GUI.decrypt(GUI.ENCRYPT_MODE_CBC, key, data1, data3); + plaintext_stream.write(data1, 0, (int) effective); + } + } + progress_value[1] = plaintext_length; + + break; + + default: + break; + } + plaintext_stream.close(); + ciphertext_stream.close(); + + } catch (IOException e) { + Utils.showExceptionDialog("文件读写异常", e); + this.cancel(true); + } + break; + default: + break; + } + + timer_now = System.nanoTime(); + progress_value[3] = progress_value[0] - progress_value[1]; // 当前周期处理的比特数 + progress_value[1] = progress_value[0]; // 已经处理的比特数 + progress_value[4] = timer_now - timer_begin; // 当前周期所用时间 + progress_value[2] = timer_now - time_begin; // 当前已用时间 + + // 通过 SwingWorker 内部机制传递数据到事件调度线程 + publish(progress_value); + + // 设置 progress 属性的值(通过属性改变监听器传递数据到事件调度线程) + // setProgress(i); // 参数介于0~100 + + // 通过 SwingWorker 内部机制传递数据到事件调度线程 + // publish(progress_value); + + long time_end = System.nanoTime(); + Long time_cost_total = time_end - time_begin; + + // 返回计算结果 + return time_cost_total; + } + + @Override + protected void process(java.util.List chunks) + { + // 此方法在 调用 doInBackground 调用 public 方法后在事件调度线程中被回调 + Long[] progress_value = chunks.get(0); + /** + * 0: 需要处理的总比特数 + * 1: 已经处理的比特数 + * 2: 当前已用时间 + * 3: 当前周期处理的比特数 + * 4: 当前周期所用时间 + */ + byte_num_total = progress_value[0]; + byte_num_processed = progress_value[1]; + time_elapsed_nano = progress_value[2]; + byte_num_per_second = (long) (progress_value[3] * 1000000000.0 / progress_value[4]); + time_estimated_nano = (long) ((byte_num_total - byte_num_processed) * 1000000000.0 / byte_num_per_second); + + bar_progress.setValue((int) (progress_value[1] * (double) (EncryptAndDecryptDialog.MAX_PROGRESS_BAR) / progress_value[0])); + label_speed.setText(String.format(EncryptAndDecryptDialog.FORMAT_SPEED_REAL_TIME, title, byte_num_per_second)); + label_time_elapsed.setText(String.format(EncryptAndDecryptDialog.FORMAT_TIME_ELAPSED, time_elapsed_nano / 1000000000.0)); + label_time_estimated.setText(String.format(EncryptAndDecryptDialog.FORMAT_TIME_ESTIMATED, time_estimated_nano / 1000000000.0)); + + } + + @Override + protected void done() + { + // 此方法将在后台任务完成后在事件调度线程中被回调 + try { + // 获取计算结果 + if (this.isDone()) { + time_elapsed_nano = get(); + bar_progress.setValue(EncryptAndDecryptDialog.MAX_PROGRESS_BAR); + label_status.setText(String.format(EncryptAndDecryptDialog.FORMAT_STATUS_COMPLETE, title)); + label_speed.setText(String.format(EncryptAndDecryptDialog.FORMAT_SPEED_AVERAGE, title, (long) (byte_num_total * 1000000000.0 / time_elapsed_nano))); + label_time_elapsed.setText(String.format(EncryptAndDecryptDialog.FORMAT_TIME_ELAPSED, time_elapsed_nano / 1000000000.0)); + label_time_estimated.setText(String.format(EncryptAndDecryptDialog.FORMAT_TIME_ESTIMATED, 0.0)); + button_confirm.setText("确定"); + } + } catch (CancellationException e) { + return; + } catch (Exception e) { + Utils.showExceptionDialog(title + "过程异常!", e); + System.exit(0); + } + } + }; + + // task.addPropertyChangeListener(new PropertyChangeListener() + // { + // @Override + // public void propertyChange(PropertyChangeEvent evt) // 捕获setProgress(i);改变progress值事件 + // { + // if ("progress".equals(evt.getPropertyName())) { + + // } + // } + // }); + + this.button_confirm = new JButton("取消"); + this.button_confirm.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + task.cancel(true); + // 关闭对话框 + dialog.dispose(); + } + }); + + Container dialog_contain = this.dialog.getContentPane(); // 获取对话框的内容窗格 + GUI.addComponent(0, 0, GridBagConstraints.REMAINDER, 1, 1, 1, 5, 5, this.bar_progress, dialog_contain, gbc); + GUI.addComponent(1, 0, 2, 1, 2, 1, 5, 5, this.label_status, dialog_contain, gbc); + GUI.addComponent(1, 2, 2, 1, 2, 1, 5, 5, this.label_speed, dialog_contain, gbc); + GUI.addComponent(1, 4, 2, 1, 2, 1, 5, 5, this.label_time_elapsed, dialog_contain, gbc); + GUI.addComponent(1, 6, 2, 1, 2, 1, 5, 5, this.label_time_estimated, dialog_contain, gbc); + GUI.addComponent(2, 7, 1, 1, 1, 1, 5, 5, this.button_confirm, dialog_contain, gbc); + + // 启动任务 + task.execute(); + + this.dialog.setVisible(true); + } +} diff --git a/assignment/src/assignment/cryptography/GUI.java b/assignment/src/assignment/cryptography/GUI.java new file mode 100644 index 0000000..1327065 --- /dev/null +++ b/assignment/src/assignment/cryptography/GUI.java @@ -0,0 +1,1837 @@ +package assignment.cryptography; + +import assignment.cryptography.crypto.AES; +import assignment.cryptography.crypto.CryptoInterface; +import assignment.cryptography.crypto.DES; +import assignment.cryptography.hash.DigestBase; +import assignment.cryptography.hash.MD5; +import assignment.cryptography.hash.SHA; +import assignment.cryptography.rsa.RSACompleteKey; +import assignment.cryptography.rsa.RSAKeyGenerator; +import assignment.cryptography.rsa.RSAPublicKey; + +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.filechooser.FileFilter; + +import java.awt.event.*; +import java.awt.*; + +import java.io.*; +import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class GUI implements CipherConstants +{ + private static final int WINDOW_WIDTH = 1920; // 窗口宽度 + private static final int WINDOW_HEIGHT = 1080; // 窗口高度 + private static final int FIELD_COLUMNS = 72; // 输入框宽度 + private static final int FIELD_FONT_SIZE = 16; // 输入框字号 + + private static final String DEFAULT_CHAR_SET; // 默认字符集 + private static final String ENCRYPT_CHAR_SET; // 密文展示字符集 + + private static final String DIGEST_ALGORITHM_LIST[]; // 下拉选项卡选项, 摘要算法 + private static final String SIGNATURE_ALGORITHM_LIST[]; // 下拉选项卡选项, 签名算法 + private static final String ENCRYPT_ALGORITHM_LIST[]; // 下拉选项卡选项, 加密算法 + private static final String ENCRYPT_KEY_LENGTH_LIST[]; // 下拉选项卡选项, 对称加密密钥长度 + private static final String ENCRYPT_MODE_LIST[]; // 下拉选项卡选项, 分组加密模式 + + public static final String ROOT_PATH_SOURCE; // 源文件选择窗口的起始目录 + public static final String ROOT_PATH_TARGET; // 目标文件选择窗口的起始目录 + public static final String ROOT_PATH_RSAKEY; // 密钥保存目录 + + public static final String FORMAT_CHARACTERS; // 格式化显示字符数 + public static final String FORMAT_FILE_SIZE; // 格式化显示文件大小 + public static final String FORMAT_TARGET_FILE_PATH; // 格式化目标文件路径 + public static final String FORMAT_KEY_FORMAT_MESSAGE; // 格式化密钥格式不规范输出信息 + + public static final String RSAKEY_FILENAME_EXTENSION; // RSA 密钥扩展名 + public static final String ENCRYPTED_FILENAME_EXTENSION; // 加密文件拓展名 + public static final String DECRYPTED_FILENAME_EXTENSION; // 若待解密文件拓展名不为加密文件拓展名, 则使用该解密文件拓展名 + + private JFrame frame; // 窗口对象 + + private JTabbedPane pane_tab_data; // 数据选项卡窗格对象 + private JTabbedPane pane_tab_user; // 用户选项卡窗格对象 + + private JSplitPane pane_split_plaintext; // 明文文本区的分隔面板 + private JSplitPane pane_split_ciphertext; // 密文文本区的分隔面板 + + private JScrollPane pane_scroll_plaintext; // 明文文本区滚动条 + private JScrollPane pane_scroll_ciphertext; // 密文文本区滚动条 + private JScrollPane pane_scroll_plaintext_hex; // 明文16进制编码文本区滚动条 + private JScrollPane pane_scroll_ciphertext_hex; // 密文16进制编码文本区滚动条 + + private JPanel panel_parameter; // 算法参数面板 + private JPanel panel_user_A; // 用户 A 操作面板 + private JPanel panel_user_B; // 用户 B 操作面板 + private JPanel panel_string; // 字符串加解密选项卡 + private JPanel panel_file_encrypt; // 文件加密选项卡 + private JPanel panel_file_decrypt; // 文件解密选项卡 + + private JLabel label_complete_key_length; // 公钥密码完整密钥长度 + private JLabel label_digest_algorithm; // 摘要算法 + private JLabel label_signature_algorithm; // 签名算法 + private JLabel label_encrypt_algorithm; // 加密算法 + private JLabel label_encrypt_key_length; // 对称密码密钥长度 + private JLabel label_encrypt_mode; // 加密方式(分组密码模式) + + private JLabel label_password_A; // 用户 A 口令 + private JLabel label_key_A; // 用户 A 对称加密密钥 + private JLabel label_n_A_A; // A 公钥密码模数 n + private JLabel label_e_A_A; // A 公钥密码公钥参数 e + private JLabel label_d_A; // A 公钥密码私钥参数 d + private JLabel label_p_A; // A 公钥密码私钥参数 p + private JLabel label_q_A; // A 公钥密码私钥参数 q + private JLabel label_dP_A; // A 公钥密码私钥参数 dP + private JLabel label_dQ_A; // A 公钥密码私钥参数 dQ + private JLabel label_qInv_A; // A 公钥密码私钥参数 qInv + + private JLabel label_n_A_B; // 显示在 B 面板的 A 公钥密码模数 n + private JLabel label_e_A_B; // 显示在 B 面板的 A 公钥密码公钥参数 e + + private JLabel label_password_B; // 用户 B 口令 + private JLabel label_key_B; // 用户 B 对称加密密钥 + private JLabel label_n_B_B; // B 公钥密码模数 n + private JLabel label_e_B_B; // B 公钥密码公钥参数 e + private JLabel label_d_B; // B 公钥密码私钥参数 d + private JLabel label_p_B; // B 公钥密码私钥参数 p + private JLabel label_q_B; // B 公钥密码私钥参数 q + private JLabel label_dP_B; // B 公钥密码私钥参数 dP + private JLabel label_dQ_B; // B 公钥密码私钥参数 dQ + private JLabel label_qInv_B; // B 公钥密码私钥参数 qInv + + private JLabel label_n_B_A; // 显示在 B 面板的 A 公钥密码模数 n + private JLabel label_e_B_A; // 显示在 B 面板的 A 公钥密码公钥参数 e + + private JLabel label_plaintext; // 明文 + private JLabel label_ciphertext; // 密文 + private JLabel label_plaintext_characters; // 明文字符数 + private JLabel label_ciphertext_characters; // 密文字符数 + // private JLabel label_plaintext_encode; // 明文编码(16进制编码) + // private JLabel label_ciphertext_encode; // 密文编码(16进制编码) + // private JLabel label_plaintext_file; // 明文文件 + // private JLabel label_ciphertext_file; // 密文文件 + private JLabel label_plaintext_target_file; // 待加密文件 + private JLabel label_ciphertext_target_file; // 待解密文件 + private JLabel label_plaintext_target_dir; // 解密文件输出目录 + private JLabel label_ciphertext_target_dir; // 加密文件输出目录 + private JLabel label_plaintext_file_size; // 明文文件大小 + private JLabel label_ciphertext_file_size; // 密文文件大小 + + private JTextField field_password_A; // 用户 A 口令 + private JTextField field_key_A; // 用户 A 密钥 + private JTextField field_n_A_A; // A 公钥密码模数 n + private JTextField field_e_A_A; // A 公钥密码公钥参数 e + private JTextField field_d_A; // A 公钥密码私钥参数 d + private JTextField field_p_A; // A 公钥密码私钥参数 p + private JTextField field_q_A; // A 公钥密码私钥参数 q + private JTextField field_dP_A; // A 公钥密码私钥参数 dP + private JTextField field_dQ_A; // A 公钥密码私钥参数 dQ + private JTextField field_qInv_A; // A 公钥密码私钥参数 qInv + + private JTextField field_n_A_B; // 显示在 B 面板的 A 公钥密码模数 n + private JTextField field_e_A_B; // 显示在 B 面板的 A 公钥密码公钥参数 e + + private JTextField field_password_B; // 用户 B 口令 + private JTextField field_key_B; // 用户 B 密钥 + private JTextField field_n_B_B; // B 公钥密码模数 n + private JTextField field_e_B_B; // B 公钥密码公钥参数 e + private JTextField field_d_B; // B 公钥密码私钥参数 d + private JTextField field_p_B; // B 公钥密码私钥参数 p + private JTextField field_q_B; // B 公钥密码私钥参数 q + private JTextField field_dP_B; // B 公钥密码私钥参数 dP + private JTextField field_dQ_B; // B 公钥密码私钥参数 dQ + private JTextField field_qInv_B; // B 公钥密码私钥参数 qInv + + private JTextField field_n_B_A; // 显示在 A 面板的 B 公钥密码模数 n + private JTextField field_e_B_A; // 显示在 A 面板的 B 公钥密码公钥参数 e + + private JTextField field_plaintext_file_path; // 明文文件路径 + private JTextField field_ciphertext_file_path; // 密文文件路径 + private JTextField field_plaintext_dir_path; // 明文目录路径 + private JTextField field_ciphertext_dir_path; // 密文目录路径 + + private JTextArea area_plaintext; // 明文字符串文本区 + private JTextArea area_ciphertext; // 密文字符串文本区 + private JTextArea area_plaintext_hex; // 明文16进制编码文本区 + private JTextArea area_ciphertext_hex; // 密文16进制编码文本区 + + private JFileChooser file_chooser_complete_key_import_A; // 导入 A 公私密钥对 + private JFileChooser file_chooser_complete_key_import_B; // 导入 B 公私密钥对 + private JFileChooser dir_chooser_complete_key_export_A; // 导出 A 公私密钥对 + private JFileChooser dir_chooser_complete_key_export_B; // 导出 B 公私密钥对 + + private JFileChooser file_chooser_plaintext; // 明文文件选择对话框 + private JFileChooser file_chooser_ciphertext; // 密文文件选择对话框 + private JFileChooser dir_chooser_plaintext; // 解密文件输出目录选择对话框 + private JFileChooser dir_chooser_ciphertext; // 加密文件输出目录选择对话框 + + private JButton button_key_generation_random_A; // A 随机生成口令与密钥 + private JButton button_key_standard_A; // A 检查密钥格式是否规范 + private JButton button_complete_key_generation_A; // 随机生成 A 公私密钥对 + private JButton button_complete_key_import_A; // 导入 A 公私密钥对 + private JButton button_complete_key_export_A; // 导出 A 公私密钥对 + + private JButton button_encrypt_A; // A 加密 + private JButton button_decrypt_A; // A 解密 + private JButton button_verify_A; // A 校验 + + private JButton button_key_generation_random_B; // B 随机生成口令与密钥 + private JButton button_key_standard_B; // B 检查密钥格式是否规范 + private JButton button_complete_key_generation_B; // 随机生成 B 公私密钥对 + private JButton button_complete_key_import_B; // 导入 B 公私密钥对 + private JButton button_complete_key_export_B; // 导出 B 公私密钥对 + + private JButton button_encrypt_B; // B 加密 + private JButton button_decrypt_B; // B 解密 + private JButton button_verify_B; // B 校验 + + private JButton button_select_plaintext_file; // 选择明文文件 + private JButton button_select_ciphertext_file; // 选择密文文件 + private JButton button_select_plaintext_dir; // 选择明文目录 + private JButton button_select_ciphertext_dir; // 选择密文目录 + + private JSpinner spinner_complete_key_length; // 公钥密码完整密钥长度(单位: bit) + + private JComboBox box_digest_algorithm; // 下拉选项卡, 摘要算法 + private JComboBox box_signature_algorithm; // 下拉选项卡, 签名算法 + private JComboBox box_encrypt_algorithm; // 下拉选项卡, 加密算法 + private JComboBox box_encrypt_key_length; // 下拉选项卡, 加密算法密钥长度 + private JComboBox box_encrypt_mode; // 下拉选项卡, 分组加密模式 + + // private JProgressBar bar_encrypt_progress; // 文件加密进度条 + // private JProgressBar bar_decrypt_progress; // 文件解密进度条 + + private GridBagConstraints gbc_both; // 网格包管理器(全部填充) + private GridBagConstraints gbc_horizontal; // 网格包管理器(仅水平填充) + + private byte key[]; // 密钥 + private int key_len = GUI.DES_KEY_LENGTH; // 密钥长度 + private DigestBase digest = new MD5(); // 摘要算法 + private CryptoInterface crypto = new DES(); // 加密算法 + private Cipher cipher = new Cipher(GUI.ENCRYPT_MODE_ECB); // 密码机 + private RSACompleteKey complete_key; // RSA 私钥 + private RSAPublicKey public_key; // RSA 公钥 + private String digest_string; // 摘要字符串 + private String _digest_string; // 签名的摘要字符串 + + private File file_rsakey_A; // 用户 A 密钥导入文件 + private File file_rsakey_B; // 用户 B 密钥导入文件 + private File dir_rsakey_A; // 用户 A 密钥导出目录 + private File dir_rsakey_B; // 用户 B 密钥导出目录 + + private File file_plaintext; // 待加密文件 + private File file_ciphertext; // 待解密文件 + private File dir_plaintext; // 解密后文件保存目录 + private File dir_ciphertext; // 加密后文件保存目录 + + // private EncryptAndDecryptDialog dialog_encryption; // 文件加密对话框 + // private EncryptAndDecryptDialog dialog_decryption; // 文件解密对话框 + + /* + interface DLL_AES extends Library + { + DLL_AES dll_AES = (DLL_AES) Native.loadLibrary("lib\\DLL_AES", DLL_AES.class); + public void AES_encrypt(byte key[], int data[]); + public void AES_decrypt(byte key[], int data[]); + } + DLL_AES.dll_AES.AES_encrypt(key, data); + DLL_AES.dll_AES.AES_decrypt(key, data); + */ + + static { + // 静态属性初始化 + DEFAULT_CHAR_SET = "UTF-8"; // 默认字符集 + ENCRYPT_CHAR_SET = "ISO-8859-1"; // 密文字符集 + DIGEST_ALGORITHM_LIST = new String[] { "MD5", "SHA" }; // 下拉选项卡选项, 摘要算法 + SIGNATURE_ALGORITHM_LIST = new String[] { "RSA" }; // 下拉选项卡选项, 签名算法 + ENCRYPT_ALGORITHM_LIST = new String[] { "DES", "AES" }; // 下拉选项卡选项, 加密算法 + ENCRYPT_MODE_LIST = new String[] { "ECB", "CBC" }; // 下拉选项卡选项, 分组加密模式 + ENCRYPT_KEY_LENGTH_LIST = new String[] { "64 (有效长度: 56)", "128", "192", "256" }; // 下拉选项卡选项, AES 密钥长度 + // ROOT_PATH_SOURCE = "assignment/authentication/experiment_2/data"; // 文件选择窗口的起始目录 + ROOT_PATH_SOURCE = "./"; // 源文件选择窗口的起始目录 + ROOT_PATH_TARGET = "./"; // 目标文件选择窗口的起始目录 + ROOT_PATH_RSAKEY = "./"; // 密钥保存目录 + FORMAT_CHARACTERS = "字符数: %d 字节数: %d"; // 格式化显示字符数 + FORMAT_FILE_SIZE = "文件大小: %d 字节"; // 格式化显示文件大小 + FORMAT_TARGET_FILE_PATH = "%s/%s"; // 格式化目标文件路径 + FORMAT_KEY_FORMAT_MESSAGE = "对称密钥格式不规范: 应为16进制表示的%d个字节(共%d个字符)"; // 格式化目标文件路径 + RSAKEY_FILENAME_EXTENSION = ".rsakey"; // RSA 密钥拓展名 + ENCRYPTED_FILENAME_EXTENSION = ".encrypted"; // 加密文件拓展名 + DECRYPTED_FILENAME_EXTENSION = ".decrypted"; // 若待解密文件拓展名不为加密文件拓展名, 则使用该解密文件拓展名 + } + + public static void main(String[] args) + { + // GUI.testCryption(); // 加解密测试 + + // GUI.testFont(); // 打印支持的字体 + + Utils.initGlobalFont(new Font("黑体", Font.PLAIN, 16)); // 统一设置字体 + new GUI().display(); + } + + public static void testFont() // 打印java支持的字体 + { + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + String font[] = env.getAvailableFontFamilyNames(); + for (String str : font) { + System.out.println(str); + } + } + + /** 检查密文格式是否规范 + * @param mode int 加密方式 0:ECB 1:CBC + * @param key byte[] 密钥, 16字节 + * @param data byte[] 输入密文数据, 输出明文数据, 16字节 + * @param init_vecotr byte[] 初始化向量, 16字节, ECB模式可设置为null + */ + public static boolean isCiphertextStandard(byte[] ciphertext) + { + if (ciphertext.length < GUI.CIPHERTEXT_DATA_OFFSET) return false; + if (ciphertext[GUI.CIPHERTEXT_MODE_OFFSET] >= GUI.ENCRYPT_MODE_LIST.length) return false; + if (ciphertext[GUI.CIPHERTEXT_EFFECTIVE_OFFSET] >= GUI.CIPHERTEXT_BLOCK_LENGTH) return false; + if ((ciphertext.length - GUI.CIPHERTEXT_DATA_OFFSET) % GUI.CIPHERTEXT_BLOCK_LENGTH != 0) return false; + return true; + } + + public void display() + { + this.initLabel(); + this.initTextField(); + this.initSpinner(); + this.initComboBox(); + this.initTextArea(); + this.initFileChooser(); + this.initButton(); + this.initGridBagConstraints(); + this.initScollPane(); + this.initSplitPane(); + this.initPanel(); + this.initTabbedPane(); + this.initFrame(); + + // new EncryptAndDecryptDialog(this.frame, this.frame, "测试", EncryptAndDecryptDialog.MODE_TEST); + } + + private void initLabel() // 初始化标签 + { + // 定义标签 + this.label_complete_key_length = new JLabel("公钥密码密钥长度 (bit)"); + this.label_digest_algorithm = new JLabel("消息摘要算法"); + this.label_signature_algorithm = new JLabel("公钥密码算法"); + this.label_encrypt_algorithm = new JLabel("对称密码算法"); + this.label_encrypt_key_length = new JLabel("对称密码密钥长度 (bit)"); + this.label_encrypt_mode = new JLabel("分组加密模式"); + + this.label_password_A = new JLabel("口令"); + this.label_key_A = new JLabel("对称密钥"); + this.label_n_A_A = new JLabel("A 密钥模数 n"); + this.label_e_A_A = new JLabel("A 公钥参数 e"); + this.label_d_A = new JLabel("A 私钥参数 d"); + this.label_p_A = new JLabel("A 私钥参数 p"); + this.label_q_A = new JLabel("A 私钥参数 q"); + this.label_dP_A = new JLabel("A 私钥参数 dP"); + this.label_dQ_A = new JLabel("A 私钥参数 dQ"); + this.label_qInv_A = new JLabel("A 私钥参数 qInv"); + + this.label_n_B_A = new JLabel("B 密钥模数 n"); + this.label_e_B_A = new JLabel("B 公钥参数 e"); + + this.label_password_B = new JLabel("口令"); + this.label_key_B = new JLabel("对称密钥"); + this.label_n_B_B = new JLabel("B 密钥模数 n"); + this.label_e_B_B = new JLabel("B 公钥参数 e"); + this.label_d_B = new JLabel("B 私钥参数 d"); + this.label_p_B = new JLabel("B 私钥参数 p"); + this.label_q_B = new JLabel("B 私钥参数 q"); + this.label_dP_B = new JLabel("B 私钥参数 dP"); + this.label_dQ_B = new JLabel("B 私钥参数 dQ"); + this.label_qInv_B = new JLabel("B 私钥参数 qInv"); + + this.label_n_A_B = new JLabel("A 密钥模数 n"); + this.label_e_A_B = new JLabel("A 公钥参数 e"); + + // this.label_plaintext = new JLabel("明文"); + // this.label_ciphertext = new JLabel("密文"); + this.label_plaintext = new JLabel("明文 & 明文编码"); + this.label_ciphertext = new JLabel("密文 & 密文编码"); + this.label_plaintext_characters = new JLabel(String.format(GUI.FORMAT_CHARACTERS, 0, 0)); + this.label_ciphertext_characters = new JLabel(String.format(GUI.FORMAT_CHARACTERS, 0, 0)); + // this.label_plaintext_encode = new JLabel("明文编码"); + // this.label_ciphertext_encode = new JLabel("密文编码"); + this.label_plaintext_target_file = new JLabel("待加密文件"); + this.label_ciphertext_target_file = new JLabel("待解密文件"); + this.label_plaintext_target_dir = new JLabel("解密文件输出目录"); + this.label_ciphertext_target_dir = new JLabel("加密文件输出目录"); + this.label_plaintext_file_size = new JLabel(String.format(GUI.FORMAT_FILE_SIZE, 0)); + this.label_ciphertext_file_size = new JLabel(String.format(GUI.FORMAT_FILE_SIZE, 0)); + } + + private void initTextField() // 初始化文本框 + { + // 定义文本框 + this.field_password_A = new JTextField(GUI.FIELD_COLUMNS); + this.field_key_A = new JTextField(GUI.FIELD_COLUMNS); + this.field_n_A_A = new JTextField(GUI.FIELD_COLUMNS); + this.field_e_A_A = new JTextField(GUI.FIELD_COLUMNS); + this.field_d_A = new JTextField(GUI.FIELD_COLUMNS); + this.field_p_A = new JTextField(GUI.FIELD_COLUMNS); + this.field_q_A = new JTextField(GUI.FIELD_COLUMNS); + this.field_dP_A = new JTextField(GUI.FIELD_COLUMNS); + this.field_dQ_A = new JTextField(GUI.FIELD_COLUMNS); + this.field_qInv_A = new JTextField(GUI.FIELD_COLUMNS); + + this.field_n_B_A = new JTextField(GUI.FIELD_COLUMNS); + this.field_e_B_A = new JTextField(GUI.FIELD_COLUMNS); + + this.field_password_B = new JTextField(GUI.FIELD_COLUMNS); + this.field_key_B = new JTextField(GUI.FIELD_COLUMNS); + this.field_n_B_B = new JTextField(GUI.FIELD_COLUMNS); + this.field_e_B_B = new JTextField(GUI.FIELD_COLUMNS); + this.field_d_B = new JTextField(GUI.FIELD_COLUMNS); + this.field_p_B = new JTextField(GUI.FIELD_COLUMNS); + this.field_q_B = new JTextField(GUI.FIELD_COLUMNS); + this.field_dP_B = new JTextField(GUI.FIELD_COLUMNS); + this.field_dQ_B = new JTextField(GUI.FIELD_COLUMNS); + this.field_qInv_B = new JTextField(GUI.FIELD_COLUMNS); + + this.field_n_A_B = new JTextField(GUI.FIELD_COLUMNS); + this.field_e_A_B = new JTextField(GUI.FIELD_COLUMNS); + + this.field_plaintext_file_path = new JTextField(); + this.field_ciphertext_file_path = new JTextField(); + this.field_plaintext_dir_path = new JTextField(); + this.field_ciphertext_dir_path = new JTextField(); + + // 文件路径不能编辑 + this.field_plaintext_file_path.setEditable(false); + this.field_ciphertext_file_path.setEditable(false); + this.field_plaintext_dir_path.setEditable(false); + this.field_ciphertext_dir_path.setEditable(false); + + // 密钥为16进制显示, 使用等宽字体 + this.field_key_A.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_n_A_A.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_e_A_A.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_d_A.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_p_A.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_q_A.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_dP_A.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_dQ_A.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_qInv_A.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_n_B_A.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_e_B_A.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_key_B.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_n_B_B.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_e_B_B.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_d_B.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_p_B.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_q_B.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_dP_B.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_dQ_B.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_qInv_B.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_n_A_B.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.field_e_A_B.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + + // 为文本框添加事件监听器 + /* 监听口令更改 */ + this.field_password_A.addCaretListener(new CaretListener() // A 口令变化自动更新密钥 + { + public void caretUpdate(CaretEvent event) + { + String password = field_password_A.getText(); + field_key_A.setText(Utils.byteToHexString(Utils.getHashByteString(password, GUI.KEY_LENGTH)).substring(0, key_len * 2)); + } + }); + this.field_password_B.addCaretListener(new CaretListener() // B 口令变化自动更新密钥 + { + public void caretUpdate(CaretEvent event) + { + String password = field_password_B.getText(); + field_key_B.setText(Utils.byteToHexString(Utils.getHashByteString(password, GUI.KEY_LENGTH)).substring(0, key_len * 2)); + } + }); + + this.field_key_A.addCaretListener(new CaretListener() // 密钥文本变化若合法自动更新key + { + public void caretUpdate(CaretEvent event) + { + try { + String key_str = field_key_A.getText(); + if (key_str.length() != key_len * 2) return; + key = Utils.hexStringToByte(key_str); + } catch (RuntimeException e) { + Utils.showStatusDialog(String.format(GUI.FORMAT_KEY_FORMAT_MESSAGE, key_len, key_len * 2), "密钥状态信息"); + } + } + }); + } + + private void initSpinner() // 初始化下拉框 + { + this.spinner_complete_key_length = new JSpinner(new SpinnerNumberModel(GUI.COMPLETE_KEY_MIN_LENGTH, GUI.COMPLETE_KEY_MIN_LENGTH, GUI.COMPLETE_KEY_MAX_LENGTH, 1)); + } + + private void initComboBox() // 初始化下拉选项 + { + this.box_digest_algorithm = new JComboBox(GUI.DIGEST_ALGORITHM_LIST); + this.box_signature_algorithm = new JComboBox(GUI.SIGNATURE_ALGORITHM_LIST); + this.box_encrypt_algorithm = new JComboBox(GUI.ENCRYPT_ALGORITHM_LIST); + this.box_encrypt_key_length = new JComboBox(GUI.ENCRYPT_KEY_LENGTH_LIST); + this.box_encrypt_mode = new JComboBox(GUI.ENCRYPT_MODE_LIST); + + this.box_digest_algorithm.addActionListener(event -> { + switch (this.box_digest_algorithm.getSelectedIndex()) { + case GUI.DEGEST_ALGORITHM_MD5: + this.digest = new MD5(); + + break; + case GUI.DEGEST_ALGORITHM_SHA: + this.digest = new SHA(); + break; + + default: + break; + } + }); + this.box_encrypt_algorithm.addActionListener(event -> { + switch (this.box_encrypt_algorithm.getSelectedIndex()) { + case GUI.ENCRYPT_ALGORITHM_DES: + this.crypto = new DES(); + if (this.box_encrypt_key_length.getSelectedIndex() != GUI.ENCRYPT_KEY_LENGTH_064) { + this.box_encrypt_key_length.setSelectedIndex(GUI.ENCRYPT_KEY_LENGTH_064); + } + break; + case GUI.ENCRYPT_ALGORITHM_AES: + this.crypto = new AES(); + if (this.box_encrypt_key_length.getSelectedIndex() == GUI.ENCRYPT_KEY_LENGTH_064) { + this.box_encrypt_key_length.setSelectedIndex(GUI.ENCRYPT_KEY_LENGTH_128); + } + break; + default: + break; + } + }); + this.box_encrypt_key_length.addActionListener(event -> { + switch (this.box_encrypt_key_length.getSelectedIndex()) { + case GUI.ENCRYPT_KEY_LENGTH_064: + this.box_encrypt_algorithm.setSelectedIndex(GUI.ENCRYPT_ALGORITHM_DES); + this.key_len = GUI.DES_KEY_LENGTH; + break; + case GUI.ENCRYPT_KEY_LENGTH_128: + this.box_encrypt_algorithm.setSelectedIndex(GUI.ENCRYPT_ALGORITHM_AES); + this.key_len = GUI.AES_KEY_LENGTH_128; + break; + case GUI.ENCRYPT_KEY_LENGTH_192: + this.box_encrypt_algorithm.setSelectedIndex(GUI.ENCRYPT_ALGORITHM_AES); + this.key_len = GUI.AES_KEY_LENGTH_192; + break; + case GUI.ENCRYPT_KEY_LENGTH_256: + this.box_encrypt_algorithm.setSelectedIndex(GUI.ENCRYPT_ALGORITHM_AES); + this.key_len = GUI.AES_KEY_LENGTH_256; + break; + default: + break; + } + }); + } + + private void initTextArea() // 初始化文本区 + { + // 定义文本区 + this.area_plaintext = new JTextArea(); + this.area_ciphertext = new JTextArea(); + this.area_plaintext_hex = new JTextArea(); + this.area_ciphertext_hex = new JTextArea(); + + // 设置自动换行 + // this.area_plaintext_hex.setLineWrap(true); + // this.area_ciphertext_hex.setLineWrap(true); + + // 16进制显示文本区禁止编辑 + this.area_plaintext_hex.setEditable(false); + this.area_ciphertext_hex.setEditable(false); + + // 16进制显示文本区设置为等宽的consoles字体 + this.area_plaintext_hex.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.area_ciphertext.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + this.area_ciphertext_hex.setFont(new Font("Consolas", Font.PLAIN, GUI.FIELD_FONT_SIZE)); + + // 为文本区添加事件监听器: 内容变化更新对应十六进制区域与字符个数 + this.area_plaintext.addCaretListener(new CaretListener() + { + public void caretUpdate(CaretEvent event) + { + String text_str = area_plaintext.getText(); + byte text_byte_arr[] = Utils.stringEncoding(text_str, GUI.DEFAULT_CHAR_SET); + area_plaintext_hex.setText(""); + for (int i = 0; i < text_byte_arr.length; i++) { + area_plaintext_hex.append(String.format("%02X", text_byte_arr[i])); + area_plaintext_hex.append(((i + 1) % 16 != 0 ? " " : "\n")); + } + + label_plaintext_characters.setText(String.format(GUI.FORMAT_CHARACTERS, text_str.length(), text_byte_arr.length)); + } + }); + this.area_ciphertext.addCaretListener(new CaretListener() + { + public void caretUpdate(CaretEvent event) + { + String text_str = area_ciphertext.getText(); + byte text_byte_arr[] = Utils.stringEncoding(text_str, GUI.ENCRYPT_CHAR_SET); + area_ciphertext_hex.setText(""); + for (int i = 0; i < text_byte_arr.length; i++) { + area_ciphertext_hex.append(String.format("%02X", text_byte_arr[i])); + area_ciphertext_hex.append(((i + 1) % 16 != 0 ? " " : "\n")); + } + + label_ciphertext_characters.setText(String.format(GUI.FORMAT_CHARACTERS, text_str.length(), text_byte_arr.length)); + + } + }); + } + + private void initFileChooser() + { + // 定义文件选择对话框 + this.file_chooser_complete_key_import_A = new JFileChooser(GUI.ROOT_PATH_RSAKEY); + this.file_chooser_complete_key_import_B = new JFileChooser(GUI.ROOT_PATH_RSAKEY); + this.dir_chooser_complete_key_export_A = new JFileChooser(GUI.ROOT_PATH_RSAKEY); + this.dir_chooser_complete_key_export_B = new JFileChooser(GUI.ROOT_PATH_RSAKEY); + + this.file_chooser_plaintext = new JFileChooser(GUI.ROOT_PATH_SOURCE); + this.file_chooser_ciphertext = new JFileChooser(GUI.ROOT_PATH_TARGET); + this.dir_chooser_plaintext = new JFileChooser(GUI.ROOT_PATH_TARGET); + this.dir_chooser_ciphertext = new JFileChooser(GUI.ROOT_PATH_TARGET); + + // 对话框标题 + this.file_chooser_complete_key_import_A.setDialogTitle("用户 A 密钥导入"); + this.file_chooser_complete_key_import_B.setDialogTitle("用户 B 密钥导入"); + this.dir_chooser_complete_key_export_A.setDialogTitle("用户 A 密钥导出"); + this.dir_chooser_complete_key_export_B.setDialogTitle("用户 B 密钥导出"); + + this.file_chooser_plaintext.setDialogTitle("待加密文件选择"); + this.file_chooser_ciphertext.setDialogTitle("待解密文件选择"); + this.dir_chooser_plaintext.setDialogTitle("解密文件输出目录选择"); + this.dir_chooser_ciphertext.setDialogTitle("加密文件输出目录选择"); + + // 选择文件/目录 + this.file_chooser_complete_key_import_A.setFileSelectionMode(JFileChooser.FILES_ONLY); + this.file_chooser_complete_key_import_B.setFileSelectionMode(JFileChooser.FILES_ONLY); + this.dir_chooser_complete_key_export_A.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + this.dir_chooser_complete_key_export_B.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + + this.file_chooser_plaintext.setFileSelectionMode(JFileChooser.FILES_ONLY); + this.file_chooser_ciphertext.setFileSelectionMode(JFileChooser.FILES_ONLY); + this.dir_chooser_plaintext.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + this.dir_chooser_ciphertext.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + + // 设置对话框类型 + this.file_chooser_complete_key_import_A.setDialogType(JFileChooser.OPEN_DIALOG); + this.file_chooser_complete_key_import_B.setDialogType(JFileChooser.OPEN_DIALOG); + this.dir_chooser_complete_key_export_A.setDialogType(JFileChooser.SAVE_DIALOG); + this.dir_chooser_complete_key_export_B.setDialogType(JFileChooser.SAVE_DIALOG); + + this.file_chooser_plaintext.setDialogType(JFileChooser.OPEN_DIALOG); + this.file_chooser_ciphertext.setDialogType(JFileChooser.OPEN_DIALOG); + this.dir_chooser_plaintext.setDialogType(JFileChooser.SAVE_DIALOG); + this.dir_chooser_ciphertext.setDialogType(JFileChooser.SAVE_DIALOG); + + // 为密钥文件选择对话框设置文件拓展名过滤器 + FileFilter rsa_key_fileter = new FileFilter() + { + @Override + public boolean accept(File file) + { + if (file.isDirectory()) return true; + return file.getName().toLowerCase().endsWith(GUI.ENCRYPTED_FILENAME_EXTENSION); + } + + @Override + public String getDescription() + { + return "RSA 密钥文件 (*" + GUI.ENCRYPTED_FILENAME_EXTENSION + ")"; + } + }; + this.file_chooser_complete_key_import_A.addChoosableFileFilter(rsa_key_fileter); + this.file_chooser_complete_key_import_B.addChoosableFileFilter(rsa_key_fileter); + + // 为待解密文件选择对话框设置文件拓展名过滤器 + this.file_chooser_ciphertext.addChoosableFileFilter(new FileFilter() + { + @Override + public boolean accept(File file) + { + if (file.isDirectory()) return true; + return file.getName().toLowerCase().endsWith(GUI.ENCRYPTED_FILENAME_EXTENSION); + } + + @Override + public String getDescription() + { + return "加密文件 (*" + GUI.ENCRYPTED_FILENAME_EXTENSION + ")"; + } + }); + } + + private void initButton() // 初始化按钮 + { + this.button_key_generation_random_A = new JButton("随机生成口令"); + this.button_key_standard_A = new JButton("检查密钥格式"); + this.button_complete_key_generation_A = new JButton("随机生成公私钥对"); + this.button_complete_key_import_A = new JButton("导入 RSA 密钥文件"); + this.button_complete_key_export_A = new JButton("导出 RSA 密钥文件"); + + this.button_encrypt_A = new JButton("加密消息 (发送给 B)"); + this.button_decrypt_A = new JButton("解密消息 (接收自 B)"); + this.button_verify_A = new JButton("校验消息摘要"); + + this.button_key_generation_random_B = new JButton("随机生成口令"); + this.button_key_standard_B = new JButton("检查密钥格式"); + this.button_complete_key_generation_B = new JButton("随机生成公私钥对"); + this.button_complete_key_import_B = new JButton("导入 RSA 密钥文件"); + this.button_complete_key_export_B = new JButton("导出 RSA 密钥文件"); + + this.button_encrypt_B = new JButton("加密消息 (发送给 A)"); + this.button_decrypt_B = new JButton("解密消息 (接收自 A)"); + this.button_verify_B = new JButton("校验消息摘要"); + + this.button_select_plaintext_file = new JButton("选择文件"); + this.button_select_ciphertext_file = new JButton("选择文件"); + this.button_select_plaintext_dir = new JButton("选择目录"); + this.button_select_ciphertext_dir = new JButton("选择目录"); + + /* A 随机生成口令 */ + this.button_key_generation_random_A.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + field_password_A.setText(Utils.getRandomString(16)); + } + }); + /* B 随机生成口令 */ + this.button_key_generation_random_B.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + field_password_B.setText(Utils.getRandomString(16)); + } + }); + + /* A 生成公私钥对 */ + this.button_complete_key_generation_A.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + RSAKeyGenerator rsa_key_generator = new RSAKeyGenerator((Integer) (spinner_complete_key_length.getValue())); + + field_n_A_A.setText(rsa_key_generator.getModulus().toString(16)); + + field_e_A_A.setText(rsa_key_generator.getPubExp().toString(16)); + + field_d_A.setText(rsa_key_generator.getPriExp().toString(16)); + field_p_A.setText(rsa_key_generator.getPrimeLarge().toString(16)); + field_q_A.setText(rsa_key_generator.getPrimeSmall().toString(16)); + field_dP_A.setText(rsa_key_generator.getCRTExpOne().toString(16)); + field_dQ_A.setText(rsa_key_generator.getCRTExpTwo().toString(16)); + field_qInv_A.setText(rsa_key_generator.getCRTCoeff().toString(16)); + + field_n_A_B.setText(rsa_key_generator.getModulus().toString(16)); + field_e_A_B.setText(rsa_key_generator.getPubExp().toString(16)); + } + }); + /* B 生成公私钥对 */ + this.button_complete_key_generation_B.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + RSAKeyGenerator rsa_key_generator = new RSAKeyGenerator((Integer) (spinner_complete_key_length.getValue())); + + field_n_B_B.setText(rsa_key_generator.getModulus().toString(16)); + + field_e_B_B.setText(rsa_key_generator.getPubExp().toString(16)); + + field_d_B.setText(rsa_key_generator.getPriExp().toString(16)); + field_p_B.setText(rsa_key_generator.getPrimeLarge().toString(16)); + field_q_B.setText(rsa_key_generator.getPrimeSmall().toString(16)); + field_dP_B.setText(rsa_key_generator.getCRTExpOne().toString(16)); + field_dQ_B.setText(rsa_key_generator.getCRTExpTwo().toString(16)); + field_qInv_B.setText(rsa_key_generator.getCRTCoeff().toString(16)); + + field_n_B_A.setText(rsa_key_generator.getModulus().toString(16)); + field_e_B_A.setText(rsa_key_generator.getPubExp().toString(16)); + } + }); + + /* 验证 A 对称密钥 */ + this.button_key_standard_A.addActionListener(e -> { + if (isKeyStandard(field_key_A)) Utils.showStatusDialog("密钥格式规范!", "密钥状态信息"); + else Utils.showStatusDialog(String.format(GUI.FORMAT_KEY_FORMAT_MESSAGE, key_len, key_len * 2), "密钥状态信息"); + }); + /* 验证 B 对称密钥 */ + this.button_key_standard_B.addActionListener(e -> { + if (isKeyStandard(field_key_B)) Utils.showStatusDialog("密钥格式规范!", "密钥状态信息"); + else Utils.showStatusDialog(String.format(GUI.FORMAT_KEY_FORMAT_MESSAGE, key_len, key_len * 2), "密钥状态信息"); + }); + + /* A 加密 */ + this.button_encrypt_A.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + if (!enableUserA()) { + Utils.showStatusDialog("RSA 私钥或 RSA 公钥不完整", "RSA 密钥状态信息"); + return; + } + /* 校验对称密钥 */ + if (!isKeyStandard(field_key_A)) { + Utils.showStatusDialog(String.format(GUI.FORMAT_KEY_FORMAT_MESSAGE, key_len, key_len * 2), "密钥状态信息"); + return; + } + + switch (pane_tab_data.getSelectedIndex()) { + case 0: + if (stringEncrypt()) { + Utils.showStatusDialog("字符串加密成功!", "字符串加密"); + } else return; + break; + case 1: + if (!isFileEffective(file_plaintext) || !isDirEffective(dir_ciphertext)) return; + if (fileEncrypt()) { + Utils.showStatusDialog("文件加密成功!", "文件加密"); + } else return; + // dialog_encryption = new EncryptAndDecryptDialog(frame, frame, "文件加密", EncryptAndDecryptDialog.MODE_ENCRYPT); + // new EncryptAndDecryptDialog( // + // frame, // + // frame, // + // "文件加密", // + // EncryptAndDecryptDialog.MODE_ENCRYPT, // + // box_encrypt_mode.getSelectedIndex(), // + // key, // + // file_plaintext, // + // dir_ciphertext // + // ); + break; + default: + break; + } + } + }); + /* B 加密 */ + this.button_encrypt_B.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + if (!enableUserB()) { + Utils.showStatusDialog("RSA 私钥或 RSA 公钥不完整", "RSA 密钥状态信息"); + return; + } + if (!isKeyStandard(field_key_B)) { + Utils.showStatusDialog(String.format(GUI.FORMAT_KEY_FORMAT_MESSAGE, key_len, key_len * 2), "密钥状态信息"); + return; + } + /* 校验非对称密钥 */ + if (!isRSAStandard() // + || field_n_A_B.getText().isEmpty() // + || field_e_A_B.getText().isEmpty() // + ) { + Utils.showStatusDialog("用户 B 的 RSA 私钥或用户 A 的 RSA 公钥不完整", "RSA 密钥状态信息"); + return; + } + switch (pane_tab_data.getSelectedIndex()) { + case 0: + if (stringEncrypt()) { + Utils.showStatusDialog("字符串加密成功!", "字符串加密"); + } else return; + break; + case 1: + if (!isFileEffective(file_plaintext) || !isDirEffective(dir_ciphertext)) return; + if (fileEncrypt()) { + Utils.showStatusDialog("文件加密成功!", "文件加密"); + } else return; + // dialog_encryption = new EncryptAndDecryptDialog(frame, frame, "文件加密", EncryptAndDecryptDialog.MODE_ENCRYPT); + // new EncryptAndDecryptDialog( // + // frame, // + // frame, // + // "文件加密", // + // EncryptAndDecryptDialog.MODE_ENCRYPT, // + // box_encrypt_mode.getSelectedIndex(), // + // key, // + // file_plaintext, // + // dir_ciphertext // + // ); + break; + default: + break; + } + } + }); + /* A 解密 */ + this.button_decrypt_A.addActionListener(new ActionListener() + { + + public void actionPerformed(ActionEvent event) + { + if (!enableUserA()) { + Utils.showStatusDialog("RSA 私钥或 RSA 公钥不完整", "RSA 密钥状态信息"); + return; + } + switch (pane_tab_data.getSelectedIndex()) { + case 0: + if (stringDecrypt()) { + Utils.showStatusDialog("字符串解密成功!", "字符串解密"); + } else return; + break; + case 2: + if (!isEncryptedFileEffective(file_ciphertext) || !isDirEffective(dir_plaintext)) return; + if (fileDecrypt()) { + Utils.showStatusDialog("文件解密成功!", "文件解密"); + } else return; + // dialog_decryption = new EncryptAndDecryptDialog(frame, frame, "文件解密", EncryptAndDecryptDialog.MODE_DECRYPT); + // new EncryptAndDecryptDialog( // + // frame, // + // frame, // + // "文件解密", // + // EncryptAndDecryptDialog.MODE_DECRYPT, // + // box_encrypt_mode.getSelectedIndex(), // + // key, // + // file_ciphertext, // + // dir_plaintext // + // ); + break; + default: + break; + } + field_password_A.setText(""); + field_key_A.setText(Utils.byteToHexString(cipher.getKey())); + } + }); + /* B 解密 */ + this.button_decrypt_B.addActionListener(new ActionListener() + { + + public void actionPerformed(ActionEvent event) + { + if (!enableUserB()) { + Utils.showStatusDialog("RSA 私钥或 RSA 公钥不完整", "RSA 密钥状态信息"); + return; + } + switch (pane_tab_data.getSelectedIndex()) { + case 0: + if (stringDecrypt()) { + Utils.showStatusDialog("字符串解密成功!", "字符串解密"); + } else return; + break; + case 2: + if (!isEncryptedFileEffective(file_ciphertext) || !isDirEffective(dir_plaintext)) return; + if (fileDecrypt()) { + Utils.showStatusDialog("文件解密成功!", "文件解密"); + } else return; + // dialog_decryption = new EncryptAndDecryptDialog(frame, frame, "文件解密", EncryptAndDecryptDialog.MODE_DECRYPT); + // new EncryptAndDecryptDialog( // + // frame, // + // frame, // + // "文件解密", // + // EncryptAndDecryptDialog.MODE_DECRYPT, // + // box_encrypt_mode.getSelectedIndex(), // + // key, // + // file_ciphertext, // + // dir_plaintext // + // ); + break; + default: + break; + } + field_password_B.setText(""); + field_key_B.setText(Utils.byteToHexString(cipher.getKey())); + } + }); + + ActionListener verifyDigest = e -> { + Utils.showStatusDialog(String.format("原始消息摘要: %s\n签名消息摘要: %s", digest_string, _digest_string), "消息摘要"); + }; + + /* 校验消息摘要 */ + this.button_verify_A.addActionListener(verifyDigest); + this.button_verify_B.addActionListener(verifyDigest); + + /* 用户 A 导入密钥 */ + this.button_complete_key_import_A.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + file_rsakey_A = selectFileOrDir(file_chooser_complete_key_import_A); + if (file_rsakey_A != null) { // 若选择成功 + try { + BufferedReader file = new BufferedReader(new FileReader(file_rsakey_A)); + String line; + line = file.readLine(); + field_n_A_A.setText(line); + field_n_A_B.setText(line); + + line = file.readLine(); + field_e_A_A.setText(line); + field_e_A_B.setText(line); + + line = file.readLine(); + field_d_A.setText(line); + line = file.readLine(); + field_p_A.setText(line); + line = file.readLine(); + field_q_A.setText(line); + line = file.readLine(); + field_dP_A.setText(line); + line = file.readLine(); + field_dQ_A.setText(line); + line = file.readLine(); + field_qInv_A.setText(line); + file.close(); + } catch (IOException e) { + Utils.showExceptionDialog("文件读写异常", e); + return; + } + } + } + }); + /* 用户 B 导入密钥 */ + this.button_complete_key_import_B.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + file_rsakey_B = selectFileOrDir(file_chooser_complete_key_import_B); + if (file_rsakey_B != null) { // 若选择成功 + try { + BufferedReader file = new BufferedReader(new FileReader(file_rsakey_B)); + String line; + line = file.readLine(); + field_n_B_B.setText(line); + field_n_B_A.setText(line); + + line = file.readLine(); + field_e_B_B.setText(line); + field_e_B_A.setText(line); + + line = file.readLine(); + field_d_B.setText(line); + line = file.readLine(); + field_p_B.setText(line); + line = file.readLine(); + field_q_B.setText(line); + line = file.readLine(); + field_dP_B.setText(line); + line = file.readLine(); + field_dQ_B.setText(line); + line = file.readLine(); + field_qInv_B.setText(line); + file.close(); + } catch (IOException e) { + Utils.showExceptionDialog("文件读写异常", e); + return; + } + } + } + }); + + /* 用户 A 导出密钥 */ + this.button_complete_key_export_A.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + dir_rsakey_A = selectFileOrDir(dir_chooser_complete_key_export_A); + if (dir_rsakey_A != null) { // 若选择成功 + try { + BufferedWriter file = new BufferedWriter(new FileWriter(String.format(GUI.FORMAT_TARGET_FILE_PATH, dir_rsakey_A.getPath(), Utils.getTimestamp() + GUI.RSAKEY_FILENAME_EXTENSION))); + file.write(field_n_A_A.getText() + "\n"); + file.write(field_e_A_A.getText() + "\n"); + file.write(field_d_A.getText() + "\n"); + file.write(field_p_A.getText() + "\n"); + file.write(field_q_A.getText() + "\n"); + file.write(field_dP_A.getText() + "\n"); + file.write(field_dQ_A.getText() + "\n"); + file.write(field_qInv_A.getText() + "\n"); + file.close(); + } catch (IOException e) { + Utils.showExceptionDialog("文件读写异常", e); + return; + } + } + } + }); + /* 用户 B 导出密钥 */ + this.button_complete_key_export_B.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + dir_rsakey_B = selectFileOrDir(dir_chooser_complete_key_export_B); + if (dir_rsakey_B != null) { // 若选择成功 + try { + BufferedWriter file = new BufferedWriter(new FileWriter(String.format(GUI.FORMAT_TARGET_FILE_PATH, dir_rsakey_B.getPath(), Utils.getTimestamp() + GUI.RSAKEY_FILENAME_EXTENSION))); + file.write(field_n_B_B.getText() + "\n"); + file.write(field_e_B_B.getText() + "\n"); + file.write(field_d_B.getText() + "\n"); + file.write(field_p_B.getText() + "\n"); + file.write(field_q_B.getText() + "\n"); + file.write(field_dP_B.getText() + "\n"); + file.write(field_dQ_B.getText() + "\n"); + file.write(field_qInv_B.getText() + "\n"); + file.close(); + } catch (IOException e) { + Utils.showExceptionDialog("文件读写异常", e); + return; + } + } + } + }); + + /* 选择明文文件 */ + this.button_select_plaintext_file.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + file_plaintext = selectFileOrDir(file_chooser_plaintext); + if (file_plaintext != null) { // 若选择成功 + field_plaintext_file_path.setText(file_plaintext.getPath()); + label_plaintext_file_size.setText(String.format(GUI.FORMAT_FILE_SIZE, file_plaintext.length())); + } + } + }); + + /* 选择密文文件 */ + this.button_select_ciphertext_file.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + file_ciphertext = selectFileOrDir(file_chooser_ciphertext); + if (file_ciphertext != null) { // 若选择成功 + field_ciphertext_file_path.setText(file_ciphertext.getPath()); + label_ciphertext_file_size.setText(String.format(GUI.FORMAT_FILE_SIZE, file_ciphertext.length())); + } + } + }); + + /* 选择明文保存目录 */ + this.button_select_plaintext_dir.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + dir_plaintext = selectFileOrDir(dir_chooser_plaintext); + if (dir_plaintext != null) { // 若选择成功 + field_plaintext_dir_path.setText(dir_plaintext.getPath()); + } + } + }); + + /* 选择密文保存目录 */ + this.button_select_ciphertext_dir.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + dir_ciphertext = selectFileOrDir(dir_chooser_ciphertext); + if (dir_ciphertext != null) { // 若选择成功 + field_ciphertext_dir_path.setText(dir_ciphertext.getPath()); + } + } + }); + } + + // private void initProgressBar() // 初始化进度条 + // { + // bar_encrypt_progress = new JProgressBar(); + // bar_decrypt_progress = new JProgressBar(); + // } + + private void initGridBagConstraints() // 初始化网格包管理器 + { + this.gbc_both = new GridBagConstraints(); // 网格包管理器 + this.gbc_both.anchor = GridBagConstraints.CENTER; // 组件在显示区域居中对齐 + this.gbc_both.fill = GridBagConstraints.BOTH; // 设置组件填充区域 + this.gbc_both.insets = new Insets(5, 5, 5, 5); // 设置与显示区域边缘的位置 + + this.gbc_horizontal = new GridBagConstraints(); // 网格包管理器 + this.gbc_horizontal.anchor = GridBagConstraints.CENTER; // 组件在显示区域居中对齐 + this.gbc_horizontal.fill = GridBagConstraints.HORIZONTAL; // 设置组件填充区域 + this.gbc_horizontal.insets = new Insets(8, 8, 8, 8); // 设置与显示区域边缘的位置 + } + + private void initScollPane() // 初始化滚动窗格 + { + // 定义滚动窗格 + this.pane_scroll_plaintext = new JScrollPane(); + this.pane_scroll_ciphertext = new JScrollPane(); + this.pane_scroll_plaintext_hex = new JScrollPane(); + this.pane_scroll_ciphertext_hex = new JScrollPane(); + + // 将文本区添加到滚动条中 + this.pane_scroll_plaintext.setViewportView(this.area_plaintext); + this.pane_scroll_ciphertext.setViewportView(this.area_ciphertext); + this.pane_scroll_plaintext_hex.setViewportView(this.area_plaintext_hex); + this.pane_scroll_ciphertext_hex.setViewportView(this.area_ciphertext_hex); + + // 仅在需要时显示水平滚动条 + this.pane_scroll_plaintext.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + this.pane_scroll_ciphertext.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + this.pane_scroll_plaintext_hex.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + this.pane_scroll_ciphertext_hex.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + + // 从不显示水平滚动条 + // this.pane_scroll_plaintext_hex.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + // this.pane_scroll_ciphertext_hex.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + + // 仅在需要时显示竖直滚动条 + this.pane_scroll_plaintext.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + this.pane_scroll_ciphertext.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + this.pane_scroll_plaintext_hex.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + this.pane_scroll_ciphertext_hex.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + } + + private void initSplitPane() // 初始化分隔面板 + { + // 定义分隔面板 + this.pane_split_plaintext = new JSplitPane(JSplitPane.VERTICAL_SPLIT); // 上下分隔 + this.pane_split_ciphertext = new JSplitPane(JSplitPane.VERTICAL_SPLIT); // 上下分隔 + + // 设置上面显示的组件 + this.pane_split_plaintext.setTopComponent(this.pane_scroll_plaintext); + this.pane_split_ciphertext.setTopComponent(this.pane_scroll_ciphertext); + + // 设置下面显示的组件 + this.pane_split_plaintext.setBottomComponent(this.pane_scroll_plaintext_hex); + this.pane_split_ciphertext.setBottomComponent(this.pane_scroll_ciphertext_hex); + + // 显示折叠/展开两边组件的小按钮 + this.pane_split_plaintext.setOneTouchExpandable(true); + this.pane_split_ciphertext.setOneTouchExpandable(true); + + // 拖动分隔条时连续重绘组件 + this.pane_split_plaintext.setContinuousLayout(true); + this.pane_split_ciphertext.setContinuousLayout(true); + + // 设置分隔条的位置,相对于 左边/顶部 的像素长度 + this.pane_split_plaintext.setDividerLocation(GUI.WINDOW_HEIGHT / 2); + this.pane_split_ciphertext.setDividerLocation(GUI.WINDOW_HEIGHT / 2); + } + + private void initPanel() // 初始化面板 + { + // 定义算法参数定义面板 + this.panel_parameter = new JPanel(new GridBagLayout()); + // 定义用户 A 面板 + this.panel_user_A = new JPanel(new GridBagLayout()); + // 定义用户 B 面板 + this.panel_user_B = new JPanel(new GridBagLayout()); + + // 定义选项卡的面板 + // this.panel_string = new JPanel(new BorderLayout(5, 5)); + this.panel_string = new JPanel(new GridBagLayout()); + this.panel_file_encrypt = new JPanel(new GridBagLayout()); + this.panel_file_decrypt = new JPanel(new GridBagLayout()); + + // 设置面板颜色 + this.panel_string.setBackground(Color.WHITE); + this.panel_file_encrypt.setBackground(Color.WHITE); + this.panel_file_decrypt.setBackground(Color.WHITE); + this.panel_parameter.setBackground(new Color(238, 238, 238)); + this.panel_user_A.setBackground(new Color(238, 238, 238)); + this.panel_user_B.setBackground(new Color(238, 238, 238)); + // this.panel_parameter.setBackground(Color.WHITE); + // this.panel_user_A.setBackground(Color.WHITE); + // this.panel_user_B.setBackground(Color.WHITE); + + int row; + // 为算法参数面板添加组件 + /* 完整密钥长度 */ + row = 0; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_complete_key_length, this.panel_parameter, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.spinner_complete_key_length, this.panel_parameter, this.gbc_horizontal); + /* 消息摘要算法 */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_digest_algorithm, this.panel_parameter, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.box_digest_algorithm, this.panel_parameter, this.gbc_horizontal); + /* 公钥密码算法 */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_signature_algorithm, this.panel_parameter, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.box_signature_algorithm, this.panel_parameter, this.gbc_horizontal); + /* 对称密码算法 */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_encrypt_algorithm, this.panel_parameter, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.box_encrypt_algorithm, this.panel_parameter, this.gbc_horizontal); + /* 对称密码密钥长度 */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_encrypt_key_length, this.panel_parameter, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.box_encrypt_key_length, this.panel_parameter, this.gbc_horizontal); + /* 分组加密模式 */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_encrypt_mode, this.panel_parameter, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.box_encrypt_mode, this.panel_parameter, this.gbc_horizontal); + + // 为用户 A 面板添加组件 + /* 口令 */ + row = 0; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_password_A, this.panel_user_A, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_password_A, this.panel_user_A, this.gbc_horizontal); + /* 对称密钥 */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_key_A, this.panel_user_A, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_key_A, this.panel_user_A, this.gbc_horizontal); + /* 随机生成口令 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_key_generation_random_A, this.panel_user_A, this.gbc_horizontal); + /* 检查密钥格式 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_key_standard_A, this.panel_user_A, this.gbc_horizontal); + /* A-n */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_n_A_A, this.panel_user_A, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_n_A_A, this.panel_user_A, this.gbc_horizontal); + /* A-e */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_e_A_A, this.panel_user_A, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_e_A_A, this.panel_user_A, this.gbc_horizontal); + /* A-d */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_d_A, this.panel_user_A, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_d_A, this.panel_user_A, this.gbc_horizontal); + /* A-p */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_p_A, this.panel_user_A, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_p_A, this.panel_user_A, this.gbc_horizontal); + /* A-q */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_q_A, this.panel_user_A, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_q_A, this.panel_user_A, this.gbc_horizontal); + /* A-dP */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_dP_A, this.panel_user_A, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_dP_A, this.panel_user_A, this.gbc_horizontal); + /* A-dQ */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_dQ_A, this.panel_user_A, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_dQ_A, this.panel_user_A, this.gbc_horizontal); + /* A-qInv */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_qInv_A, this.panel_user_A, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_qInv_A, this.panel_user_A, this.gbc_horizontal); + + /* A 生成密钥 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_complete_key_generation_A, this.panel_user_A, this.gbc_horizontal); + /* A 导入密钥 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_complete_key_import_A, this.panel_user_A, this.gbc_horizontal); + /* A 导出密钥 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_complete_key_export_A, this.panel_user_A, this.gbc_horizontal); + + /* B-n */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_n_B_A, this.panel_user_A, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_n_B_A, this.panel_user_A, this.gbc_horizontal); + /* B-e */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_e_B_A, this.panel_user_A, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_e_B_A, this.panel_user_A, this.gbc_horizontal); + + /* 加密 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_encrypt_A, this.panel_user_A, this.gbc_horizontal); + /* 解密 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_decrypt_A, this.panel_user_A, this.gbc_horizontal); + /* 校验 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_verify_A, this.panel_user_A, this.gbc_horizontal); + + // 为用户 B 面板添加组件 + /* 口令 */ + row = 0; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_password_B, this.panel_user_B, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_password_B, this.panel_user_B, this.gbc_horizontal); + /* 对称密钥 */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_key_B, this.panel_user_B, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_key_B, this.panel_user_B, this.gbc_horizontal); + /* 随机生成口令 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_key_generation_random_B, this.panel_user_B, this.gbc_horizontal); + /* 检查密钥格式 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_key_standard_B, this.panel_user_B, this.gbc_horizontal); + /* B-n */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_n_B_B, this.panel_user_B, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_n_B_B, this.panel_user_B, this.gbc_horizontal); + /* B-e */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_e_B_B, this.panel_user_B, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_e_B_B, this.panel_user_B, this.gbc_horizontal); + /* B-d */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_d_B, this.panel_user_B, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_d_B, this.panel_user_B, this.gbc_horizontal); + /* B-p */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_p_B, this.panel_user_B, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_p_B, this.panel_user_B, this.gbc_horizontal); + /* B-q */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_q_B, this.panel_user_B, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_q_B, this.panel_user_B, this.gbc_horizontal); + /* B-dP */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_dP_B, this.panel_user_B, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_dP_B, this.panel_user_B, this.gbc_horizontal); + /* B-dQ */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_dQ_B, this.panel_user_B, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_dQ_B, this.panel_user_B, this.gbc_horizontal); + /* B-qInv */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_qInv_B, this.panel_user_B, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_qInv_B, this.panel_user_B, this.gbc_horizontal); + + /* B 生成密钥 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_complete_key_generation_B, this.panel_user_B, this.gbc_horizontal); + /* B 导入密钥 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_complete_key_import_B, this.panel_user_B, this.gbc_horizontal); + /* B 导出密钥 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_complete_key_export_B, this.panel_user_B, this.gbc_horizontal); + + /* A-n */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_n_A_B, this.panel_user_B, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_n_A_B, this.panel_user_B, this.gbc_horizontal); + /* A-e */ + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 0, this.label_e_A_B, this.panel_user_B, this.gbc_horizontal); + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.field_e_A_B, this.panel_user_B, this.gbc_horizontal); + + /* 加密 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_encrypt_B, this.panel_user_B, this.gbc_horizontal); + /* 解密 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_decrypt_B, this.panel_user_B, this.gbc_horizontal); + /* 校验 */ + row++; + GUI.addComponent(row, 1, 1, 1, 7, 1, 2, 0, this.button_verify_B, this.panel_user_B, this.gbc_horizontal); + + // 为字符串加密面板添加组件 + row = 0; + GUI.addComponent(row, 0, 2, 1, 2, 0, 2, 0, this.label_plaintext, this.panel_string, this.gbc_both); + GUI.addComponent(row, 3, 2, 1, 2, 0, 2, 0, this.label_plaintext_characters, this.panel_string, this.gbc_both); + GUI.addComponent(row, 5, 2, 1, 2, 0, 2, 0, this.label_ciphertext, this.panel_string, this.gbc_both); + GUI.addComponent(row, 8, 2, 1, 2, 0, 2, 0, this.label_ciphertext_characters, this.panel_string, this.gbc_both); + row++; + GUI.addComponent(row, 0, 5, 1, 5, 9, 2, 0, this.pane_split_plaintext, this.panel_string, this.gbc_both); + GUI.addComponent(row, 5, 5, 1, 5, 9, 2, 0, this.pane_split_ciphertext, this.panel_string, this.gbc_both); + // this.panel_string.add(this.pane_split_plaintext, BorderLayout.WEST); + // this.panel_string.add(this.pane_split_ciphertext, BorderLayout.EAST); + + // 为文件加密面板添加组件 + row = 0; + GUI.addComponent(row, 0, 2, 1, 2, 1, 2, 2, this.label_plaintext_target_file, this.panel_file_encrypt, this.gbc_horizontal); + GUI.addComponent(row, 2, 1, 1, 1, 1, 2, 2, this.button_select_plaintext_file, this.panel_file_encrypt, this.gbc_horizontal); + row++; + GUI.addComponent(row, 0, GridBagConstraints.REMAINDER, 1, 8, 1, 2, 2, this.field_plaintext_file_path, this.panel_file_encrypt, this.gbc_horizontal); + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 2, this.label_plaintext_file_size, this.panel_file_encrypt, this.gbc_horizontal); + + row++; + GUI.addComponent(row, 0, 2, 1, 2, 1, 2, 2, this.label_ciphertext_target_dir, this.panel_file_encrypt, this.gbc_horizontal); + GUI.addComponent(row, 2, 1, 1, 1, 1, 2, 2, this.button_select_ciphertext_dir, this.panel_file_encrypt, this.gbc_horizontal); + row++; + GUI.addComponent(row, 0, GridBagConstraints.REMAINDER, 1, 8, 1, 2, 2, this.field_ciphertext_dir_path, this.panel_file_encrypt, this.gbc_horizontal); + + // 为文件解密面板添加组件 + row = 0; + GUI.addComponent(row, 0, 2, 1, 2, 1, 2, 2, this.label_ciphertext_target_file, this.panel_file_decrypt, this.gbc_horizontal); + GUI.addComponent(row, 2, 1, 1, 1, 1, 2, 2, this.button_select_ciphertext_file, this.panel_file_decrypt, this.gbc_horizontal); + row++; + GUI.addComponent(row, 0, GridBagConstraints.REMAINDER, 1, 8, 1, 2, 2, this.field_ciphertext_file_path, this.panel_file_decrypt, this.gbc_horizontal); + row++; + GUI.addComponent(row, 0, 1, 1, 1, 1, 2, 2, this.label_ciphertext_file_size, this.panel_file_decrypt, this.gbc_horizontal); + + row++; + GUI.addComponent(row, 0, 2, 1, 2, 1, 2, 2, this.label_plaintext_target_dir, this.panel_file_decrypt, this.gbc_horizontal); + GUI.addComponent(row, 2, 1, 1, 1, 1, 2, 2, this.button_select_plaintext_dir, this.panel_file_decrypt, this.gbc_horizontal); + row++; + GUI.addComponent(row, 0, GridBagConstraints.REMAINDER, 1, 8, 1, 2, 2, this.field_plaintext_dir_path, this.panel_file_decrypt, this.gbc_horizontal); + + } + + private void initTabbedPane() // 初始化选项卡窗格 + { + this.pane_tab_data = new JTabbedPane(JTabbedPane.TOP); + this.pane_tab_data.addTab("字符串加密/解密", this.panel_string); + this.pane_tab_data.addTab("文件加密", this.panel_file_encrypt); + this.pane_tab_data.addTab("文件解密", this.panel_file_decrypt); + + // 添加事件监听者 + this.pane_tab_data.addChangeListener(new ChangeListener() + { + public void stateChanged(ChangeEvent event) + { + int index = pane_tab_data.getSelectedIndex(); // 获得选项卡的序号 + if (index == 0) { + button_encrypt_A.setEnabled(true); + button_decrypt_A.setEnabled(true); + button_encrypt_B.setEnabled(true); + button_decrypt_B.setEnabled(true); + } else if (index == 1) { + button_encrypt_A.setEnabled(true); + button_decrypt_A.setEnabled(false); + button_encrypt_B.setEnabled(true); + button_decrypt_B.setEnabled(false); + } else if (index == 2) { + button_encrypt_A.setEnabled(false); + button_decrypt_A.setEnabled(true); + button_encrypt_B.setEnabled(false); + button_decrypt_B.setEnabled(true); + } + } + }); + + this.pane_tab_user = new JTabbedPane(JTabbedPane.TOP); + this.pane_tab_user.addTab("算法参数", this.panel_parameter); + this.pane_tab_user.addTab("用户 A", this.panel_user_A); + this.pane_tab_user.addTab("用户 B", this.panel_user_B); + } + + private void initFrame() // 初始化窗口 + { + this.frame = new JFrame("密码学小作业"); + + this.frame.setSize(GUI.WINDOW_WIDTH, GUI.WINDOW_HEIGHT); + this.frame.setLocation(0, 100); + this.frame.setBackground(Color.WHITE); + this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + // this.frame.setLayout(new GridBagLayout()); // 使用网格包布局 + this.frame.setLayout(new BorderLayout(5, 5)); // 使用边界布局 + + this.frame.add(this.pane_tab_data, BorderLayout.CENTER); + this.frame.add(this.pane_tab_user, BorderLayout.EAST); + this.frame.setVisible(true); + } + + private boolean isKeyStandard(JTextField field) + { + try { + String key_str = field.getText(); + if (key_str.length() != this.key_len * 2) throw new RuntimeException(); + this.key = Utils.hexStringToByte(key_str); + return true; + } catch (RuntimeException e) { + this.key = null; + return false; + } + } + + private boolean isRSAStandard() + { + return this.complete_key.isValid(); + } + + private boolean isFileEffective(File file) + { + if (file == null) { + Utils.showStatusDialog("未选择文件!", "文件状态信息"); + return false; + } else if (!file.exists()) { + Utils.showStatusDialog("文件不存在!", "文件状态信息"); + return false; + } else if (!file.canRead()) { + Utils.showStatusDialog("文件不可访问!", "文件状态信息"); + return false; + } else if (!file.isFile()) { + Utils.showStatusDialog("未选择文件!", "文件状态信息"); + return false; + } + return true; + } + + private boolean isEncryptedFileEffective(File file) + { + if (!isFileEffective(file)) { + return false; + } else { + try { + if (!file.getName().endsWith(GUI.ENCRYPTED_FILENAME_EXTENSION)) { + /* + * 返回用户点击的选项, 值为下面三者之一: + * 是: JOptionPane.YES_OPTION + * 否: JOptionPane.NO_OPTION + * 取消: JOptionPane.CANCEL_OPTION + * 关闭: JOptionPane.CLOSED_OPTION + */ + int result = JOptionPane.showConfirmDialog(this.frame, "警告: 文件拓展名称不正确, 可能并非加密文件!\n是否继续解密?", "文件状态信息", JOptionPane.YES_NO_CANCEL_OPTION); + switch (result) { + case JOptionPane.YES_OPTION: + return true; + case JOptionPane.NO_OPTION: + case JOptionPane.CANCEL_OPTION: + case JOptionPane.CLOSED_OPTION: + default: + return false; + } + + } + } catch (Exception e) { + Utils.showExceptionDialog("未知异常", e); + System.exit(0); + } + return true; + } + } + + private boolean isDirEffective(File dir) + { + if (dir == null) { + Utils.showStatusDialog("未选择目录!", "目录状态信息"); + return false; + } else if (!dir.exists()) { + Utils.showStatusDialog("目录不存在!", "目录状态信息"); + return false; + } else if (!dir.canRead()) { + Utils.showStatusDialog("目录不可访问!", "目录状态信息"); + return false; + } else if (!dir.isDirectory()) { + Utils.showStatusDialog("未选择目录!", "目录状态信息"); + return false; + } + return true; + } + + /** 打开文件/目录 + * @param chooser JFileChooser 所使用的文件选择对话框 + * @return File 若选择成功, 返回选择的文件/目录; 若选择失败, 返回 null + */ + private File selectFileOrDir(JFileChooser chooser) + { + String target_type = ""; + if (chooser.getFileSelectionMode() == JFileChooser.FILES_ONLY) target_type = "文件"; + else if (chooser.getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) target_type = "目录"; + + File file = null; + int status = chooser.showOpenDialog(this.frame); + switch (status) { + case JFileChooser.APPROVE_OPTION: // 点击确定按钮 + try { + file = chooser.getSelectedFile(); + if (chooser.getFileSelectionMode() == JFileChooser.FILES_ONLY) { + if (!file.isFile()) throw new Exception("文件无效"); + else if (!file.canRead()) throw new Exception("文件不可读"); + } else if (chooser.getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) { + if (!file.isDirectory()) throw new Exception("目录无效"); + } + // Utils.showStatusDialog(target_type + "选择成功", target_type + "选择提示信息"); + } catch (Exception e) { + Utils.showExceptionDialog(target_type + "选择出错", e); + } + break; + case JFileChooser.CANCEL_OPTION: + // Utils.showStatusDialog("没有选择" + target_type, target_type + "选择提示信息"); + break; + case JFileChooser.ERROR_OPTION: + default: + Utils.showStatusDialog(target_type + "选择出错", target_type + "选择提示信息"); + break; + } + + return file; + } + + /* 切换到用户 A 视角 */ + private boolean enableUserA() + { + try { + this.complete_key = new RSACompleteKey( // + new BigInteger(this.field_p_A.getText(), 16), // + new BigInteger(this.field_q_A.getText(), 16), // + new BigInteger(this.field_n_A_A.getText(), 16), // + new BigInteger(this.field_e_A_A.getText(), 16), // + new BigInteger(this.field_d_A.getText(), 16), // + new BigInteger(this.field_dP_A.getText(), 16), // + new BigInteger(this.field_dQ_A.getText(), 16), // + new BigInteger(this.field_qInv_A.getText(), 16) // + ); + this.public_key = new RSAPublicKey( // + new BigInteger(this.field_n_B_A.getText(), 16), // + new BigInteger(this.field_e_B_A.getText(), 16) // + ); + if (isRSAStandard()) return true; + return false; + } catch (Exception e) { + return false; + } + } + + /* 切换到用户 B 视角 */ + private boolean enableUserB() + { + try { + this.complete_key = new RSACompleteKey( // + new BigInteger(this.field_p_B.getText(), 16), // + new BigInteger(this.field_q_B.getText(), 16), // + new BigInteger(this.field_n_B_B.getText(), 16), // + new BigInteger(this.field_e_B_B.getText(), 16), // + new BigInteger(this.field_d_B.getText(), 16), // + new BigInteger(this.field_dP_B.getText(), 16), // + new BigInteger(this.field_dQ_B.getText(), 16), // + new BigInteger(this.field_qInv_B.getText(), 16) // + ); + this.public_key = new RSAPublicKey( // + new BigInteger(this.field_n_A_B.getText(), 16), // + new BigInteger(this.field_e_A_B.getText(), 16) // + ); + if (isRSAStandard()) return true; + return false; + } catch (Exception e) { + return false; + } + } + + /* 提取消息摘要 */ + private void setDigest() + { + this.digest_string = Utils.byteToHexString(cipher.digest); + if (cipher._digest == null) { + this._digest_string = Utils.byteToHexString(cipher.digest); + } else { + this._digest_string = Utils.byteToHexString(cipher._digest); + } + } + + /* 加密 */ + private boolean encrypt(byte[] message) + { + try { + this.cipher // + .setParams(this.box_encrypt_mode.getSelectedIndex()) // + .setMessage(message) // + .buildDigest(this.digest) // + .buildSignature(this.complete_key) // + .constructDatagram() // + .encryptDatagram(this.crypto, this.key) // + .encryptKey(this.public_key) // + .constructDataPacket() // + ; + return true; + } catch (Exception e) { + Utils.showStatusDialog("消息加密失败", "消息加密"); + return false; + } + } + + /* 解密 */ + private boolean decrypt(byte[] packet) + { + try { + this.cipher // + .setParams(this.box_encrypt_mode.getSelectedIndex()) // + .setPacket(packet) // + .deconstructDataPacket() // + .decryptKey(this.complete_key) // + .decryptDatagram(crypto) // + .deconstructDatagram() // + .verifySignature(this.public_key) // + .buildDigest(this.digest) // + ; + return true; + } catch (Exception e) { + Utils.showStatusDialog("消息解密失败", "消息解密"); + return false; + } + } + + /* 加密字符串 */ + private boolean stringEncrypt() + { + byte message[] = Utils.stringEncoding(this.area_plaintext.getText(), GUI.DEFAULT_CHAR_SET); + if (!encrypt(message)) return false; + this.area_ciphertext.setText(Utils.stringDecoding(cipher.getPacket(), GUI.ENCRYPT_CHAR_SET)); + setDigest(); + return true; + } + + /* 解密字符串 */ + private boolean stringDecrypt() + { + + byte packet[] = Utils.stringEncoding(this.area_ciphertext.getText(), GUI.ENCRYPT_CHAR_SET); + if (!decrypt(packet)) return false; + this.area_plaintext.setText(Utils.stringDecoding(cipher.getMessage(), GUI.DEFAULT_CHAR_SET)); + setDigest(); + return true; + } + + /* 加密文件 */ + private boolean fileEncrypt() + { + try { + String filename = this.file_plaintext.getName(); + String dir = this.dir_ciphertext.getPath(); + String output_filename = String.format(GUI.FORMAT_TARGET_FILE_PATH, dir, filename + GUI.ENCRYPTED_FILENAME_EXTENSION); + byte[] message = Files.readAllBytes(this.file_plaintext.toPath()); + if (!encrypt(message)) return false; + Files.write(Paths.get(output_filename), this.cipher.getPacket()); + setDigest(); + return true; + } catch (IOException e) { + Utils.showExceptionDialog("文件读写异常", e); + return false; + } + } + + /* 解密文件 */ + private boolean fileDecrypt() + { + try { + String filename = this.file_ciphertext.getName(); + String dir = this.dir_plaintext.getPath(); + String output_filename = filename.endsWith(GUI.ENCRYPTED_FILENAME_EXTENSION) // + ? String.format(GUI.FORMAT_TARGET_FILE_PATH, dir, filename.substring(0, filename.lastIndexOf(GUI.ENCRYPTED_FILENAME_EXTENSION))) // + : String.format(GUI.FORMAT_TARGET_FILE_PATH, dir, filename + GUI.DECRYPTED_FILENAME_EXTENSION); + byte[] packet = Files.readAllBytes(this.file_ciphertext.toPath()); + if (!decrypt(packet)) return false; + Files.write(Paths.get(output_filename), this.cipher.getMessage()); + setDigest(); + return true; + } catch (IOException e) { + Utils.showExceptionDialog("文件读写异常", e); + return false; + } + } + + public static void addComponent( // + int row, // 第几行格子 + int column, // 第几列格子 + int width, // 占几列格子 + int height, // 占几行格子 + double weightx, // 横向权重 + double weighty, // 纵向权重 + int ipadx, // 左右边缘的内边距 + int ipady, // 上下边缘的内边距 + Component c, // 组件 + Container contain, // 面板 + GridBagConstraints gbc // 布局管理器 + ) + { + gbc.gridy = row; // 第几行的格子 + gbc.gridx = column; // 第几列的格子 + gbc.gridwidth = width; // 占用几列格子 + gbc.gridheight = height; // 占用几行格子 + gbc.weightx = weightx; // 行方向分配的权重 + gbc.weighty = weighty; // 列方向分配的权重 + gbc.ipadx = ipadx; // 左右边缘的内边距 + gbc.ipady = ipady; // 上下边缘的内边距 + contain.add(c, gbc); // 将组件c按照约束条件gbc添加到容器contain中 + } + +} diff --git a/assignment/src/assignment/cryptography/Utils.java b/assignment/src/assignment/cryptography/Utils.java new file mode 100644 index 0000000..237b52f --- /dev/null +++ b/assignment/src/assignment/cryptography/Utils.java @@ -0,0 +1,390 @@ +package assignment.cryptography; + +import java.awt.*; + +import javax.swing.*; +import javax.swing.plaf.FontUIResource; + +import java.security.*; +import java.text.SimpleDateFormat; +import java.util.*; +import java.io.*; + +/** + * REF https://www.iteye.com/blog/zxmzfbdc-1915912 + * REF [jdk8/jdk8/jdk: 687fd7c7986d src/share/classes/sun/security/provider/ByteArrayAccess.java](https://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/sun/security/provider/ByteArrayAccess.java) + */ +public class Utils +{ + public static final byte[] HEX_TABLE = { 0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF }; + public static final char[] HEX_CHAR_TABLE = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + public static void main(String[] args) + { + System.out.println(Utils.byteToHexString(Utils.getHashByteString128(""))); + System.out.println(Utils.byteToHexString(Utils.getHashByteString256(""))); + System.out.println(Utils.byteToHexString(Utils.getHashByteString512(""))); + } + + /** byte 数组转 int32 数组 + * @param bytes byte[]: byte 数组 + * @param bigEndian boolean: 是否为大字节序 + * @return String 16进制字符串(大写字母) + */ + public static int[] byteToInt32(byte[] bytes, boolean bigEndian) + { + if ((bytes.length == 0) || (bytes.length % 4 != 0)) return null; + + int[] ints = new int[bytes.length / 4]; + int value = 0; + + for (int i = 0, begin = 0; i < ints.length; begin = (++i) << 2) { + if (bigEndian) { // 大字节序 + value = bytes[3 | begin] // + | (bytes[2 | begin]) << 8 // + | (bytes[1 | begin]) << 16 // + | (bytes[0 | begin]) << 24; // + } else { // 小字节序 + value = bytes[0 | begin] // + | (bytes[1 | begin]) << 8 // + | (bytes[2 | begin]) << 16 // + | (bytes[3 | begin]) << 24; // + } + ints[i] = value; + } + return ints; + } + + /** int32 数组 转 byte 数组 + * @param ints byte[]: int32 数组 + * @param bigEndian boolean: 是否为大字节序 + * @return String 16进制字符串(大写字母) + */ + public static byte[] int32ToByte(int[] ints, boolean bigEndian) + { + if (ints.length == 0) return null; + + byte[] bytes = new byte[ints.length * 4]; + int value = 0; + + for (int i = 0, begin = 0; i < ints.length; begin = (++i) << 2) { + value = ints[i]; + if (bigEndian) { // 大字节序 + bytes[begin | 3] = (byte) (value); + bytes[begin | 2] = (byte) (value >> 8); + bytes[begin | 1] = (byte) (value >> 16); + bytes[begin | 0] = (byte) (value >> 24); + } else { // 小字节序 + bytes[begin | 0] = (byte) (value); + bytes[begin | 1] = (byte) (value >> 8); + bytes[begin | 2] = (byte) (value >> 16); + bytes[begin | 3] = (byte) (value >> 24); + } + } + + return bytes; + } + + /** 字节序转换 + * @param input byte[]: byte 数组 + * @return byte[]: byte 数组 + */ + public static byte[] endiannessConvert(byte[] input) + { + if (input.length == 0) return null; + + byte[] output = new byte[input.length]; + for (int i = 0; i < input.length; ++i) { + for (int j = 0; j < 8; ++j) { + output[i] <<= 1; + output[i] |= 1 & (input[i] >>> j); + // output[input.length - i - 1] <<= 1; + // output[input.length - i - 1] |= 1 & (input[i] >>> j); + } + } + + return output; + } + + /** 字节序转换 + * @param input int[]: int 数组 + * @return int[]: int 数组 + */ + public static int[] endiannessConvert(int[] input) + { + if (input.length == 0) return null; + + int[] output = new int[input.length]; + for (int i = 0; i < input.length; ++i) { + for (int j = 0; j < 32; ++j) { + output[i] <<= 1; + output[i] |= 1 & (input[i] >>> j); + } + } + + return output; + } + + /** byte 数组转 16 进制字符串 + * @param data byte数组 + * @return String 16进制字符串(大写字母) + */ + public static String byteToHexString(byte[] data) + { + if (data == null || data.length == 0) return null; + byte[] hex = new byte[data.length * 2]; + int index = 0; + for (byte b : data) { + int v = b & 0xFF; + hex[index++] = (byte) HEX_CHAR_TABLE[v >>> 4]; + hex[index++] = (byte) HEX_CHAR_TABLE[v & 0xF]; + } + return new String(hex); + } + + /** 16 进制字符串转 byte 数组 + * @param hexString 16进制字符串 + * @return byte[] + */ + public static byte[] hexStringToByte(String hexString) throws RuntimeException + { + if (hexString == null || hexString.length() == 0) return null; + if (hexString.length() % 2 != 0) throw new RuntimeException("字节数未对齐"); + hexString.toUpperCase(); + byte[] data = new byte[hexString.length() / 2]; + char[] chars = hexString.toCharArray(); + for (int i = 0; i < hexString.length(); i = i + 2) { + data[i / 2] = (byte) (HEX_TABLE[getHexCharValue(chars[i])] << 4 | HEX_TABLE[getHexCharValue(chars[i + 1])]); + } + return data; + } + + /** 打印 byte[] + * @param data byte[] 字节数组 + * @return byte[] + */ + public static void print(byte[] data) + { + System.out.println(Utils.byteToHexString(data)); + } + + /** 打印 int[] + * @param hexString 16进制字符串 + * @return byte[] + */ + public static void print(int[] data) + { + Utils.print(Utils.int32ToByte(data, true)); + } + + public static byte[] getHashByteString(String str, int bytes) + { + try { + MessageDigest digest = null; + switch (bytes) { + case 16: + digest = MessageDigest.getInstance("MD5"); + break; + + case 32: + digest = MessageDigest.getInstance("SHA-256"); + break; + + case 64: + digest = MessageDigest.getInstance("SHA-512"); + break; + + default: + throw new RuntimeException("哈希函数位数设置错误"); + } + // digest.update(String.valueOf(str).getBytes()); + // return new ByteArrayInputStream(digest.digest()); + return digest.digest(str.getBytes()); + } catch (NoSuchAlgorithmException e) { + Utils.showExceptionDialog("哈希函数名称错误", e); + System.exit(0); + } catch (RuntimeException e) { + Utils.showExceptionDialog("哈希函数位数设置错误", e); + System.exit(0); + } + return null; + } + + public static byte[] getHashByteString128(String str) + { + try { + MessageDigest digest = MessageDigest.getInstance("MD5"); + // digest.update(String.valueOf(str).getBytes()); + // return new ByteArrayInputStream(digest.digest()); + return digest.digest(str.getBytes()); + } catch (NoSuchAlgorithmException e) { + Utils.showExceptionDialog("哈希函数名称错误", e); + System.exit(0); + } + return null; + } + + public static byte[] getHashByteString256(String str) + { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + // digest.update(String.valueOf(str).getBytes()); + // return new ByteArrayInputStream(digest.digest()); + return digest.digest(str.getBytes()); + } catch (NoSuchAlgorithmException e) { + Utils.showExceptionDialog("哈希函数名称错误", e); + System.exit(0); + } + return null; + } + + public static byte[] getHashByteString512(String str) + { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-512"); + // digest.update(String.valueOf(str).getBytes()); + // return new ByteArrayInputStream(digest.digest()); + return digest.digest(str.getBytes()); + } catch (NoSuchAlgorithmException e) { + Utils.showExceptionDialog("哈希函数名称错误", e); + System.exit(0); + } + return null; + } + + /** 获取异常所有信息 + * @param e Exception 异常类 + * @return String 异常的堆栈信息 + */ + public static String getExceptionAllInfo(Exception e) + { + String info = ""; + StackTraceElement[] trace = e.getStackTrace(); + for (StackTraceElement s : trace) { + info += "\tat " + s + "\r\n"; + } + return info; + } + + /** 使用对话框显示异常信息 + * @param info String 自定义的异常信息 + * @param e Exception 异常类 + */ + public static void showExceptionDialog(String info, Exception e) + { + JOptionPane.showMessageDialog(null, String.format("%s\n异常信息:\n%s\n堆栈信息:\n%s", info, e.toString(), Utils.getExceptionAllInfo(e)), "运行时异常", JOptionPane.ERROR_MESSAGE); + } + + /** 使用对话框显示状态信息 + * @param info String 自定义状态信息 + * @param title String 自定义对话框标题, 若为null, 则默认为"状态提示信息" + */ + public static void showStatusDialog(String info, String title) + { + if (title == null) title = "状态提示信息"; + JOptionPane.showMessageDialog(null, info, title, JOptionPane.INFORMATION_MESSAGE); + } + + /** 生成指定长度随机字符串 + * @param length int 自定义长度 + * @return String 生成的字符串 + */ + public static String getRandomString(int length) + { + String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + Random random = new Random(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < length; i++) { + int number = random.nextInt(62); + sb.append(str.charAt(number)); + } + return sb.toString(); + } + + /** 统一设置字体,父界面设置之后,所有由父界面进入的子界面都不需要再次设置字体 + * @param font Font 字体 + */ + public static void initGlobalFont(Font font) + { + FontUIResource fontRes = new FontUIResource(font); + for (Enumeration keys = UIManager.getDefaults().keys(); keys.hasMoreElements();) { + Object key = keys.nextElement(); + Object value = UIManager.get(key); + if (value instanceof FontUIResource) { + UIManager.put(key, fontRes); + } + } + } + + /** 字符串编码 + * @param str String 字符串 + * @param charSetName Stirng 字符集名称 + * @return byte[] 字符串编码 + */ + public static byte[] stringEncoding(String str, String charSetName) + { + byte ans[] = null; + try { + ans = str.getBytes(charSetName); + } catch (UnsupportedEncodingException e) { + Utils.showExceptionDialog("不支持设定的字符集", e); + System.exit(0); + } + return ans; + } + + /** 字符串解码 + * @param byteString byte[] 字节串 + * @param charSetName Stirng 字符集名称 + * @return String 字符串解码 + */ + public static String stringDecoding(byte[] byteString, String charSetName) + { + String str = null; + try { + str = new String(byteString, charSetName); + } catch (UnsupportedEncodingException e) { + Utils.showExceptionDialog("不支持设定的字符集", e); + System.exit(0); + } + return str; + } + + /** + * 字节首填充 + */ + public static byte[] paddingStart(byte[] origin, int len) + { + byte[] ans = new byte[len]; + System.arraycopy(origin, 0, ans, len - origin.length, origin.length); + return ans; + } + + /** + * 字节尾填充 + */ + public static byte[] paddingEnd(byte[] origin, int len) + { + byte[] ans = new byte[len]; + System.arraycopy(origin, 0, ans, len, ans.length); + return ans; + } + + /* 当前时间戳字符串 */ + public static String getTimestamp() + { + return new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date()); + } + + private static int getHexCharValue(char c) throws RuntimeException + { + int index = -1; + if (Character.isDigit(c)) index = c - '0'; + else if (Character.isUpperCase(c)) index = c - 'A' + 10; + else if (Character.isLowerCase(c)) index = c - 'a' + 10; + + if (index < 0 || index >= 16) throw new RuntimeException("出现非16进制合法字符"); + return index; + } + +} diff --git a/assignment/src/assignment/cryptography/crypto/AES.java b/assignment/src/assignment/cryptography/crypto/AES.java new file mode 100644 index 0000000..d14e2d5 --- /dev/null +++ b/assignment/src/assignment/cryptography/crypto/AES.java @@ -0,0 +1,985 @@ +package assignment.cryptography.crypto; + +import java.util.Arrays; + +import assignment.cryptography.Utils; +import static assignment.cryptography.ByteArrayAccess.*; + +enum Mode { + ENCRYPT, DECRYPT, +}; + +public class AES implements CryptoInterface +{ + private static final int BLOCK_SIZE = 16; // 分组大小(单位: Byte) + private static final int[] S_BOX_FORWORD; // S 盒代换表-正变换 + private static final int[] S_BOX_INVERSE; // S 盒代换表-逆变换 + + private static final int[][] T_FORWORD; // S 盒*列混淆 组合代换查找表-正变换 + private static final int[][] T_INVERSE; // S 盒*列混淆 组合代换查找表-逆变换 + + private static final int[] OFFSET_ENCRYPT; // 加密时行移位偏移量 + private static final int[] OFFSET_DECRYPT; // 解密时行移位偏移量 + + private boolean debug; // 是否开启调试模式 + private int[] key; // 密钥(大字节序) + private int[] subkeys_encrypt; // 加密子密钥(大字节序) + private int[] subkeys_decrypt; // 子密钥(大字节序) + private int Nk; // 密钥长度(单位: 32位字) + private int Nb; // 分组长度(单位: 32位字) + private int Nr; // 轮数 + + private Mode mode; // 模式(加密/解密) + private int[] state; // 当前状态 + private int[] temp; // 临时状态 + + private int[] S_BOX; // 当前使用的字节代换表 + private int[][] T; // 当前使用的组合代换查找表 + + private int[] offset; // 行移位偏移量 + private int[] subkeys; // 当前使用的子密钥 + private int subkeys_index; // 当前使用的子密钥的字索引 + + static { + /* S 盒代换-正变换(8bit => 8bit) */ + S_BOX_FORWORD = new int[] { // + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, // + 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, // + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, // + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, // + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, // + 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, // + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, // + 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, // + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, // + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, // + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, // + 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, // + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, // + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, // + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, // + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, // + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, // + 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, // + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, // + 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, // + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, // + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, // + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, // + 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, // + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, // + 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, // + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, // + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, // + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, // + 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, // + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, // + 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, // + }; + + /* S 盒代换-逆变换(8bit => 8bit) */ + S_BOX_INVERSE = new int[] { // + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, // + 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, // + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, // + 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, // + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, // + 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, // + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, // + 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, // + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, // + 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, // + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, // + 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, // + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, // + 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, // + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, // + 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, // + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, // + 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, // + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, // + 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, // + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, // + 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, // + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, // + 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, // + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, // + 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, // + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, // + 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, // + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, // + 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, // + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, // + 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d, // + }; + + /* 组合代换查找表-正代换(32bit => 32bit) */ + T_FORWORD = new int[][] { // + { // + 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, // + 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, // + 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, // + 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, // + 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, // + 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, // + 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, // + 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, // + 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, // + 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, // + 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, // + 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, // + 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, // + 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, // + 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, // + 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, // + 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, // + 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, // + 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, // + 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, // + 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, // + 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, // + 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, // + 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, // + 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, // + 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, // + 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, // + 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, // + 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, // + 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, // + 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, // + 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, // + 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, // + 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, // + 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, // + 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, // + 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, // + 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, // + 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, // + 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, // + 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, // + 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, // + 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, // + 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, // + 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, // + 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, // + 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, // + 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, // + 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, // + 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, // + 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, // + 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, // + 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, // + 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, // + 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, // + 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, // + 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, // + 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, // + 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, // + 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, // + 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, // + 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, // + 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, // + 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, // + }, { // + 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, // + 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, // + 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, // + 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, // + 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, // + 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, // + 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, // + 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, // + 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, // + 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, // + 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, // + 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, // + 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, // + 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, // + 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, // + 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, // + 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, // + 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, // + 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, // + 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, // + 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, // + 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, // + 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, // + 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, // + 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, // + 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, // + 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, // + 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, // + 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, // + 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, // + 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, // + 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, // + 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, // + 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, // + 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, // + 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, // + 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, // + 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, // + 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, // + 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, // + 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, // + 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, // + 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, // + 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, // + 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, // + 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, // + 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, // + 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, // + 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, // + 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, // + 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, // + 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, // + 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, // + 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, // + 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, // + 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, // + 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, // + 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, // + 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, // + 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, // + 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, // + 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, // + 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, // + 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, // + }, { // + 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, // + 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, // + 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, // + 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, // + 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, // + 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, // + 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, // + 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, // + 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, // + 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, // + 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, // + 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, // + 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, // + 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, // + 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, // + 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, // + 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, // + 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, // + 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, // + 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, // + 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, // + 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, // + 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, // + 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, // + 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, // + 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, // + 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, // + 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, // + 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, // + 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, // + 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, // + 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, // + 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, // + 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, // + 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, // + 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, // + 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, // + 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, // + 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, // + 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, // + 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, // + 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, // + 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, // + 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, // + 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, // + 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, // + 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, // + 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, // + 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, // + 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, // + 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, // + 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, // + 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, // + 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, // + 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, // + 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, // + 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, // + 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, // + 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, // + 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, // + 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, // + 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, // + 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, // + 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, // + }, { // + 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, // + 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, // + 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, // + 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, // + 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, // + 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, // + 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, // + 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, // + 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, // + 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, // + 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, // + 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, // + 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, // + 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, // + 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, // + 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, // + 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, // + 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, // + 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, // + 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, // + 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, // + 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, // + 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, // + 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, // + 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, // + 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, // + 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, // + 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, // + 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, // + 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, // + 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, // + 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, // + 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, // + 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, // + 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, // + 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, // + 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, // + 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, // + 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, // + 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, // + 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, // + 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, // + 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, // + 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, // + 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, // + 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, // + 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, // + 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, // + 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, // + 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, // + 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, // + 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, // + 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, // + 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, // + 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, // + 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, // + 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, // + 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, // + 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, // + 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, // + 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, // + 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, // + 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, // + 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, // + }, // + }; + + /* 组合代换查找表-逆代换(32bit => 32bit) */ + T_INVERSE = new int[][] { // + { // + 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, // + 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, // + 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, // + 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, // + 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, // + 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, // + 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, // + 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, // + 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, // + 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, // + 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, // + 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, // + 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, // + 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, // + 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, // + 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, // + 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, // + 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, // + 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, // + 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, // + 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, // + 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, // + 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, // + 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, // + 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, // + 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, // + 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, // + 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, // + 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, // + 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, // + 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, // + 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, // + 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, // + 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, // + 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, // + 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, // + 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, // + 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, // + 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, // + 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, // + 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, // + 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, // + 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, // + 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, // + 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, // + 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, // + 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, // + 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, // + 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, // + 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, // + 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, // + 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, // + 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, // + 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, // + 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, // + 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, // + 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, // + 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, // + 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, // + 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, // + 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, // + 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, // + 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, // + 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742, // + }, { // + 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, // + 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, // + 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, // + 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, // + 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, // + 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, // + 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, // + 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, // + 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, // + 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, // + 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, // + 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, // + 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, // + 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, // + 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, // + 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, // + 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, // + 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, // + 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, // + 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, // + 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, // + 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, // + 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, // + 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, // + 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, // + 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, // + 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, // + 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, // + 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, // + 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, // + 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, // + 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, // + 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, // + 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, // + 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, // + 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, // + 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, // + 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, // + 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, // + 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, // + 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, // + 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, // + 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, // + 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, // + 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, // + 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, // + 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, // + 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, // + 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, // + 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, // + 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, // + 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, // + 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, // + 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, // + 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, // + 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, // + 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, // + 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, // + 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, // + 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, // + 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, // + 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, // + 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, // + 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857, // + }, { // + 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, // + 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, // + 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, // + 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, // + 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, // + 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, // + 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, // + 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, // + 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, // + 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, // + 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, // + 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, // + 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, // + 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, // + 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, // + 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, // + 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, // + 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, // + 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, // + 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, // + 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, // + 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, // + 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, // + 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, // + 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, // + 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, // + 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, // + 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, // + 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, // + 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, // + 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, // + 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, // + 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, // + 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, // + 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, // + 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, // + 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, // + 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, // + 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, // + 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, // + 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, // + 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, // + 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, // + 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, // + 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, // + 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, // + 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, // + 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, // + 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, // + 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, // + 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, // + 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, // + 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, // + 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, // + 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, // + 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, // + 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, // + 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, // + 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, // + 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, // + 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, // + 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, // + 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, // + 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8, // + }, { // + 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, // + 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, // + 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, // + 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, // + 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, // + 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, // + 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, // + 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, // + 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, // + 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, // + 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, // + 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, // + 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, // + 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, // + 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, // + 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, // + 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, // + 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, // + 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, // + 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, // + 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, // + 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, // + 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, // + 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, // + 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, // + 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, // + 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, // + 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, // + 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, // + 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, // + 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, // + 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, // + 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, // + 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, // + 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, // + 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, // + 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, // + 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, // + 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, // + 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, // + 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, // + 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, // + 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, // + 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, // + 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, // + 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, // + 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, // + 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, // + 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, // + 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, // + 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, // + 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, // + 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, // + 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, // + 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, // + 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, // + 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, // + 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, // + 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, // + 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, // + 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, // + 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, // + 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, // + 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0, // + }, // + }; + + OFFSET_ENCRYPT = new int[] { 0, 1, 2, 3 }; + OFFSET_DECRYPT = new int[] { 0, 3, 2, 1 }; + } + + /** 获得下一个轮常数 + * @param rconst int: 本次轮常数 + * @return int: 下一个轮常数 + */ + private static int nextRconst(int rconst) + { + return ((rconst & 0x7F) << 1) ^ ((rconst & 0x80) > 0 ? 0x1B : 0); + } + + /** 构造函数 + * @param debug {@code int}: 是否开启调试模式 + */ + public AES(boolean debug) + { + this.debug = debug; + } + + /* 构造函数 */ + public AES(int keyLen) + { + this.key = new int[keyLen]; + } + + /* 构造函数 */ + public AES() + { + this.debug = false; + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + public int getKeySize() + { + return this.key.length * 4; + } + + /** 设置密钥 + * @param key byte[]: 密钥字节串(16/24/32 Byte, 128/192/256 bit) + */ + public void setKey(byte[] key) + { + this.setKey(key, true); + } + + /** 设置密钥 + * @param key byte[]: 密钥字节串(16/24/32 Byte, 128/192/256 bit) + * @param bigEndian boolean: 是否为大字节序 + */ + public void setKey(byte[] key, boolean bigEndian) + { + assert (key.length == 16 || key.length == 24 || key.length == 32) : "密钥长度异常!"; + + // int[] key_ints = Utils.byteToInt32(key, bigEndian); + int[] key_ints = new int[key.length / 4]; + if (bigEndian) b2iBig(key, 0, key_ints, 0, key.length); + else b2iLittle(key, 0, key_ints, 0, key.length); + this.setKey(key_ints); + } + + /** 设置密钥 + * @param key int[]: 密钥字串(4/6/8 32word, 16/24/32 Byte, 128/192/256 bit) + */ + public void setKey(int[] key) + { + assert (key.length == 4 || key.length == 6 || key.length == 8) : "密钥长度异常!"; + + this.key = key; + this.globalInit(); + this.subkeysGenerate(); + } + + /** 初始化全局参数 + */ + private void globalInit() + { + this.Nk = this.key.length; + this.Nb = 4; + this.Nr = 6 + this.Nk; + this.temp = new int[this.Nb]; + } + + /** 子密钥生成 + * + */ + private void subkeysGenerate() + { + final int SUBKEYS_LEN = (this.Nr + 1) * this.Nb; // 子密钥长度(单位: 32位字) + this.subkeys_encrypt = new int[SUBKEYS_LEN]; + this.subkeys_decrypt = new int[SUBKEYS_LEN]; + int[] temp_bytes = new int[4]; + /* + * 加密密钥生成 + * 每轮循环生成一个字的子密钥, 最终使用的与分组长度等长的子密钥在其中截取 + * rconst: 轮常数 + */ + for (int i = 0, rconst = 0x01; i < SUBKEYS_LEN; ++i) { + if (i < this.Nk) { // 第一组密钥不进行扩展, 而是作为扩展的基础 + this.subkeys_encrypt[i] = this.key[i]; + } else { + int temp = this.subkeys_encrypt[i - 1]; // 子密钥的上一个字 + if (i % this.Nk == 0) { // 一组子密钥的第一个字进行一次 G 变换 + /* 子密钥的上一个字进行一字节的循环左移 */ + temp_bytes[0] = (temp >> 16) & 0xFF; + temp_bytes[1] = (temp >> 8) & 0xFF; + temp_bytes[2] = (temp >> 0) & 0xFF; + temp_bytes[3] = (temp >> 24) & 0xFF; + + /* 新的子密钥的一个字 */ + temp = AES.S_BOX_FORWORD[temp_bytes[0]] << 24 // + | AES.S_BOX_FORWORD[temp_bytes[1]] << 16 // + | AES.S_BOX_FORWORD[temp_bytes[2]] << 8 // + | AES.S_BOX_FORWORD[temp_bytes[3]] << 0; + + /* 与轮常数异或 */ + temp ^= rconst << 24; + + /* 获得下一个轮常数 */ + rconst = AES.nextRconst(rconst); + } else if (i % this.Nk == 4 && this.Nk > 6) { // 子密钥长度为 8 字时需要在一组子密钥的第5个字进行一次 G 变换 + /* 子密钥的上一个字进行一字节的循环左移 */ + temp_bytes[0] = (temp >> 24) & 0xFF; + temp_bytes[1] = (temp >> 16) & 0xFF; + temp_bytes[2] = (temp >> 8) & 0xFF; + temp_bytes[3] = (temp >> 0) & 0xFF; + + /* 新的子密钥的一个字 */ + temp = AES.S_BOX_FORWORD[temp_bytes[0]] << 24 // + | AES.S_BOX_FORWORD[temp_bytes[1]] << 16 // + | AES.S_BOX_FORWORD[temp_bytes[2]] << 8 // + | AES.S_BOX_FORWORD[temp_bytes[3]] << 0; + } + /* 由上一组子密钥对应的字生成本组子密钥的一个字 */ + this.subkeys_encrypt[i] = this.subkeys_encrypt[i - this.Nk] ^ temp; + } + + /* 子密钥生成调试 */ + // if (this.debug) { + // if ((i % this.Nb) == (this.Nb - 1)) { + // Utils.print(new int[] { // + // this.subkeys_encrypt[i - 3], // + // this.subkeys_encrypt[i - 2], // + // this.subkeys_encrypt[i - 1], // + // this.subkeys_encrypt[i - 0], // + // }); + // } + // } + } + + /** 解密密钥生成 + * 逆行变换 -> 逆字节代换 -> 轮密钥加 -> 逆列混淆 + * = 逆行变换 -> 逆字节代换 -> 逆列混淆 -> 轮密钥加(逆列混淆的轮密钥) + */ + for (int i = 0; i <= this.Nr; ++i) { // 每轮循环生成一个与一个分组等长的一组子密钥 + for (int j = 0; j < this.Nb; ++j) { // 每轮循环生成一组子密钥中的一个字 + int temp = this.subkeys_encrypt[(this.Nr - i) * this.Nb + j]; // 加密时某个轮密钥加操作使用的子密钥的一个字 + if (i != 0 && i != this.Nr) { // 解密时第一轮与最后一轮无需进行逆向列混淆 + temp_bytes[0] = (temp >> 24) & 0xFF; + temp_bytes[1] = (temp >> 16) & 0xFF; + temp_bytes[2] = (temp >> 8) & 0xFF; + temp_bytes[3] = (temp >> 0) & 0xFF; + + // T' 代换 = 字节代换逆变换 -> 列混淆逆变换 + // T'(S(s)) = 字节代换正变换 -> 字节代换逆变换 -> 列混淆逆变换 = 列混淆逆变换 + temp = AES.T_INVERSE[0][AES.S_BOX_FORWORD[temp_bytes[0]]] // + ^ AES.T_INVERSE[1][AES.S_BOX_FORWORD[temp_bytes[1]]] // + ^ AES.T_INVERSE[2][AES.S_BOX_FORWORD[temp_bytes[2]]] // + ^ AES.T_INVERSE[3][AES.S_BOX_FORWORD[temp_bytes[3]]]; + } + this.subkeys_decrypt[i * this.Nb + j] = temp; // 解密轮密钥组关联逆序的加密轮密钥组 + } + } + } + + /** 初始化参数 + * @param mode Mode: 模式(加密/解密) + * @param data int[]: 分组 + */ + private void init(Mode mode, int[] block) + { + assert (block.length == this.Nb) : "分组长度异常!"; + + this.mode = mode; // 模式 + this.state = Arrays.copyOf(block, this.Nb); // 分组/状态 + this.subkeys_index = 0; // 当前使用的子密钥字索引 + + switch (mode) { + case ENCRYPT: + this.S_BOX = AES.S_BOX_FORWORD; + this.T = AES.T_FORWORD; + + this.subkeys = this.subkeys_encrypt; + this.offset = AES.OFFSET_ENCRYPT; + break; + + case DECRYPT: + this.S_BOX = AES.S_BOX_INVERSE; + this.T = AES.T_INVERSE; + + this.subkeys = this.subkeys_decrypt; + this.offset = AES.OFFSET_DECRYPT; + break; + + } + } + + /** 轮密钥加 + */ + private void addRoundKey() + { + for (int i = 0; i < this.Nb; ++i, ++this.subkeys_index) { + this.state[i] ^= this.subkeys[this.subkeys_index]; + } + } + + /** 组合代换(行移位 -> 字节替代 -> 列混淆) + * 行移位对应的是列优先的状态, 如果是行优先, 那么实际上是在列上移位 + * i = 0 时 + * ((block[(0 + 0 % 4)] >> 24) & 0xFF); s_{0, 0} + * ((block[(0 + 1 % 4)] >> 16) & 0xFF); s_{1, 1} + * ((block[(0 + 2 % 4)] >> 8) & 0xFF); s_{2, 2} + * ((block[(0 + 3 % 4)] >> 0) & 0xFF); s_{3, 3} + * i = 1 时 + * ((block[(1 + 0 % 4)] >> 24) & 0xFF); s_{1, 0} + * ((block[(1 + 1 % 4)] >> 16) & 0xFF); s_{2, 1} + * ((block[(1 + 2 % 4)] >> 8) & 0xFF); s_{3, 2} + * ((block[(1 + 3 % 4)] >> 0) & 0xFF); s_{0, 3} + * i = 2时 + * ((block[(2 + 0 % 4)] >> 24) & 0xFF); s_{2, 0} + * ((block[(2 + 1 % 4)] >> 16) & 0xFF); s_{3, 1} + * ((block[(2 + 2 % 4)] >> 8) & 0xFF); s_{0, 2} + * ((block[(2 + 3 % 4)] >> 0) & 0xFF); s_{1, 3} + * i = 3时 + * ((block[(3 + 0 % 4)] >> 24) & 0xFF); s_{3, 0} + * ((block[(3 + 1 % 4)] >> 16) & 0xFF); s_{0, 1} + * ((block[(3 + 2 % 4)] >> 8) & 0xFF); s_{1, 2} + * ((block[(3 + 3 % 4)] >> 0) & 0xFF); s_{2, 3} + */ + private void substitute() + { + /* 数组拷贝 */ + // this.temp = Arrays.copyOf(this.state, this.Nb); + System.arraycopy(this.state, 0, this.temp, 0, this.Nb); + + for (int i = 0; i < this.Nb; ++i) { + this.state[i] = this.T[0][(this.temp[(i + this.offset[0]) % this.Nb] >> 24) & 0xFF] // + ^ this.T[1][(this.temp[(i + this.offset[1]) % this.Nb] >> 16) & 0xFF] // + ^ this.T[2][(this.temp[(i + this.offset[2]) % this.Nb] >> 8) & 0xFF] // + ^ this.T[3][(this.temp[(i + this.offset[3]) % this.Nb] >> 0) & 0xFF]; + } + } + + /** 最后一次组合代换(行移位 -> 字节替代) */ + private void lastSubstitute() + { + this.temp = Arrays.copyOf(this.state, this.Nb); + for (int i = 0; i < this.Nb; ++i) { + this.state[i] = (this.S_BOX[(this.temp[(i + this.offset[0]) % this.Nb] >> 24) & 0xFF] << 24) // + | (this.S_BOX[(this.temp[(i + this.offset[1]) % this.Nb] >> 16) & 0xFF] << 16) // + | (this.S_BOX[(this.temp[(i + this.offset[2]) % this.Nb] >> 8) & 0xFF] << 8) // + | (this.S_BOX[(this.temp[(i + this.offset[3]) % this.Nb] >> 0) & 0xFF] << 0); + } + } + + /** 分组处理 */ + private void handle() + { + this.addRoundKey(); // 轮密钥加 + for (int i = 0; i < this.Nr - 1; ++i) { + this.substitute(); // 行移位 -> 字节替代 -> 列置换 + this.addRoundKey(); // 轮密钥加 + } + this.lastSubstitute(); // 行移位 -> 字节替代 + this.addRoundKey(); // 轮密钥加 + } + + /** AES 加密 + * @param plaintext int[]: 4 * 32位数据(32位int*4, 128位) + * @return int[]: 4 * 32位数据(32位int*4, 128位) + */ + public int[] encrypt(int[] plaintext) + { + /* 初始化参数 */ + this.init(Mode.ENCRYPT, plaintext); + /* 运算 */ + this.handle(); + return this.state; + } + + /** AES 解密 + * @param ciphertext int[]: 4 * 32位数据(32位int*4, 128位) + * @return int[]: 4 * 32位数据(32位int*4, 128位) + */ + public int[] decrypt(int[] ciphertext) + { + /* 初始化参数 */ + this.init(Mode.DECRYPT, ciphertext); + /* 运算 */ + this.handle(); + return this.state; + } + + public static void main(String[] args) + { + AES aes = new AES(true); + + /* 密钥 */ + int[] key = { // + 0x0F1571C9, // + 0x47D9E859, // + 0x0CB7ADD6, // + 0xAF7F6798, // + }; + + /* 明文 */ + int[] plaintext1 = { // + 0x01234567, // + 0x89ABCDEF, // + 0xFEDCBA98, // + 0x76543210, // + }; + + /* 设置密钥 */ + aes.setKey(key); + + /* 加解密测试 */ + int[] ciphertext = aes.encrypt(plaintext1); + int[] plaintext2 = aes.decrypt(ciphertext); + + Utils.print(plaintext1); + Utils.print(ciphertext); + Utils.print(plaintext2); + } +} diff --git a/assignment/src/assignment/cryptography/crypto/CryptoInterface.java b/assignment/src/assignment/cryptography/crypto/CryptoInterface.java new file mode 100644 index 0000000..0817525 --- /dev/null +++ b/assignment/src/assignment/cryptography/crypto/CryptoInterface.java @@ -0,0 +1,14 @@ +package assignment.cryptography.crypto; + +public interface CryptoInterface +{ + public void setKey(int[] key); + + public int getBlockSize(); + + public int getKeySize(); + + public int[] encrypt(int[] plaintext); + + public int[] decrypt(int[] ciphertext); +} diff --git a/assignment/src/assignment/cryptography/crypto/DES.java b/assignment/src/assignment/cryptography/crypto/DES.java new file mode 100644 index 0000000..ec1f617 --- /dev/null +++ b/assignment/src/assignment/cryptography/crypto/DES.java @@ -0,0 +1,367 @@ +package assignment.cryptography.crypto; + +import assignment.cryptography.Utils; +import static assignment.cryptography.ByteArrayAccess.*; + +public class DES implements CryptoInterface +{ + private static final int BLOCK_SIZE = 8; // 分组大小(单位: Byte) + private static final int[] PC1; // 子密钥生成器置换1 + private static final int[] PC2; // 子密钥生成器置换2 + private static final int[] IP; // 初始置换 + private static final int[] FP; // 初始逆置换 + private static final int[] E; // 扩充置换 + private static final int[] P; // 置换 + private static final int[][][] SP_BOX; // S 盒 + + private static final int[] ROL_SHIFT; // 自密钥生成时每轮循环左移位数 + + private int[] key = new int[2]; // 密钥(小字节序, key[0]最低位为首, key[1]最高位为末) + private int[][] subkeys = new int[16][2]; // 子密钥 + + static { + PC1 = new int[] { // 子密钥生成器置换1 (64 -> 56) + 57, 49, 41, 33, 25, 17, 9, // + 1, 58, 50, 42, 34, 26, 18, // + 10, 2, 59, 51, 43, 35, 27, // + 19, 11, 3, 60, 52, 44, 36, // + 0, 0, 0, 0, // 移除奇偶校验后的半个密钥 28 位, 需要 4 位填充位 + + 63, 55, 47, 39, 31, 23, 15, // + 7, 62, 54, 46, 38, 30, 22, // + 14, 6, 61, 53, 45, 37, 29, // + 21, 13, 5, 28, 20, 12, 4, // + 0, 0, 0, 0, // + }; + PC2 = new int[] { // 子密钥生成器置换2 (56 -> 48) + 14, 17, 11, 24, 1, 5, // + 3, 28, 15, 6, 21, 10, // + 23, 19, 12, 4, 26, 8, // + 16, 7, 27, 20, 13, 2, // + 0, 0, 0, 0, 0, 0, 0, 0, // 半个子密钥 24 位, 需要 8 位填充位 + + 41, 52, 31, 37, 47, 55, // + 30, 40, 51, 45, 33, 48, // + 44, 49, 39, 56, 34, 53, // + 46, 42, 50, 36, 29, 32, // + 0, 0, 0, 0, 0, 0, 0, 0, // + }; + IP = new int[] { // 初始置换 (64->64) + 58, 50, 42, 34, 26, 18, 10, 2, // + 60, 52, 44, 36, 28, 20, 12, 4, // + 62, 54, 46, 38, 30, 22, 14, 6, // + 64, 56, 48, 40, 32, 24, 16, 8, // + + 57, 49, 41, 33, 25, 17, 9, 1, // + 59, 51, 43, 35, 27, 19, 11, 3, // + 61, 53, 45, 37, 29, 21, 13, 5, // + 63, 55, 47, 39, 31, 23, 15, 7, // + }; + FP = new int[] { // 初始逆置换 (64->64) + 40, 8, 48, 16, 56, 24, 64, 32, // + 39, 7, 47, 15, 55, 23, 63, 31, // + 38, 6, 46, 14, 54, 22, 62, 30, // + 37, 5, 45, 13, 53, 21, 61, 29, // + + 36, 4, 44, 12, 52, 20, 60, 28, // + 35, 3, 43, 11, 51, 19, 59, 27, // + 34, 2, 42, 10, 50, 18, 58, 26, // + 33, 1, 41, 9, 49, 17, 57, 25, // + }; + E = new int[] { // 扩充置换 (32->48) + 32, 1, 2, 3, 4, 5, // + 4, 5, 6, 7, 8, 9, // + 8, 9, 10, 11, 12, 13, // + 12, 13, 14, 15, 16, 17, // + 0, 0, 0, 0, 0, 0, 0, 0, // 8 位填充位 + + 16, 17, 18, 19, 20, 21, // + 20, 21, 22, 23, 24, 25, // + 24, 25, 26, 27, 28, 29, // + 28, 29, 30, 31, 32, 1, // + 0, 0, 0, 0, 0, 0, 0, 0, // + }; + P = new int[] { // 置换 (32->32) + 16, 7, 20, 21, 29, 12, 28, 17, // + 1, 15, 23, 26, 5, 18, 31, 10, // + 2, 8, 24, 14, 32, 27, 3, 9, // + 19, 13, 30, 6, 22, 11, 4, 25, // + }; + SP_BOX = new int[][][] { // S 盒 (48->32) + { // + { 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, }, // + { 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, }, // + { 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, }, // + { 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13, }, // + }, { // + { 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, }, // + { 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, }, // + { 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, }, // + { 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9, }, // + }, { // + { 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, }, // + { 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, }, // + { 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, }, // + { 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12, }, // + }, { // + { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, }, // + { 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, }, // + { 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, }, // + { 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14, }, // + }, { // + { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, }, // + { 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, }, // + { 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, }, // + { 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3, }, // + }, { // + { 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, }, // + { 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, }, // + { 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, }, // + { 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13, }, // + }, { // + { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, }, // + { 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, }, // + { 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, }, // + { 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12, }, // + }, { // + { 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, }, // + { 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, }, // + { 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, }, // + { 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11, }, // + }, // + }; + ROL_SHIFT = new int[] { // 自密钥生成时每轮循环左移位数 + 1, 1, 2, 2, 2, 2, 2, 2, // + 1, 2, 2, 2, 2, 2, 2, 1, // + }; + } + + /** 构造函数 + */ + public DES() + { + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + public int getKeySize() + { + return this.key.length * 4; + } + + /** 32 位字 序列 位置换 + * @param input int[]: 32 位字序列 + * @param map int[]: 位映射 + * @param output int[]: 置换后的 32 位字序列 + * @return int[]: 置换后的 32 位字序列 + */ + private static int[] permute(int[] input, int[] map, int[] output) + { + for (int i = 0, pos = 0; i < input.length * 32; ++i) { + pos = map[i] - 1; + output[i / 32] >>>= 1; + if (pos >= 0) output[i / 32] |= (0x01 & (input[pos / 32] >>> (pos % 32))) << 31; + } + return output; + } + + /** 32 位字 位置换 + * @param input int: 32 位字 + * @param map int[]: 位映射 + * @return int: 置换后的 32 位字 + */ + private static int permute(int input, int[] map) + { + int output = 0; + for (int i = 0, pos = 0; i < 32; ++i) { + pos = map[i] - 1; + output >>>= 1; + if (pos >= 0) output |= (0x01 & (input >>> (pos % 32))) << 31; + } + return output; + } + + /** 循环左移 + * @param input int: 循环左移的 32 位字 + * @param shift int: 循环左移位数 + * @param bit int: 循环左移有效位数 + * @return int: 循环左移结果 + */ + private static int ROL(int input, int shift, int bit) + { + return (((input << shift) | (input >> (bit - shift))) & (0xFFFFFFFF >>> (32 - bit))); + } + + /** 轮函数 + * @param input int: 输入 + * @param subkey int[]: 子密钥 + * @return int: 输出 + */ + private static int round(int input, int[] subkey) + { + int[] temp = new int[2]; + int output = 0; + + /* 扩充置换 */ + DES.permute(new int[] { input }, DES.E, temp); + + /* 与密钥异或 */ + temp[0] ^= subkey[0]; + temp[1] ^= subkey[1]; + + /* S 盒混淆 */ + for (int i = 0; i < DES.SP_BOX.length; ++i) { + int t = (temp[i / 4] >>> ((i % 4) * 6)) & 0b00111111; + int row = (t & 0b01) | ((t & 0b00100000) >>> 4); // S 盒行号 + int col = (t & 0b00011110) >> 1; // S 盒列号 + output ^= DES.SP_BOX[i][row][col] << (i * 4); + } + + /* 置换 */ + return DES.permute(output, DES.P); + } + + /** + * 设置密钥 + * @param key byte[]: 密钥字节串(16/24/32 Byte, 128/192/256 bit) + */ + public void setKey(byte[] key) + { + this.setKey(key, true); + } + + /** + * 设置密钥 + * @param key byte[]: 密钥字节串(8 Byte, 64 bit) + * @param bigEndian boolean: 是否为大字节序 + */ + public void setKey(byte[] key, boolean bigEndian) + { + assert (key.length == 8) : "密钥长度异常!"; + + // int[] key_ints = Utils.byteToInt32(key, bigEndian); + int[] key_ints = new int[key.length / 4]; + if (bigEndian) b2iBig(key, 0, key_ints, 0, key.length); + else b2iLittle(key, 0, key_ints, 0, key.length); + + this.setKey(key_ints); + } + + /** + * 设置密钥 + * @param key int[]: 密钥字串(2 word32, 64 bit) + */ + public void setKey(int[] key) + { + assert (key.length == 2) : "密钥长度异常!"; + + this.key = key; + this.subkeysGenerate(); + } + + /** DES 加密 + * @param plaintext int[]: 2 * 32位数据(32位int*2, 64位) + * @return int[]: 2 * 32位数据(32位int*2, 64位) + */ + public int[] encrypt(int[] plaintext) + { + int[] temp = new int[2]; + DES.permute(plaintext, DES.IP, temp); // 初始置换 + + for (int i = 0; i < 16; ++i) { // 16 轮轮函数 + temp[i & 1] ^= DES.round(temp[i & 1 ^ 1], this.subkeys[i]); + } + /* 交换左右两个字 */ + temp[0] ^= temp[1]; + temp[1] ^= temp[0]; + temp[0] ^= temp[1]; + return DES.permute(temp, DES.FP, new int[2]); // 初始逆置换 + } + + /** DES 解密 + * @param ciphertext int[]: 2 * 32位数据(32位int*2, 64位) + * @return int[]: 2 * 32位数据(32位int*2, 64位) + */ + public int[] decrypt(int[] ciphertext) + { + int[] temp = new int[2]; + DES.permute(ciphertext, DES.IP, temp); // 初始置换 + for (int i = 0; i < 16; ++i) { // 16 轮轮函数 + temp[i & 1] ^= DES.round(temp[i & 1 ^ 1], this.subkeys[15 - i]); + } + /* 交换左右两个字 */ + temp[0] ^= temp[1]; + temp[1] ^= temp[0]; + temp[0] ^= temp[1]; + return DES.permute(temp, DES.FP, new int[2]); // 初始逆置换 + } + + /** 子密钥生成 + * + */ + private void subkeysGenerate() + { + int[] key_valid = new int[2]; + /* 置换1 */ + DES.permute(this.key, DES.PC1, key_valid); // 经过置换的有效密钥(2字, 每个字 28 位, 共 56 位) + for (int i = 0; i < DES.ROL_SHIFT.length; ++i) { // 生成 16 个子密钥 + /* 循环左移 */ + key_valid[0] = DES.ROL(key_valid[0], DES.ROL_SHIFT[i], 28); + key_valid[1] = DES.ROL(key_valid[1], DES.ROL_SHIFT[i], 28); + /* 置换2 */ + DES.permute(key_valid, DES.PC2, this.subkeys[i]); + // Utils.print(this.subkeys[i]); + } + } + + public static void main(String[] args) + { + DES des = new DES(); + /* 弱密钥 */ + /* 奇校验 */ + // int[] key = { 0x01010101, 0x01010101 }; + // int[] key = { 0x1F1F1F1F, 0x0E0E0E0E }; + // int[] key = { 0xE0E0E0E0, 0xF1F1F1F1 }; + // int[] key = { 0xFEFEFEFE, 0xFEFEFEFE }; + + /* 偶校验 */ + // int[] key = { 0x00000000, 0x00000000 }; + // int[] key = { 0x1E1E1E1E, 0x0F0F0F0F }; + // int[] key = { 0xE1E1E1E1, 0xF0F0F0F0 }; + // int[] key = { 0xFFFFFFFF, 0xFFFFFFFF }; + + /* 其他密钥 */ + int[] key = { 0x0F1571C9, 0x47D9E859 }; + + /* 设置密钥 */ + // des.setKey(key); + des.setKey(Utils.endiannessConvert(key)); + + /* 明文 */ + // int[] plaintext1 = { 0, 0 }; + // int[] plaintext1 = { 0, 0x01 }; + // int[] plaintext1 = { 0x01234567, 0x89ABCDEF }; + int[] plaintext1 = { 0x02468ACE, 0xECA86420 }; + + /* 置换测试 */ + // Utils.print(plaintext1); + // Utils.print(DES.permute(plaintext1, DES.IP, new int[2])); + // Utils.print(DES.permute(DES.permute(plaintext1, DES.IP, new int[2]), DES.FP, new int[2])); + + /* 加解密测试 */ + int[] ciphertext = des.encrypt(plaintext1); + int[] plaintext2 = des.decrypt(ciphertext); + + Utils.print(plaintext1); + Utils.print(ciphertext); + Utils.print(plaintext2); + + /* 弱密钥测试 */ + // int[] plaintext3 = des.encrypt(ciphertext); + // Utils.print(plaintext3); + + } +} diff --git a/assignment/src/assignment/cryptography/hash/DigestBase.java b/assignment/src/assignment/cryptography/hash/DigestBase.java new file mode 100644 index 0000000..721802e --- /dev/null +++ b/assignment/src/assignment/cryptography/hash/DigestBase.java @@ -0,0 +1,262 @@ +// TODO 分析 +/* + * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package assignment.cryptography.hash; + +import java.security.MessageDigestSpi; +import java.security.DigestException; +import java.security.ProviderException; + +/** + * Common base message digest implementation for the Sun provider. + * It implements all the JCA methods as suitable for a Java message digest + * implementation of an algorithm based on a compression function (as all + * commonly used algorithms are). The individual digest subclasses only need to + * implement the following methods: + * + * . abstract void implCompress(byte[] b, int ofs); + * . abstract void implDigest(byte[] out, int ofs); + * . abstract void implReset(); + * + * See the inline documentation for details. + * + * @since 1.5 + * @author Andreas Sterbenz + */ +public abstract class DigestBase extends MessageDigestSpi implements Cloneable { + + // one element byte array, temporary storage for update(byte) + private byte[] oneByte; + + // algorithm name to use in the exception message + private final String algorithm; + // length of the message digest in bytes + private final int digestLength; + + // size of the input to the compression function in bytes + private final int blockSize; + // buffer to store partial blocks, blockSize bytes large + // Subclasses should not access this array directly except possibly in their + // implDigest() method. See MD5.java as an example. + byte[] buffer; + // offset into buffer + private int bufOfs; + + // number of bytes processed so far. subclasses should not modify + // this value. + // also used as a flag to indicate reset status + // -1: need to call engineReset() before next call to update() + // 0: is already reset + long bytesProcessed; + + /** + * Main constructor. + */ + DigestBase(String algorithm, int digestLength, int blockSize) + { + super(); + this.algorithm = algorithm; + this.digestLength = digestLength; + this.blockSize = blockSize; + buffer = new byte[blockSize]; + } + + /* 返回摘要长度(单位: Byte) */ + public int getDigestLength() + { + return this.digestLength; + } + + /* 返回分组长度(单位: Byte) */ + public int getBlockSize() + { + return this.blockSize; + } + + /** + * 更新一组字节序列 + * @param {@code byte[]} b: 字节序列 + * @return {@code int}: 字节序列长度 + */ + public int update(byte[] b) + { + engineUpdate(b, 0, b.length); + return b.length; + } + + /** + * 更新一组字节序列的切片 + * @param {@code byte[]} b: 字节序列 + * @param {@code int} ofs: 起始位置 + * @param {@code int} len: 长度 + * @return {@code int}: 字节序列切片长度 + */ + public int update(byte[] b, int ofs, int len) + { + engineUpdate(b, ofs, len); + return len; + } + + /** + * 生成签名 + * @return {@code byte[]}: 签名 + */ + public byte[] digest() + { + byte[] out = new byte[engineGetDigestLength()]; + implDigest(out, 0); + engineReset(); + return out; + } + + // return digest length. See JCA doc. + protected final int engineGetDigestLength() { + return digestLength; + } + + // single byte update. See JCA doc. + protected final void engineUpdate(byte b) { + if (oneByte == null) { + oneByte = new byte[1]; + } + oneByte[0] = b; + engineUpdate(oneByte, 0, 1); + } + + // array update. See JCA doc. + protected final void engineUpdate(byte[] b, int ofs, int len) { + if (len == 0) { + return; + } + if ((ofs < 0) || (len < 0) || (ofs > b.length - len)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (bytesProcessed < 0) { + engineReset(); + } + bytesProcessed += len; + // if buffer is not empty, we need to fill it before proceeding + if (bufOfs != 0) { + int n = Math.min(len, blockSize - bufOfs); + System.arraycopy(b, ofs, buffer, bufOfs, n); + bufOfs += n; + ofs += n; + len -= n; + if (bufOfs >= blockSize) { + // compress completed block now + implCompress(buffer, 0); + bufOfs = 0; + } + } + // compress complete blocks + while (len >= blockSize) { + implCompress(b, ofs); + len -= blockSize; + ofs += blockSize; + } + // copy remainder to buffer + if (len > 0) { + System.arraycopy(b, ofs, buffer, 0, len); + bufOfs = len; + } + } + + // reset this object. See JCA doc. + protected final void engineReset() { + if (bytesProcessed == 0) { + // already reset, ignore + return; + } + implReset(); + bufOfs = 0; + bytesProcessed = 0; + } + + // return the digest. See JCA doc. + protected final byte[] engineDigest() { + byte[] b = new byte[digestLength]; + try { + engineDigest(b, 0, b.length); + } catch (DigestException e) { + throw (ProviderException) + new ProviderException("Internal error").initCause(e); + } + return b; + } + + // return the digest in the specified array. See JCA doc. + protected final int engineDigest(byte[] out, int ofs, int len) + throws DigestException { + if (len < digestLength) { + throw new DigestException("Length must be at least " + + digestLength + " for " + algorithm + "digests"); + } + if ((ofs < 0) || (len < 0) || (ofs > out.length - len)) { + throw new DigestException("Buffer too short to store digest"); + } + if (bytesProcessed < 0) { + engineReset(); + } + implDigest(out, ofs); + bytesProcessed = -1; + return digestLength; + } + + /** + * Core compression function. Processes blockSize bytes at a time + * and updates the state of this object. + */ + abstract void implCompress(byte[] b, int ofs); + + /** + * Return the digest. Subclasses do not need to reset() themselves, + * DigestBase calls implReset() when necessary. + */ + abstract void implDigest(byte[] out, int ofs); + + /** + * Reset subclass specific state to their initial values. DigestBase + * calls this method when necessary. + */ + abstract void implReset(); + + public Object clone() throws CloneNotSupportedException { + DigestBase copy = (DigestBase) super.clone(); + copy.buffer = copy.buffer.clone(); + return copy; + } + + // padding used for the MD5, and SHA-* message digests + static final byte[] padding; + + static { + // we need 128 byte padding for SHA-384/512 + // and an additional 8 bytes for the high 8 bytes of the 16 + // byte bit counter in SHA-384/512 + padding = new byte[136]; + padding[0] = (byte)0x80; + } +} diff --git a/assignment/src/assignment/cryptography/hash/MD5.java b/assignment/src/assignment/cryptography/hash/MD5.java new file mode 100644 index 0000000..10d4174 --- /dev/null +++ b/assignment/src/assignment/cryptography/hash/MD5.java @@ -0,0 +1,235 @@ +/** + * REF + */ +package assignment.cryptography.hash; + +import assignment.cryptography.Utils; +import static assignment.cryptography.ByteArrayAccess.*; + +public final class MD5 extends DigestBase +{ + + // 状态 + private int[] state; + // 用于 implCompress() 的临时缓存 + private int[] x; + + private static final int[][] S; // 轮常数 + private static final int[] T; // 表 + + static { + S = new int[][] { // + { 7, 12, 17, 22, }, // + { 5, 9, 14, 20, }, // + { 4, 11, 16, 23, }, // + { 6, 10, 15, 21, }, // + }; + T = new int[] { // + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, // + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, // + + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, // + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, // + + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, // + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, // + + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, // + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, // + }; + } + + public MD5() + { + super( // + "MD5", // 算法名 + 16, // 摘要长度(16 字节, 128 位) + 64 // 分组长度(64 字节, 512 位) + ); + state = new int[4]; // 4 个 32 位寄存器 + x = new int[16]; // 16 个 8 位寄存器 + implReset(); // 初始化缓冲区 + } + + // clone this object + public Object clone() throws CloneNotSupportedException + { + MD5 copy = (MD5) super.clone(); + copy.state = copy.state.clone(); + copy.x = new int[16]; + return copy; + } + + /** + * 初始化缓冲区(128 bits = 16 Bytes = 4 * 32 word) + */ + void implReset() + { + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + } + + /** + * 签名 + * 1. 将缓冲区数据填充到待计算摘要数据的末尾 + * 2. 将消息长度添加到待计算摘要数据的末尾 + * 3. 计算摘要 + */ + public void implDigest(byte[] out, int ofs) + { + /** + * 需要将消息填充至 448 mod 512 bits (56 mod 64 Bytes) + * bytesProcessed 数据字节数 + * bitsProcessed 数据位数 + */ + long bitsProcessed = bytesProcessed << 3; + /** + * 最后一个分组的长度 + * index = bytesProcessed % 64 = bitsProcessed % 512 + */ + int index = (int) bytesProcessed & 0x3f; + + /** + * 56 Byte = 448 bit + */ + int padLen = (index < 56) // 最后一个分组 < 448 bits (56 Bytes) + ? (56 - index) // 将最后一个分组填充长度至 448 bits (56 Bytes) + : (120 - index); // 将最后一个分组填充长度至 512 bits (64 Bytes) 后再填充 448 bits (56 Bytes) (64 + 56 = 120) + /** + * padding 大字节序 byte 数组, padding[0] = 0b_1000_0000 + */ + engineUpdate(padding, 0, padLen); // 填充分组 (100...000) + + /** + * 将消息长度(单位: 位)低 64 位添加到待计算摘要数据的末尾 + * 生成最后一个 512 位的分组 + */ + i2bLittle4((int) bitsProcessed, buffer, 56); // 低 32 位(4 字节)附加到消息末尾(小字节序) + i2bLittle4((int) (bitsProcessed >>> 32), buffer, 60); // 高 32 位(4字节)附加到消息末尾(小字节序) + implCompress(buffer, 0); // 处理最后一个 512 位的分组 + + i2bLittle(state, 0, out, ofs, 16); // 将 4 个 32 位寄存器转换为 16 字节的摘要数据(小字节序) + } + + /* 逻辑函数接口 */ + @FunctionalInterface + private static interface Func + { + int call(int b, int c, int d); + } + + /* 轮函数 */ + private static int R(int a, int b, int c, int d, int x, int s, int ac, Func g) + { + a += g.call(b, c, d) + x + ac; + return ((a << s) | (a >>> (32 - s))) + b; + } + + /* 4 个基本逻辑函数 */ + static Func F = (b, c, d) -> (b & c) | ((~b) & d); + static Func G = (b, c, d) -> (b & d) | (c & (~d)); + static Func H = (b, c, d) -> (b ^ c) ^ d; + static Func I = (b, c, d) -> c ^ (b | (~d)); + + /** + * 处理指定偏移量开始的 16 字节 (128 位)分组 + */ + void implCompress(byte[] buf, int ofs) + { + b2iLittle64(buf, ofs, x); // byte[64] 转换为 int[16] 小字节序 + + /* 缓冲区的 4 个 32 位字 */ + int a = state[0]; + int b = state[1]; + int c = state[2]; + int d = state[3]; + + // a←b+((a+g(b,c,d)+X[k]+T[i]<<Service Provider Interface (SPI) + * for the {@code MessageDigest} class, which provides the functionality + * of a message digest algorithm, such as MD5 or SHA. Message digests are + * secure one-way hash functions that take arbitrary-sized data and output a + * fixed-length hash value. + * + *

All the abstract methods in this class must be implemented by a + * cryptographic service provider who wishes to supply the implementation + * of a particular message digest algorithm. + * + *

Implementations are free to implement the Cloneable interface. + * + * @author Benjamin Renaud + * + * + * @see MessageDigest + */ + +public abstract class MessageDigestSpi { + + // for re-use in engineUpdate(ByteBuffer input) + private byte[] tempArray; + + /** + * Returns the digest length in bytes. + * + *

This concrete method has been added to this previously-defined + * abstract class. (For backwards compatibility, it cannot be abstract.) + * + *

The default behavior is to return 0. + * + *

This method may be overridden by a provider to return the digest + * length. + * + * @return the digest length in bytes. + * + * @since 1.2 + */ + protected int engineGetDigestLength() { + return 0; + } + + /** + * Updates the digest using the specified byte. + * + * @param input the byte to use for the update. + */ + protected abstract void engineUpdate(byte input); + + /** + * Updates the digest using the specified array of bytes, + * starting at the specified offset. + * + * @param input the array of bytes to use for the update. + * + * @param offset the offset to start from in the array of bytes. + * + * @param len the number of bytes to use, starting at + * {@code offset}. + */ + protected abstract void engineUpdate(byte[] input, int offset, int len); + + /** + * Update the digest using the specified ByteBuffer. The digest is + * updated using the {@code input.remaining()} bytes starting + * at {@code input.position()}. + * Upon return, the buffer's position will be equal to its limit; + * its limit will not have changed. + * + * @param input the ByteBuffer + * @since 1.5 + */ + protected void engineUpdate(ByteBuffer input) { + if (input.hasRemaining() == false) { + return; + } + if (input.hasArray()) { + byte[] b = input.array(); + int ofs = input.arrayOffset(); + int pos = input.position(); + int lim = input.limit(); + engineUpdate(b, ofs + pos, lim - pos); + input.position(lim); + } else { + int len = input.remaining(); + int n = JCAUtil.getTempArraySize(len); + if ((tempArray == null) || (n > tempArray.length)) { + tempArray = new byte[n]; + } + while (len > 0) { + int chunk = Math.min(len, tempArray.length); + input.get(tempArray, 0, chunk); + engineUpdate(tempArray, 0, chunk); + len -= chunk; + } + } + } + + /** + * Completes the hash computation by performing final + * operations such as padding. Once {@code engineDigest} has + * been called, the engine should be reset (see + * {@link #engineReset() engineReset}). + * Resetting is the responsibility of the + * engine implementor. + * + * @return the array of bytes for the resulting hash value. + */ + protected abstract byte[] engineDigest(); + + /** + * Completes the hash computation by performing final + * operations such as padding. Once {@code engineDigest} has + * been called, the engine should be reset (see + * {@link #engineReset() engineReset}). + * Resetting is the responsibility of the + * engine implementor. + * + * This method should be abstract, but we leave it concrete for + * binary compatibility. Knowledgeable providers should override this + * method. + * + * @param buf the output buffer in which to store the digest + * + * @param offset offset to start from in the output buffer + * + * @param len number of bytes within buf allotted for the digest. + * Both this default implementation and the SUN provider do not + * return partial digests. The presence of this parameter is solely + * for consistency in our API's. If the value of this parameter is less + * than the actual digest length, the method will throw a DigestException. + * This parameter is ignored if its value is greater than or equal to + * the actual digest length. + * + * @return the length of the digest stored in the output buffer. + * + * @exception DigestException if an error occurs. + * + * @since 1.2 + */ + protected int engineDigest(byte[] buf, int offset, int len) + throws DigestException { + + byte[] digest = engineDigest(); + if (len < digest.length) + throw new DigestException("partial digests not returned"); + if (buf.length - offset < digest.length) + throw new DigestException("insufficient space in the output " + + "buffer to store the digest"); + System.arraycopy(digest, 0, buf, offset, digest.length); + return digest.length; + } + + /** + * Resets the digest for further use. + */ + protected abstract void engineReset(); + + /** + * Returns a clone if the implementation is cloneable. + * + * @return a clone if the implementation is cloneable. + * + * @exception CloneNotSupportedException if this is called on an + * implementation that does not support {@code Cloneable}. + */ + public Object clone() throws CloneNotSupportedException { + if (this instanceof Cloneable) { + return super.clone(); + } else { + throw new CloneNotSupportedException(); + } + } +} diff --git a/assignment/src/assignment/cryptography/hash/SHA.java b/assignment/src/assignment/cryptography/hash/SHA.java new file mode 100644 index 0000000..36fc693 --- /dev/null +++ b/assignment/src/assignment/cryptography/hash/SHA.java @@ -0,0 +1,181 @@ +package assignment.cryptography.hash; + +import static assignment.cryptography.ByteArrayAccess.*; + +import assignment.cryptography.Utils; + +public final class SHA extends DigestBase +{ + + // Buffer of int's and count of characters accumulated + // 64 bytes are included in each hash block so the low order + // bits of count are used to know how to pack the bytes into ints + // and to know when to compute the block and start the next one. + private int[] W; // 存放 80 步计算中每一步使用的参数 + + private int[] state; // 存放摘要 + + /** + * Creates a new SHA object. + */ + public SHA() + { + super( // + "SHA-1", // 算法名 + 20, // 摘要长度(20 字节, 128 位) + 64 // 分组长度(64 字节, 512 位) + ); + state = new int[5]; // 5 个 32 位寄存器 + W = new int[80]; // 由初始的 16 个 32 位字派生的每一步使用的字 + implReset(); + } + + /* + * Clones this object. + */ + public Object clone() throws CloneNotSupportedException + { + SHA copy = (SHA) super.clone(); + copy.state = copy.state.clone(); + copy.W = new int[80]; + return copy; + } + + /** + * 初始化缓冲区(160 bits = 20 Bytes = 5 * 32 word) + */ + void implReset() + { + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + state[4] = 0xc3d2e1f0; + } + + /** + * 签名 + * 1. 将缓冲区数据填充到待计算摘要数据的末尾 + * 2. 将消息长度添加到待计算摘要数据的末尾 + * 3. 计算摘要 + */ + void implDigest(byte[] out, int ofs) + { + /** + * 需要将消息填充至 448 mod 512 bits (56 mod 64 Bytes) + * bytesProcessed 数据字节数 + * bitsProcessed 数据位数 = bytesProcessed * 8 + */ + long bitsProcessed = bytesProcessed << 3; + + /** + * 最后一个分组的长度 + * index = bytesProcessed % 64 = bitsProcessed % 512 + */ + int index = (int) bytesProcessed & 0x3f; + + /** + * 56 Byte = 448 bit + */ + int padLen = (index < 56) // 最后一个分组 < 448 bits (56 Bytes) + ? (56 - index) // 将最后一个分组填充长度至 448 bits (56 Bytes) + : (120 - index); // 将最后一个分组填充长度至 512 bits (64 Bytes) 后再填充 448 bits (56 Bytes) (64 + 56 = 120) + + /** + * 更新填充位 + * padding 大字节序 byte 数组, padding[0] = 0b_1000_0000 + */ + engineUpdate(padding, 0, padLen); + + /** + * 附加长度(单位: bit, 使用一个 64 bit (8 Byte) 字表示) + * 大字节序, 附加到最后一个分组 57~64 Byte (449~512 bit) + */ + i2bBig4((int) (bitsProcessed >>> 32), buffer, 56); // 高 32 位(4 字节)附加到消息末尾(大字节序) + i2bBig4((int) bitsProcessed, buffer, 60); // 低 32 位(4字节)附加到消息末尾(大字节序) + implCompress(buffer, 0); // 处理最后一个 512 位的分组 + + i2bBig(state, 0, out, ofs, 20); // 将 5 个 32 位寄存器转换为 20 字节的摘要数据(大字节序) + } + + // 轮常数 + private final static int round1_kt = 0x5a827999; + private final static int round2_kt = 0x6ed9eba1; + private final static int round3_kt = 0x8f1bbcdc; + private final static int round4_kt = 0xca62c1d6; + + /** + * 计算当前块(64 Byte, 512 bit)的哈希值 + */ + void implCompress(byte[] buf, int ofs) + { + b2iBig64(buf, ofs, W); // 将 64 Byte 转换为 16 * 32 word (大字节序) + + // 由前 16 个 32 位字派生剩余 64 个 32 位字 + for (int t = 16; t <= 79; ++t) { + int temp = W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]; + W[t] = (temp << 1) | (temp >>> 31); // 循环左移 1 bit + } + + int a = state[0]; + int b = state[1]; + int c = state[2]; + int d = state[3]; + int e = state[4]; + + // Round 1 + for (int i = 0; i < 20; ++i) { + int temp = ((a << 5) | (a >>> (32 - 5))) // 循环左移 5 bit + + ((b & c) | ((~b) & d)) // + + e + W[i] + round1_kt; + e = d; + d = c; + c = ((b << 30) | (b >>> (32 - 30))); // 循环左移 32 bit + b = a; + a = temp; + } + + // Round 2 + for (int i = 20; i < 40; ++i) { + int temp = ((a << 5) | (a >>> (32 - 5))) + (b ^ c ^ d) + e + W[i] + round2_kt; + e = d; + d = c; + c = ((b << 30) | (b >>> (32 - 30))); + b = a; + a = temp; + } + + // Round 3 + for (int i = 40; i < 60; ++i) { + int temp = ((a << 5) | (a >>> (32 - 5))) + ((b & c) | (b & d) | (c & d)) + e + W[i] + round3_kt; + e = d; + d = c; + c = ((b << 30) | (b >>> (32 - 30))); + b = a; + a = temp; + } + + // Round 4 + for (int i = 60; i < 80; ++i) { + int temp = ((a << 5) | (a >>> (32 - 5))) + (b ^ c ^ d) + e + W[i] + round4_kt; + e = d; + d = c; + c = ((b << 30) | (b >>> (32 - 30))); + b = a; + a = temp; + } + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + } + + public static void main(String[] args) + { + SHA sha = new SHA(); + String plaintext = "Hello World!"; + sha.update(plaintext.getBytes()); + Utils.print(sha.digest()); // 2EF7BDE608CE5404E97D5F042F95F89F1C232871 + } +} diff --git a/assignment/src/assignment/cryptography/rsa/RSABaseInterface.java b/assignment/src/assignment/cryptography/rsa/RSABaseInterface.java new file mode 100644 index 0000000..8230592 --- /dev/null +++ b/assignment/src/assignment/cryptography/rsa/RSABaseInterface.java @@ -0,0 +1,14 @@ +package assignment.cryptography.rsa; + +/** + * Write a description of interface RSABaseInterface here. + * 基本接口 + * REF https://github.com/rhgrant10/RSA/blob/master/RSA/src/com/rhg/rsa/RSABaseInterface.java + */ + +public interface RSABaseInterface +{ + byte PUBLIC_KEY = 1; + byte PRIVATE_KEY = 2; + byte COMPLETE_KEY = 3; +} diff --git a/assignment/src/assignment/cryptography/rsa/RSACompleteKey.java b/assignment/src/assignment/cryptography/rsa/RSACompleteKey.java new file mode 100644 index 0000000..cdb3f38 --- /dev/null +++ b/assignment/src/assignment/cryptography/rsa/RSACompleteKey.java @@ -0,0 +1,359 @@ +package assignment.cryptography.rsa; + +import java.math.BigInteger; + +import assignment.cryptography.Utils; + +/** + * Class representing full private and public RSA keys. + * 包含完整私钥与公钥 + * REF https://github.com/rhgrant10/RSA/blob/master/RSA/src/com/rhg/rsa/RSACompleteKey.java + */ +public class RSACompleteKey extends RSAKey +{ + /** The larger prime factor of the modulus. + * 较大的大素数 p + */ + private BigInteger p; + + /** The smaller prime factor of the modulus. + * 较小的大素数 q + */ + private BigInteger q; + + /** The private exponent. + * 私钥参数 d + */ + private BigInteger d; + + /** The public exponent. + * 公钥参数 e + */ + private BigInteger e; + + /** The LCM of the primes. + * ϕ(p*q) = (p-1)(p-1), p*q 的最小公倍数 (Lowest Common Multiple, LCM) + */ + private BigInteger phi; + + /** The first CRT exponent. + * 中国剩余定理 指数1 M_1^{-1} + */ + private BigInteger dP; + + /** The second CRT exponent. + * 中国剩余定理 指数2 M_2^{-1} + */ + private BigInteger dQ; + + /** The CRT coefficient. + * 中国剩余定理 系数 + */ + private BigInteger qInv; + + /** Default constructor. + * 默认构造函数 + */ + public RSACompleteKey() + { + super(null); + setPubExp(null); + setPrimes(null, null); + setCRTExpOne(null); + setCRTExpTwo(null); + setCRTCoeff(null); + return; + } + + /** Main constructor. + * 主构造函数 + * @param prime1 BigInteger: + * @param prime2 BigInteger: + * @param modulus BigInteger: + * @param pubExp BigInteger: + * @param priExp BigInteger: + * @param crtExp1 BigInteger: + * @param crtExp2 BigInteger: + * @param crtCoeff BigInteger: + */ + public RSACompleteKey( // + BigInteger prime1, // 大质数 1 + BigInteger prime2, // 大质数 2 + BigInteger modulus, // + BigInteger pubExp, // + BigInteger priExp, // + BigInteger crtExp1, // + BigInteger crtExp2, // + BigInteger crtCoeff // + ) + { + super(modulus); + this.setPrimes(prime1, prime2); + this.setPriExp(priExp); + this.setPubExp(pubExp); + this.setCRTExpOne(crtExp1); + this.setCRTExpTwo(crtExp2); + this.setCRTCoeff(crtCoeff); + return; + } + + /** Sets the prime factors. + * 设置质因数(p 为较大的质因数, q 为较小的质因数) + * @param prime1 BigInteger: 第一个质因数 + * @param prime2 BigInteger: 第二个质因数 + */ + public void setPrimes(BigInteger prime1, BigInteger prime2) + { + if (isNull(prime1 = weedOut(prime1)) || isNull(prime2 = weedOut(prime2))) { + return; + } else { + if (isPositive(prime1.subtract(prime2))) { // prime1 > prime2 + this.p = prime1; + this.q = prime2; + } else if (isPositive(prime2.subtract(prime1))) { // prime2 > prime1 + this.p = prime2; + this.q = prime1; + } else { // prime1 == prime2 + return; + } + } + return; + } + + /** Sets the public exponent. + * 设置公钥参数 e + * @param pubExp BigInteger: 公钥参数 e + */ + public void setPubExp(BigInteger pubExp) + { + this.e = weedOut(pubExp); + } + + /** Sets the private exponent. + * 设置私钥参数 d + * @param priExp BigInteger: 私钥参数 d + */ + public void setPriExp(BigInteger priExp) + { + this.d = weedOut(priExp); + } + + /** Sets phi. + * 设置两个大素数的最小公倍数 + * @param phi BigInteger: 大素数的最小公倍数 + */ + public void setPhi(BigInteger phi) + { + this.phi = weedOut(phi); + } + + /** Sets the first CRT exponent. + * 设置中国剩余定理的第一个指数 + * @param crtExp1 BigInteger: CRT 指数1 + */ + public void setCRTExpOne(BigInteger crtExp1) + { + this.dP = weedOut(crtExp1); + } + + /** Sets the second CRT exponent. + * 设置中国剩余定理的第二个指数 + * @param crtExp2 BigInteger: CRT 指数2 + */ + public void setCRTExpTwo(BigInteger crtExp2) + { + this.dQ = weedOut(crtExp2); + } + + /** Sets the CRT coefficient. + * 设置中国剩余定理的系数 + * @param crtCoeff BigInteger: CRT 系数 + */ + public void setCRTCoeff(BigInteger crtCoeff) + { + this.qInv = weedOut(crtCoeff); + } + + /** Computes the Euler number of the product of two large prime numbers p, q + * 计算两个大素数 p, q 之积的欧拉数 + */ + protected void computePhi() + { + // this.phi = lcm(pMinusOne(), qMinusOne()); + this.phi = pMinusOne().multiply(qMinusOne()); // phi(p*q) = (p-1)(q-1) + } + + /** Performs the classical RSA computation. + * 执行经典的 RSA 公钥加密计算 + * @param m BigInteger: 待处理的消息 + * @return BigInteger: 处理后的消息 + */ + public BigInteger encrypt(BigInteger m) + { + // m^e mod n + return m.modPow(this.getPubExp(), this.getModulus()); + } + + /** Performs the classical RSA computation. + * 执行应用 CRT 的快速 RSA 计算 + * @param c BigInteger: 消息 + */ + public BigInteger decrypt(BigInteger c) + { + BigInteger m1, m2, dm, h; + m1 = c.modPow(getCRTExpOne(), getPrimeLarge()); // m1 = c^dP mod p + m2 = c.modPow(getCRTExpTwo(), getPrimeSmall()); // m2 = c^dQ mod q + dm = m1.subtract(m2).abs(); // dm = |m1 - m2| + h = getCRTCoeff().multiply(dm).mod(getPrimeLarge()); // h = qInv * dm mod p + return m2.add(getPrimeSmall().multiply(h)); // m2 + q * h + } + + /** Returns the larger prime factor. + * 返回较大的质因数 + * @return BigInteger: 较大的质因数 p + */ + public BigInteger getPrimeLarge() + { + return this.p; + } + + /** Returns the smaller prime factor. + * 返回较小的质因数 + * @return BigInteger: 较小的质因数 q + */ + public BigInteger getPrimeSmall() + { + return this.q; + } + + /** Returns the public exponent. + * 返回公钥参数 e + * @return BigInteger: 公钥参数 e + */ + public BigInteger getPubExp() + { + return this.e; + } + + /** Returns the private exponent. + * 返回私钥参数 d + * @return BigInteger: 私钥参数 d + */ + public BigInteger getPriExp() + { + return this.d; + } + + /** Returns phi. + * 返回两个质因数的最小公倍数 + * @return BigInteger: 质因数的最小公倍数 phi + */ + public BigInteger getPhi() + { + return this.phi; + } + + /** Returns the first CRT exponent. + * 返回中国剩余定理的第一个指数 + * @return BigInteger: CRT 指数1 + */ + public BigInteger getCRTExpOne() + { + return this.dP; + } + + /** Returns the second CRT exponent. + * 返回中国剩余定理的第二个指数 + * @return BigInteger: CRT 指数2 + */ + public BigInteger getCRTExpTwo() + { + return this.dQ; + } + + /** Returns the CRT coefficient. + * 返回中国剩余定理的系数 + * @return BigInteger: CRT 系数 + */ + public BigInteger getCRTCoeff() + { + return this.qInv; + } + + /** Returns true when key is valid. + * 测试密钥是否有效 + * @return boolean: {@code true}: 密钥有效; {@code false}: 密钥无效 + */ + public boolean isValid() + { + if (noValuesNull() // + && isPrime(p) // + && isPrime(q) // + ) { + computePhi(); // φ(n) = (p-1)(q-1) + return p.multiply(q).equals(getModulus()) // n = p * q + && e.compareTo(INT_3) >= 0 // e >= 3 + && e.compareTo(getModulus()) < 0 // e < n + && e.gcd(phi).equals(INT_1) // gcd(e, φ(n)) = 1 + && getPriExp().compareTo(getModulus()) < 0 // d < n + && getPriExp().equals(e.modInverse(phi)) // d = e^{-1} mod φ(n) + && dP.compareTo(p) < 0 // dP < p + && dQ.compareTo(q) < 0 // dQ < q + && dP.equals(e.modInverse(pMinusOne())) // dP = e^{-1} mod (p - 1) + && dQ.equals(e.modInverse(qMinusOne())) // dQ = e^{-1} mod (q - 1) + && qInv.compareTo(p) < 0 // qInv < p + && qInv.equals(q.modInverse(p)) // qInv = q^{-1} mod p + && decrypt(encrypt(INT_65537)).equals(INT_65537); + } else { + return false; + } + } + + /** Returns true when no fields are null. + * 判断所有参数是否都已设置 + * @return boolean: {@code true}: 所有参数均设置; {@code false}: 存在未设置的参数 + */ + private boolean noValuesNull() + { + return !(isNull(this.p) // + || isNull(this.q) // + || isNull(this.e) // + || isNull(this.d) // + || isNull(this.getModulus()) // + || isNull(this.dP) // + || isNull(this.dQ) // + || isNull(this.qInv) // + ); + } + + /** Returns p minus one. + * 返回 p-1 + * @return BigInteger: 返回 p-1 + */ + protected BigInteger pMinusOne() + { + if (isNull(this.p)) { + return null; + } else { + return this.p.subtract(INT_1); + } + } + + /** Returns q minus one. + * 返回 q-1 + * @return BigInteger: 返回 q-1 + */ + protected BigInteger qMinusOne() + { + if (isNull(this.q)) { + return null; + } else { + return this.q.subtract(INT_1); + } + } + + public boolean use(String source, String destination) + { + return false; + } +} diff --git a/assignment/src/assignment/cryptography/rsa/RSAConstants.java b/assignment/src/assignment/cryptography/rsa/RSAConstants.java new file mode 100644 index 0000000..a34e4c9 --- /dev/null +++ b/assignment/src/assignment/cryptography/rsa/RSAConstants.java @@ -0,0 +1,21 @@ +package assignment.cryptography.rsa; + +import java.math.BigInteger; + +/** + * Interface providing often used BigInteger constants. + * 大整数相关常量 + * REF https://github.com/rhgrant10/RSA/blob/master/RSA/src/com/rhg/rsa/RSAConstants.java + */ + +public interface RSAConstants +{ + static final BigInteger INT_0 = BigInteger.ZERO; + static final BigInteger INT_1 = BigInteger.ONE; + static final BigInteger INT_2 = BigInteger.valueOf(2); + static final BigInteger INT_3 = BigInteger.valueOf(3); + static final BigInteger INT_256 = BigInteger.valueOf(256); + static final BigInteger INT_65537 = BigInteger.valueOf(65537); + + static final int CERTAINTY = 128; // 判断一个素数的确定性(概率为 \frac{1}{2 * CERTAINTY}) +} diff --git a/assignment/src/assignment/cryptography/rsa/RSAKey.java b/assignment/src/assignment/cryptography/rsa/RSAKey.java new file mode 100644 index 0000000..6ef1bf7 --- /dev/null +++ b/assignment/src/assignment/cryptography/rsa/RSAKey.java @@ -0,0 +1,313 @@ +package assignment.cryptography.rsa; + +import java.math.BigInteger; +import java.io.*; +import java.util.Arrays; +import java.util.Random; + +/** + * Abstract class representing common features of RSA keys. + * RSA 密钥的常见功能的抽象类 + * REF https://github.com/rhgrant10/RSA/blob/master/RSA/src/com/rhg/rsa/java + */ +public abstract class RSAKey implements RSAConstants, RSABaseInterface +{ + + static final int MODULUS_MIN_LENGTH = 96; // 模数 n 的最短长度 + + private BigInteger n; // 模数 + + /** Returns true when the argument is null. + * 判断一个参数是否为空对象 + * @param obj Object: 对象 + * @return boolean: 对象是否为空的判断结果 + */ + public static final boolean isNull(Object obj) + { + // return !(obj != null); + return obj == null; + } + + /** Returns true when the argument is greater than zero. + * 判断一个大整数是否为正数 + * @param number BigInteger: 一个大整数 + * @return boolean: 是否为正数 + */ + public static final boolean isPositive(BigInteger number) + { + return (number.compareTo(INT_0) > 0); + } + + /** Returns true when the argument is prime. + * 判断一个大整数是否为素数 + * @param number BigInteger: 一个大整数 + * @return boolean: 是否为素数({@code true}: 有可能是素数; {@code false}: 不是素数) + */ + public static final boolean isPrime(BigInteger number) + { + return number.isProbablePrime(CERTAINTY); + } + + /** Weeds out bad inputs. + * 过滤无效的输入(忽视为空或者非正的整数) + * @param arg BigInteger: 输入的整数 + * @return BigInteger: 输入非空且为正整数(无效则为 null) + */ + public static BigInteger weedOut(BigInteger arg) + { + if (!isNull(arg) && isPositive(arg)) { + return arg; + } else { + return null; + } + } + + /** Converts a byte array into a BigInteger. + * 字节数组转换为大整数 + * @param X byte[]: 表示整数的字节数组(大字节序) + * @return BigInteger: 大整数 + */ + protected static BigInteger bytesToBigInt(byte[] X) + { + // BigInteger x = INT_0; + + // for (int i = 0; i < X.length; i++) { + // x = x.add(BigInteger.valueOf(X[i]).multiply(INT_256.pow(X.length - 1 - i))); + // } + + // return x; + return new BigInteger(X); + } + + /** Converts a BigInteger into a byte array of the specified length. + * 将大整数转换为指定长度的字节数组 + * @param x BigInteger: 大整数 + * @param numBytes int: 字节数组长度 + * @return {@code byte[]}: 字节数组; {@code null}: 字节数组长度不足 + */ + protected static byte[] bigIntToBytes(BigInteger x, int numBytes) + { + if (x.compareTo(INT_256.pow(numBytes)) >= 0) { + return null; // number is to big to fit in the byte array + } + + // byte[] ba = new byte[numBytes--]; + // BigInteger[] divAndRem = new BigInteger[2]; + + // for (int power = numBytes; power >= 0; power--) { + // divAndRem = x.divideAndRemainder(INT_256.pow(power)); + // ba[numBytes - power] = (byte) divAndRem[0].intValue(); + // x = divAndRem[1]; + // } + + // return ba; + + byte[] input = x.toByteArray(); + byte[] output = new byte[numBytes]; + System.arraycopy(input, 0, output, numBytes - input.length, input.length); + return output; + } + + /** Returns the contents of a file as a byte array. + * 返回字节数组表示的指定文件 + * @param pathname String: 文件路径 + * @return byte[]: 文件字节数组 + */ + protected static byte[] fileToBytes(String pathname) + { + File fIn = new File(pathname); // 打开文件 + if (!fIn.canRead()) { // 文件不可读 + System.err.println("Can't read " + pathname); + return null; + } + + FileInputStream in = null; // 文件输入流 + byte[] bytes = null; // 字节数组 + try { + in = new FileInputStream(fIn); // 文件转换为文件输入流 + + long fileSize = fIn.length(); // 文件长度 + if (fileSize > Integer.MAX_VALUE) { // 文件过大 + System.out.println("Sorry, file was too large!"); + } + + bytes = new byte[(int) fileSize]; // 要写入的字节数组 + + int offset = 0; // 当前指针偏移量 + int numRead = 0; // 本次读取的字节数 + while (offset < bytes.length && (numRead = in.read(bytes, offset, bytes.length - offset)) >= 0) { + offset += numRead; + } + } catch (IOException e) { + } finally { + try { + if (in != null) in.close(); // 关闭文件 + } catch (IOException e) { + } + } + + return bytes; + } + + /** Computes the least common multiple. + * 计算最小公倍数 + * @param a BigInteger: 正整数 + * @param b BigInteger: 正整数 + * @return BigInteger: a 与 b 的最小公倍数 + */ + public BigInteger lcm(BigInteger a, BigInteger b) + { + // a * b / (a 与 b 的最大公约数) + return (a.multiply(b).divide(a.gcd(b))); + } + + /** Generates an array of pseudo-random nonzero bytes. + * 生成伪随机非零字节的数组 + * @param len int: 字节数组的长度 + * @return byte[]: 生成的字节数组(用于字节填充) + */ + protected byte[] makePaddingString(int len) + { + return this.makePaddingString(len, 0); + } + + /** Generates an array of pseudo-random nonzero bytes. + * 生成伪随机非零字节的数组(自定义随机种子) + * @param len int: 字节数组的长度 + * @param seed long: 随机种子 + * @return byte[]: 生成的字节数组(用于字节填充) + */ + protected byte[] makePaddingString(int len, long seed) + { + if (len < 0) return null; + Random random = seed != 0 // + ? new Random(seed) // + : new Random(); // 伪随机数发生器 + + byte[] PS = new byte[len]; + + for (int i = 0; i < len; i++) { + PS[i] = (byte) (random.nextInt(255) + 1); + } + // PS[0] = (byte) (random.nextInt(255) + 1); // 高 8 位不全为 0 + // for (int i = 1; i < len; i++) { + // PS[i] = (byte) (random.nextInt(256)); // 其他位可以全为 0 + // } + + return PS; + } + + /** Returns a portion of the array argument. + * 返回一个字节数组的切片 + * @param inBytes byte[]: 原始字节数组 + * @param start int: 字节数组切片起始位置 + * @param end int: 字节数组切片末尾位置 + * @return byte[]: 字节数组的切片 + */ + protected static byte[] getSubArray(byte[] inBytes, int start, int end) + { + if (start >= inBytes.length) { // 数组起始位置过界 + return null; + } + if (end > inBytes.length) { // 数组结束位置过界 + end = inBytes.length; // 设置为数组末尾 + } + int bytesToGet = end - start; // 需要拷贝的数组切片长度 + if (bytesToGet < 1) { // 切片长度小于 0 + return null; + } + + // byte[] outBytes = new byte[bytesToGet]; + // for (int i = start; i < end; i++) { + // outBytes[i - start] = inBytes[i]; + // } + + // return outBytes; + return Arrays.copyOfRange(inBytes, start, end); + } + + /** Reshapes a byte array into an array of byte arrays. + * 将一维字节数组的形状改为二维字节数组 + * @param inBytes byte[]: 一维字节数组 + * @param colSize int: 二维字节数组的列数 + * @return byte[][]: 二维字节数组 + */ + protected static byte[][] reshape(byte[] inBytes, int colSize) + { + if (colSize < 1) { + colSize = 1; + } + + // 总行数 + int rowSize = (int) Math.ceil((double) inBytes.length / (double) colSize); + + if (rowSize == 0) { + return null; + } + + byte[][] outBytes = new byte[rowSize][]; + + for (int i = 0; i < rowSize; i++) { + outBytes[i] = getSubArray(inBytes, i * colSize, (i + 1) * colSize); + } + return outBytes; + } + + /** Default constructor. + * 默认构造方法 + */ + protected RSAKey() + { + this.setModulus(null); + return; + } + + /** Main constructor. + * 主要构造方法 + */ + protected RSAKey(BigInteger modulus) + { + this.setModulus(modulus); + return; + } + + /** Sets the modulus. + * 设置模数 + * @param modulus BigInteger: 模数 + */ + public void setModulus(BigInteger modulus) + { + n = weedOut(modulus); // 判断模数是否有效 + if (isNull(n) || n.bitLength() < MODULUS_MIN_LENGTH) { // 如果模数无效或者长度过短 + n = null; + } + return; + } + + /** Returns the modulus. + * 返回模数 n + * @return BigInteger: 模数 n + */ + public BigInteger getModulus() + { + return this.n; + } + + /** Returns the number of bytes required to store the modulus. + * 返回需要多少字节储存模数 + * @return int: 模数 n 的长度(单位: 字节, 向上取整) + */ + protected int getModulusByteSize() + { + return (int) Math.ceil(this.getModulus().bitLength() / 8.0); + } + + /** Uses the key and returns true if use was successful. + * 判断密钥是否可用 + * @param source String: 原文件路径 + * @param destination String: 目标文件路径 + * @return boolean: {@code true}: 密钥可用; {@code false}: 密钥不可用 + */ + public abstract boolean use(String source, String destination); + +} diff --git a/assignment/src/assignment/cryptography/rsa/RSAKeyGenerator.java b/assignment/src/assignment/cryptography/rsa/RSAKeyGenerator.java new file mode 100644 index 0000000..a16e212 --- /dev/null +++ b/assignment/src/assignment/cryptography/rsa/RSAKeyGenerator.java @@ -0,0 +1,201 @@ +package assignment.cryptography.rsa; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import assignment.cryptography.Utils; + +/** + * Class representing private and public keys with the ability to generate new keys. + * + * @author Rob + * @version 05/31/2010 + */ +public class RSAKeyGenerator extends RSACompleteKey +{ + /** Minimum number of bits in the modulus allowed. + * 模数 n 的最小长度(单位: bit) + */ + private static final int MIN_BIT_LENGTH = 200; + + /** Source of securely (pseudo-) random bits. + * 安全的伪随机 bit 生成器 + */ + private SecureRandom rand; + + /** The number of bits required in the modulus. + * 模数 n 的需要的长度(单位: bit) + */ + private int bitLength; + + /** + * 默认的构造方法 + */ + public RSAKeyGenerator() + { + super(); + this.setRand(); + this.setBitLength(MIN_BIT_LENGTH); + this.generateNewKeys(); + return; + } + + /** + * 主要构造方法 + * @param bitLength int: 模数 n 的长度(单位: bit) + */ + public RSAKeyGenerator(int bitLength) + { + super(); + this.setRand(); + this.setBitLength(bitLength); + this.generateNewKeys(); + return; + } + + /** + * 指定随机种子的主要构造方法 + * @param bitLength int: 模数 n 的长度(单位: bit) + * @param seed {@code byte[]}: 随机种子 + */ + public RSAKeyGenerator(int bitLength, byte[] seed) + { + super(); + this.setRand(seed); + this.setBitLength(bitLength); + this.generateNewKeys(); + return; + } + + /** + * 设置模数 n 的长度 + * @param bitLength int: 模数 n 的长度(单位: bit) + */ + public void setBitLength(int bitLength) + { + this.bitLength = (bitLength >= MIN_BIT_LENGTH ? bitLength : MIN_BIT_LENGTH); + return; + } + + /** + * 设置伪随机 bit 生成器 + */ + public void setRand() + { + this.rand = new SecureRandom(); + return; + } + + /** + * 设置伪随机 bit 生成器 + * @param seed {@code byte[]}: 随机种子 + */ + public void setRand(byte[] seed) + { + this.rand = new SecureRandom(seed); + return; + } + + /** Generates new private and public keys. + * 生成一对新的公私钥对 + */ + public void generateNewKeys() + { + do { + this.setPrimes( // + BigInteger.probablePrime(75 * bitLength / 100, rand), // 质数 p + BigInteger.probablePrime(25 * bitLength / 100, rand) // 质数 q + ); + setModulus(getPrimeLarge().multiply(getPrimeSmall())); // 模数 n = p * q + computePhi(); // 欧拉数 φ(n) = (p - 1) * (q - 1) + + /* 公钥指数 e */ + // setPubExp(BigInteger.valueOf(65537)); + BigInteger e; + for (e = BigInteger.probablePrime((bitLength / 10), rand); e.compareTo(getModulus()) < 0; e = e.nextProbablePrime()) { + if (e.gcd(getPhi()).equals(INT_1)) { + setPubExp(e); + break; + } + } + + setPriExp(getPubExp().modInverse(getPhi())); // 私钥指数 d = e^{-1} mod φ(n) + setCRTExpOne(getPubExp().modInverse(pMinusOne())); // CRT 指数 dP = e^{-1} mod (p - 1) + setCRTExpTwo(getPubExp().modInverse(qMinusOne())); // CRT 指数 dQ = e^{-1} mod (q - 1) + setCRTCoeff(getPrimeSmall().modInverse(getPrimeLarge())); // CRT 系数 q^{-1} mod p + } while (!isValid()); + } + + /** Makes a new RSA key. + * 密钥生成 + * @param whichKey byte: {@code PUBLIC_KEY}: 公钥; {@code PRIVATE_KEY}: 私钥; {@code COMPLETE_KEY}: 公私钥对; + * @return RSAKey: 密钥 + */ + public RSAKey makeKey(byte whichKey) + { + switch (whichKey) { + case PUBLIC_KEY: + return makePublicKey(); + case PRIVATE_KEY: + return makePrivateKey(); + case COMPLETE_KEY: + return makeCompleteKey(); + default: + return null; + } + } + + /** Makes public RSA key objects. + * 生成 RSA 公钥对象 + * @return RSAPublicKey: 生成的公钥对象 + */ + private RSAPublicKey makePublicKey() + { + return new RSAPublicKey(getModulus(), getPubExp()); + } + + /** Makes private RSA key objects. + * 生成 RSA 私钥对象 + * @return RSAPrivateKey: 生成的私钥对象 + */ + private RSAPrivateKey makePrivateKey() + { + return new RSAPrivateKey(getModulus(), getPriExp()); + } + + /** Makes complete RSA key objects. + * 生成 RSA 公私钥对象 + * @return RSACompleteKey: 生成的公私钥对象 + */ + private RSACompleteKey makeCompleteKey() + { + return new RSACompleteKey( // + getPrimeLarge(), // + getPrimeSmall(), // + getModulus(), // + getPubExp(), // + getPriExp(), // + getCRTExpOne(), // + getCRTExpTwo(), // + getCRTCoeff() // + ); + } + + public static void main(String[] args) + { + RSAKeyGenerator rsa_key_generator = new RSAKeyGenerator(256); + RSACompleteKey complete_key = (RSACompleteKey) (rsa_key_generator.makeKey(COMPLETE_KEY)); + RSAPublicKey public_key = (RSAPublicKey) (rsa_key_generator.makeKey(PUBLIC_KEY)); + RSAPrivateKey private_key = (RSAPrivateKey) (rsa_key_generator.makeKey(PRIVATE_KEY)); + + // Utils.print(complete_key.getModulus().toByteArray()); + // Utils.print(complete_key.getPubExp().toByteArray()); + // Utils.print(complete_key.getPriExp().toByteArray()); + // Utils.print(public_key.getPubExp().toByteArray()); + // Utils.print(private_key.getPriExp().toByteArray()); + + /* 验证能否加正确解密 */ + Utils.print(INT_65537.toByteArray()); + Utils.print(complete_key.decrypt(complete_key.encrypt(INT_65537)).toByteArray()); + } +} diff --git a/assignment/src/assignment/cryptography/rsa/RSAPrivateKey.java b/assignment/src/assignment/cryptography/rsa/RSAPrivateKey.java new file mode 100644 index 0000000..3cfa0f8 --- /dev/null +++ b/assignment/src/assignment/cryptography/rsa/RSAPrivateKey.java @@ -0,0 +1,125 @@ +package assignment.cryptography.rsa; + +import java.math.BigInteger; +import java.io.*; + +/** + * Class representing a private RSA key. + * 描述 RSA 私钥及其处理消息过程 + * REF https://github.com/rhgrant10/RSA/blob/master/RSA/src/com/rhg/rsa/RSAPrivateKey.java + */ +public class RSAPrivateKey extends RSAKey +{ + /** The private exponent. */ + private BigInteger d; // 私钥参数 d + + /** Extracts the data portion of the byte array. + * 移除分组中的填充内容 + * @param EB byte[]: 消息分组 + * @return byte[]: 移除填充内容的消息分组 + */ + protected static byte[] extractData(byte[] EB) + { + if (EB.length < 12 || EB[0] != 0x00 || EB[1] != 0x02) { + return null; + } + int index = 2; + do { + } while (EB[index++] != 0x00); + + return getSubArray(EB, index, EB.length); + } + + /** Default constructor. + * 默认构造函数 + */ + public RSAPrivateKey() + { + super(); + this.setPriExp(null); + } + + /** Main constructor. + * 主要构造函数 + * @param modulus BigInteger: 模数 n + * @param priExp BigInteger: 私钥参数 d + */ + public RSAPrivateKey(BigInteger modulus, BigInteger priExp) + { + super(modulus); + this.setPriExp(priExp); + } + + /** Sets the private exponent. + * 设置私钥参数 d + * @param priExp BigInteger: 私钥参数 d + */ + public void setPriExp(BigInteger priExp) + { + this.d = weedOut(priExp); + } + + /** Returns the private exponent. + * 返回私钥参数 d + * @return BigInteger: 私钥参数 d + */ + public BigInteger getPriExp() + { + return this.d; + } + + /** Performs the classical RSA computation. + * 执行经典的 RSA 私钥解密计算 + * @param c BigInteger: 待处理的消息 + * @return BigInteger: 处理后的消息 + */ + public BigInteger decrypt(BigInteger c) + { + // c^d mod n + return c.modPow(this.getPriExp(), this.getModulus()); + } + + /** Uses key and returns true if decryption was successful. + * 使用本私钥进行解密, 判断是否解密成功 + * @param source String: 原文件路径 + * @param destination String: 目标文件路径 + * @return boolean: {@code true}: 加密成功; {@code false}: 加密失败 + */ + public boolean use(String source, String destination) + { + byte[] sourceBytes = fileToBytes(source); // 读取文件内容 + if (isNull(sourceBytes)) { // 文件读取失败 + return false; + } + + // PKCS1-V1.5 Padding + int k = this.getModulusByteSize(); // 模数长度(单位: 字节) + BigInteger c, m; // 密文分组, 明文分组 (BigInteger) + byte[] EB, M; // 临时分组, 密文分组 + byte[][] C = reshape(sourceBytes, k); // 分片的明文消息 + BufferedOutputStream out = null; // 文件输出流 + + try { + out = new BufferedOutputStream(new FileOutputStream(destination)); // 文件输出流 + for (int i = 0; i < C.length; i++) { // 每次处理一个消息分组 + if (C[i].length != k) return false; // 消息分组长度不合法 + c = new BigInteger(C[i]); // 密文分组 + m = this.decrypt(c); // 解密 + EB = bigIntToBytes(m, k); // 带填充的明文分组 + M = extractData(EB); // 移除填充的明文分组 + out.write(M); // 明文分组输出至输出文件 + } + out.close(); // 输出文件关闭 + } catch (IOException e) { + return false; + } finally { + try { + if (isNull(out)) out.close(); + } catch (IOException e) { + return false; + } + } + + return true; + } +} diff --git a/assignment/src/assignment/cryptography/rsa/RSAPublicKey.java b/assignment/src/assignment/cryptography/rsa/RSAPublicKey.java new file mode 100644 index 0000000..107afb0 --- /dev/null +++ b/assignment/src/assignment/cryptography/rsa/RSAPublicKey.java @@ -0,0 +1,116 @@ +package assignment.cryptography.rsa; + +import java.math.BigInteger; +import java.io.*; + +/** + * Class representing a public RSA key. + * 描述 RSA 公钥及其处理消息过程 + * REF https://github.com/rhgrant10/RSA/blob/master/RSA/src/com/rhg/rsa/RSAPublicKey.java + */ +public class RSAPublicKey extends RSAKey +{ + /** The public exponent. */ + private BigInteger e; // 公钥参数 e + + /** Default constructor. + * 默认构造函数 + */ + public RSAPublicKey() + { + super(null); + this.setPubExp(null); + } + + /** Main constructor. + * 主要构造函数 + * @param modulus BigInteger: 模数 n + * @param pubExp BigInteger: 公钥参数 e + */ + public RSAPublicKey(BigInteger modulus, BigInteger pubExp) + { + super(modulus); + this.setPubExp(pubExp); + } + + /** Sets the public exponent. + * 设置公钥参数 e + * @param pubExp BigInteger: 公钥参数 e + */ + public void setPubExp(BigInteger pubExp) + { + this.e = weedOut(pubExp); + } + + /** Returns the public exponent. + * 返回公钥参数 e + * @return BigInteger: 公钥参数 e + */ + public BigInteger getPubExp() + { + return this.e; + } + + /** Performs the classical RSA computation. + * 执行经典的 RSA 公钥加密计算 + * @param m BigInteger: 待处理的消息 + * @return BigInteger: 处理后的消息 + */ + public BigInteger encrypt(BigInteger m) + { + // m^e mod n + return m.modPow(this.getPubExp(), this.getModulus()); + } + + /** Uses the key and returns true if encryption was successful. + * 使用本公钥进行加密, 判断是否加密成功 + * @param source String: 原文件路径 + * @param destination String: 目标文件路径 + * @return boolean: {@code true}: 加密成功; {@code false}: 加密失败 + */ + public boolean use(String source, String destination) + { + byte[] sourceBytes = fileToBytes(source); // 读取文件内容 + if (isNull(sourceBytes)) { // 文件读取失败 + System.err.println(String.format("%s contained nothing.", source)); + return false; + } + + // PKCS1-V1.5 Padding + int k = this.getModulusByteSize(); // 模数长度(单位: 字节) + byte BT = 0x02; // 填充 + byte[] C, M; // 密文分组, 明文分组 (byte[]) + byte[][] D = reshape(sourceBytes, k - 11); // 0x00 0x02 8个非零字节填充 0x00 消息分组 + ByteArrayOutputStream EB = new ByteArrayOutputStream(k); // 缓冲的临时分组(分组长度位模数 n 的长度) + FileOutputStream out = null; // 文件输出流 + BigInteger m, c; // 明文分组, 密文分组 (BigInteger) + + try { + out = new FileOutputStream(destination); // 文件输出流 + for (int i = 0; i < D.length; i++) { // 每次处理一个消息分组 + EB.reset(); // 重置临时分组 + /* 字节填充 */ + EB.write(0x00); + EB.write(BT); + EB.write(makePaddingString(k - D[i].length - 3)); + EB.write(0x00); + /* 消息分组 */ + EB.write(D[i]); + /* 临时分组转换为字节数组并加密 */ + M = EB.toByteArray(); + m = new BigInteger(M); + c = this.encrypt(m); + C = bigIntToBytes(c, k); + /* 加密后的消息写入输出文件 */ + out.write(C); + } + out.close(); + } catch (Exception e) { + String errMsg = "An exception occured!%n%s%n%s%n%s"; + System.err.println(String.format(errMsg, e.getClass(), e.getMessage(), e.getStackTrace())); + return false; + } + + return true; + } +} diff --git a/assignment/src/assignment/cryptography/rsa/RSATest.java b/assignment/src/assignment/cryptography/rsa/RSATest.java new file mode 100644 index 0000000..ac74212 --- /dev/null +++ b/assignment/src/assignment/cryptography/rsa/RSATest.java @@ -0,0 +1,136 @@ +package assignment.cryptography.rsa; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +public class RSATest { + private static int TESTS_PASS = 0; + private static int TEST_ERROR = 1; + private static int INCORRECT_INVOCATION = 127; + + /** + * @param args + */ + public static void main(String[] args) { + // File paths. Only the file specified in plaintext must exist. + String plaintext = null; // 明文文本 + String cryptotext = null; // 密文文本 + String decryptedText1 = null; // 使用私钥解密文本 + String decryptedText2 = null; // 使用完整密钥解密文本 + + // Make sure we have the necessary arguments. + if (args.length != 2) { + System.out.println("You must provide a readable source file and a writable desination."); + System.exit(RSATest.INCORRECT_INVOCATION); + } else { + plaintext = args[0]; + cryptotext = args[1]; + decryptedText1 = plaintext + "-private"; + decryptedText2 = plaintext + "-complete"; + } + + // Create the keys. + // 密钥生成 + RSAKeyGenerator keygen = new RSAKeyGenerator(); + RSACompleteKey completeKey = (RSACompleteKey)keygen.makeKey(RSAKey.COMPLETE_KEY); + RSAPublicKey publicKey = (RSAPublicKey)keygen.makeKey(RSAKey.PUBLIC_KEY); + RSAPrivateKey privateKey = (RSAPrivateKey)keygen.makeKey(RSAKey.PRIVATE_KEY); + + // Test encryption using the public key. Other tests rely on this part working, + // so just quit if it fails. + if (!publicKey.use(plaintext, cryptotext)) { + System.out.println("Public key encryption failed."); + File f = new File(plaintext); + System.out.println(String.format("plaintext from: %s", f.getAbsolutePath())); + System.exit(RSATest.TEST_ERROR); + } + + // Make the assumption that tests will pass. + int status = RSATest.TESTS_PASS; + + // Test decryption using the private key. Check accuracy if successful. + if (!privateKey.use(cryptotext, decryptedText1)) { + System.out.println("Private key decryption failed."); + status = RSATest.TEST_ERROR; + } else if (isDiff(plaintext, decryptedText1)) { + System.out.println("Private key decryption produced wrong plaintext!"); + status = RSATest.TEST_ERROR; + } + + // Test decryption using the complete key. Check accuracy if successful. + if (!completeKey.use(cryptotext, decryptedText2)) { + System.out.println("Complete key decryption failed."); + status = RSATest.TEST_ERROR; + } else if (isDiff(plaintext, decryptedText2)) { + System.out.println("Complete key decryption produced the wrong plaintext!"); + status = RSATest.TEST_ERROR; + } + + // Report success, if that is the case. + if (status == RSATest.TESTS_PASS) { + System.out.println("All tests passed!"); + } + System.exit(status); + } + + /** + * Returns true if there is a difference between the files specified by textA and textB + * @param textA path to file A + * @param textB path to file B + * @return true if the contents of file A are exactly the contents of file B; otherwise false. + */ + private static boolean isDiff(String textA, String textB) { + byte[] aBytes = getBytes(textA); + byte[] bBytes = getBytes(textB); + + if (aBytes.length != bBytes.length) { + return true; + } + + for (int i = 0; i < aBytes.length; i++) { + if (aBytes[i] != bBytes[i]) return true; + } + + return false; + } + + /** + * Returns the contents of the file specified by fileName as an array of bytes. + * @param fileName path of the file whose bytes should be returned + * @return the contents of fileName as a byte array + */ + private static byte[] getBytes(String fileName) { + File fIn = new File(fileName); + FileInputStream in = null; + byte[] bytes = null; + + try { + in = new FileInputStream(fIn); + + long fileSize = fIn.length(); + if (fileSize > Integer.MAX_VALUE) { + System.out.println("Sorry, file was too large!"); + } + + bytes = new byte[(int) fileSize]; + + int offset = 0; + int numRead = 0; + while (offset < bytes.length + && (numRead = in.read(bytes, offset, bytes.length - offset)) >= 0) { + offset += numRead; + } + } catch (IOException e) { + return null; + } finally { + try { + in.close(); + } catch (IOException e) { + return null; + } + } + + return bytes; + } +} -- Gitee