package com.limegroup.gnutella.simpp; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.io.UnsupportedEncodingException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.EncodedKeySpec; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import com.bitzi.util.Base32; import com.limegroup.gnutella.security.SignatureVerifier; import com.limegroup.gnutella.util.CommonUtils; import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.Log; public class SimppDataVerifier { private static final Log LOG = LogFactory.getLog(SimppDataVerifier.class); private static final byte SEP = (byte)124; //We use DSA keys since they are fast, secure and the standard for //signatures public final String DSA_ALGORITHM = "DSA"; private byte[] simppPayload; private byte[] verifiedData; /** * Constructor. payload contains bytes with the following format: * [Base32 Encoded signature bytes]|[versionnumber|<props in xml format>] */ public SimppDataVerifier(byte[] payload) { this.simppPayload = payload; } public boolean verifySource() { int sepIndex = findSeperator(simppPayload); if(sepIndex < 0) //no separator? this cannot be the real thing. return false; byte[] temp = new byte[sepIndex]; System.arraycopy(simppPayload, 0, temp, 0, sepIndex); String base32 = null; try { base32 = new String(temp, "UTF-8"); } catch (UnsupportedEncodingException uex) { return false; } byte[] signature = Base32.decode(base32); byte[] propsData = new byte[simppPayload.length-1-sepIndex]; System.arraycopy(simppPayload, sepIndex+1, propsData, 0, simppPayload.length-1-sepIndex); PublicKey pk = getPublicKey(); if(pk == null) return false; String algo = DSA_ALGORITHM; SignatureVerifier verifier = new SignatureVerifier(propsData, signature, pk, algo); boolean ret = verifier.verifySignature(); if(ret) verifiedData = propsData; return ret; } /** * @return the verified bytes. Null if we were unable to verify */ public byte[] getVerifiedData() { return verifiedData; } ////////////////////////////helpers///////////////////// private PublicKey getPublicKey() { //1. Get the file that has the public key //File pubKeyFile = File pubKeyFile=new File(CommonUtils.getUserSettingsDir(), "pub1.key"); //TODO: work this out with the setting telling us which public key to //use String base32Enc = null; RandomAccessFile raf = null; //2. read the base32 encoded string of the public key try { raf = new RandomAccessFile(pubKeyFile,"r"); byte[] bytes = new byte[(int)raf.length()]; raf.readFully(bytes); base32Enc = new String(bytes, "UTF-8"); } catch (IOException iox) { LOG.error("IOX reading file", iox); return null; } finally { try { if(raf!=null) raf.close(); } catch (IOException iox) {} } //3. convert the base32 encoded String into the original bytes byte[] pubKeyBytes = Base32.decode(base32Enc); //4. Make a public key out of it PublicKey ret = null; try { KeyFactory factory = KeyFactory.getInstance(DSA_ALGORITHM); EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyBytes); ret = factory.generatePublic(keySpec); } catch(NoSuchAlgorithmException nsax) { LOG.error("no algorithm", nsax); } catch(InvalidKeySpecException iksx) { LOG.error("invalid key", iksx); } return ret; } static int findSeperator(byte[] data) { boolean found = false; int i = 0; for( ; i< data.length; i++) { if(data[i] == SEP) { found = true; break; } } if(found) return i; return -1; } }