• 密码学和Java加密与解密技术


    密码学大概像中国的历史发展一样,可分为古典密码学、近代密码学、现代密码学。

    一、古典密码学

    古典密码主要是替换密码和移位密码。核心原理也就是替换法、移位法。

    替换法

    替换法也有两种,单表替换和多表替换。
    单表替换即原文和密文用的同一张表
    如表:j替换u,a替换q,v替换为f
    所以明文java替换后变密文uqfq。
    也有多表替换,即原文和密文用的是多张表
    如:表 abcdef---swtrpv、abcdef---chfhkd、abcdef---jftoud
    秘钥为 312
    表示分别替换第三张表、第一张表、第二张表
    则明文的abc替换后为密文sht。

    移位法

    移位法就是将原文所有字母在字母表上按照一个固定数组进行前后移位,得出密文。
    如:明文abcde变为密文cdefg,就是在字母表里每个字母分别往后移动两位。
    典型的移位法应用有"恺撒加密"。

    java 代码实现"恺撒加密"

    1. package javaweb.com.demo;
    2. public class Encryption {
    3. public static void main(String[] args) throws Exception {
    4. String a = "cseroad";
    5. int key = 3;
    6. char[] chars = a.toCharArray();
    7. //字符串变成字节数组
    8. StringBuilder sb = new StringBuilder();
    9. for (char c : chars) {
    10. int ascii = c;
    11. ascii = ascii + key;
    12. //向右移动三位后的ascii
    13. char newa = (char)ascii;
    14. //System.out.println(newa);
    15. sb.append(newa);
    16. }
    17. System.out.println("密文:"+sb.toString());
    18. //转化为String对象
    19. }
    20. }

    近代密码学

    恩尼格玛机,本质上使用的还是替换和移位。后被图灵破解。

    二、现代密码学

    ASCII编码

    我们知道在计算机中,存储数据都是用二进制表示的(即0和1)。那么像我们平时用到的字母、数字也是需要用二进制表示。于是有了ASCII编码表来统一进行换算。

    java实现求字符对应的ASCII编码:

    1. package javaweb.com.demo;
    2. public class Encryption {
    3. public static void main(String[] args) throws Exception {
    4. char a = 'a';
    5. int b = a;
    6. System.out.println(b);
    7. }
    8. }

    即char字符类型自动转化为int类型。
    字符a的ASCII码为97,存储到计算机的二进制为0110 0001。

    Unicode编码

    那么汉字、日文、韩文又是怎么在计算机中存储的呢?
    这就有了Unicode编码表,也被称为万国码。为每种语言每个字符设置了统一并唯一的二进制编码。包含ASCII码,从0到127的字符。如上面的a的ASCII码为97,十六进制为61,则unicode编码为0x0061。该编码一般用十六进制表示。

    1. package javaweb.com.demo;
    2. public class Encryption {
    3. public static void main(String[] args) throws Exception {
    4. char a = '经';
    5. int b = a;
    6. System.out.println(b);
    7. }
    8. }

    汉字"经"的十六进制为0x7ecf,转化十进制就是32463,即输出值。
    但是char只能存储单一的字符。如果遇到字符串呢?

    java实现求字符串对应的ASCII编码:

    1. package javaweb.com.demo;
    2. public class Encryption {
    3. public static void main(String[] args) throws Exception {
    4. String a = "cseroad";
    5. char[] chars = a.toCharArray();
    6. //字符串转化为字节数组,然后遍历字符
    7. for (char c : chars) {
    8. int ascii = c;
    9. System.out.println(ascii);
    10. }
    11. }
    12. }

    需要补充的是,UTF-8是Unicode实现的方式之一。两者之间也是通过程序进行转换的。
    转换规则参考 字符编码笔记:ASCII,Unicode 和 UTF-8

    二进制位

    不管是什么编码方式,运行在计算机里只能是二进制数。

    java 实现求某字符串二进制数:

    1. package javaweb.com.demo;
    2. public class Encryption {
    3. public static void main(String[] args) throws Exception {
    4. String a = "经";
    5. byte[] bytes = a.getBytes("UTF-8");
    6. for (byte c : bytes) {
    7. int ascii = c;
    8. String s = Integer.toBinaryString(ascii);
    9. System.out.println("二进制值:"+s);
    10. }
    11. }
    12. }

    UTF-8编码格式下,一个中文等于3个字节,一个字节等于8个二进制位。
    GBK 编码格式下,一个中文等于2个字节,一个字节等于8个二进制位。

    char和byte区别

    char

    • 字符数据类型,占两个字节,无符号
    • 范围在0-65535之间
    • 不可以表示负数,但是可以表示中文

    byte

    • 字节数据类型,有符号
    • 范围在-128-127之间
    • 可以和 ASCII 码相互转换,不可以表示中文
    • 一字节等于8个二进制位

    三、编码方式

    URL编码

    url编码指的是浏览器发送到服务器时使用的编码,用来便于浏览器和服务器进行通信。
    url编码就是一个字符ascii码的十六进制,每个字节前面添加%。

    java 实现url编码:

    1. package javaweb.com.demo;
    2. import java.net.URLDecoder;
    3. import java.net.URLEncoder;
    4. public class Encryption {
    5. public static void main(String[] args) throws Exception {
    6. String old = "url 编码";
    7. String encode = URLEncoder.encode(old,"UTF-8");
    8. System.out.println(encode);
    9. String decode = URLDecoder.decode(encode,"UTF-8");
    10. System.out.println(decode);
    11. }
    12. }

    Base64编码

    base64编码是一种基于64个字符把二进制数据用文本表示的编码算法,方便传输,提高可读性。
    分别是A-Z、a-z、0-9、两个字符+和/,正好64个字符。

    原理
    每3个8位二进制转化为4个6位二进制数,然后在每个6位二进制数都填两个高位0,得到4个8位二进制数,如果缺位则补0。再将其分别转化为十进制,最后通过base64编码表获取编码后字符。

    如:

    字符串"中国" 的utf-8编码对应的十六进制为e4b8ad、e59bbd,转化二进制为111001001011100010101101、111001011001101110111101,然后每6个二进制位为1组。

    1. 111001
    2. 001011
    3. 100010
    4. 101101
    5. 111001
    6. 011001
    7. 101110
    8. 111101

    再分别填补两个高位0。

    1. 00111001
    2. 00001011
    3. 00100010
    4. 00101101
    5. 00111001
    6. 00011001
    7. 00101110
    8. 00111101

    再转化十进制为57、11、34、45、57、25、46、61,按照base64编码表转为5Lit5Zu9。

    java 实现base64编码:

    1. package javaweb.com.demo;
    2. import java.util.Base64;
    3. public class Encryption {
    4. public static void main(String[] args) throws Exception {
    5. String old = "中国";
    6. //encode
    7. String bs64 = Base64.getEncoder().encodeToString(old.getBytes("UTF-8"));
    8. System.out.println(bs64);
    9. //decode
    10. String ori = new String(Base64.getDecoder().decode(bs64),"UTF-8");
    11. System.out.println(ori);
    12. }
    13. }

    四、消息摘要算法

    消息摘要算法也叫散列算法、哈希函数、单向函数。是一种将任意长度输入转换为固定长度输出的算法。

    主要作用不是用于加密与解密,而是用于验证信息的完整性。常用于密码加密、文件校验。
    消息摘要算法主要分为三类,MD(消息摘要算法)、SHA(安全散列算法)、MAC(消息认证码算法)

    常见的消息摘要算法有MD5、SHA-1、SHA-256、SHA-512等等。

    MD5

    md5的长度默认为128个二进制位,这样表达是很不友好的,所以将二进制转成了16进制,每4个二进制位表示一个16进制,所以就变为32位了。

    1. package javaweb.com.demo;
    2. import java.security.MessageDigest;
    3. public class Encryption {
    4. public static void main(String[] args) throws Exception {
    5. String input = "cseroad";
    6. //明文
    7. String algorithm = "MD5";
    8. //加密类型
    9. MessageDigest md = MessageDigest.getInstance(algorithm);
    10. //初始化
    11. md.update(input.getBytes("UTF-8"));
    12. //更新摘要
    13. byte[] r = md.digest();
    14. //获得密文完成哈希计算
    15. StringBuilder sb = new StringBuilder();
    16. for (byte b : r) {
    17. String s = Integer.toHexString(b & 0xff);
    18. //密文10进制转16进制
    19. if (s.length() == 1) {
    20. s = "0"+s;
    21. }
    22. //判断如果密文长度为1,高位需要补0
    23. sb.append(s);
    24. }
    25. System.out.println(sb.toString());
    26. }
    27. }

     

    要点

    使用MessageDigest类用于为应用程序提供信息摘要算法的功能。要实现SHA-1、SHA-256、SHA-512 替换algorithm变量即可。

    也可以通过BigInteger类处理任意精度的整数运算,这样代码比较简洁。

    java 实现MD5加密:

    1. package javaweb.com.demo;
    2. import java.math.BigInteger;
    3. import java.security.MessageDigest;
    4. public class Encryption {
    5. public static void main(String[] args) throws Exception {
    6. String input = "cseroad";
    7. String algorithm = "MD5";
    8. MessageDigest md = MessageDigest.getInstance(algorithm);
    9. md.update(input.getBytes("UTF-8"));
    10. byte[] r = md.digest();
    11. BigInteger a = new BigInteger(1,r);
    12. //转化为16进制
    13. System.out.println(String.format("%032x",a));
    14. }
    15. }

    SHA-1

    java实现SHA-1加密:

    1. package javaweb.com.demo;
    2. import java.math.BigInteger;
    3. import java.security.MessageDigest;
    4. public class Encryption {
    5. public static void main(String[] args) throws Exception {
    6. String input = "cseroad";
    7. String algorithm = "SHA-1";
    8. MessageDigest md = MessageDigest.getInstance(algorithm);
    9. md.update(input.getBytes("UTF-8"));
    10. byte[] r = md.digest();
    11. BigInteger a = new BigInteger(1,r);
    12. //转化为16进制
    13. System.out.println(String.format("%040x",a));
    14. }
    15. }

    SHA-1的返回值时160位二进制位,同样和MD5一样,每四个二进制位换算为一个16进制,所以结果为40位。

    盐salt

    在密码学中,是指通过在任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这种过程称之为"加盐"。
    注意加盐的目的是为了增加攻击者破解的难度,所以盐值不能太短,也不能使用固定盐值。

    五、位运算加密

    位运算指的是内存中二进制为的操作。

    1. 与运算 & 0&0=0,0&1=0,1&0=0,1&1=1
    2. 异或运算 ^ 同为假,异为真。0&0=0,0&1=1,1&0=1,1&1=0
    3. 非运算 ~ 取反运算,1变0,0变1
    4. 或运算 | 0&0=0,0&1=1,1&0=1,1&1=1

    举个例子:

    1. 99 ^ 2 = 97
    2. 转化二进制位进行异或运算
    3. 01100011^00000010 = 01100001

    java实现异或运算:

    1. package javaweb.com.demo;
    2. public class Encryption {
    3. public static void main(String[] args) throws Exception {
    4. int len,i;
    5. char[] newstr;
    6. char[] str = {'a','b','c'};//定义char类型数组
    7. char n = 2;//定义秘钥
    8. len = str.length;
    9. newstr = new char[len];//定义数组
    10. for(i=0;i
    11. newstr[i]=(char) (str[i]^n);
    12. //异或运算后转char类型
    13. }
    14. System.out.println(newstr);
    15. }
    16. }

    转化为char类型直接和密码进行运算。

    java实现位运算加解密:

    1. package javaweb.com.demo;
    2. public class Encryption {
    3. public static void main(String[] args) throws Exception {
    4. String strs = "cseroad";
    5. int key = 10;
    6. //加密
    7. char[] newchr = strs.toCharArray();
    8. for (int i = 0; i < newchr.length; i++) {
    9. newchr[i] = (char) (newchr[i]^key);
    10. }
    11. System.out.println("加密结果如下:"+new String(newchr));
    12. //解密
    13. StringBuilder sb = new StringBuilder();
    14. for (int i = 0; i < newchr.length; i++) {
    15. char oldchr = (char) (newchr[i]^key);
    16. sb.append(oldchr);
    17. }
    18. System.out.println(sb);
    19. }
    20. }

    怎么位运算加密的,就怎么位运算解密。

    六、对称加密

    对称密码体制分为两种:
    一种对明文字节进行运算,称为流加密,也叫序列加密;
    一种把明文信息划分不同的组或块后再进行加密,称为分组加密。
    如123456789,流加密就是先对1加密,再对2加密,往后依次,最后拼成密文。
    块加密是把该数字分成不同的块,如123成块,456成块,789成块,再对不同的块加密,最后形成密文。
    DES、AES都是常见的对称加密算法,且属于分组加密。
    分组加密总共有五种工作模式:电子密码本模式ECB、密文链接模式CBC、密文反馈模式CFB、输出反馈模式OFB、计数器模式CTR。

    AES加密

    先看代码,java 实现AES加解密:

    1. package javaweb.com.demo;
    2. import java.util.Base64;
    3. import javax.crypto.Cipher;
    4. import javax.crypto.spec.SecretKeySpec;
    5. public class Encryption {
    6. public static void main(String[] args) throws Exception {
    7. String input = "cseroad";
    8. //原文
    9. String key = "1234567890abcdef";
    10. //秘钥
    11. String transformation = "AES";
    12. //AES算法
    13. String algorithm = "AES";
    14. //加密类型
    15. //加密方法
    16. String encryptdes = encryptDES(input,key,transformation,algorithm);
    17. System.out.println("AES加密:"+encryptdes);
    18. //解密方法
    19. String decryptdes = decryptDES(encryptdes,key,transformation,algorithm);
    20. System.out.println("AES解密:"+decryptdes);
    21. }
    22. private static String encryptDES(String input,String key,String transformation,String algorithm) throws Exception {
    23. Cipher cipher = Cipher.getInstance(transformation);
    24. //创建加密对象
    25. SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
    26. //创建加密规则。第一个参数key秘钥,第二个参数加密的类型
    27. cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
    28. //加密初始化。第一个参数表示加密模式,第二个参数表示加密规则
    29. byte[] bytes = cipher.doFinal(input.getBytes());
    30. //原文字节数组进行加密
    31. String bs64 = Base64.getEncoder().encodeToString(bytes);
    32. //直接打印出现乱码,采用base64编码后输出
    33. return bs64;
    34. //打印密文
    35. }
    36. private static String decryptDES(String encryptdes, String key, String transformation, String algorithm) throws Exception {
    37. Cipher cipher = Cipher.getInstance(transformation);
    38. SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
    39. cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
    40. byte[] old = Base64.getDecoder().decode(encryptdes);
    41. byte[] bytes = cipher.doFinal(old);
    42. return new String(bytes);
    43. }
    44. }

    要点

    使用Cipher这个类实现对AES、DES的加解密。

    AES的秘钥key长度必须是16byte。

    算法transformation变量这里是AES,其完整格式应该为AES/ECB/PKCS5Padding,也是默认值

    第一个参数加密算法:

    • AES,DES,DESede(DES3)和 RSA 四种

    第二个参数工作模式,就是上面介绍的那五种。这里再说一下经常使用的ECB、CBC工作模式:

    • ECB 电子密码本,需要加密的消息按照块密码的块大小分成数块,并对块独立加密。
    • CBC 密码块链接,每个明文块先与前一个密文块进行异或后,再进行加密。

    第三个参数填充模式:

    • Nopadding 不填充,默认值
    • PKCS5Padding 填充,加密内容不足8位就填充。

    如果要使用CBC加密模式,需要定义一个IvParameterSpec对象,向量模式也可以简单理解为偏移量。

    代码如下:

    1. package javaweb.com.demo;
    2. import java.util.Base64;
    3. import javax.crypto.Cipher;
    4. import javax.crypto.spec.IvParameterSpec;
    5. import javax.crypto.spec.SecretKeySpec;
    6. public class Encryption {
    7. public static void main(String[] args) throws Exception {
    8. String input = "cseroad";
    9. //原文
    10. String key = "1234567890abcdef";
    11. //秘钥
    12. String transformation = "AES/CBC/PKCS5Padding";
    13. //AES算法
    14. String algorithm = "AES";
    15. //加密类型
    16. //加密方法
    17. String encryptdes = encryptDES(input,key,transformation,algorithm);
    18. System.out.println("AES加密:"+encryptdes);
    19. //解密方法
    20. String decryptdes = decryptDES(encryptdes,key,transformation,algorithm);
    21. System.out.println("AES解密:"+decryptdes);
    22. }
    23. private static String encryptDES(String input,String key,String transformation,String algorithm) throws Exception {
    24. Cipher cipher = Cipher.getInstance(transformation);
    25. //创建加密对象
    26. SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
    27. //创建加密规则。第一个参数key字节,第二个参数加密的类型
    28. IvParameterSpec iv = new IvParameterSpec("9876543210qwerty".getBytes());
    29. //创建IV向量
    30. cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec,iv);
    31. //加密初始化。第一个参数表示加密模式,第二个参数表示加密规则
    32. byte[] bytes = cipher.doFinal(input.getBytes("UTF-8"));
    33. //原文字节数组进行加密
    34. String bs64 = Base64.getEncoder().encodeToString(bytes);
    35. //直接打印出现乱码,采用base64编码后输出
    36. return bs64;
    37. //打印密文
    38. }
    39. private static String decryptDES(String encryptdes, String key, String transformation, String algorithm) throws Exception {
    40. Cipher cipher = Cipher.getInstance(transformation);
    41. SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
    42. IvParameterSpec iv = new IvParameterSpec("9876543210qwerty".getBytes());
    43. cipher.init(Cipher.DECRYPT_MODE, secretKeySpec,iv);
    44. byte[] old = Base64.getDecoder().decode(encryptdes);
    45. byte[] bytes = cipher.doFinal(old);
    46. return new String(bytes);
    47. }
    48. }

    要点

    iv向量长度必须16个字节。

    DES 加密

    DES加密和上面的AES大同小异,直接替换AES就可以。需要注意的是DES的秘钥key必须是8位byte。

    七、非对称加密

    非对称加密中,加密解密使用不同的秘钥。对外公开的秘钥为公钥,对外保密的秘钥为私钥。
    公钥加密,私钥解密;私钥加密,公钥解密。

    常见的非对称加密方式:RSA、DSA、ECC。

    RSA 加密

    使用KeyPairGenerator类创建密码对。

    1. package javaweb.com.demo;
    2. import java.security.KeyPair;
    3. import java.security.KeyPairGenerator;
    4. import java.security.PrivateKey;
    5. import java.security.PublicKey;
    6. import java.util.Base64;
    7. import javax.crypto.Cipher;
    8. public class Encryption {
    9. public static void main(String[] args) throws Exception {
    10. //创建秘钥对
    11. String algorithm = "RSA";
    12. KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
    13. //生成秘钥对
    14. KeyPair keyPair = keyPairGenerator.generateKeyPair();
    15. //生成私钥
    16. PrivateKey privateKey = keyPair.getPrivate();
    17. //生成公钥
    18. PublicKey publicKey = keyPair.getPublic();
    19. //获取私钥自己数组
    20. byte[] privateKeyEncode = privateKey.getEncoded();
    21. //获取公钥自己数组
    22. byte[] publicKeyEncode = publicKey.getEncoded();
    23. //base64编码
    24. String privateKeyEncodeString = Base64.getEncoder().encodeToString(privateKeyEncode);
    25. System.out.println(privateKeyEncodeString);
    26. String publicKeyEncodeString = Base64.getEncoder().encodeToString(publicKeyEncode);
    27. System.out.println(publicKeyEncodeString);
    28. }
    29. }

    base64编码输出。

    java 实现私钥加密,公钥解密:

    1. package javaweb.com.demo;
    2. import java.security.KeyPair;
    3. import java.security.KeyPairGenerator;
    4. import java.security.PrivateKey;
    5. import java.security.PublicKey;
    6. import java.util.Base64;
    7. import javax.crypto.Cipher;
    8. public class Encryption {
    9. public static void main(String[] args) throws Exception {
    10. //创建秘钥对
    11. String algorithm = "RSA";
    12. KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
    13. //生成秘钥对
    14. KeyPair keyPair = keyPairGenerator.generateKeyPair();
    15. //生成私钥
    16. PrivateKey privateKey = keyPair.getPrivate();
    17. //生成公钥
    18. PublicKey publicKey = keyPair.getPublic();
    19. //原文
    20. String input = "cseroad";
    21. //创建加密对象
    22. Cipher cipher = Cipher.getInstance(algorithm);
    23. //加密初始化,使用私钥加密
    24. cipher.init(Cipher.ENCRYPT_MODE, privateKey);
    25. byte[] bytes = cipher.doFinal(input.getBytes("UTF-8"));
    26. String bs64 = Base64.getEncoder().encodeToString(bytes);
    27. System.out.println("密文"+bs64);
    28. //解密
    29. cipher.init(Cipher.DECRYPT_MODE, publicKey);
    30. byte[] bytes1 = cipher.doFinal(bytes);
    31. System.out.println("原文"+new String(bytes1));
    32. }
    33. }

    DSA 加密

    DSA是一种更高级的验证方式,用作数字签名。不单单只有公钥、私钥,还有数字签名。私钥加密生成数字签名,公钥验证数据及签名。

    java实现DSA加密并数字签名的认证,部分代码参考github:

    1. package javaweb.com.demo;
    2. import java.security.KeyFactory;
    3. import java.security.KeyPair;
    4. import java.security.KeyPairGenerator;
    5. import java.security.Signature;
    6. import java.security.interfaces.DSAPrivateKey;
    7. import java.security.interfaces.DSAPublicKey;
    8. import java.security.spec.PKCS8EncodedKeySpec;
    9. import java.security.spec.X509EncodedKeySpec;
    10. import java.util.Base64;
    11. public class Encryption {
    12. private static String PUBLIC_KEY = "MIIBuDCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYUAAoGBAOgR1bl0s3/9tsxVU2tkq5/nfOKZ2z8jT6cLG/axihbSEOAhmXTgPTD3V3bSLtK9O2CIwvR6Ljz70Sl/LUFI0+YBIFGB/jASCJ2av/Im0SItyRDiT+n8GDo6OHc8SyTSwxPv4tVGGy52lqdHO9IqK6QpFZ00LNaPy+5IRfoZrMZh";
    13. private static String PRIVATE_KEY = "MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFgIUcwvMm8OVRd1UsTx+q/IgcIUlsns=";
    14. public static void main(String[] args) {
    15. //随机生成成对密钥
    16. genKeyPair();
    17. String sign=sign("cseroad", PRIVATE_KEY);
    18. System.out.println("数字签名:" + sign);
    19. //字符串解码
    20. System.out.println("数字签名校验:" + verify("cseroad",sign,PUBLIC_KEY));
    21. }
    22. /**
    23. * 随机生成密钥对
    24. */
    25. public static void genKeyPair() {
    26. try {
    27. // KeyPairGenerator类用于生成公钥和私钥对,基于DSA算法生成对象 可以使用RSA算法生成 成对出现就好
    28. KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("DSA");
    29. // 初始化密钥对生成器,密钥大小为96-1024位 可自定义随机产生器 SecureRandom
    30. keyPairGen.initialize(1024);
    31. // 生成一个密钥对,保存在keyPair中
    32. KeyPair keyPair = keyPairGen.generateKeyPair();
    33. // 得到私钥
    34. DSAPrivateKey privateKey = (DSAPrivateKey) keyPair.getPrivate();
    35. // 得到公钥
    36. DSAPublicKey publicKey = (DSAPublicKey) keyPair.getPublic();
    37. // 得到公钥字符串
    38. String publicKeyString = new String(Base64.getEncoder().encode(publicKey.getEncoded()));
    39. // 得到私钥字符串
    40. String privateKeyString = new String(Base64.getEncoder().encode((privateKey.getEncoded())));
    41. // 打印公钥和私钥
    42. System.out.println("公匙:" + publicKeyString);
    43. System.out.println("私匙:" + privateKeyString);
    44. } catch (Exception e) {
    45. e.printStackTrace();
    46. }
    47. }
    48. /**
    49. * 用私钥对信息生成数字签名
    50. */
    51. public static String sign(String str, String privateKey) {
    52. try {
    53. byte[] bytes = str.getBytes();
    54. //获得DSA公匙
    55. DSAPrivateKey keyFactory = (DSAPrivateKey) KeyFactory.getInstance("DSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey.getBytes())));
    56. Signature signature = Signature.getInstance("DSA");
    57. signature.initSign(keyFactory);
    58. signature.update(bytes);
    59. return new String(Base64.getEncoder().encode(signature.sign()));
    60. } catch (Exception e) {
    61. e.printStackTrace();
    62. return null;
    63. }
    64. }
    65. /**
    66. * 校验数字签名
    67. */
    68. public static boolean verify(String str, String sign,String publicKey) {
    69. try {
    70. byte[] bytes = Base64.getDecoder().decode(sign);
    71. DSAPublicKey keyFactory = (DSAPublicKey) KeyFactory.getInstance("DSA").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey.getBytes())));
    72. Signature signature=Signature.getInstance("DSA");
    73. signature.initVerify(keyFactory);
    74. signature.update(str.getBytes());
    75. return signature.verify(bytes);
    76. } catch (Exception e) {
    77. e.printStackTrace();
    78. return false;
    79. }
    80. }
    81. }

    八、数字证书

    数字证书是由权威的CA(Certificate Authority)机构颁布的一种证书。集合了多种密码学算法,用来加解密、身份认证、签名等功能。

    非对称加密:对数据加解密
    签名算法:确保数据完整性
    摘要算法:确保数据没有篡改

    我们熟知的HTTPS协议就是应用了数字证书。

    数字证书的格式遵循X.509 标准

    • 证书的发布机构
    • 证书的有效期
    • 公钥
    • 证书所有者(Subject)
    • 签名所使用的算法
    • 指纹以及指纹算法

    注意证书链的验证是从根证书开始的。

  • 相关阅读:
    电脑开机时报错No Bootable Device找不到索引的解决方法
    VI/VIM的使用
    如何与ChatGPT愉快地聊天
    使用chat-GPT接口提取合同中关键信息
    Git使用简明教程
    sd-webui-controlnet代码分析
    uniapp-时间格式和距离格式的转换
    Java开源工具库使用之Lombok
    实现外网手机或者电脑随时随地远程访问家里的电脑主机(linux为例)
    解决OpenOCD烧录STM32失败, 无法通过SWD连接的问题
  • 原文地址:https://blog.csdn.net/qq_35029061/article/details/126154143