All Products
Search
Document Center

Elastic Compute Service:Encrypt command content with Cloud Assistant

Last Updated:Apr 27, 2026

Encrypt sensitive data such as passwords or keys in Cloud Assistant commands with a temporary RSA key pair to prevent plaintext exposure in API calls and logs.

How it works

Cloud Assistant encrypts command content with RSA-OAEP asymmetric encryption:

  1. Generate a temporary key pair: Cloud Assistant generates a temporary RSA key pair (public key and private key) in the Cloud Assistant Agent's process memory on the target ECS instance.

  2. Protect the private key: The private key exists only in memory — never transmitted, written to disk, or logged. The key pair is automatically destroyed after 60 seconds by default.

  3. Encrypt locally: The Cloud Assistant Agent returns the public key to the client or server, which uses it to encrypt sensitive information such as a password into ciphertext.

  4. Transmit the ciphertext: A new command containing the decryption instruction and ciphertext is sent to the target instance through Cloud Assistant.

  5. Decrypt and use within the instance: The Cloud Assistant Agent decrypts the ciphertext with the private key in memory and passes the plaintext to the script logic.

Sensitive information never appears in plaintext on Alibaba Cloud management platforms, such as API responses or ActionTrail logs, from client to instance execution.

Usage notes

  • Cloud Assistant Agent version: 2.x.3.398 or later.

  • Permission requirements: The RAM user or RAM role must have the ecs:RunCommand permission for the target instance.

Procedure

Step 1: Generate a temporary key pair

Create a temporary key pair on the target ECS instance for encryption.

  1. Call the RunCommand API or run a command in the console to send the following instruction to the target instance:

    aliyun-service data-encryption -g --json

    Parameters

    • -g: Abbreviation for generate. Generates a key pair.

    • --json: (Optional) Outputs the result in JSON format for programmatic parsing.

    • -i <key_pair_id>: (Optional) Specifies a key pair ID. If omitted, the system generates one automatically.

    • -t <timeout_in_seconds>: (Optional) Key pair validity period in seconds. Default: 60.

  2. The JSON response contains the public key and key ID. Record the id and publicKey values for subsequent steps.

    Sample output

    {
      "id": "t-hy03a65fmrd****",
      "createdTimestamp": 1675309078,
      "expiredTimestamp": 1675309138,
      "publicKey": "-----BEGIN PUBLIC KEY-----\n****\n****\n-----END PUBLIC KEY-----\n"
    }

    Parameters

    • id: Key ID.

    • createdTimestamp: Key pair creation time.

    • expiredTimestamp: Key pair expiration time.

    • publicKey: Public key for encrypting command content.

Step 2: Encrypt sensitive data locally

Use the public key from the previous step to encrypt sensitive data, such as a password, into ciphertext. This example resets the logon password for the root user of a Linux instance. Ensure required dependency libraries such as pycryptodome for Python are installed in your local environment.

import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.util.Base64;
import java.nio.charset.StandardCharsets;

public class RsaOaepSha256Encryptor {

    public static String encrypt(String publicKeyPem, String secret) {
        try {
            // 1. Process the PEM-formatted public key. Remove the header, footer, and line breaks.
            String publicKeyContent = publicKeyPem
                    .replace("-----BEGIN PUBLIC KEY-----", "")
                    .replace("-----END PUBLIC KEY-----", "")
                    .replaceAll("\\s", ""); // Remove line breaks and spaces.

            byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyContent);

            // 2. Generate the public key object.
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey publicKey = keyFactory.generatePublic(keySpec);

            // 3. Configure Cipher to use RSA-OAEP and SHA-256.
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
            
            // Specify that both the main hash and MGF1 hash are SHA-256.
            OAEPParameterSpec oaepParams = new OAEPParameterSpec(
                "SHA-256", 
                "MGF1", 
                MGF1ParameterSpec.SHA256, 
                PSource.PSpecified.DEFAULT
            );
            
            cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParams);

            // 4. Encrypt and then Base64-encode.
            byte[] encryptedBytes = cipher.doFinal(secret.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(encryptedBytes);

        } catch (Exception e) {
            System.err.println("Encryption failed: " + e.getMessage());
            return null;
        }
    }

    public static void main(String[] args) {
        // The public key obtained in Step 1.
        String publicKeyPem = "-----BEGIN PUBLIC KEY-----\n" +
                "****\n" +
                "****\n" +
                "-----END PUBLIC KEY-----";

        // The sensitive information to encrypt.
        String secretToEncrypt = "<New_Password>";

        String encryptedText = encrypt(publicKeyPem, secretToEncrypt);

        if (encryptedText != null) {
            System.out.println("Encrypted ciphertext (Base64):");
            System.out.println(encryptedText);
        }
    }
}
import base64
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Hash import SHA256

