From 3ac6d14ea7d88144c9c7c0d84f3c55e40ce35cdf Mon Sep 17 00:00:00 2001 From: liulu Date: Sun, 19 Apr 2026 15:44:46 +0800 Subject: [PATCH] ukey-tools --- ukey-tool/pom.xml | 66 +++ ukey-tool/skflib/HidAPI.dll | Bin 0 -> 12800 bytes ukey-tool/skflib/MscAPI.dll | Bin 0 -> 21504 bytes ukey-tool/skflib/SKF.dll | Bin 0 -> 241152 bytes .../com/cisd/ukey/UkeyToolApplication.java | 15 + .../com/cisd/ukey/config/SkfProperties.java | 22 + .../com/cisd/ukey/controller/ApiResponse.java | 50 ++ .../ukey/controller/ApiResponseAdvice.java | 28 ++ .../cisd/ukey/controller/SkfController.java | 110 +++++ .../ukey/controller/SkfExceptionHandler.java | 26 ++ .../SkfErrorCodeMessageResolver.java | 82 ++++ .../com/cisd/ukey/exception/SkfException.java | 27 ++ .../com/cisd/ukey/jna/BlockCipherParam.java | 12 + .../main/java/com/cisd/ukey/jna/DevInfo.java | 37 ++ .../com/cisd/ukey/jna/EccPublicKeyBlob.java | 19 + .../com/cisd/ukey/jna/EccSignatureBlob.java | 10 + .../java/com/cisd/ukey/jna/FileAttribute.java | 12 + .../java/com/cisd/ukey/jna/SkfLibrary.java | 74 +++ .../main/java/com/cisd/ukey/jna/Version.java | 14 + .../java/com/cisd/ukey/service/BCECUtils.java | 428 ++++++++++++++++++ .../ukey/service/SM2PreprocessSigner.java | 294 ++++++++++++ .../com/cisd/ukey/service/SkfService.java | 306 +++++++++++++ .../cisd/ukey/service/SkfSessionTemplate.java | 297 ++++++++++++ ukey-tool/src/main/resources/application.yml | 7 + 24 files changed, 1936 insertions(+) create mode 100644 ukey-tool/pom.xml create mode 100644 ukey-tool/skflib/HidAPI.dll create mode 100644 ukey-tool/skflib/MscAPI.dll create mode 100644 ukey-tool/skflib/SKF.dll create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/UkeyToolApplication.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/config/SkfProperties.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/controller/ApiResponse.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/controller/ApiResponseAdvice.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/controller/SkfController.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/controller/SkfExceptionHandler.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/exception/SkfErrorCodeMessageResolver.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/exception/SkfException.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/jna/BlockCipherParam.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/jna/DevInfo.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/jna/EccPublicKeyBlob.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/jna/EccSignatureBlob.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/jna/FileAttribute.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/jna/SkfLibrary.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/jna/Version.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/service/BCECUtils.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/service/SM2PreprocessSigner.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/service/SkfService.java create mode 100644 ukey-tool/src/main/java/com/cisd/ukey/service/SkfSessionTemplate.java create mode 100644 ukey-tool/src/main/resources/application.yml diff --git a/ukey-tool/pom.xml b/ukey-tool/pom.xml new file mode 100644 index 0000000..a1c296b --- /dev/null +++ b/ukey-tool/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.5.11 + + + com.cisd + ukey-tool + 1.0-SNAPSHOT + + + 17 + 17 + UTF-8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + net.java.dev.jna + jna + 5.14.0 + + + + org.bouncycastle + bcprov-jdk18on + 1.81 + + + + org.projectlombok + lombok + provided + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + cisd-ukey-tool + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/ukey-tool/skflib/HidAPI.dll b/ukey-tool/skflib/HidAPI.dll new file mode 100644 index 0000000000000000000000000000000000000000..d036094d764ff13fa36a5a47f22ed3d17429caa3 GIT binary patch literal 12800 zcmeHN4Rlk-l^*?p_%Sj`M1VjLMr0ubA`syx1}b3*o|7jq*!+~32q8a%iY=?s6M;RX zHI*H#PL+~2O}pJRO>?>@Nl$k}(_JvoGdn*3C;TYO`Dij?a*l<#9zofQbUv+-NQM9dZ{A|N-RlYSF&Z+&c==2|L zcvWp**5Su0Tp9_lr#U`vMP+`*svhXaeqU7|$9&S(+{xGxu*RlC9zeE+v7cicR>qi5osf`7 zF^#@!Dr2KG%->eJ7<)W_oKJ8~k)=&Ca8KNTf<&gZ)BJhK0jZ|Mt3q;!vGY(5!OyiN9dC9}r{JiErWYyi$b}&B)=}M<#+42?M3hy%Sa_u{CtivNArWOUNwS<#%`b-7Q4a;d83Cr&BKmH5Z*O#XcD|cP9~d zFe@hT5n4zGAb0?R2OxOh#}Id$$XkecmXH>m6NHTD6gvFdz>GDEP6y^t>>pxv0^b6f zjJ*+ELaM{8c05HLSlbA6G*Aaoe^*1=8jKJbQfBN+v@WRX81hNv)5zzLFCr_*{gG{o zeI>DG>?4Ri6F4SjEc2;~8Tz9>4|T=vQ-@dNyY|rGRA(Fkl-9}!2E{(`YIBo8CAb7% zhymXa8aw6dgh?H0ogc%}vri92SE^GXLGM3y{y&6^hXtim;2-)kTWkiQ@q}EQ@fv-_ z-EntnzXE+8o75`s13ptXHtd)%X@742Y&(sD@di_pWZE^Dz7~&chB>OP1$u1DAFl{BBxpI`BNya9ZT&z5Jpt zegBhQu88UTJFpY5J#D8H`vY*<{>jGv!Vt@#>z zz6g6h?dJ!{o)732`Xac=p3jq5Fn4J5V$Pfoi0Lh`LCx?RK*$46D)ulcM1?VOrW|-u zb`>#5< zt?jh0`TOrFU%mqFuN!EATMpr-QMbFn*`;C{oDNJ~%0DEx2S?c0X=^x1J&i|f4Zo#I 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 be_bBTfwKr&o%V3W!_^N1`R4f_C4v70WvKf? literal 0 HcmV?d00001 diff --git a/ukey-tool/skflib/SKF.dll b/ukey-tool/skflib/SKF.dll new file mode 100644 index 0000000000000000000000000000000000000000..b43154e7eb550495ab9d86c1bae92f6fd703d63b GIT binary patch literal 241152 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%0AguN&Yw@{;=egQ(kfOeuw@z>727YdF3J4Zh2T* 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 { + + private int code; + private T data; + private String msg; + + public ApiResponse() { + } + + public ApiResponse(int code, T data, String msg) { + this.code = code; + this.data = data; + this.msg = msg; + } + + public static ApiResponse success(T data) { + return new ApiResponse<>(0, data, "success"); + } + + public static ApiResponse fail(int code, String msg) { + int actualCode = code == 0 ? -1 : code; + return new ApiResponse<>(actualCode, null, msg); + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/controller/ApiResponseAdvice.java b/ukey-tool/src/main/java/com/cisd/ukey/controller/ApiResponseAdvice.java new file mode 100644 index 0000000..734287b --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/controller/ApiResponseAdvice.java @@ -0,0 +1,28 @@ +package com.cisd.ukey.controller; + +import org.springframework.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; + +@RestControllerAdvice +public class ApiResponseAdvice implements ResponseBodyAdvice { + + @Override + public boolean supports(MethodParameter returnType, Class> converterType) { + return true; + } + + @Override + public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, + Class> selectedConverterType, + ServerHttpRequest request, ServerHttpResponse response) { + if (body instanceof ApiResponse) { + return body; + } + return ApiResponse.success(body); + } +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/controller/SkfController.java b/ukey-tool/src/main/java/com/cisd/ukey/controller/SkfController.java new file mode 100644 index 0000000..327056b --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/controller/SkfController.java @@ -0,0 +1,110 @@ +package com.cisd.ukey.controller; + +import com.cisd.ukey.service.SkfService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/api/skf") +public class SkfController { + + private final SkfService skfService; + + public SkfController(SkfService skfService) { + this.skfService = skfService; + } + + @GetMapping("/devices") + public List listDevices(@RequestParam(defaultValue = "true") boolean presentOnly) { + return skfService.listDevices(presentOnly); + } + + @PostMapping("/pin/verify") + public SkfService.PinVerifyResult verifyPin(@RequestBody PinVerifyRequest request) { + return skfService.verifyPin(request.deviceName(), request.pinType(), request.pin()); + } + + @PostMapping("/pin/change") + public SkfService.PinVerifyResult changePin(@RequestBody ChangePinRequest request) { + return skfService.changePin(request.deviceName(), request.pinType(), request.oldPin(), request.newPin()); + } + + @PostMapping("/pin/reset") + public SkfService.PinVerifyResult resetPin(@RequestBody ResetPinRequest request) { + return skfService.unblockPin(request.deviceName(), request.adminPin(), request.newUserPin()); + } + + @PostMapping("/file/list") + public List listFiles(@RequestBody DeviceRequest request) { + return skfService.listFiles(request.deviceName()); + } + + @PostMapping("/file/read") + public SkfService.FileContent readFile(@RequestBody ReadFileRequest request) { + return skfService.readFile(request.deviceName(), request.fileName()); + } + + @PostMapping("/file/write") + public SkfService.FileContent writeFile(@RequestBody WriteFileRequest request) { + return skfService.writeFile(request.deviceName(), request.fileName(), request.content()); + } + + @PostMapping("/file/delete") + public void deleteFile(@RequestBody DeleteFileRequest request) { + skfService.deleteFile(request.deviceName(), request.fileName()); + } + + @PostMapping("/app/delete") + public void deleteApplication(@RequestBody DeviceRequest request) { + skfService.deleteApplication(request.deviceName()); + } + + @PostMapping("/ecc/public-key/export") + public SkfService.EccPublicKeyResult exportPublicKey(@RequestBody DeviceRequest request, + @RequestParam(defaultValue = "true") boolean signFlag) { + return skfService.exportEccPublicKey(request.deviceName(), signFlag); + } + + @PostMapping("/ecc/sign") + public SkfService.EccSignatureResult sign(@RequestBody SignRequest request) { + return skfService.eccSign(request.deviceName(), request.plainText()); + } + + @PostMapping("/ecc/verify") + public SkfService.VerifyResult verify(@RequestBody VerifyRequest request) { + return skfService.eccVerify(request.deviceName(), request.publicKey(), request.plainText(), request.signatureBase64()); + } + + public record DeviceRequest(String deviceName) { + } + + public record PinVerifyRequest(String deviceName, int pinType, String pin) { + } + + public record ChangePinRequest(String deviceName, int pinType, String oldPin, String newPin) { + } + + public record ResetPinRequest(String deviceName, String adminPin, String newUserPin) { + } + + public record ReadFileRequest(String deviceName, String fileName) { + } + + public record WriteFileRequest(String deviceName, String fileName, String content) { + } + + public record DeleteFileRequest(String deviceName, String fileName) { + } + + public record SignRequest(String deviceName, String plainText) { + } + + public record VerifyRequest(String deviceName, String publicKey, String plainText, String signatureBase64) { + } +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/controller/SkfExceptionHandler.java b/ukey-tool/src/main/java/com/cisd/ukey/controller/SkfExceptionHandler.java new file mode 100644 index 0000000..f2b0533 --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/controller/SkfExceptionHandler.java @@ -0,0 +1,26 @@ +package com.cisd.ukey.controller; + +import com.cisd.ukey.exception.SkfErrorCodeMessageResolver; +import com.cisd.ukey.exception.SkfException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class SkfExceptionHandler { + + @ExceptionHandler(SkfException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ApiResponse handleSkfException(SkfException ex) { + String zhMessage = SkfErrorCodeMessageResolver.resolve(ex.getCode()); + String message = zhMessage == null ? ex.getMessage() : ex.getMessage() + "(" + zhMessage + ")"; + return ApiResponse.fail(ex.getCode(), message); + } + + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ApiResponse handleException(Exception ex) { + return ApiResponse.fail(-1, ex.getMessage()); + } +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/exception/SkfErrorCodeMessageResolver.java b/ukey-tool/src/main/java/com/cisd/ukey/exception/SkfErrorCodeMessageResolver.java new file mode 100644 index 0000000..aee8884 --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/exception/SkfErrorCodeMessageResolver.java @@ -0,0 +1,82 @@ +package com.cisd.ukey.exception; + +import java.util.Map; + +public final class SkfErrorCodeMessageResolver { + + private static final Map CODE_MESSAGE_MAP = Map.ofEntries( + Map.entry(0x00000000, "成功"), + Map.entry(0x0A000001, "失败"), + Map.entry(0x0A000002, "异常错误"), + Map.entry(0x0A000003, "不支持的服务"), + Map.entry(0x0A000004, "文件操作错误"), + Map.entry(0x0A000005, "无效的句柄"), + Map.entry(0x0A000006, "无效的参数"), + Map.entry(0x0A000007, "读文件错误"), + Map.entry(0x0A000008, "写文件错误"), + Map.entry(0x0A000009, "名称长度错误"), + Map.entry(0x0A00000A, "密钥用途错误"), + Map.entry(0x0A00000B, "模的长度错误"), + Map.entry(0x0A00000C, "未初始化"), + Map.entry(0x0A00000D, "对象错误"), + Map.entry(0x0A00000E, "内存错误"), + Map.entry(0x0A00000F, "超时"), + Map.entry(0x0A000010, "输入数据长度错误"), + Map.entry(0x0A000011, "输入数据错误"), + Map.entry(0x0A000012, "生成随机数错误"), + Map.entry(0x0A000013, "HASH对象错误"), + Map.entry(0x0A000014, "HASH运算错误"), + Map.entry(0x0A000015, "产生RSA密钥错误"), + Map.entry(0x0A000016, "RSA密钥模长错误"), + Map.entry(0x0A000017, "CSP服务导入公钥错误"), + Map.entry(0x0A000018, "RSA加密错误"), + Map.entry(0x0A000019, "RSA解密错误"), + Map.entry(0x0A00001A, "HASH值不相等"), + Map.entry(0x0A00001B, "密钥未发现"), + Map.entry(0x0A00001C, "证书未发现"), + Map.entry(0x0A00001D, "对象未导出"), + Map.entry(0x0A00001E, "解密填充错误"), + Map.entry(0x0A00001F, "MAC长度错误"), + Map.entry(0x0A000020, "缓冲区不足"), + Map.entry(0x0A000021, "密钥类型错误"), + Map.entry(0x0A000022, "无事件错误"), + Map.entry(0x0A000023, "设备已移除"), + Map.entry(0x0A000024, "PIN不正确"), + Map.entry(0x0A000025, "PIN被锁死"), + Map.entry(0x0A000026, "PIN无效"), + Map.entry(0x0A000027, "PIN长度错误"), + Map.entry(0x0A000028, "用户已经登录"), + Map.entry(0x0A000029, "没有初始化用户口令"), + Map.entry(0x0A00002A, "PIN类型错误"), + Map.entry(0x0A00002B, "应用名称无效"), + Map.entry(0x0A00002C, "应用已经存在"), + Map.entry(0x0A00002D, "用户没有登录"), + Map.entry(0x0A00002E, "应用不存在"), + Map.entry(0x0A00002F, "文件已经存在"), + Map.entry(0x0A000030, "空间不足"), + Map.entry(0x0A000031, "文件不存在"), + Map.entry(0x0A000032, "已达到最大可管理容器数"), + Map.entry(0x0A000033, "密钥已被锁住"), + Map.entry(0x0A000034, "证书未被使用"), + Map.entry(0x0A000035, "无效容器"), + Map.entry(0x0A000036, "容器不存在"), + Map.entry(0x0A000037, "容器已存在"), + Map.entry(0x0A000038, "证书已经被使用"), + Map.entry(0x0A000039, "密钥未被使用"), + Map.entry(0x0A00003A, "文件操作权限错误"), + Map.entry(0x0A00003B, "设备未认证"), + Map.entry(0x0A00003C, "请重新插拔卡"), + Map.entry(0x0A00003D, "应用处于非活跃状态"), + Map.entry(0x0A00003E, "应用处于打开状态"), + Map.entry(0x0A000040, "传入的固件数据无效(格式错误)"), + Map.entry(0x0A000041, "传入的固件数据的签名值无效(验签失败)"), + Map.entry(0x0A0000A4, "MF不存在") + ); + + private SkfErrorCodeMessageResolver() { + } + + public static String resolve(int code) { + return CODE_MESSAGE_MAP.get(code); + } +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/exception/SkfException.java b/ukey-tool/src/main/java/com/cisd/ukey/exception/SkfException.java new file mode 100644 index 0000000..fff3ec0 --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/exception/SkfException.java @@ -0,0 +1,27 @@ +package com.cisd.ukey.exception; + +public class SkfException extends RuntimeException { + + private final String apiName; + private final int code; + + public SkfException(String apiName, int code, String message) { + super(message); + this.apiName = apiName; + this.code = code; + } + + public SkfException(String apiName, String message, Throwable cause) { + super(message, cause); + this.apiName = apiName; + this.code = -1; + } + + public String getApiName() { + return apiName; + } + + public int getCode() { + return code; + } +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/jna/BlockCipherParam.java b/ukey-tool/src/main/java/com/cisd/ukey/jna/BlockCipherParam.java new file mode 100644 index 0000000..f5c132a --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/jna/BlockCipherParam.java @@ -0,0 +1,12 @@ +package com.cisd.ukey.jna; + +import com.sun.jna.Structure; + +@Structure.FieldOrder({"iv", "ivLen", "paddingType", "feedBitLen"}) +public class BlockCipherParam extends Structure { + + public byte[] iv = new byte[32]; + public int ivLen; + public int paddingType; + public int feedBitLen; +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/jna/DevInfo.java b/ukey-tool/src/main/java/com/cisd/ukey/jna/DevInfo.java new file mode 100644 index 0000000..97e14a0 --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/jna/DevInfo.java @@ -0,0 +1,37 @@ +package com.cisd.ukey.jna; + +import com.sun.jna.Structure; + +@Structure.FieldOrder({ + "version", "manufacturer", "issuer", "label", "serialNumber", + "hwVersion", "firmwareVersion", "algSymCap", "algAsymCap", + "algHashCap", "devAuthAlgId", "totalSpace", "freeSpace", + "maxEccBufferSize", "maxBufferSize", "reserved" +}) +public class DevInfo extends Structure { + + public DevInfo() { + setAlignType(ALIGN_NONE); + } + + public Version version; + public byte[] manufacturer = new byte[64]; + public byte[] issuer = new byte[64]; + public byte[] label = new byte[32]; + public byte[] serialNumber = new byte[32]; + public Version hwVersion; + public Version firmwareVersion; + public int algSymCap; + public int algAsymCap; + public int algHashCap; + public int devAuthAlgId; + public int totalSpace; + public int freeSpace; + public int maxEccBufferSize; + public int maxBufferSize; + public byte[] reserved = new byte[64]; + + public long devAuthAlgIdUnsigned() { + return Integer.toUnsignedLong(devAuthAlgId); + } +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/jna/EccPublicKeyBlob.java b/ukey-tool/src/main/java/com/cisd/ukey/jna/EccPublicKeyBlob.java new file mode 100644 index 0000000..db12641 --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/jna/EccPublicKeyBlob.java @@ -0,0 +1,19 @@ +package com.cisd.ukey.jna; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +@Structure.FieldOrder({"bitLen", "xCoordinate", "yCoordinate"}) +public class EccPublicKeyBlob extends Structure { + + public int bitLen; + public byte[] xCoordinate = new byte[64]; + public byte[] yCoordinate = new byte[64]; + + public EccPublicKeyBlob() { + } + + public EccPublicKeyBlob(Pointer pointer) { + super(pointer); + } +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/jna/EccSignatureBlob.java b/ukey-tool/src/main/java/com/cisd/ukey/jna/EccSignatureBlob.java new file mode 100644 index 0000000..63d990e --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/jna/EccSignatureBlob.java @@ -0,0 +1,10 @@ +package com.cisd.ukey.jna; + +import com.sun.jna.Structure; + +@Structure.FieldOrder({"r", "s"}) +public class EccSignatureBlob extends Structure { + + public byte[] r = new byte[64]; + public byte[] s = new byte[64]; +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/jna/FileAttribute.java b/ukey-tool/src/main/java/com/cisd/ukey/jna/FileAttribute.java new file mode 100644 index 0000000..d14e3fb --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/jna/FileAttribute.java @@ -0,0 +1,12 @@ +package com.cisd.ukey.jna; + +import com.sun.jna.Structure; + +@Structure.FieldOrder({"fileName", "fileSize", "readRights", "writeRights"}) +public class FileAttribute extends Structure { + + public byte[] fileName = new byte[32]; + public int fileSize; + public int readRights; + public int writeRights; +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/jna/SkfLibrary.java b/ukey-tool/src/main/java/com/cisd/ukey/jna/SkfLibrary.java new file mode 100644 index 0000000..94f0988 --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/jna/SkfLibrary.java @@ -0,0 +1,74 @@ +package com.cisd.ukey.jna; + +import com.sun.jna.Pointer; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; +import com.sun.jna.win32.StdCallLibrary; + +public interface SkfLibrary extends StdCallLibrary { + + int SKF_EnumDev(int present, Pointer nameList, IntByReference size); + + int SKF_ConnectDev(byte[] name, PointerByReference devHandle); + + int SKF_DisConnectDev(Pointer devHandle); + + int SKF_GetDevInfo(Pointer devHandle, DevInfo devInfo); + + int SKF_GenRandom(Pointer devHandle, byte[] random, int randomLen); + + int SKF_DevAuth(Pointer devHandle, byte[] authData, int len); + + int SKF_OpenApplication(Pointer devHandle, byte[] appName, PointerByReference applicationHandle); + + int SKF_CreateApplication(Pointer devHandle, byte[] appName, byte[] adminPin, int adminPinRetryCount, + byte[] userPin, int userPinRetryCount, int createFileRights, + PointerByReference applicationHandle); + + int SKF_DeleteApplication(Pointer devHandle, byte[] appName); + + int SKF_CloseApplication(Pointer applicationHandle); + + int SKF_VerifyPIN(Pointer applicationHandle, int pinType, byte[] pin, IntByReference retryCount); + + int SKF_ChangePIN(Pointer applicationHandle, int pinType, byte[] oldPin, byte[] newPin, IntByReference retryCount); + + int SKF_UnblockPIN(Pointer applicationHandle, byte[] adminPin, byte[] newUserPin, IntByReference retryCount); + + int SKF_GetPINInfo(Pointer applicationHandle, int pinType, IntByReference maxRetryCount, + IntByReference remainRetryCount, IntByReference defaultPin); + + int SKF_CreateFile(Pointer applicationHandle, byte[] fileName, int fileSize, int readRights, int writeRights); + + int SKF_DeleteFile(Pointer applicationHandle, byte[] fileName); + + int SKF_EnumFiles(Pointer applicationHandle, Pointer fileList, IntByReference size); + + int SKF_GetFileInfo(Pointer applicationHandle, byte[] fileName, FileAttribute fileAttribute); + + int SKF_ReadFile(Pointer applicationHandle, byte[] fileName, int offset, int size, Pointer outData, IntByReference outLen); + + int SKF_WriteFile(Pointer applicationHandle, byte[] fileName, int offset, byte[] data, int size); + + int SKF_OpenContainer(Pointer applicationHandle, byte[] containerName, PointerByReference containerHandle); + + int SKF_CreateContainer(Pointer applicationHandle, byte[] containerName, PointerByReference containerHandle); + + int SKF_GenECCKeyPair(Pointer containerHandle, int algId, EccPublicKeyBlob publicKeyBlob); + + int SKF_CloseContainer(Pointer containerHandle); + + int SKF_ECCSignData(Pointer containerHandle, byte[] data, int dataLen, EccSignatureBlob signature); + + int SKF_ECCVerify(Pointer devHandle, EccPublicKeyBlob publicKey, byte[] data, int dataLen, EccSignatureBlob signature); + + int SKF_ExportPublicKey(Pointer containerHandle, int signFlag, Pointer blob, IntByReference blobLen); + + int SKF_SetSymmKey(Pointer devHandle, byte[] key, int algId, PointerByReference keyHandle); + + int SKF_EncryptInit(Pointer keyHandle, BlockCipherParam encryptParam); + + int SKF_Encrypt(Pointer keyHandle, byte[] data, int dataLen, byte[] encryptedData, IntByReference encryptedLen); + + int SKF_CloseHandle(Pointer handle); +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/jna/Version.java b/ukey-tool/src/main/java/com/cisd/ukey/jna/Version.java new file mode 100644 index 0000000..50bef94 --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/jna/Version.java @@ -0,0 +1,14 @@ +package com.cisd.ukey.jna; + +import com.sun.jna.Structure; + +@Structure.FieldOrder({"major", "minor"}) +public class Version extends Structure { + + public Version() { + setAlignType(ALIGN_NONE); + } + + public byte major; + public byte minor; +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/service/BCECUtils.java b/ukey-tool/src/main/java/com/cisd/ukey/service/BCECUtils.java new file mode 100644 index 0000000..8266f75 --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/service/BCECUtils.java @@ -0,0 +1,428 @@ +package com.cisd.ukey.service; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.asn1.x9.X962Parameters; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.asn1.x9.X9ECPoint; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.generators.ECKeyPairGenerator; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECKeyGenerationParameters; +import org.bouncycastle.crypto.params.ECKeyParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; +import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECNamedCurveSpec; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.FixedPointCombMultiplier; +import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; +import org.bouncycastle.util.io.pem.PemWriter; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +/** + * 这个工具类的方法,也适用于其他基于BC库的ECC算法 + */ +public class BCECUtils { + private static final String ALGO_NAME_EC = "EC"; + private static final String PEM_STRING_PUBLIC = "PUBLIC KEY"; + private static final String PEM_STRING_ECPRIVATEKEY = "EC PRIVATE KEY"; + + + public static final SM2P256V1Curve CURVE = new SM2P256V1Curve(); + public final static BigInteger SM2_ECC_P = CURVE.getQ(); + public final static BigInteger SM2_ECC_A = CURVE.getA().toBigInteger(); + public final static BigInteger SM2_ECC_B = CURVE.getB().toBigInteger(); + public final static BigInteger SM2_ECC_N = CURVE.getOrder(); + public final static BigInteger SM2_ECC_H = CURVE.getCofactor(); + public final static BigInteger SM2_ECC_GX = new BigInteger( + "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16); + public final static BigInteger SM2_ECC_GY = new BigInteger( + "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16); + public static final ECPoint G_POINT = CURVE.createPoint(SM2_ECC_GX, SM2_ECC_GY); + public static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, G_POINT, + SM2_ECC_N, SM2_ECC_H); + + /** + * 生成ECC密钥对 + * + * @return ECC密钥对 + */ + public static AsymmetricCipherKeyPair generateKeyPairParameter( + ECDomainParameters domainParameters, SecureRandom random) { + ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParameters, + random); + ECKeyPairGenerator keyGen = new ECKeyPairGenerator(); + keyGen.init(keyGenerationParams); + return keyGen.generateKeyPair(); + } + + public static KeyPair generateKeyPair(ECDomainParameters domainParameters, SecureRandom random) + throws NoSuchProviderException, NoSuchAlgorithmException, + InvalidAlgorithmParameterException { + KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME); + ECParameterSpec parameterSpec = new ECParameterSpec(domainParameters.getCurve(), domainParameters.getG(), + domainParameters.getN(), domainParameters.getH()); + kpg.initialize(parameterSpec, random); + return kpg.generateKeyPair(); + } + + public static int getCurveLength(ECKeyParameters ecKey) { + return getCurveLength(ecKey.getParameters()); + } + + public static int getCurveLength(ECDomainParameters domainParams) { + return (domainParams.getCurve().getFieldSize() + 7) / 8; + } + + public static byte[] fixToCurveLengthBytes(int curveLength, byte[] src) { + if (src.length == curveLength) { + return src; + } + + byte[] result = new byte[curveLength]; + if (src.length > curveLength) { + System.arraycopy(src, src.length - result.length, result, 0, result.length); + } else { + System.arraycopy(src, 0, result, result.length - src.length, src.length); + } + return result; + } + + + + public static String getHexPubKey(BCECPublicKey publicKey) { + byte[] x = publicKey.getQ().getXCoord().getEncoded(); + byte[] y = publicKey.getQ().getYCoord().getEncoded(); + return Hex.toHexString(x) + Hex.toHexString(y); + } + + + + /** + * 根据EC私钥构造EC公钥 + * + * @param priKey ECC私钥参数对象 + * @return + */ + public static ECPublicKeyParameters buildECPublicKeyByPrivateKey(ECPrivateKeyParameters priKey) { + ECDomainParameters domainParameters = priKey.getParameters(); + ECPoint q = new FixedPointCombMultiplier().multiply(domainParameters.getG(), priKey.getD()); + return new ECPublicKeyParameters(q, domainParameters); + } + + + public static ECPublicKeyParameters createECPublicKeyParameters(byte[] xBytes, byte[] yBytes) { + return createECPublicKeyParameters(xBytes, yBytes, CURVE, DOMAIN_PARAMS); + } + + /** + * @param xBytes 十六进制形式的公钥x分量,如果是SM2算法,应该是32字节 + * @param yBytes 十六进制形式的公钥y分量,如果是SM2算法,应该是32字节 + * @param curve EC曲线参数,一般是固定的,如果是SM2算法的可参考{@link CURVE} + * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link DOMAIN_PARAMS} + * @return + */ + public static ECPublicKeyParameters createECPublicKeyParameters( + byte[] xBytes, byte[] yBytes, ECCurve curve, ECDomainParameters domainParameters) { + final byte uncompressedFlag = 0x04; + int curveLength = getCurveLength(domainParameters); + xBytes = fixToCurveLengthBytes(curveLength, xBytes); + yBytes = fixToCurveLengthBytes(curveLength, yBytes); + byte[] encodedPubKey = new byte[1 + xBytes.length + yBytes.length]; + encodedPubKey[0] = uncompressedFlag; + System.arraycopy(xBytes, 0, encodedPubKey, 1, xBytes.length); + System.arraycopy(yBytes, 0, encodedPubKey, 1 + xBytes.length, yBytes.length); + return new ECPublicKeyParameters(curve.decodePoint(encodedPubKey), domainParameters); + } + + public static ECPrivateKeyParameters convertPrivateKeyToParameters(BCECPrivateKey ecPriKey) { + ECParameterSpec parameterSpec = ecPriKey.getParameters(); + ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(), + parameterSpec.getN(), parameterSpec.getH()); + return new ECPrivateKeyParameters(ecPriKey.getD(), domainParameters); + } + + public static ECPublicKeyParameters convertPublicKeyToParameters(BCECPublicKey ecPubKey) { + ECParameterSpec parameterSpec = ecPubKey.getParameters(); + ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(), + parameterSpec.getN(), parameterSpec.getH()); + return new ECPublicKeyParameters(ecPubKey.getQ(), domainParameters); + } + + public static BCECPublicKey createPublicKeyFromSubjectPublicKeyInfo(SubjectPublicKeyInfo subPubInfo) + throws NoSuchProviderException, + NoSuchAlgorithmException, InvalidKeySpecException, IOException { + return BCECUtils.convertX509ToECPublicKey(subPubInfo.toASN1Primitive().getEncoded(ASN1Encoding.DER)); + } + + /** + * 将ECC私钥转换为PKCS8标准的字节流 + * + * @param priKey + * @param pubKey 可以为空,但是如果为空的话得到的结果OpenSSL可能解析不了 + * @return + */ + public static byte[] convertECPrivateKeyToPKCS8( + ECPrivateKeyParameters priKey, ECPublicKeyParameters pubKey) { + ECDomainParameters domainParams = priKey.getParameters(); + ECParameterSpec spec = new ECParameterSpec(domainParams.getCurve(), domainParams.getG(), + domainParams.getN(), domainParams.getH()); + BCECPublicKey publicKey = null; + if (pubKey != null) { + publicKey = new BCECPublicKey(ALGO_NAME_EC, pubKey, spec, + BouncyCastleProvider.CONFIGURATION); + } + BCECPrivateKey privateKey = new BCECPrivateKey(ALGO_NAME_EC, priKey, publicKey, + spec, BouncyCastleProvider.CONFIGURATION); + return privateKey.getEncoded(); + } + + /** + * 将PKCS8标准的私钥字节流转换为私钥对象 + * + * @param pkcs8Key + * @return + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException + * @throws InvalidKeySpecException + */ + public static BCECPrivateKey convertPKCS8ToECPrivateKey(byte[] pkcs8Key) + throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException { + PKCS8EncodedKeySpec peks = new PKCS8EncodedKeySpec(pkcs8Key); + KeyFactory kf = KeyFactory.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME); + return (BCECPrivateKey) kf.generatePrivate(peks); + } + + /** + * 将PKCS8标准的私钥字节流转换为PEM + * + * @param encodedKey + * @return + * @throws IOException + */ + public static String convertECPrivateKeyPKCS8ToPEM(byte[] encodedKey) throws IOException { + return convertEncodedDataToPEM(PEM_STRING_ECPRIVATEKEY, encodedKey); + } + + /** + * 将PEM格式的私钥转换为PKCS8标准字节流 + * + * @param pemString + * @return + * @throws IOException + */ + public static byte[] convertECPrivateKeyPEMToPKCS8(String pemString) throws IOException { + return convertPEMToEncodedData(pemString); + } + + /** + * 将ECC私钥转换为SEC1标准的字节流 + * openssl d2i_ECPrivateKey函数要求的DER编码的私钥也是SEC1标准的, + * 这个工具函数的主要目的就是为了能生成一个openssl可以直接“识别”的ECC私钥. + * 相对RSA私钥的PKCS1标准,ECC私钥的标准为SEC1 + * + * @param priKey + * @param pubKey + * @return + * @throws IOException + */ + public static byte[] convertECPrivateKeyToSEC1( + ECPrivateKeyParameters priKey, ECPublicKeyParameters pubKey) throws IOException { + byte[] pkcs8Bytes = convertECPrivateKeyToPKCS8(priKey, pubKey); + PrivateKeyInfo pki = PrivateKeyInfo.getInstance(pkcs8Bytes); + ASN1Encodable encodable = pki.parsePrivateKey(); + ASN1Primitive primitive = encodable.toASN1Primitive(); + byte[] sec1Bytes = primitive.getEncoded(); + return sec1Bytes; + } + + + /** + * 将ECC公钥对象转换为X509标准的字节流 + * + * @param pubKey + * @return + */ + public static byte[] convertECPublicKeyToX509(ECPublicKeyParameters pubKey) { + ECDomainParameters domainParams = pubKey.getParameters(); + ECParameterSpec spec = new ECParameterSpec(domainParams.getCurve(), domainParams.getG(), + domainParams.getN(), domainParams.getH()); + BCECPublicKey publicKey = new BCECPublicKey(ALGO_NAME_EC, pubKey, spec, + BouncyCastleProvider.CONFIGURATION); + return publicKey.getEncoded(); + } + + /** + * 将X509标准的公钥字节流转为公钥对象 + * + * @param x509Bytes + * @return + * @throws NoSuchProviderException + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + */ + public static BCECPublicKey convertX509ToECPublicKey(byte[] x509Bytes) throws NoSuchProviderException, + NoSuchAlgorithmException, InvalidKeySpecException { + X509EncodedKeySpec eks = new X509EncodedKeySpec(x509Bytes); + KeyFactory kf = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME); + return (BCECPublicKey) kf.generatePublic(eks); + } + + /** + * 将X509标准的公钥字节流转为PEM + * + * @param encodedKey + * @return + * @throws IOException + */ + public static String convertECPublicKeyX509ToPEM(byte[] encodedKey) throws IOException { + return convertEncodedDataToPEM(PEM_STRING_PUBLIC, encodedKey); + } + + /** + * 将PEM格式的公钥转为X509标准的字节流 + * + * @param pemString + * @return + * @throws IOException + */ + public static byte[] convertECPublicKeyPEMToX509(String pemString) throws IOException { + return convertPEMToEncodedData(pemString); + } + + /** + * copy from BC + * + * @param genSpec + * @return + */ + public static X9ECParameters getDomainParametersFromGenSpec(ECGenParameterSpec genSpec) { + return getDomainParametersFromName(genSpec.getName()); + } + + /** + * copy from BC + * + * @param curveName + * @return + */ + public static X9ECParameters getDomainParametersFromName(String curveName) { + X9ECParameters domainParameters; + try { + if (curveName.charAt(0) >= '0' && curveName.charAt(0) <= '2') { + ASN1ObjectIdentifier oidID = new ASN1ObjectIdentifier(curveName); + domainParameters = ECUtil.getNamedCurveByOid(oidID); + } else { + if (curveName.indexOf(' ') > 0) { + curveName = curveName.substring(curveName.indexOf(' ') + 1); + domainParameters = ECUtil.getNamedCurveByName(curveName); + } else { + domainParameters = ECUtil.getNamedCurveByName(curveName); + } + } + } catch (IllegalArgumentException ex) { + domainParameters = ECUtil.getNamedCurveByName(curveName); + } + return domainParameters; + } + + /** + * copy from BC + * + * @param ecSpec + * @param withCompression + * @return + */ + public static X962Parameters getDomainParametersFromName( + java.security.spec.ECParameterSpec ecSpec, boolean withCompression) { + X962Parameters params; + + if (ecSpec instanceof ECNamedCurveSpec) { + ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec) ecSpec).getName()); + if (curveOid == null) { + curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec) ecSpec).getName()); + } + params = new X962Parameters(curveOid); + } else if (ecSpec == null) { + params = new X962Parameters(DERNull.INSTANCE); + } else { + ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); + + X9ECParameters ecP = new X9ECParameters( + curve, + new X9ECPoint(EC5Util.convertPoint(curve, ecSpec.getGenerator()), withCompression), + ecSpec.getOrder(), + BigInteger.valueOf(ecSpec.getCofactor()), + ecSpec.getCurve().getSeed()); + + //// 如果是1.62或更低版本的bcprov-jdk15on应该使用以下这段代码,因为高版本的EC5Util.convertPoint没有向下兼容 + /* + X9ECParameters ecP = new X9ECParameters( + curve, + EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression), + ecSpec.getOrder(), + BigInteger.valueOf(ecSpec.getCofactor()), + ecSpec.getCurve().getSeed()); + */ + + params = new X962Parameters(ecP); + } + + return params; + } + + private static String convertEncodedDataToPEM(String type, byte[] encodedData) throws IOException { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + PemWriter pWrt = new PemWriter(new OutputStreamWriter(bOut)); + try { + PemObject pemObj = new PemObject(type, encodedData); + pWrt.writeObject(pemObj); + } finally { + pWrt.close(); + } + return new String(bOut.toByteArray()); + } + + private static byte[] convertPEMToEncodedData(String pemString) throws IOException { + ByteArrayInputStream bIn = new ByteArrayInputStream(pemString.getBytes()); + PemReader pRdr = new PemReader(new InputStreamReader(bIn)); + try { + PemObject pemObject = pRdr.readPemObject(); + return pemObject.getContent(); + } finally { + pRdr.close(); + } + } +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/service/SM2PreprocessSigner.java b/ukey-tool/src/main/java/com/cisd/ukey/service/SM2PreprocessSigner.java new file mode 100644 index 0000000..85f601b --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/service/SM2PreprocessSigner.java @@ -0,0 +1,294 @@ +package com.cisd.ukey.service; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SM3Digest; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECKeyParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.crypto.params.ParametersWithID; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.crypto.signers.DSAKCalculator; +import org.bouncycastle.crypto.signers.RandomDSAKCalculator; +import org.bouncycastle.math.ec.ECAlgorithms; +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECMultiplier; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.FixedPointCombMultiplier; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; + +import java.io.IOException; +import java.math.BigInteger; + +/** + * 有的国密需求是用户可以自己做预处理,签名验签只是对预处理的结果进行签名和验签 + */ +public class SM2PreprocessSigner implements ECConstants { + private static final int DIGEST_LENGTH = 32; // bytes + + private final DSAKCalculator kCalculator = new RandomDSAKCalculator(); + private Digest digest = null; + + private ECDomainParameters ecParams; + private ECPoint pubPoint; + private ECKeyParameters ecKey; + private byte[] userID; + + /** + * 初始化 + * + * @param forSigning true表示用于签名,false表示用于验签 + * @param param + */ + public void init(boolean forSigning, CipherParameters param) { + init(forSigning, new SM3Digest(), param); + } + + /** + * 初始化 + * + * @param forSigning true表示用于签名,false表示用于验签 + * @param digest SM2算法的话,一般是采用SM3摘要算法 + * @param param + * @throws RuntimeException + */ + public void init(boolean forSigning, Digest digest, CipherParameters param) throws RuntimeException { + CipherParameters baseParam; + + if (digest.getDigestSize() != DIGEST_LENGTH) { + throw new RuntimeException("Digest size must be " + DIGEST_LENGTH); + } + this.digest = digest; + + if (param instanceof ParametersWithID) { + baseParam = ((ParametersWithID) param).getParameters(); + userID = ((ParametersWithID) param).getID(); + } else { + baseParam = param; + userID = Hex.decode("31323334353637383132333435363738"); // the default value + } + + if (forSigning) { + if (baseParam instanceof ParametersWithRandom) { + ParametersWithRandom rParam = (ParametersWithRandom) baseParam; + + ecKey = (ECKeyParameters) rParam.getParameters(); + ecParams = ecKey.getParameters(); + kCalculator.init(ecParams.getN(), rParam.getRandom()); + } else { + ecKey = (ECKeyParameters) baseParam; + ecParams = ecKey.getParameters(); + kCalculator.init(ecParams.getN(), CryptoServicesRegistrar.getSecureRandom()); + } + pubPoint = createBasePointMultiplier().multiply(ecParams.getG(), ((ECPrivateKeyParameters) ecKey).getD()).normalize(); + } else { + ecKey = (ECKeyParameters) baseParam; + ecParams = ecKey.getParameters(); + pubPoint = ((ECPublicKeyParameters) ecKey).getQ(); + } + } + + /** + * 预处理,辅助方法 + * ZA=H256(ENT LA ∥ IDA ∥ a ∥ b ∥ xG ∥yG ∥ xA ∥ yA)。 + * M=ZA ∥ M; + * e = Hv(M) + * + * @return + */ + public byte[] preprocess(byte[] m) { + return preprocess(m, 0, m.length); + } + + + public byte[] preprocess(byte[] m, int off, int len) { + byte[] z = getZ(userID); + digest.update(z, 0, z.length); + digest.update(m, off, len); + byte[] eHash = new byte[DIGEST_LENGTH]; + digest.doFinal(eHash, 0); + return eHash; + } + + public boolean verifySignature(byte[] eHash, byte[] signature) { + try { + BigInteger[] rs = derDecode(signature); + if (rs != null) { + return verifySignature(eHash, rs[0], rs[1]); + } + } catch (IOException e) { + } + + return false; + } + + public void reset() { + digest.reset(); + } + + public byte[] generateSignature(byte[] eHash) throws CryptoException { + BigInteger n = ecParams.getN(); + BigInteger e = calculateE(eHash); + BigInteger d = ((ECPrivateKeyParameters) ecKey).getD(); + + BigInteger r, s; + + ECMultiplier basePointMultiplier = createBasePointMultiplier(); + + // 5.2.1 Draft RFC: SM2 Public Key Algorithms + do // generate s + { + BigInteger k; + do // generate r + { + // A3 + k = kCalculator.nextK(); + + // A4 + ECPoint p = basePointMultiplier.multiply(ecParams.getG(), k).normalize(); + + // A5 + r = e.add(p.getAffineXCoord().toBigInteger()).mod(n); + } + while (r.equals(ZERO) || r.add(k).equals(n)); + + // A6 + BigInteger dPlus1ModN = d.add(ONE).modInverse(n); + + s = k.subtract(r.multiply(d)).mod(n); + s = dPlus1ModN.multiply(s).mod(n); + } + while (s.equals(ZERO)); + + // A7 + try { + return derEncode(r, s); + } catch (IOException ex) { + throw new CryptoException("unable to encode signature: " + ex.getMessage(), ex); + } + } + + private boolean verifySignature(byte[] eHash, BigInteger r, BigInteger s) { + BigInteger n = ecParams.getN(); + + // 5.3.1 Draft RFC: SM2 Public Key Algorithms + // B1 + if (r.compareTo(ONE) < 0 || r.compareTo(n) >= 0) { + return false; + } + + // B2 + if (s.compareTo(ONE) < 0 || s.compareTo(n) >= 0) { + return false; + } + + // B3 eHash + + // B4 + BigInteger e = calculateE(eHash); + + // B5 + BigInteger t = r.add(s).mod(n); + if (t.equals(ZERO)) { + return false; + } + + // B6 + ECPoint q = ((ECPublicKeyParameters) ecKey).getQ(); + ECPoint x1y1 = ECAlgorithms.sumOfTwoMultiplies(ecParams.getG(), s, q, t).normalize(); + if (x1y1.isInfinity()) { + return false; + } + + // B7 + BigInteger expectedR = e.add(x1y1.getAffineXCoord().toBigInteger()).mod(n); + + return expectedR.equals(r); + } + + private byte[] digestDoFinal() { + byte[] result = new byte[digest.getDigestSize()]; + digest.doFinal(result, 0); + + reset(); + + return result; + } + + private byte[] getZ(byte[] userID) { + digest.reset(); + + addUserID(digest, userID); + + addFieldElement(digest, ecParams.getCurve().getA()); + addFieldElement(digest, ecParams.getCurve().getB()); + addFieldElement(digest, ecParams.getG().getAffineXCoord()); + addFieldElement(digest, ecParams.getG().getAffineYCoord()); + addFieldElement(digest, pubPoint.getAffineXCoord()); + addFieldElement(digest, pubPoint.getAffineYCoord()); + + byte[] result = new byte[digest.getDigestSize()]; + + digest.doFinal(result, 0); + + return result; + } + + private void addUserID(Digest digest, byte[] userID) { + int len = userID.length * 8; + digest.update((byte) (len >> 8 & 0xFF)); + digest.update((byte) (len & 0xFF)); + digest.update(userID, 0, userID.length); + } + + private void addFieldElement(Digest digest, ECFieldElement v) { + byte[] p = v.getEncoded(); + digest.update(p, 0, p.length); + } + + protected ECMultiplier createBasePointMultiplier() { + return new FixedPointCombMultiplier(); + } + + protected BigInteger calculateE(byte[] message) { + return new BigInteger(1, message); + } + + protected BigInteger[] derDecode(byte[] encoding) + throws IOException { + ASN1Sequence seq = ASN1Sequence.getInstance(ASN1Primitive.fromByteArray(encoding)); + if (seq.size() != 2) { + return null; + } + + BigInteger r = ASN1Integer.getInstance(seq.getObjectAt(0)).getValue(); + BigInteger s = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue(); + + byte[] expectedEncoding = derEncode(r, s); + if (!Arrays.constantTimeAreEqual(expectedEncoding, encoding)) { + return null; + } + + return new BigInteger[]{r, s}; + } + + protected byte[] derEncode(BigInteger r, BigInteger s) + throws IOException { + + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(new ASN1Integer(r)); + v.add(new ASN1Integer(s)); + return new DERSequence(v).getEncoded(ASN1Encoding.DER); + } +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/service/SkfService.java b/ukey-tool/src/main/java/com/cisd/ukey/service/SkfService.java new file mode 100644 index 0000000..0f0a355 --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/service/SkfService.java @@ -0,0 +1,306 @@ +package com.cisd.ukey.service; + +import com.cisd.ukey.config.SkfProperties; +import com.cisd.ukey.jna.DevInfo; +import com.cisd.ukey.jna.EccPublicKeyBlob; +import com.cisd.ukey.jna.EccSignatureBlob; +import com.cisd.ukey.jna.FileAttribute; +import com.cisd.ukey.jna.SkfLibrary; +import com.sun.jna.Memory; +import com.sun.jna.Pointer; +import com.sun.jna.ptr.IntByReference; +import org.bouncycastle.crypto.CipherParameters; +import org.springframework.stereotype.Service; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; + +@Service +public class SkfService { + + public static final int ADMIN_TYPE = 0; + public static final int USER_TYPE = 1; + private static final int SAR_APPLICATION_NOT_EXISTS = 0x0A00002E; + private static final int SAR_FILE_ALREADY_EXIST = 0x0A00002F; + private static final int SAR_FILE_NOT_EXIST = 0x0A000031; + private static final int FILE_ACCESS_ANYONE = 0x000000FF; + + private final SkfSessionTemplate sessionTemplate; + private final SkfProperties properties; + + public SkfService(SkfSessionTemplate sessionTemplate, SkfProperties properties) { + this.sessionTemplate = sessionTemplate; + this.properties = properties; + } + + public List listDevices(boolean presentOnly) { + SkfLibrary lib = sessionTemplate.library(); + IntByReference size = new IntByReference(); + sessionTemplate.ensureSuccess("SKF_EnumDev", lib.SKF_EnumDev(presentOnly ? 1 : 0, Pointer.NULL, size)); + Memory buffer = new Memory(size.getValue()); + sessionTemplate.ensureSuccess("SKF_EnumDev", lib.SKF_EnumDev(presentOnly ? 1 : 0, buffer, size)); + List result = new ArrayList<>(); + for (String deviceName : parseMultiString(buffer.getByteArray(0, size.getValue()))) { + result.add(sessionTemplate.withDevice("listDevices", deviceName, (nativeLib, devHandle) -> { + DevInfo devInfo = new DevInfo(); + sessionTemplate.ensureSuccess("SKF_GetDevInfo", nativeLib.SKF_GetDevInfo(devHandle, devInfo)); + devInfo.read(); + return new DeviceInfo( + deviceName, + trimCString(devInfo.serialNumber), + trimCString(devInfo.label), + trimCString(devInfo.manufacturer), + trimCString(devInfo.issuer) + ); + })); + } + return result; + } + + public PinVerifyResult verifyPin(String deviceName, int pinType, String pin) { + return sessionTemplate.withApplication("verifyPin", deviceName, properties.getApplicationName(), (lib, devHandle, appHandle) -> { + IntByReference retryCount = new IntByReference(); + sessionTemplate.ensureSuccess("SKF_VerifyPIN", + lib.SKF_VerifyPIN(appHandle, pinType, sessionTemplate.cString(pin), retryCount)); + return pinInfo(lib, appHandle, pinType, retryCount.getValue()); + }); + } + + public PinVerifyResult changePin(String deviceName, int pinType, String oldPin, String newPin) { + return sessionTemplate.withApplication("changePin", deviceName, properties.getApplicationName(), (lib, devHandle, appHandle) -> { + IntByReference retryCount = new IntByReference(); + sessionTemplate.ensureSuccess("SKF_ChangePIN", + lib.SKF_ChangePIN(appHandle, pinType, sessionTemplate.cString(oldPin), sessionTemplate.cString(newPin), retryCount)); + return pinInfo(lib, appHandle, pinType, retryCount.getValue()); + }); + } + + public PinVerifyResult unblockPin(String deviceName, String adminPin, String newUserPin) { + verifyPin(deviceName, ADMIN_TYPE, adminPin); + return sessionTemplate.withApplication("resetPin", deviceName, properties.getApplicationName(), (lib, devHandle, appHandle) -> { + IntByReference retryCount = new IntByReference(); + sessionTemplate.ensureSuccess("SKF_UnblockPIN", + lib.SKF_UnblockPIN(appHandle, sessionTemplate.cString(adminPin), sessionTemplate.cString(newUserPin), retryCount)); + return pinInfo(lib, appHandle, USER_TYPE, retryCount.getValue()); + }); + } + + public List listFiles(String deviceName) { + return sessionTemplate.withApplication("listFiles", deviceName, properties.getApplicationName(), (lib, devHandle, appHandle) -> { + IntByReference size = new IntByReference(); + sessionTemplate.ensureSuccess("SKF_EnumFiles", lib.SKF_EnumFiles(appHandle, Pointer.NULL, size)); + Memory buffer = new Memory(size.getValue()); + sessionTemplate.ensureSuccess("SKF_EnumFiles", lib.SKF_EnumFiles(appHandle, buffer, size)); + List result = new ArrayList<>(); + for (String fileName : parseMultiString(buffer.getByteArray(0, size.getValue()))) { + FileAttribute attribute = new FileAttribute(); + sessionTemplate.ensureSuccess("SKF_GetFileInfo", + lib.SKF_GetFileInfo(appHandle, sessionTemplate.cString(fileName), attribute)); + attribute.read(); + result.add(new FileInfo(fileName, attribute.fileSize, attribute.readRights, attribute.writeRights)); + } + return result; + }); + } + + public FileContent readFile(String deviceName, String fileName) { + return sessionTemplate.withApplication("readFile", deviceName, properties.getApplicationName(), (lib, devHandle, appHandle) -> { + FileAttribute attribute = new FileAttribute(); + sessionTemplate.ensureSuccess("SKF_GetFileInfo", + lib.SKF_GetFileInfo(appHandle, sessionTemplate.cString(fileName), attribute)); + attribute.read(); + + int readSize = attribute.fileSize; + if (readSize <= 0) { + return new FileContent(fileName, 0, ""); + } + + Memory buffer = new Memory(readSize); + IntByReference outLen = new IntByReference(readSize); + sessionTemplate.ensureSuccess("SKF_ReadFile", + lib.SKF_ReadFile(appHandle, sessionTemplate.cString(fileName), 0, readSize, buffer, outLen)); + byte[] data = buffer.getByteArray(0, outLen.getValue()); + return new FileContent(fileName, outLen.getValue(), new String(data, StandardCharsets.UTF_8)); + }); + } + + public FileContent writeFile(String deviceName, String fileName, String content) { + byte[] data = content.getBytes(StandardCharsets.UTF_8); + return sessionTemplate.withApplication("writeFile", deviceName, properties.getApplicationName(), (lib, devHandle, appHandle) -> { + FileAttribute attribute = new FileAttribute(); + int infoRc = lib.SKF_GetFileInfo(appHandle, sessionTemplate.cString(fileName), attribute); + if (infoRc == 0) { + throw new com.cisd.ukey.exception.SkfException("SKF_WriteFile", SAR_FILE_ALREADY_EXIST, "文件已被写入"); + } + if (infoRc != SAR_FILE_NOT_EXIST) { + sessionTemplate.ensureSuccess("SKF_GetFileInfo", infoRc); + } + + sessionTemplate.ensureSuccess("SKF_CreateFile", + lib.SKF_CreateFile(appHandle, sessionTemplate.cString(fileName), data.length, FILE_ACCESS_ANYONE, FILE_ACCESS_ANYONE)); + sessionTemplate.ensureSuccess("SKF_WriteFile", + lib.SKF_WriteFile(appHandle, sessionTemplate.cString(fileName), 0, data, data.length)); + return new FileContent(fileName, data.length, content); + }); + } + + public void deleteFile(String deviceName, String fileName) { + sessionTemplate.withApplication("deleteFile", deviceName, properties.getApplicationName(), (lib, devHandle, appHandle) -> { + sessionTemplate.ensureSuccess("SKF_DeleteFile", lib.SKF_DeleteFile(appHandle, sessionTemplate.cString(fileName))); + return null; + }); + } + + public void deleteApplication(String deviceName) { + sessionTemplate.withAuthenticatedDevice("deleteApplication", deviceName, (lib, devHandle) -> { + int rc = lib.SKF_DeleteApplication(devHandle, sessionTemplate.cString(properties.getApplicationName())); + if (rc != 0 && rc != SAR_APPLICATION_NOT_EXISTS) { + sessionTemplate.ensureSuccess("SKF_DeleteApplication", rc); + } + return null; + }); + } + + public EccPublicKeyResult exportEccPublicKey(String deviceName, boolean signFlag) { + return sessionTemplate.withContainer("exportEccPublicKey", deviceName, properties.getApplicationName(), properties.getContainerName(), + (lib, devHandle, appHandle, containerHandle) -> { + IntByReference blobLen = new IntByReference(); + sessionTemplate.ensureSuccess("SKF_ExportPublicKey", + lib.SKF_ExportPublicKey(containerHandle, signFlag ? 1 : 0, Pointer.NULL, blobLen)); + Memory blob = new Memory(blobLen.getValue()); + sessionTemplate.ensureSuccess("SKF_ExportPublicKey", + lib.SKF_ExportPublicKey(containerHandle, signFlag ? 1 : 0, blob, blobLen)); + byte[] raw = blob.getByteArray(0, blobLen.getValue()); + return new EccPublicKeyResult( + signFlag, + Base64.getEncoder().encodeToString(raw) + ); + }); + } + + public EccSignatureResult eccSign(String deviceName, String plainText) { + EccPublicKeyResult eccPublicKeyResult = exportEccPublicKey(deviceName, true); + byte[] data = preprocessForSm2Sign(eccPublicKeyResult.pubKey, plainText); + return sessionTemplate.withContainer("eccSign", deviceName, properties.getApplicationName(), properties.getContainerName(), + (lib, devHandle, appHandle, containerHandle) -> { + EccSignatureBlob signature = new EccSignatureBlob(); + sessionTemplate.ensureSuccess("SKF_ECCSignData", + lib.SKF_ECCSignData(containerHandle, data, data.length, signature)); + signature.read(); + return new EccSignatureResult( + Base64.getEncoder().encodeToString(signature.getPointer().getByteArray(0, signature.size())), + toHex(signature.r), + toHex(signature.s) + ); + }); + } + + public VerifyResult eccVerify(String deviceName, String pubKey, String plainText, String signatureBase64) { + byte[] data = preprocessForSm2Sign(pubKey, plainText); + byte[] signatureBytes = Base64.getDecoder().decode(signatureBase64); + byte[] pubkeyBytes = Base64.getDecoder().decode(pubKey); + return sessionTemplate.withContainer("eccVerify", deviceName, properties.getApplicationName(), properties.getContainerName(), + (lib, devHandle, appHandle, containerHandle) -> { + EccPublicKeyBlob publicKey = new EccPublicKeyBlob(); + publicKey.getPointer().write(0, pubkeyBytes, 0, Math.min(pubkeyBytes.length, publicKey.size())); + publicKey.read(); + + EccSignatureBlob signature = new EccSignatureBlob(); + signature.write(); + signature.getPointer().write(0, signatureBytes, 0, Math.min(signatureBytes.length, signature.size())); + signature.read(); + + sessionTemplate.ensureSuccess("SKF_ECCVerify", + lib.SKF_ECCVerify(devHandle, publicKey, data, data.length, signature)); + return new VerifyResult(true); + }); + } + + private byte[] preprocessForSm2Sign(String publicKey, String plainText) { + byte[] source = plainText.getBytes(StandardCharsets.UTF_8); + byte[] pubBytes = Base64.getDecoder().decode(publicKey); + byte[] x = Arrays.copyOfRange(pubBytes, 36, 68); + byte[] y = Arrays.copyOfRange(pubBytes, 100, 132); + + CipherParameters parameters = BCECUtils.createECPublicKeyParameters(x, y); + SM2PreprocessSigner signer = new SM2PreprocessSigner(); + signer.init(false, parameters); + byte[] preHash = signer.preprocess(source); + return preHash; + } + + private PinVerifyResult pinInfo(SkfLibrary lib, Pointer applicationHandle, int pinType, int retryCountFromCall) { + IntByReference maxRetryCount = new IntByReference(); + IntByReference remainRetryCount = new IntByReference(); + IntByReference defaultPin = new IntByReference(); + sessionTemplate.ensureSuccess("SKF_GetPINInfo", + lib.SKF_GetPINInfo(applicationHandle, pinType, maxRetryCount, remainRetryCount, defaultPin)); + return new PinVerifyResult(pinType, maxRetryCount.getValue(), remainRetryCount.getValue(), defaultPin.getValue() != 0, retryCountFromCall); + } + + private List parseMultiString(byte[] bytes) { + List result = new ArrayList<>(); + int start = 0; + for (int i = 0; i < bytes.length; i++) { + if (bytes[i] != 0) { + continue; + } + if (i == start) { + break; + } + result.add(new String(bytes, start, i - start, StandardCharsets.UTF_8)); + start = i + 1; + } + return result; + } + + private String trimCString(byte[] bytes) { + int len = 0; + while (len < bytes.length && bytes[len] != 0) { + len++; + } + return new String(bytes, 0, len, StandardCharsets.UTF_8).trim(); + } + + private String toHex(byte[] bytes) { + StringBuilder builder = new StringBuilder(bytes.length * 2); + for (byte value : bytes) { + builder.append(String.format("%02X", value)); + } + return builder.toString(); + } + + private byte[] fromHex(String hex, int maxSize) { + byte[] result = new byte[maxSize]; + int usable = Math.min(hex.length() / 2, maxSize); + for (int i = 0; i < usable; i++) { + int index = i * 2; + result[i] = (byte) Integer.parseInt(hex.substring(index, index + 2), 16); + } + return result; + } + + public record DeviceInfo(String deviceName, String serialNumber, String label, String manufacturer, String issuer) { + } + + public record PinVerifyResult(int pinType, int maxRetryCount, int remainRetryCount, boolean defaultPin, int retryCountFromCall) { + } + + public record FileInfo(String fileName, int fileSize, int readRights, int writeRights) { + } + + public record FileContent(String fileName, int size, String content) { + } + + public record EccPublicKeyResult(boolean signFlag, String pubKey) { + } + + public record EccSignatureResult(String signatureBase64, String rHex, String sHex) { + } + + public record VerifyResult(boolean success) { + } +} diff --git a/ukey-tool/src/main/java/com/cisd/ukey/service/SkfSessionTemplate.java b/ukey-tool/src/main/java/com/cisd/ukey/service/SkfSessionTemplate.java new file mode 100644 index 0000000..112c919 --- /dev/null +++ b/ukey-tool/src/main/java/com/cisd/ukey/service/SkfSessionTemplate.java @@ -0,0 +1,297 @@ +package com.cisd.ukey.service; + +import com.cisd.ukey.config.SkfProperties; +import com.cisd.ukey.exception.SkfException; +import com.cisd.ukey.jna.BlockCipherParam; +import com.cisd.ukey.jna.DevInfo; +import com.cisd.ukey.jna.EccPublicKeyBlob; +import com.cisd.ukey.jna.SkfLibrary; +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.NativeLibrary; +import com.sun.jna.Pointer; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; +import org.springframework.stereotype.Component; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +@Component +public class SkfSessionTemplate { + + private static final int SAR_APPLICATION_NOT_EXISTS = 0x0A00002E; + private static final int SAR_CONTAINER_NOT_EXISTS = 0x0A000036; + private static final int SGD_SM1 = 0x00000100; + private static final int SGD_SM4 = 0x00000400; + private static final int SGD_AES128 = 0x00000800; + private static final int SGD_AES192 = 0x00000810; + private static final int SGD_AES256 = 0x00000820; + private static final int SGD_DES = 0x00001000; + private static final int SGD_D3DES = 0x00001010; + private static final int SGD_T3DES = 0x00001020; + private static final int SGD_SM7 = 0x00002000; + + @FunctionalInterface + public interface DeviceCallback { + T apply(SkfLibrary lib, Pointer devHandle); + } + + @FunctionalInterface + public interface ApplicationCallback { + T apply(SkfLibrary lib, Pointer devHandle, Pointer applicationHandle); + } + + @FunctionalInterface + public interface ContainerCallback { + T apply(SkfLibrary lib, Pointer devHandle, Pointer applicationHandle, Pointer containerHandle); + } + + private final Object lock = new Object(); + private final SkfProperties properties; + private volatile SkfLibrary library; + + public SkfSessionTemplate(SkfProperties properties) { + this.properties = properties; + } + + public T withDevice(String apiName, String deviceName, DeviceCallback callback) { + synchronized (lock) { + SkfLibrary lib = ensureLibraryLoaded(); + Pointer devHandle = connectDevice(apiName, lib, deviceName); + try { + return callback.apply(lib, devHandle); + } finally { + disconnectQuietly(lib, devHandle); + } + } + } + + public T withApplication(String apiName, String deviceName, String applicationName, ApplicationCallback callback) { + return withDevice(apiName, deviceName, (lib, devHandle) -> { + Pointer applicationHandle = ensureApplication(apiName, lib, devHandle, applicationName); + try { + return callback.apply(lib, devHandle, applicationHandle); + } finally { + closeApplicationQuietly(lib, applicationHandle); + } + }); + } + + public T withAuthenticatedDevice(String apiName, String deviceName, DeviceCallback callback) { + return withDevice(apiName, deviceName, (lib, devHandle) -> { + authenticateDevice(apiName, lib, devHandle); + return callback.apply(lib, devHandle); + }); + } + + public T withContainer(String apiName, String deviceName, String applicationName, String containerName, + ContainerCallback callback) { + return withApplication(apiName, deviceName, applicationName, (lib, devHandle, applicationHandle) -> { + Pointer containerHandle = ensureContainer(apiName, lib, applicationHandle, containerName); + try { + return callback.apply(lib, devHandle, applicationHandle, containerHandle); + } finally { + closeContainerQuietly(lib, containerHandle); + } + }); + } + + public SkfLibrary library() { + return ensureLibraryLoaded(); + } + + public void ensureSuccess(String apiName, int retCode) { + if (retCode != 0) { + throw new SkfException(apiName, retCode, apiName + " failed, ret=0x" + Integer.toHexString(retCode).toUpperCase()); + } + } + + public byte[] cString(String value) { + return (value + "\0").getBytes(StandardCharsets.UTF_8); + } + + private SkfLibrary ensureLibraryLoaded() { + SkfLibrary cached = library; + if (cached != null) { + return cached; + } + synchronized (this) { + if (library != null) { + return library; + } + Map options = new HashMap<>(); + options.put(Library.OPTION_STRING_ENCODING, "UTF-8"); + try { + Path extractDir = Path.of(properties.getLibDir()).toAbsolutePath().normalize(); + Path libPath = extractDir.resolve(properties.getVendorLibName()).toAbsolutePath().normalize(); + NativeLibrary.addSearchPath(stripExtension(properties.getVendorLibName()), extractDir.toString()); + library = Native.load(libPath.toString(), SkfLibrary.class, options); + } catch (UnsatisfiedLinkError ex) { + throw new SkfException("Native.load", "load SKF native library failed", ex); + } + return library; + } + } + + private String stripExtension(String fileName) { + int index = fileName.lastIndexOf('.'); + return index > 0 ? fileName.substring(0, index) : fileName; + } + + private Pointer connectDevice(String apiName, SkfLibrary lib, String deviceName) { + PointerByReference ref = new PointerByReference(); + ensureSuccess(apiName + ":SKF_ConnectDev", lib.SKF_ConnectDev(cString(deviceName), ref)); + return ref.getValue(); + } + + private Pointer openApplication(String apiName, SkfLibrary lib, Pointer devHandle, String applicationName) { + PointerByReference ref = new PointerByReference(); + ensureSuccess(apiName + ":SKF_OpenApplication", lib.SKF_OpenApplication(devHandle, cString(applicationName), ref)); + return ref.getValue(); + } + + private Pointer ensureApplication(String apiName, SkfLibrary lib, Pointer devHandle, String applicationName) { + PointerByReference ref = new PointerByReference(); + int openRc = lib.SKF_OpenApplication(devHandle, cString(applicationName), ref); + if (openRc == 0) { + return ref.getValue(); + } + if (openRc != SAR_APPLICATION_NOT_EXISTS) { + ensureSuccess(apiName + ":SKF_OpenApplication", openRc); + } + + authenticateDevice(apiName, lib, devHandle); + int createRc = lib.SKF_CreateApplication( + devHandle, + cString(applicationName), + cString(properties.getInitAdminPin()), + properties.getAdminPinRetryCount(), + cString(properties.getInitUserPin()), + properties.getUserPinRetryCount(), + properties.getCreateFileRights(), + ref + ); + ensureSuccess(apiName + ":SKF_CreateApplication", createRc); + return ref.getValue(); + } + + private Pointer openContainer(String apiName, SkfLibrary lib, Pointer applicationHandle, String containerName) { + PointerByReference ref = new PointerByReference(); + ensureSuccess(apiName + ":SKF_OpenContainer", lib.SKF_OpenContainer(applicationHandle, cString(containerName), ref)); + return ref.getValue(); + } + + private Pointer ensureContainer(String apiName, SkfLibrary lib, Pointer applicationHandle, String containerName) { + PointerByReference ref = new PointerByReference(); + int openRc = lib.SKF_OpenContainer(applicationHandle, cString(containerName), ref); + if (openRc == 0) { + return ref.getValue(); + } + if (openRc != SAR_CONTAINER_NOT_EXISTS) { + ensureSuccess(apiName + ":SKF_OpenContainer", openRc); + } + + int createRc = lib.SKF_CreateContainer(applicationHandle, cString(containerName), ref); + ensureSuccess(apiName + ":SKF_CreateContainer", createRc); + EccPublicKeyBlob publicKeyBlob = new EccPublicKeyBlob(); + ensureSuccess(apiName + ":SKF_GenECCKeyPair", + lib.SKF_GenECCKeyPair(ref.getValue(), properties.getSignKeyAlgId(), publicKeyBlob)); + return ref.getValue(); + } + + private void authenticateDevice(String apiName, SkfLibrary lib, Pointer devHandle) { + DevInfo devInfo = new DevInfo(); + ensureSuccess(apiName + ":SKF_GetDevInfo", lib.SKF_GetDevInfo(devHandle, devInfo)); + devInfo.read(); + + byte[] random = new byte[16]; + ensureSuccess(apiName + ":SKF_GenRandom", lib.SKF_GenRandom(devHandle, random, random.length)); + + PointerByReference keyHandleRef = new PointerByReference(); + byte[] authKey = normalizeAuthKey(properties.getDeviceAuthKey(), devInfo.devAuthAlgId); + ensureSuccess(apiName + ":SKF_SetSymmKey", + lib.SKF_SetSymmKey(devHandle, authKey, devInfo.devAuthAlgId, keyHandleRef)); + + Pointer keyHandle = keyHandleRef.getValue(); + try { + BlockCipherParam cipherParam = new BlockCipherParam(); + cipherParam.ivLen = 0; + cipherParam.paddingType = 0; + cipherParam.feedBitLen = 0; + cipherParam.write(); + + ensureSuccess(apiName + ":SKF_EncryptInit", lib.SKF_EncryptInit(keyHandle, cipherParam)); + + byte[] encrypted = new byte[random.length + 32]; + IntByReference encryptedLen = new IntByReference(encrypted.length); + ensureSuccess(apiName + ":SKF_Encrypt", + lib.SKF_Encrypt(keyHandle, random, random.length, encrypted, encryptedLen)); + + byte[] authData = new byte[encryptedLen.getValue()]; + System.arraycopy(encrypted, 0, authData, 0, encryptedLen.getValue()); + ensureSuccess(apiName + ":SKF_DevAuth", lib.SKF_DevAuth(devHandle, authData, authData.length)); + } finally { + closeHandleQuietly(lib, keyHandle); + } + } + + private byte[] normalizeAuthKey(String configuredKey, int algId) { + byte[] raw = configuredKey.getBytes(StandardCharsets.UTF_8); + if (raw.length == 0) { + throw new SkfException("SKF_DevAuth", -1, "ukey.skf.device-auth-key must not be blank"); + } + int keyLen = requiredKeyLength(algId); + if (raw.length == keyLen) { + return raw; + } + byte[] normalized = new byte[keyLen]; + for (int i = 0; i < keyLen; i++) { + normalized[i] = raw[i % raw.length]; + } + return normalized; + } + + private int requiredKeyLength(int algId) { + int family = algId & 0xFFFFFFF0; + if (family == SGD_AES256) { + return 32; + } + if (family == SGD_AES192 || family == SGD_T3DES) { + return 24; + } + if (family == SGD_SM4 || family == SGD_AES128 || family == SGD_SM1 || family == SGD_SM7 || family == SGD_D3DES) { + return 16; + } + if (family == SGD_DES) { + return 8; + } + return 16; + } + + private void disconnectQuietly(SkfLibrary lib, Pointer devHandle) { + if (devHandle != null) { + lib.SKF_DisConnectDev(devHandle); + } + } + + private void closeApplicationQuietly(SkfLibrary lib, Pointer applicationHandle) { + if (applicationHandle != null) { + lib.SKF_CloseApplication(applicationHandle); + } + } + + private void closeContainerQuietly(SkfLibrary lib, Pointer containerHandle) { + if (containerHandle != null) { + lib.SKF_CloseContainer(containerHandle); + } + } + + private void closeHandleQuietly(SkfLibrary lib, Pointer handle) { + if (handle != null) { + lib.SKF_CloseHandle(handle); + } + } +} diff --git a/ukey-tool/src/main/resources/application.yml b/ukey-tool/src/main/resources/application.yml new file mode 100644 index 0000000..ea328ef --- /dev/null +++ b/ukey-tool/src/main/resources/application.yml @@ -0,0 +1,7 @@ +server: + port: 8690 + +ukey: + skf: + create-file-rights: 255 + sign-key-alg-id: 131328