通过JCA实现DES/CBC文件加密解密
在sun的网站上查找Java语言security包:查找到得是J2SE6的JCA使用手册(http://java.sun.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html)。
阅读以上文档,获取DES/CBC加密模式的API,该文档中含有一个使用样例,通过阅读样例基本掌握利用JCA实现文件加密解密的方法。
通过编写代码实现文件加密解密并检查(DesEncrypter.java)。
用swing构建简单的主界面(MainGUI.java):
密钥框(KeyPanel.java)为JTextField,但是考虑到有些人只需要随机的16位的密钥,所以附加了一个密钥随机产生的内部类(class GenerateKey)。
为了方便用户,利用Java提供的JFileChoose类实现一个文件选择器(FilePanel.java):
为了保证用户界面友好,使用JOptionPane.showMessageDialog()来提醒用户。
另外,将文件加密解密调用分离出来(FileEncrypter.java),一是为了节省代码量,二是为了结构清晰——分离对文件的加密解密控制。
系统UML类图如下:
具体代码:
MainGUI.java
import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * @author littlekid * */ public class MainGUI extends JFrame { private static final long serialVersionUID = 1L; private static final int WIDTH = 520; private static final int HEIGHT = 222; private FilePanel inputPanel; private KeyPanel keyPanel; private JPanel buttonPanel; private JButton encryptButton; private JButton decryptButton; private JLabel myInfo; MainGUI(String title) { super(title); setSize(WIDTH, HEIGHT); Container mainPanel = getContentPane(); mainPanel.setLayout(new GridLayout(5, 1)); setLocation( (Toolkit.getDefaultToolkit().getScreenSize().width - WIDTH) / 2, (Toolkit.getDefaultToolkit().getScreenSize().height - HEIGHT) / 2); buttonPanel = new JPanel(); add(buttonPanel); inputPanel = new FilePanel(); mainPanel.add(inputPanel); keyPanel = new KeyPanel(); mainPanel.add(keyPanel); encryptButton = new FileEncrypter("加密", this); decryptButton = new FileEncrypter("解密", this); buttonPanel = new JPanel(); buttonPanel.add(encryptButton); buttonPanel.add(decryptButton); add(buttonPanel); myInfo = new JLabel("Author:littlekid E-mail:xxxxx"); add(myInfo); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public String getKey() { return ((JTextField) keyPanel.getComponent(1)).getText(); } public String getFileName() { return ((JTextField) inputPanel.getComponent(1)).getText(); } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub MainGUI gui = new MainGUI("DESCBC文件加密解密系统"); gui.setVisible(true); } }
FilePanel.java
import java.io.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * @author LiuLixiang * */ public class FilePanel extends JPanel { private JLabel label; private JTextField fileNameText; private JButton chooseButton; FilePanel() { label = new JLabel("选择文件:"); fileNameText = new JTextField(32); chooseButton = new JButton("浏览"); add(label); add(fileNameText); add(chooseButton); chooseButton.addActionListener(new ClickAction(this)); } public String getFileName() { JTextField jtf = (JTextField) this.getComponent(1); return jtf.getText(); } private class ClickAction implements ActionListener { private Component comp; ClickAction(Component c) { comp = c; } public void actionPerformed(ActionEvent event) { JFileChooser chooser = new JFileChooser(); chooser.setCurrentDirectory(new File(".")); int ret = chooser.showOpenDialog(comp); if (ret == JFileChooser.APPROVE_OPTION) { JPanel jp = (JPanel) comp; JTextField jtf = (JTextField) jp.getComponent(1); jtf.setText(chooser.getSelectedFile().getPath()); } } } }
import java.io.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; import java.security.*; import java.security.spec.*; import java.math.*; import javax.crypto.*; import javax.crypto.spec.*; /** * @author littlekid * */ public class FileEncrypter extends JButton implements ActionListener { private MainGUI mainFrame; private DesEncrypter encrypter; FileEncrypter(String name, MainGUI mainFrame) { super(name); this.mainFrame = mainFrame; this.addActionListener(this); } /* * (non-Javadoc) * * @see * java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed(ActionEvent e) { String inputFileName = mainFrame.getFileName(); String outputFileName; String source = ((JButton) (e.getSource())).getText(); String keyStr = mainFrame.getKey(); byte[] bytKey; try { bytKey = getKeyByStr(keyStr); } catch (Exception e2) { JOptionPane.showMessageDialog(mainFrame, "密码长度应该为16位!", "提示", JOptionPane.ERROR_MESSAGE); return; } // System.out.println("Test: source=" + source);//// if (source == "加密") { outputFileName = inputFileName + ".cipher"; try { getFileEncrypt(inputFileName, outputFileName, bytKey); JOptionPane.showMessageDialog(mainFrame, "加密成功!加密文件为" + outputFileName, "信息", JOptionPane.CLOSED_OPTION); } catch (FileNotFoundException e1) { // TODO Auto-generated catch block JOptionPane.showMessageDialog(mainFrame, "未找到相应文件!", "提示", JOptionPane.ERROR_MESSAGE); return; } } else { // System.out.println("Test: name=*" + // inputFileName.substring(inputFileName.length()-7, // inputFileName.length()) +"*");//// if (!inputFileName.substring(inputFileName.length() - 7, inputFileName.length()).equals(".cipher")) { JOptionPane.showMessageDialog(mainFrame, "错误的文件名,被解密文件应该是带有.cipher后缀的文件", "提示", JOptionPane.ERROR_MESSAGE); return; } outputFileName = inputFileName + ".decrypt"; // System.out.println("Test: outputFileName=*" + outputFileName); try { getFileDecrypt(inputFileName, outputFileName, bytKey); JOptionPane.showMessageDialog(mainFrame, "解密成功!解密文件为" + outputFileName, "信息", JOptionPane.CLOSED_OPTION); } catch (FileNotFoundException e1) { // TODO Auto-generated catch block JOptionPane.showMessageDialog(mainFrame, "未找到相应文件!", "提示", JOptionPane.ERROR_MESSAGE); return; } } } private void getFileEncrypt(String sourceFileName, String targetFileName, byte[] bytKey) throws FileNotFoundException { // System.out.println("Test: encrypt");//// SecretKeySpec desKey = new SecretKeySpec(bytKey, "DES"); encrypter = new DesEncrypter(desKey); encrypter.encrypt(new FileInputStream(sourceFileName), new FileOutputStream(targetFileName)); } private void getFileDecrypt(String sourceFileName, String targetFileName, byte[] bytKey) throws FileNotFoundException { // System.out.println("Test: decrypt");//// SecretKeySpec desKey = new SecretKeySpec(bytKey, "DES"); encrypter = new DesEncrypter(desKey); encrypter.decrypt(new FileInputStream(sourceFileName), new FileOutputStream(targetFileName)); } private byte[] getKeyByStr(String str) throws Exception { if (str.length() != 16) { throw new Exception("The length of this string is not 16!"); } byte[] resByte = new byte[str.length() / 2]; for (int i = 0; i < str.length() / 2; i++) { Integer integer = new Integer(16 * charToInt(str.charAt(2 * i)) + charToInt(str.charAt(2 * i + 1))); resByte[i] = integer.byteValue(); } return resByte; } private int charToInt(char chr) { if (chr >= 'A') { return (10 + chr - 'A'); } else { return (chr - '0'); } } // /** // * @param args // * @throws IOException // * @throws NoSuchPaddingException // * @throws NoSuchAlgorithmException // */ // public static void main(String[] args) throws IOException, // NoSuchAlgorithmException, NoSuchPaddingException { // FileEncrypter driver = new FileEncrypter(); // driver.getFileEncrypt("D://input.txt", "output.txt"); // System.out.println("OK!"); // } }
import java.io.*; import java.security.*; import java.security.*; import java.security.spec.*; import javax.crypto.*; import javax.crypto.spec.*; /** * @author littlekid * */ public class DesEncrypter { private static final String IV = "1234567-"; private Cipher ecipher; private Cipher dcipher; private byte[] buf = new byte[1024];// / DesEncrypter(SecretKey key) { IvParameterSpec iv; AlgorithmParameterSpec paramSpec; try { iv = new IvParameterSpec(IV.getBytes("UTF-8")); paramSpec = iv; } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); return; } try { ecipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); dcipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec); dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec); } catch (InvalidAlgorithmParameterException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (NoSuchPaddingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (NoSuchAlgorithmException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (InvalidKeyException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } /** * @param in * @param out */ public void encrypt(InputStream in, OutputStream out) { out = new CipherOutputStream(out, ecipher); int numRead = 0; try { while ((numRead = in.read(buf)) >= 0) { out.write(buf, 0, numRead); } out.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * @param in * @param out */ public void decrypt(InputStream in, OutputStream out) { in = new CipherInputStream(in, dcipher); int numRead = 0; try { while ((numRead = in.read(buf)) >= 0) { out.write(buf, 0, numRead); } out.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
大缺陷:
没有认证机制
类的设计组合上有问题(看起来不是很好),需要重构~