def encrypt_data(public_key_pem: str, secret: str) -> str:
    """Encrypt data using RSA-OAEP and SHA-256."""
    try:
        # Import the public key.
        public_key = RSA.import_key(public_key_pem)
        # Create the cipher.
        cipher = PKCS1_OAEP.new(public_key, hashAlgo=SHA256)
        # Encrypt the data and Base64-encode it.
        encrypted_bytes = cipher.encrypt(secret.encode('utf-8'))
        return base64.b64encode(encrypted_bytes).decode('utf-8')
    except Exception as e:
        print(f"Encryption failed: {e}")
        return ""

def main():
    # The public key obtained in Step 1.
    public_key_pem = """-----BEGIN PUBLIC KEY-----
********
-----END PUBLIC KEY-----"""
    
    # The sensitive information to encrypt.
    secret_to_encrypt = "<New_Password>"
    
    encrypted_text = encrypt_data(public_key_pem, secret_to_encrypt)
    
    if encrypted_text:
        print("Encrypted ciphertext (Base64):")
        print(encrypted_text)

if __name__ == "__main__":
    main()
package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	"os"
)

func encryptData(publicKeyPEM string, secret string) (string, error) {
	// 1. Parse the PEM block.
	block, _ := pem.Decode([]byte(publicKeyPEM))
	if block == nil {
		return "", fmt.Errorf("failed to parse PEM block")
	}

	// 2. Parse the public key.
	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return "", fmt.Errorf("failed to parse public key: %v", err)
	}

	rsaPub, ok := pub.(*rsa.PublicKey)
	if !ok {
		return "", fmt.Errorf("key is not an RSA public key")
	}

	// 3. Encrypt using RSA-OAEP (SHA-256).
	// The label is usually empty.
	secretBytes := []byte(secret)
	encryptedBytes, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, rsaPub, secretBytes, nil)
	if err != nil {
		return "", fmt.Errorf("encryption failed: %v", err)
	}

	// 4. Base64-encode.
	return base64.StdEncoding.EncodeToString(encryptedBytes), nil
}

func main() {
	// The public key obtained in Step 1.
	publicKeyPEM := `-----BEGIN PUBLIC KEY-----
********
-----END PUBLIC KEY-----`

	// The sensitive information to encrypt.
	secretToEncrypt := "<New_Password>"

	encryptedText, err := encryptData(publicKeyPEM, secretToEncrypt)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}

	fmt.Println("Encrypted ciphertext (Base64):")
	fmt.Println(encryptedText)
}
const crypto = require('crypto');

function encrypt(publicKey, secret) {
  try {
    const secretBuffer = Buffer.from(secret, 'utf8');
    const encryptedBuffer = crypto.publicEncrypt(
      {
        key: publicKey,
        padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
        oaepHash: 'sha256',
      },
      secretBuffer
    );
    return encryptedBuffer.toString('base64');
  } catch (e) {
    console.error(`Encryption failed: ${e.message}`);
    return null;
  }
}

// The public key obtained in Step 1.
const publicKey = `-----BEGIN PUBLIC KEY-----
********
-----END PUBLIC KEY-----`;

// The sensitive information to encrypt.
const secretToEncrypt = "<New_Password>";

const encryptedText = encrypt(publicKey, secretToEncrypt);

if (encryptedText) {
  console.log("Encrypted ciphertext (Base64):");
  console.log(encryptedText);
}
/*
Convert a string into an ArrayBuffer
from https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
*/
function base64ToArrayBuffer(base64Text: string): ArrayBuffer {
  var binary = window.atob(base64Text);
  var len = binary.length;
  var bytes = new Uint8Array(len);
  for (var i = 0; i < len; i++)        {
    bytes[i] = binary.charCodeAt(i);
  }
  return bytes;
};

function arrayBufferToBase64(buffer: ArrayBuffer): string {
  const uint8Array = new Uint8Array(buffer)
  const binary = String.fromCharCode(...uint8Array)
  return window.btoa(binary)
}

async function encrypt(publicKey: string, password: string): Promise<string> {
  const content = publicKey.replace(/-+(BEGIN|END) PUBLIC KEY-+/ig, '');
const pemText = content.replace(/[\r\n]+/g, '');
const binaryDer = base64ToArrayBuffer(pemText);

const crypto = window.crypto
  || (window as any).webkitCrypto
  || (window as any).mozCrypto
  || (window as any).oCrypto
  || (window as any).msCrypto;

const cryptoKey = await crypto!.subtle.importKey(
  'spki',
  binaryDer,
  {name: 'RSA-OAEP', hash: 'SHA-256'},
  true,
  ['encrypt']
)

const uint8Pwd: Uint8Array = Buffer.from(password);
const encrypted: ArrayBuffer = await crypto!.subtle.encrypt(
  {name: 'RSA-OAEP'},
  cryptoKey,
  uint8Pwd
);  
return arrayBufferToBase64(encrypted);
}

const pem = "-----BEGIN PUBLIC KEY-----" +
  "********" +
  "-----END PUBLIC KEY-----"

