Appearance
移动签章
移动证书签署允许用户在电脑上选择印章、加盖并保存 PDF,通过手机扫码完成签署授权,无需插入 UKey 或输入 UKey PIN。
本页先介绍 PC 页面使用方式,再提供页面接入和自定义交互示例。SDK 通用配置见 SDK 通用设置,文件加载与结果交付见文件操作。
使用前准备
- 在 PC 浏览器中使用本功能;当前不支持 H5、App 和蓝牙签署入口。
- 用户电脑已安装并运行支持移动证书签署的本地签署驱动,驱动能够访问移动证书服务。
- 用户可通过配套手机应用扫码,并具有可用的移动证书和印章。
- 已按快速上手引入 SDK,待签文件为未加密 PDF。
- SDK 配置
canSign: true。文件大小受signFileMaxSize限制,默认最大为 50 MB。
支持普通章和各文件独立骑缝章。独立骑缝章至少覆盖两页;当前版本暂不支持将多份 PDF 合并为同一组骑缝章后保存,请选择各文件独立加盖。
页面使用流程
操作顺序:打开 PDF → 选择移动签章 → 手机扫码授权 → 放置普通章或骑缝章 → 保存 → 下载或提交签署结果。
以下截图使用当前 SDK、演示文件和示例印章,点击截图可查看原图。截图中的授权二维码仅作示意,实际使用时请扫描自己签署页面生成的二维码。
第一步:进入移动签章
在电脑上打开待签 PDF,点击工具栏的签章图标,在左侧面板选择“移动签章”。页面先检查本地签署驱动及网络是否可用,通过后显示“扫码使用移动签章”和本次签署的授权二维码。
看到“驱动加载中, 请稍后”时,请等待检查完成,无需重复切换页签或点击授权。

第二步:使用手机扫码授权
可选择以下任意一种方式:
- 使用「电子签约」APP:打开 APP,扫描电脑面板中的授权二维码,按手机提示完成签署授权。
- 使用「契约锁移动签章」小程序:点击面板文案中的“「契约锁移动签章」”,弹窗会展示小程序码及“请使用微信扫码进入移动签章小程序”。使用微信扫码进入小程序,再用小程序扫描电脑面板中的授权二维码,按手机提示完成签署授权。

两个二维码的用途
弹窗中的小程序码用于进入小程序;左侧面板中的授权二维码用于授权本次签署。进入小程序后,还需要完成本次扫码授权。关闭小程序码弹窗不会取消已有授权,也不会重新生成授权二维码。
扫码后,电脑显示“扫码成功,请授权签署”,此时需继续在手机上确认。随后出现“签名授权中”表示正在同步授权印章,请等待左侧出现“已授权的移动印章”和章面图片后再加盖。

第三步:拖拽加盖普通章
- 将鼠标移到左侧章面,按住并拖到 PDF 的目标位置后松开,即可放置待签章面。
- 保存前可在 PDF 上拖动章面调整位置;不需要的位置可通过章面工具栏的“删除”移除。
- 如需在其他位置或其他 PDF 中加盖,切换到目标页面或文件后继续拖拽同一章面。
章面按授权返回的尺寸和比例展示。单印章授权完成后会自动选中该印章;若授权列表中有多个印章,拖拽哪一个或使用其对应的骑缝章入口,就会选中哪一个。
业务开启批注功能后,可在 PC 页面为普通章添加说明文字,具体操作见批注。

第四步:按需加盖骑缝章
- 点击左侧章面显示区域下方的“加盖骑缝章”。
- 在“批量应用多文件多页面”弹窗中选择应用页面;加载多份文件时,可勾选需要加盖的文件,并选择“每份文件单独加盖骑缝章”。
- 选择“全部页面”时,每份文件至少需要两页;选择“奇数页”适用于双面打印,每份文件至少需要三页。加密文件不能参与加盖。
- 点击“确定”后查看 PDF 中的骑缝章位置,保存前可上下拖动调整位置。
当前版本支持各文件独立加盖骑缝章,暂不支持将多份 PDF 合并为同一组骑缝章后保存。多文件操作请选择“每份文件单独加盖骑缝章”。

