如何从Buffer中正确提取2048位Diffie-Hellman质数?
问题描述
我正尝试使用Telegram API实现系统内直接发起Telegram通话,该库要求端到端加密,因此调用Api.messages.GetDhConfig方法从API获取2048位Diffie-Hellman质数。但该质数以Buffer形式返回,我尝试转换时得到负数,结果明显错误,转字符串再转数字也无效。以下是我的代码:
async requestCall(userId: string) { try { const dhConfig = await this.invoke( new Api.messages.GetDhConfig({ randomLength: 32 }) ); // convert p to number // @ts-expect-error ignore const p = dhConfig.p as Buffer; // convert p to integer const pInt = p.readInt32BE(0); // @ts-expect-error ignore const g = dhConfig.g; // chose random value between 1 and p-1 const a = Math.floor(Math.random() * (pInt - 1)) + 1; // calculate g^a mod p const ga = Math.pow(g, a) % pInt; this.ga = ga; // get the ga hash SHA256 of g^a mod p as 32 bytes const gaHash = crypto.createHash('sha256').update(ga.toString()).digest(); // save in the session this.a = a; this.p = pInt; // request the call await this.invoke( new Api.phone.RequestCall({ userId, randomId: Math.floor(Math.random() * 100000000), gAHash: gaHash, protocol: new Api.PhoneCallProtocol({ udpP2p: true, udpReflector: true, minLayer: 65, maxLayer: 65, libraryVersions: ['1.0.0', '2.0.0', '3.0.0', '4.0.0'] }) }) ); } catch (err) { console.log('[Telegram Client] [call] error: ', err); } }
请问该如何从这个Buffer中正确提取出质数?
解决方案
你的问题核心在于2048位的质数远超出了JavaScript原生Number类型的精度范围(Number最多支持53位精度),而且readInt32BE仅读取Buffer前4个字节,完全没拿到完整质数,自然得到错误结果。
下面提供两种可行的处理方案:
方案1:使用原生BigInt
直接将Buffer转为十六进制字符串,再用BigInt解析完整的超大整数:
// 将Buffer转为十六进制字符串,用BigInt解析完整质数 const pBigInt = BigInt('0x' + p.toString('hex')); const gBigInt = BigInt(dhConfig.g);
生成密码学安全的随机数a(不能用Math.random(),精度不足且不安全):
// 生成1到pBigInt-1之间的安全随机大数 function generateRandomBigInt(max: bigint): bigint { const byteLength = max.toString(16).length / 2; let randomBytes; let randomBigInt; do { randomBytes = crypto.randomBytes(byteLength); randomBigInt = BigInt('0x' + randomBytes.toString('hex')); } while (randomBigInt === 0n || randomBigInt >= max); return randomBigInt; } const a = generateRandomBigInt(pBigInt - 1n);
计算g^a mod p时,用BigInt原生的幂取模运算(避免Math.pow的精度丢失):
const ga = gBigInt ** a % pBigInt;
生成哈希时,要基于ga的原始字节而不是字符串:
// 将BigInt转回Buffer function bigIntToBuffer(num: bigint): Buffer { let hex = num.toString(16); // 补前导零确保字节数为偶数 if (hex.length % 2 !== 0) hex = '0' + hex; return Buffer.from(hex, 'hex'); } const gaBuffer = bigIntToBuffer(ga); const gaHash = crypto.createHash('sha256').update(gaBuffer).digest();
方案2:使用bn.js库
如果项目依赖bn.js(多数Telegram API库会自带),处理更简洁:
import BN from 'bn.js'; // 将Buffer转为BN实例 const pBN = new BN(p); const gBN = new BN(dhConfig.g); // 生成安全随机数a const a = new BN(crypto.randomBytes(32)).mod(pBN.subn(1)).addn(1); // 计算g^a mod p const gaBN = gBN.pow(a, pBN); // 转Buffer生成哈希 const gaBuffer = gaBN.toBuffer(); const gaHash = crypto.createHash('sha256').update(gaBuffer).digest();
关键注意事项
- 密码学场景下,绝对不能用
Math.random()生成随机数,必须用crypto.randomBytes这类安全随机源。 - 超大整数运算必须用
BigInt或专门的大数库,原生Number会丢失精度导致加密逻辑完全失效。 - 生成哈希时要基于原始字节(Buffer),转字符串会改变哈希结果,无法通过Telegram的校验。
内容的提问来源于stack exchange,提问作者mohammad hanafi
相关产品推荐
相关产品推荐

