Created
October 10, 2015 21:42
-
-
Save tengel/7bbae41ff1443ac1f8fa to your computer and use it in GitHub Desktop.
Converter for OTR keys between libotr (Pidgin, Psi+) and Xabber.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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