个人网站要在公安备案吗/产品推广策划方案
目录
- 一、OFD 简介
- 1.1 什么是 OFD?
- 1.2 什么是 版式文档?
- 1.3 为什么要用 OFD 而不是PDF?
- 二、ofdrw 简介
- 2.1 定义
- 2.2 Maven 依赖
- 2.3 ofdrw 的 13 个模块
- 三、PDF/文本/图片 转 OFD(ofdrw-conterver)
- 3.1 介绍:
- 3.2 Maven 依赖:
- 3.3 PDF转换OFD
- 3.5 文本转换OFD
- 3.6 图片转换OFD
- 四、OFD 转 图片/HTML/文本/PDF(ofdrw-conterver)
- 4.1 介绍:
- 4.2 Maven 依赖:
- 4.3 导出为图片
- 4.4 导出为SVG图形
- 4.5 导出为HTML网页
- 4.6 导出为文本
- 4.7 [不推荐] 导出为PDF
- 五、OFD签署
- 5.1 在线生成 sm2 证书
- 5.2 制作 esl 印章
- 5.3 坐标签署OFD
- 5.4 骑缝签署OFD
- 5.5 验签 OFD

- OFDRW-Gitee地址: https://gitee.com/ofdrw/ofdrw
- OFDRW-GitHub地址: https://github.com/ofdrw/ofdrw
- gmhelper-GitHub地址: https://github.com/ZZMarquis/gmhelper
一、OFD 简介
1.1 什么是 OFD?
OFD
是开放版式文档(Open Fixed-layout Document)
的英文缩写,是我国国家版式文档格式标准——《GB/T 33190-2016电子文件存储与交换格式-版式文档》。
1.2 什么是 版式文档?
版式文档
是与Word(doc、docx)
等流式文件
相对的,具有格式独立、版本固定、固化呈现的文档。版式文档不宜修改,且在不同设备中显示效果不变,而 流式文档会根据设备版面显示发生变化。
举例来说:
一个 doc 格式的 Word 文档,使用 Word 与 WPS 打开,容易发生版面(样式)变化、内容重排现象,同一篇 Word 文档,在 Office 的不同版本中打开也会发生不一致的情况。而版式文档则是不受设备影响,版式固定。在版式、版面、字体、字号等方面与纸质文档保持完全一致。版式文档格式的特点使它称为 严肃类电子文档 发布、数字化信息传播和存档的理想文档格式。
版式文档的代表就是我们工作和生活中非常熟悉的 PDF
文档,OFD
文档则是 我国自主研发,自主指定的版式文件格式标准。在 PDF基础上加入了许多基于我国社会发展需要的应用场景功能。可以说 OFD 与 PDF 定位一致,同为版式文档格式,但 OFD 后发制人,青出于蓝。
1.3 为什么要用 OFD 而不是PDF?
既然 PDF 和 OFD 都是版式文件,那么问题来了,PDF 用得好好的,我们为什么要自己再做一个 OFD 的文件格式呢?
主要出于以下几点原因:
-
格式不统一: 目前在国内可使用的版式文件格式包括
PDF
、CEB
等在内有不下十种,来自不同厂商和不同的技术,没有统一标准,就会导致不同机构、不同企业之间的文件交流存在阻碍,文件长期存档很困难。 -
难以自主可控: 我们知道,在目前办公文档市场上,来自 Adobe 的 PDF 五一占据绝对主流,而在 PDF 阅读工具的市场上,Adobe 旗下的
Adobe Acrobat DC
又以 55.18% 的市占率位于领先地位,国内厂商 Foxit福昕
(昕,xin,一声)虽然排名第二,但占有率仅有 1.92%。这背后有一个严重的问题,就是在版式文件技术上,我们目前难以做到自主可控,如果我们相对文件做一些针对国内特殊领域的技术扩展时,极容易受制于外部厂商,或者如果未来 Adobe 停止技术授权,那可能有很多文档遭受损失。
此外,OFD 作为我们自主研发、自主可控的版式文件格式,相比 PDF 等其他版式文件,OFD 有一些技术上的优势:
- 第一:OFD 文档内部采用可扩展标记语言
XML
来描述数据和结构,体积精简,安全开放,易于扩展。 - 第二:OFD 支持国产加密算法,具有全面的安全保障体系,可防止信息被窃取,并且和数字签名技术集合,可防篡改,更加安全。
- 第三:永久刻度可用,可对文件长久保存,且可以精准呈现,文件的版式内容在不同场景、设备下都能保持一致性。
- 第四:支持直接进行文件归档的一系列处理。
这些优势总结起来,就是:在 PDF 基础上加入了许多基于我国社会发展需要的应用场景功能。
二、ofdrw 简介
2.1 定义
ofdrw
,全称OFD Reader & Writer
,意为 OFD 读写器,是开源的 OFD 处理库,支持 文档生成、数字签名、文档保护、文档合并、转换、导出 等功能。
Gitee地址: https://gitee.com/ofdrw/ofdrw
GitHub地址: https://github.com/ofdrw/ofdrw
2.2 Maven 依赖
Maven 依赖如下:
<!-- ofdrw -->
<dependency><groupId>org.ofdrw</groupId><artifactId>ofdrw-full</artifactId><version>2.3.3</version>
</dependency>
2.3 ofdrw 的 13 个模块
- ofdrw-core OFD核心API,参考《GB/T 33190-2016 电子文件存储与交换格式版式文档》实现的基础数据结构。
- ofdrw-font 生成OFD字体相关。
- ofdrw-layout OFD布局引擎库,用于文档构建和渲染。
- ofdrw-pkg OFD文件的容器,用于文档的打包。
- ofdrw-reader OFD文档解析器,用于OFD的反序列化以及签名签章。
- ofdrw-sign OFD文档数字签章。
- ofdrw-gm 用于支持签章模块需要的国密电子签章数据结构。
- ofrw-crypto 用于实现《GM/T 0099-2020 开放版式文档密码应用技术规范》对OFD的密码相关功能。
- ofdrw-gv OFDRW 所有模块所共用的全局变量。
- ofdrw-converter OFD文档转换。
- ofdrw-tool OFD文档工具,文档合并、裁剪、重组。
- ofdrw-graphics2d 实现了AWT Graphics2D接口,生成OFD文档内容。
- ofdrw-full 上述所有模块整合包,用于简化依赖引入。
由于篇幅约束,这里我们只介绍 ofdrw-converter
、ofdrw-sign
两个模块对应的 OFD 转换 和 OFD 签署 两个功能。
三、PDF/文本/图片 转 OFD(ofdrw-conterver)
- 官方文档: https://gitee.com/ofdrw/ofdrw/blob/master/ofdrw-converter/doc/CONVERTER.md
3.1 介绍:
ofdrw-converter
提供了 将其它类型媒体文件或文档转换成 OFD 文档内容 的功能。ofdrw-converter 模块在2.0.0
之后开始提供其它文档或媒体类型向 OFD 文档转换功能。
核心接口类: org.ofdrw.converter.ofdconverter.DocConverter
核心方法签名:
void convert(Path filepath, int... indexes) throws GeneralConvertException;
3 个实现类:
接口实现类命名格式: 原媒体格式+Converter
- PDF文档:
PDFConverter
- 纯文本:
TextConverter
- 图片:
ImageConverter
注意:
convert()
方法的参数页码均从 0 起,例如文档中的第 1 页的 Index 也就是 0,并非所有媒体格式都有页码,在转换无页码的没给是,页码参数无效。
3.2 Maven 依赖:
<dependency><groupId>org.ofdrw</groupId><artifactId>ofdrw-converter</artifactId><version>2.3.3</version>
</dependency>
3.3 PDF转换OFD
将PDF中页面转换为OFD页面,采用PDFBox PDFRenderer
接口,以AWT graphics2d接口桥接,并通过ofdrw-graphics2d 模块完成转换功能。
实现类:org.ofdrw.converter.ofdconverter.PDFConverter
注意事项:
- 转换后的页面将采用PDF中页面尺寸。
- 目前该转换器任然有改进空间,可能存在部分特性在转换过程中丢失,显示效果与原PDF文档不一致。
示例:
import org.ofdrw.converter.ofdconverter.PDFConverter;import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;/*** pdf 转 ofd*/
private static void pdf2Ofd() {Path src = Paths.get("D:\\test.pdf");Path dst = Paths.get("D:\\test.ofd");try (PDFConverter converter = new PDFConverter(dst)) {converter.convert(src);} catch (IOException e) {log.error("pdf 转 ofd 失败,请稍后重试,原因:{}", e.getMessage(), e);throw new RuntimeException("pdf 转 ofd 失败,请稍后重试", e);}System.out.println("pdf 转 ofd 成功,路径: " + dst.toAbsolutePath());
}
特有方法 | 用途 |
---|---|
void setEnableCopyAttachFiles(boolean enableCopyAttachFiles) | 设置是否复制附件(默认复制)。 |
void setEnableCopyBookmarks(boolean enableCopyBookmarks) | 设置是否复制书签(默认复制)。 |
void setUUPMM(double UUPMM) | 设置毫米表示的用户单元数(PDF单位) |
详见 测试用例
执行结果:
3.5 文本转换OFD
将文本转换为OFD。
实现类:org.ofdrw.converter.ofdconverter.TextConverter
注意事项:
- 文本文件为无格式文件,若您需要设置文本格式请使用ofdrw-layout模块。
示例:
import org.ofdrw.converter.ofdconverter.TextConverter;import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;/*** text 转 ofd*/
private static void text2Ofd() {Path src = Paths.get("D:\\test.txt");Path dst = Paths.get("D:\\test.ofd");try (TextConverter converter = new TextConverter(dst)) {converter.convert(src);converter.convert(src);converter.convert(src);} catch (IOException e) {log.error("text 转 ofd 失败,请稍后重试,原因:{}", e.getMessage(), e);throw new RuntimeException("text 转 ofd 失败,请稍后重试", e);}System.out.println("text 转 ofd 成功,路径: " + dst.toAbsolutePath());
}
特有方法 | 用途 |
---|---|
void append(String txt) | 追加文本,文本将新起一行。 |
void setPageSize(PageLayout pageLayout) | 设置OFD页面尺寸。 |
void setFontSize(double fontSize) | 设置字号,单位毫米。 |
详见 测试用例
执行结果:
3.6 图片转换OFD
导入图片到OFD中,图片格式支持PNG、BPM、JPG。
实现类:org.ofdrw.converter.ofdconverter.ImageConverter
注意事项:
- 可以通过构造器指定导出的图片类型,目前支持
PNG
、JPG
、BPM
,默认为PNG
格式。- 若图片格式不在上述范围您可能需要通过手动的方式设置加入图片大小。
- 可以通过方法设置导出图片的质量,也就是
ppm
参数,默认ppm
为15(15像素1毫米)。- 每个添加的图片都将独立为一页,并且居中。
示例:
import org.ofdrw.converter.ofdconverter.ImageConverter;import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;/*** img 转 ofd*/
private static void img2Ofd() {Path src = Paths.get("D:\\signature.png");Path dst = Paths.get("D:\\test.ofd");try (ImageConverter converter = new ImageConverter(dst)) {// 可以加多个图片,每张图片一页converter.convert(src);converter.convert(src);converter.convert(src);} catch (IOException e) {log.error("img 转 ofd 失败,请稍后重试,原因:{}", e.getMessage(), e);throw new RuntimeException("img 转 ofd 失败,请稍后重试", e);}System.out.println("img 转 ofd 成功,路径: " + dst.toAbsolutePath());
}
特有方法 | 用途 |
---|---|
void setPPM(double ppm) | 设置图片质量,单位为:每毫米像素数量。 |
void append(Path filepath, double width, double height) | 追加图片到新页面并指定显示大小。 |
void setPageSize(PageLayout pageLayout) | 设置OFD页面尺寸。 |
详见 测试用例
执行结果:
四、OFD 转 图片/HTML/文本/PDF(ofdrw-conterver)
- 官方文档: https://gitee.com/ofdrw/ofdrw/blob/master/ofdrw-converter/doc/EXPORTER.md
4.1 介绍:
ofdrw-converter
的 OFDExporter
接口具有多个实现,其实现与导出的目的文档有关。
接口实现类命名格式为: 目标格式+Exporter
ofdrw-converter
支持导出为以下类型:
- 图片:
ImageExporter
- SVG矢量图形:
SVGExporter
- HTML网页:
HTMLExporter
- 纯文本:
TextExporter
- PDF文档:
PDFExporterIText
、PDFExporterPDFBox
4.2 Maven 依赖:
<dependency><groupId>org.ofdrw</groupId><artifactId>ofdrw-converter</artifactId><version>2.3.3</version>
</dependency>
4.3 导出为图片
导出OFD文档页面为图片,图片格式支持PNG、BPM、JPG。
实现类:org.ofdrw.converter.export.ImageExporter
注意事项:
- 可以通过构造器指定导出的图片类型,目前支持
PNG
、JPG
、BPM
,默认为PNG
格式。 - 可以通过构造器或方法设置导出图片的质量,也就是
ppm
参数,默认ppm
为15。 - 导出图片将存放于同一个目录,在该目录中图片以的页面索引作为文件名,如第1页的文件名为
0.png
。
示例:
Path ofdPath = Paths.get("src/test/resources/999.ofd");
Path imgDirPath = Paths.get("target/999.ofd/");
try (ImageExporter exporter = new ImageExporter(ofdPath, imgDirPath, "PNG", 20d)) {exporter.export();
}
效果如下:
特有方法 | 用途 |
---|---|
List<Path> getImgFilePaths() | 获取导出页面对应图片文件路径,列表中次序与导出时的页码次序一致。 |
void setPPM(double ppm) | 设置导出图片质量,单位为:每毫米像素数量。 |
详见 测试用例
4.4 导出为SVG图形
导出OFD文档页面为SVG图形,文本中的所有文字都将转换为矢量路径。
实现类:org.ofdrw.converter.export.SVGExporter
注意事项:
- 可以通过构造器或方法设置导出SVG图形大小,也就是
ppm
参数,默认ppm
为15。 - 导出SVG图形文件将存放于同一个目录,在该目录中以页面索引作为文件名,如第1页的文件名为
0.svg
。
示例:
Path ofdPath = Paths.get("src/test/resources/999.ofd");
Path svgPath = Paths.get("target/999.ofd/");
try (SVGExporter exporter = new SVGExporter(ofdPath, svgPath, 15d)) {exporter.export();
}
效果如下:(背景已经变成透明色了)
特有方法 | 用途 |
---|---|
List<Path> getSvgFilePaths() | 获取导出页面对应SVG文件路径,列表中次序与导出时的页码次序一致。 |
void setPPM(double ppm) | 设置导出SVG大小,单位为:每毫米像素数量。 |
详见 测试用例
4.5 导出为HTML网页
导出OFD文档页面为HTML网页,需要浏览器支持HTML5才可正常预览,由于是基于SVG方案导出的HTML,因此导出文件可能较大。
实现类:org.ofdrw.converter.export.HTMLExporter
注意事项:
- 若您需要调整HTML网页样式,可以通过继承
HTMLExporter
并覆盖header
、booter
、margin_bottom
属性,使用自定义的HTML样式。 - 导出的HTML网页需要浏览器支持HTML5才可正常预览。
- 若页面文字内容由文字图元构成且都由Unicode组成,那么导出网页可能可以通过鼠标选中与复制。
示例:
Path ofdPath = Paths.get("src/test/resources/n.ofd");
Path htmlPath = Paths.get("target/n.html");
try (HTMLExporter exporter = new HTMLExporter(ofdPath, htmlPath)) {exporter.export();
}
效果如下:
详见 测试用例
4.6 导出为文本
导出OFD文档页面为文本文件,并非所有OFD页面都能导出文本,只有符合特定条件的OFD才可导出。
实现类:org.ofdrw.converter.export.TextExporter
注意事项:
- 部分OFD文档由于采用字形索引来定位文字、有个OFD整个页面均为路径数据图元而不是文字图元、有的OFD页面整个都为图片等诸多原因,无法保证一定能够导出文本。
- 由于文本布局等各种因素,导出文本顺序也难以与原文文本顺序一致。
示例:
Path ofdPath = Paths.get("src/test/resources/999.ofd");
Path txtPath = Paths.get("target/999.txt");
try (TextExporter exporter = new TextExporter(ofdPath, txtPath)) {exporter.export();
}
效果如下:
详见 测试用例
4.7 [不推荐] 导出为PDF
警告:不推荐导出为PDF,OFD本身就是国产的板式文件,非特殊场景没有必要导出为PDF文件,该模块将进入LTS状态,不再持续更新!
导出OFD文档页面为PDF文件,该导出根据实现所使用的库不一致具有两种导出实现。
实现类:
org.ofdrw.converter.export.PDFExporterIText
org.ofdrw.converter.export.PDFExporterPDFBox
注意事项:
- 导出无法保证文档效果一致性,若您有建设性意见请提交PR。
基于PDFBox实现示例:
Path ofdPath = Paths.get("src/test/resources/999.ofd");
Path pdfPath = Paths.get("target/999.pdf");
try (OFDExporter exporter = new PDFExporterPDFBox(ofdPath, pdfPath)) {exporter.export();
}
详见 基于PDFBox导出 测试用例
基于iText实现示例:
Path ofdPath = Paths.get("src/test/resources/999.ofd");
Path pdfPath = Paths.get("target/999.pdf");
try (OFDExporter exporter = new PDFExporterIText(ofdPath, pdfPath)) {exporter.export();
}
详见 基于iText导出 测试用例
效果如下:
五、OFD签署
5.1 在线生成 sm2 证书
- 在线生成地址: https://www.gmcrt.cn/gmcrt/index.jsp
生成证书如下所示:
5.2 制作 esl 印章
Java 实现代码如下:
/*** 生成ESL印章*/
public static void buildEsl() throws IOException, CertificateEncodingException {String imagePath = "D:\\signature.jpg";String sealerCertPath = "D:\\keystore\\sm2.p12";String imageBase64 = java.util.Base64.getEncoder().encodeToString(FileUtils.readFileToByteArray(new File(imagePath)));Path userP12Path = Paths.get(sealerCertPath);PrivateKey sealerPrvKey;try {byte[] bytes = FileUtils.readFileToByteArray(new File(sealerCertPath));sealerPrvKey = NativePKCS12Tools.ReadPrvKey(java.util.Base64.getEncoder().encodeToString(bytes), "ACGkaka", "123456");} catch (Exception e) {throw new RuntimeException(e);}Certificate signCert = null ;try {signCert = PKCS12Tools.ReadUserCert(userP12Path, "ACGkaka", "123456");} catch (GeneralSecurityException e) {throw new RuntimeException(e);}byte[] encoded = signCert.getEncoded();String s1 = java.util.Base64.getEncoder().encodeToString(encoded);System.out.println("s1="+s1);System.out.println("imageBase64:"+imageBase64);long start = System.currentTimeMillis();Calendar now = Calendar.getInstance();now.add(Calendar.YEAR, 2);Date then = now.getTime();BuildSESealBase64 seal = new BuildSESealBase64().setEsID(UUID.randomUUID().toString().replace("-", ""))//印章ID.setSealImageBase64(imageBase64)//印章图片base64.setSealName("ACGkaka测试章")//印章名称.setBuildSealerCertBase64(s1)//制章人公钥.setHeight(40)//印章高度单位毫米.setWidth(40)//印章宽度单位毫米.setSesheader("chinamobile sign")//印章头部信息.setUseSealerCertBase64(s1)//用章人公钥.setValidStartDate(new Date())//印章有效期 传空,固定读用章人证书有效.setValidEndDate(then);//印章有效期传空,固定读用章人证书有效try {PrivateKey finalSealerPrvKey = sealerPrvKey;Map<String, Object> stringObjectMap = SESeal.buildBase64(seal, new CASignInterface() {//制章人私钥签名方法(需要调用CA密码机服务)@Overridepublic byte[] sign(byte[] data) {Signature sg = null;try {sg = Signature.getInstance("SM3WithSM2", new BouncyCastleProvider());} catch (NoSuchAlgorithmException e) {e.printStackTrace();}try {sg.initSign(finalSealerPrvKey);} catch (InvalidKeyException e) {e.printStackTrace();}try {sg.update(data);} catch (SignatureException e) {e.printStackTrace();}byte[] sigVal = new byte[0];try {sigVal = sg.sign();} catch (SignatureException e) {e.printStackTrace();}System.out.println(sigVal.length);return sigVal;}});String s = stringObjectMap.get("base64").toString();FileUtils.writeByteArrayToFile(new File("D:\\keystore\\sm2-signature.esl"), java.util.Base64.getDecoder().decode(s));} catch (Exception e) {e.printStackTrace();}long end = System.currentTimeMillis();System.out.println("执行耗时间="+(end-start)/1000);
}
生成 ESL 印章如下所示:
5.3 坐标签署OFD
Java 实现代码如下所示:
/*** OFD坐标签*/
private static void signByXy() throws GeneralSecurityException, IOException {Pos pos = new Pos();pos.setPage(1);pos.setX(100);pos.setY(100);pos.setWidth(40);pos.setHeigh(40);List<Pos> apList = new ArrayList<>();apList.add(pos);String req = "D:\\test.ofd";String reqbase = java.util.Base64.getEncoder().encodeToString(FileUtils.readFileToByteArray(new File(req)));String eslpath = "D:\\keystore\\sm2-signature.esl";String eslbase = java.util.Base64.getEncoder().encodeToString(FileUtils.readFileToByteArray(new File(eslpath)));Path userP12Path = Paths.get("D:\\keystore\\sm2.p12");PrivateKey sealerPrvKey = null ;try {sealerPrvKey = PKCS12Tools.ReadPrvKey(userP12Path, "ACGkaka", "123456");} catch (GeneralSecurityException e) {throw new RuntimeException(e);}Certificate signCert = null ;try {signCert = PKCS12Tools.ReadUserCert(userP12Path, "ACGkaka", "123456");} catch (GeneralSecurityException e) {throw new RuntimeException(e);}byte[] encoded = signCert.getEncoded();String s1 = java.util.Base64.getEncoder().encodeToString(encoded);System.out.println("s1="+s1);BuildOFDSignerBase64 buildOFDSigner = new BuildOFDSignerBase64().setOfdReqBase64(reqbase).setEslBase64(eslbase).setSignMode(0).setUseSealerCertBase64(s1).setApList(apList);byte[] bytes = null;try {PrivateKey finalSealerPrvKey = sealerPrvKey;bytes = OFDSigner.signByXyBase64(buildOFDSigner, new CASignInterface() {@Overridepublic byte[] sign(byte[] data) {Signature sg = null;try {sg = Signature.getInstance("SM3WithSM2", new BouncyCastleProvider());} catch (NoSuchAlgorithmException e) {e.printStackTrace();}try {sg.initSign(finalSealerPrvKey);} catch (InvalidKeyException e) {e.printStackTrace();}try {sg.update(data);} catch (SignatureException e) {e.printStackTrace();}byte[] sigVal = new byte[0];try {sigVal = sg.sign();} catch (SignatureException e) {e.printStackTrace();}System.out.println(sigVal.length);return sigVal;}});}catch (Exception e){e.printStackTrace();}FileUtils.writeByteArrayToFile(new File("D:\\test3.ofd"),bytes);
}
签署结果如下所示:
5.4 骑缝签署OFD
Java实现代码如下,除了 main() 方法之外还有 3 个方法:
public static void main(String[] args) throws IOException, GeneralSecurityException {byte[] pdfBytes = FileUtils.readFileToByteArray(new File("D:\\test_2页.ofd"));byte[] certBytes = FileUtils.readFileToByteArray(new File("D:\\keystore\\sm2.p12"));String certBase64 = Base64.getEncoder().encodeToString(certBytes);// 坐标签署
// List<ItemMapDTO> itemList = getItemList();
// byte[] newPdfBytes = OFDSignUtil.signSm2PDFBySeal(pdfBytes, itemList, "els", certBase64);
// FileUtils.writeByteArrayToFile(new File("D:\\test\\test2.ofd"), newPdfBytes);// 骑缝签署String side = "Right";double offset = 40.0d;byte[] eslBytes = FileUtils.readFileToByteArray(new File("D:\\keystore\\sm2-signature.esl"));String eslBase64 = Base64.getEncoder().encodeToString(eslBytes);String pdfBase64 = Base64.getEncoder().encodeToString(pdfBytes);byte[] bytes1 = OFDSignUtil.signByRidingStampPos(pdfBase64, eslBase64, certBase64, side, offset);FileUtils.writeByteArrayToFile(new File("D:\\test\\test3.ofd"), bytes1);
}/*** 骑缝签署** @param reqBase64 待签署文件base64* @param eslBase64 印章文件base64* @param certBase64 用户证书base64* @param side Left左骑缝 Right右骑缝(默认)* @return 签署后文件*/
public static byte[] signByRidingStampPos(String reqBase64, String eslBase64, String certBase64, String side, double offset) {log.info("OFD骑缝签署开始");BuildOFDSignerRideBase64 buildOFDSigner = new BuildOFDSignerRideBase64().setOfdReqBase64(reqBase64).setEslBase64(eslBase64).setSignMode(0).setUseSealerCertBase64(fileToInputStream(certBase64)).setSide(side).setOffset(offset);try {byte[] bytes = OFDSigner.signByRidingStampPosBase64(buildOFDSigner, data -> {try {PrivateKey sealerPrvKey = NativePKCS12Tools.ReadPrvKey(certBase64, "ACGkaka", "123456");Signature sg = null;try {sg = Signature.getInstance("SM3WithSM2", new BouncyCastleProvider());} catch (NoSuchAlgorithmException e) {e.printStackTrace();}try {sg.initSign(sealerPrvKey);} catch (InvalidKeyException e) {e.printStackTrace();}try {sg.update(data);} catch (SignatureException e) {e.printStackTrace();}byte[] sigVal = new byte[0];try {sigVal = sg.sign();} catch (SignatureException e) {e.printStackTrace();}System.out.println(sigVal.length);return sigVal;} catch (Exception e) {log.error("OFD骑缝签署异常", e);throw new IllegalArgumentException("骑缝签署异常");}});log.info("骑缝签署完成,签署后文件大小:{}kb", bytes == null ? 0 : bytes.length / 1024);return bytes;} catch (Exception e) {log.error("OFD骑缝签署异常", e);throw new IllegalArgumentException("骑缝签署异常");}
}/*** 读取证书内容* @param base64String* @return*/
public static String fileToInputStream(String base64String){try {Certificate signCert = PKCS12Tools.ReadUserCert(base64ToInputStream(base64String), "ACGkaka", "123456");byte[] encoded = signCert.getEncoded();return Base64.getEncoder().encodeToString(encoded);} catch (IOException | GeneralSecurityException e) {throw new RuntimeException(e);}
}
签署结果如下所示:
5.5 验签 OFD
Java 实现代码如下:
/*** 验签OFD*/
public static void signVerify() throws IOException, GeneralSecurityException {Path out = Paths.get("D:\\test3.ofd");// 验证try (OFDReader reader = new OFDReader(out);OFDValidator validator = new OFDValidator(reader)) {validator.setValidator(new SESV4ValidateContainer());validator.exeValidate();System.out.println(">> 验证通过");}
}
验签结果如下:
整理完毕,完结撒花~ 🌻
参考地址:
1.科普 | ofd文件是什么,https://zhuanlan.zhihu.com/p/145599784
2.一文读懂 OFD 文件格式:国产 PDF,关键,重要,https://www.ithome.com/0/521/264.htm
相关文章:

SpringBoot实战(三十二)集成 ofdrw,实现 PDF 和 OFD 的转换、SM2 签署OFD
目录 一、OFD 简介1.1 什么是 OFD?1.2 什么是 版式文档?1.3 为什么要用 OFD 而不是PDF? 二、ofdrw 简介2.1 定义2.2 Maven 依赖2.3 ofdrw 的 13 个模块 三、PDF/文本/图片 转 OFD(ofdrw-conterver)3.1 介绍:…...

linux环境人大金仓数据库修改密码
1.进入人大金仓安装目录 cd /home/opt/Kingbase/ES/V9/Server/bin2.连接数据库 ./ksql -U system -d mydb -h 127.0.0.1 -p 54321-u 用户名 -d 数据库名 -h ip地址 -p 端口号 3.修改密码 ALTER USER system WITH PASSWORD 密码;...

使用ESP32通过Arduino IDE点亮1.8寸TFT显示屏
开发板选择 本次使用开发板模块丝印为ESP32-WROOM-32E 开发板库选择 Arduino IDE上型号选择为ESP32-WROOM-DA Module 显示屏选择 使用显示屏为8针SPI接口显示屏 驱动IC为ST7735S 使用库 使用三个Arduino平台库 分别是 Adafruit_GFXAdafruit_ST7735SPI 代码详解 首…...

Spring Cloud Alibaba(六)
目录: 分布式链路追踪-SkyWalking为什么需要链路追踪什么是SkyWalkingSkyWalking核心概念什么是探针Java AgentJava探针日志监控实现之环境搭建Java探针日志监控实现之探针实现编写探针类TestAgent搭建 ElasticsearchSkyWalking服务环境搭建搭建微服务微服务接入Sky…...

【包教包会】CocosCreator3.x——重写Sprite,圆角、3D翻转、纹理循环、可合批调色板、不影响子节点的位移旋转缩放透明度
一、效果演示 重写Sprite组件,做了以下优化: 1、新增自变换,在不影响子节点的前提下位移、旋转、缩放、改变透明度 新增可合批调色板,支持色相、明暗调节 新增圆角矩形、3D透视旋转、纹理循环 所有功能均支持合批、原生平台&…...

jupyter-lab 环境构建
我平时用来调试各种代码的。 创建环境,安装库 conda create --name jupyterlab python3.12 -y conda activate jupyterlab conda install -c conda-forge jupyterlab nodejs之前用的是3.10的,但是最近安装的时候,发现3.10的python里面的jup…...

【C++】LeetCode:LCR 026. 重排链表
题干 LCR 026. 重排链表 给定一个单链表 L 的头节点 head ,单链表 L 表示为: L0 → L1 → … → Ln-1 → Ln 请将其重新排列后变为: L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → … 不能只是单纯的改变节点内部的值,而是需要实…...

【Linux】vim编辑器
vim是什么? vim就是命令行模式下的文本编辑器,相当于windows中的记事本,可以用来进行文本编辑。 vim有三种运行模式,分别可以执行不同的操作: 普通模式(Normal Mode):用于浏览和编辑…...

delphi 12 idhttpsever(S)+idhttp(C) 实现简单的JSON API服务
这篇博客展示了如何使用Delphi创建一个简单的HTTP服务器,并处理GET和POST请求。服务器监听6600端口,响应JSON格式的数据。客户端通过IdHttp组件进行GET和POST请求,获取并显示服务器响应的内容。 http服务器测试代码 procedure TForm1.FormSh…...

JVM 主副内存 详解
在 JVM (Java Virtual Machine) 中,内存的设计主要分为主内存和工作内存(又称为线程内存)。这种设计是基于 Java 内存模型(Java Memory Model, JMM) 的规定,它确保了多线程环境下数据的一致性和线程间的通信…...

sscanf与sprintf函数
本期介绍🍖 主要介绍:sscanf()、sprintf()这对输入/输出函数,并详细讲解了这两个函数的应用场景。 概述🍖 在C语言的输出和输入库中,有三对及其相似的库函数:printf()、scanf()、fprintf()、fscanf()、spri…...

【k8s】创建基于sa的token的kubeconfig
需求 创建一个基于sa的token的kubeconfig文件,并用这个文件来访问集群。 具体创建sa 和sa的token请参考文章: 【k8s】给ServiceAccount 创建关联的 Secrets-CSDN博客 创建sa apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata:namespace: jtkjdevnam…...

Gentoo Linux部署LNMP
一、安装nginx 1.gentoo-chxf ~ # emerge -av nginx 提示配置文件需更新 2.gentoo-chxf ~ # etc-update 3.gentoo-chxf ~ # emerge -av nginx 4.查看并启动nginx gentoo-chxf ~ # systemctl status nginx gentoo-chxf ~ # systemctl start nginx gentoo-chxf ~ # syst…...

2411C++,CXImage简单使用
介绍 CxImage是一个可非常简单快速的加载,保存,显示和转换图像的C类. 文件格式和链接的C库 Cximage对象基本上是加了一些成员变量来保存有用信息的一个位图: class CxImage{...protected:void* pDib; //包含标题,调色板,像素BITMAPINFOHEADER head; //标准头文件CXIMAGEINFO…...

什么是 Kubernetes(K8s)?
什么是 Kubernetes(K8s)? Kubernetes(简称 K8s) 是一个用来管理容器的开源工具,它可以自动化部署、扩展和管理容器化应用。简单来说,K8s 就是一个“容器管家”,负责确保你的应用程序…...

深入解析:TypeScript 与 Vue 的完美结合
文章目录 前言一、准备工作二、基本用法三、进阶主题结语 前言 Vue.js 是一款流行的渐进式 JavaScript 框架,它以易于学习和灵活的特性而闻名。TypeScript 则是 JavaScript 的一个超集,它引入了静态类型检查等高级功能,有助于构建更大型且复…...

机器学习周志华学习笔记-第13章<半监督学习>
机器学习周志华学习笔记-第13章<半监督学习> 卷王,请看目录 13半监督学习13.1 生成式方法13.2 半监督SVM13.3 基于分歧的方法13.4 半监督聚类 13半监督学习 前面我们一直围绕的都是监督学习与无监督学习,监督学习指的是训练样本包…...

软件工程——期末复习(1)
名词解释: 名词解释--人月 答案:人月是软件开发工作量的单位,1人月表示1个程序员1个月的工作时间所开发的代码量。 请解释软件缺陷、错误和失败,并简单举例说明。 答案:缺陷(defect)指系统代…...

【JavaEE初阶 — 网络编程】实现基于TCP协议的Echo服务
TCP流套接字编程 1. TCP & UDP 的区别 TCP 的核心特点是面向字节流,读写数据的基本单位是字节 byte 2 API介绍 2.1 ServerSocket 定义 ServerSocket 是创建 TCP 服务端 Socket 的API。 构造方法 方法签名 方法说明 ServerS…...

vue结合canvas动态生成水印效果
在 Vue 项目中添加水印可以通过以下几种方式实现: 方法一:使用 CSS 直接通过 CSS 的 background 属性实现水印: 实现步骤 在需要添加水印的容器中设置背景。使用 rgba 设置透明度,并通过 background-repeat 和 background-size…...

Qt 5 中的 QTextStream 使用指南
文章目录 Qt 5 中的 QTextStream 使用指南介绍基本概念读取文件注意事项结论 Qt 5 中的 QTextStream 使用指南 介绍 QTextStream 是 Qt 框架中用于处理文本数据的类。它提供了方便的接口来读写文本文件或字符串,支持多种编码格式,并且可以与 QIODevice…...

中安证件OCR识别技术助力鸿蒙生态:智能化证件识别新体验
在数字化和智能化的浪潮中,伴随国产化战略的深入推进,国产操作系统和软件生态的建设逐渐走向成熟。鸿蒙操作系统(HarmonyOS Next)作为华为推出的重要操作系统,凭借其开放、灵活和高效的特点,正在加速在多个…...

SpringBoot 框架下基于 MVC 的高校办公室行政事务管理系统:设计开发全解析
2系统开发环境 2.1vue技术 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。 [5] 与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第…...

【ArkTS】使用AVRecorder录制音频 --内附录音机开发详细代码
系列文章目录 【ArkTS】关于ForEach的第三个参数键值 【ArkTS】“一篇带你读懂ForEach和LazyForEach” 【小白拓展】 【ArkTS】“一篇带你掌握TaskPool与Worker两种多线程并发方案” 【ArkTS】 一篇带你掌握“语音转文字技术” --内附详细代码 【ArkTS】技能提高–“用户授权”…...

Selenium3+Python如何操作键盘
selenium操作键盘,需要导入Keys类:“from selenium.webdriver.common.keys import Keys” 调用键盘操作的快捷键的方法 : 单键值:直接传入对应的键值“element.send_keys”(快捷键的键值) 组合键:键值之间由逗号分隔…...

PLC协议
PLC协议通常指的是可编程逻辑控制器(Programmable Logic Controller, PLC)与其他设备之间通信时所使用的协议。PLC广泛应用于工业自动化领域,用于控制和监控设备。不同厂商和应用场景可能使用不同的通信协议。 常见的PLC通信协议 1. Modbus …...

C_字符串的一些函数
1.字符串输入函数 scanf("%s",数组名); gets(数组名); 区别: scanf(“%s”,数组名); 把空格识别为输入结束 #include <stdio.h>int main() {char a[10];printf("输入:");scanf("%s",a)…...

使用Native AOT发布C# dll 提供给C++调用
Native AOT,即提前本地编译(Ahead-Of-Time Compilation),是一种将托管代码(如 C#)编译为本机可执行文件的技术,无需在运行时进行任何代码生成。 (Native AOT 优缺点截图摘自张善友博…...

Git 提交代码日志信息
前言 在项目中经常用到git提交代码,每次提交时需要添加日志信息,那么一套规范的日志信息会让整个git仓库看起来赏心悦目! 以下是Git 提交代码日志信息的建议: 一、格式规范 标题(Subject) 标题是日志信息中…...

Request method ‘POST‘ not supported(500)
前端路径检查 查看前端的请求路径地址、请求类型、方法名是否正确,结果没问题 后端服务检查 查看后端的传参uri、传参类型、方法名,结果没问题 nacos服务名检查 检查注册的服务是否对应(我这里是后端的服务名是‘ydlh-gatway’,服务列表走…...