<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

HarmonyOS開發案例:【字符串加解密】

jf_46214456 ? 來源:jf_46214456 ? 作者:jf_46214456 ? 2024-04-21 21:36 ? 次閱讀

介紹

本Codelab針對用戶隱私安全,使用加密算法API對密碼進行加密存儲,模擬開發一個用戶注冊登錄應用。實現如下功能:

  1. 實現登錄、注冊、登錄成功頁面。
  2. 注冊的用戶數據保存到關系型數據庫。
  3. 登錄時通過查詢數據庫校驗用戶是否存在、密碼是否正確。
  4. 密碼通過加密算法保存和使用。

相關概念

  • [加解密算法庫框架]:為屏蔽底層硬件和算法庫,向上提供統一的密碼算法庫加解密相關接口。
  • [關系型數據庫]:關系型數據庫(Relational Database,RDB)是一種基于關系模型來管理數據的數據庫。

環境搭建

軟件要求

  • [DevEco Studio]版本:DevEco Studio 3.1 Release。
  • OpenHarmony SDK版本:API version 9。

硬件要求

  • 開發板類型:[潤和RK3568開發板]。
  • OpenHarmony系統:3.2 Release。

環境搭建

完成本篇Codelab我們首先要完成開發環境的搭建,本示例以RK3568開發板為例,參照以下步驟進行:

  1. [獲取OpenHarmony系統版本]:標準系統解決方案(二進制)。以3.2 Release版本為例:
  2. 搭建燒錄環境。
    1. [完成DevEco Device Tool的安裝]
    2. [完成RK3568開發板的燒錄]
  3. 搭建開發環境。
    1. 開始前請參考[工具準備],完成DevEco Studio的安裝和開發環境配置。
    2. 開發環境配置完成后,請參考[使用工程向導]創建工程(模板選擇“Empty Ability”)。
    3. 工程創建完成后,選擇使用[真機進行調測]。
    4. 鴻蒙開發指導文檔:gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md點擊或者復制轉到。

代碼結構解讀

HarmonyOS與OpenHarmony鴻蒙文檔籽料:mau123789是v直接拿

搜狗高速瀏覽器截圖20240326151547.png

本篇Codelab只對核心代碼進行講解,對于完整代碼,我們會在gitee中提供。

├──entry/src/main/ets                   // 代碼區
│  ├──common
│  │  ├──constants
│  │  │  └──CommonConstants.ets         // 公共常量類
│  │  └──utils
│  │     ├──AesUtil.ets                 // 加解密工具類
│  │     ├──DataTransformUtil.ets       // 數據轉換工具類
│  │     ├──Logger.ets                  // 日志打印工具類
│  │     └──PromptUtil.ts               // 彈窗工具類
│  ├──entryability
│  │  └──EntryAbility.ts                // 程序入口類
│  ├──model
│  │  ├──RdbModel.ets                   // 數據庫業務處理文件
│  │  └──UserTableApi.ets               // 用戶表具體業務文件
│  ├──pages
│  │  ├──Login.ets                      // 登錄頁
│  │  ├──Register.ets                   // 注冊頁
│  │  └──Welcome.ets                    // 歡迎頁
│  └──viewmodel
│     └──User.ets                       // 用戶實體類
└──entry/src/main/resources             // 資源文件目錄

關系型數據庫

首先編寫創建表的SQL語句,其中user為表名、id為主鍵并自動遞增、username為用戶名、password為加密后的密碼、authTag為加解密認證信息。

// CommonConstants.ets
  /**
   * 創建表的SQL語句
   */
  static readonly CREATE_TABLE_SQL: string = 'CREATE TABLE IF NOT EXISTS user(' +
    'id INTEGER PRIMARY KEY AUTOINCREMENT, ' +
    'username TEXT NOT NULL, ' +
    'password TEXT NOT NULL, ' +
    'authTag TEXT NOT NULL)';

在RdbModel的構造方法中,調用getRdbStore方法創建數據庫。其中STORE_CONFIG為數據庫相關配置,sqlCreateTable為創建user用戶表所需的SQL語句。

// RdbModel.ets
import dataRdb from '@ohos.data.relationalStore';
...
export class RdbModel {
  private rdbStore: dataRdb.RdbStore | null = null;
  private tableName: string = '';
  private sqlCreateTable: string = '';
  ...
  constructor(tableName: string, sqlCreateTable: string, columns: Array< string >) {
    this.tableName = tableName;
    this.sqlCreateTable = sqlCreateTable;
    this.columns = columns;
    this.getRdbStore();
  }

