Explorar el Código

修改360借条展示逻辑

syh hace 5 años
padre
commit
5bce7cad97
Se han modificado 17 ficheros con 1654 adiciones y 37 borrados
  1. 22 0
      jeecg-boot-module-system/pom.xml
  2. 13 0
      jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/controller/SignController.java
  3. 1 1
      jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/entity/PlatformCampaignStatisticInfo.java
  4. 5 3
      jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/mapper/PlatformCampaignStatisticInfoMapper.java
  5. 141 11
      jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/mapper/xml/PlatformCampaignStatisticInfoMapper.xml
  6. 28 11
      jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/service/impl/ReportServiceImpl.java
  7. 10 0
      jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext5/Extension.java
  8. 544 0
      jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext5/GenX509Cert.java
  9. 197 0
      jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext5/ItextUtils.java
  10. 280 0
      jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext5/Pkcs.java
  11. 137 0
      jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext5/SignHighPdf.java
  12. 101 0
      jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext5/SignImage.java
  13. 114 0
      jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext5/SignPdf.java
  14. 21 0
      jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext7/PdfSignInfo.java
  15. 29 0
      jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext7/Sign7.java
  16. 7 7
      jeecg-boot-module-system/src/main/resources/static/generic/build/pdf.worker.js
  17. 4 4
      jeecg-boot-module-system/src/test/java/org/jeecg/SampleTest.java

+ 22 - 0
jeecg-boot-module-system/pom.xml

@@ -68,6 +68,28 @@
             <artifactId>commons-csv</artifactId>
             <version>1.5</version>
         </dependency>
+        <!--签章-->
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15on</artifactId>
+            <version>1.49</version>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpkix-jdk15on</artifactId>
+            <version>1.49</version>
+        </dependency>
+        <dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>itextpdf</artifactId>
+            <version>5.5.13</version>
+        </dependency>
+        <!--<dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>itext7-core</artifactId>
+            <version>7.1.4</version>
+            <type>pom</type>
+        </dependency>-->
     </dependencies>
 
     <build>

+ 13 - 0
jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/controller/SignController.java

@@ -0,0 +1,13 @@
+package org.jeecg.modules.ctop.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 签章controller
+ */
+@RestController
+@RequestMapping("sign")
+public class SignController {
+
+}

+ 1 - 1
jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/entity/PlatformCampaignStatisticInfo.java

@@ -199,7 +199,7 @@ public class PlatformCampaignStatisticInfo {
                 if (campaignInfo.indexOf("-") >= 0) {
                     this.campaignCode = campaignInfo.substring(0, campaignInfo.indexOf("-"));
                 } else {
-                    this.campaignCode = campaignCode;
+                    this.campaignCode = campaignInfo;
                 }
             } else {
                 this.campaignCode = "";

+ 5 - 3
jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/mapper/PlatformCampaignStatisticInfoMapper.java

@@ -14,9 +14,11 @@ import org.jeecg.modules.ctop.vo.StatisticCampaignVo;
  * @Version: V1.0
  */
 public interface PlatformCampaignStatisticInfoMapper extends BaseMapper<PlatformCampaignStatisticInfo> {
-    List<StatisticCampaignVo> getVoByParams(@Param("statisticDate") String statisticDate, @Param("accountId") Long accountId);
+    List<StatisticCampaignVo> getVoByParams(@Param("accountId") Long accountId);
 
-    StatisticCampaignVo getTotalStatisticVoByparams(@Param("statisticDate") String statisticDate, @Param("accountId") Long accountId);
+    StatisticCampaignVo getTotalStatisticVoByparams(@Param("accountId") Long accountId);
 
-    List<StatisticCampaignVo> sumZeroVoByParams(@Param("statisticDate") String statisticDate, @Param("accountId") Long accountId);
+    List<StatisticCampaignVo> sumZeroVoByParams(@Param("accountId") Long accountId);
+
+    List<StatisticCampaignVo> sumZaodianVoByParams(@Param("accountId") Long accountId1);
 }

+ 141 - 11
jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/mapper/xml/PlatformCampaignStatisticInfoMapper.xml

@@ -6,7 +6,7 @@
         <result column="proxy" property="proxy"/>
         <result column="deliverySystem" property="deliverySystem"/>
         <result column="channelName" property="channelName"/>
-        <result column="content" property="content"/>
+		<result column="content" property="content"/>
         <result column="meterial" property="meterial"/>
         <result column="priceType" property="priceType"/>
 		<result column="directCondition" property="directCondition"/>
@@ -15,17 +15,17 @@
         <result column="coverViews" property="coverViews"/>
         <result column="coverClick" property="coverClick"/>
         <result column="creditRegistRateDay" property="creditRegistRateDay"/>
-        <result column="creditRateDay" property="creditRateDay"/>
+		<result column="creditRateDay" property="creditRateDay"/>
         <result column="completeRateDay" property="completeRateDay"/>
         <result column="registCreditRate" property="registCreditRate"/>
         <result column="registCompleteRate" property="registCompleteRate"/>
         <result column="creditRate" property="creditRate"/>
-        <result column="completeRate" property="completeRate"/>
+		<result column="completeRate" property="completeRate"/>
         <result column="materialViews" property="materialViews"/>
         <result column="actions" property="actions"/>
 		<result column="clickRate" property="clickRate"/>
-        <result column="actionClickRate" property="actionClickRate"/>
-        <result column="abctr" property="abctr"/>
+		<result column="actionClickRate" property="actionClickRate"/>
+		<result column="abctr" property="abctr"/>
         <result column="clickPrice" property="clickPrice"/>
         <result column="actionPrice" property="actionPrice"/>
         <result column="registUserNum" property="registUserNum"/>
@@ -39,10 +39,10 @@
         <result column="registCompletePrice" property="registCompletePrice"/>
         <result column="registCreditPrice" property="registCreditPrice"/>
 		<result column="clickRegistRate" property="clickRegistRate"/>
-        <result column="actionRegistRate" property="actionRegistRate"/>
+		<result column="actionRegistRate" property="actionRegistRate"/>
     </resultMap>
 
-    <select id="getTotalStatisticVoByparams" resultMap="voMap">
+	<select id="getTotalStatisticVoByparams" resultMap="voMap">
         SELECT
 	SUM(a.cost) AS 'cost',
 	SUM(a.coverViews) AS 'coverViews',
@@ -101,8 +101,7 @@ FROM
 				FROM
 					ctop_kuaishou_customer_plan_statistic_info a44
 				WHERE
-					a44.statistic_date = #{statisticDate}
-				AND a44.campaign_code = c.campaign_code
+				a44.campaign_code = c.campaign_code
 				ORDER BY
 					a44.id DESC
 				LIMIT 1
@@ -265,7 +264,7 @@ FROM
 
 	<select id="sumZeroVoByParams" resultMap="voMap">
 SELECT
-	'2019/8/28' AS 'statisticDate',
+	a.statisticDate AS 'statisticDate',
 			'汇创思拓' AS 'proxy',
 			'消费0用户统计信息' as 'channelName',
 			'消费0用户' AS 'content',
@@ -387,10 +386,141 @@ FROM
 		FROM
 			ctop_kuaishou_platform_campaign_statistic_info AS c
 		WHERE
-			c.account_id = #{accountId}
+			c.account_id = #{accountId} and c.campaign_code != 'biaodan'
 			AND c.cost = 0
 		ORDER BY
 			c.campaign_info
 	) AS h) AS a
     </select>
