177 lines
5.9 KiB
Plaintext
177 lines
5.9 KiB
Plaintext
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
|
|
}
|
|
}
|