  /**
   * 獲取數據庫操作對象rdbStore.
   */
  getRdbStore() {
    let getPromiseRdb = dataRdb.getRdbStore(getContext(), { name: CommonConstants.DATABASE_NAME, securityLevel: dataRdb.SecurityLevel.S1 });
    getPromiseRdb.then(rdbStore = > {
      this.rdbStore = rdbStore;
      this.rdbStore.executeSql(this.sqlCreateTable);
    }).catch((err: Error) = > {
      Logger.error(`getRdbStore err ${JSON.stringify(err)}`);
    });
  }

創建UserTableApi.ets文件,實例化RdbModel創建userTable對象。并對外提供可操作用戶數據表的API接口,包括插入數據、根據用戶名查詢用戶信息等方法。

// UserTableApi.ets
export class UserTableApi {
  private userTable = new RdbModel(TABLE_NAME, CREATE_USER_TABLE, COLUMNS);

  /**
   * 將數據保存到數據庫中
   *
   * @param user 需要保存的User類型的數據對象
   */
  insertUserData(user: User) {
    this.userTable.insertData(user);
  }

  /**
   * 根據用戶名查詢用戶信息
   *
   * @param username 查詢的用戶名
   * @returns 查詢結果集
   */
  async queryUserByUsername(username: string): Promise< User[] > {
    let resultList: Array< User >;
    // 過濾條件
    let predicates = new dataRdb.RdbPredicates(TABLE_NAME);
    predicates.equalTo('username', username);
    // 將查詢到的結果封裝成User對應的用戶信息
    let ret = await this.userTable.query(predicates);
    resultList = this.getListFromResultSet(ret);
    return resultList;
  }
  ...
}

密碼加解密

創建AesUtil工具類,封裝加解密相關邏輯。首先引入@ohos.security.cryptoFramework包,在構造方法中初始化加解密算法框架所需的環境,包括密鑰規格的選擇、加解密規格的選擇等。本示例采用對稱AES加解密算法,密鑰長度為256位,分組模式為GCM。具體有以下步驟:

  1. 創建對稱密鑰生成器。
  2. 通過密鑰生成器生成對稱密鑰。
  3. 創建加解密生成器。
  4. 通過加解密生成器加密或解密數據。

說明: 對于對稱密鑰、非對稱密鑰、加解密算法模式。

// AesUtil.ets
import cryptoFramework from '@ohos.security.cryptoFramework';
...
class AesUtil {
  private globalCipher: cryptoFramework.Cipher = cryptoFramework.createCipher(CommonConstants.GENERATOR_NAME;
  private globalKey: cryptoFramework.SymKey = null;

  /**
   * 構造函數初始化加解密環境、生成密鑰
   */
  constructor() {
    let symAlgName = CommonConstants.ENCRYPTION_MODE;
    // 創建對稱密鑰生成器
    let symKeyGenerator = cryptoFramework.createSymKeyGenerator(symAlgName);
    // 通過密鑰生成器和keyMaterialBlob生成256位長度的對稱密鑰
    let keyMaterialBlob = this.genKeyMaterialBlob(CommonConstants.KEY_DATA);
    symKeyGenerator.convertKey(keyMaterialBlob, (err, symKey) = > {
      if (err) {
        Logger.error(`Convert symKey failed, ${err.code}, ${err.message}`);
        return;
      }
      this.globalKey = symKey;
      let cipherAlgName = CommonConstants.GENERATOR_NAME;
      try {
        // 生成加解密生成器
        this.globalCipher = cryptoFramework.createCipher(cipherAlgName);
      } catch (error) {
        Logger.error(`createCipher failed, error is ${JSON.stringify(err)}`);
      }
    });
  }

  // 加密
  async encrypt(content: string, authTag: string): Promise< string > {
    ...
  }

  // 解密 
  async decrypt(content: string, authTag: string): Promise< string > {
    ...
  }
}

密碼加密

由于加密算法采用GCM分組模式,在加密前需要獲取GCM模式加解密所需的參數GcmParamsSpec。依次生成長度為12字節、8字節、16字節的DataBlob類型的數據,并封裝成GcmParamsSpec對象。

// AesUtil.ets
class AesUtil {
  ...
  /**
   * 獲取GCM分組加解密所需的參數
   *
   * @returns 返回加密所需參數的promise實例
   */
  async genGcmParamsSpec(): Promise< cryptoFramework.GcmParamsSpec > {
    let ivBlob: cryptoFramework.DataBlob = this.genKeyMaterialBlob(CommonConstants.GCM_IV_DATA);
    let aadBlob: cryptoFramework.DataBlob = this.genKeyMaterialBlob(CommonConstants.GCM_AAD_DATA);
    let tagBlob: cryptoFramework.DataBlob = this.genKeyMaterialBlob(CommonConstants.GCM_TAG_DATA);
    let gcmParamsSpec: cryptoFramework.GcmParamsSpec = {
      iv: ivBlob,
      aad: aadBlob,
      authTag: tagBlob,
      algName: `GcmParamsSpec`
    };
    return gcmParamsSpec;
  }

