Menu

#2840 Export Credentials from KeePass with Parent Group

KeePass_2.x
closed
nobody
5
2023-10-24
2023-09-20
Kayvan
No

Hello,

I need to export the contents of KeePass and import them to another password managment tool (CyberArk). The issue I am having is when exporting the KeePass vault to CSV, all entries loose the parent directory structure. I can see in the options tab for export there is an option for "Additionally export parent groups" however this is not available for CSV file format and CyberArk only accepts CSV for import.

Are there any work arounds for this issue or have others encountered this issue before and what was the resolution.

Any help is greatly appreciated, thank you.

Discussion

  • Paul

    Paul - 2023-09-20

    To export all data you need to use XML format, then convert the XML to CSV and add/remove columns as required.
    You can use the KeePass built-in transform export, but you will need an XSL sheet to define the fields you want to export.
    You can use an online converter, not recommended.
    A 3rd party utility.

    cheers, Paul

     
    • Kayvan

      Kayvan - 2023-09-20

      Hello Paul,

      Thank you for your response. From the export options I can see KeePass XML (2.x) listed and is the only option including XML however, after selecting KeePass XML (2.x), under options the "Additionally export parent group" options is grey-ed out and I cannot select it.

      As for a 3rd party utility to convert XML to CSV, do you have any recommendations?

      Thank you for you help.

       
  • Paul

    Paul - 2023-09-21

    XML exports contain all KeePass data. You can't select anything.

    I've been testing XML to CSV conversion and it doesn't work because it doesn't know how to treat the KeePass data. Your only solution is export using "Transform using XSL Stylesheet".
    KeePass has a number of stylesheets but not one for a full CSV conversion.
    I'm having a play to see if I can make one, stay tuned...

    cheers, Paul

     
    • Kayvan

      Kayvan - 2023-09-22

      Thank you Paul, I look forward to your update. Have a good weekend.

       
  • Paul

    Paul - 2023-09-22

    XML transform isn't looking good because you can't (easily, at all?) get all custom fields and group structure into a tabular format. I'm having a go in PowerShell.

    cheers, Paul

     
  • Paul

    Paul - 2023-09-27

    Edit: the latest version can be found here.

    Here it is! V1.1
    You can either copy the code below and save or download the file direct. (Don't forget to allow PS1 files to run on your system.)

    Use:
    Export your database as KeePass XML (2.x).
    Run the PS1.
    Choose the file you just exported.
    Choose a CSV file.
    Import the CSV into your spreadsheet program and check / clean.
    Import CSV into whatever.

    cheers, Paul

    ##############################
    # KeePass XML to CSV converter V1.1
    # Fixed bug, not collecting root entries
    # Date:20230930
    ##############################
    
    ## function Get-FileName
    Function Get-FileName {
        param($Type='open', $Title='Select File', $FileFilter)
        [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
        if ($Type -eq 'open') {$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog}
        if ($Type -eq 'create') {$OpenFileDialog = New-Object System.Windows.Forms.SaveFileDialog}
        $OpenFileDialog.filter = $FileFilter
        $OpenFileDialog.Title = $Title
        $OpenFileDialog.ShowDialog() | Out-Null
        $OpenFileDialog.ShowHelp = $true
        $OpenFileDialog.filename
    } 
    ## end function Get-FileName
    
    ## Function AddToEntryArray
    Function AddToEntryArray($Key,$Value) {
        if ($Key -eq $KPGP) { # add group path
           $global:aEntryValues = New-Object string[] $FieldNames.count # init array
            $aGroups.Keys | ForEach-Object {
                if ($Value -eq $_) {
                    $global:aEntryValues[0] = $aGroups."$_"  # Group path
                    Return
                }
            }
        }
        else {
            # add values
            $i = 0
            $FieldNames | ForEach-Object { # lookup key
                If ($Key -eq $_) {
                    $global:aEntryValues[$i] = $Value
                    Return
                }
            $i++
            }
        }
    }
    ## End Function AddToEntryArray
    
    ## Function FormatData
    Function FormatData($aData) { # convert array to formatted string
        $i = 0; $sData = $null
        $aData | ForEach-Object { # add data at correct offset
            if ($i -eq 0) {
                if ($_ -eq $null){$sData += ('""')}
                else {$sData += '"' + $_.replace('"', '""') + '"'}
            }
            else {
                if ($_ -eq $null){$sData += (',""')}
                else {$sData += ',"' + $_.replace('"', '""') + '"'}
            }
            $i++
        }
        $sData += "`r`n"
        Return $sData
        }
    ## End Function FormatData
    
    #########################
    ## Begin routine
    
    # Ask for input file
    $XMLFileName = Get-FileName -Title "Select XML file to Import" -FileFilter "XML (*.xml)| *.xml"
    if ($XMLFileName -eq '') {exit}
    
    # Ask for output file
    $CSVFileName = Get-FileName -Type "create" -Title "Select an Output File for the CSV" -FileFilter "CSV (*.csv)| *.csv|Text (*.txt)| *.txt|All Files (*.*)| *.*"
    if ($CSVfileName -eq '') {exit}
    
    $xml_file = New-Object xml; $xml_file.Load($XMLFileName)
    
    # init 
    $KPGP = 'KeePassGroupFullPath' # column name for full group path
    
    # collect group nodes
    $DatabaseUUID = $xml_file.SelectNodes("/*/*/Group").UUID
    $GroupNodes = $xml_file.SelectNodes("//Group/Group")
    
    # List groups with heirarchy - use a dictionary with UUID as key
    $NodeName = $null
    $aGroups = $null
    $aGroups += @{$DatabaseUUID = '\'} # add root to List
    $GroupNodes | ForEach-Object {
        $nodeUUID = $_.UUID
        While ($_.UUID -ne $DatabaseUUID){
            $NodeName = '\' + $_.name + $NodeName
            $_ = $_.parentnode
        }
        $aGroups += @{$nodeUUID = $NodeName}
        $NodeName = $null
    }
    # collect field names in header, default values first
    $FieldNames = @($KPGP, "Title", "UserName", "Password", "URL", "Notes", "ExpiryTime", "Tags")
    $xp="//Entry/String"
    $tmpFieldNames = $xml_file.SelectNodes($xp).Key | Sort-Object | Get-Unique
    $tmpFieldNames | ForEach-Object {
        if ($FieldNames.contains($_)){ # name already exists
            #do nothing
        }
        else {$FieldNames += $_}
    }
    # init output
    $sOutputData = $null
    $j = 0
    $FieldNames | ForEach-Object {
        if ($j -eq 0){
        $sOutputData += '"' + $_ + '"'
        }
        else {
            $sOutputData += ',"' + $_ + '"'
        }
        $j++
    }
    $sOutputData += "`r`n"
    
    # loop through all entries
    $nodeEntry = $xml_file.SelectNodes("//Group/Entry")
    $nodeEntry | ForEach-Object {
        AddToEntryArray $KPGP $_.ParentNode.UUID # add group name first
        if ($_.Tags){
            AddToEntryArray 'Tags' $_.Tags # add Tags
        }
        if ($_.Times.Expires -eq 'True'){
            AddToEntryArray 'ExpiryTime' $_.Times.ExpiryTime # add ExpiryTime
        }
        $_.String | ForEach-Object { # add all strings
            if ($_.value -ne $null){
                if ($_.value.GetType().Name -eq 'XmlElement'){
                    if ($_.value.InnerText -ne ""){AddToEntryArray $_.key $_.value.InnerText}
                    }
            else {
                if ($_.value -ne ""){
                   AddToEntryArray $_.key $_.value
                } 
                }
            }
        }
        # collect entry data in output
        $sOutputData += FormatData($aEntryValues)
    }
    [System.IO.File]::WriteAllLines($CSVFileName, $sOutputData) # write UTF8 w/o BOM
    Return  
    # end of routine
    
     

    Last edit: Paul 2024-06-12
  • Dominik Reichl

    Dominik Reichl - 2023-10-24
    • status: open --> closed
    • Group: KeePass --> KeePass_2.x
    • Priority: 3 --> 5
     

Log in to post a comment.

MongoDB Logo MongoDB