encrypt(pem, "new-secret-value").then(result=>{
  document.writeln("result = " + result)
}).catch(reason=>{
  document.writeln("error = " + reason)
})
using System;
using System.Security.Cryptography;
using System.Text;

public class Program
{
    public static string Encrypt(string publicKeyPem, string secret)
    {
        try
        {
            using (var rsa = RSA.Create())
            {
                // 1. Import the PEM-formatted public key (for .NET Core 3.0+, .NET 5+).
                rsa.ImportFromPem(publicKeyPem);

                // 2. Convert the data to a byte array.
                byte[] dataToEncrypt = Encoding.UTF8.GetBytes(secret);

                // 3. Encrypt using RSA-OAEP (SHA-256).
                byte[] encryptedData = rsa.Encrypt(dataToEncrypt, RSAEncryptionPadding.OaepSHA256);

                // 4. Return a Base64 string.
                return Convert.ToBase64String(encryptedData);
            }
        }
        catch (Exception e)
        {
            Console.Error.WriteLine($"Encryption failed: {e.Message}");
            return null;
        }
    }

    public static void Main()
    {
        // The public key obtained in Step 1.
        string publicKeyPem = @"-----BEGIN PUBLIC KEY-----
********
-----END PUBLIC KEY-----";

        // The sensitive information to encrypt.
        string secretToEncrypt = "<New_Password>";

        string encryptedText = Encrypt(publicKeyPem, secretToEncrypt);

        if (!string.IsNullOrEmpty(encryptedText))
        {
            Console.WriteLine("Encrypted ciphertext (Base64):");
            Console.WriteLine(encryptedText);
        }
    }
}

Step 3: Decrypt and execute

Create a shell script that decrypts the ciphertext on the instance, performs the target operation such as changing a password, and clears the temporary key after completion.

  1. Write a shell script. The following security-hardened example resets the password for the root user.

    The simple echo "root:$decrypted_text" | chpasswd command poses a security risk. If decryption fails (for example, due to key expiration), $decrypted_text is empty, setting the root password to an empty value. Use the following script with error checking and automatic cleanup.
    #!/bin/bash
    
    # ##################################################################
    # Secure execution script: Decrypt and reset password
    # ##################################################################
    
    # --- Configuration section ---
    # The key pair ID from Step 1
    key_pair_id="t-hy03a65fmrd****"
    
    # The encrypted ciphertext from Step 2
    encrypted_password="YOUR_ENCRYPTED_BASE64_STRING_HERE"
    
    # --- Core logic section ---
    
    # Define a cleanup function to automatically delete the key pair when the script exits.
    cleanup() {
      echo "Performing cleanup: Deleting temporary key pair $key_pair_id ..."
      aliyun-service data-encryption --remove-keypair -i "$key_pair_id" > /dev/null 2>&1
    }
    
    # Register the cleanup function to ensure it runs when the script exits for any reason (success, error, or interruption).
    trap cleanup EXIT
    
    # Perform the decryption operation.
    decrypted_text=$(aliyun-service data-encryption -d -i "$key_pair_id" -T "$encrypted_password")
    
    # Critical security check: Ensure decryption was successful and the content is not empty.
    if [ -z "$decrypted_text" ]; then
        echo "Error: Decryption failed. The key may have expired, the key ID may be incorrect, or the ciphertext may be invalid. Operation aborted." >&2
        exit 1
    fi
    
    # Use the decrypted plaintext to reset the password.
    echo "root:$decrypted_text" | chpasswd
    
    if [ $? -eq 0 ]; then
        echo "Password reset successfully."
    else
        echo "Error: Password reset command failed." >&2
        exit 1
    fi
    
    # The script will exit normally here, and the trap will automatically trigger the cleanup function.
  2. Send the script content as a command to the target instance using the RunCommand API.

Apply in production

  • Automation risks: The default TTL for a temporary key pair is 60 seconds. In complex automated workflows, network latency, API queuing, and instance load can cause key expiration and process failures. Use the -t parameter to extend the validity period when generating the key.

  • Code robustness: Always validate the decryption return value. Ignoring failures (null or empty string) can break business logic and introduce security vulnerabilities, such as accidentally resetting a password to an empty value.

Limitations

  • Security boundaries: Command content encryption prevents sensitive information from being exposed on the Alibaba Cloud management platform. It does not protect against users already logged on to the ECS instance. A user with instance logon permissions can run the decryption command repeatedly within the key's validity period to obtain the plaintext.

  • Open source agent risks: Cloud Assistant Agent is open source. A maliciously modified agent can intercept the private key or decrypted plaintext. Always obtain and use the Cloud Assistant Agent from official Alibaba Cloud channels.

FAQ

If the decryption script fails midway, will the key be leaked?

The private key is not leaked, but it remains in memory until expiration (default: 60 seconds). During this window, a user with instance logon access can attempt decryption. Use the trap command to ensure the key cleanup command (aliyun-service data-encryption --remove-keypair) runs regardless of script outcome. See the best practice script in the procedure.