import cryptoFramework from '@ohos.security.cryptoFramework' import util from '@ohos.util' const UTF8_ENCODER = new util.TextEncoder('utf-8') const UTF8_DECODER = util.TextDecoder.create('utf-8') const BASE64_HELPER = new util.Base64Helper() const PUBLIC_PEM_FORMATS: string[] = ['X509', 'x509', 'SPKI', 'spki', 'PKCS1', 'pkcs1'] const PRIVATE_PEM_FORMATS: string[] = ['PKCS8', 'pkcs8', 'PKCS1', 'pkcs1'] const RSA_GENERATOR_ALGS: string[] = ['RSA'] const RSA_CIPHER_ALGS: string[] = [ 'RSA/ECB/PKCS1Padding', 'RSA/None/PKCS1Padding', 'RSA/None/OAEPWithSHA256AndMGF1Padding', 'RSA' ] export class NativeRsaBundle { public publicKey: string public privateKey: string constructor(publicKey: string = '', privateKey: string = '') { this.publicKey = publicKey this.privateKey = privateKey } } export class NativeHarmonyRsaCore { static createRsaBundle(keySize: number): NativeRsaBundle { const generator = NativeHarmonyRsaCore.createKeyGenerator(keySize) const keyPair = generator.generateKeyPairSync() const publicKey = NativeHarmonyRsaCore.exportPublicPem(keyPair.pubKey) const privateKey = NativeHarmonyRsaCore.exportPrivatePem(keyPair.priKey) return new NativeRsaBundle(publicKey, privateKey) } static rsaEncrypt(publicKey: string, plainText: string, outputKind: string | null): string { const keyPair = NativeHarmonyRsaCore.importPemKeyPair(publicKey, null) const cipher = NativeHarmonyRsaCore.createCipher() cipher.initSync(cryptoFramework.CryptoMode.ENCRYPT_MODE, keyPair.pubKey, null) const output = cipher.doFinalSync({ data: NativeHarmonyRsaCore.encodeUtf8(plainText) }) return NativeHarmonyRsaCore.encodeCipherOutput(output.data, outputKind) } static rsaDecrypt(privateKey: string, cipherText: string, outputKind: string | null): string { const keyPair = NativeHarmonyRsaCore.importPemKeyPair(null, privateKey) const cipher = NativeHarmonyRsaCore.createCipher() cipher.initSync(cryptoFramework.CryptoMode.DECRYPT_MODE, keyPair.priKey, null) const sourceBytes = NativeHarmonyRsaCore.decodeCipherInput(cipherText) const output = cipher.doFinalSync({ data: sourceBytes }) return NativeHarmonyRsaCore.decodePlainOutput(output.data, outputKind) } private static createKeyGenerator(keySize: number): cryptoFramework.AsyKeyGenerator { const candidates: string[] = [ 'RSA' + keySize.toString(), 'RSA|' + keySize.toString(), 'RSA/' + keySize.toString(), 'RSA' ] let lastError: Error | null = null for (const algName of candidates) { try { return cryptoFramework.createAsyKeyGenerator(algName) } catch (error) { lastError = error as Error } } throw lastError ?? new Error('create RSA key generator failed') } private static importPemKeyPair(publicKey: string | null, privateKey: string | null): cryptoFramework.KeyPair { let lastError: Error | null = null for (const algName of RSA_GENERATOR_ALGS) { try { const generator = cryptoFramework.createAsyKeyGenerator(algName) return generator.convertPemKeySync(publicKey, privateKey) } catch (error) { lastError = error as Error } } throw lastError ?? new Error('import RSA PEM failed') } private static createCipher(): cryptoFramework.Cipher { let lastError: Error | null = null for (const algName of RSA_CIPHER_ALGS) { try { return cryptoFramework.createCipher(algName) } catch (error) { lastError = error as Error } } throw lastError ?? new Error('create RSA cipher failed') } private static exportPublicPem(publicKey: cryptoFramework.PubKey): string { let lastError: Error | null = null for (const formatName of PUBLIC_PEM_FORMATS) { try { return publicKey.getEncodedPem(formatName) } catch (error) { lastError = error as Error } } throw lastError ?? new Error('export public PEM failed') } private static exportPrivatePem(privateKey: cryptoFramework.PriKey): string { let lastError: Error | null = null for (const formatName of PRIVATE_PEM_FORMATS) { try { return privateKey.getEncodedPem(formatName) } catch (error) { lastError = error as Error } } throw lastError ?? new Error('export private PEM failed') } private static encodeUtf8(input: string): Uint8Array { return UTF8_ENCODER.encodeInto(input) } private static decodeUtf8(input: Uint8Array): string { return UTF8_DECODER.decode(input) } private static encodeCipherOutput(input: Uint8Array, outputKind: string | null): string { if (outputKind === 'hex') { return NativeHarmonyRsaCore.bytesToHex(input) } return BASE64_HELPER.encodeToStringSync(input) } private static decodePlainOutput(input: Uint8Array, outputKind: string | null): string { if (outputKind === 'hex') { return NativeHarmonyRsaCore.bytesToHex(input) } return NativeHarmonyRsaCore.decodeUtf8(input) } private static decodeCipherInput(cipherText: string): Uint8Array { const normalized = cipherText.trim() if (NativeHarmonyRsaCore.looksLikeHex(normalized)) { return NativeHarmonyRsaCore.hexToBytes(normalized) } return BASE64_HELPER.decodeSync(normalized) } private static looksLikeHex(input: string): boolean { return input.length > 0 && input.length % 2 == 0 && /^[0-9a-fA-F]+$/.test(input) } private static bytesToHex(input: Uint8Array): string { let output = '' for (let i = 0; i < input.length; i++) { const item = input[i] if (item < 16) { output += '0' } output += item.toString(16) } return output } private static hexToBytes(input: string): Uint8Array { const length = input.length / 2 const output = new Uint8Array(length) for (let i = 0; i < length; i++) { const offset = i * 2 output[i] = Number.parseInt(input.substring(offset, offset + 2), 16) } return output } }