mtgsig
- 内容介绍
- 文章标签
- 相关推荐
#!/usr/bin/env node
'use strict';
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
/**
* 完整的单文件mtgsig纯算离线签名器
* 支持离线生成mtgsig签名,使用预设的profile参数
*/
// ============= 基础MD5和XOR工具 =============
function md5Hex(input) {
return crypto.createHash('md5').update(input).digest('hex');
}
function xorHex(buffer, xorKeyHex) {
const xorKey = Buffer.from(xorKeyHex, 'hex');
const out = Buffer.alloc(16);
for (let i = 0; i < 16; i++) {
out[i] = buffer[i] ^ xorKey[i];
}
return out.toString('hex');
}
function getSaltFromA6(a6) {
const s = String(a6 || '');
if (!s.startsWith('h1.9') || s.length < 10) {
throw new Error('Invalid a6 payload: expected prefix h1.9 and at least 6 salt chars');
}
return s.slice(4, 10);
}
// ============= A3生成(从WEBDFPID) =============
function deriveA3FromWebdfpid(webdfpid) {
const s = String(webdfpid || '').trim();
if (!s) {
return '';
}
return s.split('-')[0] || '';
}
function parseCookieHeader(cookieHeader) {
if (!cookieHeader) {
return {};
}
return String(cookieHeader)
.split(';')
.map((part) => part.trim())
.filter(Boolean)
.reduce((cookies, part) => {
const idx = part.indexOf('=');
if (idx === -1) {
return cookies;
}
const key = part.slice(0, idx).trim();
const value = part.slice(idx + 1).trim();
if (key) {
cookies[key] = value;
}
return cookies;
}, {});
}
function pickCookieHeader(input) {
if (!input) {
return '';
}
return input.cookie
|| input.Cookie
|| (input.headers && (input.headers.Cookie || input.headers.cookie))
|| '';
}
function resolveA3(input = {}) {
if (input.a3) {
return String(input.a3);
}
const cookies = parseCookieHeader(pickCookieHeader(input));
return deriveA3FromWebdfpid(input.webdfpid || input.dfpid || cookies.WEBDFPID);
}
// ============= A8生成(核心签名) =============
function generateA8({ a6, timestamp, xorKeyHex, suffix = '2' }) {
const salt = getSaltFromA6(a6);
const input = `h1.9${salt}${String(timestamp)}${suffix}`;
const digest = crypto.createHash('md5').update(input).digest();
return xorHex(digest, xorKeyHex);
}
// ============= A6Key生成 =============
function generateA6Key(a8, a9, a10) {
return md5Hex(`${a8}${a9}${a10}`);
}
// ============= D1生成 =============
function generateD1({ d1, sessionId, payload }) {
if (d1 && typeof d1 === 'string' && d1.length === 32) {
return d1;
}
if (sessionId) {
return md5Hex(String(sessionId));
}
return md5Hex(String(payload || ''));
}
// ============= A5生成(假设使用默认值或提供) =============
function generateA5({ a5, timestamp }) {
if (a5) {
return String(a5);
}
// 如果没有提供,返回空白或使用placeholder
return '';
}
// ============= 默认Profile(从浏览器捕获) =============
const DEFAULT_PROFILE = {
a9: '4.2.0,7,39',
a10: '10',
a8Suffix: '2',
xorKeyHex: '19f02f45fb1dee6ce3e613b8aefd6d13', // 从latest_request_20260420.json推导
description: 'Default MTGSig profile for offline signing'
};
// ============= 签名生成器 =============
function createOfflineMtgsigSigner(profile = {}) {
const resolvedProfile = {
...DEFAULT_PROFILE,
...profile
};
function sign(request = {}, options = {}) {
const timestamp = options.clock || request.a2 || request.timestamp || Date.now();
const a9 = options.a9 || request.a9 || resolvedProfile.a9;
const a10 = options.a10 || request.a10 || resolvedProfile.a10;
const suffix = options.suffix || resolvedProfile.a8Suffix;
// 需要的数据:a6payload或从请求推导
const a6 = request.a6 || request.payload;
if (!a6) {
throw new Error('Missing a6 payload in request or profile');
}
// 生成各个字段
const a8 = generateA8({
a6,
timestamp,
xorKeyHex: resolvedProfile.xorKeyHex,
suffix
});
const a3 = resolveA3(request);
const d1 = generateD1({
d1: request.d1,
sessionId: request.sessionId || request.arg23,
payload: a6
});
const a6Key = generateA6Key(a8, a9, a10);
const a5 = generateA5({
a5: request.a5,
timestamp
});
const a1 = '1.2';
const a2 = String(timestamp);
return {
a1,
a2,
a3,
a5,
a6,
a8,
a9,
a10,
d1,
a6Key,
x0: 4 // 固定值
};
}
return { sign };
}
// ============= 使用工具和CLI =============
function loadJsonFile(filePath) {
if (!filePath) {
return null;
}
try {
const content = fs.readFileSync(path.resolve(filePath), 'utf8');
return JSON.parse(content);
} catch (err) {
console.error(`Failed to load ${filePath}:`, err.message);
return null;
}
}
function normalizeInput(input) {
// 支持多种输入格式
if (typeof input === 'object' && input.request) {
return input.request;
}
return input || {};
}
function main() {
const args = process.argv.slice(2);
const requestPath = args[0];
const profilePath = args[1];
// 如果没有参数,显示帮助和演示
if (!requestPath) {
console.log('══════════════════════════════════════════════════════');
console.log(' mtgsig 离线签名生成器');
console.log('══════════════════════════════════════════════════════');
console.log('');
console.log('用法:');
console.log(' node mtgsig_pure_offline_signer.js [request.json] [profile.json]');
console.log('');
console.log('示例 1 - 使用默认参数(旧版本 4.2.0,7,39):');
console.log(' node mtgsig_pure_offline_signer.js request.json');
console.log('');
console.log('示例 2 - 使用新版本参数(4.2.0,7,8):');
console.log(' node mtgsig_pure_offline_signer.js request.json new_version_profile.json');
console.log('');
console.log('request.json 格式:');
console.log(JSON.stringify({
a2: '当前时间戳(毫秒),如: ' + Date.now(),
a6: 'h1.9HK1PkF...(真实的payload)',
a3: '372887v7w5yu5282067xww2y1463u8wy80yw0z8xu4z9795881u44vw7',
webdfpid: '372887v7w5yu5282067xww2y1463u8wy80yw0z8xu4z9795881u44vw7-...'
}, null, 2));
console.log('');
console.log('══════════════════════════════════════════════════════');
console.log('执行演示(使用测试数据):');
console.log('══════════════════════════════════════════════════════');
console.log('');
// 演示模式:使用测试数据
const demoProfile = {
a9: '4.2.0,7,8',
a10: '98',
a8Suffix: '2',
xorKeyHex: 'b5ba5e10d16d19d1f2858b6497caabdd'
};
const demoRequest = {
a2: Date.now(), // 使用当前时间戳而不是写死的值
a6: 'h1.9HK1PkFlf4Ob8girMsbirMgPXRSgRpTkkG6ek+9XOfrTyBfz/ElmPja0cIA5BI+MVJTPmY38oUCtvWO2Caf4Eu9TsvpzGWF1/sc0go/luDfjsSIp8WN3Ngl6rBYvMeOggErpTlqvfDmGOpZ2wc+hVEHJxt5Q2kKZnPxI8E0IIdELX4+gNL0a4AF5p76g2Wn+bBLVnpvytVNKpqL/Lfr3y2Mk0asGbuqHNzmV3ivHSxnNui3ltOUB7HUGQYhMonCY0kzHlNGhCbTw3/7DQvQgck4UERlc3UrVukC9cES1kIYjxf52Rj1j4V0kfo1cf9OnWYRd9fCWmgXOIoJi5J9Da2+6R+pUtNhnnAkdgxVWWuQGieJQAOFStvImm6qjKvgPpCmBalIVenyKm79g1e6nF5w==',
a3: '372887v7w5yu5282067xww2y1463u8wy80yw0z8xu4z9795881u44vw7',
webdfpid: '372887v7w5yu5282067xww2y1463u8wy80yw0z8xu4z9795881u44vw7-1776843046392-1768882819070QUEUOWCfd79fef3d01d5e9aadc18ccd4d0c95072412'
};
console.log('📋 演示配置 (新版本):');
console.log(` a9: ${demoProfile.a9}`);
console.log(` a10: ${demoProfile.a10}`);
console.log(` xorKeyHex: ${demoProfile.xorKeyHex}`);
console.log('');
console.log('📋 演示请求数据:');
console.log(` timestamp: ${demoRequest.a2}`);
console.log(` a3: ${demoRequest.a3.substring(0, 30)}...`);
console.log('');
const signer = createOfflineMtgsigSigner(demoProfile);
try {
const signature = signer.sign(demoRequest);
console.log('✅ 生成成功!');
console.log('');
console.log('📤 生成的签名:');
console.log(JSON.stringify(signature, null, 2));
console.log('');
console.log(`✓ a8 (关键签名): ${signature.a8}`);
console.log(`✓ a6Key (验证码): ${signature.a6Key}`);
} catch (err) {
console.error('❌ 生成失败:', err.message);
process.exitCode = 1;
}
return;
}
// 加载请求数据
const request = loadJsonFile(requestPath) || {};
console.error(`[INFO] Loaded request from: ${requestPath}`);
// 加载自定义profile
let customProfile = null;
if (profilePath) {
customProfile = loadJsonFile(profilePath);
console.error(`[INFO] Loaded profile from: ${profilePath}`);
}
// 如果输入JSON包含request字段,使用它
const normalizedRequest = normalizeInput(request);
// 创建签名器
const signer = createOfflineMtgsigSigner(customProfile);
// 生成签名
try {
const signature = signer.sign(normalizedRequest);
console.log(JSON.stringify(signature, null, 2));
} catch (err) {
console.error('[ERROR]', err.message);
process.exitCode = 1;
}
}
// ============= 导出 =============
if (require.main === module) {
main();
}
module.exports = {
md5Hex,
xorHex,
getSaltFromA6,
generateA8,
generateA6Key,
generateD1,
generateA5,
resolveA3,
createOfflineMtgsigSigner,
DEFAULT_PROFILE
};
大佬勿喷,AI撕的“纯算”,大家参考,我调用了一下没问题
网友解答:--【壹】--:
mtgsig的算法不是一样的吗?目前最新的,我没细看 好像是1.2
--【贰】--:
你这个是网页端,还是app端的? 啥版本的?
#!/usr/bin/env node
'use strict';
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
/**
* 完整的单文件mtgsig纯算离线签名器
* 支持离线生成mtgsig签名,使用预设的profile参数
*/
// ============= 基础MD5和XOR工具 =============
function md5Hex(input) {
return crypto.createHash('md5').update(input).digest('hex');
}
function xorHex(buffer, xorKeyHex) {
const xorKey = Buffer.from(xorKeyHex, 'hex');
const out = Buffer.alloc(16);
for (let i = 0; i < 16; i++) {
out[i] = buffer[i] ^ xorKey[i];
}
return out.toString('hex');
}
function getSaltFromA6(a6) {
const s = String(a6 || '');
if (!s.startsWith('h1.9') || s.length < 10) {
throw new Error('Invalid a6 payload: expected prefix h1.9 and at least 6 salt chars');
}
return s.slice(4, 10);
}
// ============= A3生成(从WEBDFPID) =============
function deriveA3FromWebdfpid(webdfpid) {
const s = String(webdfpid || '').trim();
if (!s) {
return '';
}
return s.split('-')[0] || '';
}
function parseCookieHeader(cookieHeader) {
if (!cookieHeader) {
return {};
}
return String(cookieHeader)
.split(';')
.map((part) => part.trim())
.filter(Boolean)
.reduce((cookies, part) => {
const idx = part.indexOf('=');
if (idx === -1) {
return cookies;
}
const key = part.slice(0, idx).trim();
const value = part.slice(idx + 1).trim();
if (key) {
cookies[key] = value;
}
return cookies;
}, {});
}
function pickCookieHeader(input) {
if (!input) {
return '';
}
return input.cookie
|| input.Cookie
|| (input.headers && (input.headers.Cookie || input.headers.cookie))
|| '';
}
function resolveA3(input = {}) {
if (input.a3) {
return String(input.a3);
}
const cookies = parseCookieHeader(pickCookieHeader(input));
return deriveA3FromWebdfpid(input.webdfpid || input.dfpid || cookies.WEBDFPID);
}
// ============= A8生成(核心签名) =============
function generateA8({ a6, timestamp, xorKeyHex, suffix = '2' }) {
const salt = getSaltFromA6(a6);
const input = `h1.9${salt}${String(timestamp)}${suffix}`;
const digest = crypto.createHash('md5').update(input).digest();
return xorHex(digest, xorKeyHex);
}
// ============= A6Key生成 =============
function generateA6Key(a8, a9, a10) {
return md5Hex(`${a8}${a9}${a10}`);
}
// ============= D1生成 =============
function generateD1({ d1, sessionId, payload }) {
if (d1 && typeof d1 === 'string' && d1.length === 32) {
return d1;
}
if (sessionId) {
return md5Hex(String(sessionId));
}
return md5Hex(String(payload || ''));
}
// ============= A5生成(假设使用默认值或提供) =============
function generateA5({ a5, timestamp }) {
if (a5) {
return String(a5);
}
// 如果没有提供,返回空白或使用placeholder
return '';
}
// ============= 默认Profile(从浏览器捕获) =============
const DEFAULT_PROFILE = {
a9: '4.2.0,7,39',
a10: '10',
a8Suffix: '2',
xorKeyHex: '19f02f45fb1dee6ce3e613b8aefd6d13', // 从latest_request_20260420.json推导
description: 'Default MTGSig profile for offline signing'
};
// ============= 签名生成器 =============
function createOfflineMtgsigSigner(profile = {}) {
const resolvedProfile = {
...DEFAULT_PROFILE,
...profile
};
function sign(request = {}, options = {}) {
const timestamp = options.clock || request.a2 || request.timestamp || Date.now();
const a9 = options.a9 || request.a9 || resolvedProfile.a9;
const a10 = options.a10 || request.a10 || resolvedProfile.a10;
const suffix = options.suffix || resolvedProfile.a8Suffix;
// 需要的数据:a6payload或从请求推导
const a6 = request.a6 || request.payload;
if (!a6) {
throw new Error('Missing a6 payload in request or profile');
}
// 生成各个字段
const a8 = generateA8({
a6,
timestamp,
xorKeyHex: resolvedProfile.xorKeyHex,
suffix
});
const a3 = resolveA3(request);
const d1 = generateD1({
d1: request.d1,
sessionId: request.sessionId || request.arg23,
payload: a6
});
const a6Key = generateA6Key(a8, a9, a10);
const a5 = generateA5({
a5: request.a5,
timestamp
});
const a1 = '1.2';
const a2 = String(timestamp);
return {
a1,
a2,
a3,
a5,
a6,
a8,
a9,
a10,
d1,
a6Key,
x0: 4 // 固定值
};
}
return { sign };
}
// ============= 使用工具和CLI =============
function loadJsonFile(filePath) {
if (!filePath) {
return null;
}
try {
const content = fs.readFileSync(path.resolve(filePath), 'utf8');
return JSON.parse(content);
} catch (err) {
console.error(`Failed to load ${filePath}:`, err.message);
return null;
}
}
function normalizeInput(input) {
// 支持多种输入格式
if (typeof input === 'object' && input.request) {
return input.request;
}
return input || {};
}
function main() {
const args = process.argv.slice(2);
const requestPath = args[0];
const profilePath = args[1];
// 如果没有参数,显示帮助和演示
if (!requestPath) {
console.log('══════════════════════════════════════════════════════');
console.log(' mtgsig 离线签名生成器');
console.log('══════════════════════════════════════════════════════');
console.log('');
console.log('用法:');
console.log(' node mtgsig_pure_offline_signer.js [request.json] [profile.json]');
console.log('');
console.log('示例 1 - 使用默认参数(旧版本 4.2.0,7,39):');
console.log(' node mtgsig_pure_offline_signer.js request.json');
console.log('');
console.log('示例 2 - 使用新版本参数(4.2.0,7,8):');
console.log(' node mtgsig_pure_offline_signer.js request.json new_version_profile.json');
console.log('');
console.log('request.json 格式:');
console.log(JSON.stringify({
a2: '当前时间戳(毫秒),如: ' + Date.now(),
a6: 'h1.9HK1PkF...(真实的payload)',
a3: '372887v7w5yu5282067xww2y1463u8wy80yw0z8xu4z9795881u44vw7',
webdfpid: '372887v7w5yu5282067xww2y1463u8wy80yw0z8xu4z9795881u44vw7-...'
}, null, 2));
console.log('');
console.log('══════════════════════════════════════════════════════');
console.log('执行演示(使用测试数据):');
console.log('══════════════════════════════════════════════════════');
console.log('');
// 演示模式:使用测试数据
const demoProfile = {
a9: '4.2.0,7,8',
a10: '98',
a8Suffix: '2',
xorKeyHex: 'b5ba5e10d16d19d1f2858b6497caabdd'
};
const demoRequest = {
a2: Date.now(), // 使用当前时间戳而不是写死的值
a6: 'h1.9HK1PkFlf4Ob8girMsbirMgPXRSgRpTkkG6ek+9XOfrTyBfz/ElmPja0cIA5BI+MVJTPmY38oUCtvWO2Caf4Eu9TsvpzGWF1/sc0go/luDfjsSIp8WN3Ngl6rBYvMeOggErpTlqvfDmGOpZ2wc+hVEHJxt5Q2kKZnPxI8E0IIdELX4+gNL0a4AF5p76g2Wn+bBLVnpvytVNKpqL/Lfr3y2Mk0asGbuqHNzmV3ivHSxnNui3ltOUB7HUGQYhMonCY0kzHlNGhCbTw3/7DQvQgck4UERlc3UrVukC9cES1kIYjxf52Rj1j4V0kfo1cf9OnWYRd9fCWmgXOIoJi5J9Da2+6R+pUtNhnnAkdgxVWWuQGieJQAOFStvImm6qjKvgPpCmBalIVenyKm79g1e6nF5w==',
a3: '372887v7w5yu5282067xww2y1463u8wy80yw0z8xu4z9795881u44vw7',
webdfpid: '372887v7w5yu5282067xww2y1463u8wy80yw0z8xu4z9795881u44vw7-1776843046392-1768882819070QUEUOWCfd79fef3d01d5e9aadc18ccd4d0c95072412'
};
console.log('📋 演示配置 (新版本):');
console.log(` a9: ${demoProfile.a9}`);
console.log(` a10: ${demoProfile.a10}`);
console.log(` xorKeyHex: ${demoProfile.xorKeyHex}`);
console.log('');
console.log('📋 演示请求数据:');
console.log(` timestamp: ${demoRequest.a2}`);
console.log(` a3: ${demoRequest.a3.substring(0, 30)}...`);
console.log('');
const signer = createOfflineMtgsigSigner(demoProfile);
try {
const signature = signer.sign(demoRequest);
console.log('✅ 生成成功!');
console.log('');
console.log('📤 生成的签名:');
console.log(JSON.stringify(signature, null, 2));
console.log('');
console.log(`✓ a8 (关键签名): ${signature.a8}`);
console.log(`✓ a6Key (验证码): ${signature.a6Key}`);
} catch (err) {
console.error('❌ 生成失败:', err.message);
process.exitCode = 1;
}
return;
}
// 加载请求数据
const request = loadJsonFile(requestPath) || {};
console.error(`[INFO] Loaded request from: ${requestPath}`);
// 加载自定义profile
let customProfile = null;
if (profilePath) {
customProfile = loadJsonFile(profilePath);
console.error(`[INFO] Loaded profile from: ${profilePath}`);
}
// 如果输入JSON包含request字段,使用它
const normalizedRequest = normalizeInput(request);
// 创建签名器
const signer = createOfflineMtgsigSigner(customProfile);
// 生成签名
try {
const signature = signer.sign(normalizedRequest);
console.log(JSON.stringify(signature, null, 2));
} catch (err) {
console.error('[ERROR]', err.message);
process.exitCode = 1;
}
}
// ============= 导出 =============
if (require.main === module) {
main();
}
module.exports = {
md5Hex,
xorHex,
getSaltFromA6,
generateA8,
generateA6Key,
generateD1,
generateA5,
resolveA3,
createOfflineMtgsigSigner,
DEFAULT_PROFILE
};
大佬勿喷,AI撕的“纯算”,大家参考,我调用了一下没问题
网友解答:--【壹】--:
mtgsig的算法不是一样的吗?目前最新的,我没细看 好像是1.2
--【贰】--:
你这个是网页端,还是app端的? 啥版本的?