+
+	<select id="sumZaodianVoByParams" resultMap="voMap">
+		SELECT
+	a.statisticDate AS 'statisticDate',
+			'汇创思拓' AS 'proxy',
+			'消费0用户统计信息' as 'channelName',
+			'早点消费0用户' AS 'content',
+	SUM(a.cost) AS 'cost',
+	SUM(a.coverViews) AS 'coverViews',
+	SUM(a.coverClick) AS 'coverClick',
+	SUM(a.materialViews) AS 'materialViews',
+	SUM(a.actions) AS'actions',
+	SUM(a.coverClick) / SUM(a.coverViews) * 100 AS 'clickRate',
+	SUM(a.actions) / SUM(a.coverClick) * 100 AS 'actionClickRate',
+	SUM(a.actions) / SUM(a.coverClick) * SUM(a.coverClick) / SUM(a.coverViews) * 10000 AS 'abctr',
+	SUM(a.cost) / SUM(a.coverClick) 'clickPrice',
+	SUM(a.cost) / SUM(a.actions) 'actionPrice',
+	sum(a.registUserNum)/SUM(1) AS 'registUserNum',
+	sum(a.complateUserNum)/SUM(1) AS 'complateUserNum',
+	sum(a.creditUserNum)/SUM(1) AS 'creditUserNum',
+	sum(a.registCompleteNum)/SUM(1) AS 'registCompleteNum',
+	sum(a.registCreditNum)/SUM(1) AS 'registCreditNum',
+	SUM(a.cost) / sum(a.registUserNum) AS 'registPrice',
+	SUM(a.cost) / sum(a.complateUserNum) AS 'completePrice',
+	SUM(a.cost) / sum(a.creditUserNum) AS 'creditPrice',
+	SUM(a.cost) / sum(a.registCompleteNum) AS 'registCompletePrice',
+	SUM(a.cost) / sum(a.registCreditNum) AS 'registCreditPrice',
+	sum(a.registUserNum) / SUM(a.coverClick) * 100 AS 'clickRegistRate',
+	sum(a.registUserNum) / SUM(a.actions) * 100 AS 'actionRegistRate',
+	sum(a.complateUserNum) / sum(a.registUserNum) * 100 AS 'completeRate',
+	sum(a.creditUserNum) / sum(a.complateUserNum) * 100 AS 'creditRate',
+	sum(a.registCompleteNum) / sum(a.registUserNum) * 100 AS 'registCompleteRate',
+	sum(a.creditUserNum) / sum(a.complateUserNum) * 100 AS 'registCreditRate',
+	sum(a.registCompleteNum) / sum(a.complateUserNum) * 100 AS 'completeRateDay',
+	sum(a.registCreditNum) / sum(a.creditUserNum) * 100 AS 'creditRateDay',
+	sum(a.registCreditNum) / sum(a.registUserNum) * 100 AS 'creditRegistRateDay'
+FROM
+	(
+	SELECT
+	h.*, h.cost / h.registUserNum AS 'registPrice',
+	h.cost / h.complateUserNum AS 'completePrice',
+	h.cost / h.creditUserNum AS 'creditPrice',
+	h.cost / h.registCompleteNum AS 'registCompletePrice',
+	h.cost / h.registCreditNum AS 'registCreditPrice',
+	h.registUserNum / h.coverClick * 100 AS 'clickRegistRate',
+	h.registUserNum / h.actions * 100 AS 'actionRegistRate',
+	h.complateUserNum / h.registUserNum * 100 AS 'completeRate',
+	h.creditUserNum / h.complateUserNum * 100 AS 'creditRate',
+	h.registCompleteNum / h.registUserNum * 100 AS 'registCompleteRate',
+	h.creditUserNum / h.complateUserNum * 100 AS 'registCreditRate',
+	h.registCompleteNum / h.complateUserNum * 100 AS 'completeRateDay',
+	h.registCreditNum / h.creditUserNum * 100 AS 'creditRateDay',
+	h.registCreditNum / h.registUserNum * 100 AS 'creditRegistRateDay'
+FROM
+	(
+		SELECT
+			c.statistic_date AS 'statisticDate',
+			'汇创思拓' AS 'proxy',
+			(
+				SELECT
+					a44.child_channel
+				FROM
+					ctop_kuaishou_customer_plan_statistic_info a44
+				WHERE
+					a44.campaign_code = c.campaign_code
+				ORDER BY
+					a44.id DESC
+				LIMIT 1
+			) AS 'channelName',
+			c.campaign_info AS 'content',
+			c.cost AS 'cost',
+			c.cover_exposure AS 'coverViews',
+			c.cover_click AS 'coverClick',
+			c.metarial_exposure AS 'materialViews',
+			c.action AS 'actions',
+			c.cover_click / c.cover_exposure * 100 AS 'clickRate',
+			c.action / c.cover_click * 100 AS 'actionClickRate',
+			c.action / c.cover_click * c.cover_click / c.cover_exposure * 10000 AS 'abctr',
+			c.cost / c.cover_click 'clickPrice',
+			c.cost / c.action 'actionPrice',
+			(
+				SELECT
+					SUM(a1.regist_account_num)
+				FROM
+					ctop_kuaishou_customer_plan_statistic_info a1
+				WHERE
+					a1.campaign_code = c.campaign_code
+			) AS 'registUserNum',
+			(
+				SELECT
+					SUM(a2.complete_user)
+				FROM
+					ctop_kuaishou_customer_plan_statistic_info a2
+				WHERE
+					a2.campaign_code = c.campaign_code
+			) AS 'complateUserNum',
+			(
+				SELECT
+					SUM(a3.estimated_credit_number)
+				FROM
+					ctop_kuaishou_customer_plan_statistic_info a3
+				WHERE
+					a3.campaign_code = c.campaign_code
+			) AS 'creditUserNum',
+			(
+				SELECT
+					SUM(a4.regist_complate_number)
+				FROM
+					ctop_kuaishou_customer_plan_statistic_info a4
+				WHERE
+					a4.campaign_code = c.campaign_code
+			) AS 'registCompleteNum',
+			(
+				SELECT
+					SUM(
+						a5.estimated_regist_credit_number
+					)
+				FROM
+					ctop_kuaishou_customer_plan_statistic_info a5
+				WHERE
+					a5.campaign_code = c.campaign_code
+			) AS 'registCreditNum'
+		FROM
+			ctop_kuaishou_platform_campaign_statistic_info AS c
+		WHERE
+			c.account_id = #{accountId} and c.campaign_code = 'biaodan'
+			AND c.cost = 0
+		ORDER BY
+			c.campaign_info
+	) AS h) AS a
+	</select>
 </mapper>

+ 28 - 11
jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/service/impl/ReportServiceImpl.java

@@ -35,6 +35,7 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
 import java.io.*;
+import java.math.BigDecimal;
 import java.net.HttpURLConnection;
 import java.net.URI;
 import java.net.URL;
