AutoJs6

- Overview - 综述
- About - 关于文档
- Progress - 文档部署进度
- Changelog - 文档更新日志

- Manual - AutoJs6 使用手册
- Q & A - 疑难解答

- Global - 全局对象
- Automator - 自动化
- AutoJs6 - 本体应用
- App - 通用应用
- Color - 颜色
- Image - 图像
- OCR - 光学字符识别
- Barcode - 条码
- QR Code - 二维码
- Keys - 按键
- Device - 设备
- Storage - 储存
- File - 文件
- Engine - 引擎
- Task - 任务
- Module - 模块
- Plugins - 插件
- Toast - 消息浮动框
- Notice - 消息通知
- Console - 控制台
- Shell
- Shizuku
- Media - 多媒体
- Sensor - 传感器
- Recorder - 记录器
- Timer - 定时器
- Thread - 线程
- Continuation - 协程
- Event - 事件监听
- Dialog - 对话框
- Floaty - 悬浮窗
- Canvas - 画布
- UI - 用户界面
- Web - 万维网
- HTTP
- Base64
- Crypto - 密文
- Internationalization - 国际化
- Standardization - 标准化
- E4X

- UiSelector - 选择器
- UiObject - 控件节点
- UiObjectCollection - 控件集合
- UiObjectActions - 控件节点行为
- WebSocket
- EventEmitter - 事件发射器
- ImageWrapper - 包装图像类
- App - 应用枚举类
- Color - 颜色类
- Version - 版本工具类
- Polyfill - 代码填泥
- Arrayx - Array 扩展
- Numberx - Number 扩展
- Mathx - Math 扩展

- Exceptions - 异常
- Intent - 意图
- Runtime - 运行时
- Context - 上下文
- Activity - 活动

- Scripting Java - 脚本化 Java
- Android API Level - 安卓 API 级别
- Color Table - 颜色列表

- Glossaries - 术语
- HttpHeader - HTTP 标头
- HttpRequestMethods - HTTP 请求方法
- MimeType - MIME 类型
- NotificationChannel - 通知渠道

- Data Types - 数据类型
- Omnipotent Types - 全能类型
- Storage - 存储类
- AndroidBundle
- AndroidRect
- CryptoCipherOptions
- CryptoKey
- CryptoKeyPair
- ConsoleBuildOptions
- HttpRequestBuilderOptions
- HttpRequestHeaders
- HttpResponseBody
- HttpResponseHeaders
- HttpResponse
- InjectableWebClient
- InjectableWebView
- NoticeOptions
- NoticeChannelOptions
- NoticePresetConfiguration
- NoticeBuilder
- Okhttp3HttpUrl
- OcrOptions
- Okhttp3Request
- OpencvPoint
- OpencvRect
- OpencvSize

- GitHub - 应用项目地址
- GitHub - 文档项目地址

# 密文 (Crypto)#

crypto 模块提供 [ 对称加密 (如 AES) / 非对称加密 (如 RSA) / 消息摘要 (如 MD5, SHA) ] 等支持.

> 注: 本章节参考自 Auto.js Pro 文档.

crypto

## [m] digest#

### digest(message, algorithm)#

`6.3.2` `Overload 1/4`

- message { string } - 待获取摘要的消息
- algorithm { CryptoDigestAlgorithm } - 消息摘要算法
- returns { string | JsByteArray }

获取 `消息 (message)` 经指定 `算法 (algorithm)` 计算后的 `消息摘要 (message digest)`.

    /* 获取字符串 "hello" 的 MD5 摘要. */
    console.log(crypto.digest('hello', 'MD5')); // 5d41402abc4b2a76b9719d911017c592

    /* 获取字符串 "hello" 的 SHA-1 摘要. */
    console.log(crypto.digest('hello', 'SHA-1')); // aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d

### digest(message)#

`6.3.2` `Overload 2/4`

- message { string } - 待获取摘要的消息
- returns { string }

获取 `消息 (message)` 的 MD5 `消息摘要 (message digest)`.