  /**
   * 根據數據組生成DataBlob類型的數據
   *
   * @param data 需要封裝的數據
   * @returns Blob DataBlob類型的數據
   */
  genKeyMaterialBlob(data: Array< number >): cryptoFramework.DataBlob {
    let keyMaterial = new Uint8Array(data);
    return { data: keyMaterial };
  }
}

在AesUtil.ets的encrypt方法中實現密碼加密邏輯。由于本示例加密數據量較小,所以這里直接使用update一步完成加密操作。若數據量較大,可通過update方法分段加密。主要實現以下步驟:

  1. 調用Cipher的init方法初始化加密環境。設置mode為ENCRYPT_MODE,傳入密鑰和生成的gcmParams。
  2. 將用戶輸入的密碼轉換為Uint8Array數組,進而封裝成DataBlob對象。
  3. 將封裝好的plainTextBlob傳入update方法中完成加密。
  4. 調用doFinal方法,傳入null,取出加密后的認證信息authTag。
  5. 取出Uint8Array類型的加密數據和authTag,轉換成Base64類型的字符串,封裝成User類型返回。
// AesUtil.ets
class AesUtil {
  ...
  /**
   * 加密
   *
   * @param content 加密內容
   * @returns 返回攜帶密文User對象的promise實例
   */
  async encrypt(content: string): Promise< User > {
    // 初始化加密環境
    let mode = cryptoFramework.CryptoMode.ENCRYPT_MODE;
    let gcmParams = await this.genGcmParamsSpec();
    await this.globalCipher.init(mode, this.globalKey, gcmParams);
    let plainTextBlob = {
      // 字符串轉Uint8Array
      data: DataTransformUtil.stringToUint8Array(content)
    };
    // 加密
    let updateOutput: cryptoFramework.DataBlob = await this.globalCipher.update(plainTextBlob);
    if (!updateOutput) {
      return Promise.reject('encrypt updateOutput is null');
    }
    let authTag: cryptoFramework.DataBlob = await this.globalCipher.doFinal(null);
    // Uint8Array轉base64
    let encryptContent: string = DataTransformUtil.uint8ArrayToBase64(updateOutput.data);
    let authTagContent: string = DataTransformUtil.uint8ArrayToBase64(authTag.data);
    let user = new User(null, ``, encryptContent, authTagContent);
    return user;
  }
}

密碼解密

解密操作與加密類似,主要實現以下步驟:

  1. 調用Cipher的init方法初始化解密環境,設置mode為DECRYPT_MODE,傳入攜帶authTag認證信息的gcmParams。
  2. 將Base64類型密文轉換為Uint8Array數組,進而封裝成DataBlob對象。
  3. 將封裝好的plainTextBlob傳入doFinal方法中完成解密。
  4. 取出Uint8Array類型的解密數據,轉換成字符串并返回。
// AesUtil.ets
class AesUtil {
  ...

  /**
   * 解密
   *
   * @param content 解密內容
   * @param authTag GCM 解密所需認證信息內容
   * @returns 返回解密內容的promise實例
   */
  async decrypt(content: string, authTag: string): Promise< string > {
    // 初始化解密環境
    let mode = cryptoFramework.CryptoMode.DECRYPT_MODE;
    let gcmParams = await this.genGcmParamsSpec();
    let authTagBlob: cryptoFramework.DataBlob = {
      data: DataTransformUtil.base64ToUint8Array(authTag)
    };
    gcmParams.authTag = authTagBlob;
    await this.globalCipher.init(mode, this.globalKey, gcmParams);
    let plainTextBlob: cryptoFramework.DataBlob = {
      // base64轉Uint8Array
      data: DataTransformUtil.base64ToUint8Array(content)
    };
    // 解密
    let finalOutput: cryptoFramework.DataBlob = await this.globalCipher.doFinal(plainTextBlob);
    if (!finalOutput) {
      return Promise.reject('decrypt finalOutput is null');
    }
    // Uint8Array轉字符串
    let decryptContent = DataTransformUtil.uint8ArrayToString(finalOutput.data);
    return decryptContent;
  }
}

審核編輯 黃宇

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 字符串
    +關注

