大数据开发成长之路——Java基础(六)

数据安全

数据的安全传输要从以下几方面入手

  • 防窃听
  • 防篡改
  • 防伪造

现代计算机加密技术已经成为一门学科,基于数学理论,很难!这部分可以做了解

URL编码

  • 类似于utf8等字符集编码规则
  • URL编码规则:
    在这里插入图片描述
  • URL编码是编码算法,为了便于浏览器和服务器处理!不是加密算法

Base64编码

  • 把二进制数据用文本表示的编码算法,适用于文本协议,例如电子邮件协议
  • 例如将“中”进行Base64编码:
    在这里插入图片描述
import java.util.Base64;

public class SecBase64 {
	public static void main(String[] args) throws Exception {
		String original = "Hello\u00ff编码测试";
		String b64 = Base64.getEncoder().encodeToString(original.getBytes("UTF-8"));
		System.out.println(b64);// 如何要转成适合URL传输的文本,需要使用getUrlEncoder()
		String ori = new String(Base64.getDecoder().decode(b64), "UTF-8");
		System.out.println(ori);
	}
}
  • Base64是编码算法,这种算法会降低效率;

MD5加密算法

  • 是一种摘要算法,摘要算法也称为哈希算法/Hash/Digest/数字指纹
  • 特点:任意长度的数据输入,固定长度的数据输出
  • 目的:验证原始数据是否被篡改
  • 碰撞:不同的输入得到相同的输出,因为将任意长度映射到固定长度,碰撞是不可避免的
  • 衡量Hash的安全性:
    在这里插入图片描述
  • 常见的Digest
    在这里插入图片描述
  • 使用md5加密算法要注意彩虹表攻击
    在这里插入图片描述
import java.math.BigInteger;
import java.security.MessageDigest;

public class MD5Salt {
	public static byte[] toMD5(byte[] input) {
		MessageDigest md;
		try {
			md = MessageDigest.getInstance("MD5");// SHA-1	SHA-256
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		md.update(input);
		return md.digest();
	}

	public static void main(String[] args) throws Exception {
		String passwd = "helloworld";
		String salt = "Random salt";
		byte[] r = MD5Salt.toMD5((salt + passwd).getBytes("UTF-8"));// 只接受字节类型,需要转化
		System.out.println(String.format("%032x", new BigInteger(1, r)));// 转成16进制显示
	}
}

Bouncy Castle

  • 适用于Java的轻量级加密API
  • Java加密扩展(JCE)和Java加密体系结构(JCA)的提供程序
  • Java安全套接字扩展(JSSE)的提供程序等
  • 可以进行动态和静态配置
import java.security.MessageDigest;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;

public class Digest {

	public static byte[] digest(String hashAlgorithm, byte[] input) {
		MessageDigest md;
		try {
			md = MessageDigest.getInstance(hashAlgorithm);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		md.update(input);
		return md.digest();
	}
	public static void main(String[] args) throws Exception {
		// 把BouncyCastle作为Provider添加到java.security:
		Security.addProvider(new BouncyCastleProvider());// 动态导入jar包到工程,这里注册
		String s = "Java摘要算法测试";
		byte[] input = s.getBytes("UTF-8");
		byte[] r1 = digest("MD5", input);
		System.out.println(r1.length + ": " + ByteUtils.toHexString(r1));
		byte[] r2 = digest("SHA-1", input);
		System.out.println(r2.length + ": " + ByteUtils.toHexString(r2));
		byte[] r3 = digest("SHA-256", input);
		System.out.println(r3.length + ": " + ByteUtils.toHexString(r3));
		byte[] r4 = digest("RipeMD160", input);	// JDK没有的算法,有BouncyCastle提供
		System.out.println(r4.length + ": " + ByteUtils.toHexString(r4));
	}
}

Hmac

  • 把key混入摘要的算法
  • 可以配合MD5、SHA-1等摘要算法,长度和原摘要算法相同
  • 可以理解为加入salt的MD5
import java.math.BigInteger;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;

public class Hmac {

	public static byte[] hmac(String hmacAlgorithm, SecretKey skey, byte[] input) throws Exception {
		Mac mac = Mac.getInstance(hmacAlgorithm);
		mac.init(skey);
		mac.update(input);
		return mac.doFinal();
	}
	public static void main(String[] args) throws Exception {
		// http://docs.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html#Mac
		String algorithm = "HmacSHA1";
		// 原始数据:
		String data = "helloworld";
		// 随机生成一个Key:
		KeyGenerator keyGen = KeyGenerator.getInstance(algorithm);
		SecretKey skey = keyGen.generateKey();
		// 打印Key:
		byte[] key = skey.getEncoded();
		System.out.println(String.format("Key: %0" + (key.length * 2) + "x", new BigInteger(1, key)));
		// 用这个Key计算HmacSHA1:
		byte[] result = hmac(algorithm, skey, data.getBytes("UTF-8"));
		System.out.println(String.format("Hash: %0" + (result.length * 2) + "x", new BigInteger(1, result)));
	}
}

对称加密算法

