Showing posts with label certificate. Show all posts
Showing posts with label certificate. Show all posts

Tuesday, August 4, 2015

Netscaler cert... damn thing hung up on me again

Netscaler has a strange GUI that I think was designed as an "afterthought" by the developers.  The more you use it, the more you try to figure out why stuff is in the order it is, or the grouping it is in.  Sometimes the Netscaler will perform an operation, and drop your connection without warning.  So here is how to install Certificates, which might end in a dropped connection when associating a cert to the device management interface.

Request a certificate as you would normally do, using IIS.  This has been documented plenty of other places, so skipped here. 

Because of the HA pair, you will need one cert, but make it good for 2 DNS names, including NS.blah.com and NS-Otherlocation.blah.com

This is also good for moving a site's SSL certificate to the Netscaler for load balancing from an IIS host.

Visit http://www.derekseaman.com/2013/05/import-iis-ssl-certificate-to-citrix-netscaler.html on how to export this new certificate into the Netscaler UNTIL the section where you have to upload it to the NETSCALER
(Mr Derek Seaman's instructions are good, but not for our NS version.  You can probably figure it out with clicking around, but just in case:)
 At this point, on the Netscaler, you select Traffic management --> ssl and "import PKCS#12"
Most of Mr Seaman's instructions will still work, but things may be very slightly out of order, like the order to click "browse" or whatever... but it is much easier with his diagrams than I can explain here.  Remember to use a good password manager to generate and store any passwords you use in this process.
When you are finished, the cert is ready to be used with your VIP.

IF YOU ARE INSTALLING THE CERT FOR THE NETSCALER DEVICE ITSELF:
Skip the step above where you upload the certificate, or remove it from the Netscaler if you have already uploaded it.

Download the "X509 Certificate only, Base64 encoded" file and open it in a text editor.
blah_com_cert.cer

Download the "X509 Intermediates/root only Reverse, Base64 encoded: " file and open it as well.
blah_com_interm.cer

Create a new text file.  Copy the X509 Certificate only, Base64 encoded  cert to it first and then copy the NEXT two X509 Intermediates/root only Reverse, Base64 encoded certs from the file below the first. (They will be the first two in the blah_com_interm.cer file. The root is the last one in that file and you don't want it.) Now save the file with a meaningful name, like "blah_com-bun-noroot.crt".  (bun=bundle)

Login into the NS and upload your cert bundle and private key. In this example they would be blah_com.key and blah_com-bun-noroot.crt.
Then under SSL/Certificates select the ns-server-certificate and update it. There's a check box on the Update Certificate window that says, Click to update Certificate/Key. Select that and then browse for the two files you just uploaded.  Also check the box "no domain check" if you are switching domain suffixes . 
 After clicking "ok", wait a min or two... then you will have to reconnect your browser.
Check the certificate in the browser, it should list new certificate.
 
You do not have to modify the other Node in the HA pair, the HA standby member gets updated automatically.

You can diagnose/view the certs you uploaded using "shell" and "openssl x509 -in NAMEOFFILE.cer -text -noout"

Happy balancing,
-_Bryan

Wednesday, April 22, 2015

Confuse your Local Admin, repeatedly. Part 2 - decrypt

Here we decrypt the password, using the private key stored in the local cert or user cert store.

####################################################################################
#.Synopsis 
# Retrieve attribute from AD using powershell
#
#.Description 
#  Retrieve attribute from AD using powershell
#
#
#.Parameter ComputerNames
#   One or more computer names that you are requesting the password for.
#
#.Example 
# ./Get-EncryptedPasswordFromCarLicenseInAD.ps1 -computer machine1
# This will return the password for the listed machine.
#
#.Example 
# ./Get-EncryptedPasswordFromCarLicenseInAD.ps1 -computernames machine1,machine2
# This will return the passwords for both machines listed
#
#
#Requires -Version 3.0 
#
#.Notes 
#  Author: Bryan Loveless bryan.loveless@gmail.com
# 
# Version: 1.0
# Updated: 17.April.2015
#   LEGAL: PUBLIC DOMAIN.  SCRIPT PROVIDED "AS IS" WITH NO WARRANTIES OR GUARANTEES OF 
#          ANY KIND, INCLUDING BUT NOT LIMITED TO MERCHANTABILITY AND/OR FITNESS FOR
#          A PARTICULAR PURPOSE.  ALL RISKS OF DAMAGE REMAINS WITH THE USER, EVEN IF
#          THE AUTHOR, SUPPLIER OR DISTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF
#          ANY SUCH DAMAGE.  IF YOUR STATE DOES NOT PERMIT THE COMPLETE LIMITATION OF
#          LIABILITY, THEN DELETE THIS FILE SINCE YOU ARE NOW PROHIBITED TO HAVE IT.
####################################################################################

# Get-EncryptedPasswordFromCarLicenseInAD.ps1


[CmdletBinding()]
Param (  
        [Parameter(
            Position=0,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$True,
   Mandatory=$false,
            HelpMessage="What is/are the computer names?"
            )
         ]
           [string[]]$ComputerNames = ("machine1","machine2") #change back to "$env:computername" to run on local machine
     )    

####################################################################################
# Decrypts TXT using public key
# Decrypt-Asymmetric -EncryptedBase64String $Base64String -CertThumbprint "‎thumbprintHere" 
####################################################################################
Function Decrypt-Asymmetric
{
    [CmdletBinding()]
    [OutputType([System.String])]
    param(
        [Parameter(Position=0, Mandatory=$true)][ValidateNotNullOrEmpty()][System.String]
        $EncryptedBase64String,
        [Parameter(Position=1, Mandatory=$true)][ValidateNotNullOrEmpty()][System.String]
        $CertThumbprint
    )
    # Decrypts cipher text using the private key
    # Assumes the certificate is in the LocalMachine\My (Personal) Store
    
 #below looks in local computer store for cert
 #$Cert = Get-ChildItem cert:\LocalMachine\My | where { $_.Thumbprint -eq $CertThumbprint }
 
 # below looks in current user store for cert
 #$Cert = Get-ChildItem cert:\CurrentUser\my  | where { $_.Thumbprint -eq $CertThumbprint }
 
 # below looks in the current user's certificates and private keys AND CHECKS THEM.
  # reference: Jason Fossen, Enclave Consulting (http://cyber-defense.sans.org/blog
 try
 {
     $readonlyflag = [System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly
     $currentuser =  [System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser
     $usercertstore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $currentuser
     $usercertstore.Open($readonlyflag) 
     $usercertificates = $usercertstore.Certificates
 }
 catch
 {
     "`nERROR: Could not open your certificates store. `n"
     exit
 }
 finally
 {
     $usercertstore.Close() 
 }

 if ($usercertificates.count -eq 0) { "`nERROR: You have no certificates or private keys.`n" ; exit }
  
# Load the correct certificate and test for possession of private key.
    $cert = $usercertificates | where { $_.thumbprint -eq $CertThumbprint } 
    if (-not $cert.hasprivatekey) 
    { 
        $output.StatusMessage = "ERROR: You do not have the private key for this certificate."
        $output.Valid = $false
        $output
        continue
    }
 
    if($Cert) {
        $EncryptedByteArray = [Convert]::FromBase64String($EncryptedBase64String)
        $ClearText = [System.Text.Encoding]::UTF8.GetString($Cert.PrivateKey.Decrypt($EncryptedByteArray,$true))
    }
    Else {Write-Error "Certificate with thumbprint: $CertThumbprint not found!"}
 
    Return $ClearText
  #reference: http://jeffmurr.com/blog/?p=228
}

#Create the array to store all decrypted passwords
$AllPasswords = @()

ForEach ($ComputerName in $ComputerNames){
# retrieve where the machine lives in AD 
 $Filter = "(&(objectCategory=Computer)(Name=$ComputerName))"
 $DirectorySearcher = New-Object System.DirectoryServices.DirectorySearcher
 $DirectorySearcher.Filter = $Filter
 $SearcherPath = $DirectorySearcher.FindOne()
 $machine = $SearcherPath.GetDirectoryEntry()

#get the "carLicense" attribute from AD
 $carLicenseAttribute = ($machine.carLicense)
  
# get thumbprint of the certificate, create object for it, then set thumbprint for comparison
 $output = ($output = " " | select-object Valid,StatusMessage,Password,Thumbprint)
    $output.Valid =        $false  #Assume password recovery will fail.
 #
 $output.Thumbprint = "‎PUTYOURCERTIFICATETHUMBPRINTHEREOFTHEONEYOUEXPECT"

# Load the current user's certificates and private keys.
 try
 {
     $readonlyflag = [System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly
     $currentuser =  [System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser
     $usercertstore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $currentuser
     $usercertstore.Open($readonlyflag) 
     $usercertificates = $usercertstore.Certificates
 }
 catch
 {
     "`nERROR: Could not open your certificates store. `n"
     exit
 }
 finally
 {
     $usercertstore.Close() 
 }

 if ($usercertificates.count -eq 0) { "`nERROR: You have no certificates or private keys.`n" ; exit }
  
# Load the correct certificate and test for possession of private key.
    $thecert = $usercertificates | where { $_.thumbprint -eq $output.thumbprint } 
    if (-not $thecert.hasprivatekey) 
    { 
        $output.StatusMessage = "ERROR: You do not have the private key for this certificate."
        $output.Valid = $false
        $output
        continue
    } 
    

# Test to confirm that the private key can be accessed, not just that it exists.  The
# problem is that it is not a trivial task to allow .NET or PowerShell to use
# private keys managed by Crytography Next Generation (CNG) key storage providers, hence,
# these scripts are only compatible with the older Cryptographic Service Providers (CSPs), such
# as the "Microsoft Enhanced Cryptographic Provider", but not the newer CNG "Microsoft
# Software Key Storage Provider".  Sorry...
    if ($thecert.privatekey -eq $null) 
    { 
        $output.StatusMessage = "ERROR: This script is not compatible with CNG key storage providers."
        $output.Valid = $false
        $output
        continue
    } 


#Remove Date information, Decrypt password using private key, and return vaule
$decryptedPassword = Decrypt-Asymmetric -EncryptedBase64String (($carLicenseAttribute.ToString()).SubString(19)) -CertThumbprint $output.Thumbprint
Write-Host $computername"'s" "password is" $decryptedPassword

$AllPasswords= $allpasswords + ($decryptedPassword)

Remove-Variable decryptedPassword
}

#returns all of the passwords in the array together, in case script was called expecting returns
Write-Host "Here are all of the passwords you requested:"
Return $AllPasswords
Remove-Variable AllPasswords

# FIN

Confuse your Local Admin, repeatedly. Part 1 - change and encrypt

We recently found out that some of our departmental desktop admin staff have been forgetting to change the password for the LocalAdmin account after imaging a machine. Our Information Security team found out this was so widespread, they asked my team to solve this issue. Politics aside, here is what we decided on:
 1) Change Local Admin username to something else.
2) Change this new user's password to something unique between every device.
 3) Encrypt this password using a public key, then store it in Active Directory.
4) Be able to control who can receive and decrypt this password, and how.
5) Be able to change this password as often as we want, by only issuing a new GPO.
6) Set how often to change the password, as every time it starts seems to harsh.

