Automatiza la generación de contraseñas de Windows

El comando gcloud compute reset-windows-password permite que un usuario con acceso de escritura al proyecto de Compute Engine recupere de forma segura las contraseñas de las cuentas en las instancias de Windows.

El comando envía un nombre de usuario y una clave pública RSA a la instancia para conseguirlo. Entonces, el agente que se ejecuta en la instancia realiza una de las siguientes acciones:

  • Crea una cuenta en la instancia para ese nombre de usuario y genera una contraseña aleatoria.
  • Restablece la contraseña a un valor aleatorio si la cuenta ya existe.

El agente que se ejecuta en la instancia encripta la contraseña con la clave pública proporcionada y la envía de vuelta al cliente para que se la desencripte con la clave privada correspondiente.

En esta sección, se describe el funcionamiento de este proceso y se ofrecen algunas secuencias de comandos de ejemplo que replican estos pasos de manera programática. Si quieres seguir estos pasos de forma manual, lee la sección de Instrucciones manuales.

Antes de comenzar

Automatiza la generación de contraseñas


//  Copyright 2018 Google Inc. All Rights Reserved.
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

package main

import (

	daisyCompute ""

var (
	instance = flag.String("instance", "", "instance to reset password on")
	zone     = flag.String("zone", "", "zone instance is in")
	project  = flag.String("project", "", "project instance is in")
	user     = flag.String("user", "", "user to reset password for")

func getInstanceMetadata(client daisyCompute.Client, i, z, p string) (*compute.Metadata, error) {
	ins, err := client.GetInstance(p, z, i)
	if err != nil {
		return nil, fmt.Errorf("error getting instance: %v", err)

	return ins.Metadata, nil

type windowsKeyJSON struct {
	ExpireOn string
	Exponent string
	Modulus  string
	UserName string

func generateKey(priv *rsa.PublicKey, u string) (*windowsKeyJSON, error) {
	bs := make([]byte, 4)
	binary.BigEndian.PutUint32(bs, uint32(priv.E))

	return &windowsKeyJSON{
		ExpireOn: time.Now().Add(5 * time.Minute).Format(time.RFC3339),
		// This is different than what the other tools produce,
		// AQAB vs AQABAA==, both are decoded as 65537.
		Exponent: base64.StdEncoding.EncodeToString(bs),
		Modulus:  base64.StdEncoding.EncodeToString(priv.N.Bytes()),
		UserName: u,
	}, nil

type credsJSON struct {
	ErrorMessage      string `json:"errorMessage,omitempty"`
	EncryptedPassword string `json:"encryptedPassword,omitempty"`
	Modulus           string `json:"modulus,omitempty"`

func getEncryptedPassword(client daisyCompute.Client, i, z, p, mod string) (string, error) {
	out, err := client.GetSerialPortOutput(p, z, i, 4, 0)
	if err != nil {
		return "", err

	for _, line := range strings.Split(out.Contents, "\n") {
		var creds credsJSON
		if err := json.Unmarshal([]byte(line), &creds); err != nil {
		if creds.Modulus == mod {
			if creds.ErrorMessage != "" {
				return "", fmt.Errorf("error from agent: %s", creds.ErrorMessage)
			return creds.EncryptedPassword, nil
	return "", errors.New("password not found in serial output")

func decryptPassword(priv *rsa.PrivateKey, ep string) (string, error) {
	bp, err := base64.StdEncoding.DecodeString(ep)
	if err != nil {
		return "", fmt.Errorf("error decoding password: %v", err)
	pwd, err := rsa.DecryptOAEP(sha1.New(), rand.Reader, priv, bp, nil)
	if err != nil {
		return "", fmt.Errorf("error decrypting password: %v", err)
	return string(pwd), nil

func resetPassword(client daisyCompute.Client, i, z, p, u string) (string, error) {
	md, err := getInstanceMetadata(client, *instance, *zone, *project)
	if err != nil {
		return "", fmt.Errorf("error getting instance metadata: %v", err)

	fmt.Println("Generating public/private key pair")
	key, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return "", err

	winKey, err := generateKey(&key.PublicKey, u)
	if err != nil {
		return "", err

	data, err := json.Marshal(winKey)
	if err != nil {
		return "", err

	winKeys := string(data)
	var found bool
	for _, mdi := range md.Items {
		if mdi.Key == "windows-keys" {
			val := fmt.Sprintf("%s\n%s", *mdi.Value, winKeys)
			mdi.Value = &val
			found = true
	if !found {
		md.Items = append(md.Items, &compute.MetadataItems{Key: "windows-keys", Value: &winKeys})

	fmt.Println("Setting new 'windows-keys' metadata")
	if err := client.SetInstanceMetadata(p, z, i, md); err != nil {
		return "", err

	fmt.Println("Fetching encrypted password")
	var trys int
	var ep string
	for {
		time.Sleep(1 * time.Second)
		ep, err = getEncryptedPassword(client, i, z, p, winKey.Modulus)
		if err == nil {
		if trys > 10 {
			return "", err

	fmt.Println("Decrypting password")
	return decryptPassword(key, ep)

func main() {
	if *instance == "" {
		log.Fatal("-instance flag required")
	if *zone == "" {
		log.Fatal("-zone flag required")
	if *project == "" {
		log.Fatal("-project flag required")
	if *user == "" {
		log.Fatal("-user flag required")

	ctx := context.Background()
	client, err := daisyCompute.NewClient(ctx)
	if err != nil {
		log.Fatalf("Error creating compute service: %v", err)

	fmt.Printf("Resetting password on instance %q for user %q\n", *instance, *user)
	pw, err := resetPassword(client, *instance, *zone, *project, *user)
	if err != nil {
	fmt.Printf("- Username: %s\n- Password: %s\n", *user, pw)


#!/usr/bin/env python

# Copyright 2015 Google Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# See the License for the specific language governing permissions and
# limitations under the License.

import base64
import copy
import datetime
import json
import time

# PyCrypto library:
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto.Util.number import long_to_bytes

# Google API Client Library for Python:
from oauth2client.client import GoogleCredentials
from googleapiclient.discovery import build

def GetCompute():
    """Get a compute object for communicating with the Compute Engine API."""
    credentials = GoogleCredentials.get_application_default()
    compute = build('compute', 'v1', credentials=credentials)
    return compute

def GetInstance(compute, instance, zone, project):
    """Get the data for a Google Compute Engine instance."""
    cmd = compute.instances().get(instance=instance, project=project,
    return cmd.execute()

def GetKey():
    """Get an RSA key for encryption."""
    # This uses the PyCrypto library
    key = RSA.generate(2048)
    return key

def GetModulusExponentInBase64(key):
    """Return the public modulus and exponent for the key in bas64 encoding."""
    mod = long_to_bytes(key.n)
    exp = long_to_bytes(key.e)

    modulus = base64.b64encode(mod)
    exponent = base64.b64encode(exp)

    return modulus, exponent

def GetExpirationTimeString():
    """Return an RFC3339 UTC timestamp for 5 minutes from now."""
    utc_now = datetime.datetime.utcnow()
    # These metadata entries are one-time-use, so the expiration time does
    # not need to be very far in the future. In fact, one minute would
    # generally be sufficient. Five minutes allows for minor variations
    # between the time on the client and the time on the server.
    expire_time = utc_now + datetime.timedelta(minutes=5)
    return expire_time.strftime('%Y-%m-%dT%H:%M:%SZ')

def GetJsonString(user, modulus, exponent, email):
    """Return the JSON string object that represents the windows-keys entry."""
    expire = GetExpirationTimeString()
    data = {'userName': user,
            'modulus': modulus,
            'exponent': exponent,
            'email': email,
            'expireOn': expire}
    return json.dumps(data)

def UpdateWindowsKeys(old_metadata, metadata_entry):
    """Return updated metadata contents with the new windows-keys entry."""
    # Simply overwrites the "windows-keys" metadata entry. Production code may
    # want to append new lines to the metadata value and remove any expired
    # entries.
    new_metadata = copy.deepcopy(old_metadata)
    new_metadata['items'] = [{
        'key': "windows-keys",
        'value': metadata_entry
    return new_metadata

def UpdateInstanceMetadata(compute, instance, zone, project, new_metadata):
    """Update the instance metadata."""
    cmd = compute.instances().setMetadata(instance=instance, project=project,
                                          zone=zone, body=new_metadata)
    return cmd.execute()

def GetSerialPortFourOutput(compute, instance, zone, project):
    """Get the output from serial port 4 from the instance."""
    # Encrypted passwords are printed to COM4 on the windows server:
    port = 4
    cmd = compute.instances().getSerialPortOutput(instance=instance,
                                                  zone=zone, port=port)
    output = cmd.execute()
    return output['contents']

def GetEncryptedPasswordFromSerialPort(serial_port_output, modulus):
    """Find and return the correct encrypted password, based on the modulus."""
    # In production code, this may need to be run multiple times if the output
    # does not yet contain the correct entry.
    output = serial_port_output.split('\n')
    for line in reversed(output):
            entry = json.loads(line)
            if modulus == entry['modulus']:
                return entry['encryptedPassword']
        except ValueError:

def DecryptPassword(encrypted_password, key):
    """Decrypt a base64 encoded encrypted password using the provided key."""
    decoded_password = base64.b64decode(encrypted_password)
    cipher =
    password = cipher.decrypt(decoded_password)
    return password

def main(instance, zone, project, user, email):
    # Setup
    compute = GetCompute()
    key = GetKey()
    modulus, exponent = GetModulusExponentInBase64(key)

    # Get existing metadata
    instance_ref = GetInstance(compute, instance, zone, project)
    old_metadata = instance_ref['metadata']

    # Create and set new metadata
    metadata_entry = GetJsonString(user, modulus,
                                   exponent, email)
    new_metadata = UpdateWindowsKeys(old_metadata, metadata_entry)
    result = UpdateInstanceMetadata(compute, instance, zone, project,

    # For this sample code, just sleep for 30 seconds instead of checking for
    # responses. In production code, this should monitor the status of the
    # metadata update operation.

    # Get and decrypt password from serial port output
    serial_port_output = GetSerialPortFourOutput(compute, instance,
                                                 zone, project)
    enc_password = GetEncryptedPasswordFromSerialPort(serial_port_output,
    password = DecryptPassword(enc_password, key)

    # Display the username, password and IP address for the instance
    print 'Username:   {0}'.format(user)
    print 'Password:   {0}'.format(password)
    ip = instance_ref['networkInterfaces'][0]['accessConfigs'][0]['natIP']
    print 'IP Address: {0}'.format(ip)

if __name__ == '__main__':
    instance = 'my-instance'
    zone = 'us-central1-a'
    project = 'my-project'
    user = 'example-user'
    email = ''
    main(instance, zone, project, user, email)


 * Copyright 2015 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * See the License for the specific language governing permissions and
 * limitations under the License.

 * This package demonstrates how to reset Windows passwords in Java.



import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.TimeZone;

import javax.crypto.Cipher;

public class ExampleCode {

  public ExampleCode() {}

  // Constants used to configure behavior.
  private static final String ZONE_NAME = "us-central1-a";
  private static final String PROJECT_NAME = "example-project-1234";
  private static final String INSTANCE_NAME = "test-instance";
  private static final String APPLICATION_NAME = "windows-pw-reset";

  // Constants for configuring user name, email, and SSH key expiration.
  private static final String USER_NAME = "example_user";
  private static final String EMAIL = "";

  // Keys are one-time use, so the metadata doesn't need to stay around for long.
  // 5 minutes chosen to allow for differences between time on the client
  // and time on the server.
  private static final long EXPIRE_TIME = 300000;

  // HttpTransport and JsonFactory used to create the Compute object.
  private static HttpTransport httpTransport;
  private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();

  public static void main(String[] args) {
    ExampleCode ec = new ExampleCode();
    try {
      // Initialize Transport object.
      httpTransport = GoogleNetHttpTransport.newTrustedTransport();

      // Reset the password.
    } catch (Exception e) {

  public void resetPassword() throws Exception {
    // Get credentials to setup a connection with the Compute API.
    Credential cred = GoogleCredential.getApplicationDefault();

    // Create an instance of the Compute API.
    Compute compute = new Compute.Builder(httpTransport, JSON_FACTORY, null)

    // Get the instance object to gain access to the instance's metadata.
    Instance inst = compute.instances().get(PROJECT_NAME, ZONE_NAME, INSTANCE_NAME).execute();
    Metadata metadata = inst.getMetadata();

    // Generate the public/private key pair for encryption and decryption.
    KeyPair keys = generateKeys();

    // Update metadata from instance with new windows-keys entry.
    replaceMetadata(metadata, buildKeyMetadata(keys));

    // Tell Compute Engine to update the instance metadata with our changes.
    compute.instances().setMetadata(PROJECT_NAME, ZONE_NAME, INSTANCE_NAME, metadata).execute();

    System.out.println("Updating metadata...");

    // Sleep while waiting for metadata to propagate - production code may
    // want to monitor the status of the metadata update operation.

    System.out.println("Getting serial output...");

    // Request the output from serial port 4.
    // In production code, this operation should be polled.
    SerialPortOutput output = compute.instances()
        .getSerialPortOutput(PROJECT_NAME, ZONE_NAME, INSTANCE_NAME).setPort(4).execute();

    // Get the last line - this will be a JSON string corresponding to the
    // most recent password reset attempt.
    String[] entries = output.getContents().split("\n");
    String outputEntry = entries[entries.length - 1];

    // Parse output using the json-simple library.
    JSONParser parser = new JSONParser();
    JSONObject passwordDict = (JSONObject) parser.parse(outputEntry);

    String encryptedPassword = passwordDict.get("encryptedPassword").toString();

    // Output user name and decrypted password.
    System.out.println("\nUser name: " + passwordDict.get("userName").toString());
    System.out.println("Password: " + decryptPassword(encryptedPassword, keys));

  private String decryptPassword(String message, KeyPair keys) {
    try {
      // Add the bouncycastle provider - the built-in providers don't support RSA
      // with OAEPPadding.
      Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

      // Get the appropriate cipher instance.
      Cipher rsa = Cipher.getInstance("RSA/NONE/OAEPPadding", "BC");

      // Add the private key for decryption.
      rsa.init(Cipher.DECRYPT_MODE, keys.getPrivate());

      // Decrypt the text.
      byte[] rawMessage = Base64.decodeBase64(message);
      byte[] decryptedText = rsa.doFinal(rawMessage);

      // The password was encoded using UTF8. Transform into string.
      return new String(decryptedText, "UTF8");
    } catch (Exception e) {
    return "";

  private void replaceMetadata(Metadata input, JSONObject newMetadataItem) {
    // Transform the JSON object into a string that the API can use.
    String newItemString = newMetadataItem.toJSONString();

    // Get the list containing all of the Metadata entries for this instance.
    List<Items> items = input.getItems();

    // If the instance has no metadata, items can be returned as null.
    if (items == null)
      items = new LinkedList<Items>();

    // Find the "windows-keys" entry and update it.
    for (Items item : items) {
      if (item.getKey().compareTo("windows-keys") == 0) {
        // Replace item's value with the new entry.
        // To prevent race conditions, production code may want to maintain a
        // list where the oldest entries are removed once the 32KB limit is
        // reached for the metadata entry.

    // "windows.keys" entry doesn't exist in the metadata - append it.
    // This occurs when running password-reset for the first time on an instance.
    items.add(new Items().setKey("windows-keys").setValue(newItemString));

  private KeyPair generateKeys() throws NoSuchAlgorithmException {
    KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");

    // Key moduli for encryption/decryption are 2048 bits long.

    return keyGen.genKeyPair();

  private JSONObject buildKeyMetadata(KeyPair pair) throws NoSuchAlgorithmException,
      InvalidKeySpecException {
    // Object used for storing the metadata values.
    JSONObject metadataValues = new JSONObject();

    // Encode the public key into the required JSON format.

    // Add username and email.
    metadataValues.put("userName", USER_NAME);
    metadataValues.put("email", EMAIL);

    // Create the date on which the new keys expire.
    Date now = new Date();
    Date expireDate = new Date(now.getTime() + EXPIRE_TIME);

    // Format the date to match rfc3339.
    SimpleDateFormat rfc3339Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
    String dateString = rfc3339Format.format(expireDate);

    // Encode the expiration date for the returned JSON dictionary.
    metadataValues.put("expireOn", dateString);

    return metadataValues;

  private JSONObject jsonEncode(KeyPair keys) throws NoSuchAlgorithmException,
      InvalidKeySpecException {
    KeyFactory factory = KeyFactory.getInstance("RSA");

    // Get the RSA spec for key manipulation.
    RSAPublicKeySpec pubSpec = factory.getKeySpec(keys.getPublic(), RSAPublicKeySpec.class);

    // Extract required parts of the key.
    BigInteger modulus = pubSpec.getModulus();
    BigInteger exponent = pubSpec.getPublicExponent();

    // Grab an encoder for the modulus and exponent to encode using RFC 3548;
    // Java SE 7 requires an external library (Google's Guava used here)
    // Java SE 8 has a built-in Base64 class that can be used instead. Apache also has an RFC 3548
    // encoder.
    BaseEncoding stringEncoder = BaseEncoding.base64();

    // Strip out the leading 0 byte in the modulus.
    byte[] arr = Arrays.copyOfRange(modulus.toByteArray(), 1, modulus.toByteArray().length);

    JSONObject returnJson = new JSONObject();

    // Encode the modulus, add to returned JSON object.
    String modulusString = stringEncoder.encode(arr).replaceAll("\n", "");
    returnJson.put("modulus", modulusString);

    // Encode exponent, add to returned JSON object.
    String exponentString = stringEncoder.encode(exponent.toByteArray()).replaceAll("\n", "");
    returnJson.put("exponent", exponentString);

    return returnJson;

Instrucciones manuales

En los pasos de esta guía manual, se usa OpenSSL con las funciones criptográficas y las herramientas de Linux/shell Bash para otras funciones, pero también hay otras implementaciones posibles.

  1. Genera un par de claves RSA de 2,048 bits. En OpenSSL, ejecuta lo siguiente para generar este par de claves:

    $ openssl genrsa -out private_key 2048

    Esto genera un archivo de claves privadas denominado private_key con contenido que luce como lo siguiente:

    $ cat private_key
    -----END RSA PRIVATE KEY-----
  2. Genera una clave pública. Ejecuta lo siguiente para generar la clave pública:

    $ openssl rsa -pubout -in private_key -out public_key

    Esto genera un archivo public_key que luce de la siguiente manera:

    $ cat public_key
    -----BEGIN PUBLIC KEY-----
    -----END PUBLIC KEY-----
  3. Extrae el módulo y el exponente. Un módulo y un exponente conforman las claves pública y privada. De la clave pública, extrae el módulo y el exponente:

    $ openssl rsa -in public_key -pubin -text -noout
    Public-Key: (2048 bit)
    Exponent: 65537 (0x10001)
  4. Codifica el módulo y el exponente. Debes extraer y codificar en Base64 el módulo y el exponente. Antes de codificar el módulo, quita el byte cero inicial. Por configuración predeterminada, el archivo public_key ya es una string de bytes codificada en Base64 que contiene la siguiente información:

    • 32 bytes de información del encabezado
    • 1 byte con el cero inicial del módulo
    • 256 bytes del módulo
    • 2 bytes del encabezado del exponente
    • 3 bytes del exponente

    El módulo y el exponente deben extraerse y codificarse por separado del resto del contenido del archivo. Extrae y codifica el módulo y el exponente con los siguientes comandos:

    $ cat public_key | grep -v -- ----- | base64 -d | dd bs=1 skip=33 count=256 2>/dev/null | base64 -w 0; echo
    $ cat public_key | grep -v -- ----- | base64 -d | dd bs=1 skip=291 count=3 2>/dev/null | base64

    Si tienes problemas para codificar el módulo, asegúrate de haber quitado el byte cero inicial del módulo antes de intentar codificarlo.

  5. Crea un objeto JSON con nombre de usuario y con información de clave pública. Crea un objeto JSON con los siguientes datos:

    • userName: el nombre de usuario para acceder a la instancia.
    • modulus: el módulo de la clave pública codificado en Base64.
    • exponent: el exponente de la clave pública codificado en Base64.
    • email: la dirección de correo electrónico del usuario que solicita la contraseña. Esta debería ser la dirección de correo electrónico de la Cuenta de Google autenticada en la API.
    • expireOn: una marca de tiempo codificada en RFC 3,399 que indica cuándo debe caducar la clave. Esta debe estar en horario UTC, adelantado unos cinco minutos en el futuro. Ya que estas claves solo se usan para generar el nombre de usuario y la contraseña, ya no son necesarias después de crear la contraseña. El agente no usa claves vencidas.

    Como en el siguiente ejemplo:

    {\"userName\": \"example-user\",  \"modulus\": \"wgsquN4IBNPqIUnu+h/5Za1kujb2YRhX1
    4FmOt0R7Q==\", \"exponent\": \"AQAB\", \"email\": \"\",
    \"expireOn\": \"2015-04-14T01:37:19Z\"}

    Ten en cuenta que no debe haber saltos de líneas en la string JSON.

  6. Agrega el objeto JSON a los metadatos de la instancia. Establece los metadatos de la instancia con la clave de metadatos windows-keys y el objeto JSON como el valor de la clave.

    A fin de actualizar los metadatos de la instancia en la API, debes proporcionar una huella digital con tu solicitud. Realiza una solicitud GET a la instancia para obtener la huella digital actual:

    "metadata": {
    "kind": "compute#metadata",
    "fingerprint": "5sFotm8Ee0I=",
    "items": [

    Luego, haz una solicitud POST al método setMetadata, en la que proporciones la huella digital y el objeto JSON que creaste:

     "fingerprint": "5sFotm8Ee0I=",
     "items": [
       "value": "{\"userName\": \"example-user\",  \"modulus\": \"wgsquN4IBNPqIUnu+h/5Za1kujb2YRhX1vCQVQAkBwnWigcCqOBVfRa5JoZfx6KIvEXjWqa77jPvlsxM4WPqnDIM2qiK36up3SKkYwFjff6F2ni/ry8vrwXCX3sGZ1hbIHlK0O012HpA3ISeEswVZmX2X67naOvJXfY5v0hGPWqCADao+xVxrmxsZD4IWnKl1UaZzI5lhAzr8fw6utHwx1EZ/MSgsEki6tujcZfN+GUDRnmJGQSnPTXmsf7Q4DKreTZk49cuyB3prV91S0x3DYjCUpSXrkVy1Ha5XicGD/q+ystuFsJnrrhbNXJbpSjM6sjo/aduAkZJl4FmOt0R7Q==\", \"exponent\": \"AQAB\", \"email\": \"\", \"expireOn': '2015\"04-14T01:37:19Z\"}\n",
       "key": "windows-keys"
      } ]

    El nombre de la clave debe ser windows-keys y el valor debe establecerse en una o más strings de JSON como la anterior. Si hay varias strings, deben separarse con saltos de línea. Cuando agregas muchas entradas, asegúrate de que el valor de los metadatos no supere los 32 KB.

  7. Lee la salida del puerto en serie número cuatro. El agente de la instancia tomará el valor de windows-keys de forma automática y creará una contraseña encriptada. Consulta el puerto en serie número cuatro para leer la contraseña encriptada. En la API, haz una solicitud GET al método getSerialPortOutput y pasa port=4 como parámetro de consulta:

     "kind": "compute#serialPortOutput",
     "selfLink": "",
     "contents": "{\"ready\":true,\"version\":\"Microsoft Windows NT 6.1.7601 Service Pack 1\"}\n{\"encryptedPassword\":\"uiHDEhxyvj6lF5GalH

    La salida del puerto en serie puede contener varias respuestas separadas por saltos de línea. Para encontrar la respuesta correcta, haz coincidir los módulos que pasaste con la salida del puerto en serie. Cada respuesta es una string codificada en JSON con los siguientes campos:

    • userName: el nombre de usuario que se pasó a la instancia.
    • passwordFound: un valor booleano que indica si la generación de la contraseña fue exitosa.
    • encryptedPassword: una contraseña encriptada codificada en Base64.
    • modulus: el módulo que se transfirió con anterioridad.
    • exponent: el exponente que se transfirió con anterioridad.

    Para obtener más información sobre la retención de salida del puerto en serie, consulta Visualiza la salida de puertos en serie.

  8. Desencripta la contraseña. A fin de obtener la contraseña, usa la clave privada ya creada para desencriptar la contraseña encriptada. La contraseña debe desencriptarse con relleno óptimo de encriptación asimétrica (OAEP). En OpenSSL, el comando para desencriptar los datos de entrada es el siguiente:

    $ openssl rsautl -decrypt -inkey private_key -oaep

    Para desencriptar la contraseña anterior, proporciona el valor encryptedPassword. Recuerda quitar los caracteres de escape \\ de la string con anterioridad o la desencriptación fallará:

    $ echo 'uiHDEhxyvj6lF5GalHh9TsMZb4bG6Y9qGmFb9S3XI291MncHcaxP0rFu0kyjxlEXDs8y4L1KOhy6iyB42Lh+vZ4XIMjmvU4rZrjsBZ5Tx
    bvGRJXyswkOJ4jTZl+7e6+SZfEal8HJyRfZKiqTjrz+DLjYSlXrfIRqlvKeAFGOJq6IRojNWiTOOh8Zorc0iHDTIkf+MY0scfbBUo5m30Bf4w==' |
    base64 -d | openssl rsautl -decrypt -inkey private_key -oaep

    El comando muestra la contraseña desencriptada:


    El nombre de usuario y la contraseña de esta cuenta serían los siguientes:

    username: example-user
    password: dDkJ_3]*QYS-#>X
  9. Descarta las claves. A diferencia de las Llaves SSH, las claves que se usan para recuperar/restablecer las contraseñas de Windows son efímeras. No se recomienda volver a usar pares de clave públicas/privadas y puede que no funcione como se espera. Si la clave se guardó en el disco, los archivos deben quitarse al final del proceso. Mejor aún, si es posible, mantén la clave en la memoria y descártala cuando se complete el proceso.

Pasos siguientes