前言
加密算法是开发中常见的业务,为了安全起见,客户端与服务端传输数据,往往是通过加密之后进行通信,这样做的目的,可以保证数据的机密性,防止敏感数据泄露,而鸿蒙系统中,关于加密算法就比较容易实现,毕竟ArkTs是基于TypeScript实现的,所以有些算法之间基本是通用的。
加密方式有很多,比如对称加密,非对称加密,还有常见的消息摘要算法MD5,SHA等等,本文主要概述常见的几种算法,并简单封装后使用。
消息摘要算法
消息摘要算法,也就是哈希算法或单向散列算法,通过任意长度的消息,运算生成固定长度摘要的算法,鸿蒙中主要是通过cryptoFramework.createMd方法来实现各个算法。
目前支持的算法与规格如下:
摘要算法 | 支持种类 | API版本 |
---|---|---|
HASH | SHA1 | 9+ |
HASH | SHA224 | 9+ |
HASH | SHA256 | 9+ |
HASH | SHA384 | 9+ |
HASH | SHA512 | 9+ |
HASH | MD5 | 9+ |
HASH | SM3 | 10+ |
举一个很简单的SHA256算法,其他规格,只需要更改种类即可。
let md = cryptoFramework.createMd("SHA256")
await md.update({ data: new Uint8Array(buffer.from("加密的数据", 'utf-8').buffer) })
let mdResult = await md.digest()
console.info('===加密后结果:' + mdResult.data)
let sha256Result = this.uint8ArrayToString(mdResult.data)
console.info('===转字符串后结果:' + sha256Result)
let mdLen = md.getMdLength()
console.info("===加密后长度: " + mdLen)
再比如,MD5:
let md = cryptoFramework.createMd("MD5")
await md.update({ data: new Uint8Array(buffer.from("加密的数据", 'utf-8').buffer) })
let mdResult = await md.digest()
console.info('===加密后结果:' + mdResult.data)
let sha256Result = this.uint8ArrayToString(mdResult.data)
console.info('===转字符串后结果:' + sha256Result)
let mdLen = md.getMdLength()
console.info("===加密后长度: " + mdLen)
从以上的代码中可以看出,基本上系统提供的Api已经足够的简洁。
Base64
其实要论严格来说,base64并不是加密算法,只是对应的编码格式,用于在文本协议中表示二进制数据,通过使用Base64编码,可以将二进制数据转换为可打印的ACSII字符,即Base64编码是从二进制到字符的过程,从而保证数据在传输过程中不受损失。
在鸿蒙当中也提供了Base64的编码与解密,使用起来也是非常的简单。
// Base64 编码
public base64Encode(str: string): string {
let blob: cryptoFramework.DataBlob = { data: new Uint8Array(buffer.from(str, 'utf-8').buffer) }
let base64Helper = new util.Base64Helper()
return base64Helper.encodeToStringSync(blob.data)
}
// Base64 解密
public base64Decode(input: string): string {
let base64Helper = new util.Base64Helper()
let uint8Array = base64Helper.decodeSync(input)
return buffer.from(uint8Array).toString("utf-8")
}
AES对称加密
对称加密有一个显著的特点,那就是加密方和解密方使用的是同一个密钥,这种方式加解密的速度比较快,适合数据比较长时使用,但是密钥传输的过程不安全,且容易被破解,密钥管理也比较麻烦,常见的对称加密有,AES,DES,一般最常用的就是AES。
对称加密AES提供了7种加密模式,分别是ECB、CBC、OFB、CFB、CTR、GCM和CCM,不同的加密模式适用的加解密参数不同。
分组模式 | 密钥长度(bit) | 填充模式 | API版本 |
---|---|---|---|
ECB | [128/192/256] | [NoPadding/PKCS5/PKCS7] | 9+ |
CBC | [128/192/256] | [NoPadding/PKCS5/PKCS7] | 9+ |
CTR | [128/192/256] | [NoPadding/PKCS5/PKCS7] | 9+ |
OFB | [128/192/256] | [NoPadding/PKCS5/PKCS7] | 9+ |
CFB | [128/192/256] | [NoPadding/PKCS5/PKCS7] | 9+ |
GCM | [128/192/256] | [NoPadding/PKCS5/PKCS7] | 9+ |
CCM | [128/192/256] | [NoPadding/PKCS5/PKCS7] | 9+ |
简单举例几个模式实际代码执行,无论哪种方式,都是需要秘钥和加解密内容,一般我们进行AES开发,首先要确定秘钥,然后确定加密内容和解密内容。
ECB模式,加解密
// 加密消息
async encryptMessagePromise(symKey: cryptoFramework.SymKey, plainText: cryptoFramework.DataBlob) {
let cipher = cryptoFramework.createCipher('AES128|ECB|PKCS7');
await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, null);
let cipherData = await cipher.doFinal(plainText);
return cipherData;
}
// 解密消息
async decryptMessagePromise(symKey: cryptoFramework.SymKey, cipherText: cryptoFramework.DataBlob) {
let decoder = cryptoFramework.createCipher('AES128|ECB|PKCS7');
await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, null);
let decryptData = await decoder.doFinal(cipherText);
re
GCM模式,加解密
genGcmParamsSpec() {
let ivBlob = this.generateRandom(12);
let arr = [1, 2, 3, 4, 5, 6, 7, 8]; // 8 bytes
let dataAad = new Uint8Array(arr);
let aadBlob: cryptoFramework.DataBlob = { data: dataAad };
arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 16 bytes
let dataTag = new Uint8Array(arr);
let tagBlob: cryptoFramework.DataBlob = {
data: dataTag
};
// GCM的authTag在加密时从doFinal结果中获取,在解密时填入init函数的params参数中
let gcmParamsSpec: cryptoFramework.GcmParamsSpec = {
iv: ivBlob,
aad: aadBlob,
authTag: tagBlob,
algName: "GcmParamsSpec"
};
return gcmParamsSpec;
}
gcmParams = this.genGcmParamsSpec();
// 加密消息
async encryptMessagePromise(symKey: cryptoFramework.SymKey, plainText: cryptoFramework.DataBlob) {
let cipher = cryptoFramework.createCipher('AES128|GCM|PKCS7');
await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, this.gcmParams);
let encryptUpdate = await cipher.update(plainText);
// gcm模式加密doFinal时传入空,获得tag数据,并更新至gcmParams对象中。
this.gcmParams.authTag = await cipher.doFinal(null);
return encryptUpdate;
}
// 解密消息
async decryptMessagePromise(symKey: cryptoFramework.SymKey, cipherText: cryptoFramework.DataBlob) {
let decoder = cryptoFramework.createCipher('AES128|GCM|PKCS7');
await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, this.gcmParams);
let decryptUpdate = await decoder.update(cipherText);
// gcm模式解密doFinal时传入空,验证init时传入的tag数据,如果验证失败会抛出异常。
let decryptData = await decoder.doFinal(null);
if (decryptData == null) {
console.info('GCM decrypt success, decryptData is null');
}
return decryptUpdate;
}
CCM模式,加解密
genCcmParamsSpec() {
let rand: cryptoFramework.Random = cryptoFramework.createRandom();
let ivBlob: cryptoFramework.DataBlob = rand.generateRandomSync(7);
let aadBlob: cryptoFramework.DataBlob = rand.generateRandomSync(8);
let arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 12 bytes
let dataTag = new Uint8Array(arr);
let tagBlob: cryptoFramework.DataBlob = {
data: dataTag
};
// CCM的authTag在加密时从doFinal结果中获取,在解密时填入init函数的params参数中
let ccmParamsSpec: cryptoFramework.CcmParamsSpec = {
iv: ivBlob,
aad: aadBlob,
authTag: tagBlob,
algName: "CcmParamsSpec"
};
return ccmParamsSpec;
}
ccmParams = this.genCcmParamsSpec();
// 加密消息
async encryptMessagePromise(symKey: cryptoFramework.SymKey, plainText: cryptoFramework.DataBlob) {
let cipher = cryptoFramework.createCipher('AES128|CCM');
await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, this.ccmParams);
let encryptUpdate = await cipher.update(plainText);
// ccm模式加密doFinal时传入空,获得tag数据,并更新至ccmParams对象中。
this.ccmParams.authTag = await cipher.doFinal(null);
return encryptUpdate;
}
// 解密消息
async decryptMessagePromise(symKey: cryptoFramework.SymKey, cipherText: cryptoFramework.DataBlob) {
let decoder = cryptoFramework.createCipher('AES128|CCM');
await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, this.ccmParams);
let decryptUpdate = await decoder.doFinal(cipherText);
return decryptUpdate;
}
秘钥,在实际的开发中,我们可以根据提供的字符串或者其他特定字符生成我们的秘钥,一般为Base64编码之后的字符串形式秘钥,在加解密的时候,再进行解码即可。
RSA非对称加密
非对称加密和对称加密不同之处就在于,它有两个秘钥,一个公钥,一个私钥,公钥用于加密,私钥用于解密,相对于对称加密而言,安全系数上上了一层台阶,但是也有中间人攻击的风险,常见的非对称加密有,RSA,DSA,ECC等,一般最常用的就是RSA。
RSA也提供了多种加密方式,比如PKCS1模式,PKCS1\_OAEP模式,以及NoPadding。
PKCS1模式
// 加密消息
async encryptMessagePromise(publicKey: cryptoFramework.PubKey, plainText: cryptoFramework.DataBlob) {
let cipher = cryptoFramework.createCipher('RSA1024|PKCS1');
await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, publicKey, null);
let encryptData = await cipher.doFinal(plainText);
return encryptData;
}
// 解密消息
async decryptMessagePromise(privateKey: cryptoFramework.PriKey, cipherText: cryptoFramework.DataBlob) {
let decoder = cryptoFramework.createCipher('RSA1024|PKCS1');
await decoder.init(cryptoFramework.CryptoMode.DECRYPT_MODE, privateKey, null);
let decryptData = await decoder.doFinal(cipherText);
return decryptData;
}
其它模式,不再举例了,大家可以去官网查看。
封装使用
大家可以使用以上的方式进行数据的加解密,当然了,也可以自行封装,或者使用他人封装好的,针对以上,我也简单封装了一下,有需要的朋友可以拿来使用。
简单功能列表
快速使用
方式一:在Terminal窗口中,执行如下命令安装三方包,DevEco Studio会自动在工程的oh-package.json5中自动添加三方包依赖。
建议:在使用的模块路径下进行执行命令。
ohpm install @abner/security
方式二:在工程的oh-package.json5中设置三方包依赖,配置示例如下:
"dependencies": { "@abner/security": "^1.0.0"}
代码使用
1、MD5
同步
let encryptContent = md5EncryptSync("加密的数据")
console.log("===加密后:" + encryptContent)
异步
md5Encrypt("加密的数据").then((content) => {
console.log("===加密后:" + content)
})
2、BASE64
编码同步
let encryptContent = base64EncodeSync("我是测试数据")
this.mBase64EncodeString = encryptContent
console.log("===编码后:" + encryptContent)
编码异步
base64Encode("我是测试数据").then((encryptContent) => {
this.mBase64EncodeString = encryptContent
console.log("===编码后:" + encryptContent)
})
解码同步
base64Decode(this.mBase64EncodeString).then((decode) => {
console.log("===解码后:" + decode)
})
3、SHA
sha默认是SHA256,可以根据需要进行修改,在第二个参数传入即可。
同步
let encryptContent = shaEncryptSync("1")
console.log("===加密后:" + encryptContent)
异步
shaEncrypt("1").then((content) => {
console.log("===加密后:" + content)
})
修改algName
let encryptContent = shaEncryptSync("1","SHA256")
console.log("===加密后:" + encryptContent)
4、SM3
同步
let encryptContent = sM3EncryptSync("加密的数据")
console.log("===加密后:" + encryptContent)
异步
sM3Encrypt("加密的数据").then((content) => {
console.log("===加密后:" + content)
})
5、AES
随机生成SymKey密钥【异步】
aesGenerateSymKey().then((symKey) => {
this.mSymKey = symKey
console.log("===密钥:" + symKey)
})
随机生成SymKey密钥【同步】
let symKey = aesGenerateSymKeySync()
this.mSymKey = symKey
console.log("===密钥:" + symKey)
字符串生成密钥【异步】
aesGenerateSymKey("1234").then((symKey) => {
this.mSymKey = symKey
console.log("===密钥:" + symKey)
})
字符串生成密钥【同步】
let symKeyString = aesGenerateSymKeySync("1234")
this.mSymKey = symKeyString
console.log("===密钥:" + symKeyString)
加密数据【异步】【ECB模式】
aesEncryptString("123", this.mSymKey).then((result) => {
this.encryptString = result
console.log("===加密后数据:" + result)
})
解密数据【异步】【ECB模式】
aesDecryptString(this.encryptString, this.mSymKey).then((result) => {
console.log("===解密后数据:" + result)
})
加密数据【同步】【ECB模式】
let result = aesEncryptStringSync("123", this.mSymKey)
this.encryptString = result
console.log("===加密后数据:" + result)
解密数据【同步】【ECB模式】
let decryptString = aesDecryptStringSync(this.encryptString!, this.mSymKey!)
console.log("===加密后数据:" + decryptString)
6、RSA
随机生成KeyPair密钥对 【异步】
rsaGenerateAsyKey().then((keyPair) => {
let pubKey = keyPair.pubKey //公钥
let priKey = keyPair.priKey //私钥
this.priKey = priKey
this.pubKey = pubKey
})
随机生成KeyPair密钥对【同步】
let keyPair = rsaGenerateAsyKeySync()
let pubKey1 = keyPair.pubKey //公钥
let priKey1 = keyPair.priKey //私钥
this.priKey = priKey1
this.pubKey = pubKey1
随机生成字符串密钥对 【异步】
rsaGenerateAsyKeyPem().then((keyPairPem) => {
let pubKey = keyPairPem.pubKey //公钥
let priKey = keyPairPem.priKey //私钥
console.log("===公钥:" + pubKey)
console.log("===私钥:" + priKey)
})
随机生成字符串密钥对【同步】
let keyPairPem = generateAsyKeyPemSync()
let pubKeyPem = keyPairPem.pubKey //公钥
let priKeyPem = keyPairPem.priKey //私钥
console.log("===公钥:" + pubKeyPem)
console.log("===私钥:" + priKeyPem)
随机生成密钥对二进制【异步】
rsaGenerateAsyKeyDataBlob().then((dataBlobArray) => {
let pubKey: cryptoFramework.DataBlob = dataBlobArray[0]
let priKey: cryptoFramework.DataBlob = dataBlobArray[1]
console.log("===公钥二进制:" + JSON.stringify(pubKey))
console.log("===私钥二进制:" + JSON.stringify(priKey))
})
随机生成密钥对二进制【同步】
let dataBlobArray = rsaGenerateAsyKeyDataBlobSync()
let pubKey: cryptoFramework.DataBlob = dataBlobArray[0]
let priKey: cryptoFramework.DataBlob = dataBlobArray[1]
console.log("===公钥二进制:" + JSON.stringify(pubKey))
console.log("===私钥二进制:" + JSON.stringify(priKey))
指定二进制数据生成KeyPair密钥对【异步】
rsaGenKeyPairByData(this.pkData, this.skData).then((keyPair) => {
let pubKey = keyPair.pubKey //公钥
let priKey = keyPair.priKey //私钥
this.priKey = priKey
this.pubKey = pubKey
})
指定二进制数据生成KeyPair密钥对【同步】
let keyPairByDataSync = rsaGenKeyPairByDataSync(this.pkData, this.skData)
let pubKeyPairByData = keyPairByDataSync.pubKey //公钥
let priKeyPairByData = keyPairByDataSync.priKey //私钥
this.priKey = priKeyPairByData
this.pubKey = pubKeyPairByData
二进制数据生成字符串密钥对【异步】
rsaGenKeyPairByDataPem(this.pkData, this.skData).then((keyPairPem) => {
let pubKeyPem = keyPairPem.pubKey //公钥
let priKeyPem = keyPairPem.priKey //私钥
console.log("===公钥:" + pubKeyPem)
console.log("===私钥:" + priKeyPem)
})
二进制数据生成字符串密钥对【同步】
let keyPairByDataPemSync = rsaGenKeyPairByDataPemSync(this.pkData, this.skData)
let pubKeyPairByDataPem = keyPairByDataPemSync.pubKey //公钥
let priKeyPairByDataPem = keyPairByDataPemSync.priKey //私钥
console.log("===公钥:" + pubKeyPairByDataPem)
console.log("===私钥:" + priKeyPairByDataPem)
字符串数据生成KeyPair密钥对【异步】
rsaGenKeyPairString(this.appRsaPublicKey, this.appRsaPrivateKey).then((keyPair) => {
let pubKey = keyPair.pubKey
let priKey = keyPair.priKey
this.priKey = priKey
this.pubKey = pubKey
})
字符串数据生成KeyPair密钥对【同步】
let keyPair2 = rsaGenKeyPairStringSync(this.publicPkcs1Str1024, this.priKeyPkcs1Str1024)
let pubKey3 = keyPair2.pubKey
let priKey3 = keyPair2.priKey
this.priKey = priKey3
this.pubKey = pubKey3
字符串数据生成字符串密钥对【异步】
rsaGenKeyPairStringPem(this.appRsaPublicKey, this.appRsaPrivateKey).then((keyPair) => {
let pubKeyPem = keyPair.pubKey //公钥
let priKeyPem = keyPair.priKey //私钥
console.log("===公钥:" + pubKeyPem)
console.log("===私钥:" + priKeyPem)
})
字符串数据生成字符串密钥对【同步】
let keyPairStringPemSync = rsaGenKeyPairStringPemSync(this.publicPkcs1Str1024, this.priKeyPkcs1Str1024)
let pubKeyPairStringPemSync = keyPairStringPemSync.pubKey //公钥
let priKeyPairStringPemSync = keyPairStringPemSync.priKey //私钥
console.log("===公钥:" + pubKeyPairStringPemSync)
console.log("===私钥:" + priKeyPairStringPemSync)
字符串密钥方式加密【异步】
let message = "我是一段要加密的数据"
console.log("===加密前数据:" + message)
rsaEncryptString(message, this.publicKey).then((data) => {
this.encryptString = data
console.log("===加密后数据:" + data)
}).catch((e: BusinessError) => {
console.log("===加密错误:" + JSON.stringify(e.message))
})
字符串密钥方式解密【异步】
//必须有私钥,还有要解密的数据
rsaDecryptString(this.encryptString, this.privateKey).then((data) => {
console.log("===解密后数据:" + data)
})
字符串密钥方式加密【同步】
let message1 = "我是一段要加密的数据"
console.log("===加密前数据:" + message1)
this.encryptString = rsaEncryptStringSync(message1, this.publicKey)
console.log("===加密后数据:" + this.encryptString)
字符串密钥方式解密【同步】
//必须有私钥,还有要解密的数据
let data = rsaDecryptStringSync(this.encryptString, this.privateKey)
console.log("===解密后数据:" + data)
加密 KeyPair密钥方式【异步】 需要cryptoFramework.PubKey
if (this.pubKey != undefined) {
let message = "我是一段要加密的数据"
console.log("===加密前数据:" + message)
rsaEncryptDataBlob(message, this.pubKey!).then((data) => {
this.encryptDataBlob = data
console.log("===加密后数据:" + JSON.stringify(data))
}).catch((e: BusinessError) => {
console.log("===加密错误:" + JSON.stringify(e.message))
})
}
解密 KeyPair密钥方式【异步】
if (this.priKey != undefined && this.encryptDataBlob != undefined) {
//必须有私钥,还有要解密的数据
rsaDecryptDataBlob(this.encryptDataBlob, this.priKey).then((data) => {
console.log("===解密后数据:" + data)
})
}
加密 KeyPair密钥方式【同步】
if (this.pubKey != undefined) {
let message1 = "我是一段要加密的数据"
console.log("===加密前数据:" + message1)
this.encryptDataBlob = rsaEncryptDataBlobSync(message1, this.pubKey!)
console.log("===加密后数据:" + JSON.stringify(this.encryptDataBlob))
}
解密 KeyPair密钥方式【同步】
if (this.priKey != undefined && this.encryptDataBlob != undefined) {
//必须有私钥,还有要解密的数据
let data = rsaDecryptDataBlobSync(this.encryptDataBlob, this.priKey)
console.log("===解密后数据:" + data)
}
私钥签名 同步
let sign = rsaEncryptPriKeyContentSync("123", this.privateKey)
console.log("=======签名:" + sign)
公钥验签 同步
//this.signData为签名内容
let signResult = rsaDecryptPubKeyContentSync("123",this.signData, this.publicKey)
console.log("=======验签:" + signResult)
私钥签名 异步
rsaEncryptPriKeyContent("123", this.privateKey).then((sign) => {
console.log("=======签名:" + sign)
})
公钥验签 异步
//this.signData为签名内容
rsaDecryptPubKeyContent("123",this.signData, this.publicKey).then((signResult) => {
console.log("=======验签:" + signResult)
})
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。