    關注

    1

    文章

    553

    瀏覽量

    20266
  • 鴻蒙
    +關注

    關注

    55

    文章

    1969

    瀏覽量

    42222
  • HarmonyOS
    +關注

    關注

    79

    文章

    1909

    瀏覽量

    29498
  • OpenHarmony
    +關注

    關注

    24

    文章

    3442

    瀏覽量

    15332
收藏 人收藏

    評論

    相關推薦

    matlab的一般字符串函數和字符串比較命令

    matlab的一般字符串函數和字符串比較命令一般字符串函數 Strings MATLAB中有關字符串函數的說明 Abs 變字符串為數值 Se
    發表于 09-22 15:59

    字符串設置

    請大神幫助 新手尋找了半天!請問怎么設置讓字符串顯示控件的標題能這樣顯示輸出的是一系列的字符串
    發表于 10-18 22:00

    加解密

    stm32F4中如何看加解密的代碼
    發表于 04-08 11:23

    串口讀取的字符串字符串輸入控件輸入的字符串的差別

    發現很多時候串口讀取的字符串字符串輸入控件輸入的字符串雖然看著一樣,但是要進行后續操作的話,卻總是不一樣的。大家有過這種感受吧,知道什么原因 嗎?
    發表于 06-06 11:44

    過濾字符串

    將輸入的字符串過濾,輸出指定的類型,如輸出整數,浮點數,A到Z等字符串。
    發表于 11-09 15:49

    數據轉換、SQL存儲,十六進制字符串、正常字符串

    到了這個方法。方法:將所有亂碼的 ,不亂碼的數據顯示成十六進制,如下圖所示,把正常顯示的字符串更換為16進制顯示,當顯示為16進制字符串時,字符串的顯示字符組成僅為1-F的
    發表于 05-27 10:29

    字符串拆分

    串口連續傳來如是字符串”V-12.98C0.18“希望把字符串拆分為V后的為一組數,C后的為一組數。上述字符串應該拆為-12.98和0.18。我嘗試了用匹配模式匹配中間的空格分成兩個字符串
    發表于 05-22 11:57

    什么是指向字符串指針?

    組成的句子,在C語言中被稱為“字符串”?! ×私馔?b class='flag-5'>字符串是由多個字符組成的之后,想要存儲字符串就變得非常容易了,我們只需要定義多個變量來存儲這些字符
    發表于 03-15 14:19

    十六進制字符串AES是如何分組的?

    文章目錄產生的疑問十六進制字符串AES是如何分組的?AES是目前最流行的分組對稱加密算法,在開發的過程中,無處不在。Intel甚至在他們的芯片中定制了底層指令來讓AES更快地執行,所以AES加解密
    發表于 07-22 08:25

    硬件加解密的分類

    文章目錄1、硬件加解密的分類2、ARM-CE / ARM-NEON3、Soc crypto engion4、cryptoisland5、cryptocell1、硬件加解密的分類在armv8的芯片
    發表于 07-22 07:55

    聊聊字符串

    大家好,我是驚覺,今天聊聊字符串。字符串的使用場景非常之多,人機交互和雙機通信都會用到。比如:通過串口向單片機發送指令,以執行操作或配置參數。單片機讀取傳感器數據,數據格式是字符串。一般GPS數據
    發表于 02-28 06:52

    LabVIEW中HEX顯示的字符串轉換成一般的字符串

    LabVIEW中HEX顯示的字符串轉換成一般的字符串HEX顯示的字符串轉換成一般的字符串。 更詳細的內容,參加如下附件的介紹。 LabVIEW、LabVIEW
    發表于 03-20 20:41

    硬件加解密主要優點及引擎種類

    字訊息所對應到的長度固定字符串(訊息摘要),且若輸入的訊息不同,它們對應到不同字符串的機率很高。 HMAC (Hash-based Message Authentication Code):用來保證
    發表于 08-28 07:29

    STM32加解密技術

    沒有加解密技術是萬萬不能的? 通訊安全? 平臺安全? *例外:STM32 RDP保護知識產權? 加解密技術不是萬能的? 只是工具? 無法代替其它STM32安全技術
    發表于 09-08 08:18

    C語言教程之字符串加密和解密

    C語言教程之字符串加密和解密,很好的C語言資料,快來學習吧。
    發表于 04-25 15:49 ?0次下載
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>