Java常见加密方式

本篇内容简要介绍 BASE64、MD5、SHA、HMAC、DES、AES、PBE、RSA、DH、DSA几种加密算法。

  • BASE64:严格地说,属于编码格式,而非加密算法
    BASE的加密解密是双向的,可以求反解.
    BASEEncoder和BASEDecoder是非官方JDK实现类。虽然可以在JDK里能找到并使用,但是在API里查不到。JRE 中 sun 和 com.sun 开头包的类都是未被文档化的,他们属于 java, javax 类库的基础,其中的实现大多数与底层平台有关,一般来说是不推荐使用的。BASE 严格地说,属于编码格式,而非加密算法主要就是BASEEncoder、BASEDecoder两个类,我们只需要知道使用对应的方法即可。另,BASE加密后产生的字节位数是的倍数,如果不够位数以=符号填充。BASE按照RFC的定义,Base被定义为:Base内容传送编码被设计用来把任意序列的位字节描述为一种不易被人直接识别的形式。常见于邮件、http加密,截取http信息,你就会发现登录操作的用户名、密码字段通过BASE加密的。
    Java代码示例:
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class BASE {
  /**
   * BASE解密
   * @param key
   * @return
   * @throws Exception
   */
  public static byte[] decryptBASE(String key) throws Exception {
    return (new BASE64Decoder()).decodeBuffer(key);
  }
  /**
   * BASE加密
   * @param key
   * @return
   * @throws Exception
   */
  public static String encryptBASE(byte[] key) throws Exception {
    return (new BASE64Encoder()).encodeBuffer(key);
  }

  public static void main(String[] args) {
    String str = "123456";
    try {

      String result = BASE.encryptBASE(str.getBytes());

      System.out.println("加密数据>>>>:" + result);
      byte[] result1 = BASE.decryptBASE(result);

      String str1 = new String(result1);

      System.out.println("解密数据>>>>:" + str1);

    } catch (Exception e) {

      e.printStackTrace();

    }
  }
}

1.单向加密

该算法在加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,密文无法解密。只有重新输入明文,并经过同样的加密算法处理,得到相同的密文并被系统重新识别后,才能真正解密。常见的有:

  • MD5(Message Digest algorithm 5,信息摘要算法)广泛用于加密和解密技术,常用于文件校验。不管文件多大,经过MD5后都能生成唯一的MD5值。好比现在的ISO校验,都 是MD5校验。怎么用?当然是把ISO经过MD5后产生MD5的值。一般下载linux-ISO的朋友都见过下载链接旁边放着MD5的串。就是用来验证文 件是否一致的。
    Java代码示例:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.MessageDigest;

public class MD5Utils {
 private static final Logger logger = LoggerFactory.getLogger(MD5Utils.class);
 private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

 public static String md5toHex(String src) {
   try {
     MessageDigest md5 = MessageDigest.getInstance("MD5");
     byte[] bytes = md5.digest(src.getBytes("UTF-8"));
     return bytesToHex(bytes);
   } catch (Exception e) {
     logger.error("md5toHex occurs exception!", e);
     return null;
   }
 }

 public static String bytesToHex(byte[] byteArray) {

   char[] resultCharArray = new char[byteArray.length * 2];
   int index = 0;
   for (byte b : byteArray) {
     resultCharArray[index++] = HEX_DIGITS[b >>> 4 & 0xf];
     resultCharArray[index++] = HEX_DIGITS[b & 0xf];
   }
   return new String(resultCharArray);
 }

}
  • SHA(Secure Hash Algorithm,安全散列算法):数字签名等密码学应用中重要的工具,被广泛地应用于电子商务等信息安全领域。虽然,SHA与MD5通过碰撞法都被破解了,但是SHA仍然是公认的安全加密算法,较之MD5更为安全
    Java代码示例:
//单向加密

package com.cn.SHATest;

import java.math.BigInteger;

import java.security.MessageDigest;

public class SHA {

public static final String KEY_SHA = "SHA";

public static String getResult(String inputStr){

 BigInteger sha =null;

 System.out.println("加密前的数据>>>:"+inputStr);

 byte[] inputData = inputStr.getBytes();

 try {

  MessageDigest messageDigest = MessageDigest.getInstance(KEY_SHA);

  messageDigest.update(inputData);

  sha = new BigInteger(messageDigest.digest());

  System.out.println("SHA加密后>>>>>:" + sha.toString());

 } catch (Exception e) {e.printStackTrace();}

 return sha.toString();

}

public static void main(String args[]){

 try {

  String inputStr = "简单加密";

  getResult(inputStr);

 } catch (Exception e) {

  e.printStackTrace();

 }

}
  • HMAC(Hash Message Authentication Code,散列消息鉴别码,基于密钥的Hash算法的认证协议。消息鉴别码实现鉴别的原理是,用公开函数和密钥产生一个固定长度的值作为认证标识,用这个标识鉴别消息的完整性。使用一个密钥生成一个固定大小的小数据块,即MAC,并将其加入到消息中,然后传输。接收方利用与发送方共享的密钥进行鉴别认证等。
    Java代码示例:
//单向加密

package com.cn.HMACTest;

import javax.crypto.KeyGenerator;

import javax.crypto.Mac;

import javax.crypto.SecretKey;

import javax.crypto.spec.SecretKeySpec;

import com.cn.comm.Tools;

/**

 * 基础加密组件

 */

public abstract class HMAC {



 public static final String KEY_MAC = "HmacMD";



 /**

  * 初始化HMAC密钥

  *

  * @return

  * @throws Exception

 */

 public static String initMacKey() throws Exception {

  KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);

  SecretKey secretKey = keyGenerator.generateKey();

  return BASE.encryptBASE(secretKey.getEncoded());

 }

 /**
  * HMAC加密 :主要方法
  *
  * @param data
  * @param key
  * @return
  * @throws Exception
  */
 public static String encryptHMAC(byte[] data, String key) throws Exception {

  SecretKey secretKey = new SecretKeySpec(BASE.decryptBASE(key), KEY_MAC);

  Mac mac = Mac.getInstance(secretKey.getAlgorithm());

  mac.init(secretKey);

  return new String(mac.doFinal(data));

 }


 public static String getResult(String inputStr){

  String path=Tools.getClassPath();

  String fileSource=path+"/file/HMAC_key.txt";

  System.out.println("加密前的数据>>>>>:"+inputStr);

  String result=null;

  try {

   byte[] inputData = inputStr.getBytes();

   String key = HMAC.initMacKey(); /*产生密钥*/

   System.out.println("Mac密钥>>>>>:" + key);

   /*将密钥写文件*/

   Tools.WriteMyFile(fileSource,key);

   result= HMAC.encryptHMAC(inputData, key);

   System.out.println("HMAC加密后>>>>:" + result);

  } catch (Exception e) {

   e.printStackTrace();

  }

  return result.toString();
 }


 public static String getResult(String inputStr){

  System.out.println("加密前的数据>>>>:"+inputStr);

  String path=Tools.getClassPath();

  String fileSource=path+"/file/HMAC_key.txt";

  String key=null;;

  try {

   /*将密钥从文件中读取*/

   key=Tools.ReadMyFile(fileSource);

   System.out.println("getResult密钥>>>>:" + key);

  } catch (Exception e) {

   e.printStackTrace();

  }

  String result=null;

  try {

   byte[] inputData = inputStr.getBytes();

   /*对数据进行加密*/

   result= HMAC.encryptHMAC(inputData, key);

   System.out.println("HMAC加密后>>>>:" + result);

  } catch (Exception e) {

   e.printStackTrace();

  }
  return result.toString();
 }

 public static void main(String args[]){

  try {

   String inputStr = "123456";

   /*使用同一密钥:对数据进行加密:查看两次加密的结果是否一样*/

   getResult(inputStr);

   getResult(inputStr);

  } catch (Exception e) {

   e.printStackTrace();

  }

 }

}

2.对称加密

对称加密算法 是应用较早的加密算法,又称为 共享密钥加密算法。在 对称加密算法 中,使用的密钥只有一个,发送 和 接收 双方都使用这个密钥对数据进行 加密 和 解密。这就要求加密和解密方事先都必须知道加密的密钥。

  • DES(Data Encryption Standard,数据加密算法):DES-Data Encryption Standard,即数据加密算法。是IBM公司于1975年研究成功并公开发表的。DES算法的入口参数有三个:Key、Data、Mode。其中 Key为8个字节共64位,是DES算法的工作密钥;Data也为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:加密 或解密。DES算法把64位的明文输入块变为64位的密文输出块,它所使用的密钥也是64位。
    Java代码示例:
    Coder类,以下代码都继承到这个类

import java.security.MessageDigest;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * 基础加密组件
 */
public abstract class Coder {
  public static final String KEY_SHA = "SHA";
  public static final String KEY_MD5 = "MD5";

  /**
   * MAC算法可选以下多种算法
   * 
   * <pre>
   * HmacMD5 
   * HmacSHA1 
   * HmacSHA256 
   * HmacSHA384 
   * HmacSHA512
   * </pre>
   */
  public static final String KEY_MAC = "HmacMD5";

  /**
   * BASE64解密
   * 
   * @param key
   * @return
   * @throws Exception
   */
  public static byte[] decryptBASE64(String key) throws Exception {
    return (new BASE64Decoder()).decodeBuffer(key);
  }

  /**
   * BASE64加密
   * 
   * @param key
   * @return
   * @throws Exception
   */
  public static String encryptBASE64(byte[] key) throws Exception {
    return (new BASE64Encoder()).encodeBuffer(key);
  }

