java解密_JAVA加解密
【前言】
本文简单的介绍了加密技术相关概念,最后总结了java中现有的加密技术以及使用方法和例子
【最简单的加密】
1.简单的概念
明文:加密前的信息
密文:机密后的信息
算法:加密或解密的算法
密钥:算法使用的钥匙(读作miyao,正确应该是miyue,但是大家都读miyao)
2.简单的例子
将123456每位数字都加1后得到234567,
其中123456就是明文,234567就是密文,加密密钥就是1,加密算法是每位加
3.对称加密和非对称加密
以上为例,
123456-->234567的加密密钥就是1,加密算法是每位+
234567-->123456的解密密钥也是1,解密算法是每位-
其中加密算法(+)和解密算法(-)相对称,这种加密算法就称作对称加密,
同样,如果加密算法和解密算法不对称就称之为非对称加密。
4.算法举例
众多的加密手段大致可以分为单项加密和双向加密。单项加密指通过对数据进行摘要计算生成密文,密文不可逆推还原,比如有Base64、MD5、SHA等;双向加密则相反,指可以把密文逆推还原成明文,其中双向加密又分为对称加密和非对称加密。对称加密是指数据使用者必须拥有同样的密钥才可以进行加密解密,就像大家共同约定了一组暗号一样,对称加密的手段有DES、3DES、AES、IDEA、RC4、RC5等;而非对称加密相对于对称加密而言,无需拥有同一组密钥,它是一种“信息公开的密钥交换协议”。非对称加密需要公开密钥和私有密钥两组密钥,公开密钥和私有密钥是配对起来的,也就是说使用公开密钥进行数据加密,只有对应的私有密钥才能进行解密。此类的加密手段有RSA、DSA等
对称加密算法:DES算法,3DES算法,TDEA算法,Blowfish算法,RC5算法,IDEA算法,AES算法。
非对称加密算法:RSA、Elgamal、背包算法、Rabin、D-H、ECC。
经典的哈希算法:MD2、MD4、MD5 和 SHA-1(目的是将任意长输入通过算法变为固定长输出,且保证输入变化一点输出都不同,且不能反向解密)
5.经典算法实现
5.1:AES算法
packagecom.meng.study.security;importjava.security.SecureRandom;importjavax.crypto.Cipher;importjavax.crypto.KeyGenerator;importjavax.crypto.SecretKey;importjavax.crypto.spec.SecretKeySpec;public classAESUtil {/*** AES加密
*
*@paramcontent
* 待加密的内容
*@paramencryptKey
* 加密密钥
*@return加密后的byte[]
*@throwsException*/
public static byte[] aesEncryptToBytes(String content, String encryptKey) throwsException {
KeyGenerator kgen= KeyGenerator.getInstance("AES");
SecureRandom random= SecureRandom.getInstance("SHA1PRNG");
random.setSeed(encryptKey.getBytes());
kgen.init(128, random);
SecretKey secretKey= new SecretKeySpec(kgen.generateKey().getEncoded(), "AES");
Cipher cipher= Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);return cipher.doFinal(content.getBytes("UTF-8"));
}/*** AES加密为base 64 code
*
*@paramcontent
* 待加密的内容
*@paramencryptKey
* 加密密钥
*@return加密后的base 64 code
*@throwsException*/
public static String aesEncrypt(String content, String encryptKey) throwsException {returnBASE64Util.base64Encode(aesEncryptToBytes(content, encryptKey));
}/*** AES解密
*
*@paramencryptBytes
* 待解密的byte[]
*@paramdecryptKey
* 解密密钥
*@return解密后的String
*@throwsException*/
public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throwsException {
KeyGenerator kgen= KeyGenerator.getInstance("AES");
SecureRandom random= SecureRandom.getInstance("SHA1PRNG");
random.setSeed(decryptKey.getBytes());
kgen.init(128, random);
SecretKey secretKey= new SecretKeySpec(kgen.generateKey().getEncoded(), "AES");
Cipher cipher= Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);byte[] decryptBytes =cipher.doFinal(encryptBytes);return newString(decryptBytes);
}/*** 将base 64 code AES解密
*
*@paramencryptStr
* 待解密的base 64 code
*@paramdecryptKey
* 解密密钥
*@return解密后的string
*@throwsException*/
public static String aesDecrypt(String encryptStr, String decryptKey) throwsException {returnaesDecryptByBytes(BASE64Util.base64Decode(encryptStr), decryptKey);
}
}
显示代码
5.2:3DES算法
packagecom.meng.study.security;importjava.io.UnsupportedEncodingException;importjavax.crypto.Cipher;importjavax.crypto.SecretKey;importjavax.crypto.spec.SecretKeySpec;importorg.apache.log4j.Logger;importcom.meng.study.cache.GuavaCacheUtil;/***
*@authorzhenbinmeng
**/
public classDESUtil {private static Logger logger = Logger.getLogger(DESUtil.class);//定义加密算法,DESede(即3DES)
private static final String DESede = "DESede";private static final String PASSWORD_CRYPT_KEY = "2015mengzhenbinStudyForSecurity@DESede";/*** 加密方法
*
*@paramsrc
* 源数据的字节数组
*@return
*/
public static byte[] encryptMode(byte[] src) {try{
SecretKey deskey= new SecretKeySpec(build3DesKey(PASSWORD_CRYPT_KEY), DESede); //生成密钥
Cipher c1 = Cipher.getInstance(DESede); //实例化负责加密/解密的Cipher工具类
c1.init(Cipher.ENCRYPT_MODE, deskey); //初始化为加密模式
returnc1.doFinal(src);
}catch(Exception e) {
logger.error("DesUtil.encryptMode error", e);
e.printStackTrace();
}return null;
}/*** 加密方法(BASE64编码返回)
*
*@paramsrc
* 源数据的字节数组
*@return
*/
public static String encryptModeBase64(byte[] src) {try{
SecretKey deskey= new SecretKeySpec(build3DesKey(PASSWORD_CRYPT_KEY), DESede); //生成密钥
Cipher c1 = Cipher.getInstance(DESede); //实例化负责加密/解密的Cipher工具类
c1.init(Cipher.ENCRYPT_MODE, deskey); //初始化为加密模式
returnBASE64Util.base64Encode(c1.doFinal(src));
}catch(Exception e) {
logger.error("DesUtil.encryptMode error", e);
e.printStackTrace();
}return null;
}/*** 解密函数
*
*@paramsrc
* 密文的字节数组
*@return
*/
public static byte[] decryptMode(byte[] src) {try{
SecretKey deskey= newSecretKeySpec(build3DesKey(PASSWORD_CRYPT_KEY), DESede);
Cipher c1=Cipher.getInstance(DESede);
c1.init(Cipher.DECRYPT_MODE, deskey);//初始化为解密模式
returnc1.doFinal(src);
}catch(Exception e) {
logger.error("DesUtil.decryptMode error", e);
e.printStackTrace();
}return null;
}/*** 解密函数(BASE64解码后解密)
*
*@paramsrc
* 密文的字节数组
*@return
*/
public staticString decryptModeBase64(String src) {try{
SecretKey deskey= newSecretKeySpec(build3DesKey(PASSWORD_CRYPT_KEY), DESede);
Cipher c1=Cipher.getInstance(DESede);
c1.init(Cipher.DECRYPT_MODE, deskey);//初始化为解密模式
return newString(c1.doFinal(BASE64Util.base64Decode(src)));
}catch(Exception e) {
logger.error("DesUtil.decryptMode error", e);
e.printStackTrace();
}return null;
}/** 根据字符串生成密钥字节数组
*
* @param keyStr 密钥字符串
*
* @return
*
* @throws UnsupportedEncodingException*/
public static byte[] build3DesKey(String keyStr) throwsUnsupportedEncodingException {byte[] key = new byte[24]; //声明一个24位的字节数组,默认里面都是0
byte[] temp = keyStr.getBytes("UTF-8"); //将字符串转成字节数组
/** 执行数组拷贝 System.arraycopy(源数组,从源数组哪里开始拷贝,目标数组,拷贝多少位)*/
if (key.length >temp.length) {//如果temp不够24位,则拷贝temp数组整个长度的内容到key数组中
System.arraycopy(temp, 0, key, 0, temp.length);
}else{//如果temp大于24位,则拷贝temp数组24个长度的内容到key数组中
System.arraycopy(temp, 0, key, 0, key.length);
}returnkey;
}
}
显示代码
5.3:MD5算法
packagecom.meng.study.security;importjava.security.MessageDigest;public classMD5Util {/*** 获取byte[]的md5值
*
*@parambytes
* byte[]
*@returnmd5
*@throwsException*/
public static byte[] md5(byte[] bytes) throwsException {
MessageDigest md= MessageDigest.getInstance("MD5");
md.update(bytes);returnmd.digest();
}/*** 获取字符串md5值
*
*@parammsg
*@returnmd5
*@throwsException*/
public static byte[] md5(String msg) throwsException {returnmd5(msg.getBytes());
}/*** 获取字符串md5值
*
*@parammsg
*@returnmd5
*@throwsException*/
public static String md5Hex(String msg) throwsException {byte[] messageDigest =md5(msg.getBytes());//Create Hex String
StringBuffer hexString = newStringBuffer();//字节数组转换为 十六进制 数
for (int i = 0; i < messageDigest.length; i++) {
String shaHex= Integer.toHexString(messageDigest[i] & 0xFF);if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}returnhexString.toString();
}/*** 结合base64实现md5加密
*
*@parammsg
* 待加密字符串
*@return获取md5后转为base64
*@throwsException*/
public static String md5Base64(String msg) throwsException {returnBASE64Util.base64Encode(md5(msg));
}
}
显示代码
5.4:SHA算法
packagecom.meng.study.security;importjava.security.MessageDigest;public classSHAUtil {public staticString SHA1(String decript) {try{
MessageDigest digest= java.security.MessageDigest.getInstance("SHA-1");
digest.update(decript.getBytes());byte messageDigest[] =digest.digest();//Create Hex String
StringBuffer hexString = newStringBuffer();//字节数组转换为 十六进制 数
for (int i = 0; i < messageDigest.length; i++) {
String shaHex= Integer.toHexString(messageDigest[i] & 0xFF);if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}returnhexString.toString();
}catch(Exception e) {
e.printStackTrace();
}return "";
}public staticString SHA(String decript) {try{
MessageDigest digest= java.security.MessageDigest.getInstance("SHA");
digest.update(decript.getBytes());byte messageDigest[] =digest.digest();//Create Hex String
StringBuffer hexString = newStringBuffer();//字节数组转换为 十六进制 数
for (int i = 0; i < messageDigest.length; i++) {
String shaHex= Integer.toHexString(messageDigest[i] & 0xFF);if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}returnhexString.toString();
}catch(Exception e) {
e.printStackTrace();
}return "";
}
}
显示代码
5.5:RSA算法
packagecom.meng.study.security;importjava.math.BigInteger;importjava.security.KeyFactory;importjava.security.KeyPair;importjava.security.KeyPairGenerator;importjava.security.NoSuchAlgorithmException;importjava.security.interfaces.RSAPrivateKey;importjava.security.interfaces.RSAPublicKey;importjava.security.spec.RSAPrivateKeySpec;importjava.security.spec.RSAPublicKeySpec;importjava.util.HashMap;importjavax.crypto.Cipher;public classRSAUtil {/*** 生成公钥和私钥
*
*@throwsNoSuchAlgorithmException
**/
public static HashMap getKeys() throwsNoSuchAlgorithmException {
HashMap map = new HashMap();
KeyPairGenerator keyPairGen= KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(1024);
KeyPair keyPair=keyPairGen.generateKeyPair();
RSAPublicKey publicKey=(RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey=(RSAPrivateKey) keyPair.getPrivate();
map.put("public", publicKey);
map.put("private", privateKey);returnmap;
}/*** 使用模和指数生成RSA公钥
* 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA
* /None/NoPadding】
*
*@parammodulus
* 模
*@paramexponent
* 指数
*@return
*/
public staticRSAPublicKey getPublicKey(String modulus, String exponent) {try{
BigInteger b1= newBigInteger(modulus);
BigInteger b2= newBigInteger(exponent);
KeyFactory keyFactory= KeyFactory.getInstance("RSA");
RSAPublicKeySpec keySpec= newRSAPublicKeySpec(b1, b2);return(RSAPublicKey) keyFactory.generatePublic(keySpec);
}catch(Exception e) {
e.printStackTrace();return null;
}
}/*** 使用模和指数生成RSA私钥
* 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA
* /None/NoPadding】
*
*@parammodulus
* 模
*@paramexponent
* 指数
*@return
*/
public staticRSAPrivateKey getPrivateKey(String modulus, String exponent) {try{
BigInteger b1= newBigInteger(modulus);
BigInteger b2= newBigInteger(exponent);
KeyFactory keyFactory= KeyFactory.getInstance("RSA");
RSAPrivateKeySpec keySpec= newRSAPrivateKeySpec(b1, b2);return(RSAPrivateKey) keyFactory.generatePrivate(keySpec);
}catch(Exception e) {
e.printStackTrace();return null;
}
}/*** 公钥加密
*
*@paramdata
*@parampublicKey
*@return*@throwsException*/
public static String encryptByPublicKey(String data, RSAPublicKey publicKey) throwsException {
Cipher cipher= Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);//模长
int key_len = publicKey.getModulus().bitLength() / 8;//加密数据长度 <= 模长-11
String[] datas = splitString(data, key_len - 11);
String mi= "";//如果明文长度大于模长-11则要分组加密
for(String s : datas) {
mi+=bcd2Str(cipher.doFinal(s.getBytes()));
}returnmi;
}/*** 私钥解密
*
*@paramdata
*@paramprivateKey
*@return*@throwsException*/
public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throwsException {
Cipher cipher= Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);//模长
int key_len = privateKey.getModulus().bitLength() / 8;byte[] bytes =data.getBytes();byte[] bcd =ASCII_To_BCD(bytes, bytes.length);//如果密文长度大于模长则要分组解密
String ming = "";byte[][] arrays =splitArray(bcd, key_len);for (byte[] arr : arrays) {
ming+= newString(cipher.doFinal(arr));
}returnming;
}/*** ASCII码转BCD码
**/
public static byte[] ASCII_To_BCD(byte[] ascii, intasc_len) {byte[] bcd = new byte[asc_len / 2];int j = 0;for (int i = 0; i < (asc_len + 1) / 2; i++) {
bcd[i]= asc_to_bcd(ascii[j++]);
bcd[i]= (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++])) + (bcd[i] << 4));
}returnbcd;
}public static byte asc_to_bcd(byteasc) {bytebcd;if ((asc >= '0') && (asc <= '9'))
bcd= (byte) (asc - '0');else if ((asc >= 'A') && (asc <= 'F'))
bcd= (byte) (asc - 'A' + 10);else if ((asc >= 'a') && (asc <= 'f'))
bcd= (byte) (asc - 'a' + 10);elsebcd= (byte) (asc - 48);returnbcd;
}/*** BCD转字符串*/
public static String bcd2Str(byte[] bytes) {char temp[] = new char[bytes.length * 2], val;for (int i = 0; i < bytes.length; i++) {
val= (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
temp[i* 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
val= (char) (bytes[i] & 0x0f);
temp[i* 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
}return newString(temp);
}/*** 拆分字符串*/
public static String[] splitString(String string, intlen) {int x = string.length() /len;int y = string.length() %len;int z = 0;if (y != 0) {
z= 1;
}
String[] strings= new String[x +z];
String str= "";for (int i = 0; i < x + z; i++) {if (i == x + z - 1 && y != 0) {
str= string.substring(i * len, i * len +y);
}else{
str= string.substring(i * len, i * len +len);
}
strings[i]=str;
}returnstrings;
}/*** 拆分数组*/
public static byte[][] splitArray(byte[] data, intlen) {int x = data.length /len;int y = data.length %len;int z = 0;if (y != 0) {
z= 1;
}byte[][] arrays = new byte[x +z][];byte[] arr;for (int i = 0; i < x + z; i++) {
arr= new byte[len];if (i == x + z - 1 && y != 0) {
System.arraycopy(data, i* len, arr, 0, y);
}else{
System.arraycopy(data, i* len, arr, 0, len);
}
arrays[i]=arr;
}returnarrays;
}
}
显示代码
5.6:Base64算法
packagecom.meng.study.security;importsun.misc.BASE64Decoder;importsun.misc.BASE64Encoder;
@SuppressWarnings("restriction")public classBASE64Util {/*** base 64 encode
*
*@parambytes
* 待编码的byte[]
*@return编码后的base 64 code*/
public static String base64Encode(byte[] bytes) {return newBASE64Encoder().encode(bytes);
}/*** base 64 decode
*
*@parambase64Code
* 待解码的base 64 code
*@return解码后的byte[]
*@throwsException*/
public static byte[] base64Decode(String base64Code) throwsException {return newBASE64Decoder().decodeBuffer(base64Code);
}
}
显示代码
5.7:测试代码
packagecom.meng.study.security;importjava.security.interfaces.RSAPrivateKey;importjava.security.interfaces.RSAPublicKey;importjava.util.HashMap;/*** 编码工具类 1.将byte[]转为各种进制的字符串 2.base 64 encode 3.base 64 decode
*
*@authoruikoo9
*@version0.0.5.20140601*/
public classEncodeUtilTest {public static void main(String[] args) throwsException {
String msg= "我爱你";
System.out.println("转换前:" +msg);//==Base64==
String base64Str =BASE64Util.base64Encode(msg.getBytes());
System.out.println("Base64转换后:" +base64Str);
System.out.println("Base64解码后:" + newString(BASE64Util.base64Decode(base64Str)));//==Md5==
String md5Base64Str =MD5Util.md5Base64(msg);
System.out.println("Md5后Base编码转换后:" +md5Base64Str);//==Des==
byte[] des =DESUtil.encryptMode(msg.getBytes());
System.out.println("【DES加密后】:" + newString(des));byte[] myMsgArr =DESUtil.decryptMode(des);
System.out.println("【DES解密后】:" + newString(myMsgArr));
String desBase64Str=DESUtil.encryptModeBase64(msg.getBytes());
System.out.println("Des后Base64编码转换后:" +desBase64Str);
System.out.println("Base64解码后Des转换后:" + newString(DESUtil.decryptModeBase64(desBase64Str)));//==Aes==
String aesKey = "123456";
String aesEnStr=AESUtil.aesEncrypt(msg,aesKey);
System.out.println("【AES加密Base64编码后】:" +aesEnStr);
String aesDeStr=AESUtil.aesDecrypt(aesEnStr,aesKey);
System.out.println("【Base64解码AES解密后】:" +aesDeStr);//==Rsa==
HashMap keys =RSAUtil.getKeys();
RSAPublicKey publicKey= (RSAPublicKey) keys.get("public");
RSAPrivateKey privateKey= (RSAPrivateKey) keys.get("private");
RSAPublicKey pubKey=RSAUtil.getPublicKey(publicKey.getModulus().toString(), publicKey.getPublicExponent().toString());
RSAPrivateKey priKey=RSAUtil.getPrivateKey(privateKey.getModulus().toString(), privateKey.getPrivateExponent().toString());
String rsaEnStr=RSAUtil.encryptByPublicKey(msg, pubKey);
System.out.println("【RSA公钥加密后】:" +rsaEnStr);
String rsaDeStr=RSAUtil.decryptByPrivateKey(rsaEnStr, priKey);
System.out.println("【RSA私钥解密后】:" +rsaDeStr);
}
}
显示代码
java解密_JAVA加解密相关推荐
- Java 3desede加解密_JAVA加解密11-对称加密算法-DES以及DESede算法
一.简述 对称加密算法就是能将数据加解密.加密的时候用密钥对数据进行加密,解密的时候使用同样的密钥对数据进行解密. DES是美国国家标准研究所提出的算法.因为加解密的数据安全性和密钥长度成正比.des ...
- rsa java代码_java加解密RSA使用方法代码示例
最近为了分析一段请求流,不得不去研究一下RSA加密. 首先,强调一点:密钥的"钥"读"yue",不是"yao",额... 网上关于RSA的原 ...
- 一个java的DES加解密类转换成C#
原文:一个java的DES加解密类转换成C# 一个java的des加密解密代码如下: //package com.visionsky.util;import java.security.*; //im ...
- Java使用AES加解密
Java使用AES加解密 目录 1.1生成密钥 1.2密钥的存储 1.3获取存储的密钥 1.4加解密 1.5使用存储的密钥进行加解密示例 AES是一种对称的加密算法,可基于相同的密钥进行加密和解密.J ...
- JAVA:实现XXTea加解密算法(附完整源码)
JAVA:实现XXTea加解密算法 public class XXTEAprivate XXTEA() {}public static byte[] encrypt(byte[] data, byte
- java实现DES加解密算法
以下是我用java实现的DES算法,实现中可能存在一点问题自己没空去找,但我觉得DES的算法过程肯定没错!现在暂时没时间去找到底是哪里的问题,有空再瞧瞧自己的代码喽! makekey.java是生成密 ...
- java加密解密代码_java加解密文件公用方法整合(多看一本书,少写三行代码)
最近接到任务(文件的安全性)需要在文件上传到服务器上时将文件加密保存, 用户下载时将文件解密后返回给用户.翻了下方法最后决定用java中的Cipher类来完成(里面的实现方式挺全的). 上手实现.po ...
- java sha加解密算法_java加解密
SHA256 ------------------java自带实现方式--------------- package com.xiayu.demo; import java.io.Unsupporte ...
- Java 进行 RSA 加解密时不得不考虑到的那些事儿
1. 加密的系统不要具备解密的功能,否则 RSA 可能不太合适 公钥加密,私钥解密.加密的系统和解密的系统分开部署,加密的系统不应该同时具备解密的功能,这样即使黑客攻破了加密系统,他拿到的也只是一堆无 ...
最新文章
- oracle缺少key xe.reg,【Oracle XE系列之一】Windows 7 64位安裝Oracle XE(32位)數據庫(REG_XE報錯、字符集、修改8080端口等)...
- php mb strimwidth,wordpress截断函数mb_strimwidth()失效的解决方法
- php 定时缓存,php如何定时删除缓存??
- boost::mp11::mp_compose相关用法的测试程序
- 关于Vue中计算属性computed和methods属性的区别,你了解多少呢
- 了解ADF Faces clientComponent属性
- 《敏捷可执行需求说明 Scrum提炼及实现技术》—— 3.4 关注干系人的“愿求”...
- 聚类的基本概念-聚类与分类的区别
- 图解TCPIP---第六章---传输层TCPUDP
- 操作系统--windows系列之windows8
- 2022年上半年网络工程师上午真题及答案
- 在死亡边缘疯狂试探:“黑暗旅游”,你敢尝试吗?
- IT行业为何如此吃香?2019学习IT就业前景分析
- OpenGL 实验一 绘制简单图形
- 爱心代码表白(可直接复制运行)
- solr dih有子查询时速度慢
- SCADA/EMS系统的子系统的划分
- Android Crash signal 4 (SIGILL), code 1 (ILL_ILLOPC), fault addr b56cb106
- 盈帆报表软件制作编号报表(报表工具)
- 链路聚合(端口聚合)
热门文章
- 游戏公司如何应对游戏黑产 ?
- 基于MDK-KILE5.23版本的STM32创建工程
- 手机通讯录系统(三层架构+JDBC+MySQL)
- python爬虫批量下载图片
- C++刷题知识点总结2
- 20层的试炼html5,Vue.js-02:新手村的试炼 - 新世界的武器(指令)
- python+matplotlib对柿子图的彩色和灰色直方图统计
- iOS Instrument使用之Core Animation(图形性能)
- Celery入门--定时任务的开发及运行
- Win 10 忘记密码不用U盘就可解决