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字节"数据源 ,组合所有摘要和摘要掩码,✅

结束

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

祝好运!

相关推荐
SameX6 小时前
HarmonyOS Next 安全生态构建与展望
前端·harmonyos
SameX6 小时前
HarmonyOS Next 打造智能家居安全系统实战
harmonyos
Random_index14 小时前
#Uniapp篇:支持纯血鸿蒙&发布&适配&UIUI
uni-app·harmonyos
鸿蒙自习室17 小时前
鸿蒙多线程开发——线程间数据通信对象02
ui·harmonyos·鸿蒙
SuperHeroWu719 小时前
【HarmonyOS】鸿蒙应用接入微博分享
华为·harmonyos·鸿蒙·微博·微博分享·微博sdk集成·sdk集成
zhangjr05751 天前
【HarmonyOS Next】鸿蒙实用装饰器一览(一)
前端·harmonyos·arkts
诗歌难吟4641 天前
初识ArkUI
harmonyos
SameX1 天前
HarmonyOS Next 设备安全特性深度剖析学习
harmonyos
郭梧悠1 天前
HarmonyOS(57) UI性能优化
ui·性能优化·harmonyos
郝晨妤2 天前
鸿蒙原生应用开发元服务 元服务是什么?和App的关系?(保姆级步骤)
android·ios·华为od·华为·华为云·harmonyos·鸿蒙