对称加密(也叫私钥加密)指加密和解密使用相同密钥的加密算法。有时又叫传统密码算法,就是加密密钥能够从解密密钥中推算出来,同时解密密钥也可以从加密密钥中推算出来。而在大多数的对称算法中,加密密钥和解密密钥是相同的,所以也称这种加密算法为秘密密钥算法或单密钥算法。它要求发送方和接收方在安全通信之前,商定一个密钥。对称算法的安全性依赖于密钥,泄漏密钥就意味着任何人都可以对他们发送或接收的消息解密,所以密钥的保密性对通信性至关重要。
特点
对称加密算法的特点是算法公开、计算量小、加密速度快、加密效率高。
不足之处是,交易双方都使用同样钥匙,安全性得不到保证。此外,每对用户每次使用对称加密算法时,都需要使用其他人不知道的惟一钥匙,这会使得发收信双方所拥有的钥匙数量呈几何级数增长,密钥管理成为用户的负担。对称加密算法在分布式网络系统上使用较为困难,主要是因为密钥管理困难,使用成本较高。而与公开密钥加密算法比起来,对称加密算法能够提供加密和认证却缺乏了签名功能,使得使用范围有所缩小。在计算机专网系统中广泛使用的对称加密算法有DES和IDEA等
算法
附上代码(生成密钥及加密、解密)
public static void desSecret() throws NoSuchAlgorithmException { // 密钥生成器 KeyGenerator key = KeyGenerator.getInstance("DESede"); /** * 初始化密钥生成器 该步骤一般指定密钥的长度。如果该步骤省略的话, 会根据算法自动使用默认的密钥长度。指定长度时, * 若第一步密钥生成器使用的是“DES”算法,则密钥长度必须是56位; * 若是“DESede”,则可以是112或168位,其中112位有效;若是“AES”, 可以是128, * 192或256位;若是“Blowfish”,则可以是32至448之间可以被8整除的数; * “HmacMD5”和“HmacSHA1”默认的密钥长度都是64个字节 */ key.init(168); // 生成密钥 SecretKey secret = key.generateKey(); FileOutputStream s; try { s = new FileOutputStream("D://des-pri.key"); ObjectOutputStream st = new ObjectOutputStream(s); st.writeObject(secret); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static byte[] desEncrypt(String str) throws Exception { byte[] miby = null; FileInputStream s = new FileInputStream("D://des-pri.key"); ObjectInputStream st = new ObjectInputStream(s); SecretKey secret = (SecretKey) st.readObject(); // 工厂模式获取Cipher实例 密码器 Cipher cp = Cipher.getInstance("DESede"); // 初始化cipher ENCRYPT_MODE表示加密DECRYPT_MODE解密 后面一个参数是密钥 cp.init(Cipher.ENCRYPT_MODE, secret); byte[] by = str.getBytes("utf-8"); // 对字节码机密 miby就是加密后的字节码 miby = cp.doFinal(by); return miby; } public static String desUnEncrypt(byte[] sct) throws Exception { // 通过提供的密钥文件 获取密钥对象 进行解密 FileInputStream s = new FileInputStream("D://des-pri.key"); ObjectInputStream st = new ObjectInputStream(s); SecretKey secret = (SecretKey) st.readObject(); // 工厂模式获取Cipher实例 密码器 Cipher cp = Cipher.getInstance("DESede"); // 初始化cipher ENCRYPT_MODE表示加密DECRYPT_MODE解密 后面一个参数是密钥 cp.init(Cipher.DECRYPT_MODE, secret); // 对密文字节码进行解密 如果密码写入文件可以使用文件流读写成一个数组 byte[] miby = cp.doFinal(sct); // 获取密文字符串 String minwen = new String(miby, "utf-8"); return minwen; }
非对称加密:非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥
特点
非对称加密与对称加密相比,其安全性更好:对称加密的通信双方使用相同的秘钥,如果一方的秘钥遭泄露,那么整个通信就会被破解。而非对称加密使用一对秘钥,一个用来加密,一个用来解密,而且公钥是公开的,秘钥是自己保存的,不需要像对称加密那样在通信之前要先同步秘钥。
非对称加密的缺点是加密和解密花费时间长、速度慢,只适合对少量数据进行加密。
算法
工作的时候有对接银行接口,整理下关于RSA加密,双方通讯如何保证数据的安全 (RSA + 数字签名)
一般通讯的双方交换对方公钥, 各方持有本方的公私钥、与对方的公钥 (私钥只有本人持有)
通讯:
定义报文格式 : 128签名+加密报文
A通讯B
1. A用B的公钥加密明文,加密后的报文用A的私钥签名,组成报文发送B(报文约定格式)
2. B根据约定的报文格式获取加密的密文与签名,通过B的私钥解密获取明文、A的公钥验证签名(加密数据)
场景
数字签名原理
- A通过使用B的公钥加密数据后发给B,B利用B的私钥解密就得到了需要的数据(进过B公钥加密的数据只有B的私钥能够
- * 解开,C没有B的私钥,所以C解不开,但C可以使用B的公钥加密一份数据发给B,这样一来,问题来了,B收到的数据到
- * 底是A发过来的还是C发过来的呢)
- * 由于私钥是唯一的,那么A就可以利用A自己的私钥进行加密,然后B再利用A的公钥来解密,就可以确定:一定是A的消息,数字签名的原理就基于此
A想将目标数据传给B,此时A需要准备1和2两部分
- A使用B的公钥将原始信息加密,以起到保密作用(只有B的私钥能解开,其他人使用其他钥匙都解不开,当然就保密咯)
- A使用A的私钥将原始信息的摘要进行签名,以起到接收方B确定是A发过来的作用(A用A的私钥对目标数据的摘要进行签名,然后传给B,同时,C用C的私钥对任意信息进行签名也传给B,B想接受的是A的数据(比如说一个转帐请求),于是B 就通过A的公钥对接受到的两个信息进行解密,解开的就是A(A的公钥能且只能解开A的私钥加密的数据))
附上代码(生成非对称公私钥及加密、解密 + 数字签名)
public static final String KEY_ALGORITHM = "RSA"; public static final String SIGNATURE_ALGORITHM = "MD5withRSA"; // 初始化RSA非对称密钥 public static void generaterA() { try { KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA"); SecureRandom secrand = new SecureRandom(); keygen.initialize(1024, secrand); KeyPair keys = keygen.genKeyPair(); PublicKey pubkey = keys.getPublic(); PrivateKey prikey = keys.getPrivate(); String strpk = new String(Base64.encodeBase64(pubkey.getEncoded())); String strprivk = new String(Base64.encodeBase64(prikey.getEncoded())); DesSecretUtil.saveData(strpk.getBytes(), "D://rsa-pub_A.pem"); DesSecretUtil.saveData(strprivk.getBytes(), "D://rsa-pri_A.pem"); } catch (java.lang.Exception e) { System.out.println("生成密钥对失败"); e.printStackTrace(); } } /** * 用私钥对信息生成数字签名 * * @param data * 加密数据 * @param privateKey * 私钥 * * @return * @throws Exception */ public static String sign(byte[] data, String privateKey) throws Exception { // 解密由base64编码的私钥 byte[] keyBytes = Base64.decodeBase64(privateKey); // 构造PKCS8EncodedKeySpec对象 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); // KEY_ALGORITHM 指定的加密算法 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); // 取私钥匙对象 PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec); // 用私钥对信息生成数字签名 Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initSign(priKey); signature.update(data); // 加密由base64编码的签名 return Base64.encodeBase64String(signature.sign()); } /** * 校验数字签名 * * @param data * 加密数据 * @param publicKey * 公钥 * @param sign * 数字签名 * * @return 校验成功返回true 失败返回false * @throws Exception * */ public static boolean verify(byte[] data, String publicKey, String sign) throws Exception { // 解密由base64编码的公钥 byte[] keyBytes = Base64.decodeBase64(publicKey); // 构造X509EncodedKeySpec对象 X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); // KEY_ALGORITHM 指定的加密算法 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); // 取公钥匙对象 PublicKey pubKey = keyFactory.generatePublic(keySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initVerify(pubKey); signature.update(data); // 验证签名是否正常 return signature.verify(Base64.decodeBase64(sign)); } /** * 解密<br> * 用私钥解密 * * @param data * @param key * @return * @throws Exception */ public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception { // 对密钥解密 byte[] keyBytes = Base64.decodeBase64(key); // 取得私钥 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); // 对数据解密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(data); } /** * 解密<br> * 用公钥解密 * * @param data * @param key * @return * @throws Exception */ public static byte[] decryptByPublicKey(byte[] data, String key) throws Exception { // 对密钥解密 byte[] keyBytes = Base64.decodeBase64(key); // 取得公钥 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key publicKey = keyFactory.generatePublic(x509KeySpec); // 对数据解密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, publicKey); return cipher.doFinal(data); } /** * 加密<br> * 用公钥加密 * * @param data * @param key * @return * @throws Exception */ public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception { // base64 解密公钥 byte[] keyBytes = Base64.decodeBase64(key); X509EncodedKeySpec x509Key = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key publicKey = keyFactory.generatePublic(x509Key); // 对数据加密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, publicKey); return cipher.doFinal(data); } /** * 加密<br> * 用私钥加密 * * @param data * @param key * @return * @throws Exception */ public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception { // 对密钥解密 byte[] keyBytes = Base64.decodeBase64(key); // 取得私钥 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); // 对数据加密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, privateKey); return cipher.doFinal(data); } public static void main(String[] args) { // A向B发送数据 try { byte[] pub_A = DesSecretUtil.readData("D://rsa-pub_A.pem"); byte[] pri_A = DesSecretUtil.readData("D://rsa-pri_A.pem"); byte[] pub_B = DesSecretUtil.readData("D://rsa-pub_B.pem"); byte[] pri_B = DesSecretUtil.readData("D://rsa-pri_B.pem"); String str = "开始测试"; byte[] data = str.getBytes("UTF-8"); //拿B的公钥加密 byte[] encrptData = encryptByPublicKey(data, new String(pub_B)); System.out.println("加密后Base64数据:"+Base64.encodeBase64String(encrptData)); //拿B的私钥解密 byte[] decrptData = decryptByPrivateKey(encrptData,new String(pri_B)); System.out.println("解密后明文:"+new String(decrptData,"UTF-8")); // A的私钥数字签名 String sign = sign(encrptData,new String(pri_A)); // 加密由base64编码的签名 System.out.println("加密由base64编码的签名 :"+sign); // A的公钥数字签名验证是否信息被篡改 boolean flag = verify(decrptData,new String(pub_A),sign); System.out.println("数字签名验证 :"+flag); } catch (Exception e) { e.printStackTrace(); } }