相当于 `crypto.digest(message, 'MD5')`.

    /* 获取字符串 "hello" 的 MD5 摘要. */
    console.log(crypto.digest('hello')); // 5d41402abc4b2a76b9719d911017c592
    console.log(crypto.digest('hello', 'MD5')); /* 同上. */

### digest(message, algorithm, options)#

`6.3.2` `Overload 3/4`

- message { string } - 待获取摘要的消息
- algorithm { CryptoDigestAlgorithm } - 消息摘要算法
- options { CryptoDigestOptions } - 选项参数
- returns { string | JsByteArray }

获取 `消息 (message)` 经指定 `算法 (algorithm)` 计算后的 `消息摘要 (message digest)`.

通过 `options` 参数可指定输入消息的类型 (文件, Base64, Hex, 字符串) 及摘要的类型 (字节数组, Base64, Hex, 字符串) 等.

    /* 获取字符串 "hello" 的 MD5 摘要, 并输出其字节数组. */
    console.log(crypto.digest('hello', 'MD5', { output: 'bytes' })); // [93, 65, 64, 42, -68, 75, 42, 118, -71, 113, -99, -111, 16, 23, -59, -110]

    /* 获取字符串 "hello" 的 SHA-1 摘要, 并输出其 Base64 编码. */
    console.log(crypto.digest('hello', 'SHA-1', { output: 'base64' })); // qvTGHdzF6KLavt4PO0gs2a6pQ00=

    /* 获取 Base64 数据的 MD5 摘要. */
    console.log(crypto.digest('qvTGHdzF6KLavt4PO0gs2a6pQ00=', 'MD5', { input: 'base64' })); // 406f65cdc25d6a9db86c06e4bc19a2cf

    /* 获取文件的 MD5 摘要. */
    let str = 'hello';
    let path = files.path(`./tmp-${Date.now()}`);
    files.create(path);
    files.write(path, str);
    console.log(crypto.digest(path, 'MD5', { input: 'file' })); // 5d41402abc4b2a76b9719d911017c592
    files.remove(path);

### digest(message, options)#

`6.3.2` `Overload 4/4`

- message { string } - 待获取摘要的消息
- algorithm { CryptoDigestAlgorithm } - 消息摘要算法
- options { CryptoDigestOptions } - 选项参数
- returns { string | JsByteArray }

获取 `消息 (message)` 的 MD5 `消息摘要 (message digest)`.

通过 `options` 参数可指定输入消息的类型 (文件, Base64, Hex, 字符串) 及摘要的类型 (字节数组, Base64, Hex, 字符串) 等.

相当于 `crypto.digest(message, 'MD5', options)`.

    /* 获取字符串 "hello" 的 MD5 摘要, 并输出其字节数组. */
    console.log(crypto.digest('hello', { output: 'bytes' })); // [93, 65, 64, 42, -68, 75, 42, 118, -71, 113, -99, -111, 16, 23, -59, -110]

    /* 获取 Base64 数据的 MD5 摘要. */
    console.log(crypto.digest('qvTGHdzF6KLavt4PO0gs2a6pQ00=', { input: 'base64' })); // 406f65cdc25d6a9db86c06e4bc19a2cf

    /* 获取文件的 MD5 摘要. */
    let str = 'hello';
    let path = files.path(`./tmp-${Date.now()}`);
    files.create(path);
    files.write(path, str);
    console.log(crypto.digest(path, { input: 'file' })); // 5d41402abc4b2a76b9719d911017c592
    files.remove(path);

## [m] encrypt#

### encrypt(data, key, transformation, options?)#

`6.3.2` `Overload [1-2]/2`

- data { string | JsByteArray | ByteArray } - 待加密数据
- key { crypto.Key | java.security.Key } - 加密密钥
- transformation { CryptoCipherTransformation } - 密码转换名称
- [ options ] { CryptoCipherOptions } - 选项参数
- returns { string | JsByteArray }

数据加密.

#### 输入#