  /**
   * MD5加密
   * 
   * @param data
   * @return
   * @throws Exception
   */
  public static byte[] encryptMD5(byte[] data) throws Exception {

    MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
    md5.update(data);

    return md5.digest();

  }

  /**
   * SHA加密
   * 
   * @param data
   * @return
   * @throws Exception
   */
  public static byte[] encryptSHA(byte[] data) throws Exception {

    MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
    sha.update(data);

    return sha.digest();

  }

  /**
   * 初始化HMAC密钥
   * 
   * @return
   * @throws Exception
   */
  public static String initMacKey() throws Exception {
    KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);

    SecretKey secretKey = keyGenerator.generateKey();
    return encryptBASE64(secretKey.getEncoded());
  }

  /**
   * HMAC加密
   * 
   * @param data
   * @param key
   * @return
   * @throws Exception
   */
  public static byte[] encryptHMAC(byte[] data, String key) throws Exception {

    SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);
    Mac mac = Mac.getInstance(secretKey.getAlgorithm());
    mac.init(secretKey);

    return mac.doFinal(data);

  }
}

DESCoder类:

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.security.Key;
import java.security.SecureRandom;

public abstract class DESCoder extends Coder{
  public static final String ALGORITHM = "DES";
  /**
 * 转换密钥<br>
 * 
 * @param key
 * @return
 * @throws Exception
   */
  private static Key toKey(byte[] key) throws Exception {
    DESKeySpec dks = new DESKeySpec(key);
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
    SecretKey secretKey = keyFactory.generateSecret(dks);

    // 当使用其他对称加密算法时,如AES、Blowfish等算法时,用下述代码替换上述三行代码
    // SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);

    return secretKey;
  }

  /**
 * 解密
 * 
 * @param data
 * @param key
 * @return
 * @throws Exception
   */
  public static byte[] decrypt(byte[] data, String key) throws Exception {
    Key k = toKey(decryptBASE64(key));

    Cipher cipher = Cipher.getInstance(ALGORITHM);
    cipher.init(Cipher.DECRYPT_MODE, k);

    return cipher.doFinal(data);
  }

  /**
 * 加密
 * 
 * @param data
 * @param key
 * @return
 * @throws Exception
   */
  public static byte[] encrypt(byte[] data, String key) throws Exception {
    Key k = toKey(decryptBASE64(key));
    Cipher cipher = Cipher.getInstance(ALGORITHM);
    cipher.init(Cipher.ENCRYPT_MODE, k);

    return cipher.doFinal(data);
  }

  /**
 * 生成密钥
 * 
 * @return
 * @throws Exception
   */
  public static String initKey() throws Exception {
    return initKey(null);
  }

  /**
 * 生成密钥
 * 
 * @param seed
 * @return
 * @throws Exception
   */
  public static String initKey(String seed) throws Exception {
    SecureRandom secureRandom = null;

    if (seed != null) {
      secureRandom = new SecureRandom(decryptBASE64(seed));
    } else {
      secureRandom = new SecureRandom();
    }

    KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM);
    kg.init(secureRandom);

    SecretKey secretKey = kg.generateKey();

    return encryptBASE64(secretKey.getEncoded());
  }

  public static void test() throws Exception {
    String inputStr = "DES";
    String key = DESCoder.initKey();
    System.err.println("原文:\t" + inputStr);

    System.err.println("密钥:\t" + key);

    byte[] inputData = inputStr.getBytes();
    inputData = DESCoder.encrypt(inputData, key);

    System.err.println("加密后:\t" + encryptBASE64(inputData));

    byte[] outputData = DESCoder.decrypt(inputData, key);
    String outputStr = new String(outputData);

    System.err.println("解密后:\t" + outputStr);

  }

  public static void main(String[] args) throws Exception {
    test();
  }
}
  • AES(Advanced Encryption Standard,高级加密算法):高级数据加密标准(Advanced Encryption Standard),简称AES,由美国政府于1997年开始公开征集的新的数据加密标准算法。经过三轮筛选,美国政府最终于2000年10月2日正式宣布选中密码学家Joan Daemen和Vincent Rijmen提出的RINJDAEL算法作为AES。
    Java代码示例:
public class AESTest {

