From 69dd449aca7901d4a0dcee1b2a67f8d264485ba0 Mon Sep 17 00:00:00 2001 From: zcy Date: Fri, 5 Sep 2025 14:36:09 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=B4=E6=97=B6=E5=AD=98=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 14 ++-- gradle.properties | 3 + gradle/wrapper/gradle-wrapper.properties | 4 +- src/com/zuc/zuc256/Method.java | 19 ++++++ src/com/zuc/zuc256/MyApplet.java | 29 --------- src/com/zuc/zuc256/XwSecurity.java | 81 ++++++++++++++++++++++++ src/com/zuc/zuc256/Zuc256Core.java | 26 ++++++-- src/com/zuc/zuc256/Zuc256Demo.java | 66 ++++++++++++------- src/com/zuc/zuc256/Zuc256EncryptCtx.java | 32 ++++++---- src/com/zuc/zuc256/Zuc256Util.java | 20 +++--- 10 files changed, 210 insertions(+), 84 deletions(-) create mode 100644 gradle.properties create mode 100644 src/com/zuc/zuc256/Method.java delete mode 100644 src/com/zuc/zuc256/MyApplet.java create mode 100644 src/com/zuc/zuc256/XwSecurity.java diff --git a/build.gradle b/build.gradle index 1869c61..bad3df6 100644 --- a/build.gradle +++ b/build.gradle @@ -15,8 +15,8 @@ sourceSets { /** 统一编译编码 */ tasks.withType(JavaCompile).configureEach { t -> t.options.encoding = "UTF-8" - t.sourceCompatibility = "1.5" - t.targetCompatibility = "1.5" + t.sourceCompatibility = "1.8" + t.targetCompatibility = "1.8" def jcHome = System.getenv("JC_HOME") if (!jcHome) throw new GradleException("缺少 JC_HOME") t.options.bootstrapClasspath = files("${jcHome}/lib/api_classic.jar") @@ -34,10 +34,12 @@ dependencies { /** ===== 用本地 JCDK converter 生成 CAP ===== */ def pkgName = "com.zuc.zuc256" // ← 改成你的包名 -def appletClass = "${pkgName}.MyApplet" // ← 改成你的 Applet 全类名 +def appletClass = "${pkgName}.XwSecurity" // ← 改成你的 Applet 全类名 def packageAID = "0xA0:0x00:0x00:0x03:0x33:0x01:0x01" // ← 改成你的包 AID def appletAID = "0xA0:0x00:0x00:0x03:0x33:0x01:0x01:0x01"// ← 改成你的实例 AID def ver = "1.0" + + tasks.register("buildCap") { group = "build" description = "Generate CAP via JCDK converter.bat" @@ -61,7 +63,7 @@ tasks.register("buildCap") { if (!batFile.exists()) throw new GradleException("未找到 converter.bat:${batFile}") // 确认 .class 存在 - def cls = file("$buildDir/classes/java/main/${pkgName.replace('.','/')}/MyApplet.class") + def cls = file("$buildDir/classes/java/main/${pkgName.replace('.','/')}/XwSecurity.class") if (!cls.exists()) throw new GradleException("未找到已编译类:${cls}") @@ -96,7 +98,7 @@ tasks.register("buildCap") { if (result.exitValue != 0) { println "---- converter.log tail ----" - println logFile.readLines("GBK").takeRight(80).join(System.lineSeparator()) + println logFile.readLines("UTF-8").takeRight(80).join(System.lineSeparator()) throw new GradleException("converter 退出码:${result.exitValue}") } else { println "CAP 生成成功,目录:${file("$buildDir/javacard").absolutePath}" @@ -104,3 +106,5 @@ tasks.register("buildCap") { } } + + diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..f1df6a1 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,3 @@ +# 统一 Gradle/JVM 的默认编码 +org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 +systemProp.file.encoding=UTF-8 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a880995..63d7772 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,6 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.4-bin.zip zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -org.gradle.jvmargs=-Dfile.encoding=UTF-8 -org.gradle.console=plain +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/src/com/zuc/zuc256/Method.java b/src/com/zuc/zuc256/Method.java new file mode 100644 index 0000000..056089e --- /dev/null +++ b/src/com/zuc/zuc256/Method.java @@ -0,0 +1,19 @@ +package com.zuc.zuc256; + +import javacard.framework.APDU; + +public class Method { + + public void processData(APDU apdu) + { + // TODO Auto-generated method stub + + } + + public void updateKey(APDU apdu) + { + // TODO Auto-generated method stub + + } + +} diff --git a/src/com/zuc/zuc256/MyApplet.java b/src/com/zuc/zuc256/MyApplet.java deleted file mode 100644 index 07f1b01..0000000 --- a/src/com/zuc/zuc256/MyApplet.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.zuc.zuc256; - -import javacard.framework.*; - -/** - * 最小 JavaCard Applet 示例 - */ -public class MyApplet extends Applet { - - // 构造函数:一般初始化状态 - protected MyApplet() { - register(); // 必须调用,完成注册 - } - - // 安装方法:CAP 文件安装时调用 - public static void install(byte[] bArray, short bOffset, byte bLength) { - new MyApplet(); - } - - // 处理 APDU 命令 - @Override - public void process(APDU apdu) throws ISOException { - if (selectingApplet()) { - return; // 选择时无需处理数据 - } - // 默认:所有指令不支持 - ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); - } -} diff --git a/src/com/zuc/zuc256/XwSecurity.java b/src/com/zuc/zuc256/XwSecurity.java new file mode 100644 index 0000000..da9bc02 --- /dev/null +++ b/src/com/zuc/zuc256/XwSecurity.java @@ -0,0 +1,81 @@ +/** + * + */ +package com.zuc.zuc256; + + +import javacard.framework.Applet; +import javacard.framework.ISOException; +import javacard.framework.ISO7816; +import javacard.framework.APDU; + +/** + * @author liuww + * + */ +public class XwSecurity extends Applet { + + public static final byte INS_PROCESS_DATA = (byte)0xCA; + + public static final byte INS_STORE_DATA = (byte)0xE2; + + public static final byte INS_INITIAL_UPDATE = (byte)0x50; + + public static final byte INS_EXTERNAL_AUTH = (byte)0x82; + + private Method method; + + public XwSecurity(byte[] bArray, short bOffset, byte bLength) { + // TODO Auto-generated constructor stub + method = new Method(); + + register(bArray, (short)(bOffset + 1), bArray[bOffset]); + } + + public static void install(byte[] bArray, short bOffset, byte bLength) + { + // GP-compliant JavaCard applet registration + new XwSecurity(bArray, bOffset, bLength); + } + + public void process(APDU apdu) + { + // Good practice: Return 9000 on SELECT + if(selectingApplet()) + { + return; + } + + if(method == null) { + return; + } + + byte[] buf = apdu.getBuffer(); + switch (buf[ISO7816.OFFSET_INS]) + { + case INS_INITIAL_UPDATE: +// SecureChannel sc = GPSystem.getSecureChannel(); +// sc.processSecurity(apdu); + break; + + case INS_EXTERNAL_AUTH: +// sc = GPSystem.getSecureChannel(); +// sc.processSecurity(apdu); + break; + + case INS_PROCESS_DATA: + method.processData(apdu); + break; + + case INS_STORE_DATA: + method.updateKey(apdu); + break; + + default: + // good practice: If you don't know the INStruction, say so: + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + } + + + } +} \ No newline at end of file diff --git a/src/com/zuc/zuc256/Zuc256Core.java b/src/com/zuc/zuc256/Zuc256Core.java index 70b51a4..71947af 100644 --- a/src/com/zuc/zuc256/Zuc256Core.java +++ b/src/com/zuc/zuc256/Zuc256Core.java @@ -63,7 +63,10 @@ public final class Zuc256Core { a = (a & 0x7FFFFFFF) + (a >>> 31); int v = (int) ((a & 0x7FFFFFFF) + (a >>> 31)); - System.arraycopy(LFSR, 1, LFSR, 0, 15); +// System.arraycopy(LFSR, 1, LFSR, 0, 15); + for (short i = 0; i < 15; i++) { + LFSR[i] = LFSR[i + 1]; + } LFSR[15] = v; state.R1 = R1; @@ -119,7 +122,10 @@ public final class Zuc256Core { a = (a & 0x7FFFFFFF) + (a >>> 31); int v = (int) ((a & 0x7FFFFFFF) + (a >>> 31)); - System.arraycopy(LFSR, 1, LFSR, 0, 15); +// System.arraycopy(LFSR, 1, LFSR, 0, 15); + for (short j = 0; j < (short)15; j++) { + LFSR[j] = LFSR[(short)(j + 1)]; + } LFSR[15] = v; } @@ -171,7 +177,11 @@ public final class Zuc256Core { a = (a & 0x7FFFFFFF) + (a >>> 31); int v = (int) ((a & 0x7FFFFFFF) + (a >>> 31)); - System.arraycopy(LFSR, 1, LFSR, 0, 15); +// System.arraycopy(LFSR, 1, LFSR, 0, 15); + for (short i = 0; i < (short)15; i++) { + LFSR[i] = LFSR[(short)(i + 1)]; + } + LFSR[15] = v; state.R1 = R1; @@ -250,7 +260,10 @@ public final class Zuc256Core { v = add31(v, rot31(LFSR[15], 15)); v = add31(v, W >>> 1); - System.arraycopy(LFSR, 1, LFSR, 0, 15); +// System.arraycopy(LFSR, 1, LFSR, 0, 15); + for (short j = 0; j < (short)15; j++) { + LFSR[j] = LFSR[(short)(j + 1)]; + } LFSR[15] = v; } @@ -284,7 +297,10 @@ public final class Zuc256Core { a = (a & 0x7FFFFFFF) + (a >>> 31); int v = (int) ((a & 0x7FFFFFFF) + (a >>> 31)); - System.arraycopy(LFSR, 1, LFSR, 0, 15); +// System.arraycopy(LFSR, 1, LFSR, 0, 15); + for (short i = 0; i < (short)15; i++) { + LFSR[i] = LFSR[(short)(i + 1)]; + } LFSR[15] = v; key.R1 = R1; diff --git a/src/com/zuc/zuc256/Zuc256Demo.java b/src/com/zuc/zuc256/Zuc256Demo.java index c28c1d7..71207a5 100644 --- a/src/com/zuc/zuc256/Zuc256Demo.java +++ b/src/com/zuc/zuc256/Zuc256Demo.java @@ -1,9 +1,8 @@ package com.zuc.zuc256; -import java.util.Arrays; + import static com.zuc.zuc256.Zuc256Util.extractIv; -import static com.zuc.zuc256.Zuc256Util.printHex; @@ -12,22 +11,45 @@ import static com.zuc.zuc256.Zuc256Util.printHex; */ public final class Zuc256Demo { - public static void main(String[] args) { + public static void main(short args[]) { // 1. 明文 - byte[] plaintext = "ZUC256对称加解密测试:1234567890".getBytes(); - int plaintextLen = plaintext.length; - System.out.println("明文: " + new String(plaintext)); - printHex("明文(十六进制)", plaintext, plaintextLen); + byte[] plaintext = new byte[] { + 0x5A, 0x55, 0x43, 0x32, 0x35, 0x36, + (byte)0xE5,(byte)0xAF,(byte)0xB9,(byte)0xE7,(byte)0xA7,(byte)0xB0, + (byte)0xE5,(byte)0x8A,(byte)0xA0,(byte)0xE8,(byte)0xA7,(byte)0xA3, + (byte)0xE5,(byte)0xAF,(byte)0x86,(byte)0xE6,(byte)0xB5,(byte)0x8B, + (byte)0xE8,(byte)0xAF,(byte)0x95, 0x3A, + 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x30 + }; // 长度=38 ZUC256加密测试 + short plaintextLen = (short)plaintext.length; +// System.out.println("明文: " + new String(plaintext)); +// printHex("明文(十六进制)", plaintext, plaintextLen); // 2. 密钥(32字节ASCII) - byte[] key = "0123456789abcdef0123456789abcdef".getBytes(); - printHex("密钥", key, 32); + byte[] key = new byte[] { + 0x30, 0x31, 0x32, 0x33, // "0123" + 0x34, 0x35, 0x36, 0x37, // "4567" + 0x38, 0x39, 0x61, 0x62, // "89ab" + 0x63, 0x64, 0x65, 0x66, // "cdef" + 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x61, 0x62, + 0x63, 0x64, 0x65, 0x66 + }; +// byte[] key = "0123456789abcdef0123456789abcdef".getBytes(); +// printHex("密钥", key, 32); // 3. 初始向量(25字节ASCII) - byte[] inputIv25Byte = "0123456789abcdefg01234567".getBytes(); +// byte[] inputIv25Byte = "0123456789abcdefg01234567".getBytes(); + byte[] inputIv25Byte = new byte[] { + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // "01234567" + 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, // "89abcdef" + 0x67, // "g" + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 // "01234567" + }; byte[] iv = new byte[23]; extractIv(inputIv25Byte, iv); - printHex("提取后的IV", iv, 23); +// printHex("提取后的IV", iv, 23); // 4. 分配加密/解密缓冲区 byte[] ciphertext = new byte[plaintextLen]; @@ -37,25 +59,25 @@ public final class Zuc256Demo { Zuc256State stateEnc = new Zuc256State(); Zuc256Core.initState(stateEnc, key, iv); zuc256Crypt(stateEnc, plaintext, plaintextLen, ciphertext); - printHex("密文", ciphertext, plaintextLen); +// printHex("密文", ciphertext, plaintextLen); // 6. 解密(重新初始化状态) Zuc256State stateDec = new Zuc256State(); Zuc256Core.initState(stateDec, key, iv); zuc256Crypt(stateDec, ciphertext, plaintextLen, decryptedtext); - printHex("解密后", decryptedtext, plaintextLen); - System.out.println("解密文本: " + new String(decryptedtext)); +// printHex("解密后", decryptedtext, plaintextLen); +// System.out.println("解密文本: " + new String(decryptedtext)); // 7. 验证结果 - if (Arrays.equals(plaintext, decryptedtext)) { - System.out.println("=== 测试成功: 解密结果与明文一致 ==="); - } else { - System.out.println("=== 测试失败: 解密结果与明文不一致 ==="); - } +// if (Arrays.equals(plaintext, decryptedtext)) { +// System.out.println("=== 测试成功: 解密结果与明文一致 ==="); +// } else { +// System.out.println("=== 测试失败: 解密结果与明文不一致 ==="); +// } } // 一次性加密 - public static void zuc256Crypt(Zuc256State state, byte[] in, int inlen, byte[] out) { + public static void zuc256Crypt(Zuc256State state, byte[] in, short inlen, byte[] out) { if (state == null || in == null || out == null) return; Zuc256EncryptCtx ctx = new Zuc256EncryptCtx(state); @@ -68,6 +90,6 @@ public final class Zuc256Demo { System.arraycopy(out, remainingOffset, finishOut, 0, finishOut.length); } ctx.finish(finishOut); - System.arraycopy(finishOut, 0, out, remainingOffset, finishOut.length); + System.arraycopy(finishOut, (short)0, out, (short)remainingOffset, (short)finishOut.length); } -} +} \ No newline at end of file diff --git a/src/com/zuc/zuc256/Zuc256EncryptCtx.java b/src/com/zuc/zuc256/Zuc256EncryptCtx.java index 9772a40..0a93d79 100644 --- a/src/com/zuc/zuc256/Zuc256EncryptCtx.java +++ b/src/com/zuc/zuc256/Zuc256EncryptCtx.java @@ -1,6 +1,6 @@ package com.zuc.zuc256; -import java.util.Arrays; +import javacard.framework.Util; import static com.zuc.zuc256.Zuc256Core.zuc256GenerateKeystream; import static com.zuc.zuc256.Zuc256Core.zuc256GenerateKeyword; @@ -33,27 +33,32 @@ public final class Zuc256EncryptCtx { // 初始化加密上下文 public void init(byte[] key32, byte[] iv) { - Arrays.fill(this.buf, (byte) 0); + Util.arrayFillNonAtomic(this.buf, (short) 0, (short) this.buf.length, (byte) 0); this.buflen = 0; Zuc256Core.initState(this.state, key32, iv); } // 分阶段处理加密数据 - public void update(byte[] in, int inlen, byte[] out) { + public void update(byte[] in, short inlen, byte[] out) { if (in == null || out == null || inlen == 0) return; // 处理缓冲区中剩余的非4字节数据 if (this.buflen > 0) { int need = 4 - this.buflen; - int copy = Math.min(inlen, need); + short copy; + if (inlen < need) { + copy = (short) inlen; + } else { + copy = (short) need; + } - System.arraycopy(in, 0, this.buf, this.buflen, copy); + Util.arrayCopyNonAtomic(in, (short) 0, this.buf, (short) this.buflen, copy); this.buflen += copy; // 调整输入指针和长度 byte[] newIn = new byte[inlen - copy]; if (inlen - copy > 0) { - System.arraycopy(in, copy, newIn, 0, inlen - copy); + Util.arrayCopyNonAtomic(in, copy, newIn, (short)0, (short) (inlen - copy)); } in = newIn; inlen -= copy; @@ -65,12 +70,12 @@ public final class Zuc256EncryptCtx { putU32(out, 0, plain ^ keystream); this.buflen = 0; - Arrays.fill(this.buf, (byte) 0); + Util.arrayFillNonAtomic(this.buf, (short) 0, (short) this.buf.length, (byte) 0); // 调整输出指针 byte[] newOut = new byte[out.length - 4]; if (out.length - 4 > 0) { - System.arraycopy(out, 4, newOut, 0, out.length - 4); + Util.arrayCopyNonAtomic(out, (short)4, newOut, (short)0, (short)(out.length - 4)); } out = newOut; } @@ -92,7 +97,7 @@ public final class Zuc256EncryptCtx { int processed = fullBlocks * 4; byte[] newIn = new byte[inlen - processed]; if (inlen - processed > 0) { - System.arraycopy(in, processed, newIn, 0, inlen - processed); + Util.arrayCopyNonAtomic(in, (short) processed, newIn, (short) 0, (short) (inlen - processed)); } in = newIn; inlen -= processed; @@ -100,7 +105,7 @@ public final class Zuc256EncryptCtx { // 缓存剩余不足4字节的数据 if (inlen > 0) { - System.arraycopy(in, 0, this.buf, 0, inlen); + Util.arrayCopyNonAtomic(in, (short) 0, this.buf, (short) 0, (short) inlen); this.buflen = inlen; } } @@ -122,9 +127,12 @@ public final class Zuc256EncryptCtx { } // 清理上下文 - Arrays.fill(this.buf, (byte) 0); + Util.arrayFillNonAtomic(this.buf, (short) 0, (short) this.buf.length, (byte) 0); this.buflen = 0; - Arrays.fill(this.state.LFSR, 0); + // 清零操作 + for (short i = 0; i < (short)this.state.LFSR.length; i++) { + this.state.LFSR[i] = 0; + } this.state.R1 = 0; this.state.R2 = 0; } diff --git a/src/com/zuc/zuc256/Zuc256Util.java b/src/com/zuc/zuc256/Zuc256Util.java index d9d6d90..6e43cd2 100644 --- a/src/com/zuc/zuc256/Zuc256Util.java +++ b/src/com/zuc/zuc256/Zuc256Util.java @@ -1,5 +1,7 @@ package com.zuc.zuc256; +import javacard.framework.Util; + /** * 辅助工具:装载/存储、位运算、线性变换、打印等。 */ @@ -77,7 +79,9 @@ public final class Zuc256Util { if (input25Byte == null || output23Byte == null) return; // 复制前17字节 - System.arraycopy(input25Byte, 0, output23Byte, 0, 17); +// System.arraycopy(input25Byte, 0, output23Byte, 0, 17); + Util.arrayCopyNonAtomic(input25Byte, (short)0, output23Byte, (short)0, (short)17); + // 处理剩余8字节 byte[] src = new byte[8]; @@ -94,11 +98,11 @@ public final class Zuc256Util { } /** 打印十六进制(调试用,TODO 生产/JC 环境可移除) */ - public static void printHex(String label, byte[] data, int len) { - System.out.print(label + ": "); - for (int i = 0; i < len; i++) { - System.out.printf("%02x ", data[i] & 0xFF); - } - System.out.println(); - } +// public static void printHex(String label, byte[] data, int len) { +// System.out.print(label + ": "); +// for (int i = 0; i < len; i++) { +// System.out.printf("%02x ", data[i] & 0xFF); +// } +// System.out.println(); +// } }