El Blog de Gualtrysoft

Windows 2000/2003/2008, Active Directory, VBScript, Hyper-V, PowerShell y todo aquello interesante a la hora de usar, configurar y administrar Windows Server. También tenemos longanizas…

PowerShell: Cómo Almacenar de Manera Segura una Contraseña y Utilizarla desde un Script

Posted by urpiano en Viernes 20 \20\UTC julio \20\UTC 2012

Un problema típico de los scripts es cuando se necesita poner una contraseña para realizar determinada tarea. Esa contraseña se escribe como texto plano, con lo que cualquiera que tenga acceso al código podrá averiguar cuál es esa contraseña ¿Cómo podemos evitar que esta contraseña sea visible para cualquiera?

En VbScript, por ejemplo, se puede cifrar el código del script, para que así no sea legible la contraseña, pero, como se ve en esta entrada de este mismo blog, no es una solución ni mucho menos definitiva, pudiendo averiguar la contraseña, al descifrar el código, cualquier usuario un poquillo despierto.

Una forma de evitar este problema es que la contraseña sea pedida de forma interactiva al usuario que lanza el script, pero no se trata de una solución válida cuando el script debe ser lanzado de forma programada, caso en el que debe ser invocada desde el código, siempre y cuando no valga con usar las credenciales del usuario que lanza la tarea programada y por tanto nos sea necesario entrar credenciales alternativas (por ejemplo, podemos querer conectar a un LDAP externo). Veremos cómo se puede hacer esto de forma segura en dos supuestos distintos:

Tanto en un caso como en otro, nos basaremos en entrar de forma interactiva la contraseña y almacenarla en un fichero cifrado, desde el cual se recuperará por parte del script para poder ser utilizada.

Pasar credenciarles como objeto System.Management.Automation.PSCredential

Veamos ahora el primer caso. Pongamos que nuestro script realiza una consulta WMI (en el ejemplo para obtener información sobre el sistema operativo) a un equipo remoto perteneciente a otro dominio que no es de confianza y que por tanto necesitamos pasar las credenciales de un usuario del dominio de destino. De forma interactiva esto lo realizaríamos de una de estas dos formas:

Get-WmiObject -Class Win32_OperatingSystem `
              -ComputerName bacteriosrv.tia.org `
              -Credential (Get-Credential)
              
Get-WmiObject -Class Win32_OperatingSystem `
              -ComputerName bacteriosrv.tia.org `
              -Credential (Get-Credential "TIA\Bacterio")

Ambas forma obtendrían el mismo resultado, la información del sistema operativo del equipo bacteriosrv.tia.org, sólo que en el primero se presentará un cuadro de diálogo de entrada de credenciales, con las cajas de texto de usuario y de contraseña en blanco, con lo que habría que entrar el nombre de usuario y la contraseña, y en el segundo la caja de texto de usuario estaría rellena con TIA\Bacterio y sólo habría que entrar la contraseña. Esto mismo se puede realizar asignando el resultado de Get-Credential a una variable y pasando esa variable como parámetro -Credential:

# Almacenamos la contraseña
$Credenciales = Get-Credential "TIA\Bacterio"
Get-WmiObject -Class Win32_OperatingSystem `
              -ComputerName bacteriosrv.tia.org `
              -Credential $Credenciales

¿Cómo podemos crear este objeto credential de forma no interactiva? El problema estriba en suministrar la contraseña, ya que no se puede establecer como parámetro con Get-Credential, y por tanto no podemos leerla desde un fichero o establecerla directamente en el script. La forma de crear una credencial sin usar Get-Credential es usando New-Object para crear un objeto de tipo System.Management.Automation.PSCredential y pasando la contraseña como cadena segura (System.Security.SecureString), no como texto plano. Vamos a ver esto por partes.

Lo primero es guardar la contraseña en un fichero, para ello la mejor forma es obteniendo la contraseña como cadena segura (System.Security.SecureString), cifrarla y guardar el resultado en un fichero:

# Para obtener la contraseña como cadena segura, solicitamos que el usuario la
# entre de forma interactiva con Read-Host, al que pondremos el modificador 
# AsSecureString para que no sea almacenada como System.String, si no como
# System.Security.SecureString, que es el tipo que se debe pasar al constructor
# del objeto System.Management.Automation.PSCredential
$Password = Read-Host "Entre la contraseña" -AsSecureString

# Ciframos la cadena con ConvertFrom-SecureString. Si lo hacemos así
# sólo el usuario que ha creado el fichero podrá descifrarlo posteriormente
$PasswordCifrada = ConvertFrom-SecureString $Password

# Si necesitamos que otros usuarios puedan descifrar el fichero, es necesario
# que establezcamos la clave de encriptación, que se trata de un array de bytes.
# En el ejemplo pondremos 32 bytes con los valores de 1 a 32
#$PasswordCifrada = ConvertFrom-SecureString $Password -Key (1..32)

# Una vez hemos cifrado la cadena segura, podemos guardar el contenido en un
# fichero
Set-Content -Path .\Password.txt -Value $PasswordCifrada

Para usar esta contraseña así almacenada en fichero, cargaríamos la cadena cifrada leyendola desde el fichero, la convertiríamos en cadena segura y crearíamos el objeto System.Management.Automation.PSCredential utilizando New-Object, declarando como tipo de objeto System.Management.Automation.PSCredential y pasándole al constructor el nombre del usuario, en texto plano y la contraseña como cadena segura:

# Obtenemos la cadena cifrada cargándola desde el fichero
$PasswordCifrada = Get-Content .\Password.txt

# Desciframos la cadena. En este caso, es descifrada por el mismo
# usuario que la cifró
$Password = ConvertTo-SecureString $PasswordCifrada

# Si hubiesemos especificado la clave de cifrado, para así poder
# descifrar la cadena segura por cualquier usuario que conociese la clave,
# tendríamos que utilizar el parámetro Key y pasarle el array de Bytes que
# componen la clave; en este ejemplo, al igual que hicimos al cifrar
# anteriormente, pondremos 32 bytes con los valores de 1 a 32
#$Password = ConvertTo-SecureString $PasswordCifrada -Key (1..32)

# Ya podemos crear el objeto System.Management.Automation.PSCredential
$Credenciales = New-Object System.Management.Automation.PSCredential `
                           "TIA\Bacterio",$Password

# Una vez creadas las credenciales, las podemos usar con Get-WmiObject
Get-WmiObject -Class Win32_OperatingSystem `
              -ComputerName bacteriosrv.tia.org `
              -Credential $Credenciales

Como podemos ver, usamos ConvertFrom-SecureString para convertir la cadena segura en una cadena cifrada. Para convertir en cadena segura la cadena cifrada usamos ConvertTo-SecureString; este Cmdlet también nos permite convertir texto plano en cadena segura si se utiliza el modificador AsPlainText, que implica usar también el modificador Force (sirve para confirmar que uno entiende las implicaciones de usar texto plano y que aún así se quiere usar). Es mucho suponer, pero supongamos que en un script necesitamos pasar un objeto System.Management.Automation.PSCredential y que no nos preocupa que se pueda ver la contraseña al editarlo; con AsPlainText y Force podemos usar ConvertTo-SecureString para crear la cadena segura:

# Obtenemos la cadena segura directamente desde la contraseña en texto plano;
# por supuesto esto no es nada deseable y sólo lo pongo por completar la
# información de este artículo
$Password = ConvertTo-SecureString "OfeliaFoca" -AsPlainText -Force

# Creamos el objeto System.Management.Automation.PSCredential
$Credenciales = New-Object System.Management.Automation.PSCredential `
                           "TIA\Bacterio",$Password

# Una vez creadas las credenciales, las podemos usar con Get-WmiObject
Get-WmiObject -Class Win32_OperatingSystem `
              -ComputerName bacteriosrv.tia.org `
              -Credential $Credenciales

Pasar credenciales como texto plano

En ocasiones, necesitaremos pasar credenciales como texto plano a determinados constructores de objetos o Cmdlets. Por ejemplo, el constructor DirectoryEntry (Constructor) (String, String, String) de un objeto System.DirectoryServices.DirectoryEntry recibe tres parámetros de tipo cadena, siendo el primero la ruta LDAP, el segundo el nombre de usuario con el que se conecta y el tercero la contraseña, en texto plano, de conexión. Si esta contraseña no puede ser pedida de forma interactiva, tendremos un problema de seguridad con nuestro script, pues pondríamos algo así:

# Establecemos la ruta LDAP del objeto de active directory a obtener
$adPath = "LDAP://cn=Ofelia,OU=Focas,OU=Secretarias,DC=tia,dc=org"
# Establecemos el nombre del usuario de conexión
$User = "TIA\Bacterio"
# Establecemos la contraseña (¡¡MUY MAL, ESTO NO SE DEBE HACER!!)
$Passw = "OfeliaFoca"
# Creamos el objeto DirectoryEntry usando los datos que hemos establecido
$Foca = New-Object System.DirectoryServices.DirectoryEntry($adPath,$User,$Passw)

Con los mecanismos de ocultación que vimos en el anterior punto, podíamos usar una cadena segura, pero no texto plano, para cifrar la contraseña y guardarla en un fichero. En este caso necesitamos trabajar con texto plano ¿cómo podremos hacer? La respuesta la encontramos en la clase System.Runtime.InteropServices.Marshal. Esta clase <pego>Proporciona una colección de métodos para asignar memoria no administrada, copiar bloques de memoria no administrados y convertir los tipos administrados en no administrados, así como otros métodos diversos que se utilizan al interactuar con código no administrado</pego>. Por tanto, se encarga del manejo de la memoria por parte del código no administrado y permite interactuar entre código administrado y código no administrado (el código no administrado es código proveniente de la importación de librerías y que no está basado en Common Language Runtime (CLI), es decir, código no .NET; el código no administrado compila directamente en lenguaje máquina en lugar de hacerlo en Intermediate Language (IL), como hacen los lenguages .NET).

Gracias a la clase System.Runtime.InteropServices.Marshal vamos a poder obtener la contraseña en texto plano a partir de una cadena segura, con lo que el mecanismo será el mismo que usábamos en el punto anterior con el añadido final de usar esta clase para convertir la cadena segura en texto plano. Para realizar esto será necesario usar los siguientes métodos de la clase System.Runtime.InteropServices.Marshal:

  • SecureStringToCoTaskMemUnicode: este método carga en un bloque de memoria la cadena segura, como caracteres Unicode, desde el localizador COM no administrado de tareas. La devolución de esta función es un puntero. Se podría usar el método SecureStringToCoTaskMemAnsi, si bien no es deseable, porque restringe a ANSI los caracteres a utilizar, con lo que si el usuario escribiera un caracter no ANSI la contraseña no funcionaría, al haber sido sustituido ese caracter por una interrogación.
  • PtrToStringUni: esta función devuelve una cadena administrada con el contenido de una cadena no administrada, que es recibida como puntero.
  • ZeroFreeCoTaskMemUnicode: este método libera de memoria una cadena no administrada que fue cargada usando el método SecureStringToCoTaskMemUnicode. Se usa este método para liberar la memoria que fue usada al cargar la cadena segura como cadena no administrada.

ConvertTo-UnsecureString

Como hemos visto antes, con la clase System.Runtime.InteropServices.Marshal podemos obtener la cadena en texto plano de una cadena segura. Así pues, para realizar esta tarea de forma cómoda podemos crear un Cmdlet, al que llamaremos ConvertTo-UnsecureString y que recibirá una cadena segura como parámetro.

El Cmdlet incluye ayuda, pego a continuación la ayuda con modificador -Detailed (esta ayuda se basa en la prestación de ayuda basada en comentarios de PowerShell 2.0 ¿Qué, todavía tienes la versión 1.0 y no la 2.0? ¿A qué esperas para instalarla si es mucho mejor?):

NOMBRE
    ConvertTo-UnsecureString
    
SINOPSIS
    Recibe una cadena segura y la devuelve como texto plano
    
SINTAXIS
    ConvertTo-UnsecureString [[-SecureString] <SecureString>] [<CommonParameters>]
    
DESCRIPCIÓN
    Esta función permite obtener el texto plano de la cadena segura que
    recibe como parámetro.

