Last active
June 4, 2025 11:00
-
-
Save ryu1/44f6b76b52ba3ee5527ad493c54af8e0 to your computer and use it in GitHub Desktop.
Implementation of AES encryption/decryption in GraalJS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const JavaBase64 = Java.type("java.util.Base64"); | |
const JavaCipher = Java.type("javax.crypto.Cipher"); | |
const JavaIvParameterSpec = Java.type("javax.crypto.spec.IvParameterSpec"); | |
const JavaSecretKeySpec = Java.type("javax.crypto.spec.SecretKeySpec"); | |
const JavaString = Java.type("java.lang.String"); | |
const JavaSystem = Java.type("java.lang.System"); | |
const JavaArrays = Java.type("java.util.Arrays"); | |
const JavaArray= Java.type("java.lang.reflect.Array"); | |
const JavaMethod = Java.type("java.lang.reflect.Method"); | |
const CHARSET_NAME = Object.freeze({ | |
WINDOWS_31J: "Windows-31J", | |
UTF_8: "UTF-8", | |
}); | |
const CIPHER_TRANSFORMATION ="AES/CBC/PKCS5Padding"; | |
const ENCRYPTION_ALGORITHM = "AES"; | |
const IV_LENGTH = 16; | |
const CRYPTOR_IV = getEnvAsByteArrayWithBase64Decoding("IV_WITH_BASE64"), | |
const CRYPTOR_SECRET_KEY = getEnvAsByteArrayWithBase64Decoding("SECRET_KEY_WITH_BASE64"), | |
/** | |
* Converts a string into a byte array using the specified charset. | |
* | |
* @param {string} str - The string to convert. | |
* @param {string} [charsetName=CHARSET_NAME.UTF_8] - The charset name to use for conversion. | |
* | |
* @returns {byte[]} - The byte array representation of the input string. | |
* | |
* @example | |
* const bytes = getBytes('Hello, World!', CHARSET_NAME.WINDOWS_31J); | |
* console.log(bytes); // Output: Uint8Array [72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33] | |
*/ | |
function getBytes(str, charsetName = CHARSET_NAME.UTF_8) { | |
const method = JavaString.class.getMethod("getBytes", JavaString.class); | |
const bytes = method.invoke(str, charsetName); | |
return bytes; | |
} | |
/** | |
* Retrieves an environment variable value and decodes it from Base64. | |
* | |
* @param {string} name - The name of the environment variable to retrieve. | |
* | |
* @returns {byte[]} - The decoded Base64 value of the environment variable. | |
* | |
* @throws {Error} - If the environment variable is not found or cannot be decoded. | |
* | |
* @example | |
* const decodedValue = getEnvWithBase64Decoding('SECRET_KEY_WITH_BASE64'); | |
* console.log(decodedValue); // Output: Uint8Array [104, 101, 120, ...] | |
*/ | |
function getEnvAsByteArrayWithBase64Decoding(name) { | |
return JavaBase64.getDecoder().decode(getEnv(name)); | |
} | |
/** | |
* Creates an AES cipher instance for encryption or decryption. | |
* | |
* @param {byte[]} secretKey - The secret key used for encryption and decryption. | |
* @param {byte[]} iv - The initialization vector (IV) used for encryption. | |
* | |
* @returns {javax.crypto.Cipher} - The initialized AES cipher instance. | |
* | |
* @throws {Error} - If an error occurs during cipher initialization. | |
* | |
* @example | |
* const cipher = createCipher('secretKey', 'ivValue'); | |
*/ | |
function createCipher(mode, secretKey, iv) { | |
const secretKeySpec = new JavaSecretKeySpec(secretKey, ENCRYPTION_ALGORITHM); | |
const ivParameterSpec = new JavaIvParameterSpec(iv); | |
const cipher = JavaCipher.getInstance(CIPHER_TRANSFORMATION); | |
cipher.init(mode, secretKeySpec, ivParameterSpec); | |
return cipher; | |
} | |
/** | |
* Encrypts a plain text using AES encryption. | |
* | |
* @param {string} plainText - The plain text to be encrypted. | |
* @param {byte[]} secretKey - The secret key used for encryption. | |
* @param {byte[]} iv - The iv used for encryption. | |
* @param {string} charsetName - The charset name used for decryption. | |
* | |
* @returns {string} - The encrypted text. | |
* | |
* @throws {Error} - If an error occurs during encryption. | |
* | |
* @example | |
* const encryptedText = encrypt('exampleText', 'secretKey'); | |
* console.log(encryptedText); // Output: 'encryptedValue' | |
*/ | |
function encrypt(plainText, secretKey, iv, charsetName = CHARSET_NAME.UTF_8) { | |
let encryptedText = ""; | |
const cipher = createCipher(JavaCipher.ENCRYPT_MODE, secretKey, iv); | |
const cipherText = cipher.doFinal(getBytes(plainText, charsetName)); | |
const ByteType = cipherText.getClass().getComponentType(); | |
const joinedArray = JavaArray.newInstance(ByteType, JavaArray.getLength(iv) + JavaArray.getLength(cipherText)); | |
JavaSystem.arraycopy(iv, 0, joinedArray, 0, JavaArray.getLength(iv)); | |
JavaSystem.arraycopy(cipherText, 0, joinedArray, JavaArray.getLength(iv), JavaArray.getLength(cipherText)); | |
const encoder = JavaBase64.getEncoder(); | |
encryptedText = encoder.encodeToString(joinedArray); | |
return encryptedText; | |
} | |
/** | |
* Decrypts an encrypted value using AES decryption. | |
* | |
* @param {string} encryptedText - The encrypted value to be decrypted. | |
* @param {byte[]} secretKey - The secret key used for decryption. | |
* @param {string} charsetName - The charset name used for decryption. | |
* | |
* @returns {string} - The decrypted value. | |
* | |
* @throws {Error} - If an error occurs during decryption. | |
* | |
* @example | |
* const encryptedEmail = 'encryptedEmailValue'; | |
* const decryptedEmail = decrypt(encryptedEmail, SECRET_KEY); | |
* console.log(decryptedEmail); // Output: '[email protected]' | |
*/ | |
function decrypt(encryptedText, secretKey, charsetName = CHARSET_NAME.UTF_8) { | |
const decoder = JavaBase64.getDecoder(); | |
const cipherText = decoder.decode(encryptedText); | |
const iv = JavaArrays.copyOf(cipherText, IV_LENGTH); | |
const actualCipherText = JavaArrays.copyOfRange(cipherText, IV_LENGTH, JavaArray.getLength(cipherText)); | |
const cipher = createCipher(JavaCipher.DECRYPT_MODE, secretKey, iv); | |
const final = cipher.doFinal(actualCipherText); | |
const decryptedText = new JavaString(final, charsetName); | |
return decryptedText; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
synthisized.io向けに、GraalJSでAES暗号化/復号処理を実装した。
Javascriptから、一部のJavaのAPIが呼び出せないので、リフレクションで強引に呼び出すことで解決した。