`data` 参数的性质可借助 `options.input` 属性进行区分, 详见 CryptoCipherOptions#input.

如果 `data` 参数为字节数组, 则 `options.input` 将被忽略. 因为字节数组可以唯一确定 `data` 的性质, 无需再通过 `options.input` 指定.

#### 输出#

`options.output` 指定输出的数据格式, 详见 CryptoCipherOptions#output.

特别地, 当 `options.output` 为 `'file'` 时, 输出格式为十六进制值. 因为加密后写入文件的数据通常是不可读的 (常被视作乱码), 因此最终的返回值类型没有采用 `'string'`, 而是 `'hex'`.

`options.output` 影响的其实仅仅是加密结果的表现形式, 这些形式之前通常可以互相转换. 真正影响加密结果的, 是加密过程.

#### 加密#

加密过程受 `key` 和 `transformation` 两个参数的影响. 详见 crypto.Key 及 CryptoCipherTransformation 小节.

为保证最大兼容性, `key` 参数同时支持原生的 `java.security.Key` 类型, 此时 `key` 将自动按照 `new crypto.Key(key.encoded)` 进行转换.

#### 示例#

AES 算法加密示例:

    let message = 'hello';

    /* 创建密钥实例. */
    /* 密钥长度需为 [ 16, 24, 32 ] 之一. */
    let key = new crypto.Key('a'.repeat(16));

    /* 加密数据, 输出格式保持默认, 即 bytes. */
    console.log(crypto.encrypt(message, key, 'AES')); // [-20, 97, -47, 124, 88, 10, 85, -42, -128, -117, 11, 98, -33, -85, 106, 13]

    /* 输出格式修改为 Base64. */
    console.log(crypto.encrypt(message, key, 'AES', { output: 'base64' })); // 7GHRfFgKVdaAiwti36tqDQ==

    /* AES 默认工作模式为 ECB, 默认填充方式为 PKCS5Padding, 结果同上. */
    console.log(crypto.encrypt(message, key, 'AES/ECB/PKCS5Padding', { output: 'base64' })); // 7GHRfFgKVdaAiwti36tqDQ==

RSA 算法加密示例:

    let message = 'hello';

    /* 生成 RSA 密钥对. */
    let key = crypto.generateKeyPair('RSA', 2048);

    /* 使用公钥加密, 转换名称为 RSA/ECB/PKCS1Padding. */
    console.log(crypto.encrypt(message, key.publicKey, 'RSA/ECB/PKCS1Padding')); /* 结果随机, 因为生成的密钥是不确定的. */

#### 可逆性#

加密与解密具有可逆性:

    let message = 'hello';
    let key = new crypto.Key('a'.repeat(16));
    let encrypted = crypto.encrypt(message, key, 'AES');

    /* 解密还原为 hello. */
    console.log(crypto.decrypt(encrypted, key, 'AES', { output: 'string' }));

## [m] decrypt#

### decrypt(data, key, transformation, options?)#

`6.3.2` `Overload [1-2]/2`

- data { string | JsByteArray | ByteArray } - 待解密数据
- key { crypto.Key | java.security.Key } - 解密密钥
- transformation { CryptoCipherTransformation } - 密码转换名称
- [ options ] { CryptoCipherOptions } - 选项参数
- returns { string | JsByteArray }

数据解密.

#### 输入#

`data` 参数的性质可借助 `options.input` 属性进行区分, 详见 CryptoCipherOptions#input.

如果 `data` 参数为字节数组, 则 `options.input` 将被忽略. 因为字节数组可以唯一确定 `data` 的性质, 无需再通过 `options.input` 指定.

#### 输出#

`options.output` 指定输出的数据格式, 详见 CryptoCipherOptions#output.

特别地, 当 `options.output` 为 `'file'` 时, 输出格式为十六进制值. 因为解密后写入文件的数据通常是不可读的 (常被视作乱码), 因此最终的返回值类型没有采用 `'string'`, 而是 `'hex'`.

