Skip to content

Instantly share code, notes, and snippets.

@tengel
Created October 10, 2015 21:42
Show Gist options
  • Save tengel/7bbae41ff1443ac1f8fa to your computer and use it in GitHub Desktop.
Save tengel/7bbae41ff1443ac1f8fa to your computer and use it in GitHub Desktop.
Converter for OTR keys between libotr (Pidgin, Psi+) and Xabber.
/**
* Converter for OTR keys between libotr (Pidgin, Psi+) and Xabber.
*
* Key files:
* Psi+: ~/.local/share/psi+/profiles/default/otr.keys
* Xabber: /data/data/com.xabber.androiddev/databases/xabber.db
*
* Compile:
* javac OtrKeyConverter.java
*
*
* Usage:
*
* Xabber -> libotr (key writtten to stdout)
* java -classpath ".:sqlite-jdbc-3.8.11.2.jar" OtrKeyConverter user@example.com
* xabber.db
*
* libotr -> Xabber (key written to database)
* java -classpath ".:sqlite-jdbc-3.8.11.2.jar" OtrKeyConverter user@example.com
* xabber.db otr.keys
*
*
*
*
*/
import java.security.KeyFactory;
import java.security.KeyPair;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.DSAPrivateKey;
import java.math.BigInteger;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;
import java.sql.*;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
class OtrKey
{
BigInteger p;
BigInteger q;
BigInteger g;
BigInteger y;
BigInteger x;
String account;
String protocol;
public String toLibotr()
{
return
"(privkeys\n" +
" (account\n" +
"(name \"" + account + "\")\n" +
"(protocol " + protocol + ")\n" +
"(private-key\n" +
" (dsa\n" +
" (p #" + p.toString(16) + "#)\n" +
" (q #" + q.toString(16) + "#)\n" +
" (g #" + g.toString(16) + "#)\n" +
" (y #" + y.toString(16) + "#)\n" +
" (x #" + x.toString(16) + "#)\n" +
" )\n" +
" )\n" +
" )\n" +
")\n";
}
public byte[] toXabberPrivate()
throws NoSuchAlgorithmException, InvalidKeySpecException
{
DSAPrivateKeySpec privSpec = new DSAPrivateKeySpec(x, p, q, g);
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
DSAPrivateKey privKey = (DSAPrivateKey) keyFactory.generatePrivate(privSpec);
PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(
privKey.getEncoded());
return encodedKeySpec.getEncoded();
}
public byte[] toXabberPublic()
throws NoSuchAlgorithmException, InvalidKeySpecException
{
DSAPublicKeySpec pubSpec = new DSAPublicKeySpec(y, p, q, g);
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
DSAPublicKey pubKey = (DSAPublicKey) keyFactory.generatePublic(pubSpec);
X509EncodedKeySpec encodedKeySpec = new X509EncodedKeySpec(pubKey.getEncoded());
return encodedKeySpec.getEncoded();
}
}
class OtrKeyConverter
{
private static OtrKey readXabberKey(byte[] privBytes, byte[] pubBytes)
throws NoSuchAlgorithmException, InvalidKeySpecException
{
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(privBytes);
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(pubBytes);
KeyFactory keyFactory;
keyFactory = KeyFactory.getInstance("DSA");
DSAPublicKey pubKey = (DSAPublicKey) keyFactory.generatePublic(pubKeySpec);
DSAPrivateKey privKey = (DSAPrivateKey) keyFactory.generatePrivate(privKeySpec);
OtrKey target = new OtrKey();
target.p = pubKey.getParams().getP();
target.q = pubKey.getParams().getQ();
target.g = pubKey.getParams().getG();
target.y = pubKey.getY();
target.x = privKey.getX();
target.account = "foobar";
target.protocol = "prpl-jabber";
return target;
}
private static OtrKey readLibotrKey(String keyFile)
throws FileNotFoundException, IOException
{
OtrKey otrKey = new OtrKey();
InputStream keyStream = new FileInputStream(keyFile);
byte[] keyBytes = new byte[10000];
keyStream.read(keyBytes);
String keyString = new String(keyBytes);
String[] tokens = keyString.split("\\(");
for (String t: tokens)
{
t = t.trim();
if (t.startsWith("p "))
{
otrKey.p = readNumber(t);
}
if (t.startsWith("q "))
{
otrKey.q = readNumber(t);
}
if (t.startsWith("g "))
{
otrKey.g = readNumber(t);
}
if (t.startsWith("y "))
{
otrKey.y = readNumber(t);
}
if (t.startsWith("x "))
{
otrKey.x = readNumber(t);
}
}
otrKey.account = "foobar";
otrKey.protocol = "prpl-jabber";
return otrKey;
}
public static BigInteger readNumber(String s)
{
BigInteger bi = new BigInteger(s.substring(s.indexOf('#') + 1,
s.lastIndexOf('#')), 16);
return bi;
}
public static void main(String[] args)
{
if (args.length != 2 && args.length != 3)
{
System.out.println("Xabber -> libotr:\n" +
"OtrKeyConverter user@example.com xabber.db\n" +
"\n" +
"libotr -> Xabber\n" +
"OtrKeyConverter user@example.com xabber.db otr.keys");
System.exit(2);
}
String[] jid = args[0].split("@");
if (jid.length != 2)
{
System.out.println("invalid jid");
System.exit(2);
}
String jidUser = jid[0].trim();
String jidServer = jid[1].trim();
String dbFile = args[1];
try
{
Class.forName("org.sqlite.JDBC");
Connection c = DriverManager.getConnection("jdbc:sqlite:" + dbFile);
if (args.length == 2)
{
// xabber to libotr
System.out.println("Reading key for " + jidUser + "@" + jidServer +
" from " + dbFile);
Statement stmt = c.createStatement();
ResultSet rs = stmt.executeQuery("SELECT public_key,private_key " +
"FROM accounts " +
"WHERE server_name='" + jidServer +
"' and user_name='" + jidUser + "';");
if (rs.next())
{
OtrKey otrKey = readXabberKey(rs.getBytes("private_key"),
rs.getBytes("public_key"));
if (otrKey != null)
{
System.out.println(otrKey.toLibotr());
}
else
{
System.out.println("something went wrong");
}
}
else
{
System.out.println("user not found in database");
}
}
else if (args.length == 3)
{
// libotr to xabber
System.out.println("Writing key for " + jidUser + "@" + jidServer +
" to " + dbFile);
OtrKey otrKey = readLibotrKey(args[2]);
PreparedStatement stmt = c.prepareStatement(
"UPDATE accounts " +
"SET public_key = ?, private_key = ? " +
"WHERE server_name='" + jidServer +
"' and user_name='" + jidUser + "';");
stmt.setBytes(1, otrKey.toXabberPublic());
stmt.setBytes(2, otrKey.toXabberPrivate());
int result = stmt.executeUpdate();
System.out.println(result + " rows updated");
}
}
catch (Exception e)
{
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
System.exit(1);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment