添加导入加密插件"laoqianjunzi-crypto"
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@ohos/crypto-js": "2.0.4"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
import type { CryptoByteCipherOptions, CryptoCodecKind, CryptoDesOptions, CryptoDigestKind, CryptoMacKind, CryptoRsaPair, CryptoRuntimeSnapshot, CryptoTextCipherOptions, CryptoTripleDesOptions, RsaOutputKind } from '../interface.uts'
|
||||
import { buildRuntimeSnapshot, createDefaultByteCipherOptions, createDefaultDesOptions, createDefaultTextCipherOptions, createDefaultTripleDesOptions, normalizeByteCipherOptions, normalizeDesOptions, normalizeTextCipherOptions, normalizeTripleDesOptions, resolveHexOutput } from '../shared/options.uts'
|
||||
import { NativeHarmonyRsaCore } from './native_rsa.ets'
|
||||
import { CryptoJS } from '@ohos/crypto-js'
|
||||
import type { CompatBridge, CompatCrypto } from '../shared/compat.uts'
|
||||
import { aesDecryptBytesCompat, aesDecryptCompat, aesEncryptBytesCompat, aesEncryptCompat, base64DecodeCompat, base64EncodeCompat, createCompatBridge, createCompatCrypto, desDecryptCompat, desEncryptCompat, generateRsaKeyPairCompat, hmacSha1Compat, hmacSha256Compat, hmacSha512Compat, md5Compat, rc4DecryptCompat, rc4EncryptCompat, rsaDecryptCompat, rsaEncryptCompat, sha1Compat, sha256Compat, sha512Compat } from '../shared/compat.uts'
|
||||
|
||||
type WordValue = CryptoJS.lib.WordArray
|
||||
|
||||
function bytesToWordArray(input : Uint8Array) : WordValue {
|
||||
const words = [] as number[]
|
||||
let index = 0
|
||||
while (index < input.length) {
|
||||
let word = 0
|
||||
let offset = 0
|
||||
while (offset < 4 && index + offset < input.length) {
|
||||
word |= input[index + offset] << (24 - offset * 8)
|
||||
offset += 1
|
||||
}
|
||||
words.push(word)
|
||||
index += 4
|
||||
}
|
||||
return CryptoJS.lib.WordArray.create(words, input.length)
|
||||
}
|
||||
|
||||
function wordArrayToBytes(input : WordValue) : Uint8Array {
|
||||
const output = new Uint8Array(input.sigBytes)
|
||||
let index = 0
|
||||
while (index < input.sigBytes) {
|
||||
const wordIndex = Math.floor(index / 4)
|
||||
const offset = index % 4
|
||||
output[index] = (input.words[wordIndex] >>> (24 - offset * 8)) & 0xff
|
||||
index += 1
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
function resolveAesMode(mode : string) : CryptoJS.BlockCipherMode {
|
||||
if (mode == 'CBC') {
|
||||
return CryptoJS.mode.CBC
|
||||
}
|
||||
if (mode == 'CFB') {
|
||||
return CryptoJS.mode.CFB
|
||||
}
|
||||
if (mode == 'CTR') {
|
||||
return CryptoJS.mode.CTR
|
||||
}
|
||||
if (mode == 'CTRGladman') {
|
||||
return CryptoJS.mode.CTRGladman
|
||||
}
|
||||
if (mode == 'ECB') {
|
||||
return CryptoJS.mode.ECB
|
||||
}
|
||||
return CryptoJS.mode.OFB
|
||||
}
|
||||
|
||||
function resolvePadding(padding : string) : CryptoJS.Padding {
|
||||
if (padding == 'PKCS7') {
|
||||
return CryptoJS.pad.Pkcs7
|
||||
}
|
||||
if (padding == 'ANSI_X923') {
|
||||
return CryptoJS.pad.AnsiX923
|
||||
}
|
||||
if (padding == 'ISO_10126') {
|
||||
return CryptoJS.pad.Iso10126
|
||||
}
|
||||
if (padding == 'ISO_97971') {
|
||||
return CryptoJS.pad.Iso97971
|
||||
}
|
||||
if (padding == 'NONE') {
|
||||
return CryptoJS.pad.NoPadding
|
||||
}
|
||||
return CryptoJS.pad.ZeroPadding
|
||||
}
|
||||
|
||||
function fitIvText(ivText : string | null, size : number) : WordValue {
|
||||
const bytes = new Uint8Array(size)
|
||||
if (ivText != null) {
|
||||
const parsed : WordValue = CryptoJS.enc.Utf8.parse(ivText)
|
||||
const source = wordArrayToBytes(parsed)
|
||||
let index = 0
|
||||
while (index < size && index < source.length) {
|
||||
bytes[index] = source[index]
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
return bytesToWordArray(bytes)
|
||||
}
|
||||
|
||||
function fitIvBytes(ivBytes : Uint8Array | null, size : number) : WordValue {
|
||||
const bytes = new Uint8Array(size)
|
||||
if (ivBytes != null) {
|
||||
let index = 0
|
||||
while (index < size && index < ivBytes.length) {
|
||||
bytes[index] = ivBytes[index]
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
return bytesToWordArray(bytes)
|
||||
}
|
||||
|
||||
function fitKeyWordFromText(secretText : string, keyLength : number | null) : WordValue {
|
||||
let target = 16
|
||||
if (keyLength == 24 || keyLength == 32) {
|
||||
target = keyLength
|
||||
}
|
||||
const sourceWord : WordValue = CryptoJS.enc.Utf8.parse(secretText)
|
||||
const source = wordArrayToBytes(sourceWord)
|
||||
const output = new Uint8Array(target)
|
||||
let index = 0
|
||||
while (index < target && index < source.length) {
|
||||
output[index] = source[index]
|
||||
index += 1
|
||||
}
|
||||
return bytesToWordArray(output)
|
||||
}
|
||||
|
||||
function fitKeyWordFromBytes(secretBytes : Uint8Array, keyLength : number | null) : WordValue {
|
||||
let target = 16
|
||||
if (keyLength == 24 || keyLength == 32) {
|
||||
target = keyLength
|
||||
}
|
||||
const output = new Uint8Array(target)
|
||||
let index = 0
|
||||
while (index < target && index < secretBytes.length) {
|
||||
output[index] = secretBytes[index]
|
||||
index += 1
|
||||
}
|
||||
return bytesToWordArray(output)
|
||||
}
|
||||
|
||||
export function defaultTextCipherOptions() : CryptoTextCipherOptions {
|
||||
return createDefaultTextCipherOptions()
|
||||
}
|
||||
|
||||
export function defaultByteCipherOptions() : CryptoByteCipherOptions {
|
||||
return createDefaultByteCipherOptions()
|
||||
}
|
||||
|
||||
export function defaultTripleDesOptions() : CryptoTripleDesOptions {
|
||||
return createDefaultTripleDesOptions()
|
||||
}
|
||||
|
||||
export function defaultDesOptions() : CryptoDesOptions {
|
||||
return createDefaultDesOptions()
|
||||
}
|
||||
|
||||
export function describeCryptoRuntime() : CryptoRuntimeSnapshot {
|
||||
return buildRuntimeSnapshot('Harmony', '@ohos/crypto-js', true, true)
|
||||
}
|
||||
|
||||
export function encodeText(codec : CryptoCodecKind, plainText : string) : string {
|
||||
if (codec == 'utf8') {
|
||||
return plainText
|
||||
}
|
||||
const word : WordValue = CryptoJS.enc.Utf8.parse(plainText)
|
||||
if (codec == 'hex') {
|
||||
return CryptoJS.enc.Hex.stringify(word)
|
||||
}
|
||||
if (codec == 'base64') {
|
||||
return CryptoJS.enc.Base64.stringify(word)
|
||||
}
|
||||
if (codec == 'base64url') {
|
||||
return CryptoJS.enc.Base64url.stringify(word)
|
||||
}
|
||||
if (codec == 'latin1') {
|
||||
return CryptoJS.enc.Latin1.stringify(word)
|
||||
}
|
||||
return CryptoJS.enc.Utf16.stringify(word)
|
||||
}
|
||||
|
||||
export function decodeText(codec : CryptoCodecKind, encodedText : string) : string {
|
||||
if (codec == 'utf8') {
|
||||
return encodedText
|
||||
}
|
||||
if (codec == 'hex') {
|
||||
return CryptoJS.enc.Hex.parse(encodedText).toString(CryptoJS.enc.Utf8)
|
||||
}
|
||||
if (codec == 'base64') {
|
||||
return CryptoJS.enc.Base64.parse(encodedText).toString(CryptoJS.enc.Utf8)
|
||||
}
|
||||
if (codec == 'base64url') {
|
||||
return CryptoJS.enc.Base64url.parse(encodedText).toString(CryptoJS.enc.Utf8)
|
||||
}
|
||||
if (codec == 'latin1') {
|
||||
return CryptoJS.enc.Latin1.parse(encodedText).toString(CryptoJS.enc.Utf8)
|
||||
}
|
||||
return CryptoJS.enc.Utf16.parse(encodedText).toString(CryptoJS.enc.Utf8)
|
||||
}
|
||||
|
||||
export function digestText(kind : CryptoDigestKind, plainText : string) : string {
|
||||
if (kind == 'md5') {
|
||||
return CryptoJS.MD5(plainText).toString()
|
||||
}
|
||||
if (kind == 'sha1') {
|
||||
return CryptoJS.SHA1(plainText).toString()
|
||||
}
|
||||
if (kind == 'sha256') {
|
||||
return CryptoJS.SHA256(plainText).toString()
|
||||
}
|
||||
return CryptoJS.SHA512(plainText).toString()
|
||||
}
|
||||
|
||||
export function signText(kind : CryptoMacKind, secretText : string, plainText : string) : string {
|
||||
if (kind == 'sha1') {
|
||||
return CryptoJS.HmacSHA1(plainText, secretText).toString()
|
||||
}
|
||||
if (kind == 'sha256') {
|
||||
return CryptoJS.HmacSHA256(plainText, secretText).toString()
|
||||
}
|
||||
return CryptoJS.HmacSHA512(plainText, secretText).toString()
|
||||
}
|
||||
|
||||
export function aesEncryptText(secretText : string, plainText : string, options : CryptoTextCipherOptions | null) : string {
|
||||
const actual = normalizeTextCipherOptions(options)
|
||||
const encrypted : CryptoJS.lib.CipherParams = CryptoJS.AES.encrypt(plainText, fitKeyWordFromText(secretText, actual.keyLength), {
|
||||
iv: fitIvText(actual.ivText, 16),
|
||||
mode: resolveAesMode(actual.mode),
|
||||
padding: resolvePadding(actual.padding)
|
||||
})
|
||||
return encrypted.toString()
|
||||
}
|
||||
|
||||
export function aesDecryptText(secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) : string {
|
||||
const actual = normalizeTextCipherOptions(options)
|
||||
const decrypted : WordValue = CryptoJS.AES.decrypt(cipherText, fitKeyWordFromText(secretText, actual.keyLength), {
|
||||
iv: fitIvText(actual.ivText, 16),
|
||||
mode: resolveAesMode(actual.mode),
|
||||
padding: resolvePadding(actual.padding)
|
||||
})
|
||||
return decrypted.toString(CryptoJS.enc.Utf8)
|
||||
}
|
||||
|
||||
export function aesEncryptBytes(secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array {
|
||||
const actual = normalizeByteCipherOptions(options)
|
||||
const encrypted : CryptoJS.lib.CipherParams = CryptoJS.AES.encrypt(bytesToWordArray(plainBytes), fitKeyWordFromBytes(secretBytes, actual.keyLength), {
|
||||
iv: fitIvBytes(actual.ivBytes, 16),
|
||||
mode: resolveAesMode(actual.mode),
|
||||
padding: resolvePadding(actual.padding)
|
||||
})
|
||||
return wordArrayToBytes(encrypted.ciphertext)
|
||||
}
|
||||
|
||||
export function aesDecryptBytes(secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array {
|
||||
const actual = normalizeByteCipherOptions(options)
|
||||
const payload : UTSJSONObject = {
|
||||
ciphertext: bytesToWordArray(cipherBytes)
|
||||
}
|
||||
const decrypted : WordValue = CryptoJS.AES.decrypt(CryptoJS.lib.CipherParams.create(payload), fitKeyWordFromBytes(secretBytes, actual.keyLength), {
|
||||
iv: fitIvBytes(actual.ivBytes, 16),
|
||||
mode: resolveAesMode(actual.mode),
|
||||
padding: resolvePadding(actual.padding)
|
||||
})
|
||||
return wordArrayToBytes(decrypted)
|
||||
}
|
||||
|
||||
export function tripleDesEncryptText(secretText : string, plainText : string, options : CryptoTripleDesOptions | null) : string {
|
||||
const actual = normalizeTripleDesOptions(options)
|
||||
const encrypted : CryptoJS.lib.CipherParams = CryptoJS.TripleDES.encrypt(plainText, fitKeyWordFromText(secretText, 24), {
|
||||
iv: fitIvText(actual.ivText, 8),
|
||||
mode: actual.mode == 'ECB' ? CryptoJS.mode.ECB : CryptoJS.mode.CBC,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
})
|
||||
return encrypted.toString()
|
||||
}
|
||||
|
||||
export function tripleDesDecryptText(secretText : string, cipherText : string, options : CryptoTripleDesOptions | null) : string {
|
||||
const actual = normalizeTripleDesOptions(options)
|
||||
const decrypted : WordValue = CryptoJS.TripleDES.decrypt(cipherText, fitKeyWordFromText(secretText, 24), {
|
||||
iv: fitIvText(actual.ivText, 8),
|
||||
mode: actual.mode == 'ECB' ? CryptoJS.mode.ECB : CryptoJS.mode.CBC,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
})
|
||||
return decrypted.toString(CryptoJS.enc.Utf8)
|
||||
}
|
||||
|
||||
export function desEncryptText(secretText : string, plainText : string, options : CryptoDesOptions | null) : string {
|
||||
const actual = normalizeDesOptions(options)
|
||||
const encrypted : CryptoJS.lib.CipherParams = CryptoJS.DES.encrypt(plainText, fitKeyWordFromText(secretText, 8), {
|
||||
iv: fitIvText(actual.ivText, 8),
|
||||
mode: resolveAesMode(actual.mode),
|
||||
padding: resolvePadding(actual.padding)
|
||||
})
|
||||
return encrypted.toString()
|
||||
}
|
||||
|
||||
export function desDecryptText(secretText : string, cipherText : string, options : CryptoDesOptions | null) : string {
|
||||
const actual = normalizeDesOptions(options)
|
||||
const decrypted : WordValue = CryptoJS.DES.decrypt(cipherText, fitKeyWordFromText(secretText, 8), {
|
||||
iv: fitIvText(actual.ivText, 8),
|
||||
mode: resolveAesMode(actual.mode),
|
||||
padding: resolvePadding(actual.padding)
|
||||
})
|
||||
return decrypted.toString(CryptoJS.enc.Utf8)
|
||||
}
|
||||
|
||||
export function rc4EncryptText(secretText : string, plainText : string) : string {
|
||||
const encrypted : CryptoJS.lib.CipherParams = CryptoJS.RC4.encrypt(plainText, CryptoJS.enc.Utf8.parse(secretText))
|
||||
return encrypted.ciphertext.toString(CryptoJS.enc.Hex)
|
||||
}
|
||||
|
||||
export function rc4DecryptText(secretText : string, cipherHexText : string) : string {
|
||||
const payload : UTSJSONObject = {
|
||||
ciphertext: CryptoJS.enc.Hex.parse(cipherHexText)
|
||||
}
|
||||
const decrypted : WordValue = CryptoJS.RC4.decrypt(CryptoJS.lib.CipherParams.create(payload), CryptoJS.enc.Utf8.parse(secretText))
|
||||
return decrypted.toString(CryptoJS.enc.Utf8)
|
||||
}
|
||||
|
||||
export function createRsaPair(keySize : number) : CryptoRsaPair {
|
||||
const bundle = NativeHarmonyRsaCore.createRsaBundle(keySize)
|
||||
return {
|
||||
publicKey: bundle.publicKey,
|
||||
privateKey: bundle.privateKey
|
||||
} as CryptoRsaPair
|
||||
}
|
||||
|
||||
export function rsaEncryptText(publicKey : string, plainText : string, outputKind : RsaOutputKind | null) : string {
|
||||
return NativeHarmonyRsaCore.rsaEncrypt(publicKey, plainText, resolveHexOutput(outputKind) ? 'hex' : 'base64')
|
||||
}
|
||||
|
||||
export function rsaDecryptText(privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) : string {
|
||||
return NativeHarmonyRsaCore.rsaDecrypt(privateKey, cipherText, resolveHexOutput(outputKind) ? 'hex' : 'base64')
|
||||
}
|
||||
|
||||
function compatBridge() : CompatBridge {
|
||||
return createCompatBridge(
|
||||
(codec : CryptoCodecKind, plainText : string) : string => encodeText(codec, plainText),
|
||||
(codec : CryptoCodecKind, encodedText : string) : string => decodeText(codec, encodedText),
|
||||
(kind : CryptoDigestKind, plainText : string) : string => digestText(kind, plainText),
|
||||
(kind : CryptoMacKind, secretText : string, plainText : string) : string => signText(kind, secretText, plainText),
|
||||
(secretText : string, plainText : string, options : CryptoTextCipherOptions | null) : string => aesEncryptText(secretText, plainText, options),
|
||||
(secretText : string, cipherText : string, options : CryptoTextCipherOptions | null) : string => aesDecryptText(secretText, cipherText, options),
|
||||
(secretBytes : Uint8Array, plainBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array => aesEncryptBytes(secretBytes, plainBytes, options),
|
||||
(secretBytes : Uint8Array, cipherBytes : Uint8Array, options : CryptoByteCipherOptions | null) : Uint8Array => aesDecryptBytes(secretBytes, cipherBytes, options),
|
||||
(secretText : string, plainText : string, options : CryptoDesOptions | null) : string => desEncryptText(secretText, plainText, options),
|
||||
(secretText : string, cipherText : string, options : CryptoDesOptions | null) : string => desDecryptText(secretText, cipherText, options),
|
||||
(keySize : number) : CryptoRsaPair => createRsaPair(keySize),
|
||||
(publicKey : string, plainText : string, outputKind : RsaOutputKind | null) : string => rsaEncryptText(publicKey, plainText, outputKind),
|
||||
(privateKey : string, cipherText : string, outputKind : RsaOutputKind | null) : string => rsaDecryptText(privateKey, cipherText, outputKind),
|
||||
(secretText : string, plainText : string) : string => rc4EncryptText(secretText, plainText),
|
||||
(secretText : string, cipherHexText : string) : string => rc4DecryptText(secretText, cipherHexText)
|
||||
)
|
||||
}
|
||||
|
||||
export function base64Encode(input : string) : string {
|
||||
return base64EncodeCompat(compatBridge(), input)
|
||||
}
|
||||
|
||||
export function base64Decode(input : string) : string {
|
||||
return base64DecodeCompat(compatBridge(), input)
|
||||
}
|
||||
|
||||
export function md5(input : string) : string {
|
||||
return md5Compat(compatBridge(), input)
|
||||
}
|
||||
|
||||
export function sha1(input : string) : string {
|
||||
return sha1Compat(compatBridge(), input)
|
||||
}
|
||||
|
||||
export function sha256(input : string) : string {
|
||||
return sha256Compat(compatBridge(), input)
|
||||
}
|
||||
|
||||
export function sha512(input : string) : string {
|
||||
return sha512Compat(compatBridge(), input)
|
||||
}
|
||||
|
||||
export function hmacSha1(key : string, data : string) : string {
|
||||
return hmacSha1Compat(compatBridge(), key, data)
|
||||
}
|
||||
|
||||
export function hmacSha256(key : string, data : string) : string {
|
||||
return hmacSha256Compat(compatBridge(), key, data)
|
||||
}
|
||||
|
||||
export function hmacSha512(key : string, data : string) : string {
|
||||
return hmacSha512Compat(compatBridge(), key, data)
|
||||
}
|
||||
|
||||
export function aesEncrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string {
|
||||
return aesEncryptCompat(compatBridge(), key, data, mode, iv, keySize)
|
||||
}
|
||||
|
||||
export function aesDecrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null, keySize : number | null = 16) : string {
|
||||
return aesDecryptCompat(compatBridge(), key, data, mode, iv, keySize)
|
||||
}
|
||||
|
||||
export function aesEncrypt2(key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array {
|
||||
return aesEncryptBytesCompat(compatBridge(), key, data, mode, iv, keySize)
|
||||
}
|
||||
|
||||
export function aesDecrypt2(key : Uint8Array, data : Uint8Array, mode : string = 'ECB', iv : Uint8Array | null = null, keySize : number | null = 16) : Uint8Array {
|
||||
return aesDecryptBytesCompat(compatBridge(), key, data, mode, iv, keySize)
|
||||
}
|
||||
|
||||
export function desEncrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string {
|
||||
return desEncryptCompat(compatBridge(), key, data, mode, iv)
|
||||
}
|
||||
|
||||
export function desDecrypt(key : string, data : string, mode : string = 'ECB', iv : string | null = null) : string {
|
||||
return desDecryptCompat(compatBridge(), key, data, mode, iv)
|
||||
}
|
||||
|
||||
export function generateRSAKeyPair(keySize : number = 2048) : CryptoRsaPair {
|
||||
return generateRsaKeyPairCompat(compatBridge(), keySize)
|
||||
}
|
||||
|
||||
export function rsaEncrypt(publicKey : string, data : string) : string {
|
||||
return rsaEncryptCompat(compatBridge(), publicKey, data)
|
||||
}
|
||||
|
||||
export function rsaDecrypt(privateKey : string, data : string) : string {
|
||||
return rsaDecryptCompat(compatBridge(), privateKey, data)
|
||||
}
|
||||
|
||||
export function rc4Encrypt(key : string, data : string) : string {
|
||||
return rc4EncryptCompat(compatBridge(), key, data)
|
||||
}
|
||||
|
||||
export function rc4Decrypt(key : string, data : string) : string {
|
||||
return rc4DecryptCompat(compatBridge(), key, data)
|
||||
}
|
||||
|
||||
export function useCrypto() : CompatCrypto {
|
||||
return createCompatCrypto(compatBridge())
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user