`options.output` 影响的其实仅仅是解密结果的表现形式, 这些形式之前通常可以互相转换. 真正影响解密结果的, 是解密过程.

#### 解密#

解密过程受 `key` 和 `transformation` 两个参数的影响. 详见 crypto.Key 及 CryptoCipherTransformation 小节.

为保证最大兼容性, `key` 参数同时支持原生的 `java.security.Key` 类型, 此时 `key` 将自动按照 `new crypto.Key(key.encoded)` 进行转换.

#### 示例#

AES 算法解密示例:

    let ec = {
        bytes: [
            -20, 97, -47, 124, 88, 10, 85, -42, -128, -117, 11, 98, -33, -85, 106, 13,
        ],
        base64: '7GHRfFgKVdaAiwti36tqDQ==',
        hex: 'ec61d17c580a55d6808b0b62dfab6a0d',
    };

    /* 创建密钥实例. */
    /* 密钥长度需为 [ 16, 24, 32 ] 之一. */
    let key = new crypto.Key('a'.repeat(16));

    /* 解密数据, 输入为字节数组形式, 输出字符串形式. */
    console.log(crypto.decrypt(ec.bytes, key, 'AES', { input: 'bytes', output: 'string' })); // hello

    /* 解密数据, 输入为 Base64 形式, 输出字符串形式. */
    console.log(crypto.decrypt(ec.base64, key, 'AES', { input: 'base64', output: 'string' })); // hello

    /* 解密数据, 输入为十六进制值形式, 输出字符串形式. */
    console.log(crypto.decrypt(ec.hex, key, 'AES', { input: 'hex', output: 'string' })); // hello

RSA 算法解密示例:

参阅下文 `可逆性` 小节.

#### 可逆性#

加密与解密具有可逆性:

    let message = 'hello';

    /* 生成 RSA 密钥对. */
    let key = crypto.generateKeyPair('RSA', 2048);

    /* 使用公钥加密, 转换名称为 RSA/ECB/OAEPwithSHA-224andMGF1Padding. */
    let ec = crypto.encrypt(message, key.publicKey, 'RSA/ECB/OAEPwithSHA-224andMGF1Padding');

    /* 使用私钥解密, 转换名称同样为 RSA/ECB/PKCS1Padding. */
    let dc = crypto.decrypt(ec, key.privateKey, 'RSA/ECB/OAEPwithSHA-224andMGF1Padding', { output: 'string' });

    /* 解密还原为 message 变量的值, 即 'hello'. */
    console.log(dc); // hello
    console.log(dc === message); // true

## [m] generateKeyPair#

### generateKeyPair(algorithm, length?)#

`6.3.2` `Overload [1-2]/2`

- algorithm { CryptoKeyPairGeneratorAlgorithm } - 密钥对生成器算法
- [ length = `256` ] { number } - 密钥长度
- returns { CryptoKeyPair } - 密钥对

生成指定算法及长度的随机密钥对.

> 注: 不同算法对密钥长度有不同的要求.

如需生成固定密钥的密钥对, 可使用 crypto.KeyPair 直接构造.

密钥对通常会参与非对称加密算法的加解密过程:

    let message = 'hello';

    /* 生成 RSA 密钥对. */
    let key = crypto.generateKeyPair('RSA', 2048);

    /* 使用公钥加密, 转换名称为 RSA/ECB/OAEPwithSHA-224andMGF1Padding. */
    let ec = crypto.encrypt(message, key.publicKey, 'RSA/ECB/OAEPwithSHA-224andMGF1Padding');

    /* 使用私钥解密, 转换名称同样为 RSA/ECB/PKCS1Padding. */
    let dc = crypto.decrypt(ec, key.privateKey, 'RSA/ECB/OAEPwithSHA-224andMGF1Padding', { output: 'string' });

    /* 解密还原为 message 变量的值, 即 'hello'. */
    console.log(dc); // hello
    console.log(dc === message); // true