And now the solutions/background of solutions:
 1) Bing how to change local admin username using GPOs. It is super easy.
 2) Most password generators I found on the internet rely on Powershell's "Get-Random"... that is not truly random. It is based upon system up-time, so someone much smarter than me might be able to figure out the random seed. So I had help from a buddy at work, and we wrote another get-randompassword function that bases passwords on the .NET security provider. (see code)
3) With the borrowed code from the internet, I discovered this is much easier than I thought it would be. So I generate a "really strong" self-signed cert that is good for 10 years (because we had to take into account a machine that was taken off the domain, and how to access the data on it after it has been purged from AD). I put the public cert in the domain's NETLOGON area, so every authenticated object is able to access it. The private key is (obviously) stored somewhere else, very, very protected. Also, this enables us to flip out the public cert if we need to someday.
4) We are still discussing this, but I will post the code to decrypt the info in "Part 2". The code right now will work if the user logged in or the machine have a certificate that matches the public key, but the idea of a webpage has been discussed allowing our desktop admins to authenticate to a webpage and never have access to the private key itself.
 5) With one digit in the POSH, easy-cheesy.
6)  I write the value of the last time the password was changed in the AD attribute, then, if over a certain amount of days, I change it again.  This prevents us from having the user who "will get around to migrating to the new machine" but doesn't do it within a week or two.  Again, this is set in the code, very easy to change.

 Now of course, I need to mention I am just a simple Sysadmin, so if this breaks your stuff, then don't blame me... you should have consulted a programmer.