PARÁMETROS
    -SecureString <SecureString>
        Cadena segura (System.Security.SecureString) de la que se quiere obtener
        su texto plano unicode. Admite los alias -SS, -CS y -S.
        
    <CommonParameters>
        Este cmdlet admite los parámetros comunes Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer y OutVariable. Para obtener más información, escriba:
        "get-help about_commonparameters".
    
    -------------------------- EJEMPLO 1 --------------------------
    
    PS C:\>$Pass = ConvertTo-SecureString "OfeliaFoca" -AsPlainText -Force    
    PS C:\> ConvertTo-UnsecureString $Pass
    OfeliaFoca
    
    En este ejemplo, convertimos a SecureString la cadena "OfeliaFoca" y la
    cadena segura resultante la pasamos a ConvertTo-UnsecureString, que nos devolverá
    el valor que establecimos en la primera línea
    
    -------------------------- EJEMPLO 2 --------------------------
    
    PS C:\>$Pass = Read-Host "Introduzca su contraseña" -AsSecureString    
    PS C:\> $cifrada = ConvertFrom-SecureString $Pass
    PS C:\> Set-Content -Path .\Password.txt -Value $cifrada
    PS C:\> $Pass = ConvertTo-SecureString (Get-Content .\Password.txt)
    PS C:\> $adPath = "cn=Ofelia,ou=Focas,ou=Secretarias,dc=tia,dc=org"
    PS C:\> $User = "TIA\Bacterio"
    PS C:\> $DE = New-Object System.DirectoryServices.DirectoryEntry( `
    >> $adPath,$User,(ConvertTo-UnsecureString $Pass))
    
    En este ejemplo se le solicita al usuario que entre una contraseña, se
    encripta la misma y se guarda en un fichero. Posteriormente, se lee el fichero y
    el contenido se transforma en cadena; por último, se crea un objeto
    DirectoryEntry al que se le pasa como contraseña la conversión a texto plano de
    la cadena segura. Esto es ideal para pensarlo en dos partes; la primera, la de
    creación del fichero con la contraseña cifrada;  la segunda, la de lectura
    del fichero y obtención de la contraseña como texto plano para poder ser usada
    en un script sin tener que ponerla de forma que pueda ser leída.
    
    -------------------------- EJEMPLO 3 --------------------------
    
    PS C:\>$Cred = Get-Credential    
    PS C:\> $adPath = "cn=Ofelia,ou=Focas,ou=Secretarias,dc=tia,dc=org"
    PS C:\> $DE = New-Object System.DirectoryServices.DirectoryEntry( `
    >> $adPath,$Cred.UserName,(ConvertTo-UnsecureString $Cred.Password))
    
    En este ejemplo se le solicita al usuario que entre unas credenciales
    con las que se conectará a AD para obtener un objeto DirectoryEntry,
    convirtiendo la propiedad Password de las credenciales, de tipo SecureString, en
    texto plano Unicode.
    
    -------------------------- EJEMPLO 4 --------------------------
    
    PS C:\>$Cred = Get-Credential    
    PS C:\> $Usr = ConvertTo-SecureString $Cred.UserName -AsPlainText -Force
    PS C:\> Set-Content -Path .\Credenciales.cred `
    >> -Value (ConvertFrom-SecureString $Usr)
    PS C:\> Add-Content -Path .\Credenciales.cred `
    >> -Value (ConvertFrom-SecureString $Cred.Password)
    PS C:\> $UsrSec = ConvertTo-SecureString (Get-Content .\Credenciales.cred)[0]
    PS C:\> $Usr = ConvertTo-UnsecureString $UsrSec
    PS C:\> $PassSec = ConvertTo-SecureString (Get-Content .\Credenciales.cred)[1]
    PS C:\> $Pass = ConvertTo-UnsecureString $PassSec
    PS C:\> $adPath = "cn=Ofelia,ou=Focas,ou=Secretarias,dc=tia,dc=org"
    PS C:\> $DE = New-Object System.DirectoryServices.DirectoryEntry( `
    >> $adPath,$Usr,$Pass)
    
    En este ejemplo se le solicita al usuario que entre unas credenciales.
    Estas credenciales son guardadas como texto cifrado, el nombre de usuario en
    la primera línea del fichero y la contraseña en la segunda. Posteriormente, se
    lee la primera línea de fichero convirtiendola en cadena segura, con lo que
    tendremos una cadena segura con el nombre de usuario, a la que convertimos en
    texto plano; después se hace lo mismo con la segunda línea, con lo que se
    obtendrá la contraseña; es muy importante el realizar esto línea por línea, ya
    que cada una ha sido cifrada por separado y por separado deben de ser
    descifradas. Se define tambien la ruta LDAP de un objeto de Active Directory
    y con esos tres datos se obtendrá un objeto DirectoryEntry.

Este es el código del Cmdlet ConvertTo-UnsecureString:

Function ConvertTo-UnsecureString(
            [Alias("SS","CS","S")]
            [System.Security.SecureString] $SecureString)
{
    # Cargamos la cadena segura recibida como parámetro como cadena no
    # unicode no administrada
    $Puntero = `
     [System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode( `
                                                                  $SecureString)

    # El puntero obtenido de la cadena no administrada lo pasamos a
    # PtrToStringUni para obtener la cadena en texto plano unicode
    [System.Runtime.InteropServices.Marshal]::PtrToStringUni($Puntero)

    # Vaciamos de la memoria la cadena no administrada
    [System.Runtime.InteropServices.Marshal]::ZeroFreeCoTaskMemUnicode($Puntero)

<#
    .SYNOPSIS
        Recibe una cadena segura y la devuelve como texto plano

    .DESCRIPTION
        Esta función permite obtener el texto plano de la cadena segura que
recibe como parámetro.

    .PARAMETER  SecureString
        Cadena segura (System.Security.SecureString) de la que se quiere obtener
su texto plano unicode. Admite los alias -SS, -CS y -S.

    .EXAMPLE
        PS C:\> $Pass = ConvertTo-SecureString "OfeliaFoca" -AsPlainText -Force
        PS C:\> ConvertTo-UnsecureString $Pass
        OfeliaFoca

        En este ejemplo, convertimos a SecureString la cadena "OfeliaFoca" y la
cadena segura resultante la pasamos a ConvertTo-UnsecureString, que nos devolverá
el valor que establecimos en la primera línea

    .EXAMPLE
        PS C:\> $Pass = Read-Host "Introduzca su contraseña" -AsSecureString
        PS C:\> $cifrada = ConvertFrom-SecureString $Pass
        PS C:\> Set-Content -Path .\Password.txt -Value $cifrada
        PS C:\> $Pass = ConvertTo-SecureString (Get-Content .\Password.txt)
        PS C:\> $adPath = "cn=Ofelia,ou=Focas,ou=Secretarias,dc=tia,dc=org"
        PS C:\> $User = "TIA\Bacterio"
        PS C:\> $DE = New-Object System.DirectoryServices.DirectoryEntry( `
        >> $adPath,$User,(ConvertTo-UnsecureString $Pass))
        
        En este ejemplo se le solicita al usuario que entre una contraseña, se
encripta la misma y se guarda en un fichero. Posteriormente, se lee el fichero y
el contenido se transforma en cadena; por último, se crea un objeto
DirectoryEntry al que se le pasa como contraseña la conversión a texto plano de
la cadena segura. Esto es ideal para pensarlo en dos partes; la primera, la de
creación del fichero con la contraseña cifrada;  la segunda, la de lectura
del fichero y obtención de la contraseña como texto plano para poder ser usada
en un script sin tener que ponerla de forma que pueda ser leída.

    .EXAMPLE
        PS C:\> $Cred = Get-Credential
        PS C:\> $adPath = "cn=Ofelia,ou=Focas,ou=Secretarias,dc=tia,dc=org"
        PS C:\> $DE = New-Object System.DirectoryServices.DirectoryEntry( `
        >> $adPath,$Cred.UserName,(ConvertTo-UnsecureString $Cred.Password))
        
        En este ejemplo se le solicita al usuario que entre unas credenciales
con las que se conectará a AD para obtener un objeto DirectoryEntry,
convirtiendo la propiedad Password de las credenciales, de tipo SecureString, en
texto plano Unicode.

    .EXAMPLE
        PS C:\> $Cred = Get-Credential
        PS C:\> $Usr = ConvertTo-SecureString $Cred.UserName -AsPlainText -Force
        PS C:\> Set-Content -Path .\Credenciales.cred `
        >> -Value (ConvertFrom-SecureString $Usr)
        PS C:\> Add-Content -Path .\Credenciales.cred `
        >> -Value (ConvertFrom-SecureString $Cred.Password)
        PS C:\> $UsrSec = ConvertTo-SecureString (Get-Content .\Credenciales.cred)[0]
        PS C:\> $Usr = ConvertTo-UnsecureString $UsrSec
        PS C:\> $PassSec = ConvertTo-SecureString (Get-Content .\Credenciales.cred)[1]
        PS C:\> $Pass = ConvertTo-UnsecureString $PassSec
        PS C:\> $adPath = "cn=Ofelia,ou=Focas,ou=Secretarias,dc=tia,dc=org"
        PS C:\> $DE = New-Object System.DirectoryServices.DirectoryEntry( `
        >> $adPath,$Usr,$Pass)
        
        En este ejemplo se le solicita al usuario que entre unas credenciales.
Estas credenciales son guardadas como texto cifrado, el nombre de usuario en
la primera línea del fichero y la contraseña en la segunda. Posteriormente, se
lee la primera línea de fichero convirtiendola en cadena segura, con lo que
tendremos una cadena segura con el nombre de usuario, a la que convertimos en
texto plano; después se hace lo mismo con la segunda línea, con lo que se
obtendrá la contraseña; es muy importante el realizar esto línea por línea, ya
que cada una ha sido cifrada por separado y por separado deben de ser
descifradas. Se define tambien la ruta LDAP de un objeto de Active Directory
y con esos tres datos se obtendrá un objeto DirectoryEntry.

    .INPUTS
        System.Security.SecureString

    .OUTPUTS
        System.String

    .NOTES
        Fernando Reyes López © 07/2012

    .LINK
        http://freyes.svetlian.com

    .LINK
        https://urpiano.wordpress.com

#>

}

Supongamos que tenemos creado un fichero con la contraseña del usuario cifrada, tal y como vimos anteriormente; la forma de usarlo para suministrar la contraseña al constructor de la clase DirectoryEntry, gracias al uso de la función anterior, sería la siguiente:

# Obtenemos la cadena segura cifrada cargándola desde el fichero
$PasswordCifrada = Get-Content .\Password.txt

# Desciframos la cadena segura. En este caso, se desencripta por el mismo
# usuario que la encriptó
$Password = ConvertTo-SecureString $PasswordCifrada

# Si hubiesemos especificado la clave de encriptación, para así poder
# descifrar la cadena segura por cualquier usuario que conociese la clave,
# tendríamos que ustilizar el parámetro Key y pasarle el array de Bytes que
# componen la clave; en este ejemplo, al igual que hicimos al cifrar
# anteriormente, pondremos 32 bytes con los valores de 1 a 32
#$Password = ConvertTo-SecureString $PasswordCifrada -Key (1..32)

# Obtenemos la contraseña en texto plano
$PasswordEnTextoPlano = ConvertTo-UnsecureString $Password

# Ya podemos crear el objeto DirectoryEntry
$Ofelia = New-Object System.DirectoryServices.DirectoryEntry( `
                     "LDAP://cn=Ofelia,ou=Focas,ou=Secretaris,dc=tia,dc=org", `
                     "TIA\Bacterio", `
                     $PasswordEnTextoPlano)

Si pensamos que el ejemplo anterior es parte de un script, podemos observar cómo no se puede saber la contraseña que será utilizada, sólo el fichero que la alberga, y que la contraseña sólo puede ser averiguada por el usuario que la encriptó, a no ser que se estableciera clave en cuyo caso es necesario conocer dicha clave. Dicho esto ¿Por qué no hacer esto mismo con el nombre de usuario? Sería mucho mejor si ni siquiera se pudiese saber el nombre del usuario. Gracias a la función anterior podemos perfectamente tener el nombre de usuario también almacenado en un fichero cifrado y de esa manera que no se pueda ver ni el usuario ni la contraseña. Con este espíritu he creado las dos funciones que expongo en el siguiente punto.

Dos Cmdlets para trabajar con credenciales almacenadas

He creado estos dos Cmdlets con la idea de simplificar el mecanismo de guardar unas credenciales (usuario y contraseña). Los dos Cmdlets son:

Ambos Cmdlets necesitan que esté definido también el Cmdlet ConvertTo-UnsecureString que hemos visto antes, pues realizan llamadas al mismo.

New-CredentialFile

Este Cmdlet crea un fichero de texto que está compuesto de dos líneas; la primera es el nombre de usuario cifrado y la segunda la contraseña, también cifrada. La función de este Cmdlet es crear el fichero para que después pueda ser utilizado en un script que incluya el Cmdlet Get-CredentialFromFile y así no poner en texto plano ni el usuario ni la contraseña en dicho script. Este Cmdlet necesita que esté definido también el Cmdlet ConvertTo-UnsecureString, que hemos visto antes, pues realiza llamadas al mismo.

El Cmdlet incluye ayuda, pego a continuación la ayuda con modificador -Detailed (esta ayuda se basa en la prestación de ayuda basada en comentarios de PowerShell 2.0 ¿Qué, todavía tienes la versión 1.0 y no la 2.0? ¿Qué tal se vive en el Medievo?):

NOMBRE
    New-CredentialFile
    
SINOPSIS
    Genera un fichero con los datos de unas credenciales (usuario y
    contraseña) cifrados.   
    
SINTAXIS
    New-CredentialFile [-Path] <String> [[-UserName] <Object>] [[-Password] <Object>] [[-Key] <Byte[]
    >] [<CommonParameters>]
    
DESCRIPCIÓN
    Esta función genera un fichero con los datos de unas credenciales
    (usuario y contraseña) cifrados. El fichero constará de dos líneas, la
    primera con el nombre de usuario y la segunda con la contraseña. Permite pasar o
    no el nombre de usuario o la contraseña, solicitando al usuario que entre el/los
    dato/s que falte/n de forma interactiva. Permite también especificar una clave
    de cifrado, por si se quiere que el fichero generado pueda ser descifrado
    por cualquier usuario que conozca la clave; si no se suministra la clave, el
    fichero sólo podrá ser descifrado por el usuario que lo creó.

PARÁMETROS
    -Path <String>
        Ruta y nombre del fichero que se generará. Puede tratarse de una ruta
        absoluta o relativa. Admite los alias "FileName", "Fichero", "F", "P", "Ruta" y
        "R".
        
    -UserName <Object>
        Nombre de usuario que se almacenará, cifrado, en el fichero que se
        genera. Puede tratarse de una cadena (System.String) o de una cadena segura
        (System.Security.SecureString). Si se omite y se omite la contraseña, se
        presentará un cuadro de diálogo de solicitud de credenciales (presentado por el
        Cmdlet Get-Credential); si se omite, pero no se omite la contraseña, se
        solicitará el nombre de usuario por medio del Cmdlet Read-Host. Admite los alias
        "User", "Usuario" y "U".
        
    -Password <Object>
        Contraseña que se almacenará, cifrada, en el fichero que se genera.
        Puede tratarse de una cadena (System.String) o de una cadena segura
        (System.Security.SecureString). Si se omite y se omite el nombre de usuario, se
        presentará un cuadro de diálogo de solicitud de credenciales (presentado por el
        Cmdlet Get-Credential); si se omite, pero no se omite el nombre de usuario, se
        solicitará el nombre de usuario también por medio del Cmdlet Get-Credential,
        pero esta vez el usuario estará ya presente en la caja de texto correspondiente,
        teniendo el usuario que entrar nada más que la contraseña. Admite el alias
        "Pass".
        
    -Key <Byte[]>
        Clave de encriptación consistente en un array de Bytes, array que debe
        ser de 16, 24 o 32 bytes, cualquier otra longitud provocará que se ignore la
        clave. Si no se pasa este parámetro, sólo el usuario que crea el fichero será
        capaz de descifrarlo posteriormente, si se pasa la clave, todo usuario que
        conozca la clave será capaz de descifrar el fichero. Esto es un problema,
        pues para el que lea un script que obtiene las credenciales a partir del fichero
        generado y vea la clave podrá obtener las credenciales; así pues, este no
        debería ser un parámetro utilizado más que en aquellos extremos en los que no
        haya otro remedio, en cuyo caso es muy probable que no sea necesario realizar
        todo este mecanismo de llevar los datos a un fichero cifrado para
        posteriormente recuperarlos en un script.
        
    <CommonParameters>
        Este cmdlet admite los parámetros comunes Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer y OutVariable. Para obtener más información, escriba:
        "get-help about_commonparameters".
    
    -------------------------- EJEMPLO 1 --------------------------
    
    PS C:\>New-CredentialFile -Path .\bacterio.cred
    
    En este ejemplo, se le solicitará al usuario que entre usuario y contraseña, en
    el cuadro de diálogo que presenta Get-Credential, y con los datos suministrados
    creará el fichero c:\bacterio.cred.
    
    -------------------------- EJEMPLO 2 --------------------------
    
    PS C:\>New-CredentialFile -F .\bacterio.cred -UserName "TIA\Bacterio"
    
    En este ejemplo, se le solicitará al usuario que entre la contraseña, en el
    cuadro de diálogo que presenta Get-Credential, en el que estará el nombre de
    usuario "TIA\Bacterio"; con los datos suministrados creará el fichero
    c:\bacterio.cred.
    
    -------------------------- EJEMPLO 3 --------------------------
    
    PS C:\>$Clave = (23,14,56,76,213,115,1,47,12,67,98,167,113,215,19,87)    
    PS C:\> New-CredentialFile -P .\bacterio.cred -Key $Clave

    En este ejemplo, se le solicitará al usuario que entre usuario y contraseña, en
    el cuadro de diálogo que presenta Get-Credential, y con los datos suministrados
    creará el fichero c:\bacterio.cred, utilizando como clave de encriptación la 
    clave de 16 bytes definida en la variable $Clave.
    
    -------------------------- EJEMPLO 4 --------------------------
    
    PS C:\>New-CredentialFile -R .\bacterio.cred -Password "OfeliaFoca"
    
    En este ejemplo, se le solicitará al usuario que entre el nombre de usuario,
    solicitud en consola hecha con Read-Host, y con los datos suministrados creará
    el fichero c:\bacterio.cred.
    
    -------------------------- EJEMPLO 5 --------------------------
    
    PS C:\>$User = Read-Host "¿Usuario?" -AsSecureString    
    PS C:\> $Pass = Read-Host "¿Contraseña?" -AsSecureString
    PS C:\> New-CredentialFile -F .\bacterio.cred -Pass $Pass -U $User

    En este ejemplo, se le solicitará al usuario que entre el nombre de usuario y la
    contraseña, solicitudes en consola hechas con Read-Host, y con los datos
    suministrados, ambos son cadenas seguras gracias al uso del modificador
    -AsSecureString del Cmdlet Read-Host, creará el fichero c:\bacterio.cred.

Este es el código del Cmdlet New-CredentialFile:

Function New-CredentialFile([parameter(Mandatory=$true)]
                            [Alias("FileName","Fichero","F","P","Ruta","R")]
                            [System.String]$Path,
                            [Alias("User","Usuario","U")]
                            $UserName,
                            [Alias("Pass")]
                            $Password,
                            [Alias("Clave","C")]
                            [System.Byte[]] $Key)
{
    # Vamos a revisar qué tipo de dato viene como nombre de usuario, cuando éste
    # no está vacío.
    If([System.String]::IsNullOrEmpty($UserName) -eq $false)
    {
        # Si el tipo de dato del nombre de usuario es texto plano
        If($UserName.GetType().FullName -eq "System.String")
        {
            # Obtenemos la cadena segura con el nombre de usuario y la guardamos
            $UserNameSec = ConvertTo-SecureString $UserName -AsPlainText -Force
            # Almacenamos el nombre de usuario recibido como nombre en texto
            # plano
            $UserNameUnsecure = $UserName
        }
        # Si el tipo de dato del nombre de usuario es cadena segura
        ElseIf($UserName.GetType().FullName -eq "System.Security.SecureString")
        {
            # Almacenamos el nombre de usuario recibido como cadena segura
            $UserNameSec = $UserName
            # Obtenem el texto plano con el nombre de usuario y lo guardamos
            $UserNameUnsecure = ConvertTo-UnsecureString $UserName
        }
        # Si el tipo de dato del nombre de usuario no es cadena ni cadena
        # segura, advertimos de ello al usuario y salimos de la función
        # devolviendo un nulo
        Else
        {
            $Mensaje = "Tipo de dato de usuario no válido:"
            $Mensaje = "$Mensaje $($UserName.GetType().Name)"
            $Mensaje = "$Mensaje`r`nLos tipos válidos son System.String o"
            $Mensaje = "$Mensaje System.Security.SecureString"
            Write-Host $Mensaje
            Return $null
        }
    }
    # Vamos a revisar qué tipo de dato viene como nombre de usuario, cuando éste
    # no está vacío.
    If([System.String]::IsNullOrEmpty($Password) -eq $false)
    {
        # Si el tipo de dato de la contraseña es texto plano
        If($Password.GetType().FullName -eq "System.String")
        {
            # Obtenemos la cadena segura con la contraseña y la guardamos
            $PasswordSec = ConvertTo-SecureString $Password -AsPlainText -Force
        }
        # Si el tipo de dato de la contraseña es cadena segura
        ElseIf($Password.GetType().FullName -eq "System.Security.SecureString")
        {
            # Almacenamos la contraseña recibida
            $PasswordSec = $Password
        }
        # Si el tipo de dato de la contraseña no es cadena ni cadena segura,
        # advertimos de ello al usuario y salimos de la función devolviendo un
        # nulo
        Else
        {
            $Mensaje = "Tipo de dato de contraseña no válido:"
            $Mensaje = "$Mensaje $($Password.GetType().Name)"
            $Mensaje = "$Mensaje`r`nLos tipos válidos son System.String o"
            $Mensaje = "$Mensaje System.Security.SecureString"
            Write-Host $Mensaje
            Return $null
        }
    }
    # Si no se ha recibido ni nombre de usuario ni contraseña
    If([System.String]::IsNullOrEmpty($UserNameSec) -and `
       [System.String]::IsNullOrEmpty($PasswordSec))
    {
        # Invocamos a Get-Credential, para que el usuario entre el nombre de
        # usuario y su contraseña y obtener un objeto Credential
        $Credential = Get-Credential
        # Obtenemos la cadena segura con el nombre de usuario
        $UserNameSec = ConvertTo-SecureString $Credential.UserName `
                                              -AsPlainText -Force
        # Guardamos la contraseña
        $PasswordSec = $Credential.Password
    }
    # Si se ha recibido sólo la contraseña
    ElseIf([System.String]::IsNullOrEmpty($UserNameSec))
    {
        # Obtenemos el nombre de usuario pidiendo al usuario que lo entre
        $UserNameUnsecure = Read-Host "Entre el nombre de usuario"
        # Obtenemos la cadena segura del nombre de usuario que fue entrado. Se
        # podría haber obtenido directamente si se le hubiese pasado el
        # modificador -AsSecureString al Cmdlet Read-Host, pero entonces el
        # usuario no habría podido leer lo que tecleaba, pues se habría
        # enmascarado, como se hace con las contraseñas.
        $UserNameSec = ConvertTo-SecureString $UserNameUnsecure -AsPlainText -Force
    }
    # Si sólo se ha recibido el nombre de usuario
    ElseIf([System.String]::IsNullOrEmpty($PasswordSec))
    {
        # Presentamos al usuario un cuadro de validación, con el nombre de
        # usuario que entró ya puesto, para que entre la contraseña
        $Credential = Get-Credential $UserNameUnsecure
        # Almacenamos la contraseña
        $PasswordSec = $Credential.Password
    }
    # Ha llegado el momento de crear el fichero de credenciales. Preparamos un
    # Splat con los parámetros que pasaremos a ConvertFrom-SecureString, uno
    # para la llamada que se hará con el usuario y el otro para la de la
    # contraseña
    $SplatUser = @{"SecureString" = $UserNameSec}
    $SplatPassword = @{"SecureString" = $PasswordSec}
    # Si se ha recibido clave para la encriptación, agregamos este parámetro
    # a los dos Splats
    If([System.String]::IsNullOrEmpty($Key) -eq $false)
    {
        # Sólo se pueden usar claves de 16, 24 ó 32 bytes, por lo que sólo con
        # claves de estas longitudes se suministrará la clave.
        If($Key.Count -eq 16 -or $Key.Count -eq 24 -or  $Key.Count -eq 32)
        {
            $SplatUser.Add("Key",$Key)
            $SplatPassword.Add("Key",$Key)
        }
        # Si la clave no tiene una de las longitudes admitidas, se ignorará la
        # clave, procediendose a advertir al usuario de esta contingencia.
        Else
        {
            $Mensaje = "La clave suministrada tiene una longitud de"
            $Mensaje = "$Mensaje $($Key.Count) bytes y será ignorada, pues sólo"
            $Mensaje = "$Mensaje se admiten claves de longitud 16, 24 o 32"
            $Mensaje = "$Mensaje bytes."
            Write-Host $Mensaje -ForegroundColor Yellow `
                                -BackgroundColor DarkCyan
        }
    }
    # Creamos el fichero poniendo en la primera línea el nombre de usuario
    # cifrado, usando para ello ConvertFrom-SecureString y pasando el Splat
    # de argumentos de nombre de usuario
    Set-Content -Path $Path `
                -Value (ConvertFrom-SecureString @SplatUser) `
                -Force
    # Agregamos al fichero la segunda línea con la contraseña cifrada, usando
    # para ello ConvertFrom-SecureString y pasando el Splat de argumentos de la
    # contraseña
    Add-Content -Path $Path `
                -Value (ConvertFrom-SecureString @SplatPassword)
    # Mostramos un mensaje de finalización del proceso
    Write-Host "Fichero de credenciales `"$Path`" creado."

<#
	.SYNOPSIS
		Genera un fichero con los datos de unas credenciales (usuario y
contraseña) cifrados.

	.DESCRIPTION
		Esta función genera un fichero con los datos de unas credenciales
(usuario y contraseña) cifrados. El fichero constará de dos líneas, la
primera con el nombre de usuario y la segunda con la contraseña. Permite pasar o
no el nombre de usuario o la contraseña, solicitando al usuario que entre el/los
dato/s que falte/n de forma interactiva. Permite también especificar una clave
de cifrado, por si se quiere que el fichero generado pueda ser descifrado
por cualquier usuario que conozca la clave; si no se suministra la clave, el
fichero sólo podrá ser descifrado por el usuario que lo creó.

	.PARAMETER  Path
		Ruta y nombre del fichero que se generará. Puede tratarse de una ruta
absoluta o relativa. Admite los alias "FileName", "Fichero", "F", "P", "Ruta" y
"R".

	.PARAMETER  UserName
		Nombre de usuario que se almacenará, cifrado, en el fichero que se
genera. Puede tratarse de una cadena (System.String) o de una cadena segura
(System.Security.SecureString). Si se omite y se omite la contraseña, se
presentará un cuadro de diálogo de solicitud de credenciales (presentado por el
Cmdlet Get-Credential); si se omite, pero no se omite la contraseña, se
solicitará el nombre de usuario por medio del Cmdlet Read-Host. Admite los alias
"User", "Usuario" y "U".

	.PARAMETER  Password
		Contraseña que se almacenará, cifrada, en el fichero que se genera.
