sm4分段加解密
先来看看文档中怎么介绍SM4:SM4为分组加密算法,分组长度128位,当前算法库提供SM4常用的7种加密模式:ECB、CBC、CTR、OFB、CFB、CFB128和GCM。
SM4算法规格:
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/crypto-sym-encrypt-decrypt-spec#sm4
华为的官方文档只给了SM4的GCM模式的分段加密解密,先把官网给的GCM模式分段加密解密实现下:
首先是加密过程:
cryptoFramework.createSymKeyGenerator("SM4_128");
2.创建加密过程的Cipher实例:Api:createCipher
https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-cryptoframework#cryptoframeworkcreatecipher
,createCipher需要传入一个参数,参数的官网解释为:待生成的Cipher的算法名称(含密钥长度),加密模式,以及填充方式的组合。这么说可能不直观,换个说法:
transformation为:算法名称|加密模式|填充方式。算法名称如:SM4_128,表示国密SM4算法,128位密钥;
加密模式如:ECB,CBC这种;填充方式如:PKCS5,PKCS7。不同的组合应对不同的场景。如下示例常见SM4,128位密钥的GCM,填充模式位PKCS7的cipher实例。
cryptoFramework.createCipher("SM4_128|GCM|PKCS7");
initSM4() {this.sm4Key = this.generateSM4Key();//调用密钥生成函数this.cipher = cryptoFramework.createCipher("SM4_128|GCM|PKCS7");//创建cipher示实例this.gcmParams = {iv: { data: new Uint8Array(12) },aad: { data: new Uint8Array(8) },authTag: { data: new Uint8Array(8) },algName: 'GcmParamsSpec'}this.cipher.initSync(cryptoFramework.CryptoMode.ENCRYPT_MODE, this.sm4Key, this.gcmParams)
https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-cryptoframework#update-1
encryptData(cipher: cryptoFramework.Cipher, data: Uint8Array, gcmParams: cryptoFramework.GcmParamsSpec) {const blockSize = 256;//分段加密的块区大小let outPut = new Uint8Array(0);try {for (let i = 0; i < data.length; i += blockSize) {//分段过程const chunk = data.slice(i, i + blockSize);const updateBlob = cipher.updateSync({data: chunk});outPut = this.concatUint8Arrays(outPut,updateBlob.data);}const finalBlob = cipher.doFinalSync(null);//获取加密后的数据gcmParams.authTag = finalBlob;//作为解密的认证信息hilog.info(0x0000,"ccTest",outPut.toString())}catch (e) {hilog.info(0x0000,"ccTest",JSON.stringify(e))}return outPut;}
decryData(decoder:cryptoFramework.Cipher,encryptedData:Uint8Array){const blockSize = 256;let output = new Uint8Array(0);try {for (let i = 0; i < encryptedData.length; i += blockSize) {const chunk = encryptedData.slice(i, i + blockSize);const updateBlob = decoder.updateSync({ data: chunk });output = this.concatUint8Arrays(output, updateBlob.data);}decoder.doFinalSync(null);}catch (e) {hilog.info(0x0000,"ccTest",JSON.stringify(e))}return output;}
concatUint8Arrays(arr1: Uint8Array, arr2: Uint8Array): Uint8Array {// 创建新数组(长度 = arr1长度 + arr2长度)const result = new Uint8Array(arr1.length + arr2.length);// 手动复制数据for (let i = 0; i < arr1.length; i++) {result[i] = arr1[i];}for (let i = 0; i < arr2.length; i++) {result[arr1.length + i] = arr2[i];}return result;}u8ArrToStr(u8Arr:Uint8Array):string{let str = "";for (let i = 0; i < u8Arr.length; i++) {str += String.fromCharCode(u8Arr[i]);}return str;}
encryptECBData(data: Uint8Array,key:cryptoFramework.SymKey) {const cipher = cryptoFramework.createCipher('SM4_128|ECB|PKCS5');cipher.initSync(cryptoFramework.CryptoMode.ENCRYPT_MODE, key, null)const blockSize = 256;let outPut = new Uint8Array(0);for (let i = 0; i < data.length; i += blockSize) {const chunk = data.subarray(i, i + blockSize);const updateBlob = cipher.updateSync({data: chunk})if(updateBlob){outPut = this.concatUint8Arrays(outPut, updateBlob.data);}}let final = cipher.doFinalSync(null);if(final){outPut = this.concatUint8Arrays(outPut,final.data)};return outPut;}decryECBData(encryptedData: Uint8Array,key:cryptoFramework.SymKey) {const cipher = cryptoFramework.createCipher('SM4_128|ECB|PKCS5');try {cipher.initSync(cryptoFramework.CryptoMode.DECRYPT_MODE, key, null)}catch (e) {hilog.info(0x0000, "ccTest", JSON.stringify(e))}const blockSize = 256;let output = new Uint8Array(0);try {for (let i = 0; i < encryptedData.length; i += blockSize) {const chunk = encryptedData.subarray(i, i + blockSize);const updateBlob = cipher.updateSync({ data: chunk });if(updateBlob){output = this.concatUint8Arrays(output, updateBlob.data);}}let final = cipher.doFinalSync(null);if(final){output = this.concatUint8Arrays(output,final.data)}} catch (e) {hilog.info(0x0000, "ccTest", JSON.stringify(e))}return output;}
这个加解密过程需要严格注意时序,不然会出现401,17360001,正常交替出现。
再看看CTR模式的分段加解密:
encryptCTRData(data: Uint8Array, key: cryptoFramework.SymKey){const cipher = cryptoFramework.createCipher('SM4_128|CTR|NoPadding');cipher.initSync(cryptoFramework.CryptoMode.ENCRYPT_MODE, key, null);const blockSize = 256;let outPut = new Uint8Array(0);try {for (let i = 0; i < data.length; i += blockSize) {const chunk = data.subarray(i, i + blockSize);const updateBlob = cipher.updateSync({data: chunk})if (updateBlob) {outPut = this.concatUint8Arrays(outPut, updateBlob.data);}}let final = cipher.doFinalSync(null);if (final) {outPut = this.concatUint8Arrays(outPut, final.data)};} catch (e) {hilog.info(0x0000, "ccTest", JSON.stringify(e))}return outPut;}decryCTRData(encryptedData: Uint8Array, key: cryptoFramework.SymKey){const cipher = cryptoFramework.createCipher('SM4_128|CTR|NoPadding');try {cipher.initSync(cryptoFramework.CryptoMode.DECRYPT_MODE, key, null)} catch (e) {hilog.info(0x0000, "ccTest", JSON.stringify(e))}const blockSize = 256;let output = new Uint8Array(0);try {for (let i = 0; i < encryptedData.length; i += blockSize) {const chunk = encryptedData.subarray(i, i + blockSize);const updateBlob = cipher.updateSync({ data: chunk });if (updateBlob) {output = this.concatUint8Arrays(output, updateBlob.data);}}let final = cipher.doFinalSync(null);if (final) {output = this.concatUint8Arrays(output, final.data)}} catch (e) {hilog.info(0x0000, "ccTest", JSON.stringify(e))}return output;}
观察GCM,ECB,CTR这三种模式的分段加解密代码,可以发现,SM4的分段加密解密的代码相似度非常高。木有错,这几种模式主要的区别在createCipher的参数上,比如
ECB需要填充PCKS7/PCKS7,GCM,CTR不需要;GCM需要解密时设置authTag用于强制验证。
但是我在用CTR进行测试的时候(就行上述代码示例),发现CTR模式下,不指定iv参数,也是能够正常加解密的,但是CTR在init的时候需要传入iv参数,猜测传入null值时,Api内部做了适配?至于为什么Api允许CTR模式的iv参数可以为null,这需要问一问api设计者了。所以实际上CTR的初始化要这么做:
encryptCTRData(data: Uint8Array, key: cryptoFramework.SymKey,ctrParams:cryptoFramework.IvParamsSpec){const cipher = cryptoFramework.createCipher('SM4_128|CTR|NoPadding');cipher.initSync(cryptoFramework.CryptoMode.ENCRYPT_MODE, key, ctrParams);const blockSize = 256;let outPut = new Uint8Array(0);try {for (let i = 0; i < data.length; i += blockSize) {const chunk = data.subarray(i, i + blockSize);const updateBlob = cipher.updateSync({data: chunk})if (updateBlob) {outPut = this.concatUint8Arrays(outPut, updateBlob.data);}}let final = cipher.doFinalSync(null);if (final) {outPut = this.concatUint8Arrays(outPut, final.data)};} catch (e) {hilog.info(0x0000, "ccTest", JSON.stringify(e))}return outPut;}decryCTRData(encryptedData: Uint8Array, key: cryptoFramework.SymKey,ctrParams:cryptoFramework.IvParamsSpec){const cipher = cryptoFramework.createCipher('SM4_128|CTR|NoPadding');try {cipher.initSync(cryptoFramework.CryptoMode.DECRYPT_MODE, key, ctrParams)} catch (e) {hilog.info(0x0000, "ccTest", JSON.stringify(e))}const blockSize = 256;let output = new Uint8Array(0);try {for (let i = 0; i < encryptedData.length; i += blockSize) {const chunk = encryptedData.subarray(i, i + blockSize);const updateBlob = cipher.updateSync({ data: chunk });if (updateBlob) {output = this.concatUint8Arrays(output, updateBlob.data);}}let final = cipher.doFinalSync(null);if (final) {output = this.concatUint8Arrays(output, final.data)}} catch (e) {hilog.info(0x0000, "ccTest", JSON.stringify(e))}return output;}
其中ctrParams为,这一块内容可以看文档的安全随机数生成:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/crypto-generate-random-number,另外建议官方在SM4文档那块增加说明,CTR在init的时候要传入iv参数
(ps:鸿蒙的开发文档真的是东一块西一块)
let randomKey = cryptoFramework.createRandom();let seed = new Uint8Array([1, 2, 3]);randomKey.setSeed({data:seed});let counter = randomKey.generateRandomSync(16);let ctrParams :cryptoFramework.IvParamsSpec = {iv: counter,algName: 'IvParamsSpec'}
以下是SM4的7种模式的差异(ds给的表,仅供参考)
| 模式 | 加密原理 | 分段处理差异 | IV 要求 | 填充要求 | 错误传播 | 并行性 | 典型场景 |
|---|---|---|---|---|---|---|---|
|
ECB
(电子密码本) |
|
|
|
(如 PKCS7) |
|
|
|
|
CBC
(密码块链) |
|
|
(通常 16 字节) |
|
|
|
|
|
CFB
(密文反馈) |
|
|
|
|
|
|
|
|
OFB
(输出反馈) |
|
|
|
|
|
|
|
|
CTR
(计数器) |
|
|
(Nonce + Counter) |
|
|
|
|
|
CTS
(密文窃取) |
|
|
|
|
|
|
|
|
GCM
(伽罗瓦计数) |
|
|
|
|
|
|
|
版权保护: 本文由 绿茶加糖-郭保升 原创,转载请保留链接: https://www.guobaosheng.com/tuijian/2025/0711/367.html
- 上一篇:sm2加解密和签名验签
- 下一篇:RSA的分段加密与解密