####################################################################################
#.Synopsis 
#    Resets the password of a local user account with a random password which is 
#    then encrypted with your pubic key certificate and stored in AD attribute under
#  the computer object. The plaintext password is 
#    displayed with the  Get-EncryptedPasswordFromCarLicenseInAD.ps1 
#  script. 
#
#.Description 
#    Resets the password of a local user account with a 16-25 character, random, 
#    complex password, which is encrypted with your own pubic key certificate and then
#  stored under an Active Directory Attribute for the computer object. 
#    Recovery of the encrypted password from Active Directory requires possession of the
#    private key corresponding to the chosen public key certificate.  The password
#    is never transmitted or stored in plaintext. The plaintext password 
#    is recovered with the companion 
#  Get-EncryptedPasswordFromCarLicenseInAD.ps1 script.  The
#    script must be run with administrative or local System privileges. 
#  The Private Key for decryption may be stored in either the Computer's Cert Store or the
#  User's cert store.
#
#.Parameter CertificateFilePath 
#    The local or UNC path to the .CER file containing the public key 
#    certificate which will be used to encrypt the password.  The .CER
#    file can be DER- or Base64-encoded.  (But note that the private
#    key for the certificate cannot be managed by a Cryptography Next
#    Generation (CNG) key storage provider, hence, do not use the Microsoft 
#    Software Key Storage Provider in the template for the certificate.)
#
#.Parameter LocalUserName
#    Name of the local user account on the computer where this script is run
#    whose password should be reset to a 16-25 character, complex, random password.
#    Do not include a "\" or "@" character, only local accounts are supported.
#    Defaults to "Guest", but any name can be specified.
#
#
#.Parameter MinimumPasswordLength
#    The minimum length of the random password.  Default is 16.  The exact length
#    used is randomly chosen to increase the workload of an attacker who can see
#    the contents of this script.  Maximum password length defaults to 25.  The
#    smallest acceptable minimum length is 4 due to complexity requirements.
#
#.Parameter MaximumPasswordLength
#    The maximum length of the random password.  Default is 16.  Max is 127.
#    The minimum and maximum values can be identical.    
#
#.Example 
#    .\Set-EcryptedPasswordToCarLicenseInAD.ps1 -CertificateFilePath \\server\share\certificate.cer 
#
#    Resets the password of the default account, encrypts that password 
#    with the public key in the certificate.cer file, and saves the encrypted
#    password in the AD attribute "carLicense".  Choose a different account with -LocalUserName.
#
#
#.Example 
#    .\Set-EcryptedPasswordToCarLicenseInAD.ps1 -LocalUserName HelpDeskUser -CertificateFilePath \\server\share\certificate.cer
#
#    The local account's password is reset by default, but any
#    local user name can be specified instead.
#
#
#Requires -Version 2.0 
#
#.Notes 
#  Author: Bryan Loveless bryan.loveless@gmail.com, based upon script by
#   Jason Fossen, Enclave Consulting (http://cyber-defense.sans.org/blog)
#   Password cryptographic method by Bryan Loveless bryan.loveless@gmail.com
# Version: 1.0
# Updated: 17.April.2015
#   LEGAL: PUBLIC DOMAIN.  SCRIPT PROVIDED "AS IS" WITH NO WARRANTIES OR GUARANTEES OF 
#          ANY KIND, INCLUDING BUT NOT LIMITED TO MERCHANTABILITY AND/OR FITNESS FOR
#          A PARTICULAR PURPOSE.  ALL RISKS OF DAMAGE REMAINS WITH THE USER, EVEN IF
#          THE AUTHOR, SUPPLIER OR DISTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF
#          ANY SUCH DAMAGE.  IF YOUR STATE DOES NOT PERMIT THE COMPLETE LIMITATION OF
#          LIABILITY, THEN DELETE THIS FILE SINCE YOU ARE NOW PROHIBITED TO HAVE IT.
####################################################################################

