提醒:API 签名需上传完整文件 再 下载已签名文件,故对网络要求比较高,大文件签名会比较耗时,建议用 CLI 秒签
通过 API 上传文件签名,支持所有操作系统,可以轻松嵌入到您的程序,拥有极高的灵活性,但需要一点代码基础
接口地址:https://api.evsign.cn/v1
请求方法:POST
| 参数 | 是否需性 | 说明 | 缺省值 |
|---|---|---|---|
| X-Key | 必须 | 您的许可证编号 XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX | — |
| X-Action | 必须 | 值必须为 api-sign |
— |
| X-File-Name | 否 | 传递文件名,需 URL 编码 后传递 | 随机名称 |
| X-Algorithm | 否 | 指定签名摘要算法 sha1,sha256,sha384,sha512 |
sha256 |
| X-Cert | 否 | 指定签名证书 ID | 默认证书 |
| X-Timestamp | 否 | 指定时间戳服务器 | auto |
| X-Password | 否 | 签名密码(若证书无密码可留空) | 空 |
| X-Append | 否 | 追加签名 'yes' / 'no' |
no |
请求体是完整的文件二进制流,请注意:不是 form-data
| 状态码 | 说明 |
|---|---|
200 |
成功,响应体为签名后的文件(二进制流),需自行保存为文件 |
| 其他 | 失败,响应体为纯文本错误信息 |
curl -X POST "https://api.evsign.cn/v1" \
-H "X-Key: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" \
-H "X-Action: api-sign" \
-H "X-Algorithm: sha256" \
-H "X-File-Name: $(python3 -c "import urllib.parse; print(urllib.parse.quote('Setup_v1.0.0.exe'), end='')")" \
-H "Content-Type: application/octet-stream" \
--data-binary @/path/to/Setup_v1.0.0.exe \
--output Setup_v1.0.0_signed.exe
import urllib.parse
import requests
url = "https://api.evsign.cn/v1"
exe_path = "Setup_v1.0.0.exe" # 待签名的安装包路径
output_path = "Setup_v1.0.0_signed.exe" # 签名后的输出路径
headers = {
"X-Key": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"X-Action": "api-sign",
"X-Algorithm": "sha256",
"X-File-Name": urllib.parse.quote("Setup_v1.0.0.exe"),
}
with open(exe_path, "rb") as f:
response = requests.post(url, headers=headers, data=f)
if response.status_code == 200:
with open(output_path, "wb") as out:
out.write(response.content)
print(f"签名成功:{output_path}")
else:
print(f"签名失败:{response.text}")
const fs = require('fs');
const https = require('https');
const exePath = 'Setup_v1.0.0.exe';
const outputPath = 'Setup_v1.0.0_signed.exe';
const exeBuffer = fs.readFileSync(exePath);
const options = {
hostname: 'api.evsign.cn',
port: 443,
path: '/v1',
method: 'POST',
headers: {
'X-Key': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
'X-Action': 'api-sign',
'X-Algorithm': 'sha256',
'X-File-Name': encodeURIComponent('Setup_v1.0.0.exe'),
'Content-Type': 'application/octet-stream',
'Content-Length': exeBuffer.length,
},
};
const req = https.request(options, (res) => {
if (res.statusCode === 200) {
const outFile = fs.createWriteStream(outputPath);
res.pipe(outFile);
outFile.on('finish', () => console.log(`签名成功:${outputPath}`));
} else {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => console.error('签名失败:', data));
}
});
req.write(exeBuffer);
req.end();
# Windows 下签名 .exe 安装包
$exePath = "C:\Build\Setup_v1.0.0.exe"
$outputPath = "C:\Build\Setup_v1.0.0_signed.exe"
# URL 编码文件名
$urlEncodedFileName = [uri]::EscapeDataString("Setup_v1.0.0.exe")
$headers = @{
"X-Key" = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
"X-Action" = "api-sign"
"X-Algorithm" = "sha256"
"X-File-Name" = $urlEncodedFileName
}
Invoke-RestMethod -Uri "https://api.evsign.cn/v1" `
-Method Post `
-Headers $headers `
-InFile $exePath `
-OutFile $outputPath
Write-Host "签名完成:$outputPath"
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
string exePath = @"C:\Build\Setup_v1.0.0.exe";
string outputPath = @"C:\Build\Setup_v1.0.0_signed.exe";
byte[] exeBytes = File.ReadAllBytes(exePath);
string urlEncodedFileName = Uri.EscapeDataString("Setup_v1.0.0.exe");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.evsign.cn/v1")
{
Content = new ByteArrayContent(exeBytes)
};
request.Headers.Add("X-Key", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX");
request.Headers.Add("X-Action", "api-sign");
request.Headers.Add("X-Algorithm", "sha256");
request.Headers.Add("X-File-Name", urlEncodedFileName);
var response = await client.SendAsync(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
byte[] signedExe = await response.Content.ReadAsByteArrayAsync();
File.WriteAllBytes(outputPath, signedExe);
Console.WriteLine($"签名成功:{outputPath}");
}
else
{
string error = await response.Content.ReadAsStringAsync();
Console.WriteLine($"签名失败:{error}");
}
}
}
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"net/url"
"os"
)
func main() {
exePath := "Setup_v1.0.0.exe"
outputPath := "Setup_v1.0.0_signed.exe"
exeBytes, err := os.ReadFile(exePath)
if err != nil {
panic(err)
}
req, err := http.NewRequest("POST", "https://api.evsign.cn/v1", bytes.NewReader(exeBytes))
if err != nil {
panic(err)
}
req.Header.Set("X-Key", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
req.Header.Set("X-Action", "api-sign")
req.Header.Set("X-Algorithm", "sha256")
req.Header.Set("X-File-Name", url.PathEscape("Setup_v1.0.0.exe"))
req.Header.Set("Content-Type", "application/octet-stream")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode == 200 {
outFile, err := os.Create(outputPath)
if err != nil {
panic(err)
}
defer outFile.Close()
io.Copy(outFile, resp.Body)
fmt.Printf("签名成功:%s\n", outputPath)
} else {
body, _ := io.ReadAll(resp.Body)
fmt.Printf("签名失败:%s\n", string(body))
}
}