# iText-GM **Repository Path**: luodinglin/iText-GM ## Basic Information - **Project Name**: iText-GM - **Description**: IText5国密PDF电子签章,基于《GB/T 38540-2020 安全电子签章规范》开发 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 222 - **Created**: 2023-10-17 - **Last Updated**: 2023-10-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # iText-GM ### 免责声明 1. 对使用本软件所产生的风险自行承担。作者不承担与使用本软件相关的任何责任。 2. 代码包含第三方软件和组件,这些软件和组件受到其自身的许可证限制。应遵守这些许可证的要求。 3. 代码可能存在错误、缺陷或安全漏洞。应自行评估和管理使用风险,并采取适当的安全措施。 4. 作者保留随时更改或终止本软件的权利,并不承担因此而产生的任何责任。 5. 测试用例中的证书,电子印章数据,印章图片均为测试数据,如名称等信息侵权,请联系我进行删除。 6. 在使用本代码之前,请确保您已详细阅读并理解了免责声明中的条款。如果您不同意本免责声明的任何部分,请立即停止使用本代码。 IText国密电子签章,基于《GB/T 38540-2020 安全电子签章规范》 准备条件: 1.签名验签服务器 2.SM2证书 3.电子印章数据(需SM2证书绑定在印章数据里,完成制章) 4.签名验签服务器SDK(调用服务器进行签名) ##### 验签地址:https://dzyz.sh.gov.cn/login?code=105 或 http://1.12.67.126:9091/verify.html ##### PDF 《GB/T 38540-2020》 签署 ``` GetPdfHashParamVo paramVo = new GetPdfHashParamVo(); Path pdf = Paths.get("src/main/resources", "test.pdf"); paramVo.setPdf(Files.readAllBytes(pdf)); paramVo.setPageNo(1); paramVo.setLlx(240); paramVo.setLly(290); paramVo.setUrx(paramVo.getLlx() + 120); paramVo.setUry(paramVo.getLly() + 120); Path seal = Paths.get("src/main/resources", "深圳测试科技有限公司.seal"); paramVo.setSeal(SESeal.getInstance(Files.readAllBytes(seal))); paramVo.setLocation("深圳"); paramVo.setReason("国密电子签章测试"); // 创建签名域,获取pdf文件摘要 GetPdfHash getPdfHash = ITextGM.getPdfHash(paramVo); TSAClient tsaClient = new GMTSAClient(new URL("https://gateway.ca.jiedanba.cn/tsa/sign?type=SM2"), null, null, new SM3.Digest()); PrivateKey prvKey = PkiUtil.getPrivateKey(Base64.decodeBase64(privateKey)); X509Certificate signCert = PkiUtil.readX509Certificate(Base64.decodeBase64(cert)); // 签署摘要》》》下面方法仅用于测试,按照合规方面,需要使用国家认可的签名验签服务器以及国家认可的CA机构的SM2证书 SESV4Container signature = new SESV4Container(prvKey, paramVo.getSeal(), signCert); GMTimeStampHook timeStampHook = new GMTimeStampHook(tsaClient); signature.setTimeStampHook(timeStampHook); byte[] p7 = signature.sign(getPdfHash.getDigesHash(), "Signature.xml"); // 签署pdf byte[] signSuccess = ITextGM.signDeferred(getPdfHash.getEmptySignaturePdf(), p7, getPdfHash.getFieldName()); FileUtils.writeByteArrayToFile(new File("src/main/resources/sign.pdf"), signSuccess); ``` ##### PDF 《GB/T 38540-2020》 分离式签名,实际使用过程,只需对byte[] p1 = getPdfHash.getTBS_Sign().getEncoded() 从你的签名服务器获取p1签名结果,即可完成国密电子签章 ``` GetPdfHashParamVo paramVo = new GetPdfHashParamVo(); Path pdf = Paths.get("src/main/resources", "test.pdf"); paramVo.setPdf(Files.readAllBytes(pdf)); paramVo.setPageNo(1); paramVo.setLlx(240); paramVo.setLly(290); paramVo.setUrx(paramVo.getLlx() + 120); paramVo.setUry(paramVo.getLly() + 120); Path seal = Paths.get("src/main/resources", "深圳测试科技有限公司.seal"); paramVo.setSeal(SESeal.getInstance(Files.readAllBytes(seal))); paramVo.setLocation("深圳"); paramVo.setReason("国密电子签章测试"); // 创建签名域,获取pdf文件摘要,组装待签名数据 GetPdfHash getPdfHash = ITextGM.getPdfHash(paramVo); TSAClient tsaClient = new GMTSAClient(new URL("https://gateway.ca.jiedanba.cn/tsa/sign?type=SM2"), null, null, new SM3.Digest()); PrivateKey prvKey = PkiUtil.getPrivateKey(Base64.decodeBase64(privateKey)); X509Certificate signCert = PkiUtil.readX509Certificate(Base64.decodeBase64(cert)); GMTimeStampHook timeStampHook = new GMTimeStampHook(tsaClient); // 以下为模拟外部签名测试,电子签章请使用符合国家规范具有国家型号证书的设备进行 SESV4ContainerV2 signV2 = new SESV4ContainerV2(paramVo.getSeal(), signCert, timeStampHook); /** * 模拟签名服务器进行签名,实际使用过程,只需要使用签名服务器对getPdfHash.getTBS_Sign().getEncoded() * 进行一个p1签名即可 */ byte[] p1 = PkiUtil.sign(prvKey, "SM3WithSM2", getPdfHash.getTBS_Sign().getEncoded()); // 组装电子签章数据 byte[] p7 = signV2.sign(getPdfHash.getTBS_Sign(), p1); // 签署pdf byte[] signSuccess = ITextGM.signDeferred(getPdfHash.getEmptySignaturePdf(), p7, getPdfHash.getFieldName()); FileUtils.writeByteArrayToFile(new File("src/main/resources/signPdf2.pdf"), signSuccess); ``` ##### PDF 《GB/T 38540-2020》签署效果 ![输入图片说明](src/main/resources/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20220805135415.png) ##### PDF 《GB/T 38540-2020》验签效果 ![输入图片说明](src/main/resources/image.png) #### 感谢 https://gitee.com/ofdrw/ofdrw #### IText RSA分离式签名 ##### 分离式签名,场景:从外部设备获取p1数据,例如ukey,签名验签服务器,KMS系统 ``` GetPdfHashParamVo paramVo = new GetPdfHashParamVo(); Path pdf = Paths.get("src/main/resources", "test.pdf"); paramVo.setPdf(Files.readAllBytes(pdf)); paramVo.setPageNo(1); paramVo.setLlx(240); paramVo.setLly(290); paramVo.setUrx(paramVo.getLlx() + 120); paramVo.setUry(paramVo.getLly() + 120); paramVo.setLocation("深圳南山区"); paramVo.setReason("分离式签名测试"); paramVo.setHashAlgorithm("SHA256"); Path sealImage = Paths.get("src/main/resources", "深圳测试科技有限公司_公章.png"); paramVo.setSignImage(Files.readAllBytes(sealImage)); // 创建签名域,获取pdf文件摘要 GetPdfHash getPdfHash = ITextSignHashUtil.getPdfHash(paramVo); TSAClient tsaClient = new TSAClientBouncyCastle("https://gateway.ca.jiedanba.cn/tsa/sign?type=RSA", null, null, 4096, "SHA256"); PrivateKey prvKey = PkiUtil.getPrivateKey(Base64.decodeBase64(pkStr)); X509Certificate signCert = PkiUtil.readX509Certificate(Base64.decodeBase64(certStr)); // 签署p1,以下为模拟外部签名测试 byte[] p1 = PkiUtil.sign(prvKey, "SHA256WithRSA", getPdfHash.getSignHash()); // 签署p7 byte[] p7 = ITextSignHashUtil.signHash(getPdfHash.getDigesHash(), p1, PkiUtil.getCertificateChain(signCert.getEncoded()), "SHA256", tsaClient); // 签署pdf byte[] signSuccess = ITextGM.signDeferred(getPdfHash.getEmptySignaturePdf(), p7, getPdfHash.getFieldName()); FileUtils.writeByteArrayToFile(new File("src/main/resources/sign_hash.pdf"), signSuccess); ``` ##### Adobe验签效果 ![输入图片说明](src/main/resources/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202022-08-09%20012047.png)