From bb8b50baffc429aa711e8c1a756a01924007745f Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Wed, 13 Aug 2008 03:56:10 +0000 Subject: Client: Added password protection support Closes #10 --- .../fourisland/instadisc/AskForPasswordForm.form | 123 +++++++++++++++ .../fourisland/instadisc/AskForPasswordForm.java | 166 +++++++++++++++++++++ .../instadisc/Database/Subscription.java | 11 ++ .../src/com/fourisland/instadisc/Functions.java | 81 ++++++++++ .../src/com/fourisland/instadisc/Item/MD5.java | 17 +-- .../instadisc/Item/SubscriptionFile.java | 48 +++++- .../fourisland/instadisc/Item/WellFormedItem.java | 89 +++++++++++ .../resources/AskForPasswordForm.properties | 9 ++ 8 files changed, 521 insertions(+), 23 deletions(-) create mode 100644 client/trunk/src/com/fourisland/instadisc/AskForPasswordForm.form create mode 100644 client/trunk/src/com/fourisland/instadisc/AskForPasswordForm.java create mode 100644 client/trunk/src/com/fourisland/instadisc/Functions.java create mode 100644 client/trunk/src/com/fourisland/instadisc/resources/AskForPasswordForm.properties (limited to 'client/trunk/src') diff --git a/client/trunk/src/com/fourisland/instadisc/AskForPasswordForm.form b/client/trunk/src/com/fourisland/instadisc/AskForPasswordForm.form new file mode 100644 index 0000000..e16a917 --- /dev/null +++ b/client/trunk/src/com/fourisland/instadisc/AskForPasswordForm.form @@ -0,0 +1,123 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/client/trunk/src/com/fourisland/instadisc/AskForPasswordForm.java b/client/trunk/src/com/fourisland/instadisc/AskForPasswordForm.java new file mode 100644 index 0000000..5b80edc --- /dev/null +++ b/client/trunk/src/com/fourisland/instadisc/AskForPasswordForm.java @@ -0,0 +1,166 @@ +/* + * AskForPasswordForm.java + * + * Created on August 12, 2008, 4:24 PM + */ + +package com.fourisland.instadisc; + +/** + * + * @author hatkirby + */ +public class AskForPasswordForm extends javax.swing.JDialog { + + /** Creates new form AskForPasswordForm */ + public AskForPasswordForm(java.awt.Frame parent, boolean modal) { + super(parent, modal); + initComponents(); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + + jLabel1 = new javax.swing.JLabel(); + jLabel2 = new javax.swing.JLabel(); + jLabel3 = new javax.swing.JLabel(); + jPasswordField1 = new javax.swing.JPasswordField(); + jButton1 = new javax.swing.JButton(); + jButton2 = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setName("Form"); // NOI18N + + org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(com.fourisland.instadisc.InstaDiscApp.class).getContext().getResourceMap(AskForPasswordForm.class); + jLabel1.setFont(resourceMap.getFont("jLabel1.font")); // NOI18N + jLabel1.setText(resourceMap.getString("jLabel1.text")); // NOI18N + jLabel1.setName("jLabel1"); // NOI18N + + jLabel2.setText(resourceMap.getString("jLabel2.text")); // NOI18N + jLabel2.setName("jLabel2"); // NOI18N + + jLabel3.setText(resourceMap.getString("jLabel3.text")); // NOI18N + jLabel3.setName("jLabel3"); // NOI18N + + jPasswordField1.setText(resourceMap.getString("jPasswordField1.text")); // NOI18N + jPasswordField1.setName("jPasswordField1"); // NOI18N + + jButton1.setText(resourceMap.getString("jButton1.text")); // NOI18N + jButton1.setName("jButton1"); // NOI18N + jButton1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton1ActionPerformed(evt); + } + }); + + jButton2.setText(resourceMap.getString("jButton2.text")); // NOI18N + jButton2.setName("jButton2"); // NOI18N + jButton2.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton2ActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(jLabel1) + .addGap(136, 136, 136)) + .addGroup(layout.createSequentialGroup() + .addGap(12, 12, 12) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 354, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(jLabel3) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jPasswordField1, javax.swing.GroupLayout.DEFAULT_SIZE, 278, Short.MAX_VALUE))) + .addContainerGap())) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(jButton2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jButton1) + .addContainerGap()))) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel2, javax.swing.GroupLayout.DEFAULT_SIZE, 68, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel3) + .addComponent(jPasswordField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jButton1) + .addComponent(jButton2)) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed + entered = true; + password = new String(jPasswordField1.getPassword()); + setVisible(false); + }//GEN-LAST:event_jButton2ActionPerformed + + private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed + entered = false; + password = ""; + setVisible(false); + }//GEN-LAST:event_jButton1ActionPerformed + + /** + * @param args the command line arguments + */ + public static void main(String args[]) { + java.awt.EventQueue.invokeLater(new Runnable() { + public void run() { + AskForPasswordForm dialog = new AskForPasswordForm(new javax.swing.JFrame(), true); + dialog.addWindowListener(new java.awt.event.WindowAdapter() { + public void windowClosing(java.awt.event.WindowEvent e) { + System.exit(0); + } + }); + dialog.setVisible(true); + } + }); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton jButton1; + private javax.swing.JButton jButton2; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + private javax.swing.JPasswordField jPasswordField1; + // End of variables declaration//GEN-END:variables + private boolean entered; + private String password; + + public boolean getEntered() + { + return entered; + } + + public String getPassword() + { + return password; + } + +} diff --git a/client/trunk/src/com/fourisland/instadisc/Database/Subscription.java b/client/trunk/src/com/fourisland/instadisc/Database/Subscription.java index 529d60a..a3ab6ed 100644 --- a/client/trunk/src/com/fourisland/instadisc/Database/Subscription.java +++ b/client/trunk/src/com/fourisland/instadisc/Database/Subscription.java @@ -19,6 +19,7 @@ public class Subscription { private String url; private String category; private String title; + private String password; public String getURL() { @@ -35,6 +36,11 @@ public class Subscription { return title; } + public String getPassword() + { + return password; + } + public void setURL(String url) { this.url = url; @@ -49,4 +55,9 @@ public class Subscription { { this.title = title; } + + public void setPassword(String password) + { + this.password = password; + } } diff --git a/client/trunk/src/com/fourisland/instadisc/Functions.java b/client/trunk/src/com/fourisland/instadisc/Functions.java new file mode 100644 index 0000000..78c6eec --- /dev/null +++ b/client/trunk/src/com/fourisland/instadisc/Functions.java @@ -0,0 +1,81 @@ +package com.fourisland.instadisc; + +public class Functions { + + public static boolean xor(boolean x, boolean y) + { + return (x == y); + } + + public static int max(int x, int y) + { + return (x > y ? x : y); + } + + public static String padleft(String in, String pad, int len) + { + while (in.length() < len) + { + in = pad + in; + } + + if (in.length() > len) + { + in = in.substring(0,len); + } + + return in; + } + + public static String reverse(String in) + { + String out = ""; + int i=0; + + for (i=0;i len) + { + in = in.substring(0,len); + } + + return in; + } + +} + \ No newline at end of file diff --git a/client/trunk/src/com/fourisland/instadisc/Item/MD5.java b/client/trunk/src/com/fourisland/instadisc/Item/MD5.java index 7c2096c..31a7a5c 100644 --- a/client/trunk/src/com/fourisland/instadisc/Item/MD5.java +++ b/client/trunk/src/com/fourisland/instadisc/Item/MD5.java @@ -4,6 +4,7 @@ */ package com.fourisland.instadisc.Item; +import com.fourisland.instadisc.Functions; import java.security.MessageDigest; import java.util.logging.Level; import java.util.logging.Logger; @@ -43,7 +44,7 @@ public class MD5 { byte buffer[] = md5.digest(create); for (i = 0; i < buffer.length; i++) { String hex = Integer.toHexString(buffer[i]); - verify.append(pad(hex.substring(max(hex.length() - 2, 0)),"0",2)); + verify.append(Functions.padleft(hex.substring(Functions.max(hex.length() - 2, 0)),"0",2)); } } catch (Exception ex) { Logger.getLogger(WellFormedItem.class.getName()).log(Level.SEVERE, null, ex); @@ -51,18 +52,4 @@ public class MD5 { ver = ""; return verify.toString(); } - - private int max(int x, int y) - { - return (x > y ? x : y); - } - - private String pad(String in, String pad, int len) - { - while (in.length() < len) - { - in = pad + in; - } - return in; - } } diff --git a/client/trunk/src/com/fourisland/instadisc/Item/SubscriptionFile.java b/client/trunk/src/com/fourisland/instadisc/Item/SubscriptionFile.java index d9ed348..0236c11 100644 --- a/client/trunk/src/com/fourisland/instadisc/Item/SubscriptionFile.java +++ b/client/trunk/src/com/fourisland/instadisc/Item/SubscriptionFile.java @@ -4,9 +4,11 @@ */ package com.fourisland.instadisc.Item; +import com.fourisland.instadisc.AskForPasswordForm; import com.fourisland.instadisc.Database.Filter; import com.fourisland.instadisc.Database.Subscription; import com.fourisland.instadisc.Database.Wrapper; +import com.fourisland.instadisc.Functions; import com.fourisland.instadisc.XmlRpc; import java.io.FileNotFoundException; import java.io.IOException; @@ -16,6 +18,7 @@ import java.net.URL; import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.JFrame; import javax.swing.JLabel; /** @@ -135,14 +138,43 @@ class SubscriptionFileThread implements Runnable { s.setURL(headerMap.get("Subscription")); s.setTitle(headerMap.get("Title")); s.setCategory(headerMap.get("Category")); - Wrapper.addSubscription(s); - - XmlRpc xmlrpc = new XmlRpc("addSubscription"); - xmlrpc.addParam(headerMap.get("Subscription")); - xmlrpc.addParam(headerMap.get("Category")); - xmlrpc.execute(); - - status.setText("You've sucessfully subscribed to that website"); + + if (Functions.xor(headerMap.containsKey("Verification"),headerMap.containsKey("Verification-ID"))) + { + if (headerMap.containsKey("Verification")) + { + AskForPasswordForm afpf = new AskForPasswordForm(new JFrame(),true); + afpf.setVisible(true); + + if (afpf.getEntered() || afpf.getPassword().equals("")) + { + MD5 md5 = new MD5(afpf.getPassword()); + MD5 hash = new MD5(s.getTitle() + ":" + md5.hash() + ":" + headerMap.get("Verification-ID")); + + if (hash.hash().equals(headerMap.get("Verification"))) + { + s.setPassword(afpf.getPassword()); + } else { + status.setText("Error: Incorrect password entered"); + return; + } + } else { + status.setText("Error: No password entered"); + return; + } + } else { + s.setPassword(""); + } + + Wrapper.addSubscription(s); + + XmlRpc xmlrpc = new XmlRpc("addSubscription"); + xmlrpc.addParam(headerMap.get("Subscription")); + xmlrpc.addParam(headerMap.get("Category")); + xmlrpc.execute(); + + status.setText("You've sucessfully subscribed to that website"); + } } else { status.setText("Error: Subscription file is not well-formed"); } diff --git a/client/trunk/src/com/fourisland/instadisc/Item/WellFormedItem.java b/client/trunk/src/com/fourisland/instadisc/Item/WellFormedItem.java index f0f5838..f04e2ad 100644 --- a/client/trunk/src/com/fourisland/instadisc/Item/WellFormedItem.java +++ b/client/trunk/src/com/fourisland/instadisc/Item/WellFormedItem.java @@ -7,15 +7,28 @@ package com.fourisland.instadisc.Item; import com.fourisland.instadisc.Database.Filter; import com.fourisland.instadisc.Database.Subscription; import com.fourisland.instadisc.Database.Wrapper; +import com.fourisland.instadisc.Functions; import com.fourisland.instadisc.Item.Categories.Category; import com.fourisland.instadisc.XmlRpc; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; +import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; /** * @@ -33,6 +46,7 @@ public class WellFormedItem { boolean good = true; good = (good ? checkForRequiredHeaders() : false); good = (good ? checkForSubscription() : false); + good = (good ? checkForEncryption() : false); good = (good ? Category.checkForLegalCategory(aThis.headerMap) : false); good = (good ? Category.checkForRequiredSemantics(aThis.headerMap) : false); good = (good ? checkForProperVerification() : false); @@ -41,6 +55,74 @@ public class WellFormedItem { return good; } + private boolean checkForEncryption() { + if (!Wrapper.getSubscription(aThis.headerMap.get("Subscription")).getPassword().equals("")) + { + try + { + Subscription s = Wrapper.getSubscription(aThis.headerMap.get("Subscription")); + MD5 md5 = new MD5(Functions.padright(s.getPassword(), aThis.headerMap.get("Encryption-ID"), 16).substring(0, 16)); + String key = md5.hash().substring(0, 16); + String iv = Functions.reverse(key); + + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES"); + IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes()); + cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); + + aThis.headerMap.put("Title", new String(cipher.doFinal(Functions.hexToBytes(aThis.headerMap.get("Title")))).trim()); + aThis.headerMap.put("Author", new String(cipher.doFinal(Functions.hexToBytes(aThis.headerMap.get("Author")))).trim()); + aThis.headerMap.put("URL", new String(cipher.doFinal(Functions.hexToBytes(aThis.headerMap.get("URL")))).trim()); + + HashMap temp = new HashMap(aThis.headerMap); + temp.remove("ID"); + temp.remove("Verification"); + temp.remove("Verification-ID"); + temp.remove("Subscription"); + temp.remove("Title"); + temp.remove("Author"); + temp.remove("URL"); + temp.remove("Encryption-ID"); + + Collection> vals = temp.entrySet(); + Iterator> i = vals.iterator(); + while (i.hasNext()) + { + Entry e = (Entry) i.next(); + aThis.headerMap.put(e.getKey(), new String(cipher.doFinal(Functions.hexToBytes(e.getValue()))).trim()); + } + + return true; + } catch (IllegalBlockSizeException ex) + { + Logger.getLogger(WellFormedItem.class.getName()).log(Level.SEVERE, null, ex); + return false; + } catch (BadPaddingException ex) + { + Logger.getLogger(WellFormedItem.class.getName()).log(Level.SEVERE, null, ex); + return false; + } catch (InvalidKeyException ex) + { + Logger.getLogger(WellFormedItem.class.getName()).log(Level.SEVERE, null, ex); + return false; + } catch (InvalidAlgorithmParameterException ex) + { + Logger.getLogger(WellFormedItem.class.getName()).log(Level.SEVERE, null, ex); + return false; + } catch (NoSuchAlgorithmException ex) + { + Logger.getLogger(WellFormedItem.class.getName()).log(Level.SEVERE, null, ex); + return false; + } catch (NoSuchPaddingException ex) + { + Logger.getLogger(WellFormedItem.class.getName()).log(Level.SEVERE, null, ex); + return false; + } + } else { + return true; + } + } + private boolean checkForEqualFilters() { boolean good = true; @@ -117,6 +199,7 @@ public class WellFormedItem { private boolean checkForRequiredHeaders() { boolean good = true; + good = (good ? checkForRequiredHeader("ID") : false); good = (good ? checkForRequiredHeader("Verification") : false); good = (good ? checkForRequiredHeader("Verification-ID") : false); @@ -124,6 +207,12 @@ public class WellFormedItem { good = (good ? checkForRequiredHeader("Title") : false); good = (good ? checkForRequiredHeader("Author") : false); good = (good ? checkForRequiredHeader("URL") : false); + + if (!Wrapper.getSubscription(aThis.headerMap.get("Subscription")).getPassword().equals("")) + { + good = (good ? checkForRequiredHeader("Encryption-ID") : false); + } + return good; } diff --git a/client/trunk/src/com/fourisland/instadisc/resources/AskForPasswordForm.properties b/client/trunk/src/com/fourisland/instadisc/resources/AskForPasswordForm.properties new file mode 100644 index 0000000..a1d9d3e --- /dev/null +++ b/client/trunk/src/com/fourisland/instadisc/resources/AskForPasswordForm.properties @@ -0,0 +1,9 @@ + +jLabel1.text=Ask For Password +#NOI18N +jLabel1.font=DejaVu Sans-Plain-18 +jLabel2.text=If you can see this form, that must mean you are attempting to subscribe to a password-protected subscription. If you don't have the password to this subscription, well then, you cannot subscribe to it. +jLabel3.text=Password: +jPasswordField1.text= +jButton1.text=Cancel +jButton2.text=OK -- cgit 1.4.1