HarmonyOS 非对称加密-RSA

1. 介绍

接上篇"HarmonyOS 对称加密",本篇将介绍RSA非对称加密,官方也给出了一些样例使用代码,可供开发者参考。

本篇从实践出发,完整的通过代码方式来深入了解HarmonyOS中的非对称加密用法。

2. 基础概念

字符串样例:RSA1024|PKCS1|MD5|MGF1_MD5

  1. 密钥算法 : RSA
  2. 密文组长度 : 1024
  3. 填充模式 : PKCS1
  4. 摘要: MD5
  5. 摘要掩码: MGF1_MD5

关于"RSA算法","填充模式", "摘要", "摘要掩码"的具体概念,可自行搜索。

3. 实践

加密使用的测试数据源有三个,具体见下图

  1. "92字节" (即,占用了92个字节的字符串)
  2. "117字节" (即,占用了117个字节的字符串)
  3. "128字节" (即,占用了128个字节的字符串)

3.1 MMI入口

3.2 RSA加解密

  • RSA加密算法密文分组长度有七种:512,718,1024,2048,3072,4096,8192;
  • 支持3种填充模式:NoPadding, PKCS1, PKCS1_OAEP;
  • 在PCKCS1_OAEP填充模式下,支持六种摘要:MD5, SHA1, SHA224, SHA256, SHA384, SHA512;
  • 在PCKCS1_OAEP填充模式下,支持六种摘要掩码:MGF1_MD5, MGF1_SHA1, MGF1_SHA224, MGF1_SHA256, MGF1_SHA384, MGF1_SHA512

本篇仅以1024密文组长度做为实践验证,默认选择"128字节"数据集

片段代码以 "RSA1024|PKCS1_OAEP|MD5|MGF1_MD5" 为例

3.2.1 加密

生成对动态密钥

ini 复制代码
//导入加密框架
import cryptoFramework from '@ohos.security.cryptoFramework';

......

//创建密钥生成器,参数为(密钥算法+密文组长度+素数,如:RSA1024|PRIMES_2)
let symKeyGenerator = cryptoFramework.createAsyKeyGenerator('RSA1024|PRIMES_2');

//生成对称密钥
let promiseSymKey = symKeyGenerator.generateKeyPair();

//获取密钥
promiseSymKey( key => {
   
 //密钥对
 this.keyPair = rsaKeyPair;

})

初始化Cipher

csharp 复制代码
//创建Cipher
globalCipher = cryptoFramework.createCipher('RSA1024|PKCS1_OAEP|MD5|MGF1_MD5');

//使用公钥初始化Cipher
let mode = cryptoFramework.CryptoMode.ENCRYPT_MODE; 
globalCipher.init(mode, this.keyPair.pubKey, null)

加密

ini 复制代码
//文字转换为Uint8Array
const encoder = new util.TextEncoder()
let u8a_encoder = encoder.encodeInto('测试')

//封装Uint8Array 数据,必须是一个带data属性的对象
let plainText = { data: u8a_encoder };

//开始加密
let promiseUpdate = globalCipher.doFinal(plainText);

//获取加密结果
promiseUpdate( result => {

   //密文
   let this.cipherText = result.data
   
})

至此,加密已经结束。

3.2.2 解密

生成密钥对象

javascript 复制代码
//创建密钥生成器,参数为(密钥算法+密文组长度+素数,如:RSA1024|PRIMES_2)
let symKeyGenerator = cryptoFramework.createSymKeyGenerator('RSA1024|PRIMES_2');

//密钥生成器使用密钥数据,开始生成密钥对象
symKeyGenerator.convertKey(this.keyPair.pubKey.getEncoded(), this.keyPair.priKey.getEncoded()).then( key => {

   //key 为生成的密钥对象
   
})

初始化Cipher

ini 复制代码
//创建Cipher
globalCipher = cryptoFramework.createCipher('RSA1024|PKCS1_OAEP|MD5|MGF1_MD5');

//初始化Cipher,参数keyPair.priKey由第一步生成的
let mode = cryptoFramework.CryptoMode.DECRYPT_MODE;
globalCipher.init(mode, keyPair.priKey
, null)

解密

kotlin 复制代码
//this.cipherText 代表密文,本篇文章中,此值来源于上述加密结果
globalCipher.doFinal({ data: this.cipherText })

3.2.3 注意点

这些是基于RSA1024场景下,实践出的结论

  1. NoPadding填充模式下,明文字节长度必须为128个字节长度的整数倍
  2. PKCS1填充模式下,单次加密(即: 调用一次doFinal)明文字节长度最大为117个字节
  3. PKCS1_OAEP填充模式下,单词加密(即: 调用一次doFinal)明文字节长度最大为92个字节
  4. 摘要和摘要掩码只有在PKCS1_OAEP填充模式下,才会出现在初始化Cipher参数中

本篇仅以1024密文组长度做为实践验证,默认选择"128字节"数据集

片段代码以 "RSA1024|PKCS1_OAEP|MD5|MGF1_MD5" 为例

3.2.4 源码

kotlin 复制代码
import cryptoFramework from '@ohos.security.cryptoFramework';
import util from '@ohos.util';
import Logger from '../../common/Logger';
import OriginData from './OriginData';
import emitter from "@ohos.events.emitter";

/**
 * 非对称密钥
 *    密钥算法:RSA
 *    密钥规格格式:(密钥算法名称 + 密钥长度) | 素数
 *    密钥长度:512,718,1024, 2048, 3072, 4096, 8192
 *    密钥规格列表:AES512|PRIMES_2, RSA768|PRIMES_2, RSA1024|PRIMES_2, RSA1024|PRIMES_3, RSA2048|PRIMES_2
 *               RSA2048|PRIMES_3, RSA3072|PRIMES_2, RSA3072|PRIMES_3, RSA4096|PRIMES_2, RSA4096|PRIMES_3
 *               RSA4096|PRIMES_4, RSA8192|PRIMES_2, RSA8192|PRIMES_3, RSA8192|PRIMES_4, RSA8192|PRIMES_5
 *
 * 非对称对称加密
 *    加密算法:RSA
 *    加密规格格式:密钥算法名称 + 密钥长度 + 填充模式 + [摘要 + 摘要掩码]
 *    密钥长度:512,718,1024, 2048, 3072, 4096, 8192
 *    填充模式:NoPadding,PKCS1,PKCS1_OAEP
 *    摘要: MD5, SHA1, SHA224, SHA256, SHA384, SHA512
 *    摘要掩码: MGF1_MD5, MGF1_SHA1, MGF1_SHA224, MGF1_SHA256, MGF1_SHA384, MGF1_SHA512
 *    加密规格样例:RSA512|NoPadding,RSA512|PKCS1,RSA512|PKCS1_OAEP|MD5|MGF1_SHA224
 *
 *
 */
class TestAsyRSAEncryptDecrypt {

  private  keyPair: cryptoFramework.KeyPair;

  private cipherText: Uint8Array;

  private algorithmWithLength: string = 'RSA1024'
  private paddingMode: string = 'NoPadding' //NoPadding,PKCS5,PKCS1_OAEP
  private digest: string = 'MD5' //MD5、SHA1、SHA224、SHA256、SHA384、SHA512
  private digestMask: string = 'MGF1_MD5' //MGF1_MD5、MGF1_SHA1、MGF1_SHA224、MGF1_SHA256、MGF1_SHA384、MGF1_SHA512

  //对称加密:RSA1024|PKCS1_OAEP|MD5|MGF1_MD5
  testAsyRASEncrypt(paddingMode: string, digest: string, digestMask, dataSource:number) {

    let globalCipher: cryptoFramework.Cipher

    let originData: string

    if(dataSource == 92){
      originData = OriginData.RSA_1024_CONTENT_92
    } else if(dataSource == 117){
      originData = OriginData.RSA_1024_CONTENT_117
    } else {
      originData = OriginData.RSA_1024_CONTENT_128
    }

    this.paddingMode = paddingMode
    this.digest = digest
    this.digestMask = digestMask

    let symKeyGenerator = cryptoFramework.createAsyKeyGenerator(this.algorithmWithLength+'|PRIMES_2');

    let promiseSymKey = symKeyGenerator.generateKeyPair();

    promiseSymKey.then( rsaKeyPair => {

      this.keyPair = rsaKeyPair;

      let p: string = ''

      for(let a of rsaKeyPair.priKey.getEncoded().data){
        p = p.concat(a+',')
      }
      Logger.d('原始私钥',p)

      let spec = ''
      if(this.paddingMode == 'PKCS1_OAEP'){
        spec = this.algorithmWithLength + '|' + this.paddingMode + '|' + this.digest + '|' + this.digestMask
      } else {
        spec = this.algorithmWithLength + '|' + this.paddingMode
      }

      console.log('加密规格:', spec)

      globalCipher = cryptoFramework.createCipher(spec);

      let mode = cryptoFramework.CryptoMode.ENCRYPT_MODE;

      return globalCipher.init(mode, this.keyPair.pubKey, null);

    }).then(() => {

        const encoder = new util.TextEncoder()
        let u8a_encoder = encoder.encodeInto(originData)

        Logger.d('开始加密')

        let plainText = { data: u8a_encoder };
        let promiseUpdate = globalCipher.doFinal(plainText);

        return promiseUpdate;

      }).then(updateOutput => {

        if(updateOutput != null && updateOutput.data != null){
          this.cipherText = updateOutput.data
          console.log('密文:', this.cipherText)
        }

      }).then(() => {

        this.testSymAESDecrypt()
        return

      }).catch( error => {
         console.error(`catch error, ${error.code}, ${error.message}`);
         this.notificationStatus('加密中-error',  error.code + '-' +  error.message)
      })

  }

