文档管理中心
我的
您当前正在浏览新版开发者文档中心,目录分类和层级有所调整。点击左侧当前文档分类名称前的“☰”图标,可切换文档分类。 了解新版目录
问题处理与运维开发与测试问题系统开发安全加解密算法(Crypto Architecture)如何解决对文件进行SM3摘要计算结果错误的问题

如何解决对文件进行SM3摘要计算结果错误的问题

问题现象

对目标文件进行SM3摘要计算,计算结果错误。这是什么原因导致,该如何解决?

示例代码:

收起
自动换行
深色代码主题
复制
  1. import { cryptoFramework } from '@kit.CryptoArchitectureKit';
  2. import { ReadOptions } from '@kit.CoreFileKit';
  3. import { buffer } from '@kit.ArkTS';
  4. import { fileIo as fs } from '@kit.CoreFileKit';
  5. /**
  6. * 计算文件SM3
  7. * @param filePath 文件路径
  8. * @returns string 摘要数据
  9. */
  10. function fileSM3(filePath: string): string {
  11. if (!fs.accessSync(filePath)) {
  12. // 如果文件不存在,则返回空字符
  13. return ''
  14. }
  15. // 定义摘要类型
  16. let md = cryptoFramework.createMd('SM3')
  17. // 打开文件
  18. let file = fs.openSync(filePath, fs.OpenMode.READ_ONLY)
  19. let fileBufferSize = 4096
  20. let readSize = 0
  21. let fileBuffer = new ArrayBuffer(fileBufferSize)
  22. let readOptions: ReadOptions = {
  23. offset: readSize,
  24. length: fileBufferSize
  25. }
  26. let readLength = fs.readSync(file.fd, fileBuffer, readOptions)
  27. while (readLength > 0) {
  28. // 更新摘要数据
  29. md.updateSync({
  30. data: new Uint8Array(fileBuffer)
  31. });
  32. readSize += readLength
  33. readOptions.offset = readSize;
  34. readLength = fs.readSync(file.fd, fileBuffer, readOptions);
  35. }
  36. // 计算摘要数据
  37. let mdResult = md.digestSync()
  38. return buffer.from(mdResult.data).toString('hex')
  39. }

待摘要文件名为demoTest.docx,文件内容为demoTest,此为测试文件。

预期SM3摘要数据结果:

收起
自动换行
深色代码主题
复制
  1. bdf367286a0458f6a6b3698cb2bfa0cf27ed139a6c9ba08addf3c009177e81ce

实际SM3摘要数据结果:

收起
自动换行
深色代码主题
复制
  1. b7e3771fdae3feb2c70d47edd2093cbe533382109ab223d41678d0eb525f731d

背景知识

消息摘要算法是一种能将任意长度的输入消息,通过特定运算生成固定长度摘要的算法。消息摘要算法也被称为哈希算法或单向散列算法。在摘要算法相同时,生成的摘要值主要有下列特点:

  • 当输入消息相同时,生成摘要序列相同。
  • 当输入消息的长度不一致时,生成摘要序列长度固定(摘要长度由算法决定)。

@ohos.file.fs模块为基础文件操作API,提供基础文件操作能力,包括文件基本管理、文件目录管理、文件信息统计、文件流式读写等常用功能。

问题定位

  1. 文件摘要数据成功计算,结果与预期不一致,表明中间数据有问题,首先检查文件数据是否成功读取完毕。
  2. SM3摘要数据为分段导入,检查摘要数据是否全部导入成功。
  3. SM3分段导入数据长度为fileBufferSize参数固定设置,检查是否额外导入fileBuffer多余数据。

分析结论

SM3分段导入数据fileBuffer长度为fileBufferSize参数固定设置,但因为读取的文件数据长度不是fileBufferSize参数的整数倍,最后一段应该导入的摘要数据长度不等于fileBufferSize参数,所以最后一段摘要数据导入错误,从而导致最终摘要结果与预期不符。

修改建议

动态传入每次需要进行摘要的数据长度,长度值readLength为每一段所导入文件的长度数据,这样可以避免传入未被写入fileBuffer中的数据。

对fileBuffer数据进行传入数据readLength长度的截取,最终运行结果符合预期,代码示例如下:

收起
自动换行
深色代码主题
复制
  1. import { cryptoFramework } from '@kit.CryptoArchitectureKit';
  2. import { ReadOptions } from '@kit.CoreFileKit';
  3. import { buffer } from '@kit.ArkTS';
  4. import { fileIo as fs } from '@kit.CoreFileKit';
  5. import { common } from '@kit.AbilityKit';
  6. /**
  7. * 计算文件SM3
  8. * @param filePath 文件路径
  9. * @returns string 摘要数据
  10. */
  11. function fileSM3(filePath: string): string {
  12. if (!fs.accessSync(filePath)) {
  13. // 如果文件不存在,则返回空字符
  14. return '';
  15. }
  16. // 定义摘要类型
  17. let md = cryptoFramework.createMd('SM3');
  18. // 打开文件
  19. let file = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
  20. let fileBufferSize = 4096;
  21. let readSize = 0;
  22. let fileBuffer = new ArrayBuffer(fileBufferSize);
  23. let readOptions: ReadOptions = {
  24. offset: readSize,
  25. length: fileBufferSize
  26. };
  27. let readLength = fs.readSync(file.fd, fileBuffer, readOptions);
  28. while (readLength > 0) {
  29. // 更新摘要数据
  30. md.updateSync({
  31. data: new Uint8Array(fileBuffer.slice(0, readLength))
  32. });
  33. readSize += readLength;
  34. readOptions.offset = readSize;
  35. readLength = fs.readSync(file.fd, fileBuffer, readOptions);
  36. }
  37. // 计算摘要数据
  38. let mdResult = md.digestSync();
  39. return buffer.from(mdResult.data).toString('hex');
  40. }

附加内容:

可以在rawfile同级目录下创建resfile目录,将测试文件放入resfile目录下,通过上下文获取文件目录,示例代码如下:

收起
自动换行
深色代码主题
复制
  1. // 获取上下文
  2. let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
  3. let resourceDir = context.resourceDir;
  4. /*demoTest.docx是resources/resfile中文件的名称
  5. 此时resourceDirTest就能拿到resfile文件夹文件的路径
  6. 沙箱路径日志为/data/storage/el1/bundle/entry/resources/resfile/demoTest.docx*/
  7. let resourceDirTest = resourceDir + '/demoTest.docx';
  8. console.info(fileSM3(resourceDirTest));

完整示例代码如下:

收起
自动换行
深色代码主题
复制
  1. import { cryptoFramework } from '@kit.CryptoArchitectureKit';
  2. import { ReadOptions } from '@kit.CoreFileKit';
  3. import { buffer } from '@kit.ArkTS';
  4. import { fileIo as fs } from '@kit.CoreFileKit';
  5. import { common } from '@kit.AbilityKit';
  6. /**
  7. * 计算文件SM3
  8. * @param filePath 文件路径
  9. * @returns string 摘要数据
  10. */
  11. function fileSM3(filePath: string): string {
  12. if (!fs.accessSync(filePath)) {
  13. // 如果文件不存在,则返回空字符
  14. return '';
  15. }
  16. // 定义摘要类型
  17. let md = cryptoFramework.createMd('SM3');
  18. // 打开文件
  19. let file = fs.openSync(filePath, fs.OpenMode.READ_ONLY);
  20. let fileBufferSize = 4096;
  21. let readSize = 0;
  22. let fileBuffer = new ArrayBuffer(fileBufferSize);
  23. let readOptions: ReadOptions = {
  24. offset: readSize,
  25. length: fileBufferSize
  26. };
  27. let readLength = fs.readSync(file.fd, fileBuffer, readOptions);
  28. while (readLength > 0) {
  29. // 更新摘要数据
  30. md.updateSync({
  31. data: new Uint8Array(fileBuffer.slice(0, readLength))
  32. });
  33. readSize += readLength;
  34. readOptions.offset = readSize;
  35. readLength = fs.readSync(file.fd, fileBuffer, readOptions);
  36. }
  37. // 计算摘要数据
  38. let mdResult = md.digestSync();
  39. return buffer.from(mdResult.data).toString('hex');
  40. }
  41. @Entry
  42. @Component
  43. struct Index {
  44. build() {
  45. RelativeContainer() {
  46. Button('SM3')
  47. .fontSize($r('app.float.page_text_font_size'))
  48. .fontWeight(FontWeight.Bold)
  49. .alignRules({
  50. center: { anchor: '__container__', align: VerticalAlign.Center },
  51. middle: { anchor: '__container__', align: HorizontalAlign.Center }
  52. })
  53. .onClick(() => {
  54. // 获取上下文
  55. let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
  56. let resourceDir = context.resourceDir;
  57. /*demoTest.docx是resources/resfile中文件的名称
  58. 此时resourceDirTest就能拿到resfile文件夹文件的路径
  59. 沙箱路径日志为/data/storage/el1/bundle/entry/resources/resfile/demoTest.docx*/
  60. let resourceDirTest = resourceDir + '/demoTest.docx';
  61. console.info(fileSM3(resourceDirTest));
  62. });
  63. }
  64. .height('100%')
  65. .width('100%');
  66. }
  67. }

常见FAQ

Q:cryptoFramework.createMd使用SHA512得到结果与服务端不一致。

A:使用上述代码,SM3改为SHA512,得到的结果与服务端一致。

总结

在进行数据处理时,除却注意数据格式的不同,还要注意数据长度是否对结果有影响。