第五步:保存并获取签署结果
确认章面位置后,点击页面头部“保存”。放置章面只是确定待签位置,保存成功后才完成 PDF 数字签署。 签署期间请等待完成,此时不能换章、取消授权或切换签章方式。
保存会处理文件列表中当前所选移动印章的待签位置,不限于当前显示的 PDF。其他印章的位置不会一并保存,需要选择对应印章后再次保存。
保存成功后,通过业务页面提供的下载或提交入口获取签署结果。SDK 的“保存”本身不会自动下载或上传文件;接入方可通过 signCb 获取签署后的 PDF,具体见下方的签署结果接入说明。
第六步:切换方式、取消或重新授权
- 切换签章方式:切换“Ukey签章”/“移动签章”会保留本次任务、二维码、授权印章及待签章面。授权仍有效时,切回移动签章无需再次扫码;切换页签本身不会清理待签位置。
- 取消授权:在等待手机确认或已授权卡片中点击“取消授权”,会结束当前签署授权。文件上已有的待签章面仍保留,已经保存的数字签章也不受影响;后续签署需要重新授权。
- 重新授权:二维码失效、授权超时或授权结束时,点击“重新授权”,扫描新二维码并在手机上确认。若重新授权的仍是同一印章,单印章会自动选中,可直接保存已有待签章面,无需再次拖拽。原授权二维码不能继续使用。
- 重新确认手机授权:若页面重新提示“扫码成功,请授权签署”,请按手机提示再次确认。确认后会重新显示授权印章,已有待签位置继续保留。

