Java-web下使用RSA进行加密解密操作

最近在看,网络安全方面的问题,我们可以使用RSA进行非对称加密防止,获取用户信息。首先我们看下Java下操作RSA进行加密解密算法,代码如下:

[html]  view plain  copy
  1. package com.jb.test;  
  2.   
  3. import java.security.InvalidKeyException;  
  4. import java.security.KeyPair;  
  5. import java.security.KeyPairGenerator;  
  6. import java.security.NoSuchAlgorithmException;  
  7. import java.security.PrivateKey;  
  8. import java.security.PublicKey;  
  9. import java.security.SecureRandom;  
  10.   
  11. import javax.crypto.BadPaddingException;  
  12. import javax.crypto.Cipher;  
  13. import javax.crypto.IllegalBlockSizeException;  
  14. import javax.crypto.NoSuchPaddingException;  
  15.   
  16. import org.apache.commons.codec.binary.Hex;  
  17.   
  18. public class RSAEntry {  
  19.   
  20.     /**  
  21.      * @Title: main  
  22.      * @Description: RSA加密算法,解密算法  
  23.      * @param args  
  24.      * void  
  25.      * @throws NoSuchAlgorithmException   
  26.      * @throws NoSuchPaddingException   
  27.      * @throws InvalidKeyException   
  28.      * @throws BadPaddingException   
  29.      * @throws IllegalBlockSizeException   
  30.      *   
  31.      * @throws  
  32.      */  
  33.     public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {  
  34. //      Security.getProviders();//获取所有支持的加密算法  
  35.         //采用非对称加密解密算法  
  36.         //生成密钥实例  
  37.         KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");  
  38.         SecureRandom random = new SecureRandom();  
  39.         random.setSeed(System.currentTimeMillis());//设置随机种子  
  40.         keygen.initialize(512, random);//设置密钥长度,应为64的整数倍  
  41.         //生成密钥公钥对  
  42.         KeyPair keyPair = keygen.generateKeyPair();  
  43.         //获取公钥  
  44.         PublicKey pubkey = keyPair.getPublic();  
  45.         //获取私钥  
  46.         PrivateKey prikey = keyPair.getPrivate();  
  47.         //测试数据  
  48.         String data = "测试数据";  
  49.         //使用公钥进行加密  
  50.         //构建加密解密类  
  51.         Cipher cipher = Cipher.getInstance("RSA");  
  52.         cipher.init(Cipher.ENCRYPT_MODE, pubkey);//设置为加密模式  
  53.         byte[] jmdata = cipher.doFinal(data.getBytes());  
  54.         //打印加密后数据  
  55.         System.out.println(new String(Hex.encodeHex(jmdata)));  
  56.         //改为解密模式进行解密  
  57.         cipher.init(Cipher.DECRYPT_MODE, prikey);//会用私钥解密  
  58.         jmdata = cipher.doFinal(jmdata);  
  59.         System.out.println(new String(jmdata));  
  60.           
  61.     }  
  62.   
  63. }  

在web应用中,我们可以通过js进行前端加密,java进行后台解密,已达到我们的目的。这里需要注意的是,要想实现正确的加密解密算法,需要使用bcprov-ext-jdk15on-147.jar。

首先创建系统的密钥提供者:

[html]  view plain  copy
  1. package com.jb.test;  
  2.   
  3. import java.security.KeyPair;  
  4. import java.security.KeyPairGenerator;  
  5. import java.security.PrivateKey;  
  6. import java.security.PublicKey;  
  7. import java.security.SecureRandom;  
  8.   
  9. import org.apache.commons.codec.binary.Hex;  
  10. import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey;  
  11. import org.bouncycastle.jce.provider.BouncyCastleProvider;  
  12.   
  13. /**  
  14.  * RSA初始化类  
  15.  * @author nmm  
  16.  * 结合前台的js使用的话,主要需要指定密钥提供者,即引入bcprov-ext-jdk15on-147.jar并使用其中的提供者  
  17.  */  
  18. public class RsaInitUtil {  
  19.       
  20.     private static KeyPair keyPair;  
  21.       
  22.     private static RsaInitUtil util;  
  23.       
  24.     private RsaInitUtil(){  
  25.         try {  
  26.             if(keyPair == null) {  
  27.                 //如果想要能够解密js的加密文件,使用此提供者是必须的  
  28.                 KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());  
  29.                 SecureRandom random = new SecureRandom();  
  30.                 random.setSeed(System.currentTimeMillis());  
  31.                 keygen.initialize(512, random);//设置512位长度  
  32.                 keyPair = keygen.generateKeyPair();  
  33.             }  
  34.         } catch (Exception e) {  
  35.             e.printStackTrace();  
  36.         }  
  37.     }  
  38.       
  39.     public static RsaInitUtil getInstance(){  
  40.         synchronized ("rsa") {  
  41.             if(util == null) {  
  42.                 util = new RsaInitUtil();  
  43.             }  
  44.         }  
  45.         return util;  
  46.     }  
  47.       
  48.     /**  
  49.      *   
  50.      * 功能说明:[获取公钥]  
  51.      * @return  
  52.      * 创建者:Nmm, Aug 19, 2013  
  53.      */  
  54.     public PublicKey getPublicKey(){  
  55.         return keyPair.getPublic();  
  56.     }  
  57.       
  58.     public PrivateKey getPrivateKey(){  
  59.         return keyPair.getPrivate();  
  60.     }  
  61.       
  62.     /**  
  63.      *   
  64.      * 功能说明:[获取公钥字符串]  
  65.      * @return  
  66.      * 创建者:Nmm, Aug 19, 2013  
  67.      */  
  68.     public String getPublicKeyStr(){  
  69.         //根据我们的提供者,这里获取的是该类型公钥  
  70.         BCRSAPublicKey pk = (BCRSAPublicKey) getPublicKey();  
  71.         String str = new String(Hex.encodeHex(pk.getModulus().toByteArray()));  
  72.         System.out.println(str);  
  73.         //获取入口10001一般都为这个  
  74.         String ss = new String(Hex.encodeHex(pk.getPublicExponent().toByteArray()));  
  75.         //获取转换字符串  
  76.         System.out.println(b2Hex(pk.getModulus().toByteArray()));  
  77.         return ss + str;  
  78.     }  
  79.     /**  
  80.      *   
  81.      * 功能说明:[手动转换]  
  82.      * @param byteArray  
  83.      * @return  
  84.      * 创建者:Nmm, Aug 19, 2013  
  85.      */  
  86.     private String b2Hex(byte[] byteArray) {  
  87.         StringBuilder sb = new StringBuilder();  
  88.         for(int i = 0; i < byteArray.length; i++ ) {  
  89.             int zhz = byteArray[i];  
  90.             if(zhz < 0) {  
  91.                 zhz += 256;  
  92.             }  
  93.             if(zhz < 16) {  
  94.                 sb.append("0");  
  95.             }  
  96.             sb.append(Integer.toHexString(zhz));  
  97.         }  
  98.         return sb.toString();  
  99.     }  
  100. }  
前台引入RSA.js,BigInt.js和Barrett.js并采用如下方法加密:

[html]  view plain  copy
  1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>  
  2. <%@page import="com.jb.test.RsaInitUtil"%>  
  3. <%  
  4.       
  5.     RsaInitUtil rsa = RsaInitUtil.getInstance();  
  6.     String my = rsa.getPublicKeyStr();  
  7.     String exp = my.substring(0,6);  
  8.     String mou = my.substring(6);  
  9. %>  
  10. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  11. <html>  
  12.   <head>  
  13.     <title>RSA测试</title>  
  14.     <script type="text/javascript" src="RSA.js"></script>  
  15.     <script type="text/javascript" src="BigInt.js"></script>  
  16.     <script type="text/javascript" src="Barrett.js"></script>  
  17.   </head>  
  18.     
  19.   <body>  
  20.   </body>  
  21. </html>  
  22. <script type="text/javascript">  
  23.   
  24.     var m = '<%=mou%>';  
  25.     var e = '<%=exp%>';  
  26.       
  27.     var key = '';  
  28.     setMaxDigits(128);  
  29.     alert(e);  
  30.     key = new RSAKeyPair(e,'',m);  
  31.     var res = encryptedString(key,encodeURIComponent('测试数据'));  
  32.       
  33.     window.location.href = 'rsaDecTry.do?res=' + res;  
  34.       
  35. </script>  
后台解密算法为:



[html]  view plain  copy
  1. package com.jb.test;  
  2.   
  3. import java.net.URLDecoder;  
  4. import java.security.NoSuchAlgorithmException;  
  5.   
  6. import javax.crypto.Cipher;  
  7. import javax.crypto.NoSuchPaddingException;  
  8.   
  9. import org.apache.commons.codec.binary.Hex;  
  10. import org.bouncycastle.jce.provider.BouncyCastleProvider;  
  11. import org.springframework.stereotype.Controller;  
  12. import org.springframework.web.bind.annotation.RequestMapping;  
  13.   
  14. /**  
  15.  * Rsa加密的控制层  
  16.  * @author nmm  
  17.  *  
  18.  */  
  19. @Controller("rsaController")  
  20. public class RsaController {  
  21.       
  22.     private RsaInitUtil rsaUtil = RsaInitUtil.getInstance();  
  23.       
  24.     /**  
  25.      *   
  26.      * 功能说明:[解密方法]  
  27.      * @param res  
  28.      * 创建者:Nmm, Aug 19, 2013  
  29.      * @throws NoSuchPaddingException   
  30.      * @throws NoSuchAlgorithmException   
  31.      */  
  32.     @RequestMapping("rsaDecTry.do")  
  33.     public void decodeTry(String res) throws Exception {  
  34.         Cipher cipher = Cipher.getInstance("RSA",new BouncyCastleProvider());//必须指定此提供者  
  35.         cipher.init(Cipher.DECRYPT_MODE, rsaUtil.getPrivateKey());  
  36.         System.out.println(res);  
  37.         byte[] buff = cipher.doFinal(Hex.decodeHex(res.toCharArray()));  
  38.         //将字符串转为字符  
  39.         StringBuilder sb = new StringBuilder(new String(buff,"UTF-8"));  
  40.         //解密后的内容是倒叙的  
  41.         sb.reverse();  
  42.         //进行URL解密,主要是为了中文乱码问题  
  43.         String result = URLDecoder.decode(sb.toString(), "UTF-8");  
  44.         System.out.println(result);  
  45.           
  46.     }  
  47.       
  48. }  
至此可完成,整个加密解密过程,下面大家可以把rsa相关的内容全部整合到一个工具类中,不用想这里处理。

下面为RSA加密解密工具类:

[html]  view plain  copy
  1. package com.jb.framework.filter;  
  2.   
  3. import java.io.FileInputStream;  
  4. import java.io.FileOutputStream;  
  5. import java.io.IOException;  
  6. import java.io.ObjectInputStream;  
  7. import java.io.ObjectOutputStream;  
  8. import java.math.BigInteger;  
  9. import java.net.URLDecoder;  
  10. import java.security.KeyFactory;  
  11. import java.security.KeyPair;  
  12. import java.security.KeyPairGenerator;  
  13. import java.security.NoSuchAlgorithmException;  
  14. import java.security.PrivateKey;  
  15. import java.security.PublicKey;  
  16. import java.security.SecureRandom;  
  17. import java.security.spec.RSAPrivateKeySpec;  
  18. import java.security.spec.RSAPublicKeySpec;  
  19. import java.util.Calendar;  
  20.   
  21. import javax.crypto.Cipher;  
  22.   
  23. import org.apache.commons.codec.binary.Hex;  
  24. import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey;  
  25. import org.bouncycastle.jce.provider.BouncyCastleProvider;  
  26.   
  27. /**  
  28.  *   
  29.  * @Package: com.jb.framework.filter<br>  
  30.  * @ClassName: RSAUtil<br>  
  31.  * @Description: RSA加密工具类,这里我们是每次系统启动时声称一套公钥,私钥,因此不能将加密串存入数据库中,如果要这么做可以预先生成密钥队写入到文件中<br>  
  32.  */  
  33. public class RSAUtil {  
  34.       
  35.     private RSAUtil(){}  
  36.       
  37.     public static final String keyPubFile = "rsaPubKey.bin";  
  38.     public static final String keyPriFile = "rsaPriKey.bin";  
  39.       
  40.     private static RSAUtil rsa;  
  41.     //密钥生成器  
  42.     private  PublicKey publicKey;  
  43.     //密钥队  
  44.     private  PrivateKey privateKey;   
  45.       
  46.       
  47.     public static RSAUtil getInstance(){  
  48.         synchronized ("rsa") {  
  49.             if(rsa == null) {  
  50.                 rsa = new RSAUtil();  
  51.                 rsa.init();  
  52.             }  
  53.         }  
  54.         return rsa;  
  55.     }  
  56.     /**  
  57.      *   
  58.      * @Title: init  
  59.      * @Description: 初始化方法  
  60.      * void  
  61.      * @throws  
  62.      */  
  63.     private void init() {  
  64.         //构建RSA算法  
  65.         try {  
  66.             KeyPairGenerator kengen = KeyPairGenerator.getInstance("RSA",new BouncyCastleProvider());  
  67.             //构建随机种子  
  68.             SecureRandom random = new SecureRandom();  
  69.             random.setSeed(Calendar.getInstance().getTimeInMillis());  
  70.             kengen.initialize(512, random);//采用512位加密  
  71.             KeyPair keyPair = kengen.generateKeyPair();  
  72.             publicKey = keyPair.getPublic();  
  73.             privateKey = keyPair.getPrivate();  
  74.         } catch (NoSuchAlgorithmException e) {  
  75.             e.printStackTrace();  
  76.         }  
  77.     }  
  78.     /**  
  79.      *   
  80.      * @Title: getPublicKey  
  81.      * @Description: 获取公钥  
  82.      * @return  
  83.      * PublicKey  
  84.      * @throws  
  85.      */  
  86.     public PublicKey getPublicKey(){  
  87.         return this.publicKey;  
  88.     }  
  89.     /**  
  90.      *   
  91.      * @Title: getPrivateKey  
  92.      * @Description: 获取私钥  
  93.      * @return  
  94.      * PrivateKey  
  95.      * @throws  
  96.      */  
  97.     public PrivateKey getPrivateKey(){  
  98.         return this.privateKey;  
  99.     }  
  100.     /**  
  101.      *   
  102.      * @Title: getPublicKeyStr  
  103.      * @Description: 获取系统公钥字符串,前6位为exponentk,后面为modlus  
  104.      * @return  
  105.      * String  
  106.      * @throws  
  107.      */  
  108.     public String getPublicKeyStr(){  
  109.         BCRSAPublicKey pk = (BCRSAPublicKey) getPublicKey();  
  110.         String pubStr = "";  
  111.         pubStr += b2hex(pk.getPublicExponent().toByteArray());  
  112.         pubStr += b2hex(pk.getModulus().toByteArray());  
  113.         return pubStr;  
  114.     }  
  115.     /**  
  116.      *   
  117.      * @Title: entryText  
  118.      * @Description: 使用默认公钥进行加密  
  119.      * @param text  
  120.      * @return  
  121.      * String  
  122.      * @throws  
  123.      */  
  124.     public String encryText(String text) {  
  125.         return encryText(text,getPublicKey());  
  126.     }  
  127.     /**  
  128.      *   
  129.      * @Title: entryText  
  130.      * @Description: 使用指定公钥进行加密,解决长字符串加密  
  131.      * @param text  
  132.      * @param publicKey2  
  133.      * @return  
  134.      * String  
  135.      * @throws  
  136.      */  
  137.     public String encryText(String text, PublicKey pk) {  
  138.         try {  
  139.             Cipher cipher = Cipher.getInstance("RSA",new BouncyCastleProvider());  
  140.             cipher.init(Cipher.ENCRYPT_MODE, pk);  
  141.             int block = cipher.getBlockSize();//获取最大加密块  
  142.             int j = 0;  
  143.             StringBuilder sb = new StringBuilder();  
  144.             byte[] targetData = text.getBytes("UTF-8");  
  145.             while (targetData.length - j*block > 0) {  
  146.                 byte[] jmdata = cipher.doFinal(targetData,j*block,Math.min(targetData.length - j*block, block));  
  147.                 sb.append(b2hex(jmdata));  
  148.                 j++;  
  149.             }  
  150.             return sb.toString();  
  151.         } catch (Exception e) {  
  152.             e.printStackTrace();  
  153.         }  
  154.         return null;  
  155.     }  
  156.     /**  
  157.      *   
  158.      * @Title: decryText  
  159.      * @Description: 使用默认的私钥进行解密解密算法  
  160.      * @param text  
  161.      * @return  
  162.      * String  
  163.      * @throws  
  164.      */  
  165.     public String decryText(String text) {  
  166.         return decryText(text,getPrivateKey());  
  167.     }  
  168.     /**  
  169.      *   
  170.      * @Title: decryText  
  171.      * @Description: 指定私钥进行解密,增加对于大字符串的解密操作  
  172.      * @param text  
  173.      * @param privateKey2  
  174.      * @return  
  175.      * String  
  176.      * @throws  
  177.      */  
  178.     public String decryText(String text, PrivateKey pk) {  
  179.         try {  
  180.             Cipher cipher = Cipher.getInstance("RSA", new BouncyCastleProvider());  
  181.             cipher.init(Cipher.DECRYPT_MODE, pk);  
  182.             byte[] targetBuff = Hex.decodeHex(text.replace(" ", "").toCharArray());  
  183.             int block = cipher.getBlockSize();  
  184.             int j = 0;  
  185.             StringBuilder sb = new StringBuilder();  
  186.             while (targetBuff.length - j * block > 0) {  
  187.                 byte[] jmdata = cipher.doFinal(targetBuff,j*block,block);  
  188.                 sb.append(new String(jmdata,"UTF-8"));  
  189.                 j++;  
  190.             }  
  191.             return sb.toString();  
  192.               
  193.         } catch (Exception e) {  
  194.             e.printStackTrace();  
  195.         }  
  196.         return null;  
  197.     }  
  198.     /**  
  199.      *   
  200.      * @Title: decryTextByUrl  
  201.      * @Description: 解密前台传递的加密串,为防止中文乱码,前台字符串最好使用encodeURIComponent方法进行url编码  
  202.      * @param text  
  203.      * @return  
  204.      * String  
  205.      * @throws  
  206.      */  
  207.     public String decryTextByUrl(String text) {  
  208.         try {  
  209.             Cipher cipher = Cipher.getInstance("RSA", new BouncyCastleProvider());  
  210.             cipher.init(Cipher.DECRYPT_MODE, getPrivateKey());  
  211.             byte[] targetBuff = Hex.decodeHex(text.replace(" ", "").toCharArray());  
  212.             int block = cipher.getBlockSize();  
  213.             int j = 0;  
  214.             StringBuilder sb = new StringBuilder();  
  215.             while (targetBuff.length - j * block > 0) {//处理大字符串的加密解密处理  
  216.                 byte[] jmdata = cipher.doFinal(targetBuff,j*block,block);  
  217.                 sb.append(new StringBuilder(new String(jmdata,"UTF-8")).reverse());  
  218.                 j++;  
  219.             }  
  220.             String res = URLDecoder.decode(sb.toString(), "UTF-8");  
  221.             return res;  
  222.               
  223.         } catch (Exception e) {  
  224.             e.printStackTrace();  
  225.         }  
  226.         return null;  
  227.     }  
  228.     /**  
  229.      *   
  230.      * @Title: createPubKey  
  231.      * @Description: 根据指定的幂和模式生成公钥  
  232.      * @param exponent  
  233.      * @param modules  
  234.      * @return  
  235.      * PublicKey  
  236.      * @throws  
  237.      */  
  238.     public PublicKey createPubKey(byte[] exponent,byte[]modules) {  
  239.         try {  
  240.             KeyFactory keyFactory = KeyFactory.getInstance("RSA", new BouncyCastleProvider());  
  241.             RSAPublicKeySpec rsaKs = new RSAPublicKeySpec(new BigInteger(modules),new BigInteger(exponent));  
  242.             return keyFactory.generatePublic(rsaKs);  
  243.               
  244.         } catch (Exception e) {  
  245.             e.printStackTrace();  
  246.         }  
  247.           
  248.         return null;  
  249.     }  
  250.     /**  
  251.      *   
  252.      * @Title: createPubKey  
  253.      * @Description: 根据指定的幂和模式生成公钥  
  254.      * @param exponent  
  255.      * @param modules  
  256.      * @return  
  257.      * PublicKey  
  258.      * @throws  
  259.      */  
  260.     public PrivateKey createPriKey(byte[] exponent,byte[]modules) {  
  261.         try {  
  262.             KeyFactory keyFactory = KeyFactory.getInstance("RSA", new BouncyCastleProvider());  
  263.             RSAPrivateKeySpec rsaKs = new RSAPrivateKeySpec(new BigInteger(modules),new BigInteger(exponent));  
  264.             return keyFactory.generatePrivate(rsaKs);  
  265.               
  266.         } catch (Exception e) {  
  267.             e.printStackTrace();  
  268.         }  
  269.         return null;  
  270.     }  
  271.     /**  
  272.      *   
  273.      * @Title: saveKeyToFile  
  274.      * @Description: 保存公钥和私钥到文件中  
  275.      * void  
  276.      * @throws  
  277.      */  
  278.     public void saveKeyToFile() {  
  279.         PublicKey pk = getPublicKey();  
  280.         PrivateKey prik = getPrivateKey();  
  281.           
  282.         String path = RSAUtil.class.getClassLoader().getResource("").getPath();  
  283.         ObjectOutputStream outPub = null;  
  284.         ObjectOutputStream outPri = null;  
  285.         try {  
  286.             System.out.println(path + keyPubFile);  
  287.             outPub = new ObjectOutputStream(new FileOutputStream(path + keyPubFile));  
  288.             outPri = new ObjectOutputStream(new FileOutputStream(path + keyPriFile));  
  289.             outPub.writeObject(pk);  
  290.             outPri.writeObject(prik);  
  291.         } catch (Exception e) {  
  292.             e.printStackTrace();  
  293.         }finally {  
  294.             try {  
  295.                 outPub.close();  
  296.                 outPri.close();  
  297.             } catch (IOException e) {  
  298.                 e.printStackTrace();  
  299.             }  
  300.               
  301.         }  
  302.     }  
  303.     /**  
  304.      *   
  305.      * @Title: readKey  
  306.      * @Description: 读取密钥  
  307.      * @param isPub  
  308.      * @return  
  309.      * Object  
  310.      * @throws  
  311.      */  
  312.     public Object readKey(boolean isPub) {  
  313.         String path = RSAUtil.class.getClassLoader().getResource("").getPath();  
  314.         ObjectInputStream in = null;  
  315.         try {  
  316.             if(isPub) {  
  317.                 path += keyPubFile;  
  318.                 in = new ObjectInputStream(new FileInputStream(path));  
  319.                 PublicKey pk = (PublicKey) in.readObject();  
  320.                 return pk;  
  321.             }else {  
  322.                 path += keyPriFile;  
  323.                 in = new ObjectInputStream(new FileInputStream(path));  
  324.                 PrivateKey pk = (PrivateKey) in.readObject();  
  325.                 return pk;  
  326.             }  
  327.         } catch (Exception e) {  
  328.             e.printStackTrace();  
  329.         }finally {  
  330.             try {  
  331.                 in.close();  
  332.             } catch (IOException e) {  
  333.                 e.printStackTrace();  
  334.             }  
  335.         }  
  336.         return null;  
  337.     }  
  338.       
  339.       
  340.     /**  
  341.      *   
  342.      * @Title: b2hex  
  343.      * @Description: 将二进制转为16进制字符串  
  344.      * @param buff  
  345.      * @return  
  346.      * String  
  347.      * @throws  
  348.      */  
  349.     public String b2hex(byte[] buff) {  
  350.         StringBuilder sb = new StringBuilder();  
  351.         for(int i = 0; i < buff.length; i++) {  
  352.             int z = buff[i];  
  353.             if(z < 0) {  
  354.                 z+= 256;  
  355.             }  
  356.             if(z < 16) {  
  357.                 sb.append("0");  
  358.             }  
  359.             sb.append(Integer.toHexString(z));  
  360.         }  
  361.         return sb.toString();  
  362.     }  
  363. }  


http://download.csdn.net/detail/niemingming/5977395

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值