 /**
* AES加密字符串
*  * @param content
*      需要被加密的字符串
* @param password
*      加密需要的密码
* @return 密文
  */
 public static byte[] encrypt(String content, String password) {

   try {
     // 创建AES的Key生产者
     KeyGenerator kgen = KeyGenerator.getInstance("AES");
     // 利用用户密码作为随机数初始化出128位的key生产者
     kgen.init(128, new SecureRandom(password.getBytes()));
     //加密没关系,SecureRandom是生成安全随机数序列,password.getBytes()是种子,只要种子相同,序列就一样,所以解密只要有password就行
     SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥
     // 返回基本编码格式的密钥,如果此密钥不支持编码,则返回null。
     byte[] enCodeFormat = secretKey.getEncoded();
     // 转换为AES专用密钥
     SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
     // 创建密码器
     Cipher cipher = Cipher.getInstance("AES");
     byte[] byteContent = content.getBytes("utf-8");
     // 初始化为加密模式的密码器
     cipher.init(Cipher.ENCRYPT_MODE, key);
     // 加密
     byte[] result = cipher.doFinal(byteContent);
     return result;
   } catch (NoSuchPaddingException e) {
     e.printStackTrace();
   } catch (NoSuchAlgorithmException e) {
     e.printStackTrace();
   } catch (UnsupportedEncodingException e) {
     e.printStackTrace();
   } catch (InvalidKeyException e) {
     e.printStackTrace();
   } catch (IllegalBlockSizeException e) {
     e.printStackTrace();
   } catch (BadPaddingException e) {
     e.printStackTrace();
   }
   return null;
 }

 /**
* 解密AES加密过的字符串
*  * @param content
*      AES加密过过的内容
* @param password
*      加密时的密码
* @return 明文
  */
 public static byte[] decrypt(byte[] content, String password) {
   try {
     // 创建AES的Key生产者
     KeyGenerator kgen = KeyGenerator.getInstance("AES");
     kgen.init(128, new SecureRandom(password.getBytes()));
     // 根据用户密码,生成一个密钥
     SecretKey secretKey = kgen.generateKey();
     // 返回基本编码格式的密钥
     byte[] enCodeFormat = secretKey.getEncoded();
     // 转换为AES专用密钥
     SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
     // 创建密码器
     Cipher cipher = Cipher.getInstance("AES");
     // 初始化为解密模式的密码器
     cipher.init(Cipher.DECRYPT_MODE, key);
     byte[] result = cipher.doFinal(content);
     // 明文
     return result;
   } catch (NoSuchAlgorithmException e) {
     e.printStackTrace();
   } catch (NoSuchPaddingException e) {
     e.printStackTrace();
   } catch (InvalidKeyException e) {
     e.printStackTrace();
   } catch (IllegalBlockSizeException e) {
     e.printStackTrace();
   } catch (BadPaddingException e) {
     e.printStackTrace();
   }
   return null;
 }


 /**将二进制转换成16进制
* @param buf
* @return
  */
 public static String parseByte2HexStr(byte buf[]) {
     StringBuffer sb = new StringBuffer();
     for (int i = 0; i < buf.length; i++) {
         String hex = Integer.toHexString(buf[i] & 0xFF);
         if (hex.length() == 1) {
             hex = '0' + hex;
         }
         sb.append(hex.toUpperCase());
     }
     return sb.toString();
 }

 /**将16进制转换为二进制
* @param hexStr
* @return
  */
 public static byte[] parseHexStr2Byte(String hexStr) {
     if (hexStr.length() < 1)
         return null;
     byte[] result = new byte[hexStr.length()/2];
     for (int i = 0;i< hexStr.length()/2; i++) {
         int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);
         int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
         result[i] = (byte) (high * 16 + low);
     }
     return result;
 }

 public static void main(String[] args) {
   String content = "美女,一起共进晚餐么?";
   String password = "123";//密码就是key
   System.out.println("加密之前:" + content);
   // 加密
   byte[] encrypt = AesTest.encrypt(content, password);
   System.out.println("加密后的内容:" + new String(encrypt));

   //如果想要加密内容不显示乱码,可以先将密文转换为16进制
   String hexStrResult = parseByte2HexStr(encrypt);
   System.out.println("16进制的密文:" + hexStrResult);

   //如果的到的是16进制密文,别忘了先转为2进制再解密
   byte[] twoStrResult = parseHexStr2Byte(hexStrResult);

   // 解密
   byte[] decrypt = AesTest.decrypt(encrypt, password);
   System.out.println("解密后的内容:" + new String(decrypt));
 }
}
  • PBE——Password-based encryption(基于密码加密)。其特点在于口令由用户自己掌管,不借助任何物理媒体;采用随机数(这里我们叫做盐)杂凑多重加密等方法保证数据的安全性。是一种简便的加密方式。
    Java代码示例:
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.security.Key;
import java.util.Random;

public abstract class PBECoder extends Coder {
 /**
* 支持以下任意一种算法
* 
* <pre>
* PBEWithMD5AndDES 
* PBEWithMD5AndTripleDES 
* PBEWithSHA1AndDESede
* PBEWithSHA1AndRC2_40
* </pre>
  */
 public static final String ALGORITHM = "PBEWITHMD5andDES";

 /**
* 盐初始化
* 
* @return
* @throws Exception
  */
 public static byte[] initSalt() throws Exception {
   byte[] salt = new byte[8];
   Random random = new Random();
   random.nextBytes(salt);
   return salt;
 }

 /**
* 转换密钥<br>
* 
* @param password
* @return
* @throws Exception
  */
 private static Key toKey(String password) throws Exception {
   PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
   SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
   SecretKey secretKey = keyFactory.generateSecret(keySpec);

   return secretKey;
 }

 /**
* 加密
* 
* @param data 数据
* @param password 密码
* @param salt 盐
* @return
* @throws Exception
  */
 public static byte[] encrypt(byte[] data, String password, byte[] salt)
     throws Exception {

   Key key = toKey(password);

   PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
   Cipher cipher = Cipher.getInstance(ALGORITHM);
   cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);

   return cipher.doFinal(data);

 }

 /**
* 解密
* 
* @param data 数据
* @param password 密码
* @param salt 盐
* @return
* @throws Exception
  */
 public static byte[] decrypt(byte[] data, String password, byte[] salt)
     throws Exception {

   Key key = toKey(password);

   PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
   Cipher cipher = Cipher.getInstance(ALGORITHM);
   cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);

   return cipher.doFinal(data);

 }


 public static void test() throws Exception {
   String inputStr = "abc";
   System.err.println("原文: " + inputStr);
   byte[] input = inputStr.getBytes();

   String pwd = "efg";
   System.err.println("密码: " + pwd);

   byte[] salt = PBECoder.initSalt();

   byte[] data = PBECoder.encrypt(input, pwd, salt);

   System.err.println("加密后: " + PBECoder.encryptBASE64(data));

   byte[] output = PBECoder.decrypt(data, pwd, salt);
   String outputStr = new String(output);

   System.err.println("解密后: " + outputStr);
 }

 public static void main(String[] args) throws Exception {
   test();
 }
}

3.非对称加密

非对称加密算法,又称为 公开密钥加密算法。它需要两个密钥,一个称为 公开密钥 (public key),即 公钥,另一个称为 私有密钥 (private key),即 私钥。

  • RSA(算法的名字以发明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman),这种算法1978年就出现了,它是第一个既能用于数据加密也能用于数字签名的算法。它易于理解和操作,也很流行。算法的名字以发明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman。
    这种加密算法的特点主要是密钥的变化,我们知道DES只有一个密钥。相当于只有一把钥匙,如果这把钥匙丢了,数据也就不安全了。RSA同时有两把钥 匙,公钥与私钥。同时支持数字签名。数字签名的意义在于,对传输过来的数据进行校验。确保数据在传输工程中不被修改。
    Java代码示例:
    RSACoder类:
package com.gomefinance.united.sale.activity.biz.service;

import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;
/**
*RSA安全编码组件
*/
public abstract class RSACoder extends Coder {
  public static final String KEY_ALGORITHM = "RSA";
  public static final String SIGNATURE_ALGORITHM = "MD5withRSA";

  private static final String PUBLIC_KEY = "RSAPublicKey";
  private static final String PRIVATE_KEY = "RSAPrivateKey";

  /**
   * 用私钥对信息生成数字签名
   * 
   * @param data
   *      加密数据
   * @param privateKey
   *      私钥
   * 
   * @return
   * @throws Exception
   */
  public static String sign(byte[] data, String privateKey) throws Exception {
    // 解密由base64编码的私钥
    byte[] keyBytes = decryptBASE64(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);

    return encryptBASE64(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 = decryptBASE64(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(decryptBASE64(sign));
  }

  /**
   * 解密<br>
   * 用私钥解密
   * 
   * @param data
   * @param key
   * @return
   * @throws Exception
   */
  public static byte[] decryptByPrivateKey(byte[] data, String key)
      throws Exception {
    // 对密钥解密
    byte[] keyBytes = decryptBASE64(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 = decryptBASE64(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 {
    // 对公钥解密
    byte[] keyBytes = decryptBASE64(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.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 = decryptBASE64(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);
  }

  /**
   * 取得私钥
   * 
   * @param keyMap
   * @return
   * @throws Exception
   */
  public static String getPrivateKey(Map<String, Object> keyMap)
      throws Exception {
    Key key = (Key) keyMap.get(PRIVATE_KEY);

    return encryptBASE64(key.getEncoded());
  }

  /**
   * 取得公钥
   * 
   * @param keyMap
   * @return
   * @throws Exception
   */
  public static String getPublicKey(Map<String, Object> keyMap)
      throws Exception {
    Key key = (Key) keyMap.get(PUBLIC_KEY);

    return encryptBASE64(key.getEncoded());
  }

  /**
   * 初始化密钥
   * 
   * @return
   * @throws Exception
   */
  public static Map<String, Object> initKey() throws Exception {
    KeyPairGenerator keyPairGen = KeyPairGenerator
        .getInstance(KEY_ALGORITHM);
    keyPairGen.initialize(1024);

    KeyPair keyPair = keyPairGen.generateKeyPair();

    // 公钥
    RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();

    // 私钥
    RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

    Map<String, Object> keyMap = new HashMap<String, Object>(2);

    keyMap.put(PUBLIC_KEY, publicKey);
    keyMap.put(PRIVATE_KEY, privateKey);
    return keyMap;
  }
}

RSACoderTest类:

package com.gomefinance.united.sale.activity.biz.service;

import java.util.Map;

/**
 * RSA测试类
 */
public class RSACoderTest {
  private static String publicKey;
  private static String privateKey;
  static {
    Map<String, Object> keyMap = null;
    try {
      keyMap = RSACoder.initKey();
      publicKey = RSACoder.getPublicKey(keyMap);
      privateKey = RSACoder.getPrivateKey(keyMap);
      System.err.println("公钥: \n\r" + publicKey);
      System.err.println("私钥: \n\r" + privateKey);
    } catch (Exception e) {
      e.printStackTrace();
    }

  }

  public static void test() throws Exception {
    System.err.println("公钥加密——私钥解密");
    String inputStr = "abc";
    byte[] data = inputStr.getBytes();

    byte[] encodedData = RSACoder.encryptByPublicKey(data, publicKey);

    byte[] decodedData = RSACoder.decryptByPrivateKey(encodedData,
        privateKey);

    String outputStr = new String(decodedData);
    System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);

  }

  public static void testSign() throws Exception {
    System.err.println("私钥加密——公钥解密");
    String inputStr = "sign";
    byte[] data = inputStr.getBytes();

    byte[] encodedData = RSACoder.encryptByPrivateKey(data, privateKey);

    byte[] decodedData = RSACoder
        .decryptByPublicKey(encodedData, publicKey);

    String outputStr = new String(decodedData);
    System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
    System.err.println("私钥签名——公钥验证签名");
    // 产生签名
    String sign = RSACoder.sign(encodedData, privateKey);
    System.err.println("签名:\r" + sign);

    // 验证签名
    boolean status = RSACoder.verify(encodedData, publicKey, sign);
    System.err.println("状态:\r" + status);

  }

  public static void main(String[] args) throws Exception {
    test();
    testSign();
  }

}
  • DH(Diffie- Hellman算法(D-H算法):密钥一致协议。是由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。简单的说就是允许两名用 户在公开媒体上交换信息以生成"一致"的、可以共享的密钥。换句话说,就是由甲方产出一对密钥(公钥、私钥),乙方依照甲方公钥产生乙方密钥对(公钥、私 钥)。以此为基线,作为数据传输保密基础,同时双方使用同一种对称加密算法构建本地密钥(SecretKey)对数据加密。这样,在互通了本地密钥 (SecretKey)算法后,甲乙双方公开自己的公钥,使用对方的公钥和刚才产生的私钥加密数据,同时可以使用对方的公钥和自己的私钥对数据解密。不单 单是甲乙双方两方,可以扩展为多方共享数据通讯,这样就完成了网络交互数据的安全通讯!该算法源于中国的同余定理——中国馀数定理。
    Java代码示例:
    DHCoder类:
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;

/**
 * DH安全编码组件
 */
public abstract class DHCoder extends Coder {
  public static final String ALGORITHM = "DH";

  /**
   * 默认密钥字节数
   * 
   * <pre>
   * DH
   * Default Keysize 1024  
   * Keysize must be a multiple of 64, ranging from 512 to 1024 (inclusive).
   * </pre>
   */
  private static final int KEY_SIZE = 1024;

  /**
   * DH加密下需要一种对称加密算法对数据加密,这里我们使用DES,也可以使用其他对称加密算法。
   */
  public static final String SECRET_ALGORITHM = "DES";
  private static final String PUBLIC_KEY = "DHPublicKey";
  private static final String PRIVATE_KEY = "DHPrivateKey";

  /**
   * 初始化甲方密钥
   * 
   * @return
   * @throws Exception
   */
  public static Map<String, Object> initKey() throws Exception {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator
        .getInstance(ALGORITHM);
    keyPairGenerator.initialize(KEY_SIZE);

    KeyPair keyPair = keyPairGenerator.generateKeyPair();

    // 甲方公钥
    DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();

    // 甲方私钥
    DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();

    Map<String, Object> keyMap = new HashMap<String, Object>(2);

    keyMap.put(PUBLIC_KEY, publicKey);
    keyMap.put(PRIVATE_KEY, privateKey);
    return keyMap;
  }

  /**
   * 初始化乙方密钥
   * 
   * @param key
   *      甲方公钥
   * @return
   * @throws Exception
   */
  public static Map<String, Object> initKey(String key) throws Exception {
    // 解析甲方公钥
    byte[] keyBytes = decryptBASE64(key);
    X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
    PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);

    // 由甲方公钥构建乙方密钥
    DHParameterSpec dhParamSpec = ((DHPublicKey) pubKey).getParams();

    KeyPairGenerator keyPairGenerator = KeyPairGenerator
        .getInstance(keyFactory.getAlgorithm());
    keyPairGenerator.initialize(dhParamSpec);

    KeyPair keyPair = keyPairGenerator.generateKeyPair();

    // 乙方公钥
    DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();

    // 乙方私钥
    DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();

    Map<String, Object> keyMap = new HashMap<String, Object>(2);

    keyMap.put(PUBLIC_KEY, publicKey);
    keyMap.put(PRIVATE_KEY, privateKey);

    return keyMap;
  }

  /**
   * 加密<br>
   * 
   * @param data
   *      待加密数据
   * @param publicKey
   *      甲方公钥
   * @param privateKey
   *      乙方私钥
   * @return
   * @throws Exception
   */
  public static byte[] encrypt(byte[] data, String publicKey,
      String privateKey) throws Exception {

    // 生成本地密钥
    SecretKey secretKey = getSecretKey(publicKey, privateKey);

    // 数据加密
    Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);

    return cipher.doFinal(data);
  }

  /**
   * 解密<br>
   * 
   * @param data
   *      待解密数据
   * @param publicKey
   *      乙方公钥
   * @param privateKey
   *      乙方私钥
   * @return
   * @throws Exception
   */
  public static byte[] decrypt(byte[] data, String publicKey,
      String privateKey) throws Exception {

    // 生成本地密钥
    SecretKey secretKey = getSecretKey(publicKey, privateKey);
    // 数据解密
    Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
    cipher.init(Cipher.DECRYPT_MODE, secretKey);

    return cipher.doFinal(data);
  }

  /**
   * 构建密钥
   * 
   * @param publicKey
   *      公钥
   * @param privateKey
   *      私钥
   * @return
   * @throws Exception
   */
  private static SecretKey getSecretKey(String publicKey, String privateKey)
      throws Exception {
    // 初始化公钥
    byte[] pubKeyBytes = decryptBASE64(publicKey);

    KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
    X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKeyBytes);
    PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);

    // 初始化私钥
    byte[] priKeyBytes = decryptBASE64(privateKey);

    PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKeyBytes);
    Key priKey = keyFactory.generatePrivate(pkcs8KeySpec);

    KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory
        .getAlgorithm());
    keyAgree.init(priKey);
    keyAgree.doPhase(pubKey, true);

    // 生成本地密钥
    SecretKey secretKey = keyAgree.generateSecret(SECRET_ALGORITHM);

    return secretKey;
  }

  /**
   * 取得私钥
   * 
   * @param keyMap
   * @return
   * @throws Exception
   */
  public static String getPrivateKey(Map<String, Object> keyMap)
      throws Exception {
    Key key = (Key) keyMap.get(PRIVATE_KEY);

    return encryptBASE64(key.getEncoded());
  }

  /**
   * 取得公钥
   * 
   * @param keyMap
   * @return
   * @throws Exception
   */
  public static String getPublicKey(Map<String, Object> keyMap)
      throws Exception {
    Key key = (Key) keyMap.get(PUBLIC_KEY);

    return encryptBASE64(key.getEncoded());
  }
}

DHCoderTest类:

package com.gomefinance.united.sale.activity.biz.service;


import java.util.Map;

/**
 * DHCoder测试类
 */
public class DHCoderTest {


  public static void test() throws Exception {
    // 生成甲方密钥对儿
    Map<String, Object> aKeyMap = DHCoder.initKey();
    String aPublicKey = DHCoder.getPublicKey(aKeyMap);
    String aPrivateKey = DHCoder.getPrivateKey(aKeyMap);

    System.err.println("甲方公钥:\r" + aPublicKey);
    System.err.println("甲方私钥:\r" + aPrivateKey);

    // 由甲方公钥产生本地密钥对儿
    Map<String, Object> bKeyMap = DHCoder.initKey(aPublicKey);
    String bPublicKey = DHCoder.getPublicKey(bKeyMap);
    String bPrivateKey = DHCoder.getPrivateKey(bKeyMap);

    System.err.println("乙方公钥:\r" + bPublicKey);
    System.err.println("乙方私钥:\r" + bPrivateKey);

    String aInput = "abc ";
    System.err.println("原文: " + aInput);

    // 由甲方公钥,乙方私钥构建密文
    byte[] aCode = DHCoder.encrypt(aInput.getBytes(), aPublicKey,
        bPrivateKey);

    // 由乙方公钥,甲方私钥解密
    byte[] aDecode = DHCoder.decrypt(aCode, bPublicKey, aPrivateKey);
    String aOutput = (new String(aDecode));

    System.err.println("解密: " + aOutput);


    System.err.println(" ===============反过来加密解密================== ");
    String bInput = "def ";
    System.err.println("原文: " + bInput);

    // 由乙方公钥,甲方私钥构建密文
    byte[] bCode = DHCoder.encrypt(bInput.getBytes(), bPublicKey,
        aPrivateKey);

    // 由甲方公钥,乙方私钥解密
    byte[] bDecode = DHCoder.decrypt(bCode, aPublicKey, bPrivateKey);
    String bOutput = (new String(bDecode));

    System.err.println("解密: " + bOutput);

  }

