2023年12月8日发(作者:)

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

3.2

  签章汇总文件

3

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

4 签章需要主要事项

  通过以上分析,可以看出签章好像并不难。其实不然,有几个问题要注意:

  4.1 不要想当然的认为OFD文件的路径都是固定的。OFD文件只有入口文件“OFD.”,名字是固定;其它任何文件名字都是可变

的。只是为了方便理解,生成的ofd文件名称遵循一定的规则。   下图只是建议的组织和命名规则。

  4.2 如果是多印章,后签的印章不能影响前一个印章。

    如果文档已经做了签章了,再签章时,除了签章汇总文件()外,其他文件不能做任何改动。

  4.3 骑缝章处理。对于骑缝章,需要计算每个章的位置。需要分析出文件的页数以及每页尺寸信息。

5 签章处理步骤

5.1 分析ofd原文件,将文件分类。

  通过入口文件“”,层层剖析,将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 对分析后的文件处理

  如果是第一次签章,需要生成。

QSharedPointer signaturesFile = GetOfdFile(EN_OfdFileType::signatures);

if(())

{

QString signaturesPath = AddSignaturesPathToRoot(rootFile);

OfdFileInfoDetail *signaturesFileInfo = CreateSignaturesFile(signaturesPath);

(signaturesFileInfo);

_(signaturesFile);

}

  计算文件的哈希值,计算印章的位置,生成。

void SignOfdFile::CreateSignature(QString signaturePath,QByteArray& signatureFileContent,QString& signedValuePath)

{{

QSharedPointer 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(_thod);

//SignatureDateTime

XmlNode *nodeSignatureDateTime = nodeSignedInfo->AddChildByName("SignatureDateTime",true);

nodeSignatureDateTime->SetText(_teTime);

//Seal

if(!_y())

{

XmlNode *nodeSeal = nodeSignedInfo->AddChildByName("Seal",true);

XmlNode *nodeSealBaseLoc= nodeSeal->AddChildByName("BaseLoc",true);

QString sealPath = OfdPathHelper::GetOfdFullPath(signaturePath,"");

nodeSealBaseLoc->SetText(OfdPathHelper::AddStartSlash(sealPath));

AddSealToFile(sealPath);

}

//References

XmlNode *nodeReferences = nodeSignedInfo->AddChildByName("References",true);

nodeReferences->SetAttr("CheckMethod",SignOfdParam::MethodName_SM3);

foreach(QSharedPointer file , _listOfdFile)

{

if(file->OfdFileType == EN_OfdFileType::signatures)

continue;

if(file->y())

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);

//

signedValuePath = OfdPathHelper::GetOfdFullPath(signaturePath,"");

XmlNode *nodeSignedValue = header->AddChildByName("SignedValue",true);

nodeSignedValue->SetText(OfdPathHelper::AddStartSlash(signedValuePath));

//添加到文件列表

QSharedPointer signatureFile(new OfdFileInfoDetail());

signatureFile->FilePath = signaturePath;

signatureFile->FileContent = header->CreateXml(true).toUtf8();

signatureFile->OfdFileType = EN_OfdFileType::signatureContent;

_(signatureFile);

signatureFileContent = signatureFile->FileContent;

}}

后记 本文粗略的描述了ofd签章的过程,实现签章的途径有多种。本文给出了一种可行、易懂的签章方法。具体的签章过程涉及大量细节处

理,对于开发人员来讲是一种挑战。作者通过多次修改完善,编写了一款签章服务软件,可以与签名接口对接,就大大减轻了签章的难度。