ofd文件电子签章实现方法
前言 文档处理一般经过三个环节:流、版、签;流式软件负责编辑,如:office、wps等。版式软件负责文档定型,保证显示样式不跑偏;版式文件格式有两种:pdf、ofd。签章软件负责对版式文档签章。签章是文档处理的最后一个环节。
当前,市面上的版式文件还是以pdf为主;对pdf的签章,国内研究的比较多。但是对ofd签章,国内研究时间不长,相关成熟的产品并不多。作者研究ofd多年,仔细分析了ofd签章标准,编写了一套签章软件,可以满足自由签章、骑缝章等类型的签章。作者采用的签章方法有以下优点:思路新颖、处理速度快、能满足各类复杂签章需求。

1 OFD签章基本概念
签章的目的是保证数据的完整性、真实性。完整性是通过记录ofd文件的哈希值来保证的(国产算法为SM3);真实性是通过非对称加密算法保证的(国产算法为SM2)。签章的过程其实就是记录ofd内的各个文件哈希值,再用私钥对哈希值签名。
2 OFD签章遵循的标准
OFD签章涉及的标准不止一个;这往往导致开发签章软件时茫然无措。
ofd签章遵循两类标准:
2.1 ofd板式文件格式标准:《 GB/T 33190-2016电子文件存储与交换格式》。
2.2 签章密码技术规范: 《GM/T 0031-2014 安全电子签章密码技术规范》,《GB/T 38540-2020信息安全技术 安全电子签章密码技术规范》。

3 签章后,哪些文件被改动?
签章过程后,以下文件被修改。
3.1 OFD.xml

3.2 Signatures.xml
签章汇总文件

3 Signature.xml
具体签章文件,记录印章数据、签章数据、各个文件哈希值、印章位置信息等。

4 签章需要主要事项
通过以上分析,可以看出签章好像并不难。其实不然,有几个问题要注意:
4.1 不要想当然的认为OFD文件的路径都是固定的。OFD文件只有入口文件“OFD.xml”,名字是固定;其它任何文件名字都是可变的。只是为了方便理解,生成的ofd文件名称遵循一定的规则。
下图只是建议的组织和命名规则。