# Set-EncryptedPasswordToCarLicenseInAD.ps1

Param ($CertificateFilePath = ".\LocalAdminPasswordChangePublicCert.cer", $LocalUserName = "bryan", $MinimumPasswordLength = 16, $MaximumPasswordLength = 25) 



####################################################################################
# Below replaces the generate-randompassword used on a previous version of this script,
#   as the older one used get-random and this new one uses the .net cryptographic provider
#####################################################################################
function Generate-Password() {


param( 
[int] $len = 16,
[string] $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_!@#$%^&*()_"
)

$result = ""
for( $i=0; $i -lt $len; $i++ )
{
  

$bytes = new-object "System.Byte[]" 1
$rnd = new-object System.Security.Cryptography.RNGCryptoServiceProvider
$rnd.GetBytes($bytes)



    if ($bytes[0] -gt (([int](256/$chars.Length))*$chars.length))
        { 
            $i-- 
            continue
        }

$result += $chars[ $bytes[0] % $chars.Length ] 
}


return $result

  <#
    .SYNOPSIS 
        Returns a secure password, based on the System's Crytpo Service Provider.

    .EXAMPLE
     Generate-Password 
        Returns a  16 character password, using A-Z, numbers, and the "easier to type" special characters

    .EXAMPLE
     Generate-Password -len 48 -chars "0123456789"
        Returns a 48 length numeric password

    .EXAMPLE
    Generate-Password -len 1 -chars "01" 
        Returns a one character password that is either a 0 or a 1"


    .NOTES
        Modified by Bryan Loveless    bryan.loveless@gmail.com Jan 2015
        Based on http://www.peterprovost.org/blog/2007/06/22/Quick-n-Dirty-PowerShell-Password-Generator/
        BUT modified to be a little bit more secure

  #>
}

####################################################################################
# Returns true if password reset accepted, false if there is an error.
# Only works on local computer, but can be modified to work remotely too.
####################################################################################
Function Reset-LocalUserPassword ($UserName, $NewPassword)
{
    Try 
    {
        $ADSI = [ADSI]("WinNT://" + $env:ComputerName + ",computer")
        $User = $ADSI.PSbase.Children.Find($UserName)
        $User.PSbase.Invoke("SetPassword",$NewPassword)
        $User.PSbase.CommitChanges()
        $User = $null 
        $ADSI = $null
        $True
    }
    Catch
    { $False } 
}