  • 加密密钥和解密密钥相同,大部分算法加密解密过程互逆
  • AES_CBC算法:
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AES_CBC_Cipher {
	// 指明算法名称/工作模式/填充模式
	static final String CIPHER_NAME = "AES/CBC/PKCS5Padding";

	// 加密:
	public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
		Cipher cipher = Cipher.getInstance(CIPHER_NAME);
		SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
		// CBC模式需要生成一个16 bytes的initialization vector:
		SecureRandom sr = SecureRandom.getInstanceStrong();
		byte[] iv = sr.generateSeed(16);
		IvParameterSpec ivps = new IvParameterSpec(iv);
		cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivps);
		byte[] data = cipher.doFinal(input);
		// IV不需要保密,把IV和密文一起返回:
		return join(iv, data);
	}

	// 解密:
	public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
		// 把input分割成IV和密文:
		byte[] iv = new byte[16];
		byte[] data = new byte[input.length - 16];
		System.arraycopy(input, 0, iv, 0, 16);
		System.arraycopy(input, 16, data, 0, data.length);
		// 解密:
		Cipher cipher = Cipher.getInstance(CIPHER_NAME);
		SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
		IvParameterSpec ivps = new IvParameterSpec(iv);
		cipher.init(Cipher.DECRYPT_MODE, keySpec, ivps);
		return cipher.doFinal(data);
	}

	public static byte[] join(byte[] bs1, byte[] bs2) {
		byte[] r = new byte[bs1.length + bs2.length];
		System.arraycopy(bs1, 0, r, 0, bs1.length);
		System.arraycopy(bs2, 0, r, bs1.length, bs2.length);
		return r;
	}

	public static void main(String[] args) throws Exception {
		// 原文:
		String message = "Hello, world! encrypted using AES!";
		System.out.println("Message: " + message);
		// 128位密钥 = 16 bytes Key:
		byte[] key = "1234567890abcdef".getBytes("UTF-8");
		// 加密:
		byte[] data = message.getBytes(StandardCharsets.UTF_8);
		byte[] encrypted = encrypt(key, data);
		System.out.println("Encrypted data: " + Base64.getEncoder().encodeToString(encrypted));
		// 解密:
		byte[] decrypted = decrypt(key, encrypted);
		System.out.println("Decrypted data: " + new String(decrypted, "UTF-8"));
	}
}
  • 使用AES256_ECB算法,由于Oracle收到相关法律限制,需要从官网下载jar包替换JDK的policy文件,否则不能进行256及以上长度密钥的加密!

密钥交换算法

  • 如何在不安全的信道上安全传送密钥?
  • 原理是各自生成公钥私钥,只传递公钥,互相组合得到相同的密钥!
    在这里插入图片描述
import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;

class Person {

	public final String name;

	public PublicKey publicKey;
	private PrivateKey privateKey;
	private SecretKey secretKey;

	public Person(String name) {
		this.name = name;
	}

	// 生成本地KeyPair:
	public void generateKeyPair() {
		try {
			KeyPairGenerator kpGen = KeyPairGenerator.getInstance("DH");// DH算法的KeyPair
			kpGen.initialize(512);
			KeyPair kp = kpGen.generateKeyPair();
			this.privateKey = kp.getPrivate();
			this.publicKey = kp.getPublic();
		} catch (GeneralSecurityException e) {
			throw new RuntimeException(e);
		}
	}

	public void generateSecretKey(byte[] receivedPubKeyBytes) {
		try {
			// 从byte[]恢复PublicKey:
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(receivedPubKeyBytes);
			KeyFactory kf = KeyFactory.getInstance("DH");
			PublicKey receivedPublicKey = kf.generatePublic(keySpec);
			// 生成本地密钥:
			KeyAgreement keyAgreement = KeyAgreement.getInstance("DH");
			keyAgreement.init(this.privateKey); // 自己的PrivateKey
			keyAgreement.doPhase(receivedPublicKey, true); // 对方的PublicKey
			// 生成AES密钥:
			this.secretKey = keyAgreement.generateSecret("AES");
		} catch (GeneralSecurityException e) {
			throw new RuntimeException(e);
		}
	}

	public void printKeys() {
		System.out.printf("Name: %s\n", this.name);
		System.out.printf("Private key: %x\n", new BigInteger(1, this.privateKey.getEncoded()));
		System.out.printf("Public key: %x\n", new BigInteger(1, this.publicKey.getEncoded()));
		System.out.printf("Secret key: %x\n", new BigInteger(1, this.secretKey.getEncoded()));
	}

	// 发送加密消息:
	public String sendMessage(String message) {
		try {
			Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
			cipher.init(Cipher.ENCRYPT_MODE, this.secretKey);
			byte[] data = cipher.doFinal(message.getBytes("UTF-8"));
			return Base64.getEncoder().encodeToString(data);
		} catch (GeneralSecurityException | IOException e) {
			throw new RuntimeException(e);
		}
	}

	// 接收加密消息并解密:
	public String receiveMessage(String message) {
		try {
			Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
			cipher.init(Cipher.DECRYPT_MODE, this.secretKey);
			byte[] data = cipher.doFinal(Base64.getDecoder().decode(message));
			return new String(data, "UTF-8");
		} catch (GeneralSecurityException | IOException e) {
			throw new RuntimeException(e);
		}
	}

}

public class DH {

	public static void main(String[] args) {
		// Bob和Alice:
		Person bob = new Person("Bob");
		Person alice = new Person("Alice");

		// 各自生成KeyPair:
		bob.generateKeyPair();
		alice.generateKeyPair();

		// 双方交换各自的PublicKey:
		// Bob根据Alice的PublicKey生成自己的本地密钥:
		bob.generateSecretKey(alice.publicKey.getEncoded());
		// Alice根据Bob的PublicKey生成自己的本地密钥:
		alice.generateSecretKey(bob.publicKey.getEncoded());

		// 检查双方的本地密钥是否相同:
		bob.printKeys();
		alice.printKeys();

		// 双方的SecretKey相同,后续通信将使用SecretKey作为密钥进行AES加解密:
		String msgBobToAlice = bob.sendMessage("Hello, Alice!");
		System.out.println("Bob -> Alice: " + msgBobToAlice);
		String aliceDecrypted = alice.receiveMessage(msgBobToAlice);
		System.out.println("Alice decrypted: " + aliceDecrypted);
	}
}

在这里插入图片描述

  • 当然,有加密就有破解,如上图所示,如果有中间人假冒,还是会被窃取;

非对称加密算法

  • 加密和解密使用不同的密钥
  • 只有同一个公钥私钥对才能正常加密/解密
  • 优点:
    在这里插入图片描述

RSA签名算法

  • 发送方用自己的私钥对消息进行签名
  • 接收方用发送方的公钥检验签名是否有效(只有用私钥的签名才能有效)
  • 保证了数据在传输过程中没有被修改,防止伪造的发送方
    在这里插入图片描述
import java.security.GeneralSecurityException;
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.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class SecRSASignature {

	PrivateKey sk;
	PublicKey pk;

	public SecRSASignature() throws GeneralSecurityException {// 还是要获得密钥对
		// generate key pair:
		KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
		kpGen.initialize(1024);
		KeyPair kp = kpGen.generateKeyPair();
		this.sk = kp.getPrivate();
		this.pk = kp.getPublic();
	}

	public SecRSASignature(byte[] pk, byte[] sk) throws GeneralSecurityException {
		// create from bytes:
		KeyFactory kf = KeyFactory.getInstance("RSA");
		X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(pk);
		this.pk = kf.generatePublic(pkSpec);
		PKCS8EncodedKeySpec skSpec = new PKCS8EncodedKeySpec(sk);
		this.sk = kf.generatePrivate(skSpec);
	}

	public byte[] getPrivateKey() {
		return this.sk.getEncoded();
	}

	public byte[] getPublicKey() {
		return this.pk.getEncoded();
	}

	public byte[] sign(byte[] message) throws GeneralSecurityException {
		// sign by sk:
		Signature signature = Signature.getInstance("SHA1withRSA");// MD5withRSA SHA256withRSA
		signature.initSign(this.sk);
		signature.update(message);
		return signature.sign();
	}

	public boolean verify(byte[] message, byte[] sign) throws GeneralSecurityException {
		// verify by pk:
		Signature signature = Signature.getInstance("SHA1withRSA");
		signature.initVerify(this.pk);
		signature.update(message);
		return signature.verify(sign);
	}

	public static void main(String[] args) throws Exception {
		byte[] message = "Hello,使用SHA1withRSA算法进行数字签名!".getBytes("UTF-8");
		SecRSASignature rsas = new SecRSASignature();
		byte[] sign = rsas.sign(message);
		System.out.println("sign: " + Base64.getEncoder().encodeToString(sign));
		boolean verified = rsas.verify(message, sign);
		System.out.println("verify: " + verified);
		// 用另一个公钥验证:
		boolean verified2 = new SecRSASignature().verify(message, sign);
		System.out.println("verify with another public key: " + verified2);
		// 修改原始信息:
		message[0] = 100;
		boolean verified3 = rsas.verify(message, sign);
		System.out.println("verify changed message: " + verified3);
	}
}
  • 类似的,还有DSA签名算法