但需额外注意, 某些算法生成的密钥对, 仅仅用作密钥交换, 密钥交换后将确定一个对称密钥, 最终使用一个对称密钥算法 (如 DES) 进行加解密. 因此密钥对不一定参与非对称加解密过程.

上述情况典型的样例, 当属 Diffie-Hellman（迪菲-赫尔曼) 算法:

    let message = 'hello';

    /* 生成 Diffie-Hellman（迪菲-赫尔曼) 密钥对. */
    let keyPair = crypto.generateKeyPair('DiffieHellman');

    /* 借助 Diffie-Hellman 密钥对, 使用 DES 算法生成一个确定的密钥. */
    let key = keyPair.toKeySpec('DES');

    /* 使用上述密钥加密, 转换名称 (此处可视为算法名称) 也为 DES. */
    /* 除 DES 外, 凡是对称加密算法均被支持, 如 AES. */
    /* 但需保证与上述 toKeySpec 方法传入的参数一致. */
    let ec = crypto.encrypt(message, key, 'DES');

    /* 使用上述同一个密钥解密, 转换名称同样为 DES (与加密算法一致). */
    let dc = crypto.decrypt(ec, key, 'DES', { output: 'string' });

    /* 解密还原为 message 变量的值, 即 'hello'. */
    console.log(dc); // hello
    console.log(dc === message); // true

上述示例再次验证, 密钥对不一定会参与加解密过程. 例如 `DiffieHellman` 密钥对, 仅仅用于确定一个对称密钥.

## [C] Key#

### [c] (data, options?)#

`6.3.2` `Overload [1-2]/2`

- data { string | JsByteArray | ByteArray } - 用于生成密钥的数据
- [ options ] { CryptoCipherOptions & {
  - keyPair?: `'public'` | `'private'` - 指定密钥的公私性质, 用于非对称加密
- }} - 选项参数
- returns { CryptoKey }

生成一个自定义密钥.

关于自定义密钥的实例方法属性及相关示例, 参阅 CryptoKey 类型章节.

使用 `crypto.Key` 构造函数可以很方便地复制密钥数据:

    let key = new crypto.Key('string with length of thrity two');

    console.log(key);

    let copiedKeyA = new crypto.Key(base64.encode(key.data), { input: "base64" });

    console.log(copiedKeyA);

    let copiedKeyB = new crypto.Key(key.data, { input: "bytes" });

    console.log(copiedKeyB);

    let copiedKeyC = new crypto.Key(Crypto.toHex(key.data), { input: "hex" });

    console.log(copiedKeyC);

## [C] KeyPair#

### [c] (publicKeyData, privateKeyData, options?)#

`6.3.2` `Overload [1-2]/2`

- publicKeyData { string | JsByteArray | ByteArray } - 用于生成公钥的数据
- privateKeyData { string | JsByteArray | ByteArray } - 用于生成私钥的数据
- [ options ] { CryptoCipherOptions } - 选项参数
- returns { CryptoKeyPair }

生成一个固定密钥对.

如需生成一个随机密钥对, 可使用 crypto.generateKeyPair.

关于密钥对的实例方法属性及相关示例, 参阅 CryptoKeyPair 类型章节.

使用 `crypto.KeyPair` 构造函数可以很方便地复制密钥对数据:

    let keyPair = crypto.generateKeyPair('RSA');

    console.log(keyPair);

    let copiedKeyPairA = new crypto.KeyPair(
        base64.encode(keyPair.publicKey.data),
        base64.encode(keyPair.privateKey.data),
        { input: 'base64' },
    );

    console.log(copiedKeyPairA);

    let copiedKeyPairB = new crypto.KeyPair(
        keyPair.publicKey.data,
        keyPair.privateKey.data,
        { input: 'bytes' },
    );

    console.log(copiedKeyPairB);

    let copiedKeyPairC = new crypto.KeyPair(
        Crypto.toHex(keyPair.publicKey.data),
        Crypto.toHex(keyPair.privateKey.data),
        { input: "hex" },
    );

    console.log(copiedKeyPairC);