####################################################################################
# Writes to console, writes to Application event log, optionally exits.
# Event log: Application, Source: "PasswordArchive", Event ID: 9013
####################################################################################
function Write-StatusLog ( $Message, [Switch] $Exit )
{
    # Define the Source attribute for when this script writes to the Application event log.
    New-EventLog -LogName Application -Source PasswordArchive -ErrorAction SilentlyContinue

    "`n" + $Message + "`n"

#The following here-string is written to the Application log only when there is an error, 
#but it contains information that could be useful to an attacker with access to the log.
#The data is written for troubleshooting purposes, but feel free change it if concerned.
#It does not contain any passwords of course.
$ErrorOnlyLogMessage = @"
$Message 

CurrentPrincipal = $($CurrentPrincipal.Identity.Name)

CertificateFilePath = $CertificateFilePath 

LocalUserName = $LocalUserName

PasswordArchivePath = $PasswordArchivePath

ArchiveFileName = $filename
"@

    if ($Exit)
    { write-eventlog -logname Application -source PasswordArchive -eventID 9013 -message $ErrorOnlyLogMessage -EntryType Error }
    else
    { write-eventlog -logname Application -source PasswordArchive -eventID 9013 -message $Message -EntryType Information }

    if ($Exit) { exit } 
}

####################################################################################
# Writes to AD attribute
# 
####################################################################################
Function SendAttribute-ToAD
{
[CmdletBinding()]
    Param (
        [Parameter(
            Position=1,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true,
            #ValidateNotNullOrEmpty(),
            HelpMessage="What is the field name that you would like to edit?"
            )
         ]
           [string]$PropertyToEdit,
       

        [Parameter(
            Position=2,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true,
            #ValidateNotNullOrEmpty(),
            Mandatory=$true,
            HelpMessage="What do you want the new entry to be?"
            )
         ]
            [string]$EditedInfo
     )    

$ComputerName = $env:computername
$Filter = "(&(objectCategory=Computer)(Name=$ComputerName))"

$DirectorySearcher = New-Object System.DirectoryServices.DirectorySearcher
$DirectorySearcher.Filter = $Filter

$SearcherPath = $DirectorySearcher.FindOne()

#---------------------------------------

$machine = $SearcherPath.GetDirectoryEntry()

write-host "editing property $PropertyToEdit to have $EditedInfo"
#edit the property
#$machine.$PropertyToEdit = $EditedInfo

$machine.InvokeSet(($PropertyToEdit),(($EditedInfo).tostring()))

write-host "committing changes"
#commit the edit on
$machine.CommitChanges()

}

####################################################################################
# Encrypts TXT using public key
# Encrypt-Asymmetric -ClearText "CLEAR TEXT DATA" -PublicCertFilePath "C:\Scripts\PowerShell\Asymmetrical-Encryption\PowerShellAsymmetricalTest.cer" 
####################################################################################
Function Encrypt-Asymmetric {
    [CmdletBinding()]
    [OutputType([System.String])]
    param(
        [Parameter(Position=0, Mandatory=$true)][ValidateNotNullOrEmpty()][System.String]
        $ClearText,
        [Parameter(Position=1, Mandatory=$true)][ValidateNotNullOrEmpty()][ValidateScript({Test-Path $_ -PathType Leaf})][System.String]
        $PublicCertFilePath
    )
    # Encrypts a string with a public key
    $PublicCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($PublicCertFilePath)
    $ByteArray = [System.Text.Encoding]::UTF8.GetBytes($ClearText)
    $EncryptedByteArray = $PublicCert.PublicKey.Key.Encrypt($ByteArray,$true)
    $Base64String = [Convert]::ToBase64String($EncryptedByteArray)
 
    Return $Base64String
 #reference: http://jeffmurr.com/blog/?p=228
}


# Sanity check the two password lengths:
if ($MinimumPasswordLength -le 3) { $MinimumPasswordLength = 4 } 
if ($MaximumPasswordLength -gt 127) { $MaximumPasswordLength = 127 } 
if ($MinimumPasswordLength -gt 127) { $MinimumPasswordLength = 127 } 
if ($MaximumPasswordLength -lt $MinimumPasswordLength) { $MaximumPasswordLength = $MinimumPasswordLength }

# Confirm that this process has administrative privileges to reset a local password.
$CurrentWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$CurrentPrincipal = new-object System.Security.Principal.WindowsPrincipal($CurrentWindowsID)
if (-not $? -or -not $CurrentPrincipal.IsInRole("Administrators")) 
   { write-statuslog -m "ERROR: This process lacks the privileges necessary to reset a password." -exit }

# Confirm that the target local account exists and that ADSI is accessible.
if ($LocalUserName -match '[\\@]')  { write-statuslog -m "ERROR: This script can only be used to reset the passwords of LOCAL user accounts, please specify a simple username without an '@' or '\' character in it." -exit }  
try 
{ 
    $ADSI = [ADSI]("WinNT://" + $env:ComputerName + ",computer") 
    $User = $ADSI.PSbase.Children.Find($LocalUserName)
    $User = $null
    $ADSI = $null 
}
catch 
{ write-statuslog -m "ERROR: Local user does not exist: $LocalUserName" -exit } 


# Generate and test new random password with min and max lengths.
$newpassword = "ConfirmThatNewPasswordIsRandom"