  //非对称解密: RSA1024|PKCS1_OAEP|MD5|MGF1_MD5
  testSymAESDecrypt() {

    let globalCipher: cryptoFramework.Cipher

    let symKeyGenerator = cryptoFramework.createAsyKeyGenerator(this.algorithmWithLength);

    // 根据指定的二进制密钥数据,生成对称密钥对象
    symKeyGenerator.convertKey(this.keyPair.pubKey.getEncoded(), this.keyPair.priKey.getEncoded())
      .then( keyPair => {
        console.log('解密-初始化Cipher')
        let spec = ''
        if(this.paddingMode == 'PKCS1_OAEP'){
          spec = this.algorithmWithLength + '|' + this.paddingMode + '|' + this.digest + '|' + this.digestMask
        } else {
          spec = this.algorithmWithLength + '|' + this.paddingMode
        }
        globalCipher = cryptoFramework.createCipher(spec);

        let mode = cryptoFramework.CryptoMode.DECRYPT_MODE;

        let p: string = ''

        for(let a of keyPair.priKey.getEncoded().data){
          p = p.concat(a+',')
        }

        Logger.d('解密私钥',p)

        globalCipher.init(mode, keyPair.priKey, null);
        return

      })
      .then(() => {

        console.log('开始解密', '密文')
        return globalCipher.doFinal({ data: this.cipherText });

      })
      .then( updateOutput => {

        let textDecoder = util.TextDecoder.create()
        let key = textDecoder.decodeWithStream(updateOutput.data);

        Logger.d('解密完成1: ', key);
        this.notificationStatus('解密完成', key)

      })
      .catch(error => {
        console.error(`catch error, ${error.code}, ${error.message}`);
        this.notificationStatus('解密中-error',  error.code + '-' +  error.message)
      })

  }

   notificationStatus(status:string, result: string){
     // 定义一个eventId为1的事件,事件优先级为Low
     let event = {
       eventId: 202404063287,
       priority: emitter.EventPriority.HIGH
     };

     let eventData = {
       data: {
         "content": result,
         'status': status,
       }
     };

     // 发送eventId为1的事件,事件内容为eventData
     emitter.emit(event, eventData);
   }
}

export default new TestAsyRSAEncryptDecrypt();

3.4 实践结果

以1024长度分组,"92字节","117字节", "128字节"作为数据源

数据集

csharp 复制代码
//117字节,PKCS1 M最大
public static readonly RSA_1024_CONTENT_117: string =
        "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567"

//92字节,PKCS1_OAEP 最大
public static readonly RSA_1024_CONTENT_92: string =
  "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012"

//128字节,RSA|NoPadding 固定
public static readonly RSA_1024_CONTENT_128: string =
  "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
  + "1234567890123456789012345678"

组合实验结果

  1. NoPadding填充模式下,使用"128字节"数据源, ✅
  2. PKCS1填充模式下,使用"117字节"数据源 ,✅
  3. PKCS1_OAEP填充模式下,使用"92字节"数据源 ,组合所有摘要和摘要掩码,✅

结束

本篇没有演示分段加密和分段解密,原因:无

祝好运!

相关推荐
小脑斧爱吃鱼鱼41 分钟前
鸿蒙项目笔记(1)
笔记·学习·harmonyos
鸿蒙布道师2 小时前
鸿蒙NEXT开发对象工具类(TS)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
zhang1062092 小时前
HarmonyOS 基础组件和基础布局的介绍
harmonyos·基础组件·基础布局
马剑威(威哥爱编程)2 小时前
在HarmonyOS NEXT 开发中,如何指定一个号码,拉起系统拨号页面
华为·harmonyos·arkts
GeniuswongAir3 小时前
Flutter极速接入IM聊天功能并支持鸿蒙
flutter·华为·harmonyos
90后的晨仔7 小时前
鸿蒙ArkUI框架中的状态管理
harmonyos
别说我什么都不会1 天前
OpenHarmony 5.0(API 12)关系型数据库relationalStore 新增本地数据变化监听接口介绍
api·harmonyos
MardaWang1 天前
HarmonyOS 5.0.4(16) 版本正式发布,支持wearable类型的设备!
华为·harmonyos
余多多_zZ1 天前
鸿蒙学习手册(HarmonyOSNext_API16)_应用开发UI设计:Swiper
学习·ui·华为·harmonyos·鸿蒙系统
斯~内克1 天前
鸿蒙网络通信全解析:从网络状态订阅到高效请求实践
网络·php·harmonyos