merge of '23901caf941a236bc7d41f0a5c16d59200569467'

and '29d47675642c85ae46ec76cb4a5baefdf6a60703'
This commit is contained in:
str4d
2013-04-13 06:56:48 +00:00
2 changed files with 465 additions and 0 deletions

View File

@ -0,0 +1,108 @@
package net.i2p.crypto;
import java.util.Random;
import net.i2p.I2PAppContext;
import net.i2p.util.PooledRandomSource;
import net.i2p.util.RandomSource;
/**
*
*/
public class DummyPooledRandomSource extends PooledRandomSource {
public DummyPooledRandomSource(I2PAppContext context) {
super(context);
}
@Override
protected void initializePool(I2PAppContext context) {
_pool = new RandomSource[POOL_SIZE];
for (int i = 0; i < POOL_SIZE; i++) {
_pool[i] = new DummyRandomSource(context);
_pool[i].nextBoolean();
}
_nextPool = 0;
}
private class DummyRandomSource extends RandomSource {
private Random _prng;
public DummyRandomSource(I2PAppContext context) {
super(context);
// when we replace to have hooks for fortuna (etc), replace with
// a factory (or just a factory method)
_prng = new Random();
}
/**
* According to the java docs (http://java.sun.com/j2se/1.4.1/docs/api/java/util/Random.html#nextInt(int))
* nextInt(n) should return a number between 0 and n (including 0 and excluding n). However, their pseudocode,
* as well as sun's, kaffe's, and classpath's implementation INCLUDES NEGATIVE VALUES.
* WTF. Ok, so we're going to have it return between 0 and n (including 0, excluding n), since
* thats what it has been used for.
*
*/
@Override
public int nextInt(int n) {
if (n == 0) return 0;
int val = _prng.nextInt(n);
if (val < 0) val = 0 - val;
if (val >= n) val = val % n;
return val;
}
/**
* Like the modified nextInt, nextLong(n) returns a random number from 0 through n,
* including 0, excluding n.
*/
@Override
public long nextLong(long n) {
long v = _prng.nextLong();
if (v < 0) v = 0 - v;
if (v >= n) v = v % n;
return v;
}
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
@Override
public boolean nextBoolean() { return _prng.nextBoolean(); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
@Override
public void nextBytes(byte buf[]) { _prng.nextBytes(buf); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
@Override
public double nextDouble() { return _prng.nextDouble(); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
@Override
public float nextFloat() { return _prng.nextFloat(); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
@Override
public double nextGaussian() { return _prng.nextGaussian(); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
@Override
public int nextInt() { return _prng.nextInt(); }
/**
* override as synchronized, for those JVMs that don't always pull via
* nextBytes (cough ibm)
*/
@Override
public long nextLong() { return _prng.nextLong(); }
}
}

357
java-utils/OpenPGPFile.java Normal file
View File

@ -0,0 +1,357 @@
package net.i2p.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.IllegalArgumentException;
import java.security.Security;
import java.util.Date;
import java.util.Iterator;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.BCPGKey;
import org.bouncycastle.bcpg.DSAPublicBCPGKey;
import org.bouncycastle.bcpg.DSASecretBCPGKey;
import org.bouncycastle.bcpg.ElGamalPublicBCPGKey;
import org.bouncycastle.bcpg.ElGamalSecretBCPGKey;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyPacket;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import net.i2p.client.I2PSessionException;
import net.i2p.crypto.CryptoConstants;
import net.i2p.data.Base32;
import net.i2p.data.Certificate;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.data.PrivateKey;
import net.i2p.data.PrivateKeyFile;
import net.i2p.data.PublicKey;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.data.VerifiedDestination;
/**
* This helper class reads and writes Destinations in the OpenPGP Transferable
* Public Key and Transferable Secret Key formats, as defined in RFC 4880
* (see https://tools.ietf.org/html/rfc4880 for specifications).
*
* @author str4d
*/
public class OpenPGPFile {
public static void main(String args[]) {
Security.addProvider(new BouncyCastleProvider());
PGPUtil.setDefaultProvider("BC");
if (args.length == 0 || args[0].equals("-?")) {
System.err.println("Syntax: OpenPGPFile [options] <command>");
System.err.println("");
System.err.println("Commands:");
System.err.println(" -e, --export <eepPriv.dat> <pgpFile>");
System.err.println(" -i, --import <pgpFile> <eepPriv.dat>");
System.err.println("");
System.err.println("Options:");
System.err.println(" -a, --armor");
System.err.println(" -f, --force-write");
return;
}
// Parse options
boolean armorOutput = false;
boolean forceWrite = false;
int numOpts = 0;
while(numOpts < args.length) {
if (args[numOpts].equals("-a") || args[numOpts].equals("--armor")) {
armorOutput = true;
} else if (args[numOpts].equals("-f") || args[numOpts].equals("--force-write")) {
forceWrite = true;
} else {
// No more options
break;
}
numOpts++;
}
// Parse commands
OpenPGPFile opf = null;
try {
if (args[numOpts].equals("-e") || args[numOpts].equals("--export")) {
if (args.length-numOpts < 3) {
System.err.println("Usage: OpenPGPFile "+args[numOpts]+" <eepPriv.dat> <pgpFile> [pubFile]");
return;
}
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("GPG passphrase to encrypt with: ");
char[] passPhrase = br.readLine().toCharArray();
opf = new OpenPGPFile();
opf.readPrivateKeyFile(new File(args[numOpts+1]));
opf.exportKeys();
opf.writeOpenPGPFile(new File(args[numOpts+2]), passPhrase, armorOutput, forceWrite, (args.length-numOpts>3 ? new File(args[numOpts+3]) : null));
} else if (args[numOpts].equals("-i") || args[numOpts].equals("--import")) {
if (args.length-numOpts < 3) {
System.err.println("Usage: OpenPGPFile "+args[numOpts]+" <pgpFile> <eepPriv.dat>");
return;
}
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("GPG passphrase to decrypt with: ");
char[] passPhrase = br.readLine().toCharArray();
opf = new OpenPGPFile();
opf.readOpenPGPFile(new File(args[numOpts+1]), passPhrase);
opf.importKeys();
opf.writePrivateKeyFile(new File(args[numOpts+2]), forceWrite);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public OpenPGPFile() {
this.dest = null;
this.privKey = null;
this.signingPrivKey = null;
this.lastMod = null;
this.pgpSigKeyPair = null;
this.pgpEncKeyPair = null;
}
/**
* Read in I2P keys from a file.
*/
public void readPrivateKeyFile(File pkFile) throws I2PSessionException, IOException, DataFormatException {
if (pkFile.exists())
this.lastMod = new Date(pkFile.lastModified());
PrivateKeyFile pkf = new PrivateKeyFile(pkFile);
this.dest = pkf.getDestination();
this.privKey = pkf.getPrivKey();
this.signingPrivKey = pkf.getSigningPrivKey();
}
/**
* Write out I2P keys to a file.
*/
public void writePrivateKeyFile(File pkFile, boolean forceWrite) throws IOException, DataFormatException {
if(!pkFile.exists() || forceWrite) {
PrivateKeyFile pkf = new PrivateKeyFile(pkFile, this.dest, this.privKey, this.signingPrivKey);
pkf.write();
pkFile.setLastModified(this.lastMod.getTime());
}
}
/**
* Read in OpenPGP keys from a file.
*/
public void readOpenPGPFile(File pgpFile, char[] passPhrase) throws FileNotFoundException, IOException, PGPException {
PGPSecretKeyRing pgpKeys = new PGPSecretKeyRing(
PGPUtil.getDecoderStream(new FileInputStream(pgpFile)),
new JcaKeyFingerprintCalculator());
Iterator it = pgpKeys.getSecretKeys();
// Only take the first two keys, and assume that the master
// key is the DSA signing key and the subkey is the ElGamal
// encryption key.
PGPSecretKey pgpSigSecret = (PGPSecretKey)it.next();
PGPSecretKey pgpEncSecret = (PGPSecretKey)it.next();
this.pgpSigKeyPair = new PGPKeyPair(
pgpSigSecret.getPublicKey(),
pgpSigSecret.extractPrivateKey(
new JcePBESecretKeyDecryptorBuilder().build(passPhrase)));
this.pgpEncKeyPair = new PGPKeyPair(
pgpEncSecret.getPublicKey(),
pgpEncSecret.extractPrivateKey(
new JcePBESecretKeyDecryptorBuilder().build(passPhrase)));
this.identity = (String)pgpSigSecret.getUserIDs().next();
}
/**
* Write out OpenPGP keys to a file.
*/
public void writeOpenPGPFile(File pgpFile, char[] passPhrase, boolean armor, boolean forceWrite) throws IOException, PGPException {
writeOpenPGPFile(pgpFile, passPhrase, armor, forceWrite, null);
}
/**
* Write out OpenPGP keys to a file.
*/
public void writeOpenPGPFile(File pgpFile, char[] passPhrase, boolean armor, boolean forceWrite, File pubFile) throws IOException, PGPException {
OutputStream secretOut = null;
OutputStream publicOut = null;
if(!pgpFile.exists() || forceWrite) {
try {
secretOut = new FileOutputStream(pgpFile);
if (armor) {
secretOut = new ArmoredOutputStream(secretOut);
}
// Set up the PGP keyring
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1);
PGPKeyRingGenerator pgpGen = new PGPKeyRingGenerator(
PGPSignature.DEFAULT_CERTIFICATION,
this.pgpSigKeyPair,
this.identity,
sha1Calc,
null,
null,
new JcaPGPContentSignerBuilder(this.pgpSigKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1),
new JcePBESecretKeyEncryptorBuilder(SymmetricKeyAlgorithmTags.CAST5, sha1Calc).build(passPhrase));
pgpGen.addSubKey(this.pgpEncKeyPair);
// Write out the secret keyring
pgpGen.generateSecretKeyRing().encode(secretOut);
secretOut.flush();
// If a public file is supplied, write out the public keyring as well
if (pubFile != null && (!pubFile.exists() || forceWrite)) {
publicOut = new FileOutputStream(pubFile);
if (armor) {
publicOut = new ArmoredOutputStream(publicOut);
}
pgpGen.generatePublicKeyRing().encode(publicOut);
publicOut.flush();
}
} finally {
if (secretOut != null) {
try { secretOut.close(); } catch (IOException ioe) {}
}
if (publicOut != null) {
try { publicOut.close(); } catch (IOException ioe) {}
}
}
}
}
/**
* Export the I2P key representations to the OpenPGP key representations.
* This will silently overwrite the current contents of the OpenPGP key
* representations.
*/
public void exportKeys() throws PGPException {
BCPGKey sigPubKey = new DSAPublicBCPGKey(
CryptoConstants.dsap,
CryptoConstants.dsaq,
CryptoConstants.dsag,
new NativeBigInteger(1, this.dest.getSigningPublicKey().getData()));
BCPGKey sigPrivKey = new DSASecretBCPGKey(
new NativeBigInteger(1, this.signingPrivKey.getData()));
BCPGKey encPubKey = new ElGamalPublicBCPGKey(
CryptoConstants.elgp,
CryptoConstants.elgg,
new NativeBigInteger(1, this.dest.getPublicKey().getData()));
BCPGKey encPrivKey = new ElGamalSecretBCPGKey(
new NativeBigInteger(1, this.privKey.getData()));
PGPPublicKey pgpSigPubKey = new PGPPublicKey(
new PublicKeyPacket(PublicKeyAlgorithmTags.DSA, this.lastMod, sigPubKey),
new JcaKeyFingerprintCalculator());
this.pgpSigKeyPair = new PGPKeyPair(pgpSigPubKey, new PGPPrivateKey(
pgpSigPubKey.getKeyID(),
pgpSigPubKey.getPublicKeyPacket(),
sigPrivKey));
PGPPublicKey pgpEncPubKey = new PGPPublicKey(
new PublicKeyPacket(PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT, this.lastMod, encPubKey),
new JcaKeyFingerprintCalculator());
this.pgpEncKeyPair = new PGPKeyPair(pgpEncPubKey, new PGPPrivateKey(
pgpEncPubKey.getKeyID(),
pgpEncPubKey.getPublicKeyPacket(),
encPrivKey));
this.identity = Base32.encode(this.dest.calculateHash().getData()) + ".b32.i2p";
}
/**
* Import the OpenPGP key representations to the I2P key representations.
* This will silently overwrite the current contents of the I2P key
* representations. If any of the cryptographic constants in the OpenPGP
* keys do not match the I2P constants, an IllegalArgumentException is
* thrown.
*/
public void importKeys() throws IllegalArgumentException {
DSAPublicBCPGKey sigPubKey = (DSAPublicBCPGKey)pgpSigKeyPair.getPublicKey().getPublicKeyPacket().getKey();
DSASecretBCPGKey sigPrivKey = (DSASecretBCPGKey)pgpSigKeyPair.getPrivateKey().getPrivateKeyDataPacket();
ElGamalPublicBCPGKey encPubKey = (ElGamalPublicBCPGKey)pgpEncKeyPair.getPublicKey().getPublicKeyPacket().getKey();
ElGamalSecretBCPGKey encPrivKey = (ElGamalSecretBCPGKey)pgpEncKeyPair.getPrivateKey().getPrivateKeyDataPacket();
// Verify the cryptographic constants
if (!CryptoConstants.dsap.equals(sigPubKey.getP()) ||
!CryptoConstants.dsaq.equals(sigPubKey.getQ()) ||
!CryptoConstants.dsag.equals(sigPubKey.getG()) ||
!CryptoConstants.elgp.equals(encPubKey.getP()) ||
!CryptoConstants.elgg.equals(encPubKey.getG()))
throw new IllegalArgumentException("Cryptographic constants do not match");
// BigInteger.toByteArray returns SIGNED integers, but since they're
// positive, signed two's complement is the same as unsigned
byte[] pubKeyData = encPubKey.getY().toByteArray();
byte[] privKeyData = encPrivKey.getX().toByteArray();
byte[] signingPubKeyData = sigPubKey.getY().toByteArray();
byte[] signingPrivKeyData = sigPrivKey.getX().toByteArray();
PublicKey pubKey = new PublicKey();
pubKey.setData(padBuffer(pubKeyData, PublicKey.KEYSIZE_BYTES));
SigningPublicKey signingPubKey = new SigningPublicKey();
signingPubKey.setData(padBuffer(signingPubKeyData, SigningPublicKey.KEYSIZE_BYTES));
this.dest = new VerifiedDestination();
this.dest.setPublicKey(pubKey);
this.dest.setSigningPublicKey(signingPubKey);
this.dest.setCertificate(Certificate.NULL_CERT);
this.privKey = new PrivateKey();
this.privKey.setData(padBuffer(privKeyData, PrivateKey.KEYSIZE_BYTES));
this.signingPrivKey = new SigningPrivateKey();
this.signingPrivKey.setData(padBuffer(signingPrivKeyData, SigningPrivateKey.KEYSIZE_BYTES));
this.lastMod = pgpSigKeyPair.getPublicKey().getCreationTime();
}
/**
* Pad the buffer w/ leading 0s or trim off leading bits so the result is the
* given length.
*/
private final static byte[] padBuffer(byte src[], int length) {
byte buf[] = new byte[length];
if (src.length > buf.length) // extra bits, chop leading bits
System.arraycopy(src, src.length - buf.length, buf, 0, buf.length);
else if (src.length < buf.length) // short bits, padd w/ 0s
System.arraycopy(src, 0, buf, buf.length - src.length, src.length);
else
// eq
System.arraycopy(src, 0, buf, 0, buf.length);
return buf;
}
// I2P key representations
private Destination dest;
private PrivateKey privKey;
private SigningPrivateKey signingPrivKey;
private Date lastMod;
// OpenPGP key representations
private PGPKeyPair pgpSigKeyPair;
private PGPKeyPair pgpEncKeyPair;
private String identity;
}