Puede tratarse de una cadena (System.String) o de una cadena segura
(System.Security.SecureString). Si se omite y se omite el nombre de usuario, se
presentará un cuadro de diálogo de solicitud de credenciales (presentado por el
Cmdlet Get-Credential); si se omite, pero no se omite el nombre de usuario, se
solicitará el nombre de usuario también por medio del Cmdlet Get-Credential,
pero esta vez el usuario estará ya presente en la caja de texto correspondiente,
teniendo el usuario que entrar nada más que la contraseña. Admite el alias
"Pass".

	.PARAMETER  Key
		Clave de encriptación consistente en un array de Bytes, array que debe
ser de 16, 24 o 32 bytes, cualquier otra longitud provocará que se ignore la
clave. Si no se pasa este parámetro, sólo el usuario que crea el fichero será
capaz de descifrarlo posteriormente, si se pasa la clave, todo usuario que
conozca la clave será capaz de descifrar el fichero. Esto es un problema,
pues para el que lea un script que obtiene las credenciales a partir del fichero
generado y vea la clave podrá obtener las credenciales; así pues, este no
debería ser un parámetro utilizado más que en aquellos extremos en los que no
haya otro remedio, en cuyo caso es muy probable que no sea necesario realizar
todo este mecanismo de llevar los datos a un fichero cifrado para
posteriormente recuperarlos en un script. Admite los alias "Clave" y "C".

	.EXAMPLE
		PS C:\> New-CredentialFile -Path .\bacterio.cred
