# @foxt/js-srp 中文文档

规范地址：https://npmcn.com/package/%40foxt/js-srp/

版本：0\.0\.3-patch2
npm 元数据更新时间：2024-02-29T13:26:27.386Z
中文译文更新时间：2026-11-15T10:52:36.888Z
软件包许可：ISC

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40foxt/js-srp
原文对照：https://npmcn.com/package/%40foxt/js-srp/?lang=original
项目主页：https://github.com/foxt/js-srp-gsa#readme
源码仓库：https://github.com/foxt/js-srp-gsa

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：80.5 kB，34 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- 运行依赖：0 个
- 首次发布：2023-10-11T00:24:37.999Z
- 最新版本发布：2024-02-29T13:26:26.990Z
- 近 30 天下载量：195051（2026-10-16 至 2026-11-14，npm 官方下载统计）
- 已发布版本数：3（近 12 个月 0 个）

## 中文简介

js-srp被修改为添加对苹果公司iCloud.com使用的 SRP 执行的支持

## 安装

```sh
npm install @foxt/js-srp
```

也可以使用：`pnpm add @foxt/js-srp`、`yarn add @foxt/js-srp`、`bun add @foxt/js-srp`

## README

# js-srp-gsa (英语)

这是叉子 [`js-srp`](https://github.com/jchv/js-srp) 添加对 Apple icloud.com 使用的 SRP 变体的支持.

## 示例用途

### SRP 包装器

```ts
import { Client, Hash, Mode, Srp, util } from "@foxt/js-srp";
import crypto from "crypto";

export type SRPProtocol = "s2k" | "s2k_fo";

export interface ServerSRPInitRequest {
    a: string;
    accountName: string;
    protocols: SRPProtocol[];
}
export interface ServerSRPInitResponse {
    iteration: number;
    salt: string;
    protocol: "s2k" | "s2k_fo";
    b: string;
    c: string;
}
export interface ServerSRPCompleteRequest {
    accountName: string;
    c: string;
    m1: string;
    m2: string;
    rememberMe: boolean;
    trustTokens: string[];
}

let srp = new Srp(Mode.GSA, Hash.SHA256, 2048);
const stringToU8Array = (str: string) => new TextEncoder().encode(str);
const base64ToU8Array = (str: string) => Uint8Array.from(Buffer.from(str, "base64"));
export class GSASRPAuthenticator {
    constructor(private username: string) { }
    private srpClient?: Client = undefined;


    private async derivePassword(protocol: "s2k" | "s2k_fo", password: string, salt: Uint8Array, iterations: number) {
        let passHash = new Uint8Array(await util.hash(srp.h, stringToU8Array(password)));
        if (protocol == "s2k_fo") {
            passHash = stringToU8Array(util.toHex(passHash));
        }

        let imported = await crypto.subtle.importKey(
            "raw",
            passHash,
            { name: "PBKDF2" },
            false,
            ["deriveBits"]
        );
        let derived = await crypto.subtle.deriveBits({
            name: "PBKDF2",
            hash: { name: "SHA-256" },
            iterations, salt
        }, imported, 256);

        return new Uint8Array(derived);
    }


    async getInit(): Promise<ServerSRPInitRequest> {
        if (this.srpClient) throw new Error("Already initialized");
        this.srpClient = await srp.newClient(
            stringToU8Array(this.username),
            // provide fake passsword because we need to get data from server
            new Uint8Array()
        );
        let a = Buffer.from(
            util.bytesFromBigint(this.srpClient.A)
        ).toString("base64");
        return {
            a, protocols: ["s2k", "s2k_fo"],
            accountName: this.username,
        };
    }
    async getComplete(password: string, serverData: ServerSRPInitResponse): Promise<Pick<ServerSRPCompleteRequest, "m1" | "m2" | "c" | "accountName">> {
        if (!this.srpClient) throw new Error("Not initialized");
        if ((serverData.protocol != "s2k") &&
            (serverData.protocol != "s2k_fo")) throw new Error("Unsupported protocol " + serverData.protocol);
        let salt = base64ToU8Array(serverData.salt);
        let serverPub = base64ToU8Array(serverData.b);
        let iterations = serverData.iteration;
        let derived = await this.derivePassword(
            serverData.protocol, password,
            salt, iterations
        );
        this.srpClient.p = derived;
        await this.srpClient.generate(salt, serverPub);
        let m1 = Buffer.from(this.srpClient._M).toString("base64");
        let M2 = await this.srpClient.generateM2();
        let m2 = Buffer.from(M2).toString("base64");
        return {
            accountName: this.username,
            m1,
            m2,
            c: serverData.c,
        };
    }
}
```

### API 接口

```ts
import prompt from "prompt";
import type { ServerSRPInitResponse } from "./GSASRPAuthenticator.js";
import { GSASRPAuthenticator } from "./GSASRPAuthenticator.js";

async function request(url: string, body: any) {
    let req = await fetch("https://idmsa.apple.com/appleauth/auth/signin" + url, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
            "Accept": "application/json, text/javascript, */*; q=0.01",
        },
        body: JSON.stringify(body)
    })
    console.log()
    console.log("POST", url)
    console.log("    ", req.status, req.statusText)

    if (!req.ok) throw new Error(
        "Failed to get init response " + req.status + " " + req.statusText + ": " 
        + await req.text()
    );
    return await req.json();
}


async function login(username: string, password: string) {
    // set up SRP authenticator & get public key
    let authenticator = new GSASRPAuthenticator(username);
    let initData = await authenticator.getInit();

    // request SRP init data from server
    let initResp = await request("/init", initData)
    

    // get proof of password
    let proof = await authenticator.getComplete(password, initResp as ServerSRPInitResponse);

    // send proof to server
    let completeResp = await request("/complete", {
            ...proof,
            rememberMe: true,
            trustTokens: []
    })
    console.log(completeResp)

    
}
prompt.start();
prompt.get({
    properties: {
        username: {
            description: "Apple ID"
        },
        password: {
            description: "Password",
            hidden: true
        }
    }
}, (err, result) => {
    if (err) return console.error(err);
    login(result.username as string, result.password as string).catch(console.error);
})


```

## 常见问题

### @foxt/js-srp 是什么？

js-srp被修改为添加对苹果公司iCloud\.com使用的 SRP 执行的支持

### 如何安装 @foxt/js-srp？

在项目目录运行 npm install @foxt/js-srp，或使用 pnpm add / yarn add / bun add。

### @foxt/js-srp 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @foxt/js-srp 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

### @foxt/js-srp 可以商用吗？

@foxt/js-srp 使用 ISC 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @foxt/js-srp 的下载量有多少？

根据 npm 官方下载统计，@foxt/js-srp 在 2026/10/16 – 2026/11/14 这 30 天内被下载 195,051 次，日均约 6,502 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @foxt/js-srp 还在维护吗？

最新版本 0\.0\.3-patch2 发布于 2024/02/29（2 年前）；至今共发布 3 个版本，其中过去 12 个月发布了 0 个；首次发布于 2023/10/11。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

