Clone a KeePass database with new credentials in PowerShell

20 February 2015

This article is about using password managers, if you don't know why this is a good idea, read the article: The Real Life Risks Of Re Using The Same Passwords by Bill Hess.

I recently migrated from Password Safe to KeePass for storing my passwords.

I'm using a very long and complex master-password, which is kind of annoying to type in every time I open the program.
KeePass offers more than just a password for authentication. You can also specify a key file or use your Windows account. On my main workstation at home, my Windows account it pretty secure. However I don't want to use it as a key for my main KeePass database.
My solution was to make a copy for local read-only use and set the master key with a less-strong password and my Windows user account.
This way I only have to type a short password, but when I use KeePass on different devices I still only use the strong password.

However after I changed any data in my master database, I had to make a new copy and change the masterkey again.
That's only a few clicks, but I rather automate things. So I wrote a PowerShell script that makes a copy of the database and sets the new master key

Save the following into Copy-KeePassDB.ps1

param(
    [parameter(Mandatory=$true)][string]$SourceDB,
    [parameter(Mandatory=$true)][string]$TargetDB,
    [parameter(Mandatory=$true)][string]$KeePassFolder
)

$pass = Read-Host 'Type the password for Master-KeePass database' -AsSecureString
$PasswordForSource = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
    [Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass))

$pass2 = Read-Host 'Type the password for new Local-KeePass database' -AsSecureString
$PasswordForTarget = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
    [Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass2))

#Load all .NET binaries in the folder
(Get-ChildItem -recurse $KeePassFolder | Where-Object {($_.Extension -EQ ".dll") -or ($_.Extension -eq ".exe")} `
| ForEach-Object { $AssemblyName=$_.FullName; Try {[Reflection.Assembly]::LoadFile($AssemblyName) } Catch{ }} ) | out-null

# remove an existing copy and copy the master
If (Test-Path $TargetDB)
{
    Remove-Item $TargetDB -force
}

Copy-Item  $SourceDB -Destination $TargetDB

# Open the new database
$PwDatabase = new-object KeePassLib.PwDatabase
$m_pKey = new-object KeePassLib.Keys.CompositeKey
$m_pKey.AddUserKey((New-Object KeePassLib.Keys.KcpPassword($PasswordForSource)));
$m_ioInfo = New-Object KeePassLib.Serialization.IOConnectionInfo
$m_ioInfo.Path = $TargetDB
$IStatusLogger = New-Object KeePassLib.Interfaces.NullStatusLogger
$PwDatabase.Open($m_ioInfo,$m_pKey,$IStatusLogger)

# Create a new key for the database
$m_pKeyNew = new-object KeePassLib.Keys.CompositeKey
$m_pKeyNew.AddUserKey((New-Object KeePassLib.Keys.KcpPassword($PasswordForTarget)));
$m_pAccount = new-object KeePassLib.Keys.KcpUserAccount
$m_pKeyNew.AddUserKey($m_pAccount);

# Set the new key and save it
$PwDatabase.MasterKey = $m_pKeyNew     
$PwDatabase.Save($null)
$PwDatabase.Close()
$PwDatabase = $null

Write-Host "Database copied and new credentials set, only accessible to $env:USERDOMAIN\$env:UserName"
Now you can use a helper-script to set your file paths:
Copy-KeePassDB.ps1 -SourceDB "C:\mymaster.kdbx" -TargetDB "C:\mylocal.kdbx" -KeePassFolder "C:\Program Files\KeepPass\"

You will be asked for two passwords, the old master one and the new weaker one.

Pages in this section

Categories

ASP.Net | Community | Development | IIS | IT Pro | Security | SQL (Server) | Tools | Web | Work on the road | Windows