@@ -247,8 +248,8 @@ public class ReportServiceImpl implements IReportService {
     }
 
     public String getExcelDataPath(String date, Long accountId1, Long accountId4) throws Exception {
-        List<StatisticCampaignVo> statisticCampaign1Vos = statisticInfoMapper.getVoByParams(date, accountId1);
-        List<StatisticCampaignVo> statisticCampaign4Vos = statisticInfoMapper.getVoByParams(date, accountId4);
+        List<StatisticCampaignVo> statisticCampaign1Vos = statisticInfoMapper.getVoByParams(accountId1);
+        List<StatisticCampaignVo> statisticCampaign4Vos = statisticInfoMapper.getVoByParams(accountId4);
         File f = new File(csvUploadPath + "template.xlsx");
         InputStream inputStream = new FileInputStream(f);
         XSSFWorkbook xssfWorkbook = new XSSFWorkbook(inputStream);
@@ -264,7 +265,8 @@ public class ReportServiceImpl implements IReportService {
             Row row = xssfSheet.getRow(index);
             setExcelRow(numbericCellStyle, vo, contents, row, "single");
         }
-        List<StatisticCampaignVo> zero1Vos = statisticInfoMapper.sumZeroVoByParams(date, accountId1);
+        List<StatisticCampaignVo> zero1Vos = statisticInfoMapper.sumZeroVoByParams(accountId1);
+        List<StatisticCampaignVo> zaodianVos = statisticInfoMapper.sumZaodianVoByParams(accountId1);
         if (null != zero1Vos && zero1Vos.size() > 0) {
             for (int z = 0; z < zero1Vos.size(); z++) {
                 StatisticCampaignVo vo = zero1Vos.get(z);
@@ -275,8 +277,18 @@ public class ReportServiceImpl implements IReportService {
                 index++;
             }
         }
+        if (null != zaodianVos && zaodianVos.size() > 0) {
+            for (int z = 0; z < zaodianVos.size(); z++) {
+                StatisticCampaignVo vo = zaodianVos.get(z);
+                Row row = xssfSheet.getRow(index);
+                String content = vo.getContent();
+                String[] contents = content.split("-");
+                setExcelRow(numbericCellStyle, vo, contents, row, "single");
+                index++;
+            }
+        }
         Row totalRow1 = xssfSheet.getRow(index);
-        StatisticCampaignVo total = statisticInfoMapper.getTotalStatisticVoByparams(date, accountId1);
+        StatisticCampaignVo total = statisticInfoMapper.getTotalStatisticVoByparams(accountId1);
         if (null != total) {
             setExcelRow(numbericCellStyle, total, new String[]{}, totalRow1, "total1");
         }
@@ -289,7 +301,7 @@ public class ReportServiceImpl implements IReportService {
             Row row = xssfSheet.getRow(index);
             setExcelRow(numbericCellStyle, vo, contents, row, "single");
         }
-        List<StatisticCampaignVo> zero4Vos = statisticInfoMapper.sumZeroVoByParams(date, accountId4);
+        List<StatisticCampaignVo> zero4Vos = statisticInfoMapper.sumZeroVoByParams(accountId4);
         if (null != zero4Vos && zero4Vos.size() > 0) {
             for (int z = 0; z < zero4Vos.size(); z++) {
                 index++;
@@ -302,7 +314,7 @@ public class ReportServiceImpl implements IReportService {
         }
         index++;
         Row totalRow4 = xssfSheet.getRow(index);
-        StatisticCampaignVo total1 = statisticInfoMapper.getTotalStatisticVoByparams(date, accountId4);
+        StatisticCampaignVo total1 = statisticInfoMapper.getTotalStatisticVoByparams(accountId4);
         if (null != total1) {
             setExcelRow(numbericCellStyle, total1, new String[]{}, totalRow4, "total4");
         }
@@ -438,27 +450,32 @@ public class ReportServiceImpl implements IReportService {
             } else if (j == 19) {
                 //注册用户
                 if (null != vo.getRegistUserNum() && !"".equals(vo.getRegistUserNum())) {
-                    cell.setCellValue(Integer.parseInt(vo.getRegistUserNum()));
+                    BigDecimal decimal = new BigDecimal(vo.getRegistUserNum());
+                    cell.setCellValue(decimal.intValue());
                 }
             } else if (j == 20) {
                 //完件用户
                 if (null != vo.getComplateUserNum() && !"".equals(vo.getComplateUserNum())) {
-                    cell.setCellValue(Integer.parseInt(vo.getComplateUserNum()));
+                    BigDecimal decimal = new BigDecimal(vo.getComplateUserNum());
+                    cell.setCellValue(decimal.intValue());
                 }
             } else if (j == 21) {
                 //授信用户
                 if (null != vo.getCreditUserNum() && !"".equals(vo.getCreditUserNum())) {
-                    cell.setCellValue(Integer.parseInt(vo.getCreditUserNum()));
+                    BigDecimal decimal = new BigDecimal(vo.getCreditUserNum());
+                    cell.setCellValue(decimal.intValue());
                 }
             } else if (j == 22) {
                 //当日注册且完件数
                 if (null != vo.getRegistCompleteNum() && !"".equals(vo.getRegistCompleteNum())) {
-                    cell.setCellValue(Integer.parseInt(vo.getRegistCompleteNum()));
+                    BigDecimal decimal = new BigDecimal(vo.getRegistCompleteNum());
+                    cell.setCellValue(decimal.intValue());
                 }
             } else if (j == 23) {
                 //当日注册且授信数
                 if (null != vo.getRegistCreditNum() && !"".equals(vo.getRegistCreditNum())) {
-                    cell.setCellValue(Integer.parseInt(vo.getRegistCreditNum()));
+                    BigDecimal decimal = new BigDecimal(vo.getRegistCreditNum());
+                    cell.setCellValue(decimal.intValue());
                 }
             } else if (j == 24) {
                 //注册成本

+ 10 - 0
jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext5/Extension.java

@@ -0,0 +1,10 @@
+package org.jeecg.modules.ctop.sign.itext5;
+
+import lombok.Data;
+
+@Data
+public class Extension {
+    private String oid;
+    private boolean critical;
+    private byte[] value;
+}

+ 544 - 0
jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext5/GenX509Cert.java

@@ -0,0 +1,544 @@
+//package org.jeecg.modules.ctop.sign.itext5;
+//
+//import java.io.File;
+//import java.io.FileInputStream;
+//import java.io.FileNotFoundException;
+//import java.io.FileOutputStream;
+//import java.io.IOException;
+//import java.math.BigInteger;
+//import java.security.KeyPair;
+//import java.security.KeyPairGenerator;
+//import java.security.KeyStore;
+//import java.security.NoSuchAlgorithmException;
+//import java.security.PrivateKey;
+//import java.security.PublicKey;
+//import java.security.SecureRandom;
+//import java.security.Signature;
+//import java.security.SignatureException;
+//import java.security.cert.Certificate;
+//import java.security.cert.CertificateException;
+//import java.security.cert.X509Certificate;
+//import java.util.Date;
+//import java.util.HashMap;
+//import java.util.Map;
+//import java.util.Vector;
+//import java.util.Map.Entry;
+//
+//
+//import sun.security.util.ObjectIdentifier;
+//import sun.security.x509.AlgorithmId;
+//import sun.security.x509.CertAndKeyGen;
+//import sun.security.x509.CertificateAlgorithmId;
+//import sun.security.x509.CertificateExtensions;
+//import sun.security.x509.CertificateSerialNumber;
+//import sun.security.x509.CertificateValidity;
+//import sun.security.x509.CertificateVersion;
+//import sun.security.x509.CertificateX509Key;
+//import sun.security.x509.ExtendedKeyUsageExtension;
+//import sun.security.x509.KeyIdentifier;
+//import sun.security.x509.KeyUsageExtension;
+//import sun.security.x509.SerialNumber;
+//import sun.security.x509.SubjectKeyIdentifierExtension;
+//import sun.security.x509.X500Name;
+//import sun.security.x509.X500Signer;
+//import sun.security.x509.X509CertImpl;
+//import sun.security.x509.X509CertInfo;
+//
+//public class GenX509Cert {
+//    private SecureRandom sr = new SecureRandom();
+//
+//
+//    private String root = "Root";// 根证书前缀
+//    private static String rootUser = "签名服务器";// 证书颁发者
+//
+//
+//    public GenX509Cert() {
+//        try {
+//            sr = SecureRandom.getInstance("SHA1PRNG", "SUN");
+//        } catch (Exception e) {
+//            System.err.println("实例化SecureRandom出错:"+e.getMessage());
+//        }
+//    }
+//
+//
+//    public void createCert(X509Certificate certificate, PrivateKey rootPrivKey,
+//                           KeyPair kp, String certPath, String user, String password,
+//                           String sbh, String ip) throws Exception {
+//        byte certbytes[] = certificate.getEncoded();
+//        X509CertImpl x509certimpl = new X509CertImpl(certbytes);
+//
+//
+//        X509CertInfo x509certinfo = (X509CertInfo) x509certimpl
+//                .get("x509.info");
+//
+//
+//        x509certinfo.set("key", new CertificateX509Key(kp.getPublic()));
+//
+//
+//        CertificateExtensions certificateextensions = new CertificateExtensions();
+//        certificateextensions.set("SubjectKeyIdentifier",
+//                new SubjectKeyIdentifierExtension((new KeyIdentifier(kp
+//                        .getPublic())).getIdentifier()));
+//        x509certinfo.set("extensions", certificateextensions);
+//
+//
+//// 设置issuer域
+//        X500Name issuer = new X500Name("CN=" + rootUser
+//                + ",OU=hackwp,O=wp,L=BJ,S=BJ,C=CN");
+//        x509certinfo.set("issuer.dname", issuer);
+//        X500Name subject = new X500Name("CN=" + user
+//                + ", OU=wps, O=wps, L=BJ, ST=BJ, C=CN");
+//        x509certinfo.set("subject.dname", subject);
+//
+//
+//        Signature signature = Signature.getInstance("MD5WithRSA");
+//        signature.initSign(kp.getPrivate());
+//        X500Signer signer = new X500Signer(signature, issuer);
+//
+//
+//        AlgorithmId algorithmid = signer.getAlgorithmId();
+//        x509certinfo
+//                .set("algorithmID", new CertificateAlgorithmId(algorithmid));
+//
+//
+//        Date bdate = new Date();
+//        Date edate = new Date();
+//// 天 小时 分 秒 毫秒
+//        edate.setTime(bdate.getTime() + 3650L * 24L * 60L * 60L * 1000L);
+//// validity为有效时间长度 单位为秒
+//        CertificateValidity certificatevalidity = new CertificateValidity(
+//                bdate, edate);
+//        x509certinfo.set("validity", certificatevalidity);
+//// 设置有效期域(包含开始时间和到期时间)域名等同与x509certinfo.VALIDITY
+//        x509certinfo.set("serialNumber", new CertificateSerialNumber(
+//                (int) (new Date().getTime() / 1000L)));
+//// 设置序列号域
+//        CertificateVersion cv = new CertificateVersion(CertificateVersion.V3);
+//        x509certinfo.set(X509CertInfo.VERSION, cv);
+//// 设置版本号 只有v1 ,v2,v3这几个合法值
+///**
+// *以上是证书的基本信息 如果要添加用户扩展信息 则比较麻烦 首先要确定version必须是v3否则不行 然后按照以下步骤
+// **/
+//
+//
+//        String userData = "Digital Signature, Non-Repudiation, Key Encipherment, Data Encipherment (f0)";
+//
+//
+//        byte l = (byte) userData.length();// 数据总长17位
+//        byte f = 0x04;
+//        byte[] bs = new byte[userData.length() + 2];
+//        bs[0] = f;
+//        bs[1] = l;
+//        for (int i = 2; i < bs.length; i++) {
+//            bs[i] = (byte) userData.charAt(i - 2);
+//        }
+//
+//
+//        KeyUsageExtension keyUsage = new KeyUsageExtension();
+//        keyUsage.set(KeyUsageExtension.DIGITAL_SIGNATURE, true);
+//        keyUsage.set(KeyUsageExtension.NON_REPUDIATION, true);
+//        keyUsage.set(KeyUsageExtension.KEY_ENCIPHERMENT, true);
+//        keyUsage.set(KeyUsageExtension.DATA_ENCIPHERMENT, true);
+//
+//
+//// 增强密钥用法
+//        ObjectIdentifier ekeyOid = new ObjectIdentifier(new int[] { 1, 3, 6, 1,
+//                5, 5, 7, 3, 3 });
+//        Vector<ObjectIdentifier> vkeyOid = new Vector<ObjectIdentifier>();
+//        vkeyOid.add(ekeyOid);
+//        ExtendedKeyUsageExtension exKeyUsage = new ExtendedKeyUsageExtension(
+//                vkeyOid);
+//
+//
+//        CertificateExtensions exts = new CertificateExtensions();
+//
+//
+//        exts.set("keyUsage", keyUsage);
+//        exts.set("extendedKeyUsage", exKeyUsage);
+//
+//
+//// 如果有多个extension则都放入CertificateExtensions 类中,
+//        x509certinfo.set(X509CertInfo.EXTENSIONS, exts);
+//
+//
+//        X509CertImpl x509certimpl1 = new X509CertImpl(x509certinfo);
+//        SerialNumber sn = new SerialNumber(new BigInteger(ip
+//                + System.currentTimeMillis()));
+//        x509certimpl1.set(X509CertImpl.SERIAL_ID, sn);// 设置证书序列号
+//        x509certimpl1.sign(rootPrivKey, "MD5WithRSA");// 使用另一个证书的私钥来签名此证书 这里使用
+//// md5散列 用rsa来加密
+//        Certificate[] certChain = { x509certimpl1 };
+//        savePfx(Util.certCA, kp.getPrivate(), password, certChain, certPath);
+//
+//
+//// 生成文件
+//        x509certimpl1.verify(certificate.getPublicKey(), null);
+//    }
+//
+//
+//    /**
+//     * 保存pfx文件,里面包括公钥,私钥,证书链别名
+//     *
+//     * @param alias
+//     * @param privKey
+//     * @param pwd
+//     * @param certChain
+//     * @param filepath
+//     * @throws Exception
+//     */
+//    public void savePfx(String alias, PrivateKey privKey, String pwd,
+//                        Certificate[] certChain, String filepath) throws Exception {
+//        FileOutputStream out = null;
+//        try {
+//            KeyStore outputKeyStore = KeyStore.getInstance("pkcs12");
+//            outputKeyStore.load(null, pwd.toCharArray());
+//            outputKeyStore.setKeyEntry(alias, privKey, pwd.toCharArray(),
+//                    certChain);
+//            out = new FileOutputStream(filepath);
+//            outputKeyStore.store(out, pwd.toCharArray());
+//        } finally {
+//            if (out != null)
+//                out.close();
+//        }
+//
+//
+//    }
+//
+//
+//    /**
+//     * 生成用户证书
+//     *
+//     * @param sbh
+//     *            纳税人识别号(文件名,不包含后缀)
+//     * @param user
+//     *            证书使用者
+//     * @param path
+//     *            证书保存路径(不包括文件名),如要保存在D盘的home目录下则输入:D:/home
+//     * @param password
+//     *            证书密码
+//     * @param ip
+//     *            客户端请求的ip
+//     * @return map里面包含两个key,一个code,一个msg,如果code等于0000则为生成成功,如果code不等于0000则为生成失败,
+//     *         msg里面保存失败原因
+//     */
+//    public Map<String, String> createCA(String sbh, String user, String path,
+//                                        String password, String ip) {
+//        Map<String, String> map = new HashMap<String, String>();
+//        if (sbh == null || "".equals(sbh)) {
+//            map.put("code", "-1");
+//            map.put("msg", "纳税人识别号不能为空");
+//            return map;
+//        }
+//        if (user == null || "".equals(user)) {
+//            map.put("code", "-1");
+//            map.put("msg", "证书使用者不能为空");
+//            return map;
+//        }
+//        if (path == null || "".equals(path)) {
+//            map.put("code", "-1");
+//            map.put("msg", "保存路径不能为空");
+//            return map;
+//        }
+//        if (password == null || "".equals(password)) {
+//            map.put("code", "-1");
+//            map.put("msg", "证书密码不能为空");
+//            return map;
+//        }
+//        if (!Util.ipCheck(ip)) {// 验证IP地址是否合法
+//            map.put("code", "-1");
+//            map.put("msg", "IP地址不合法");
+//            return map;
+//        }
+//        if(!new File(path).exists()){
+//            map.put("code", "5555");
+//            map.put("msg", "保存文件的目录不存在");
+//            return map;
+//        }
+//        String rootPath = path + File.separator + root + sbh + ".pfx";
+//        String certPath = path + File.separator + sbh + ".pfx";
+//        File file = new File(rootPath);
+//        if (file.exists()) {
+//            map.put("code", "6666");
+//            map.put("msg", "文件已存在");
+//            return map;
+//        }
+//        try {
+//            CertAndKeyGen cak = new CertAndKeyGen("RSA", "MD5WithRSA", null);
+//// 参数分别为 公钥算法 签名算法 providername(因为不知道确切的 只好使用null 既使用默认的provider)
+//            cak.generate(1024);
+//            cak.setRandom(sr);
+//// 生成一对key 参数为key的长度 对于rsa不能小于512
+//            X500Name subject = new X500Name(
+//                    "CN=root,OU=root,O=wp,L=BJ,S=BJ,C=CN");
+//// subject name
+//            X509Certificate certificate = cak.getSelfCertificate(subject,
+//                    new Date(), 365L * 24L * 60L * 60L * 1000L * 5L);
+//
+//
+//            X509Certificate[] certs = { certificate };
+//
+//
+//            savePfx(Util.rootCA, cak.getPrivateKey(), password, certs, rootPath);
+//            ip = Util.getIpNum(ip);
+//            signCert(rootPath, certPath, user, password, sbh, ip);
+//            map.put("code", "0000");
+//            map.put("msg", "用户证书生成成功");
+//            return map;
+//        } catch (Exception e) {
+//            if (file != null) {
+//                file.delete();
+//            }
+//            File file2 = new File(certPath);
+//            if (file2.exists())
+//                file2.delete();
+//            map.put("code", "9999");
+//            map.put("msg", "生成用户证书发生异常:" + e.getMessage());
+//            return map;
+//        }
+//    }
+//
+//
+//    /**
+//     *
+//     * @param rootPath
+//     *            根证书路径
+//     * @param certPath
+//     *            用户证书路径
+//     * @param user
+//     *            证书使用者
+//     * @param password
+//     *            证书密码
+//     * @param sbh
+//     *            纳税人识别号
+//     * @param ip
+//     *            请求地址的ip
+//     * @throws Exception
+//     */
+//    public void signCert(String rootPath, String certPath, String user,
+//                         String password, String sbh, String ip) throws Exception {
+//        FileInputStream ksfis = null;
+//        try {
+//            KeyStore ks = KeyStore.getInstance("pkcs12");
+//
+//
+//            ksfis = new FileInputStream(rootPath);
+//            char[] storePwd = password.toCharArray();
+//            char[] keyPwd = password.toCharArray();
+//
+//
+//            ks.load(ksfis, storePwd);
+//
+//
+//// 从密钥仓库得到私钥
+//            PrivateKey privK = (PrivateKey) ks.getKey(Util.rootCA, keyPwd);
+//
+//
+//            X509Certificate certificate = (X509Certificate) ks
+//                    .getCertificate(Util.rootCA);
+//
+//
+//            createCert(certificate, privK, genKey(), certPath, user, password,
+//                    sbh, ip);
+//        } finally {
+//            if (ksfis != null)
+//                ksfis.close();
+//        }
+//    }
+//
+//
+//    public KeyPair genKey() throws NoSuchAlgorithmException {
+//        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+//        kpg.initialize(1024, sr);
+//        KeyPair kp = kpg.generateKeyPair();
+//        return kp;
+//    }
+//
+//
+//    /**
+//     * 获取证书序列号和公钥
+//     *
+//     * @param path
+//     *            证读取证书的路径
+//     * @param pass
+//     *            读取证书的密码
+//     * @return
+//     */
+//    public Map<String, String> getCertMessage(String path, String pass) {
+//        Map<String, String> map = new HashMap<String, String>();
+//        KeyStore ks;
+//        FileInputStream fis = null;
+//        if (pass == null || "".equals(pass) || path == null || "".equals(path)) {
+//            map.put("code", "-1");
+//            map.put("msg", "参数错误");
+//            return map;
+//        }
+//        try {
+//            ks = KeyStore.getInstance("PKCS12");
+//            fis = new FileInputStream(path);
+//
+//
+//            char[] nPassword = null;
+//            if ((pass == null) || pass.trim().equals("")) {
+//                nPassword = null;
+//            } else {
+//                nPassword = pass.toCharArray();
+//            }
+//            ks.load(fis, nPassword);
+//
+//
+//            X509Certificate cert = (X509Certificate) ks
+//                    .getCertificate(Util.certCA);
+//            PublicKey pubkey = cert.getPublicKey();
+//
+//
+//            byte pb[] = Serializer.serialize(pubkey);
+//            map.put("code", "0000");
+//            map.put("msg", "数据返回成功");
+//            map.put("certNumber", cert.getSerialNumber().toString());
+//            map.put("cert", Base64Utils.encode(pb));
+//            return map;
+//        } catch (FileNotFoundException e) {
+//            map.put("code", "1111");
+//            map.put("msg", "文件没有找到:" + e.getMessage());
+//            return map;
+//        } catch (CertificateException e) {
+//            map.put("code", "2222");
+//            map.put("msg", "读取证书异常:" + e.getMessage());
+//            return map;
+//        } catch (IOException e) {
+//            map.put("code", "4444");
+//            map.put("msg", "IO异常:" + e.getMessage());
+//            return map;
+//        } catch (Exception e) {
+//            map.put("code", "9999");
+//            map.put("msg", "未知异常:" + e.getMessage());
+//            return map;
+//        }finally{
+//            if(fis!=null)
+//                try {
+//                    fis.close();
+//                } catch (IOException e) {
+//                    e.printStackTrace();
+//                }
+//        }
+//    }
+//
+//
+//    /**
+//     * 签名数据
+//     *
+//     * @param path
+//     *            读取证书路径
+//     * @param pass
+//     *            读取证书密码
+//     * @param data
+//     *            待签名数据
+//     * @return
+//     */
+//    public Map<String, String> signData(String path, String pass, String data) {
+//        Map<String, String> map = new HashMap<String, String>();
+//        KeyStore ks;
+//        FileInputStream fis = null;
+//        if (pass == null || "".equals(pass) || path == null || "".equals(path)
+//                || data == null || "".equals(data)) {
+//            map.put("code", "-1");
+//            map.put("msg", "参数错误");
+//            return map;
+//        }
+//        try {
+//            ks = KeyStore.getInstance("PKCS12");
+//
+//
+//            fis = new FileInputStream(path);
+//
+//
+//            char[] nPassword = null;
+//            if ((pass == null) || pass.trim().equals("")) {
+//                nPassword = null;
+//            } else {
+//                nPassword = pass.toCharArray();
+//            }
+//            ks.load(fis, nPassword);
+//            X509Certificate cert = (X509Certificate) ks.getCertificate(Util.certCA);
+//
+//
+//            PrivateKey prikey = (PrivateKey) ks.getKey(Util.certCA, nPassword);
+//
+//
+//            Signature sig = Signature.getInstance("MD5WithRSA");
+//            sig.initSign(prikey);
+//            sig.update(data.getBytes("UTF-8"));
+//            byte b[] = sig.sign();
+//
+//            map.put("code", "0000");
+//            map.put("msg", "数据返回成功");
+//            map.put("certNumber", cert.getSerialNumber().toString());
+//            map.put("sign", Base64Utils.encode(b));
+//            return map;
+//        } catch (FileNotFoundException e) {
+//            map.put("code", "1111");
+//            map.put("msg", "文件没有找到:" + e.getMessage());
+//            return map;
+//        } catch (CertificateException e) {
+//            map.put("code", "2222");
+//            map.put("msg", "读取证书异常:" + e.getMessage());
+//            return map;
+//        } catch (NoSuchAlgorithmException e) {
+//            map.put("code", "2222");
+//            map.put("msg", "读取证书异常:" + e.getMessage());
+//            return map;
+//        } catch (SignatureException e) {
+//            map.put("code", "3333");
+//            map.put("msg", "签名异常:" + e.getMessage());
+//            return map;
+//        } catch (IOException e) {
+//            map.put("code", "4444");
+//            map.put("msg", "IO异常:" + e.getMessage());
+//            return map;
+//        } catch (Exception e) {
+//            map.put("code", "9999");
+//            map.put("msg", "未知异常:" + e.getMessage());
+//            return map;
+//        } finally {
+//            if (fis != null)
+//                try {
+//                    fis.close();
+//                } catch (IOException e) {
+//                    System.out.println(e.getMessage());
+//                }
+//        }
+//    }
+//
+//
+//    public void verify(String key,String sign,String signData) throws Exception{
+//        byte []pb = Base64Utils.decode(key);
+//        PublicKey pk = (PublicKey) Serializer.unserialize(pb);
+//        Signature sig2=Signature.getInstance("MD5WithRSA");
+//        sig2.initVerify(pk);
+//        sig2.update(sign.getBytes("UTF-8"));
+//        System.out.println("验签结果是:"+sig2.verify(Base64Utils.decode(signData)));
+//    }
+//    public static void main(String[] args) {
+//        try {
+//            GenX509Cert gcert = new GenX509Cert();
+//            String data = "adowdn@#&kajdk";
+//            Map<String, String> map = gcert.createCA("133456", "小科", "e:",
+//                    "123456", "10.26.27.28");
+//            System.out.println(map.get("code") + "\t" + map.get("msg"));
+//            Map<String, String> map1 = gcert.getCertMessage("e:/133456.pfx",
+//                    "123456");
+//            for (Entry<String, String> e : map1.entrySet()) {
+//                System.out.println(e.getKey() + "-->" + e.getValue());
+//            }
+//            Map<String, String> map2 = gcert.signData("e:/133456.pfx",
+//                    "123456", data);
+//            for (Entry<String, String> e : map2.entrySet()) {
+//                System.out.println(e.getKey() + "-->" + e.getValue());
+//            }
+//            gcert.verify(map1.get("cert"), data, map2.get("sign"));
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//        }
+//    }
+//
+//}

+ 197 - 0
jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext5/ItextUtils.java

@@ -0,0 +1,197 @@
+package org.jeecg.modules.ctop.sign.itext5;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.CRLDistPoint;
+import org.bouncycastle.asn1.x509.DistributionPoint;
+import org.bouncycastle.asn1.x509.DistributionPointName;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+
+public class ItextUtils {
+    private static KeyPair getKey() throws NoSuchAlgorithmException {
+        // 密钥对 生成器,RSA算法 生成的  提供者是 BouncyCastle
+        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
+        generator.initialize(1024);  // 密钥长度 1024
+        // 证书中的密钥 公钥和私钥
+        KeyPair keyPair = generator.generateKeyPair();
+        return keyPair;
+    }
+
+    /**
+     * @param password       密码
+     * @param issuerStr      颁发机构信息
+     * @param subjectStr     使用者信息
+     * @param certificateCRL 颁发地址
+     * @return
+     */
+    public static Map<String, byte[]> createCert(String password, String issuerStr, String subjectStr, String certificateCRL) {
+
+        Map<String, byte[]> result = new HashMap<String, byte[]>();
+        ByteArrayOutputStream out = null;
+        try {
+            //  生成JKS证书
+            //  KeyStore keyStore = KeyStore.getInstance("JKS");
+            //  标志生成PKCS12证书
+            KeyStore keyStore = KeyStore.getInstance("PKCS7", new BouncyCastleProvider());
+            keyStore.load(null, null);
+            KeyPair keyPair = getKey();
+            //  issuer与 subject相同的证书就是CA证书
+            Certificate cert = generateCertificateV3(issuerStr, subjectStr, keyPair, result, certificateCRL, null);
+            // cretkey随便写,标识别名
+            keyStore.setKeyEntry("cretkey", keyPair.getPrivate(), password.toCharArray(), new Certificate[]{cert});
+            out = new ByteArrayOutputStream();
+            cert.verify(keyPair.getPublic());
+            keyStore.store(out, password.toCharArray());
+            byte[] keyStoreData = out.toByteArray();
+            result.put("keyStoreData", keyStoreData);
+            return result;
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @param issuerStr
+     * @param subjectStr
+     * @param keyPair
+     * @param result
+     * @param certificateCRL
+     * @param extensions
+     * @return
+     */
+    public static Certificate generateCertificateV3(String issuerStr, String subjectStr, KeyPair keyPair, Map<String, byte[]> result,
+                                                    String certificateCRL, List<Extension> extensions) {
+
+        ByteArrayInputStream bout = null;
+        X509Certificate cert = null;
+        try {
+            PublicKey publicKey = keyPair.getPublic();
+            PrivateKey privateKey = keyPair.getPrivate();
+            Date notBefore = new Date();
+            Calendar rightNow = Calendar.getInstance();
+            rightNow.setTime(notBefore);
+            // 日期加1年
+            rightNow.add(Calendar.YEAR, 1);
+            Date notAfter = rightNow.getTime();
+            // 证书序列号
+            BigInteger serial = BigInteger.probablePrime(256, new Random());
+            X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
+                    new X500Name(issuerStr), serial, notBefore, notAfter, new X500Name(subjectStr), publicKey);
+            JcaContentSignerBuilder jBuilder = new JcaContentSignerBuilder("SHA1withRSA");
+            SecureRandom secureRandom = new SecureRandom();
+            jBuilder.setSecureRandom(secureRandom);
+            ContentSigner singer = jBuilder.setProvider(new BouncyCastleProvider()).build(privateKey);
+            // 分发点
+            ASN1ObjectIdentifier cRLDistributionPoints = new ASN1ObjectIdentifier("2.5.29.31");
+            GeneralName generalName = new GeneralName(GeneralName.uniformResourceIdentifier, certificateCRL);
+            GeneralNames seneralNames = new GeneralNames(generalName);
+            DistributionPointName distributionPoint = new DistributionPointName(seneralNames);
+            DistributionPoint[] points = new DistributionPoint[1];
+            points[0] = new DistributionPoint(distributionPoint, null, null);
+            CRLDistPoint cRLDistPoint = new CRLDistPoint(points);
+            builder.addExtension(cRLDistributionPoints, true, cRLDistPoint);
+            // 用途
+            ASN1ObjectIdentifier keyUsage = new ASN1ObjectIdentifier("2.5.29.15");
+            // | KeyUsage.nonRepudiation | KeyUsage.keyCertSign
+            builder.addExtension(keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment));
+            // 基本限制 X509Extension.java
+            ASN1ObjectIdentifier basicConstraints = new ASN1ObjectIdentifier("2.5.29.19");
+            builder.addExtension(basicConstraints, true, new BasicConstraints(true));
+            // privKey:使用自己的私钥进行签名,CA证书
+            if (extensions != null) {
+                for (Extension ext : extensions) {
+                    builder.addExtension(
+                            new ASN1ObjectIdentifier(ext.getOid()),
+                            ext.isCritical(),
+                            ASN1Primitive.fromByteArray(ext.getValue()));
+                }
+            }
+            X509CertificateHolder holder = builder.build(singer);
+            CertificateFactory cf = CertificateFactory.getInstance("X.509");
+            bout = new ByteArrayInputStream(holder.toASN1Structure().getEncoded());
+            cert = (X509Certificate) cf.generateCertificate(bout);
+            byte[] certBuf = holder.getEncoded();
+            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+            // 证书数据
+            result.put("certificateData", certBuf);
+            //公钥
+            result.put("publicKey", publicKey.getEncoded());
+            //私钥
+            result.put("privateKey", privateKey.getEncoded());
+            //证书有效开始时间
+            result.put("notBefore", format.format(notBefore).getBytes("utf-8"));
+            //证书有效结束时间
+            result.put("notAfter", format.format(notAfter).getBytes("utf-8"));
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (bout != null) {
+                try {
+                    bout.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        return cert;
+    }
+
+    public static void main(String[] args) throws Exception {
+        // CN: 名字与姓氏    OU : 组织单位名称
+        // O :组织名称  L : 城市或区域名称  E : 电子邮件
+        // ST: 州或省份名称  C: 单位的两字母国家代码
+        String issuerStr = "CN=天天凭证,OU=研发部,O=gitbook有限公司,C=CN,E=gitbook@sina.com,L=北京,ST=北京";
+        String subjectStr = "CN=huangjinjin,OU=用户,O=借款人,C=CN,E=huangjinjin@sina.com,L=北京,ST=北京";
+        String certificateCRL = "https://gitbook.cn";
+        Map<String, byte[]> result = createCert("123456", issuerStr, subjectStr, certificateCRL);
+
+//		FileOutputStream outPutStream = new FileOutputStream("C:/pdf/keystore.p12"); // 生成.p12
+//		outPutStream.write(result.get("keyStoreData"));
+//		outPutStream.close();
+        FileOutputStream fos = new FileOutputStream(new File("D:/sign/zheng.cer"));//生成.p12颁发给用户的证书
+        fos.write(result.get("certificateData"));
+        fos.flush();
+        fos.close();
+    }
+}

+ 280 - 0
jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext5/Pkcs.java

@@ -0,0 +1,280 @@
+package org.jeecg.modules.ctop.sign.itext5;
+
+import java.io.*;
+import java.math.BigInteger;
+import java.security.*;
+import java.security.cert.*;
+import java.security.cert.Certificate;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.CRLDistPoint;
+import org.bouncycastle.asn1.x509.DistributionPoint;
+import org.bouncycastle.asn1.x509.DistributionPointName;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+
+public class Pkcs {
+
+    private static KeyPair getKey() throws NoSuchAlgorithmException {
+        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA",
+                new BouncyCastleProvider());
+        generator.initialize(1024);
+        // 证书中的密钥 公钥和私钥
+        KeyPair keyPair = generator.generateKeyPair();
+        return keyPair;
+    }
+
+    /**
+     * @param password       密码
+     * @param issuerStr      颁发机构信息
+     * @param subjectStr     使用者信息
+     * @param certificateCRL 颁发地址
+     * @return
+     */
+    public static Map<String, byte[]> createCert(String password,
+                                                 String issuerStr, String subjectStr, String certificateCRL) {
+        Map<String, byte[]> result = new HashMap<String, byte[]>();
+        ByteArrayOutputStream out = null;
+        try {
+            // 生成JKS证书
+            // KeyStore keyStore = KeyStore.getInstance("JKS");
+            // 标志生成PKCS12证书
+            KeyStore keyStore = KeyStore.getInstance("PKCS12",
+                    new BouncyCastleProvider());
+            keyStore.load(null, null);
+            KeyPair keyPair = getKey();
+            // issuer与 subject相同的证书就是CA证书
+            Certificate cert = generateCertificateV3(issuerStr, subjectStr,
+                    keyPair, result, certificateCRL, null);
+            // cretkey随便写,标识别名
+            keyStore.setKeyEntry("cretkey", keyPair.getPrivate(),
+                    password.toCharArray(), new Certificate[]{cert});
+            out = new ByteArrayOutputStream();
+            cert.verify(keyPair.getPublic());
+            keyStore.store(out, password.toCharArray());
+            byte[] keyStoreData = out.toByteArray();
+            result.put("keyStoreData", keyStoreData);
+            return result;
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @param issuerStr
+     * @param subjectStr
+     * @param keyPair
+     * @param result
+     * @param certificateCRL
+     * @param extensions
+     * @return
+     */
+    public static Certificate generateCertificateV3(String issuerStr,
+                                                    String subjectStr, KeyPair keyPair, Map<String, byte[]> result,
+                                                    String certificateCRL, List<Extension> extensions) {
+        ByteArrayInputStream bout = null;
+        X509Certificate cert = null;
+        try {
+            PublicKey publicKey = keyPair.getPublic();
+            PrivateKey privateKey = keyPair.getPrivate();
+            Date notBefore = new Date();
+            Calendar rightNow = Calendar.getInstance();
+            rightNow.setTime(notBefore);
+            // 日期加1年
+            rightNow.add(Calendar.YEAR, 1);
+            Date notAfter = rightNow.getTime();
+            // 证书序列号
+            BigInteger serial = BigInteger.probablePrime(256, new Random());
+            X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
+                    new X500Name(issuerStr), serial, notBefore, notAfter,
+                    new X500Name(subjectStr), publicKey);
+            JcaContentSignerBuilder jBuilder = new JcaContentSignerBuilder(
+                    "SHA1withRSA");
+            SecureRandom secureRandom = new SecureRandom();
+            jBuilder.setSecureRandom(secureRandom);
+            ContentSigner singer = jBuilder.setProvider(
+                    new BouncyCastleProvider()).build(privateKey);
+            // 分发点
+            ASN1ObjectIdentifier cRLDistributionPoints = new ASN1ObjectIdentifier(
+                    "2.5.29.31");
+            GeneralName generalName = new GeneralName(
+                    GeneralName.uniformResourceIdentifier, certificateCRL);
+            GeneralNames seneralNames = new GeneralNames(generalName);
+            DistributionPointName distributionPoint = new DistributionPointName(
+                    seneralNames);
+            DistributionPoint[] points = new DistributionPoint[1];
+            points[0] = new DistributionPoint(distributionPoint, null, null);
+            CRLDistPoint cRLDistPoint = new CRLDistPoint(points);
+            builder.addExtension(cRLDistributionPoints, true, cRLDistPoint);
+            // 用途
+            ASN1ObjectIdentifier keyUsage = new ASN1ObjectIdentifier(
+                    "2.5.29.15");
+            // | KeyUsage.nonRepudiation | KeyUsage.keyCertSign
+            builder.addExtension(keyUsage, true, new KeyUsage(
+                    KeyUsage.digitalSignature | KeyUsage.keyEncipherment));
+            // 基本限制 X509Extension.java
+            ASN1ObjectIdentifier basicConstraints = new ASN1ObjectIdentifier(
+                    "2.5.29.19");
+            builder.addExtension(basicConstraints, true, new BasicConstraints(
+                    true));
+            // privKey:使用自己的私钥进行签名,CA证书
+            if (extensions != null)
+                for (Extension ext : extensions) {
+                    builder.addExtension(
+                            new ASN1ObjectIdentifier(ext.getOid()),
+                            ext.isCritical(),
+                            ASN1Primitive.fromByteArray(ext.getValue()));
+                }
+            X509CertificateHolder holder = builder.build(singer);
+            CertificateFactory cf = CertificateFactory.getInstance("X.509");
+            bout = new ByteArrayInputStream(holder.toASN1Structure()
+                    .getEncoded());
+            cert = (X509Certificate) cf.generateCertificate(bout);
+            byte[] certBuf = holder.getEncoded();
+            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+            // 证书数据
+            result.put("certificateData", certBuf);
+            //公钥
+            result.put("publicKey", publicKey.getEncoded());
+            //私钥
+            result.put("privateKey", privateKey.getEncoded());
+            //证书有效开始时间
+            result.put("notBefore", format.format(notBefore).getBytes("utf-8"));
+            //证书有效结束时间
+            result.put("notAfter", format.format(notAfter).getBytes("utf-8"));
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (bout != null) {
+                try {
+                    bout.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        return cert;
+    }
+
+    /**
+     * 使用java自带的安全包。由于jdk中没有对PKCS#7格式签名数据的处理。在做验证时需要三个参数:原文;签名数据和签名者证书/签名者公钥,都是BASE64编码
+     *
+     * @param signData
+     * @param signedData
+     * @param cert
+     * @return
+     */
+    public boolean SignedData_Verify(byte[] signData, byte[] signedData, byte[] cert) {
+        boolean verifyRet = true;
+        try {
+            // 创建Factory对象
+            CertificateFactory oCf = CertificateFactory.getInstance("X.509");
+            // 创建X509证书对象
+            InputStream ois = new ByteArrayInputStream(cert);
+            X509Certificate oCert = (X509Certificate) oCf.generateCertificate(ois);
+            // 创建签名对象
+            Signature oSign = Signature.getInstance("SHA1withRSA");
+            // 初始化签名对象
+            oSign.initVerify(oCert);
+            // 传入签名原文
+            oSign.update(signData);
+            // 验证数字签名
+            verifyRet = oSign.verify(signedData);
+        } catch (Exception e) {
+            verifyRet = false;
+            e.printStackTrace();
+            System.out.println("验证数字签名失败");
+        }
+        return verifyRet;
+    }
+
+    /**
+     * 通过bouncycastle的CMSSignedData可以实现pkcs#7格式签名数据的验证,利用CMSSignedData生成pkcs#7格式签名数据。由于pkcs#7里面包含了原文和证书信息,所以参数只需要签名值即可
+     *
+     * @param SignedData
+     * @return
+     */
+    public boolean SignedData_Verify(byte[] signedData) {
+        boolean verifyRet = true;
+        try {
+            // 新建PKCS#7签名数据处理对象
+            CMSSignedData sign = new CMSSignedData(signedData);
+
+            // 添加BouncyCastle作为安全提供
+            Security.addProvider(new
+                    org.bouncycastle.jce.provider.BouncyCastleProvider());
+
+            // 获得证书信息
+            CertStore certs = sign.getCertificatesAndCRLs("Collection", "BC");
+
+            // 获得签名者信息
+            SignerInformationStore signers = sign.getSignerInfos();
+            Collection c = signers.getSigners();
+            Iterator it = c.iterator();
+
+            // 当有多个签名者信息时需要全部验证
+            while (it.hasNext()) {
+                SignerInformation signer = (SignerInformation) it.next();
+
+                // 证书链
+                Collection certCollection = certs.getCertificates((CertSelector) signer
+                        .getSID());
+                Iterator certIt = certCollection.iterator();
+                X509Certificate cert = (X509Certificate) certIt.next();
+
+                // 验证数字签名
+                if (signer.verify(cert.getPublicKey(), "BC")) {
+                    verifyRet = true;
+                } else {
+                    verifyRet = false;
+                }
+            }
+
+        } catch (Exception e) {
+            verifyRet = false;
+            e.printStackTrace();
+            System.out.println("验证数字签名失败");
+        }
+        return verifyRet;
+    }
+
+    public static void main(String[] args) throws Exception {
+        // CN: 名字与姓氏    OU : 组织单位名称
+        // O :组织名称  L : 城市或区域名称  E : 电子邮件
+        // ST: 州或省份名称  C: 单位的两字母国家代码
+        String issuerStr = "CN=在线医院,OU=gitbook研发部,O=gitbook有限公司,C=CN,E=gitbook@sina.com,L=北京,ST=北京";
+        String subjectStr = "CN=huangjinjin,OU=gitbook研发部,O=gitbook有限公司,C=CN,E=huangjinjin@sina.com,L=北京,ST=北京";
+        String certificateCRL = "https://gitbook.cn";
+        Map<String, byte[]> result = createCert("123456", issuerStr, subjectStr, certificateCRL);
+
+        FileOutputStream outPutStream = new FileOutputStream("D:/keystore.p12"); // ca.jks
+        outPutStream.write(result.get("keyStoreData"));
+        outPutStream.close();
+        FileOutputStream fos = new FileOutputStream(new File("D:/keystore.cer"));
+        fos.write(result.get("certificateData"));
+        fos.flush();
+        fos.close();
+    }
+}

+ 137 - 0
jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext5/SignHighPdf.java

@@ -0,0 +1,137 @@
+package org.jeecg.modules.ctop.sign.itext5;
+
+import java.io.*;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.cert.Certificate;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import com.itextpdf.awt.AsianFontMapper;
+import com.itextpdf.text.BaseColor;
+import com.itextpdf.text.Element;
+import com.itextpdf.text.Font;
+import com.itextpdf.text.Paragraph;
+import com.itextpdf.text.Rectangle;
+import com.itextpdf.text.pdf.BaseFont;
+import com.itextpdf.text.pdf.ColumnText;
+import com.itextpdf.text.pdf.PdfReader;
+import com.itextpdf.text.pdf.PdfSignatureAppearance;
+import com.itextpdf.text.pdf.PdfStamper;
+import com.itextpdf.text.pdf.PdfStream;
+import com.itextpdf.text.pdf.PdfTemplate;
+import com.itextpdf.text.pdf.security.BouncyCastleDigest;
+import com.itextpdf.text.pdf.security.DigestAlgorithms;
+import com.itextpdf.text.pdf.security.ExternalDigest;
+import com.itextpdf.text.pdf.security.ExternalSignature;
+import com.itextpdf.text.pdf.security.MakeSignature;
+import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
+import com.itextpdf.text.pdf.security.PrivateKeySignature;
+
+
+public class SignHighPdf {
+
+    /**
+     * @param password     秘钥密码
+     * @param keyStorePath 秘钥文件路径
+     * @param signPdfSrc   签名的PDF文件
+     * @param x
+     * @param y
+     * @return
+     */
+    public static byte[] sign(String password, String keyStorePath, String signPdfSrc,
+                              float x, float y,
+                              String signText) {
+        File signPdfSrcFile = new File(signPdfSrc);
+        PdfReader reader = null;
+        ByteArrayOutputStream signPDFData = null;
+        PdfStamper stp = null;
+        FileInputStream fos = null;
+        try {
+            BouncyCastleProvider provider = new BouncyCastleProvider();
+            Security.addProvider(provider);
+            KeyStore ks = KeyStore.getInstance("PKCS12", new BouncyCastleProvider());
+            fos = new FileInputStream(keyStorePath);
+            ks.load(fos, password.toCharArray()); // 私钥密码
+            String alias = (String) ks.aliases().nextElement();
+            PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
+            Certificate[] chain = ks.getCertificateChain(alias);
+            reader = new PdfReader(signPdfSrc);
+            signPDFData = new ByteArrayOutputStream();
+            // 临时pdf文件
+            File temp = new File(signPdfSrcFile.getParent(), System.currentTimeMillis() + ".pdf");
+            stp = PdfStamper.createSignature(reader, signPDFData, '\0', temp, true);
+            PdfSignatureAppearance sap = stp.getSignatureAppearance();
+            sap.setReason("数字签名,不可改变");
+            // 是对应x轴和y轴坐标
+            sap.setVisibleSignature(new Rectangle(x, y, x + 150, y + 65), 1,
+                    "sr" + String.valueOf(System.nanoTime()));
+            /////////////////layer 0 Creating the appearance for layer 0
+            PdfTemplate n0 = sap.getLayer(0);
+            n0.reset();
+            float lx = n0.getBoundingBox().getLeft();
+            float by = n0.getBoundingBox().getBottom();
+            float width = n0.getBoundingBox().getWidth();
+            float height = n0.getBoundingBox().getHeight();
+            n0.setRGBColorFill(255, 0, 0);
+            n0.rectangle(lx, by, 5, height);
+            n0.rectangle(lx, by, width, 5);
+            n0.rectangle(lx, by + height - 5, width, 5);
+            n0.rectangle(lx + width - 5, by, 5, height);
+            n0.fill();
+            ///////////////////////layer 2
+            PdfTemplate n2 = sap.getLayer(2);
+            n2.setCharacterSpacing(0.0f);
+            ColumnText ct = new ColumnText(n2);
+            ct.setSimpleColumn(n2.getBoundingBox());
+            n2.setRGBColorFill(255, 0, 0);
+            //做一个占位的动作
+            Paragraph p1 = new Paragraph(" ");
+            BaseFont bf = BaseFont.createFont(AsianFontMapper.ChineseSimplifiedFont, AsianFontMapper.ChineseSimplifiedEncoding_H,
+                    BaseFont.NOT_EMBEDDED);
+            Font font1 = new Font(bf, 5, Font.BOLD, BaseColor.RED);
+            Font font2 = new Font(bf, 13, Font.BOLD, BaseColor.RED);
+            p1.setFont(font1);
+            ct.addElement(p1);
+            Paragraph p = new Paragraph(signText);
+            p.setAlignment(Element.ALIGN_CENTER);
+            p.setFont(font2);
+            ct.addElement(p);
+            ct.go();
+            stp.getWriter().setCompressionLevel(PdfStream.BEST_COMPRESSION);
+            ExternalDigest digest = new BouncyCastleDigest();
+            ExternalSignature signature = new PrivateKeySignature(key, DigestAlgorithms.SHA512, provider.getName());
+            MakeSignature.signDetached(sap, digest, signature, chain, null, null, null, 0, CryptoStandard.CADES);
+            stp.close();
+            reader.close();
+            return signPDFData.toByteArray();
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (signPDFData != null) {
+                try {
+                    signPDFData.close();
+                } catch (IOException e) {
+                }
+            }
+            if (fos != null) {
+                try {
+                    fos.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        return null;
+    }
+
+    public static void main(String[] args) throws Exception {
+        //对已经签章的signed.pdf文件再次签章,这次是高清签章
+        byte[] fileData = sign("123456", "C:\\Users\\syh\\Desktop\\keystore.p12",//
+                "C:\\Users\\syh\\Desktop\\test.pdf", 350, 290, "华佗\n2017-12-20");
+        FileOutputStream f = new FileOutputStream(new File("C:\\Users\\syh\\Desktop\\signed2.pdf"));
+        f.write(fileData);
+        f.close();
+    }
+
+}

+ 101 - 0
jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext5/SignImage.java

@@ -0,0 +1,101 @@
+package org.jeecg.modules.ctop.sign.itext5;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import sun.font.FontDesignMetrics;
+
+import com.sun.image.codec.jpeg.JPEGCodec;
+import com.sun.image.codec.jpeg.JPEGEncodeParam;
+import com.sun.image.codec.jpeg.JPEGImageEncoder;
+
+public class SignImage {
+
+    /**
+     * @param doctorName   String 医生名字
+     * @param hospitalName String 医生名称
+     * @param date         String 签名日期
+     *                     图片高度
+     * @param jpgname      String jpg图片名
+     * @return
+     */
+    public static boolean createSignTextImg(
+            String doctorName, //
+            String hospitalName, //
+            String date,
+            String jpgname) {
+        int width = 255;
+        int height = 100;
+        FileOutputStream out = null;
+        //背景色
+        Color bgcolor = Color.WHITE;
+        //字色
+        Color fontcolor = Color.RED;
+        Font doctorNameFont = new Font(null, Font.BOLD, 20);
+        Font othorTextFont = new Font(null, Font.BOLD, 18);
+        try { // 宽度 高度
+            BufferedImage bimage = new BufferedImage(width, height,
+                    BufferedImage.TYPE_INT_RGB);
+            Graphics2D g = bimage.createGraphics();
+            g.setColor(bgcolor); // 背景色
+            g.fillRect(0, 0, width, height); // 画一个矩形
+            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                    RenderingHints.VALUE_ANTIALIAS_ON); // 去除锯齿(当设置的字体过大的时候,会出现锯齿)
+
+            g.setColor(Color.RED);
+            g.fillRect(0, 0, 8, height);
+            g.fillRect(0, 0, width, 8);
+            g.fillRect(0, height - 8, width, height);
+            g.fillRect(width - 8, 0, width, height);
+
+            g.setColor(fontcolor); // 字的颜色
+            g.setFont(doctorNameFont); // 字体字形字号
+            FontMetrics fm = FontDesignMetrics.getMetrics(doctorNameFont);
+            int font1_Hight = fm.getHeight();
+            int strWidth = fm.stringWidth(doctorName);
+            int y = 35;
+            int x = (width - strWidth) / 2;
+            g.drawString(doctorName, x, y); // 在指定坐标除添加文字
+
+            g.setFont(othorTextFont); // 字体字形字号
+
+            fm = FontDesignMetrics.getMetrics(othorTextFont);
+            int font2_Hight = fm.getHeight();
+            strWidth = fm.stringWidth(hospitalName);
+            x = (width - strWidth) / 2;
+            g.drawString(hospitalName, x, y + font1_Hight); // 在指定坐标除添加文字
+
+            strWidth = fm.stringWidth(date);
+            x = (width - strWidth) / 2;
+            g.drawString(date, x, y + font1_Hight + font2_Hight); // 在指定坐标除添加文字
+
+            g.dispose();
+            out = new FileOutputStream(jpgname); // 指定输出文件
+            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
+            JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bimage);
+            param.setQuality(50f, true);
+            encoder.encode(bimage, param); // 存盘
+            out.flush();
+            return true;
+        } catch (Exception e) {
+            return false;
+        } finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
+    public static void main(String[] args) {
+        createSignTextImg("华佗", "在线医院", "2018.01.01", "C:\\sign.jpg");
+    }
+}

+ 114 - 0
jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext5/SignPdf.java

@@ -0,0 +1,114 @@
+package org.jeecg.modules.ctop.sign.itext5;
+
+import java.io.*;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.Signature;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.UUID;
+
+import cn.hutool.core.codec.Base64;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import com.itextpdf.text.Image;
+import com.itextpdf.text.Rectangle;
+import com.itextpdf.text.pdf.PdfReader;
+import com.itextpdf.text.pdf.PdfSignatureAppearance;
+import com.itextpdf.text.pdf.PdfSignatureAppearance.RenderingMode;
+import com.itextpdf.text.pdf.PdfStamper;
+import com.itextpdf.text.pdf.security.BouncyCastleDigest;
+import com.itextpdf.text.pdf.security.DigestAlgorithms;
+import com.itextpdf.text.pdf.security.ExternalDigest;
+import com.itextpdf.text.pdf.security.ExternalSignature;
+import com.itextpdf.text.pdf.security.MakeSignature;
+import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
+import com.itextpdf.text.pdf.security.PrivateKeySignature;
+import sun.security.pkcs.ContentInfo;
+import sun.security.pkcs.PKCS7;
+import sun.security.pkcs.SignerInfo;
+import sun.security.x509.AlgorithmId;
+import sun.security.x509.X500Name;
+
+
+public class SignPdf {
+    /**
+     * @param password     秘钥密码
+     * @param keyStorePath 秘钥文件路径
+     * @param signPdfSrc   签名的PDF文件
+     * @param signImage    签名图片文件
+     * @param x            x坐标
+     * @param y            y坐标
+     * @return
+     */
+    public static byte[] sign(String password, String keyStorePath, String signPdfSrc, String signImage,
+                              float x, float y) {
+        File signPdfSrcFile = new File(signPdfSrc);
+        PdfReader reader = null;
+        ByteArrayOutputStream signPDFData = null;
+        PdfStamper stp = null;
+        FileInputStream fos = null;
+        try {
+            BouncyCastleProvider provider = new BouncyCastleProvider();
+            Security.addProvider(provider);
+            KeyStore ks = KeyStore.getInstance("JKS", new BouncyCastleProvider());
+            fos = new FileInputStream(keyStorePath);
+            // 私钥密码 为Pkcs生成证书是的私钥密码 123456
+            ks.load(fos, password.toCharArray());
+            String alias = (String) ks.aliases().nextElement();
+            PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
+            Certificate[] chain = ks.getCertificateChain(alias);
+            reader = new PdfReader(signPdfSrc);
+            signPDFData = new ByteArrayOutputStream();
+            // 临时pdf文件
+            File temp = new File(signPdfSrcFile.getParent(), System.currentTimeMillis() + ".pdf");
+            stp = PdfStamper.createSignature(reader, signPDFData, '\0', temp, true);
+            stp.setFullCompression();
+            PdfSignatureAppearance sap = stp.getSignatureAppearance();
+            sap.setReason("数字签名,不可改变");
+            // 使用png格式透明图片
+            Image image = Image.getInstance(signImage);
+            sap.setImageScale(0);
+            sap.setSignatureGraphic(image);
+            sap.setRenderingMode(RenderingMode.GRAPHIC);
+            // 是对应x轴和y轴坐标
+            sap.setVisibleSignature(new Rectangle(x, y, x + 185, y + 68), 1,
+                    UUID.randomUUID().toString().replaceAll("-", ""));
+            stp.getWriter().setCompressionLevel(5);
+            ExternalDigest digest = new BouncyCastleDigest();
+            ExternalSignature signature = new PrivateKeySignature(key, DigestAlgorithms.SHA512, provider.getName());
+            MakeSignature.signDetached(sap, digest, signature, chain, null, null, null, 0, CryptoStandard.CADES);
+            stp.close();
+            reader.close();
+            return signPDFData.toByteArray();
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+
+            if (signPDFData != null) {
+                try {
+                    signPDFData.close();
+                } catch (IOException e) {
+                }
+            }
+
+            if (fos != null) {
+                try {
+                    fos.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        return null;
+    }
+
+    public static void main(String[] args) throws Exception {
+        byte[] fileData = sign("123456", "C:\\Users\\宋英豪\\Desktop\\sign\\keystore.p12", //
+                "C:\\Users\\宋英豪\\Desktop\\sign\\test.pdf",//
+                "C:\\Users\\宋英豪\\Desktop\\sign\\sign.jpg", 100, 290);
+        FileOutputStream f = new FileOutputStream(new File("C:\\Users\\宋英豪\\Desktop\\sign\\signed.pdf"));
+        f.write(fileData);
+        f.close();
+    }
+}

+ 21 - 0
jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext7/PdfSignInfo.java

@@ -0,0 +1,21 @@
+package org.jeecg.modules.ctop.sign.itext7;
+
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 获取签名信息实体类
+ */
+@Data
+public class PdfSignInfo {
+    private Date signDate;
+    private String digestAlgorithm;
+    private String reason;
+    private String location;
+    private String signatureName;
+    private String encryptionAlgorithm;
+    private String signerName;
+    private String contactInfo;
+    private int revisionNumber;
+}

+ 29 - 0
jeecg-boot-module-system/src/main/java/org/jeecg/modules/ctop/sign/itext7/Sign7.java

@@ -0,0 +1,29 @@
+//package org.jeecg.modules.ctop.sign.itext7;
+//
+//import com.itextpdf.text.DocumentException;
+//import com.itextpdf.text.Image;
+//import com.itextpdf.text.Rectangle;
+//import com.itextpdf.text.pdf.*;
+//
+//import java.io.ByteArrayInputStream;
+//import java.io.FileOutputStream;
+//import java.io.IOException;
+//import java.io.InputStream;
+//import java.security.GeneralSecurityException;
+//
+//public class Sign7 {
+//    public void get() throws Exception {
+//        PdfReader reader = new PdfReader("C:\\Users\\宋英豪\\Desktop\\sign\\test.pdf");
+//        FileOutputStream fout = new FileOutputStream("C:\\Users\\宋英豪\\Desktop\\sign\\signed.pdf");
+//        PdfStamper stp = PdfStamper.createSignature(reader,fout,'\0',null,true);
+//        PdfSignatureAppearance sap = stp.getSignatureAppearance();
+//        sap.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED);
+//        sap.setVisibleSignature(new Rectangle(60,100,773,231),1,"signature1");
+//        sap.setSignatureGraphic(Image.getInstance("C:\\Users\\宋英豪\\Desktop\\sign\\sign.jpg"));
+//        sap.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);
+//
+//        PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE,new PdfName("adbe.pkcs7.detached"));
+//        dic.setReason(sap.getReason());
+//    }
+//
+//}

+ 7 - 7
jeecg-boot-module-system/src/main/resources/static/generic/build/pdf.worker.js

@@ -27390,7 +27390,7 @@ PDFJS.build = '82536f8';
                         // DecodeStream's data.  But if it came from a Stream, we need to
                         // copy it because it'll only be a portion of the Stream's data, and
                         // the rest will be read later on.
-                        if (this.image instanceof DecodeStream) {
+                        if (org.jeecg.modules.ctop.sign.itext5.image instanceof DecodeStream) {
                             imgData.data = imgArray;
                         } else {
                             var newArray = new Uint8Array(imgArray.length);
@@ -27407,7 +27407,7 @@ PDFJS.build = '82536f8';
                         }
                         return imgData;
                     }
-                    if (this.image instanceof JpegStream && !this.smask && !this.mask) {
+                    if (org.jeecg.modules.ctop.sign.itext5.image instanceof JpegStream && !this.smask && !this.mask) {
                         imgData.kind = ImageKind.RGB_24BPP;
                         imgData.data = this.getImageBytes(originalHeight * rowBytes,
                             drawWidth, drawHeight, true);
@@ -27502,11 +27502,11 @@ PDFJS.build = '82536f8';
             getImageBytes: function PDFImage_getImageBytes(length,
                                                            drawWidth, drawHeight,
                                                            forceRGB) {
-                this.image.reset();
-                this.image.drawWidth = drawWidth || this.width;
-                this.image.drawHeight = drawHeight || this.height;
-                this.image.forceRGB = !!forceRGB;
-                return this.image.getBytes(length);
+                org.jeecg.modules.ctop.sign.itext5.image.reset();
+                org.jeecg.modules.ctop.sign.itext5.image.drawWidth = drawWidth || this.width;
+                org.jeecg.modules.ctop.sign.itext5.image.drawHeight = drawHeight || this.height;
+                org.jeecg.modules.ctop.sign.itext5.image.forceRGB = !!forceRGB;
+                return org.jeecg.modules.ctop.sign.itext5.image.getBytes(length);
             }
         };
         return PDFImage;

+ 4 - 4
jeecg-boot-module-system/src/test/java/org/jeecg/SampleTest.java

@@ -78,8 +78,8 @@ public class SampleTest {
         String statisticDate = "2019-08-23";
         Long accountId = 4L;
         Long secondId = 1L;
-        List<StatisticCampaignVo> statisticCampaign4Vos = statisticInfoMapper.getVoByParams(statisticDate, accountId);
-        List<StatisticCampaignVo> statisticCampaign1Vos = statisticInfoMapper.getVoByParams(statisticDate, secondId);
+        List<StatisticCampaignVo> statisticCampaign4Vos = statisticInfoMapper.getVoByParams(accountId);
+        List<StatisticCampaignVo> statisticCampaign1Vos = statisticInfoMapper.getVoByParams(secondId);
 
         File f = new File("D:\\upload\\excel\\template.xlsx");
         InputStream inputStream = new FileInputStream(f);
@@ -98,7 +98,7 @@ public class SampleTest {
             setExcelRow(numbericCellStyle, vo, contents, row, "single");
         }
         Row totalRow = xssfSheet.getRow(index);
-        StatisticCampaignVo total = statisticInfoMapper.getTotalStatisticVoByparams(statisticDate, accountId);
+        StatisticCampaignVo total = statisticInfoMapper.getTotalStatisticVoByparams(accountId);
         if (null != total) {
             setExcelRow(numbericCellStyle, total, new String[]{}, totalRow, "total4");
         }
@@ -114,7 +114,7 @@ public class SampleTest {
         }
         index++;
         Row totalRow1 = xssfSheet.getRow(index);
-        StatisticCampaignVo total1 = statisticInfoMapper.getTotalStatisticVoByparams(statisticDate, secondId);
+        StatisticCampaignVo total1 = statisticInfoMapper.getTotalStatisticVoByparams(secondId);
         if (null != total1) {
             setExcelRow(numbericCellStyle, total1, new String[]{}, totalRow1, "total1");
         }