Generazione automatica delle password Windows

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

Il comando gcloud compute reset-windows-password consente a un utente con accesso in scrittura al progetto Compute Engine per recuperare in modo sicuro le password per gli account sulle istanze Windows.

A questo scopo, invia un nome utente e una chiave pubblica RSA all'istanza. L'agente in esecuzione sull'istanza, quindi, esegue una delle seguenti operazioni:

  • Crea un account sull'istanza per il nome utente e genera una password casuale.
  • Reimposta la password su un valore casuale se l'account esiste già.

L'agente in esecuzione sull'istanza cripta la password con la chiave pubblica fornita e la invia al client per essere decriptata dalla chiave privata corrispondente.

Questa sezione descrive il funzionamento del processo e fornisce alcuni script di esempio che replicano questi passaggi in modo programmatico. Se vuoi seguire manualmente questi passaggi, leggi la sezione Istruzioni manuali.

Prima di iniziare

Automatizza la generazione delle password


//  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;

Istruzioni manuali

I passaggi in questa guida manuale utilizzano OpenSSL per le funzioni crittografiche e gli strumenti shell Bash/Linux per alcune altre funzioni, ma sono possibili molte altre implementazioni.

  1. Genera una coppia di chiavi RSA a 2048 bit. In OpenSSL, genera questa coppia di chiavi eseguendo:

    $ openssl genrsa -out private_key 2048

    Viene creato un file di chiave privata denominato private_key con contenuti simili ai seguenti:

    $ cat private_key
    -----END RSA PRIVATE KEY-----
  2. Genera una chiave pubblica. Per creare la chiave pubblica, esegui:

    $ openssl rsa -pubout -in private_key -out public_key

    In questo modo viene creato un file public_key simile al seguente:

    $ cat public_key
    -----BEGIN PUBLIC KEY-----
    -----END PUBLIC KEY-----
  3. Estrai modulo e esponente. Un modulo e un esponente compongono le chiavi pubbliche e private. Dalla chiave pubblica, estrai il modulo e l'esponente:

    $ openssl rsa -in public_key -pubin -text -noout
    Public-Key: (2048 bit)
    Exponent: 65537 (0x10001)
  4. Codifica il modulo e l'esponente. Devi estrarre e codificare in base 64 il modulo e l'esponente. Prima di codificare il modulo, rimuovi lo zero iniziale iniziale dal modulo. Per impostazione predefinita, il file public_key è già una stringa codificata in base64 di byte contenente le seguenti informazioni:

    • 32 byte di informazioni sull'intestazione
    • 1 byte con lo zero iniziale del modulo
    • 256 byte di modulo
    • 2 byte di intestazione esponente
    • 3 byte di esponente

    Il modulo e l'esponente devono essere estratti e codificati separatamente dagli altri contenuti del file. Estrai e codifica il modulo e l'esponente utilizzando i seguenti comandi:

    $ 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

    Se stai riscontrando problemi con la codifica del modulo, assicurati di aver rimosso lo zero byte iniziale dal modulo prima di tentare di codificarlo.

  5. Crea un oggetto JSON con le informazioni di un nome utente e di una chiave pubblica. Crea un oggetto JSON con i seguenti dati:

    • userName: il nome utente per accedere all'istanza.
    • modulus: il modulo con codifica Base64 della chiave pubblica.
    • exponent: l'esponente codificato in base64 della chiave pubblica
    • email: l'indirizzo email dell'utente che richiede la password. Deve essere l'indirizzo email dell'Account Google autenticato all'API.
    • expireOn: un timestamp con codifica RFC 3399 che indica la scadenza della chiave. Questa dovrebbe essere nel fuso orario UTC, imposta circa cinque minuti nel futuro. Poiché queste chiavi vengono utilizzate solo per generare il nome utente e la password, non sono più necessarie dopo la creazione della password. L'agente non utilizza chiavi scadute.

    Ad esempio:

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

    Tieni presente che non devono esserci nuove righe nella stringa JSON.

  6. Aggiungi l'oggetto JSON ai metadati dell'istanza. Imposta i metadati dell'istanza utilizzando la chiave dei metadati windows-keys e l'oggetto JSON come valore della chiave.

    Per aggiornare i metadati dell'istanza nell'API, devi fornire un'impronta con la tua richiesta. Recupera l'impronta corrente dell'istanza effettuando una richiesta GET all'istanza:

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

    Successivamente, effettua una richiesta POST al metodo setMetadata, fornendo l'impronta e l'oggetto JSON che hai creato:

     "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"
      } ]

    Il nome della chiave deve essere windows-keys e il valore deve essere impostato su una o più stringhe JSON come quella sopra. Più stringhe devono essere separate da caratteri di nuova riga. Quando aggiungi più voci, assicurati che il valore dei metadati non superi i 32 kB.

  7. Leggi l'output dalla porta seriale numero quattro. L'agente dell'istanza prenderà automaticamente il valore windows-keys e creerà una password criptata. Leggere la password criptata eseguendo una query sulla porta seriale numero quattro. Nell'API, esegui una richiesta GET al metodo getSerialPortOutput, trasmettendo port=4 come parametro di ricerca:

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

    L'output della porta seriale può contenere più risposte, separate da caratteri di nuova riga. Per trovare la risposta corretta, abbina il modulo che hai passato all'output della porta seriale. Ogni risposta è una stringa codificata JSON con i seguenti campi:

    • userName: il nome utente trasmesso all'istanza.
    • passwordFound: un valore booleano che indica se la generazione della password è riuscita.
    • encryptedPassword: una password criptata in base64.
    • modulus: il modulo trasmesso in precedenza.
    • exponent: l'esponente trasmesso in precedenza.

    Per informazioni sulla conservazione dell'output della porta seriale, consulta Visualizzare l'output della porta seriale.

  8. Decripta la password. Per recuperare la password, utilizza la chiave privata creata in precedenza per decriptare la password criptata. La password deve essere decriptata con Optimal Asymmetric Encryption Padding (OAEP). Per OpenSSL, il comando per decriptare i dati di input è:

    $ openssl rsautl -decrypt -inkey private_key -oaep

    Per decriptare la password riportata sopra, fornisci il valore encryptedPassword. Ricorda di rimuovere i caratteri di escape \\ dalla stringa in anticipo, altrimenti la decriptazione non riuscirà:

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

    Il comando stampa la password decriptata:


    Il nome utente e la password per questo account saranno:

    username: example-user
    password: dDkJ_3]*QYS-#>X
  9. Togli le chiavi. A differenza delle chiavi SSH, le chiavi utilizzate per il recupero/reimpostazione delle password di Windows sono destinate a essere temporanee. Ti consigliamo di non riutilizzare le coppie di chiavi pubbliche/private perché potrebbero non funzionare come previsto. Se la chiave è stata salvata su disco, i file devono essere eliminati alla fine del processo. Meglio ancora, se possibile, conserva la chiave in memoria e eliminala al termine del processo.

Passaggi successivi