注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

widebright的个人空间

// 编程和生活

 
 
 

日志

 
 

Spark聊天信息加密插件第二部分Encryptor类,JAVA里RSA与AES加密的例子  

2010-07-01 19:57:28|  分类: 程序设计 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

package papaya.guava.mango;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;

import java.io.*;

public class Encryptor {
    private static Encryptor singleton;
    private static final Object LOCK = new Object();
       
private static final String KEY_FILENAME = "c:\\mykey.dat";
private static final String OTHERS_KEY_FILENAME = "c:\\Otherskey.dat";
// private static final int KEY_SIZE = 1024;
// private static final int BLOCK_SIZE = 117;
// private static final int OUTPUT_BLOCK_SIZE = 128;
private static final int KEY_SIZE = 2048; //RSA key 是多少位的
private static final int BLOCK_SIZE = 245;    //一次RSA加密操作所允许的最大长度
   //这个值与 KEY_SIZE 已经padding方法有关。因为 1024的key的输出是128,2048key输出是256字节
   //可能11个字节用于保存padding信息了,所以最多可用的就只有245字节了。
private static final int OUTPUT_BLOCK_SIZE = 256;

private Cipher aesCipher; //AES 加密
private SecureRandom secrand;
private Cipher rsaCipher; //RSA 加密
private KeyPair keys;

private Map<String, Key> allUserKeys;

public Encryptor() throws Exception {

   try {
    allUserKeys = new HashMap<String, Key>();
    secrand = new SecureRandom();
    //SunJCE Provider 中只支持ECB mode,试了一下只有PKCS1PADDING可以直接还原原始数据,
    //NOPadding导致解压出来的都是blocksize长度的数据,还要自己处理
    //参见 http://java.sun.com/javase/6/docs/technotes/guides/security/SunProviders.html#SunJCEProvider
    //
    //另外根据 Open-JDK-6.b17-src(http://www.docjar.com/html/api/com/sun/crypto/provider/RSACipher.java.html
    // 中代码的注释,使用RSA来加密大量数据不是一种标准的用法。所以现有实现一次doFinal调用之进行一个RSA操作,
    //如果用doFinal来加密超过的一个操作所允许的长度数据将抛出异常。
    //根据keysize的长度,典型的1024个长度的key和PKCS1PADDING一起使用时
    //一次doFinal调用只能加密117个byte的数据。(NOPadding 和1024 keysize时128个字节长度)
    //(2048长度的key和PKCS1PADDING 最多允许245字节一次)
    //想用来加密大量数据的只能自己用其他办法实现了。可能RSA加密速度比较慢吧,要用AES才行
  

    rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
    aesCipher = Cipher.getInstance("AES");
   } catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
   } catch (NoSuchPaddingException e) {
    e.printStackTrace();
    throw e;
   }

   ObjectInputStream in;

   try {
    in = new ObjectInputStream(new FileInputStream(KEY_FILENAME));
   } catch (FileNotFoundException e) {
    if (false == GenerateKeys())
    {
     throw e;
    }
    LoadKeys();
    return;
   }

   keys = (KeyPair) in.readObject();
   in.close();
   LoadKeys();

}


    /**
     * Returns the singleton instance of <CODE>Encryptor</CODE>,
     * creating it if necessary.
     * <p/>
     *
     * @return the singleton instance of <Code>Encryptor</CODE>
     */
    public static Encryptor getInstance() {
        // Synchronize on LOCK to ensure that we don't end up creating
        // two singletons.
        synchronized (LOCK) {
            if (null == singleton) {
            Encryptor enc;
           try {
            enc = new Encryptor();
           } catch (Exception ex) {
            System.out.println(ex.getMessage());
            return null;
           }
           singleton = enc;
                return enc;
            }
        }
        return singleton;
    }



/*
* 生成自己的公钥和私钥
*/
private Boolean GenerateKeys() {

   try {
    KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
    // secrand = new SecureRandom();
    // sedSeed之后会造成 生成的密钥都是一样的
    // secrand.setSeed("chatencrptor".getBytes()); // 初始化随机产生器
    //key长度至少512长度,不过好像说现在用2048才算比较安全的了
    keygen.initialize(KEY_SIZE, secrand); // 初始化密钥生成器

    keys = keygen.generateKeyPair(); // 生成密钥组

    AddKey("me", EncodeKey(keys.getPublic()));

   } catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
    return false;
   }

   ObjectOutputStream out;
   try {
    out = new ObjectOutputStream(new FileOutputStream(KEY_FILENAME));

   } catch (IOException e) {
    e.printStackTrace();
    return false;
   }

   try {
    out.writeObject(keys);

   } catch (IOException e) {
    e.printStackTrace();
    return false;
   } finally {
    try {
     out.close();
    } catch (IOException e) {
     e.printStackTrace();
     return false;
    }
   }

   return true;
}

public String EncryptMessageAES(String password, String Message)
throws IOException {
        byte[] raw = password.getBytes("ASCII");
        if (raw.length != 16)
        {
        //因为AES默认采用128bit的秘密,刚好16个字节,其他长度初始化
        //好像也有AES 256bit的,但还不知道怎么获取。
        throw new IOException("InvalidKey");
        }
  
        return EncryptMessageAES(raw, Message);
}

public String EncryptMessageAES(byte[] password, String Message)
    throws IOException {

   byte[] encryptedData;
   byte[] data = Message.getBytes("utf-8");
        if (password.length != 16)
        {
        //因为AES默认采用128bit的秘密,刚好16个字节,其他长度初始化
        //好像也有AES 256bit的,但还不知道怎么获取。
        throw new IOException("InvalidKey");
        }
        SecretKeySpec key = new SecretKeySpec(password, "AES");

        //如果想自己生成AES key,可以这样。  
        //KeyGenerator keygen = KeyGenerator.getInstance("AES");
        //SecretKey aesKey = keygen.generateKey();
   // aesCipher.init(Cipher.ENCRYPT_MODE, aesKey);

        try {
        synchronized (LOCK) {
            aesCipher.init(Cipher.ENCRYPT_MODE, key);
     encryptedData = aesCipher.doFinal(data );
    }

    return Base64.encodeBytes(encryptedData);

   } catch (InvalidKeyException e) {
    e.printStackTrace();
    throw new IOException("InvalidKey");

   } catch (IllegalBlockSizeException e) {
    e.printStackTrace();
    throw new IOException("IllegalBlockSize");
   } catch (BadPaddingException e) {
    e.printStackTrace();
    throw new IOException("BadPadding");
   } finally {
    // catch 中 return 或者throw之前都会先调用一下这里
   }

}

public String DecryptMessageAES(String password,String Message) throws IOException {
        byte[] raw = password.getBytes("ASCII");
        if (raw.length != 16)
        {
        //因为AES默认采用128bit的秘密,刚好16个字节,其他长度初始化
        //好像也有AES 256bit的,但还不知道怎么获取。
        throw new IOException("InvalidKey");
        }
   return DecryptMessageAES(raw, Message);
}

public String DecryptMessageAES(byte[] password,String Message) throws IOException {

   byte[] decoded = Base64.decode(Message);
        if (password.length != 16)
        {
        //因为AES默认采用128bit的秘密,刚好16个字节,其他长度初始化
        //好像也有AES 256bit的,但还不知道怎么获取。
        throw new IOException("InvalidKey");
        }
        SecretKeySpec key = new SecretKeySpec(password, "AES");
       
        try {
    byte[] decryptedData;
            synchronized (LOCK) {
            aesCipher.init(Cipher.DECRYPT_MODE, key);
            decryptedData = aesCipher.doFinal(decoded );
    }
            return new String(decryptedData, "UTF-8");

   } catch (InvalidKeyException e) {
    e.printStackTrace();
    throw new IOException("InvalidKey");
   } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
    throw new IOException("UnsupportedEncoding");
   } catch (IllegalBlockSizeException e) {
    e.printStackTrace();
    throw new IOException("IllegalBlockSize");
   } catch (BadPaddingException e) {
    e.printStackTrace();
    throw new IOException("BadPadding");
   } finally {
    // catch 中 return 或者throw之前都会先调用一下这里。
   }

}

/*
* RSA 加密Message 字符串,得到Base64
* 注意: 长度大于1000的字符串最好就不要用RSA加解密了,速度非常慢
*
* */
public String EncryptMessage(String toUser, String Message)
    throws IOException {

   Key pubkey = allUserKeys.get(toUser);
   if (pubkey == null) {
    throw new IOException("NoKeyForThisUser");
   }

   try {
    byte[] encryptedData;
    synchronized (LOCK) {
     // PublicKey pubkey = keys.getPublic();
     rsaCipher.init(Cipher.ENCRYPT_MODE, pubkey, secrand);
     // System.out.println(rsaCipher.getBlockSize()); 返回0,非block
     // 加密算法来的?
     // System.out.println(Message.getBytes("utf-8").length);
     // byte[] encryptedData =
     // rsaCipher.doFinal(Message.getBytes("utf-8"));
     byte[] data = Message.getBytes("utf-8");
     int blocks = data.length / BLOCK_SIZE;
     int lastBlockSize = data.length % BLOCK_SIZE;
     encryptedData = new byte[(lastBlockSize == 0 ? blocks
       : blocks + 1)
       * OUTPUT_BLOCK_SIZE];
     for (int i = 0; i < blocks; i++) {
      // int thisBlockSize = ( i + 1 ) * BLOCK_SIZE > data.length
      // ? data.length - i * BLOCK_SIZE : BLOCK_SIZE ;
      rsaCipher.doFinal(data, i * BLOCK_SIZE, BLOCK_SIZE,
        encryptedData, i * OUTPUT_BLOCK_SIZE);
     }
     if (lastBlockSize != 0) {
      rsaCipher.doFinal(data, blocks * BLOCK_SIZE, lastBlockSize,
        encryptedData, blocks * OUTPUT_BLOCK_SIZE);
     }
    }
    // System.out.println(encrypted.length);
    // 如果要机密的数据不足128/256字节,加密后补全成为变为256长度的。
    // 数量比较小时,Base64.GZIP产生的长度更长,没什么优势
    // System.out.println(Base64.encodeBytes(encrypted,Base64.GZIP).length());
    // System.out.println(Base64.encodeBytes(encrypted).length());
    // System.out.println (rsaCipher.getOutputSize(30));
    // 这个getOutputSize 只对 输入小于最大的block时才能得到正确的结果。其实就是补全 数据为128/256
    // 字节

    return Base64.encodeBytes(encryptedData);

   } catch (InvalidKeyException e) {
    e.printStackTrace();
    throw new IOException("InvalidKey");

   } catch (ShortBufferException e) {
    e.printStackTrace();
    throw new IOException("ShortBuffer");
   } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
    throw new IOException("UnsupportedEncoding");
   } catch (IllegalBlockSizeException e) {
    e.printStackTrace();
    throw new IOException("IllegalBlockSize");
   } catch (BadPaddingException e) {
    e.printStackTrace();
    throw new IOException("BadPadding");
   } finally {
    // catch 中 return 或者throw之前都会先调用一下这里
   }

}

/*
* RSA 解密Base64编码的Message 字符串,得到原始字符串
* 注意: 长度大于1000的字符串最好就不要用RSA加解密了,速度非常慢
*
* */
public String DecryptMessage(String Message) throws IOException {

   byte[] decoded = Base64.decode(Message);
   PrivateKey prikey = keys.getPrivate();

   try {
    ByteArrayOutputStream decodedStream;
    synchronized (LOCK) {
     rsaCipher.init(Cipher.DECRYPT_MODE, prikey, secrand);
     int blocks = decoded.length / OUTPUT_BLOCK_SIZE;
     decodedStream = new ByteArrayOutputStream(decoded.length);
     for (int i = 0; i < blocks; i++) {
      decodedStream.write(rsaCipher.doFinal(decoded, i
        * OUTPUT_BLOCK_SIZE, OUTPUT_BLOCK_SIZE));
     }
    }
    return new String(decodedStream.toByteArray(), "UTF-8");

   } catch (InvalidKeyException e) {
    e.printStackTrace();
    throw new IOException("InvalidKey");
   } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
    throw new IOException("UnsupportedEncoding");
   } catch (IllegalBlockSizeException e) {
    e.printStackTrace();
    throw new IOException("IllegalBlockSize");
   } catch (BadPaddingException e) {
    e.printStackTrace();
    throw new IOException("BadPadding");
   } finally {
    // catch 中 return 或者throw之前都会先调用一下这里。
   }

}

public boolean AddKey(String user, String key) {
   PublicKey publickey;
   try {
    publickey = DecodePublicKey(key);
   } catch (Exception e) {
    return false;
   }

   allUserKeys.put(user, publickey);

   SaveKeys();

   return true;
}

private boolean LoadKeys() {
   BufferedReader input;

   try {
    input = new BufferedReader(new InputStreamReader(
      new FileInputStream(OTHERS_KEY_FILENAME)));
   } catch (FileNotFoundException e1) {
    // e1.printStackTrace();
    return false;
   }

   try {
    allUserKeys.clear();

    String line;
    while ((line = input.readLine()) != null) {
     String[] temp = line.split("\\|");
     String user = temp[0];
     PublicKey key = DecodePublicKey(temp[1]);
     allUserKeys.put(user, key);
    }

   } catch (Exception e) {
    return false;
   } finally {
    try {
     input.close();
    } catch (Exception e) {
     return false;
    }
   }
   return true;

}

private boolean SaveKeys() {
   FileWriter output;

   try {
    output = new FileWriter(OTHERS_KEY_FILENAME);
   } catch (IOException e1) {
    // 1.printStackTrace();
    return false;
   }

   try {

    for (String user : allUserKeys.keySet()) {
     Key key = allUserKeys.get(user);
     output.write(user + "|" + EncodeKey(key) + "\n");
    }

   } catch (IOException e1) {
    // 1.printStackTrace();
    return false;
   } finally {
    try {
     output.close();
    } catch (Exception e) {
     return false;
    }

   }

   return true;
}

/**
* 解密base64编码得到公钥
*
* @param key
*            密钥字符串(经过base64编码)
* @throws Exception
*/
private static PublicKey DecodePublicKey(String key) throws Exception {
   byte[] keyBytes;
   keyBytes = Base64.decode(key);

   X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
   KeyFactory keyFactory = KeyFactory.getInstance("RSA");
   PublicKey publicKey = keyFactory.generatePublic(keySpec);
   return publicKey;
}

/**
* 解密base64编码得到私钥
*
* @param key
*            密钥字符串(经过base64编码)
* @throws Exception
*/
private static PrivateKey DecodePrivateKey(String key) throws Exception {
   byte[] keyBytes;
   keyBytes = Base64.decode(key);
   PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
   KeyFactory keyFactory = KeyFactory.getInstance("RSA");
   PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
   return privateKey;
}

/**
* 编码key为base64字符串
*
* @return
*/
private static String EncodeKey(Key key) {
   byte[] keyBytes = key.getEncoded();
   // System.out.print(key.getFormat()) ;
   String s = Base64.encodeBytes(keyBytes);
   return s;
}

/**
* 获取自己public key的base64字符串
*
* @return
*/
public String GetMyPublicKey() {
   return EncodeKey ( keys.getPublic());
}

/**
* 获取一个可用的AES key
*
* @return
*/
public byte[] GetAESKey() {
        KeyGenerator keygen;
   try {
    keygen = KeyGenerator.getInstance("AES");
    SecretKey aesKey = keygen.generateKey();
    byte[] keybytes = aesKey.getEncoded();
    return keybytes;
   } catch (NoSuchAlgorithmException e) {
    //e.printStackTrace();
    return null;
   }
}


}

  评论这张
 
阅读(851)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017