if ($MinimumPasswordLength -eq $MaximumPasswordLength)
{  
#    $newpassword = Generate-RandomPassword -Length $MaximumPasswordLength
 $newpassword = Generate-Password -len $MaximumPasswordLength

} 
else
{ 
#    $newpassword = Generate-RandomPassword -Length $(Get-Random -Minimum $MinimumPasswordLength -Maximum $MaximumPasswordLength) 
 $newpassword = Generate-Password -len $(Get-Random -Minimum $MinimumPasswordLength -Maximum $MaximumPasswordLength)

}

# Users outside USA might modify the Generate-RandomPassword function, hence this check.
if ($newpassword -eq "ConfirmThatNewPasswordIsRandom") 
{ write-statuslog -m "ERROR: Password generation failure, password not reset." -exit } 


#encrypt password 
$encryptedPassword = Encrypt-Asymmetric -ClearText "$newpassword" -PublicCertFilePath "$CertificateFilePath"
 
#if ($encryptedPassword -not $?) { write-statuslog -m "ERROR, password was not encrypted, password not reset." -exit } 


# Attempt to reset the password.
if ( Reset-LocalUserPassword -UserName $LocalUserName -NewPassword $newpassword )
{
    remove-variable -name newpassword  #Just tidying up, not really necessary at this point...
    write-statuslog -m "SUCCESS: $LocalUserName password reset."  
}
else
{
    # Write the RESET-FAILURE file to statuslog; these failure files are used by the other scripts too.

    write-statuslog -m "ERROR: Failed to reset password:`n`n $error[0]" -exit 
} 

#now write the encrypted password($content) to the machine's AD attribute($Property), appending today's date on the front
 $Property = "carLicense"
 $TodaysDate=(GET-DATE -Format s)
 SendAttribute-ToAD -PropertyToEdit $Property -EditedInfo "$TodaysDate $encryptedPassword"
# remove-variable -name encryptedPassword  #Removing Variable, just in case.

# FIN



Thursday, January 8, 2015

Yubikey and Windows, Certifiably a pain no longer

At my new job, we are looking to use Yubikeys to store our personal certificates to log into Windows servers as a method of multifactor authentication. After searching for quite some time, it became apparent to me that there is little to no documentation that is easily read (at least for Windows machines), nor did I find any scripts to help the process of importing a certificate.

Here is my script that not only asks the user where the .PFX file is located, but also configures the management key and PUK based on a random number, asks the user for their preferred pin, and includes a function that allows you to reset the device easily. This script still requires Yubikey's PIV tool, found at https://developers.yubico.com/yubico-piv-tool/ . This script has been tested/used on version 0.1.3.

 I am not primarily a programmer, so I apologize for any functionality that could have been done better or faster.


####################################################################################################
#
# Script to help out some of the management of YubiKeys in windows using powershell
# Bryan Loveless (bryan.loveless@gmail.com)
# January 2015
# http://actualreverend.blogspot.com/
#
# requires Yubikey PIV tool to be downloaded somewhere (0.1.3 was the verison tested)
# requires PFX to be exported in a pfx file, and not already assigned to a smartcard in use
#          PFX must be protected with a password
#
# User must not use the pin numbers 12345 or 4711, as these are default and Reset-yubikey will fail
#
###################################################################################################

# Show an Open File Dialog and return the file selected by the user. 
#Borrowed from http://blog.danskingdom.com/powershell-multi-line-input-box-dialog-open-file-dialog-folder-browser-dialog-input-box-and-message-box/ 
function Read-OpenFileDialog([string]$WindowTitle, [string]$InitialDirectory, [string]$Filter = "All files (*.*)|*.*", [switch]$AllowMultiSelect)
{ 
    Add-Type -AssemblyName System.Windows.Forms
    $openFileDialog = New-Object System.Windows.Forms.OpenFileDialog
    $openFileDialog.Title = $WindowTitle
    if (![string]::IsNullOrWhiteSpace($InitialDirectory)) { $openFileDialog.InitialDirectory = $InitialDirectory }
    $openFileDialog.Filter = $Filter
    if ($AllowMultiSelect) { $openFileDialog.MultiSelect = $true }
    $openFileDialog.ShowHelp = $true    # Without this line the ShowDialog() function may hang depending on system configuration and running from console vs. ISE.
    $openFileDialog.ShowDialog() > $null
    if ($AllowMultiSelect) { return $openFileDialog.Filenames } else { return $openFileDialog.Filename }
}

# Show an Open Folder Dialog and return the directory selected by the user.
#Borrowed from http://blog.danskingdom.com/powershell-multi-line-input-box-dialog-open-file-dialog-folder-browser-dialog-input-box-and-message-box/
function Read-FolderBrowserDialog([string]$Message, [string]$InitialDirectory, [switch]$NoNewFolderButton)
{
    $browseForFolderOptions = 0
    if ($NoNewFolderButton) { $browseForFolderOptions += 512 }
 
    $app = New-Object -ComObject Shell.Application
    $folder = $app.BrowseForFolder(0, $Message, $browseForFolderOptions, $InitialDirectory)
    if ($folder) { $selectedDirectory = $folder.Self.Path } else { $selectedDirectory = '' }
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($app) > $null
    return $selectedDirectory
}