4.2 如果是多印章,后签的印章不能影响前一个印章。
如果文档已经做了签章了,再签章时,除了签章汇总文件(Signatures.xml)外,其他文件不能做任何改动。
4.3 骑缝章处理。对于骑缝章,需要计算每个章的位置。需要分析出文件的页数以及每页尺寸信息。
5 签章处理步骤
5.1 分析ofd原文件,将文件分类。
通过入口文件“OFD.xml”,层层剖析,将ofd内各类文件分类,具体分类如下:
enum class EN_OfdFileType
{
unset,
root,
doucument,
publicRes,
documentRes,
pageContent,
resFile,
annotations,
annotation_page,
customTags,
customTagContent,
templatePage,
signatures,
signatureContent,
signedValue,
signedSeal,
attachments,
attachmentContent
};
在分析过程中,同时解析出ofd页文件的尺寸。得出每个文件的属性。
class OfdFileInfoDetail
{
public:
EN_OfdFileType OfdFileType = EN_OfdFileType::unset; QString FilePath; //文件的完整路径
QByteArray FileContent; //文件内容 int OfdFileIndex = -1; //文件索引 多文档的情况下 有用 //为ofd页面时,有效;OfdFileType=pageContent
int PageIndex = -1; //页索引
int PageId = -1; //页id
QString PhysicalBox; //页尺寸 //为ofd Signature时,有效;OfdFileType=signatureContent
int SignatureIndex = -1;//在文件Signatures中的索引
QString PathSeal; //印章文件路径
QString PathSignedValue;//签名后文件路径
};
5.2 对分析后的文件处理
如果是第一次签章,需要生成Signatures.xml。
QSharedPointer<OfdFileInfoDetail> signaturesFile = GetOfdFile(EN_OfdFileType::signatures);
if(signaturesFile.isNull())
{
QString signaturesPath = AddSignaturesPathToRoot(rootFile);
OfdFileInfoDetail *signaturesFileInfo = CreateSignaturesFile(signaturesPath);
signaturesFile.reset(signaturesFileInfo); _listOfdFile.append(signaturesFile);
}
计算文件的哈希值,计算印章的位置,生成Signature.xml。
void SignOfdFile::CreateSignature(QString signaturePath,QByteArray& signatureFileContent,QString& signedValuePath)
{
QSharedPointer<XmlNode> header(new XmlNode());
header->SetName("Signature");
header->SetNameSpace(OfdCreatorParam::OFD_NameSpace);
header->SetNameSpaceUrl(OfdCreatorParam::OFD_NameSpaceUrl); XmlNode *nodeSignedInfo = header->AddChildByName("SignedInfo",true); //nodeProvider
XmlNode *nodeProvider = nodeSignedInfo->AddChildByName("Provider",true);
nodeProvider->SetAttr("Company",_company);
nodeProvider->SetAttr("Version",_version);
nodeProvider->SetAttr("ProviderName",_providerName); //SignatureMethod
XmlNode *nodeSignatureMethod = nodeSignedInfo->AddChildByName("SignatureMethod",true);
nodeSignatureMethod->SetText(_signInfoInput.signMethod); //SignatureDateTime
XmlNode *nodeSignatureDateTime = nodeSignedInfo->AddChildByName("SignatureDateTime",true);
nodeSignatureDateTime->SetText(_signInfoInput.signDateTime); //Seal
if(!_sealData.isEmpty())
{
XmlNode *nodeSeal = nodeSignedInfo->AddChildByName("Seal",true);
XmlNode *nodeSealBaseLoc= nodeSeal->AddChildByName("BaseLoc",true);
QString sealPath = OfdPathHelper::GetOfdFullPath(signaturePath,"Seal.esl");
nodeSealBaseLoc->SetText(OfdPathHelper::AddStartSlash(sealPath));
AddSealToFile(sealPath);
} //References
XmlNode *nodeReferences = nodeSignedInfo->AddChildByName("References",true);
nodeReferences->SetAttr("CheckMethod",SignOfdParam::MethodName_SM3);
foreach(QSharedPointer<OfdFileInfoDetail> file , _listOfdFile)
{
if(file->OfdFileType == EN_OfdFileType::signatures)
continue;
if(file->FileContent.isEmpty())
continue; XmlNode *nodeReference = nodeReferences->AddChildByName("Reference",true);
nodeReference->SetAttr("FileRef",OfdPathHelper::AddStartSlash(file->FilePath)); XmlNode *nodeCheckValue = nodeReference->AddChildByName("CheckValue",true);
nodeCheckValue->SetText(sm3_digest_base64(file->FileContent));
} //StampAnnot
CreateStampAnnot(nodeSignedInfo); //SignedValue.dat
signedValuePath = OfdPathHelper::GetOfdFullPath(signaturePath,"SignedValue.dat");
XmlNode *nodeSignedValue = header->AddChildByName("SignedValue",true);
nodeSignedValue->SetText(OfdPathHelper::AddStartSlash(signedValuePath)); //添加到文件列表
QSharedPointer<OfdFileInfoDetail> signatureFile(new OfdFileInfoDetail());
signatureFile->FilePath = signaturePath;
signatureFile->FileContent = header->CreateXml(true).toUtf8();
signatureFile->OfdFileType = EN_OfdFileType::signatureContent;
_listOfdFile.append(signatureFile); signatureFileContent = signatureFile->FileContent;
}
后记 本文粗略的描述了ofd签章的过程,实现签章的途径有多种。本文给出了一种可行、易懂的签章方法。具体的签章过程涉及大量细节处理,对于开发人员来讲是一种挑战。作者通过多次修改完善,编写了一款签章服务软件,可以与签名接口对接,就大大减轻了签章的难度。
ofd文件电子签章实现方法的更多相关文章
- 【电子签章】HTML格式合同转化成PDF文件
代码地址如下:http://www.demodashi.com/demo/12476.html 一.主要思路 通过itextpdf 生成想要的pdf 文件 通过itextpdf 中 XMLWorker ...
- 在基于ABP框架的前端项目Vue&Element项目中采用电子签章处理文件和打印处理
在一些内部OA或者流转的文件,或者给一些客户的报价文件.合同,或者一些医院出示的给保险机构的病历资料等,有时候可能都希望快速的使用电子签章的处理方式来给文件盖上特定的印章,本篇随笔介绍基于Vue&am ...
- Java 浅析,生成OFD文件
摘要:这几天遇到个需要,需要提供用户下载电子证照,最简单的方法实现:word做了一份模板,利用网页工具转成OFD文件,http://www.yozodcs.com/page/example.html用 ...
- c#实现ofd文件转图片功能 (附执行程序)
前言 ofd文件的作用就是保证信息能如实的存储.传递.显示.保证ofd文件的真实性靠的是签名:ofd 的显示需要专用软件.ofd标准是新的国家标准,应用范围远不如pdf:现有浏览器不能解析ofd.支持 ...
- 基于CA认证(结合文档在线预览)的电子签章解决方案
分享一个基于CA认证(结合文档在线预览)的电子签章实现思路,恰巧是最近项目中遇到的,欢迎大家一起讨论. 一. 项目背景 在公司业务系统中,按照传统的签章方式,存在以下痛点: 1.成本高,体现在纸质合同 ...
- elf格式转换为hex格式文件的两种方法
这周工作终于不太忙了,可以写点笔记总结一下了. 之前的文章如何在Keil-MDK开发环境生成Bin格式文件,介绍了如何在Keil开发环境使用fromelf软件,将生成的axf文件转换为bin文件,这次 ...
- mapreduce多文件输出的两方法
mapreduce多文件输出的两方法 package duogemap; import java.io.IOException; import org.apache.hadoop.conf ...
- xib文件的加载方法
xib文件的加载方法 以UITableViewCell的cell为例 很多时候因为系统的cell无法满足我们的日常需求,我们都会自定义cell 因为cell的界面比较固定,所以通常都会选择用xib来描 ...
- [转载]C#读写txt文件的两种方法介绍
C#读写txt文件的两种方法介绍 by 大龙哥 1.添加命名空间 System.IO; System.Text; 2.文件的读取 (1).使用FileStream类进行文件的读取,并将它转换成char ...
随机推荐
- 51nod1675-序列变换【莫比乌斯反演】
正题 题目连接:http://www.51nod.com/Challenge/Problem.html#problemId=1675 题目大意 给出两个长度为\(n\)的序列\(a,b\),求有多少对 ...
- 11.4.3 LVS-TUN
LVS-TUN 用IP隧道技术实现虚拟服务器。这种方式是在集群的节点不在同一个网段时可用的转发机制,是将IP包封装在其他网络流量中的方法。为了安全的考虑,应该使用隧道技术中的VPN,也可使用租用专线。 ...
- Mysql读写分离集群的搭建且与MyCat进行整合
1. 概述 老话说的好:不熟悉的东西不要不懂装懂,做人要坦诚,知道就是知道,不知道就是不知道. 言归正传,今天我们来聊聊 Mysql主从读写分离集群是如何搭建的,并且聊一下如何用 MyCat 去访问这 ...
- Java初步学习——2021.10.12每日总结,第六周周二
(1)今天做了什么: (2)明天准备做什么? (3)遇到的问题,如何解决? 今天学习了菜鸟教程Java实例,数组 1.数组的排序和元素的查找--sort和binarySearch方法 import j ...
- MySQL初步学习——2021.09.27每日总结,第四周周一
(1)今天做了什么: (2)明天准备做什么? (3)遇到的问题,如何解决? 今天学习了SQL语句的分类: SQL语句很多,分为 1.DQL:数据查询语言(凡是带有select关键字的都是查询语句) 2 ...
- 2020.1.30--vj补题
C - C CodeForces - 991C 题目内容: After passing a test, Vasya got himself a box of n candies. He decided ...
- Endian
Endian 寻址 多字节对象被存储为连续的字节序列,对象的地址为所使用字节中最小的地址. 例如,假设一个类型为 int 的变量 a 的地址为 0x100,也就是说,地址表达式 &a 的值为 ...
- 项目实战:Qt文件改名工具 v1.2.0(支持递归检索,搜索:模糊匹配,前缀匹配,后缀匹配;重命名:模糊替换,前缀追加,后缀追加)
需求 在整理文件和一些其他头文件的时候,需要对其名称进行整理和修改,此工具很早就应该写了,创业后,非常忙,今天抽空写了一个顺便提供给学习. 工具和源码下载地址 本篇文章的应用包和源码包可在 ...
- Noip模拟68 2021.10.4
T1 玩水 成功在考试的时候注释掉正解,换成了暴力,只因为不敢保证正解思路的正确 脑子瓦特了,不知道把暴力打成函数拼在一起,不知道当时咋想的.... 就是你找有没有一个点上面和左面的字符一样, 如果这 ...
- 函数指针和qsort函数
1.函数指针的形式: 函数指针:int (*funcP) (int *a, int *b) 表示定义了一个funcP函数指针,指向了返回值为int类型,参数为int* 和int* 的函数 使用方式: ...