DEV Community

HarmonyOS
HarmonyOS

Posted on

Session-Based Symmetric Key Generation with CryptoKit

Read the original article:Session-Based Symmetric Key Generation with CryptoKit

Problem Description

In secure applications, it is often necessary to generate a session-specific symmetric key that exists only for the duration of a single communication or workflow.
This key must be created securely on-device and used for cryptographic operations such as message authentication or encryption, then discarded after the session.

Background Knowledge

  • CryptoKit (HarmonyOS ArkTS): Provides APIs for generating and managing cryptographic keys such as HMAC or 3DES keys.
  • Symmetric key: The same key is used for both encryption and decryption, or for signing and verification (HMAC).
  • Session key: A temporary symmetric key created for a single session, improving security by limiting key lifetime.

Troubleshooting Process

While implementing a session-based key:

  1. Verified that CryptoKit supports runtime generation of symmetric keys (generateSymKey method).
  2. Checked correct algorithm naming (HMAC for HMAC keys).
  3. Ensured ArkTS async/await syntax is used to avoid type errors and to properly await key generation.
  4. Confirmed that the generated key can be encoded and represented in hex for debugging.

Analysis Conclusion

CryptoKit’s createSymKeyGenerator with the generateSymKey method is sufficient to create a session-specific key entirely within a single .ets file, without external services or multiple modules.

Solution

Below is a single ArkTS .ets file example that:

  • Generates a 256-bit HMAC session key on demand,
  • Computes and verifies an HMAC value for a sample message,
  • Displays the key and verification results on the UI.
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct Index {
  @State sessionKeyHex: string = '';
  @State hmacResultHex: string = '';
  @State hmacVerify: string = '';
  @State isGenerating: boolean = false;
  @State isComputing: boolean = false;

  private currentSessionKey: cryptoFramework.SymKey | null = null;

  private async generateSessionKey(): Promise<cryptoFramework.SymKey> {
    return new Promise((resolve, reject) => {
      try {
        const keyData = new Uint8Array(32);
        for (let i = 0; i < keyData.length; i++) {
          keyData[i] = Math.floor(Math.random() * 256);
        }

        const keyBlob: cryptoFramework.DataBlob = { data: keyData };
        const generator = cryptoFramework.createSymKeyGenerator('HMAC');

        generator.convertKey(keyBlob)
          .then((key: cryptoFramework.SymKey) => {
            resolve(key);
          })
          .catch((err: BusinessError) => {
            reject(new Error(`Key Generation Error: ${err.code} - ${err.message}`));
          });
      } catch (e) {
        let err = e as BusinessError;
        reject(new Error(`Key Generation Setup Error: ${err.code} - ${err.message}`));
      }
    });
  }

  private async handleGenerateKey(): Promise<void> {
    this.isGenerating = true;
    try {
      this.currentSessionKey = await this.generateSessionKey();
      const encoded = this.currentSessionKey.getEncoded();
      this.sessionKeyHex = Array.from(encoded.data)
        .map(b => b.toString(16).padStart(2, '0'))
        .join('');

      this.hmacResultHex = '';
      this.hmacVerify = '';

      console.log('Session key generated successfully');
    } catch (error) {
      console.error('Key generation failed:', error);
      this.sessionKeyHex = `Error: ${error.message}`;
      this.currentSessionKey = null;
    } finally {
      this.isGenerating = false;
    }
  }


  build() {
    Scroll() {
      Column() {
        Column() {
          Button(this.isGenerating ? 'Generating...' : 'Generate Session Key')
            .onClick(() => this.handleGenerateKey())
            .enabled(!this.isGenerating)
            .width('100%')
            .height(48)
            .backgroundColor(this.isGenerating ? '#cccccc' : '#007AFF')
          if (this.sessionKeyHex.length > 0) {
            if (this.sessionKeyHex.startsWith('Error:')) {
              Text(this.sessionKeyHex)
                .fontSize(14)
                .fontColor(Color.Red)
                .margin({ top: 8 })
                .textAlign(TextAlign.Center)
            } else {
              Column({ space: 4 }) {
                Text('Session Key (256-bit HMAC):')
                  .fontSize(16)
                  .fontWeight(FontWeight.Medium)
                  .fontColor('#333333')
                  .padding(12)
                Text(this.sessionKeyHex)
                  .fontSize(12)
                  .fontColor('#666666')
                  .backgroundColor('#f5f5f5')
                  .padding(12)
                  .borderRadius(8)
                  .wordBreak(WordBreak.BREAK_ALL)
                  .width('100%')
                  .textAlign(TextAlign.Start)
              }
              .alignItems(HorizontalAlign.Start)
              .width('100%')
            }
          }
        }
        .padding(16)
        .backgroundColor('#f8f9fa')
        .borderRadius(12)
      }
      .width('100%')
      .padding(20)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#ffffff')
  }
}
Enter fullscreen mode Exit fullscreen mode

Verification Result

  • Running this code on a HarmonyOS device generates a new 256-bit HMAC session key every time the button is pressed.
  • The computed HMAC is displayed in hex format and verification confirms correctness.

Related Documents or Links

Written by Hatice Akyel

Top comments (0)