# reset the device easily
function Reset-Yubikey{
write-host "Are you sure you want to reset the yubikey?  Ctrl-C to stop now!"
pause

#prompt user where the yubico-piv executable is:
$yubiPIVExecutable = Read-OpenFileDialog -InitialDirectory 'C:\Program Files (x86)' -WindowTitle "Select yubico-piv-tool.exe file" -filter "yubico-piv-exe files (yubico-piv-tool.exe)|yubico-piv-tool.exe"

# lock the pin an puk numbers on device
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567

& "$yubiPIVExecutable" -a reset
}

# password generator, using it to get new managment key, borrowed from http://blog.morg.nl/2014/01/generate-a-random-strong-password-in-powershell/
function Generate-Password() {
    Param (
    [int]$length = 8,   
    [bool] $includeLowercaseLetters = $true,
    [bool] $includeUppercaseLetters = $true,
    [bool] $includeNumbers = $true,
    [bool] $includeSpecialChars = $false,
    [bool] $noSimilarCharacters = $true
    )
 
    <#
    (c) Morgan de Jonge CC BY SA
    Generates a random password. you're able to specify:
    - The desired password length (minimum = 4)
    - Whether or not to use lowercase characters
    - Whether or not to use uppercase characters
    - Whether or not to use numbers
    - Whether or not to use special characters
    - Whether or not to avoid using similar characters ( e.g. i, l, o, 1, 0, I)
    #>
 
    # Validate params
    if($length -lt 4) {
        $exception = New-Object Exception "The minimum password length is 4"
        Throw $exception
    }
    if ($includeLowercaseLetters -eq $false -and
            $includeUppercaseLetters -eq $false -and
            $includeNumbers -eq $false -and
            $includeSpecialChars -eq $false) {
        $exception = New-Object Exception "At least one set of included characters must be specified"
        Throw $exception
    }
 
    #Available characters
    $CharsToSkip = [char]"i", [char]"l", [char]"o", [char]"1", [char]"0", [char]"I"
    $AvailableCharsForPassword = $null;
    $uppercaseChars = $null
    for($a = 65; $a -le 90; $a++) { if($noSimilarCharacters -eq $false -or [char][byte]$a -notin $CharsToSkip) {$uppercaseChars += ,[char][byte]$a }}
    $lowercaseChars = $null
    for($a = 97; $a -le 122; $a++) { if($noSimilarCharacters -eq $false -or [char][byte]$a -notin $CharsToSkip) {$lowercaseChars += ,[char][byte]$a }}
    $digitChars = $null
    for($a = 48; $a -le 57; $a++) { if($noSimilarCharacters -eq $false -or [char][byte]$a -notin $CharsToSkip) {$digitChars += ,[char][byte]$a }}
    $specialChars = $null
    $specialChars += [char]"=", [char]"+", [char]"_", [char]"?", [char]"!", [char]"-", [char]"#", [char]"$", [char]"*", [char]"&", [char]"@"
 
    $TemplateLetters = $null
    if($includeLowercaseLetters) { $TemplateLetters += "L" }
    if($includeUppercaseLetters) { $TemplateLetters += "U" }
    if($includeNumbers) { $TemplateLetters += "N" }
    if($includeSpecialChars) { $TemplateLetters += "S" }
    $PasswordTemplate = @()
    # Set password template, to ensure that required chars are included
    do {  
        $PasswordTemplate.Clear()
        for($loop = 1; $loop -le $length; $loop++) {
            $PasswordTemplate += $TemplateLetters.Substring((Get-Random -Maximum $TemplateLetters.Length),1)
        }
    }
    while ((
        (($includeLowercaseLetters -eq $false) -or ($PasswordTemplate -contains "L")) -and
        (($includeUppercaseLetters -eq $false) -or ($PasswordTemplate -contains "U")) -and
        (($includeNumbers -eq $false) -or ($PasswordTemplate -contains "N")) -and
        (($includeSpecialChars -eq $false) -or ($PasswordTemplate -contains "S"))) -eq $false
    )
    #$PasswordTemplate now contains an array with at least one of each included character type (uppercase, lowercase, number and/or special)
 
    foreach($char in $PasswordTemplate) {
        switch ($char) {
            L { $Password += $lowercaseChars | Get-Random }
            U { $Password += $uppercaseChars | Get-Random }
            N { $Password += $digitChars | Get-Random }
            S { $Password += $specialChars | Get-Random }
        }
    }
 
    return $Password
}

#function configure-yubikey{

#change key, pin, and puk from default setting:

#generate a random number and store it in keepass as USER-YUBIKEY-ManagementKEY must be 16 characters (010203040506070801020304050607080102030405060708 is default)
#$randomkey = get-random -minimum 999999999999999 -maximum 9999999999999999 
$randomkey = Generate-Password 48 $false $false $true $false $false
write-host "create a keepass entry named yourusername-YUBIKEY-ManagmentKEY and enter $randomkey in for the value" -ForeGroundColor Red
pause