En este ejemplo, se le solicitará al usuario que entre usuario y contraseña, en
el cuadro de diálogo que presenta Get-Credential, y con los datos suministrados
creará el fichero c:\bacterio.cred.

	.EXAMPLE
		PS C:\> New-CredentialFile -F .\bacterio.cred -UserName "TIA\Bacterio"
En este ejemplo, se le solicitará al usuario que entre la contraseña, en el
cuadro de diálogo que presenta Get-Credential, en el que estará el nombre de
usuario "TIA\Bacterio"; con los datos suministrados creará el fichero
c:\bacterio.cred.

	.EXAMPLE
		PS C:\> $Clave = (23,14,56,76,213,115,1,47,12,67,98,167,113,215,19,87)
		PS C:\> New-CredentialFile -P .\bacterio.cred -Key $Clave

En este ejemplo, se le solicitará al usuario que entre usuario y contraseña, en
el cuadro de diálogo que presenta Get-Credential, y con los datos suministrados
creará el fichero c:\bacterio.cred, utilizando como clave de encriptación la 
clave de 16 bytes definida en la variable $Clave.

	.EXAMPLE
		PS C:\> New-CredentialFile -R .\bacterio.cred -Password "OfeliaFoca"
En este ejemplo, se le solicitará al usuario que entre el nombre de usuario,
solicitud en consola hecha con Read-Host, y con los datos suministrados creará
el fichero c:\bacterio.cred.

	.EXAMPLE
        PS C:\> $User = Read-Host "¿Usuario?" -AsSecureString
        PS C:\> $Pass = Read-Host "¿Contraseña?" -AsSecureString
		PS C:\> New-CredentialFile -F .\bacterio.cred -Pass $Pass -U $User

En este ejemplo, se le solicitará al usuario que entre el nombre de usuario y la
contraseña, solicitudes en consola hechas con Read-Host, y con los datos
suministrados, ambos son cadenas seguras gracias al uso del modificador
-AsSecureString del Cmdlet Read-Host, creará el fichero c:\bacterio.cred.

	.INPUTS
		System.String,System.Security.SecureString,System.Byte[]

	.OUTPUTS
		None

    .NOTES
        Fernando Reyes López © 07/2012

    .LINK
        http://freyes.svetlian.com

    .LINK
        https://urpiano.wordpress.com

#>

}

Get-CredentialFromFile

Este Cmdlet permite crear un objeto System.Management.Automation.PSCredential a partir de un fichero creado con New-CredentialFile, lo que permite usar este Cmdlet en un script que tenga que crear unas credenciales sin que éstas tengan que ser explícitamente escritas en el código, a la vista de ojos indiscretos. Este Cmdlet necesita que esté definido también el Cmdlet ConvertTo-UnsecureString, que hemos visto antes, pues realiza llamadas al mismo.

El Cmdlet incluye ayuda, pego a continuación la ayuda con modificador -Detailed (esta ayuda se basa en la prestación de ayuda basada en comentarios de PowerShell 2.0 ¿Qué, todavía tienes la versión 1.0 y no la 2.0? ¿También consideras el Rigodón un baile de actualidad?):

NOMBRE
    Get-CredentialFromFile
    
SINOPSIS
    Obtiene un objeto PsCredential con los datos contenidos en un fichero
    generado por New-CredentialFile.
    
SINTAXIS
    Get-CredentialFromFile [[-Path] <String>] [[-Key] <Object>] [<CommonParameters>]
    
DESCRIPCIÓN
    Este Cmdelt permite obtener un objeto
    System.Management.Automation.PSCredential con los datos contenidos en un fichero
    cifrado que fue generado por el Cmdlet New-CredentialFile, fichero en el que hay
    dos líneas, la primera es el nombre de usuario y la segunda la contraseña.
    Permite especificar la clave que se usó para cifrar el fichero, lo que habilita
    que sea descifrado por cualquier usuario que conozca la clave; en caso de no
    haberse especificado una clave, cuando se creó el fichero, sólo el usuario que
    lo creó podrá descifrarlo.
    

PARÁMETROS
    -Path <String>
        Ruta y nombre del fichero, que fue creado con New-CredentialFile, donde
        están los datos de usuario y contraseña. Puede tratarse de una ruta absoluta o
        relativa. Admite los alias "FileName", "Fichero", "F", "P", "Ruta" y "R".
        
    -Key <Object>
        Clave que fue utilizada para cifrar el fichero cuando se creó con
        New-CredentialFile. Se trata de un array de bytes; éste debe tener una longitud
        de 16, 24 ó 32 bytes, cualquier otra longitud provocará que este parámetro sea
        ignorado, con lo que se intentará descifrar el fichero sin usar clave. Admite
        los alias "Clave" y "C".
        
    <CommonParameters>
        Este cmdlet admite los parámetros comunes Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer y OutVariable. Para obtener más información, escriba:
        "get-help about_commonparameters".
    
    -------------------------- EJEMPLO 1 --------------------------
    
    PS C:\>$Cred = Get-CredentialFromFile .\ofelia.cred
    
    Se obtiene un objeto System.Management.Automation.PSCredential a partir del
    fichero c:\ofelia.cred, creado con New-CredentialFile. Dado que no se pasa clave
    de cifrado, es necesario que el fichero fuese creado sin especificarla y que el
    usuario que está ejecutando Get-CredentialFromFile sea el mismo que ejecutó
    New-CredentialFile para crearlo.
    
    -------------------------- EJEMPLO 2 --------------------------
    
    PS C:\>$Cred = Get-CredentialFromFile .\ofelia.cred -Key (1..32)
    
    Se obtiene un objeto System.Management.Automation.PSCredential a partir del
    fichero c:\ofelia.cred, creado con New-CredentialFile. Dado que se pasa clave
    de cifrado, es necesario que el fichero fuese creado especificando la misma
    clave. Esto permite que cualquier usuario que conozca la clave pueda obtener
    las credenciales. Realmente, nunca se debería cifrar un archivo especificando
    la clave, cuando se quieren utilizar las credenciales almacenadas en un script,
    pues seria necesario escribir en el script la clave, lo que cualquiera que tenga
    acceso al código del script podrá obtener estas claves.

Este es el código del Cmdlet Get-CredentialFromFile:

Function Get-CredentialFromFile([Alias("FileName","Fichero","F","P","Ruta","R")]
                                [System.String]$Path,
                                [Alias("Clave","C")]
                                $Key)
{
    # Obtenemos el contenido del fichero de credenciales pasado como parámetro
    $Content = Get-Content $Path
    # Montaremos ahora dos Splat para suministrar los parámetros a las llamadas
    # a ConvertTo-SecureString que se realizarán, una para obtener el usuario y
    # la otra para obtener la contraseña. De momento, a ambos Splats les
    # añadiremos el parámetro String, con el nombre de usuario en uno (primera
    # línea del fichero, índice 0) y con la contraseña en el otro (segunda
    # línea, índice 1)
    $SplatUser = @{"String" = $Content[0]}
    $SplatPassword = @{"String" = $Content[1]}
    # Si se ha recibido clave para la encriptación, agregamos la misma, como
    # parámetro Key, a ambos Splats
    If([System.String]::IsNullOrEmpty($Key)-eq $false)
    {
        # Sólo se pueden usar claves de 16, 24 ó 32 bytes, por lo que sólo con
        # claves de estas longitudes se suministrará la clave.
        If($Key.Count -eq 16 -or $Key.Count -eq 24 -or  $Key.Count -eq 32)
        {
            $SplatUser.Add("Key",$Key)
            $SplatPassword.Add("Key",$Key)
        }
        # Si la clave no tiene una de las longitudes admitidas, se ignorará la
        # clave, procediendose a advertir al usuario de esta contingencia.
        Else
        {
            $Mensaje = "La clave suministrada tiene una longitud de"
            $Mensaje = "$Mensaje $($Key.Count) bytes y será ignorada, pues sólo"
            $Mensaje = "$Mensaje se admiten claves de longitud 16, 24 o 32"
            $Mensaje = "$Mensaje bytes."
            Write-Host $Mensaje -ForegroundColor Yellow `
                                -BackgroundColor DarkCyan
        }
    }
    # Obtenemos el nombre de usuario como cadena segura
    $UserNameSec = ConvertTo-SecureString @SplatUser
    # Obtenemos la contraseña como cadena segura
    $PasswordSec = ConvertTo-SecureString @SplatPassword
    # Obtenemos el nombre del usuario como texto plano
    $UserNamePlane = ConvertTo-UnsecureString $UserNameSec
    # Creamos, y devolvemos, un objeto Credential, pasando como parámetros el
    # nombre de usuario en texto plano y la contraseña como cadena segura
    New-Object System.Management.Automation.PSCredential($UserNamePlane,$PasswordSec)

<#
    .SYNOPSIS
        Obtiene un objeto PsCredential con los datos contenidos en un fichero
generado por New-CredentialFile.

    .DESCRIPTION
        Este Cmdelt permite obtener un objeto
System.Management.Automation.PSCredential con los datos contenidos en un fichero
cifrado que fue generado por el Cmdlet New-CredentialFile, fichero en el que hay
dos líneas, la primera es el nombre de usuario y la segunda la contraseña.
Permite especificar la clave que se usó para cifrar el fichero, lo que habilita
que sea descifrado por cualquier usuario que conozca la clave; en caso de no
haberse especificado una clave, cuando se creó el fichero, sólo el usuario que
lo creó podrá descifrarlo.

    .PARAMETER  Path
        Ruta y nombre del fichero, que fue creado con New-CredentialFile, donde
están los datos de usuario y contraseña. Puede tratarse de una ruta absoluta o
relativa. Admite los alias "FileName", "Fichero", "F", "P", "Ruta" y "R".

    .PARAMETER  Key
        Clave que fue utilizada para cifrar el fichero cuando se creó con
New-CredentialFile. Se trata de un array de bytes; éste debe tener una longitud
de 16, 24 ó 32 bytes, cualquier otra longitud provocará que este parámetro sea
ignorado, con lo que se intentará descifrar el fichero sin usar clave. Admite
los alias "Clave" y "C".

    .EXAMPLE
        PS C:\> $Cred = Get-CredentialFromFile .\ofelia.cred
Se obtiene un objeto System.Management.Automation.PSCredential a partir del
fichero c:\ofelia.cred, creado con New-CredentialFile. Dado que no se pasa clave
de cifrado, es necesario que el fichero fuese creado sin especificarla y que el
usuario que está ejecutando Get-CredentialFromFile sea el mismo que ejecutó
New-CredentialFile para crearlo.

    .EXAMPLE
        PS C:\> $Cred = Get-CredentialFromFile .\ofelia.cred -Key (1..32)
Se obtiene un objeto System.Management.Automation.PSCredential a partir del
fichero c:\ofelia.cred, creado con New-CredentialFile. Dado que se pasa clave
de cifrado, es necesario que el fichero fuese creado especificando la misma
clave. Esto permite que cualquier usuario que conozca la clave pueda obtener
las credenciales. Realmente, nunca se debería cifrar un archivo especificando
la clave, cuando se quieren utilizar las credenciales almacenadas en un script,
pues seria necesario escribir en el script la clave, lo que cualquiera que tenga
acceso al código del script podrá obtener estas claves.

    .INPUTS
        System.String,System.Byte[]

    .OUTPUTS
        System.Management.Automation.PSCredential

    .NOTES
        Fernando Reyes López © 07/2012

    .LINK
        http://freyes.svetlian.com

    .LINK
        https://urpiano.wordpress.com

#>
}

Uso de los tres Cmdlets expuestos en este artículo

Veremos ahora unos ejemplos de utilización de estos Cmdlets, uno por cada uno de los dos casos que dijimos:

Pasar las credenciales como objeto System.Management.Automation.PSCredential

Es muy común que los Cmdlets, cuando de utilizar unas credenciales diferentes de las del usuario que tiene abierto Powershell se trata, tengan un parámetro de nombre Credential al que se le puede pasar una de estas dos cosas:

  • Sólo el nombre de usuario como una cadena, con lo que presentará un cuadro de diálogo de entrar credenciales con el nombre de usuario ya puesto en la caja de texto correspondiente.
  • Un objeto System.Management.Automation.PSCredential.

De las dos posibilidades, la que nos interesa es la segunda, pues nos permitirá, usando los Cmdlets expuestos en este artículo, que un script pase las credenciales sin que se interrumpa la ejecución pidiendo una contraseña y sin que esa contraseña se vea; es más tampoco se verá el nombre de usuario.

La operativa es bien sencilla:

Creación del fichero de credenciales

Vamos a crear el fichero de credenciales “d:\Credenciales\Bacterio.cred”, que contendrá las credenciales del usuario “TIA\Bacterio”. Tenemos varias posibilidades, según los parámetros que se pasen; veremos algunas (vease la ayuda de New-CredentialFile para ver las distintas posibilidades):

# Creamos el fichero solicitando al usuario que entre usuario y contraseña
New-CredentialFile -Path d:\Credenciales\Bacterio.cred

# Creamos el fichero suministrando el nombre de usuario como texto plano y
# solicitando al usuario que entre la contraseña
New-CredentialFile -P d:\Credenciales\Bacterio.cred -UserName "TIA\Bacterio"

# Creamos el fichero suministrando el nombre de usuario y la contraseña, ambos
# como texto plano
New-CredentialFile -R d:\Credenciales\Bacterio.cred `
                   -U "TIA\Bacterio" `
                   -Pass "OfeliaFoca"

# Creamos el fichero suministrando el nombre de usuario y la contraseña, esta
# última como cadena segura
$Password = Read-Host "¿Contraseña?" -AsSecureString
New-CredentialFile -R d:\Credenciales\Bacterio.cred `
                   -U "TIA\Bacterio" `
                   -Pass $Password

Creación y uso del objeto System.Management.Automation.PSCredential

Ya en el script en el que necesitamos usar las credenciales que hemos almacenado en el fichero, tenemos dos posibilidades para obtenerlas desde el fichero y usarlas: con clave o sin ella (vease la ayuda de New-CredentialFile). Dado que el uso de clave es insegura, nos centraremos en el caso de no usar clave. En los ejemplos será Get-WmiObject el Cmdlet al que se le pasarán las credenciales alternativas:

# Usamos las credenciales pasando como parametro -Credential de Get-WmiObject
# directamente la llamada a Get-CredentialFromFile
Get-WmiObject -Class Win32_OperatingSystem `
              -ComputerName bacteriosrv.tia.org `
              -Credential (Get-CredentialFromFile d:\Credenciales\Bacterio.cred)

# Cargamos en una variable el objeto PsCredential y pasamos esa variable a
# Get-WmiObject
$Usuario = Get-CredentialFromFile d:\Credenciales\Bacterio.cred
Get-WmiObject -Class Win32_OperatingSystem `
              -ComputerName bacteriosrv.tia.org `
              -Credential $Usuario

Pasar las credenciales como texto plano

Cuando necesitamos suministrar las credenciales como texto plano, tanto el usuario como la contraseña, el modo de hacerlo es igual que en el caso de tener que crear un objeto System.Management.Automation.PSCredential pero con una paso final añadido, el de conseguir pasar a cadena la cadena segura de la contraseña. Por tanto los pasos serían:

Dado que los pasos de creación del fichero con los datos de las credenciales y la obtención del objeto PsCredential a partir de dicho fichero ya han sido vistos anteriormente, tan sólo mostraremos los últimos pasos, es decir, conversión de la contraseña de cadena segura a cadena y uso de los datos obtenidos. Para el ejemplo, crearemos un objeto System.DirectoryServices.DirectoryEntry usando el constructor al que se le pasa la ruta ADS, el nombre de usuario y la contraseña:

# Obtenemos la contraseña en texto plano a partir del objeto PsCredential que
# cargamos anteriormente
$Password = ConvertTo-UnsecureString $Usuario.Password
# Obtenemos el objeto DirectoryEntry
$Ofelia = New-Object System.DirectoryServices.DirectoryEntry( `
                     "LDAP://cn=Ofelia,ou=Focas,ou=Secretarias,dc=tia,dc=org", `
                     $Usuario.UserName,$Password)

# También podemos ahorrarnos la variable $Password anterior realizando la
# llamada a ConvertTo-UnsecureString en la propia creación del objeto
# DirectoryEntry
$Ofelia = New-Object System.DirectoryServices.DirectoryEntry( `
                     "LDAP://cn=Ofelia,ou=Focas,ou=Secretarias,dc=tia,dc=org", `
                     $Usuario.UserName, `
                     (ConvertTo-UnsecureString $Usuario.Password))

Sobre el parámetro Key

Aunque he implementado este parámetro, tanto en New-CredentialFile como en Get-CredentialFromFile, no recomiendo su uso, pues implica que al final se pone en el script la clave que permite descifrar el fichero y, por tanto, ¿para qué tanta molestia en hacer esto si al final al que mire el código le decimos cómo puede averiguar el nombre y la contraseña del usuario que hay en el fichero gracias a que ponemos a la vista la clave de cifrado? Por ello lo recomendable es que se genere el fichero con el usuario que tenga que lanzar la tarea programada, pues es él el único capaz de descifrar el fichero.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

 
A %d blogueros les gusta esto: