总述: 对github上的CryptLib(github.com/skavinvarna… )的加密解密逻辑进行ArkTS化改造,以及逻辑验证、使用示例。跨平台支持增加HarmoyOS平台!
背景:公司现有项目中Android、iOS、小程序、服务端在处理用户登录业务时,对用户密码进行了加密处理,使用了github上的CryptLib(github.com/skavinvarna… ),对Android、iOS、Javascript/NodeJS/Web各个端都有支持和示例代码,使用简单,跨平台支持。现在HarmoyOS平台来了,问题也来了!Harmony app怎么处理?
心路历程:首先想到的是使用现有工具的NodeJS代码使用示例进行操作,那就要在鸿蒙项目工程中引入CryptLib的Js依赖(npm install @skavinvarnan/cryptlib --save),但是鸿蒙工程仅支持ohpm引入,也搜索了如何在鸿蒙工程如何使用npm引入三方库,各种操作吧,还是无法使用(网上很多,读者可以自行查找,个人意见,既然鸿蒙只允许ohpm,那就不要走npm了)。鸿蒙官方ohpm也有加密解密的三方库,可能是我们现有的加密解密业务逻辑,或者我使用的不对,总之还是放弃了。无奈之下,只能基于CryptLib中的Android代码改造吧。
从ArkTS API9开始的加密解密算法框架都使用@kit.CryptoArchitectureKit,本文代码中也是基于该框架实现。同时要考虑到现有项目在使用CryptLib加密解密时使用的是AES256|CBC。
仿照CryptLib中的样式,贴出使用示例和逻辑代码
使用示例
import { OHCryptLib } from './OHCryptLib'
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
RelativeContainer() {
Text(this.message)
.id('HelloWorld')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
.onClick(async () => {
testOHCryptLib()
})
}
.height('100%')
.width('100%')
}
}
async function testOHCryptLib() {
let plainText = "this is my plain text"
let key = "1234567890" //your key
let encrypt = await OHCryptLib.encryptPlainTextWithRandomIV(plainText, key)
console.info("test OHCryptLib encrypt: " + encrypt);
let decrypt = await OHCryptLib.decryptCipherTextWithRandomIV(encrypt, key)
console.info("test OHCryptLib decrypt: " + decrypt);
}
运行结果
OHCryptLib源码
import { cryptoFramework } from "@kit.CryptoArchitectureKit";
import { buffer, util } from "@kit.ArkTS";
enum EncryptMode{
ENCRYPT='ENCRYPT',
DECRYPT='DECRYPT'
}
export class OHCryptLib {
//加密
public static async encryptPlainTextWithRandomIV(plainText: string, key: string) {
console.info('OHCryptLib 加密参数 plainText:' + plainText + ',key:' + key);
let randomIV16 = await OHCryptLib.generateRandomIV16()
let initVector = await OHCryptLib.generateRandomIV16()
let encryptionKey = OHCryptLib.SHA256(key, 32)
let encrypt = await OHCryptLib.encryptDecrypt( randomIV16 + plainText,encryptionKey, EncryptMode.ENCRYPT, initVector);
return encrypt
}
//解密
public static async decryptCipherTextWithRandomIV(cipherText: string, key: string) {
console.info('OHCryptLib 解密参数 cipherText:' + cipherText + ',key:' + key);
let initVector = await OHCryptLib.generateRandomIV16()
let encryptionKey = OHCryptLib.SHA256(key, 32)
let decrypt = await OHCryptLib.encryptDecrypt( cipherText,encryptionKey, EncryptMode.DECRYPT, initVector);
return decrypt
}
private static async encryptDecrypt(inputText: string, encryptionKey: string, mode: string, initVector: string){
console.info('OHCryptLib encryptDecrypt inputText:' + inputText+",encryptionKey:"+encryptionKey+",mode:"+mode+",initVector:"+initVector);
let _key = OHCryptLib.stringToUint8Array(encryptionKey)
let symKey = await OHCryptLib.genSymKeyByData(_key);
let ivBlobData = OHCryptLib.stringToUint8Array(initVector)
let ivBlob: cryptoFramework.DataBlob = { data: ivBlobData };
let ivParamsSpec: cryptoFramework.IvParamsSpec = {
algName: "IvParamsSpec",
iv: ivBlob
}
let cipher = cryptoFramework.createCipher('AES256|CBC|PKCS7');
if (mode==EncryptMode.ENCRYPT) {
await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, ivParamsSpec);
let uint8Array = OHCryptLib.stringToUint8Array(inputText);
let dataBlobData: cryptoFramework.DataBlob = { data: uint8Array }
let encryptData = await cipher.doFinal(dataBlobData);
if (encryptData.data.toString()) {
let encrypt = OHCryptLib.Base64().encodeToStringSync(encryptData.data)
console.info('OHCryptLib encryptDecrypt encrypt:' + encrypt);
return encrypt
} else {
console.info('OHCryptLib encryptDecrypt encrypt failed');
return ''
}
}else if (mode==EncryptMode.DECRYPT){
await cipher.init(cryptoFramework.CryptoMode.DECRYPT_MODE, symKey, ivParamsSpec);
let decodedValue = OHCryptLib.Base64().decodeSync(inputText);
let dataBlobData: cryptoFramework.DataBlob = { data: decodedValue }
let decryptData = await cipher.doFinal(dataBlobData);
console.info('OHCryptLib encryptDecrypt decryptData.data:' + decryptData.data);
if (decryptData.data.toString()) {
let decryptDataStr = buffer.from(decryptData.data).toString('utf-8')
let decrypt = ""
if (decryptDataStr.length>16) {
decrypt = decryptDataStr.substring(16,decryptDataStr.length)
}else {
decrypt = decryptDataStr
}
console.info('OHCryptLib encryptDecrypt decrypt:' + decrypt);
return decrypt
} else {
console.info('OHCryptLib encryptDecrypt decrypt failed');
return ''
}
}else{
console.info('OHCryptLib encryptDecrypt mode:'+mode);
return ''
}
}
private static SHA256(key: string,length:number): string {
let md = cryptoFramework.createMd('SHA256');
let firstArray = OHCryptLib.stringToUint8Array(key)
md.updateSync({ data: firstArray });
let mdResult = md.digestSync();
let hex = buffer.from(mdResult.data).toString('hex')
let result: string
if (hex.length > length) {
result = hex.substring(0, length)
} else {
result = hex
}
console.info('OHCryptLib SHA256 result:' + result);
return result
}
private static async genSymKeyByData(symKeyData: Uint8Array) {
let symKeyBlob: cryptoFramework.DataBlob = { data: symKeyData };
let aesGenerator = cryptoFramework.createSymKeyGenerator('AES256');
let symKey = await aesGenerator.convertKey(symKeyBlob);
return symKey;
}
private static stringToUint8Array(str: string) {
return new Uint8Array(buffer.from(str, 'utf-8').buffer)
}
private static async generateRandomIV16() {
let rand = cryptoFramework.createRandom();
let seed = new Uint8Array([0]);
rand.setSeed({ data: seed });
let len = 16;
let randOutput = await rand.generateRandom(len);
let hex = buffer.from(randOutput.data).toString('hex')
let result = ""
if (len < hex.length) {
result = hex.substring(0, len)
} else {
result = hex
}
return result
}
private static Base64() {
return new util.Base64Helper()
}
}
进一步业务逻辑验证
本项目现有的用户密码加密业务逻辑:
1、客户端使用本地密钥A对从服务端获取的appSecret数据进行解密,从而得到结果B(对密码加密的密钥);
2、客户端使用B对用户密码password进行加密得到C,将C作为登录接口参数传递给服务端。
如果登录接口成功返回用户数据,则证明鸿蒙版密码加密逻辑成功适配现有加密逻辑;否则返回“用户不存在或密码错误”,则证明适配失败。
业务逻辑验证代码及接口返回结果截图:
鸿蒙版密码加密逻辑成功获取到用户数据。