更换签章方式后的加盖确认
实际加盖时,若所有已加载 PDF 中存在另一种签章方式的未保存章面,会出现“温馨提示”。点击“确认”后,将清理所有已加载 PDF 中的全部未保存章面,再加盖本次章面;这也包括当前签章方式此前的待签位置。点击“取消”或关闭提示框,原章面保持不变。已保存的数字签章始终保留。
页面提示与处理方式
| 页面提示或现象 | 含义与操作 |
|---|---|
| “驱动加载中, 请稍后” | 正在检查驱动或创建授权,请等待请求完成。 |
| “扫码成功,请授权签署” | 手机已扫码,请继续在手机上确认授权。 |
| “签名授权中” | 手机授权已通过,正在同步印章,等待章面出现即可。 |
| “移动印章/签名不可使用” | 检查本地签署驱动是否运行、是否支持移动签章及能否访问外网服务,恢复后点击“重新检测”。 |
| 二维码失效、授权超时或授权已结束 | 点击“重新授权”,使用新二维码完成手机授权。 |
| “本地签署驱动已断开连接” | 先恢复本地驱动连接,再点击“重新授权”。 |
| 章面不能拖动或“保存”不可用 | 确认已打开未加密 PDF、手机授权已完成且当前没有签署请求;保存还需要存在当前所选印章的待签位置。 |
SDK 页面接入
渲染页面并加载 PDF
容器需要设置宽高。以下示例使用 npm 项目的引入方式,包名沿用快速上手,请按实际交付包名调整;通过 script 引入时,直接使用全局 QysUKeySdk。
html
<input id="pdf-input" type="file" accept="application/pdf" multiple>
<div id="sign-container" style="width: 100%; height: 720px;"></div>
typescript
import { QysUKeySdk } from 'ukey-sign-sdk';
import type { FileDescriptor } from 'ukey-sign-sdk';
/** 创建签章实例,签署完成后在回调中接收最新 PDF 文件。 */
const sdk = new QysUKeySdk({
canSign: true,
showSaveBtn: true,
/** files 是本批已成功签署的文件数组。 */
signCb(files) {
files.forEach(file => {
console.info('已签署文件:', file.filename, file.rawFileData.byteLength);
});
},
/** 接入业务系统自己的错误提示。 */
errorCb(message, type) {
console.error(type, message);
},
});
/** 在加载文件之前注册通知。 */
sdk.bus.on('FILE_LIST_CHANGE', files => {
console.info('当前已加载文件:', files.map(file => file.filename));
});
/** 用于承载签章页面的容器。 */
const container = document.getElementById('sign-container');
if (!container) throw new Error('缺少签章页面容器');
sdk.render(container);
/** 用户通过此输入框选择待签 PDF。 */
const pdfInput = document.querySelector<HTMLInputElement>('#pdf-input');
if (!pdfInput) throw new Error('缺少 PDF 文件输入框');
pdfInput.addEventListener('change', () => {
if (pdfInput.files && pdfInput.files.length > 0) {
sdk.addFile(pdfInput.files);
}
});
render() 和 addFile() 不返回文件加载完成的 Promise。请等待文件加载通知,或在用户后续操作时通过 getDocuments()、getCurrentDocument() 获取已加载文件;不要在 addFile() 后立即假定 PDF 已可用。
获取签署后的 PDF
signCb(files) 的参数是本批成功文件数组,每个文件的 rawFileData 为签署后的 PDF 字节,filename 为文件名。若批次中途失败,已经成功的文件仍会通过回调交付;没有成功文件时不触发该回调。
签署成功后,可在业务按钮中调用 sdk.downloadCurrentFile() 下载当前文件,或对回调中的文件调用 sdk.downloadFile(file)。如需上传到业务系统,将最新的 rawFileData 转为 PDF Blob 后按自己的上传接口提交。
下面的函数可在 signCb 或保存完成后准备上传表单;文件字段名称和提交方式按业务上传接口约定调整:
typescript
/** 使用签署后的文件准备上传内容。 */
function createSignedPdfForm(file: FileDescriptor): FormData {
/** 表单中携带签署后的 PDF 字节和文件名。 */
const form = new FormData();
form.append('file', new Blob([file.rawFileData], { type: 'application/pdf' }), file.filename);
return form;
}
保存与上传
“保存”表示将电子签名写入 PDF。SDK 不会自动将结果上传到业务服务器;下载方法也不会触发签署。请在保存成功后下载或上传结果。
签署约定
移动 RSA 证书签署固定使用 SHA-256 计算 PDF 文档摘要,不受 PDF 版本影响;SM2 证书继续使用 SM3。
签署时自动使用当前 PDF 的文件名;未提供文件名时,签署名称使用“网络签章工具”。批量签署时各文件分别使用自己的名称。
自定义扫码、选章和保存交互
以下示例继续使用上节已渲染并加载 PDF 的 sdk。使用自定义交互时,由业务按钮统一控制授权请求,避免与内置面板同时发起授权。
1. 订阅二维码、授权状态和印章列表
在业务页面中提供二维码、状态提示和印章列表容器:
html
<img id="mobile-qr" alt="移动证书授权二维码" hidden>
<p id="mobile-status"></p>
<div id="mobile-certificates"></div>
typescript
/** 移动证书签署操作入口。 */
const mobileCertificate = sdk.mobileCertificate;
/** 展示本次授权任务的二维码。 */
const qrImage = document.querySelector<HTMLImageElement>('#mobile-qr');
/** 展示授权状态或错误。 */
const statusText = document.getElementById('mobile-status');
/** 由用户点击选择当前授权中的印章。 */
const sealButtons = document.getElementById('mobile-certificates');
if (!qrImage || !statusText || !sealButtons) throw new Error('缺少移动签章展示容器');
/** 将 Promise 错误显示到业务页面。 */
function reportMobileError(error: unknown): void {
console.error(error instanceof Error ? error.message : '移动证书操作失败');
}
/** 订阅立即提供当前快照,之后自动更新,无需自行轮询。 */
const unsubscribeMobile = mobileCertificate.subscribe(state => {
statusText.textContent = state.error ? state.error.message : (state.status || '等待授权状态');
qrImage.hidden = state.task === null;
if (state.task) qrImage.src = `data:image/png;base64,${state.task.qrImage}`;
else qrImage.removeAttribute('src');
sealButtons.textContent = '';
if (state.error || state.status !== 'SIGNING') return;
state.seals.forEach(seal => {
/** 点击时才选章,避免在状态通知内反复修改选择。 */
const button = document.createElement('button');
button.type = 'button';
button.textContent = `${state.selectedSeal?.id === seal.id ? '✓ ' : ''}${seal.name}`;
button.addEventListener('click', () => {
try {
mobileCertificate.selectSeal(seal.id);
} catch (error) {
reportMobileError(error);
}
});
sealButtons.appendChild(button);
});
});
二维码 task.qrImage 和印章图片 seal.image 均为不带前缀的 PNG Base64;作为图片展示时需要添加 data:image/png;base64,。印章 width / height 的单位为毫米。
当前驱动为每个 SID 返回一个印章,SDK 通过 state.seals 单元素数组提供给选章界面。驱动未提供印章名称,seal.name 使用印章 ID;seal.sealType 保留驱动返回的原始类型字符串。
subscribe() 会立即通知一次当前快照,随后在授权数据变化时通知。轮询返回相同状态、重复选择同一印章时保留当前快照,避免重复刷新界面。回调应更新界面;选章等业务操作应由用户事件触发。
2. 开启授权
将下面函数绑定到业务页面的“开始授权”按钮:
typescript
/** 开启扫码授权;返回二维码后仍需等待手机确认。 */
async function startMobileAuthorization(): Promise<void> {
try {
await mobileCertificate.startAuthorization();
} catch (error) {
reportMobileError(error);
}
}
startAuthorization() 会检查可用性、创建二维码并自动查询授权结果,返回二维码不代表已经授权成功。需要提前判断是否展示移动签署入口时,可单独调用 await mobileCertificate.checkAvailability(),检查返回的 available;不可用时读取 error。该检查不会切换签署方式,也不代替后续开启授权。
| 状态 | 界面应如何处理 |
|---|---|
null | 尚未取得授权状态,等待后续通知 |
TO_BE_SCAN | 展示二维码,提示用户扫码 |
TO_BE_SIGN | 提示已扫码,等待手机确认授权 |
SIGNING | 无错误且印章列表非空时允许选章;列表为空时继续等待 |
FINISHED | 本次授权已结束,提示重新授权;不能据此判断 PDF 已保存 |
QR_INVALID / AUTHORIZE_TIMEOUT / AUTHORIZE_CLOSE | 二维码失效、授权超时或关闭,提供重新授权入口 |
CLOSED | 已在本地取消,可重新授权 |
DISCONNECTED | 驱动连接中断,恢复驱动后重新授权 |
3. 选章后添加签署位置
用户点击上例中的印章按钮完成选择后,可将下面函数绑定到业务页面的“加盖”按钮。示例在当前 PDF 第一页添加一处普通章:
typescript
/** 在当前 PDF 上创建待签位置,尚未写入电子签名。 */
function addMobileCertificateToCurrentPdf(): void {
try {
/** 从 SDK 获取当前已解析的文件。 */
const file = sdk.getCurrentDocument();
if (!file) throw new Error('请先加载并选择 PDF');
mobileCertificate.addStamper(file, {
positions: [1],
left: 0.1,
top: 0.2,
});
} catch (error) {
reportMobileError(error);
}
}
| 参数 | 使用说明 |
|---|---|
file | SDK 已解析的 FileDescriptor,可通过 getCurrentDocument() 或 getDocuments() 获取;不能直接传入原始 File、Blob 或 URL |
positions | 从 1 开始的 PDF 页码数组,不得为空或重复 |
left / top | 相对于旋转后可见页面的水平、垂直位置比例;应在 0 到 1 之间,完整章面也必须位于页面范围内 |
type | 省略或传 SEAL_CORPORATE 添加普通章;传 ACROSS_PAGE 添加骑缝章 |
mode | 独立骑缝章使用 single;当前不支持跨文件合并组保存 |
添加独立骑缝章时,将 type 设置为 ACROSS_PAGE、mode 设置为 single,并通过 positions 指定至少两页,例如 [1, 2]。印章图片、尺寸和证书类型取自当前选择,无需接入方自行指定。
4. 保存并处理结果
将下面函数绑定到自定义“保存”按钮。使用 signMobilePdf() 可等待保存完成:
typescript
/** 保存当前所选移动印章在所有已加载文件中的待签位置。 */
async function saveMobileDocuments(): Promise<void> {
try {
/** 返回值同时包含完成状态和本批成功文件。 */
const result = await sdk.signMobilePdf();
if (result.signedFiles.length > 0) {
console.info('本批已保存文件:', result.signedFiles.map(file => file.filename));
}
if (result.status === 'failed') {
console.error('保存失败:', result.failedFile.filename, result.error.code, result.error.message);
return;
}
if (result.signedFiles.length === 0) console.info('当前所选印章没有需要保存的位置');
} catch (error) {
reportMobileError(error);
}
}
- 成功返回
status: 'success'和signedFiles。空数组表示没有新增签署,不代表新增了签名。 - 中途失败返回
status: 'failed'、已成功的signedFiles、failedFile和error;失败文件不提交本次修改,后续文件停止处理。 - 尚未授权、操作冲突或不支持的加盖方式等前置检查失败,会直接拒绝 Promise,因此需要保留
try / catch。 - 已成功文件不会因后续失败而撤销。再次保存会跳过已经保存的位置;更换所选印章后,需要再执行一次保存。
signMobilePdf() 会同步 SDK 页面中的文件和验签结果,并触发 signCb。返回值与回调可能包含同一批文件,业务上传应选择一个位置执行,避免重复上传。保存后如需再次读取文件,应获取最新返回值或重新调用 getDocuments(),避免使用保存前缓存的文件引用。
原有 sdk.signPdf() 仍可触发当前方式的保存,但不返回可等待的保存结果。自定义移动签署按钮需要判断完成和部分失败时,请使用 signMobilePdf()。
取消、重试和切回 UKey
将以下函数分别绑定到“取消授权”“重新授权”“切回 UKey”及页面卸载事件:
typescript
/** 取消本次授权,保留文件上的待签位置。 */
async function cancelMobileAuthorization(): Promise<void> {
try {
await mobileCertificate.endAuthorization();
} catch (error) {
reportMobileError(error);
}
}
/** 重新生成二维码,用户需要再次扫码授权。 */
async function retryMobileAuthorization(): Promise<void> {
try {
await mobileCertificate.restartAuthorization();
} catch (error) {
reportMobileError(error);
}
}
/** 切回硬件签署,同时保留已有移动授权。 */
async function switchToUKey(): Promise<void> {
try {
await sdk.useUKeySigning();
} catch (error) {
reportMobileError(error);
}
}
/** 离开业务页面时释放订阅和签章实例。 */
function closeSigningPage(): void {
unsubscribeMobile();
sdk.unmount();
}
startAuthorization() 在已有任务时恢复移动签署方式并返回原任务,不重建二维码、印章列表或选择;没有任务时才检查网络并创建二维码。需要新二维码时显式使用 restartAuthorization()。重新授权不会删除待签位置,但新授权必须包含原印章,且章面与尺寸一致,才能继续保存。
endAuthorization() 立即取消本地授权并停止轮询,当前任务存在时使用其 signSid 请求驱动结束签署,等待成功响应后完成。重复取消不会重复发送;请求失败会拒绝 Promise,旧授权仍保持取消,文件上的待签位置保留。该方法不会自动切回 UKey;useUKeySigning() 只切换当前方式并恢复硬件发现,保留移动任务和后台授权查询。
关闭签章面板或取消订阅不会结束授权;离开整个业务页面时调用 unmount() 或其别名 close(),卸载仅释放本地资源,不追加远端结束请求。卸载后不要继续使用此前保存的 mobileCertificate 引用。
操作返回 WAITING 时,等待当前操作完成再尝试。PDF 正在保存时不要重复保存、换章、重新授权或切换方式。
API 速查
SDK 入口
| 方法 | 返回值与用途 |
|---|---|
sdk.mobileCertificate | 获取当前实例的移动证书操作入口,仅 PC 可用 |
sdk.signMobilePdf() | Promise<MobileSigningResult>;保存已加载文件、同步页面并交付成功结果 |
sdk.getSigningSource() | 返回 { type: 'UKEY' } 或 { type: 'MOBILE_CERTIFICATE' } |
sdk.useUKeySigning() | Promise<void>;切回 UKey 并恢复硬件发现,保留移动授权 |
sdk.isNeedSave() | boolean;按当前方式、身份与移动授权就绪情况判断是否有可保存内容 |
移动证书入口
以下方法均通过 sdk.mobileCertificate 调用。
| 方法 | 参数与返回值 |
|---|---|
checkAvailability() | Promise<MobileCertificateAvailability>;成功为 { available: true },未确认可用为 { available: false, error };操作冲突等前置检查仍可能拒绝 Promise |
startAuthorization() | Promise<MobileAuthorizationTask>;恢复并返回已有任务;没有任务时创建 { qrImage, signSid } 并自动查询授权状态 |
restartAuthorization() | Promise<MobileAuthorizationTask>;重开授权并返回新任务 |
getState() | MobileCertificateState;只读的 task / status / seals / selectedSeal / error 快照 |
subscribe(listener) | 立即及后续通知状态;返回取消订阅函数 () => void,取消订阅不会结束授权 |
selectSeal(sealId: string) | 返回选中的 AuthorizedMobileCertificate;只能选择当前授权列表中的印章 |
addStamper(file, placement) | 接收 FileDescriptor 和 MobileCertificatePlacement,返回创建的待签记录 Stamper |
signDocuments(files) | 高级入口,接收 readonly FileDescriptor[],返回 Promise<MobileSigningResult>;自行管理文件的接入方需负责并发控制与结果保存,不会自动同步 SDK 页面、触发 signCb 或校验 canSign |
endAuthorization() | Promise<void>;立即取消本地授权,并为当前任务发送一次结束签署请求,保留待签位置 |
dispose() | Promise<void>;永久释放该移动业务对象,通常由 sdk.unmount() 管理,不用于代替取消授权 |
已挂载 SDK 页面的接入方应使用 signMobilePdf() 保存,不与 signDocuments() 混用。SDK 未提供将原始文件直接解析为无界面 FileDescriptor 的公开方法;无需自行构造该对象。
常见错误与处理
移动操作通过 Promise 拒绝或状态快照中的 error 提供错误;批次中途失败还会在结果中提供 error。可读取其 code 和 message。
code | 处理建议 |
|---|---|
UNAVAILABLE | 检查驱动外网访问和移动证书服务,恢复后重新检测或授权 |
WAITING | 等待当前操作完成,避免重复点击 |
NOT_READY | 先完成手机授权、获取印章并选章;若提示跨文件合并骑缝章不支持,请改为独立加盖 |
SESSION_ENDED | 原授权已结束或被替换,重新授权后再保存 |
INVALID_RESPONSE | 检查驱动版本是否支持当前 SDK 的移动证书功能,并联系技术支持 |
INVALID_STAMP | 检查页码、章面、尺寸和位置;独立骑缝章至少两页 |
REQUEST_FAILED | 根据错误提示检查驱动或服务;需要重试授权时调用 restartAuthorization() |
UNSUPPORTED_PLATFORM | 改在 PC 浏览器使用此功能 |
授权状态回到待扫码或待授权时,SDK 会保留二维码任务、清空当前印章列表和选择;请等待用户再次授权并重新选章。SIGNING 表示授权可用于签署,不代表 PDF 已保存;以 signCb 或 signMobilePdf() 的成功文件结果为准。