I have too many passwords to remember, so I’ve been using a password manager for years. First there was Keyring for Palm OS, then 1Password on the Mac. 1Password’s a very polished commercial program, but it only has Mac and Windows desktop clients. Sadly, it had to go.
Finding a replacement was tough. It needed to be free, and yet cross-platform. It needed to work on iOS and Android. It also needed to integrate with a cloud service like Dropbox so I could keep my passwords in sync. The only program that met all of these requirements was KeePassX. I’ve stuck with the stable (v 0.4.3) branch rather than the flashy 2.0 version, as the older database format does all I need and is fully portable. MiniKeePass on iOS and KeePassDroid on Android look after my mobile needs. But first, I needed to get my password data out of 1Password.
1Password offers two export formats: a delimited text format (which seemed to drop some of the more obscure fields), and the 1Password Interchange Format (
So armed with my favourite swiss army chainsaw, I set about picking the file apart. JSON::XS and Data::Dumper::Simple were invaluable for this process, and pretty soon I had all the fields picked apart that I cared about. I decided to write a converter that wrote KeePassX 1.x XML, since it was readily imported into KeePassX, would could then write a database readable by all of the KeePass variants.
To run this converter you’ll need Perl, the JSON::XS and Data::Dumper::Simple modules, and if your Perl is older than about 5.12, the Time::Piece module (it’s a core module for newer Perls, so you don’t have to install it). Here’s the code:
#!/usr/bin/perl -w # 1pw2kpxxml.pl - convert 1Password Exchange file to KeePassX XML # created by scruss on 02013/04/21 use strict; use JSON::XS; use HTML::Entities; use Time::Piece; # print xml header print <<HEADER; <!DOCTYPE KEEPASSX_DATABASE> <database> <group> <title>General</title> <icon>2</icon> HEADER ############################################################## # Field Map # # 1Password KeePassX # ============================ ============================== # title title # username username # password password # location url # notesPlain comment # - icon # createdAt creation # - lastaccess (use updatedAt) # updatedAt lastmod # - expire ('Never') # 1PW exchange files are made of single lines of JSON (O_o) # interleaved with separators that start '**' while (<>) { next if (/^\*\*/); # skip separator my $rec = decode_json($_); # throw out records we don't want: # - 'trashed' entries # - system.sync.Point entries next if ( exists( $rec->{'trashed'} ) ); next if ( $rec->{'typeName'} eq 'system.sync.Point' ); print ' <entry>', "\n"; # begin entry ################ # title field print ' <title>', xq( $rec->{'title'} ), '</title>', "\n"; ################ # username field - can be in one of two places my $username = ''; # 1. check secureContents as array foreach ( @{ $rec->{'secureContents'}->{'fields'} } ) { if ( ( exists( $_->{'designation'} ) && ( $_->{'designation'} eq 'username' ) ) ) { $username = $_->{'value'}; } } # 2. check secureContents as scalar if ( $username eq '' ) { $username = $rec->{'secureContents'}->{'username'} if ( exists( $rec->{'secureContents'}->{'username'} ) ); } print ' <username>', xq($username), '</username>', "\n"; ################ # password field - as username my $password = ''; # 1. check secureContents as array foreach ( @{ $rec->{'secureContents'}->{'fields'} } ) { if ( ( exists( $_->{'designation'} ) && ( $_->{'designation'} eq 'password' ) ) ) { $password = $_->{'value'}; } } # 2. check secureContents as scalar if ( $password eq '' ) { $password = $rec->{'secureContents'}->{'password'} if ( exists( $rec->{'secureContents'}->{'password'} ) ); } print ' <password>', xq($password), '</password>', "\n"; ################ # url field print ' <url>', xq( $rec->{'location'} ), '</url>', "\n"; ################ # comment field my $comment = ''; $comment = $rec->{'secureContents'}->{'notesPlain'} if ( exists( $rec->{'secureContents'}->{'notesPlain'} ) ); $comment = xq($comment); # pre-quote $comment =~ s,\\n,<br/>,g; # replace escaped NL with HTML $comment =~ s,\n,<br/>,mg; # replace NL with HTML print ' <comment>', $comment, '</comment>', "\n"; ################ # icon field (placeholder) print ' <icon>2</icon>', "\n"; ################ # creation field my $creation = localtime( $rec->{'createdAt'} ); print ' <creation>', $creation->datetime, '</creation>', "\n"; ################ # lastaccess field my $lastaccess = localtime( $rec->{'updatedAt'} ); print ' <lastaccess>', $lastaccess->datetime, '</lastaccess>', "\n"; ################ # lastmod field (= lastaccess) print ' <lastmod>', $lastaccess->datetime, '</lastmod>', "\n"; ################ # expire field (placeholder) print ' <expire>Never</expire>', "\n"; print ' </entry>', "\n"; # end entry } # print xml footer print <<FOOTER; </group> </database> FOOTER exit; sub xq { # encode string for XML $_ = shift; return encode_entities( $_, q/<>&"'/ ); }
To run it,
./1pw2kpxxml.pl data.1pif > data.xml
You can then import data.xml into KeePassX.
Please be careful to delete the 1PIF file and the data.xml once you’ve finished the export/import. These files contain all of your passwords in plain text; if they fell into the wrong hands, it would be a disaster for your online identity. Be careful that none of these files accidentally slip onto backups, too. Also note that, while I think I’m quite a trustworthy bloke, to you, I’m Some Random Guy On The Internet. Check this code accordingly; I don’t warrant it for anything save for looking like line noise.
Now on github: scruss / 1pw2kpxxml, or download: 1pw2kpxxml.zip (gpg signature: 1pw2kpxxml.zip.sig)
SHA1 Checksums:
- 3c25eb72b2cfe3034ebc2d251869d5333db74592 — 1pw2kpxxml.pl
- 99b7705ff30a2b157be3cfd29bb1d4f137920c25 — readme.txt
- de4a51fbe0dd6371b8d68674f71311a67da76812 — 1pw2kpxxml.zip
- f6bd12e33b927bff6999e9e80506aef53e6a08fa — 1pw2kpxxml.zip.sig.txt
The converter has some limitations:
- All attached files in the database are lost.
- All entries are stored under the same folder, with the same icon.
- It has not been widely tested, and as I’m satisfied with its conversion, it will not be developed further.
Works for me! Thanks!
Worked great! Sure, I had to add all entries to their respective groups. But still, the most important thing here is that no important data was lost. Awesome.
Thank you.
Thanks mate. This worked well.
Sorry about the loss of groups, Anon. I never used them (didn’t even know they existed). Let me put this up on github so more people can fix it up.
Hi , I am not experienced with Perl at all. I have tried to run perl2exe to make this script work on windows. But.. no luck )-:
could you please explain how I can run this code on windows?
Thanks a lot!
I’m not sure, sorry. Been a long time since I ran Perl on Windows. ActivePerl, maybe?
Hello,
I am a complete noob. But I know that this exactly what I need to do. Can anyone post a step by step of this process? I am running ubuntu 12.04 – I have a 1000 Doge that I can send in return for the help. Just post the address and I can send them over!
Thank you!
Under Ubuntu, you’ll need to install libjson-xs-perl:
sudo apt-get install libjson-xs-perl
very
Download the code either from this page, or from github: scruss / 1pw2kpxxml.
Export your 1Password database to a 1Password Interchange Format (1PIF) file. The actual data is a bit buried in a deeper folder, so have a dig about.
Then you can run the program from the terminal, assuming the program 1pw2kpxxml.pl and your data.1pif file are in the current folder:
chmod +x ./1pw2kpxxml.pl
./1pw2kpxxml.pl data.1pif > data.xml
You should now be able to import data.xml into Keepass. Let me know if you have problems.
Be very careful with how you handle the data.1pif and data.xml files. They have all your passwords, unencrypted!
My Dogecoin address is D9PSD9URAARwnXoTHfWDbjfJ2TmGYBvbaC
Hi, I am from Holland, and just installed linux for the first time on macbookpro 5.5
Your program works flawless on Xubuntu 14
Thanks for sharing!!
Still have one interesting question though:
How can you sync (with dropbox)?
Haven’t seen a plugin, do have seen it for keepass 1.x..
Hope you can help me with this
Thanks in advance,
Thomas
You just keep the Keepass database in your dropbox folder. No plugin required.
such a quick reply!
A simple solution, i was almost there 🙂
Thanks Scruss, you helped me out
Looks good! Thanks for being awesome.