数字证书

  • 摘要算法保证了数据没被篡改(平时也可加盐作为数据加密)
  • 非对称加密算法实现了对数据的加密解密
  • 签名算法确保了数据安全传输(完整性、抗否认性)
  • 数字证书就是集合了多种密码学算法,用于实现数据加解密、身份认证、签名等多种功能的网络安全标准
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.Base64;

import javax.crypto.Cipher;

public class X509 {
	private final PrivateKey privateKey;
	public final X509Certificate certificate;

	public X509(KeyStore ks, String certName, String password) {
		try {
			this.privateKey = (PrivateKey) ks.getKey(certName, password.toCharArray());
			this.certificate = (X509Certificate) ks.getCertificate(certName);
		} catch (GeneralSecurityException e) {
			throw new RuntimeException(e);
		}
	}
	public byte[] encrypt(byte[] message) {
		try {
			Cipher cipher = Cipher.getInstance(this.privateKey.getAlgorithm());
			cipher.init(Cipher.ENCRYPT_MODE, this.privateKey);
			return cipher.doFinal(message);
		} catch (GeneralSecurityException e) {
			throw new RuntimeException(e);
		}
	}
	public byte[] decrypt(byte[] data) {
		try {
			PublicKey publicKey = this.certificate.getPublicKey();
			Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
			cipher.init(Cipher.DECRYPT_MODE, publicKey);
			return cipher.doFinal(data);
		} catch (GeneralSecurityException e) {
			throw new RuntimeException(e);
		}
	}
	public byte[] sign(byte[] message) {
		try {
			Signature signature = Signature.getInstance(this.certificate.getSigAlgName());
			signature.initSign(this.privateKey);
			signature.update(message);
			return signature.sign();
		} catch (GeneralSecurityException e) {
			throw new RuntimeException(e);
		}
	}
	public boolean verify(byte[] message, byte[] sig) {
		try {
			Signature signature = Signature.getInstance(this.certificate.getSigAlgName());
			signature.initVerify(this.certificate);
			signature.update(message);
			return signature.verify(sig);
		} catch (GeneralSecurityException e) {
			throw new RuntimeException(e);
		}
	}
	static KeyStore loadKeyStore(String keyStoreFile, String password) {
		try (InputStream input = new BufferedInputStream(new FileInputStream(keyStoreFile))) {
			KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
			ks.load(input, password.toCharArray());
			return ks;
		} catch (GeneralSecurityException | IOException e) {
			throw new RuntimeException(e);
		}
	}
	public static void main(String[] args) throws Exception {
		byte[] message = "Hello,使用X.509证书进行加密和签名!".getBytes("UTF-8");
		// 读取KeyStore:
		KeyStore ks = loadKeyStore("my.keystore", "456789");// 根据命令行命令生成证书,证书密码
		// 读取证书:
		X509 x509 = new X509(ks, "mycert", "123456");
		// 加密:
		byte[] encrypted = x509.encrypt(message);
		System.out.println("encrypted: " + Base64.getEncoder().encodeToString(encrypted));
		// 解密:
		byte[] decrypted = x509.decrypt(encrypted);
		System.out.println("decrypted: " + new String(decrypted, "UTF-8"));
		// 签名:
		byte[] sign = x509.sign(message);
		System.out.println("sign: " + Base64.getEncoder().encodeToString(sign));
		// 验证签名:
		boolean verified = x509.verify(message, sign);
		System.out.println("verify: " + verified);
	}
}
  • 生成证书命令:(命令行下项目目录执行)
// 如果要应用于网站,域名需保证一致
keytool -genkeypair -keyalg RSA -keysize 1024 -sigalg SHA1withRSA -validity 36500 -alias mycert -keystore my.keystore -dname "CN=www.sample.com, OU=sample, O=sample, L=BJ, ST=BJ, C=CN" -keypass 123456 -storepass 456789
// 查看证书命令
keytool -list -keystore my.keystore -storepass 456789

小结

安全问题是网络通信的重难点,如果能到研究这一步,算是学有所成了吧!
在这里插入图片描述

发布了11 篇原创文章 · 获赞 26 · 访问量 4297

猜你喜欢

转载自blog.csdn.net/weixin_39757637/article/details/105147894