#generate a random number and store it in keepass as USER-YUBIKEY-PUK, must be 8 characters
#$randompuk = get-random -minimum 9999999 -maximum 99999999 
$randompuk = Generate-Password 8 $false $false $true $false $false
write-host "create a keepass entry named yourusername-YUBIKEY-PUK and enter $randompuk in for the value" -ForeGroundColor Red
pause

#prompt user with protected pin entering box:
$pinencrypted = read-host -prompt "What do you want your pin number to be?" -AsSecureString

#extract plain text pin number from encrypted varible above
$BSTR = `
    [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($pinencrypted)
$pin = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)   

#prompt user where the yubico-piv executable is:
$yubiPIVExecutable = Read-OpenFileDialog -InitialDirectory 'C:\Program Files (x86)' -WindowTitle "Select yubico-piv-tool.exe file" -filter "yubico-piv-exe files (yubico-piv-tool.exe)|yubico-piv-tool.exe"

#ask user where PFX file is
$pfxfile = Read-OpenFileDialog -InitialDirectory c:\ -WindowTitle "Select PFX file" -filter "PFX files (*.pfx)|*.pfx"

#prompt user for PFX password:
$PFXpasswordencrypted = read-host -prompt "What is your PFX password?" -AsSecureString

#extract plain text pin number from encrypted varible above
$BSTR = `
    [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($PFXpasswordencrypted)
$PFXpassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) 

#changing pin from default(none) to random number generated above:
write-host "setting the yubikey managment key now"
& "$yubiPIVExecutable" -a set-mgm-key -n $randomkey

#changing PIN from default to user's seleciton
write-host "Changing the PIN number now"
& "$yubiPIVExecutable" -k $randomkey -a change-pin -P 123456 -N $pin

#Changing pin from default to random number generated above:
write-host "Changing the PUK number now"
& "$yubiPIVExecutable" -k $randomkey -a change-puk -P 12345678 -N $randompuk



# imports the key
& "$yubiPIVExecutable" --slot 9a --input=$pfxfile --password=$PFXpassword --key-format=PKCS12 --action=set-chuid --action=import-key --action=import-certificate -v2 --key=$randomkey


#}

Friday, August 27, 2010

Convert an IIS cert to an apache one

Here is a simple powershell script I wrote to convert a "iis" cert to one that apache is happy with to use with SSL:

####################################################


#convert IIS certs to openSSL ones for Apache to use
# openssl is installed with Apache by default.
#
#created May 24, 2010
#Bryan Loveless 
#
#

Write-host "This script assumes you have openssl installed in the C:\Program Files (x86)\Apache Software Foundation\Apache2.2\bin Directory 
And your certs are in the C:\Program Files (x86)\Apache Software Foundation\Apache2.2\certs Directory"


####################################################

Set-Location "C:\Program Files (x86)\Apache Software Foundation\Apache2.2\bin"

$OrgCert=Read-Host "What is the name of your cert? (Include the .pfx)"

$NewCert= Read-Host "What do you want to name the new one? (Include the .pem) Normally it is apache.key.pem"

$NewKey= Read-Host "What do you want to name the new one? (Include the .key) Normally it is apache.key"

$newkey2= Read-Host "What do you want to name the new one? (Include the .pem) Normally it is apache.cert.pem"

#PS C:\Program Files (x86)\Apache Software Foundation\Apache2.2\bin> 

.\openssl.exe pkcs12 -in ..\certs\$OrgCert -nocerts -out ..\certs\$NewCert -nodes

#Enter Import Password:
#MAC verified OK
#Enter PEM pass phrase:
#Verifying - Enter PEM pass phrase:

#PS C:\Program Files (x86)\Apache Software Foundation\Apache2.2\bin> 
.\openssl.exe pkcs12 -in ..\certs\$OrgCert -clcerts -nokeys -out $newkey2
#Enter Import Password:
#PS C:\Program Files (x86)\Apache Software Foundation\Apache2.2\bin> 
.\openssl.exe rsa -in ..\certs\$NewCert -out ..\certs\$NewKey
#Enter pass phrase for ..\certs\apache.key.pem:
#writing RSA key
 for converting the chain 
#OpenSSL Convert P7B
#
#Convert P7B to PEM
#
#openssl pkcs7 -print_certs -in certificate.p7b -out certificate.cer
#
#Convert P7B to PFX
#
#openssl pkcs7 -print_certs -in certificate.p7b -out certificate.cer
#
#openssl pkcs12 -export -in certificate.cer -inkey privateKey.key -out certificate.pfx -certfile CACert.cer
#OpenSSL Convert PFX
#
#Convert PFX to PEM
#
#openssl pkcs12 -in certificate.pfx -out certificate.cer -nodes