  public static void main(String[] args) throws Exception {
    test();
  }

}
  • DSA-Digital Signature Algorithm 是Schnorr和ElGamal签名算法的变种,被美国NIST作为DSS(DigitalSignature Standard)。简单的说,这是一种更高级的验证方式,用作数字签名。不单单只有公钥、私钥,还有数字签名。私钥加密生成数字签名,公钥验证数据及签 名。如果数据和签名不匹配则认为验证失败!数字签名的作用就是校验数据在传输过程中不被修改。数字签名,是单向加密的升级!
    java代码示例:
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
* DSA安全编码组件
*/
public abstract class DSACoder extends Coder {

 public static final String ALGORITHM = "DSA";

 /**
  * 默认密钥字节数
  * 
  * <pre>
  * DSA 
  * Default Keysize 1024  
  * Keysize must be a multiple of 64, ranging from 512 to 1024 (inclusive).
  * </pre>
  */
 private static final int KEY_SIZE = 1024;

 /**
  * 默认种子
  */
 private static final String DEFAULT_SEED = "0f22507a10bbddd07d8a3082122966e3";

 private static final String PUBLIC_KEY = "DSAPublicKey";
 private static final String PRIVATE_KEY = "DSAPrivateKey";

 /**
  * 用私钥对信息生成数字签名
  * 
  * @param data
  *      加密数据
  * @param privateKey
  *      私钥
  * 
  * @return
  * @throws Exception
  */
 public static String sign(byte[] data, String privateKey) throws Exception {
   // 解密由base64编码的私钥
   byte[] keyBytes = decryptBASE64(privateKey);

   // 构造PKCS8EncodedKeySpec对象
   PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);

   // KEY_ALGORITHM 指定的加密算法
   KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);

   // 取私钥匙对象
   PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);

   // 用私钥对信息生成数字签名
   Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
   signature.initSign(priKey);
   signature.update(data);

   return encryptBASE64(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 = decryptBASE64(publicKey);

   // 构造X509EncodedKeySpec对象
   X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

   // ALGORITHM 指定的加密算法
   KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);

   // 取公钥匙对象
   PublicKey pubKey = keyFactory.generatePublic(keySpec);

   Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
   signature.initVerify(pubKey);
   signature.update(data);

   // 验证签名是否正常
   return signature.verify(decryptBASE64(sign));
 }

 /**
  * 生成密钥
  * 
  * @param seed
  *      种子
  * @return 密钥对象
  * @throws Exception
  */
 public static Map<String, Object> initKey(String seed) throws Exception {
   KeyPairGenerator keygen = KeyPairGenerator.getInstance(ALGORITHM);
   // 初始化随机产生器
   SecureRandom secureRandom = new SecureRandom();
   secureRandom.setSeed(seed.getBytes());
   keygen.initialize(KEY_SIZE, secureRandom);

   KeyPair keys = keygen.genKeyPair();

   DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic();
   DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate();

   Map<String, Object> map = new HashMap<String, Object>(2);
   map.put(PUBLIC_KEY, publicKey);
   map.put(PRIVATE_KEY, privateKey);

   return map;
 }

 /**
  * 默认生成密钥
  * 
  * @return 密钥对象
  * @throws Exception
  */
 public static Map<String, Object> initKey() throws Exception {
   return initKey(DEFAULT_SEED);
 }

 /**
  * 取得私钥
  * 
  * @param keyMap
  * @return
  * @throws Exception
  */
 public static String getPrivateKey(Map<String, Object> keyMap)
     throws Exception {
   Key key = (Key) keyMap.get(PRIVATE_KEY);

   return encryptBASE64(key.getEncoded());
 }

 /**
  * 取得公钥
  * 
  * @param keyMap
  * @return
  * @throws Exception
  */
 public static String getPublicKey(Map<String, Object> keyMap)
     throws Exception {
   Key key = (Key) keyMap.get(PUBLIC_KEY);

   return encryptBASE64(key.getEncoded());
 }

 public static void test() throws Exception {
   String inputStr = "123";
   byte[] data = inputStr.getBytes();

   // 构建密钥
   Map<String, Object> keyMap = DSACoder.initKey();

   // 获得密钥
   String publicKey = DSACoder.getPublicKey(keyMap);
   String privateKey = DSACoder.getPrivateKey(keyMap);

   System.err.println("公钥:\r" + publicKey);
   System.err.println("私钥:\r" + privateKey);

   // 产生签名
   String sign = DSACoder.sign(data, privateKey);
   System.err.println("签名:\r" + sign);

   // 验证签名
   boolean status = DSACoder.verify(data, publicKey, sign);
   System.err.println("状态:\r" + status);

 }

 public static void main(String[] args) throws Exception {
   test();
 }
}
  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值