Java enc解密 encrypt java
2023-05-18 09:10:47
1.简介
加密方法分为对称加密和非对称加密,区别在于对称加密只有一个密钥,而非对称加密有两个密钥
- 对称加解密:enccrypt(明文,密钥)=密文,decrypt(密文,密钥)=明文
- 非对称加解密:encrypt(明文,公钥)=密文,decrypt(密文,私钥)=明文
.der密钥以二进制编码,在JAVA中需要以流的形式读取密钥。以下是获取公钥的例子:
/** * der公钥解密,密文Base64输出 */ public static PublicKey getPublicKeyByDer(InputStream is) throws Exception { try { if (certificatefactory == null) { certificatefactory = CertificateFactory.getInstance("X.509"); } Certificate cert = certificatefactory.generateCertificate(is); return cert.getPublicKey(); }finally { if (is != null) { is.close(); } } }
2.1.2.pem.pem以"-----BEGIN..."开头, "-----END...最后,内容是BASE64编码。以下是获取公钥的例子:
public static PublicKey getPublicKeyByPem(String publicKey) throws Exception { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); X509EncodKeySpec keySpec = new X509EncodKeySpec(Base64.getDecoder().decode(publicKey)); return keyFactory.generatePublic(keySpec); }
2.1.3.PKCS12.简单地说,P12PKCS#12证书包括证书和私钥,并允许密码保护。 以下是获取私钥的例子:
public static Key getP12Key() throws Exception{ try (InputStream is = new FileInputStream( "xx.p12")) { if (certificatefactory == null) { certificatefactory = CertificateFactory.getInstance("X.509"); } KeyStore keystore = KeyStore.getInstance(PKCS12); keystore.load(is, “p12证书密码”.toCharArray()); Key key = keystore.getKey(p12证书的别名”, “p12证书密码”.toCharArray()); key.getAlgorithm();//密钥对应的加密算法,可以用在 Cipher.getInstance(key.getAlgorithm()) return key; } }
2.1.4.纯字符串字符串密钥也可以是Base64编码或16进制编码,甚至直接是字符串,找到将密钥转换为byte[]的方法来生成密钥。
可以这样获得RSA密钥:
public static PublicKey getRSAPublicKey(byte[] publicKey) throws Exception { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); X509EncodKeySpec keySpec = new X509EncodKeySpec(publicKey); return keyFactory.generatePublic(keySpec); }
AES密钥可以这样获得:
public static Key getKey(byte[] key) throws Exception { return new SecretKeySpec(key, "AES"); }
2.2.类型- X509EncodedKey
- PKCS8EncodedKey
- RSAPublicKey
- RSAPrivateKey
- DSAPublicKey
- DSAPrivateKey
- 128bit
- 256bit JAVA对AES的密钥长度有限制,最多不超过128bit,即16位字符串。如果是256bit的密钥,即32位字符串,可以通过修改JRE文件或使用CryptoJSAES来加密解密。
- PBKDF2Withhmachachachacha1PBKDF2(Password-Based Key Derivation Function)它是一种用于导出密钥的函数,常用于生成加密密码。其基本原理是通过伪随机函数(如HMAC函数)将明文和盐值作为输入参数,然后重复操作,最终产生密钥。若重复次数足够大,破解成本就会变高。增加盐值也会增加“彩虹表”攻击的难度。
以下是PBKDF2Withmacsha1JAVA的实现
private static final String DEFAULT_ALGORITHM = PBKDF2Withmacsha1; private static final int DEFAULT_ITERATIONS = 10000; private static final int DEFAULT_HASH_SIZE = 128; private static final byte[] DEFAULT_SALT = new byte[]{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; public static byte[] hash(String key) throws Exception { char[] password = key.toCharArray(); return hash(password, DEFAULT_ALGORITHM, DEFAULT_ITERATIONS, DEFAULT_HASH_SIZE, DEFAULT_SALT); } /** * @param password 密钥 * @param algorithm 运算算法 * @param iteration 重复计算的次数 * @param hashSize 密钥的长度预期 * @param salt 盐值 * @return 最后生成的密钥 * @throws Exception 各种异常 */ public static byte[] hash(char[] password, String algorithm, int iteration, int hashSize, byte[] salt) throws Exception { SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm); PBEKeySpec spec = new PBEKeySpec(password, salt, iteration, hashSize); return skf.generateSecret(spec).getEncoded(); }
3.加密算法3.1.对称3.1.1.DESDES是对称加密中常见的一种,全称Data Encryption Standard,也就是说,数据加密标准是一种使用密钥加密的块算法。64位密钥长度(bit),超位数密钥被忽略。所谓对称加密,加密和解密钥是一样的。对称加密通常根据固定长度将待加密字符串分成块。不到一整块,或者最后有一个特殊的填充字符。DES加密解密通常是跨语言的,经常会出现问题。通常是填充方式错误,或编码不一致,或选择加密解密模式(ECB,CBC,CTR,OFB,CFB,NCFB,NOFB)没有相应的原因。常见的填充方式有: "pkcs5","pkcs7","iso10126","ansix923"zero' 类型,包括DES-ECB,DES-CBC,DES-CTR,DES-OFB,DES-CFB
public static byte[] desEncrypt(byte[] data, byte[] key) throws Exception { Cipher cipher = Cipher.getInstance("DES"); SecretKeySpec secretKeySpec = new SecretKeySpec(key, "DES"); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); return cipher.doFinal(data); }
3.1.2.3DES3DES(也叫Triplees) DES)是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)块密码的通称。它相当于在每个数据块中使用三次DES加密算法。密钥长度为128,192(bit),若密码位数小于或等于64位,则加密结果与DES相同。原DES容易破解,新的3DES出现,增加了加密安全性,避免了暴力破解。它也是对称加密,也涉及加密编码和填充。包括3DES-ECB,3DES-CBC,3DES-CTR,3DES-OFB,3DES-CFB
public static byte[] desedeEncrypt(byte[] data, byte[] key) throws Exception { Cipher cipher = Cipher.getInstance("DESede"); SecretKeySpec secretKeySpec = new SecretKeySpec(key, "DESede"); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); return cipher.doFinal(data); }
3.1.3. AESAES,高级加密标准(英语:Advanced Encryption Standard,缩写:AES),Rijndael加密法,又称Rijndael加密法,是美国联邦政府采用的区块加密标准。该标准用于替代原始DES,已被世界各地广泛分析和使用。严格地说,AES和Rijndael加密方法并不完全相同(尽管它们可以在实际应用中交换),因为Rijndael加密方法可以支持更大范围的块和密钥长度:AES块长固定为128 密钥长度可为128、192或256,而Rijndael使用的密钥和区块长度可为32位的整数倍,以128位为下限,256比特为上限。包括AES-ECB,AES-CBC,AES-CTR,AES-OFB,AES-CFB
public static byte[] aesEncrypt(byte[] data, byte[] key) throws Exception { Cipher cipher = Cipher.getInstance("AES"); SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); return cipher.doFinal(data); }
AES的算法模式和填充有以下选择,其中CBC需要偏移量,而ECB不需要偏移量。NoPadding是不填充的。此时,需要手动确保加密或解密的字节数为16倍;PKCS5Padding与PKCS7Padding几乎相同。它可以是通用的。填充的内容是填充字节序列的长度。例如,如果Byte数组的长度为15,则需要填充1位才能达到16,则填充0x1。如果Byte数组的长度为1,150xf需要填充;ZeroPadding用0x0填充。
Cipher.getInstance("AES/CBC/NoPadding");Cipher.getInstance("AES/CBC/PKCS5Padding");Cipher.getInstance("AES/CBC/PKCS7Padding");Cipher.getInstance("AES/CBC/ZeroBytePadding");Cipher.getInstance("AES/ECB/NoPadding");Cipher.getInstance("AES/ECB/PKCS5Padding");Cipher.getInstance("AES/ECB/PKCS7Padding");Cipher.getInstance("AES/ECB/ZeroBytePadding");
此外,AES的密钥长度分别为128byte、192byte、256byte对应的字符串长度分别为16位、24位和32位。当密钥大于128byte,即16位字符串长度时,代码会抛出java.security.InvalidKeyException: Illegal key size or default parameters。Illegal key size or default parameters是指密钥长度有限,java运行时环境读取有限的policy文件。文件位于${java_home}/jre/lib/security,这一限制是由于美国对软件出口的控制。有两种解决方案
- 替换${java_home}/jre/lib/security/ 下面的local_policy.jar和US_export_policy.jar。- 使用CryptoJs,这是谷歌开源的AES加解密库,用Js实现,速度和本地JDK差不多,除了第一次加载Js文件外,还需要额外的时间。
3.1.4.RC4RC4加密算法是RSA三人组的头号人物Ron 1987年Rivest设计的密钥长度可变流加密算法簇。该算法的速度可以达到DES加密的10倍左右,并且具有高水平的非线性。其算法于1994年9月在互联网上发布。由于RC4算法采用xor加密,一旦子密钥序列重复,密文可能会被破解。RC4作为一种旧的验证和加密算法,很容易被黑客攻击,现在逐渐不推荐使用。
public static byte[] rc4Encrypt(byte[] data, byte[] key) throws Exception { Cipher cipher = Cipher.getInstance(RC4); SecretKeySpec secretKeySpec = new SecretKeySpec(key, "ARCFOUR"); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); return cipher.doFinal(data); }
3.2.非对称3.2.1. RSA加解密钥不一致,一般私钥不公开,使用公钥加密,私钥解密,使用私钥加密,公钥可以解密。与AES不同,每次加密的结果都是不同的。需要进行分组加密,RSA加密每组最多117个byte,每组最多128个byte。
public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) throws Exception { Cipher cipher = Cipher.getInstance("RSA"); KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); X509EncodKeySpec keySpec = new X509EncodKeySpec(publicKey); PublicKey key = keyFactory.generatePublic(keySpec); cipher.init(Cipher.ENCRYPT_MODE, key); //执行分组加密操作,每组RSA加密最多长度为117 int segment = 117; int inputLen = data.length; try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { int offSet = 0; byte[] cache; int i = 0; // 加密数据分段 while (inputLen - offSet > 0) { if (inputLen - offSet > segment) { cache = cipher.doFinal(data, offSet, segment); } else { cache = cipher.doFinal(data, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * segment; } return out.toByteArray(); } } public static byte[] encryptByPrivateKey(byte[] data, byte[] publicKey) throws Exception { Cipher cipher = Cipher.getInstance("RSA"); KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); X509EncodKeySpec keySpec = new X509EncodKeySpec(publicKey); PrivateKey key = keyFactory.generatePrivate(keySpec); cipher.init(Cipher.ENCRYPT_MODE, key); //执行分组加密操作,每组RSA加密最多长度为117 int segment = 117; int inputLen = data.length; try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { int offSet = 0; byte[] cache; int i = 0; // 加密数据分段 while (inputLen - offSet > 0) { if (inputLen - offSet > segment) { cache = cipher.doFinal(data, offSet, segment); } else { cache = cipher.doFinal(data, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * segment; } return out.toByteArray(); } }
3.3.补充额外算法添加BouncyCastle的依赖性,在程序初始化时调用一次
Security.addProvider(new BouncyCastleProvider());
可以在Cipher.getInstance在方法中获得更多的加密算法。
4.签名4.1.算法4.1.1.SHA1withRSA/** * RSA签名 * * @param content 待签名数据 * @param privateKey 商户私钥 * @param encode 字符集编码 * @return 使用Base64编码的签名值 */ public static String sign(String content, String privateKey, String encode) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, UnsupportedEncodingException, SignatureException { PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decode(privateKey)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); java.security.Signature signature = java.security.Signature.getInstance(SHA256withRSA); signature.initSign(priKey); signature.update(content.getBytes()); byte[] signed = signature.sign(); return Base64.encode(signed); }
4.1.2. 其他通用私钥签名/** * 底层操作私钥签名 * * @param algorithm NONEwithRSA * MD2withRSA,MD5withRSA * SHA1withRSA,SHA256withRSA ,SHA384withRSA ,SHA512withRSA * NONEwithDSA * SHA1withDSA * NONEwithECDSA ,SHA1withECDSA ,SHA256withECDSA ,SHA384withECDSA ,SHA512withECDSA * @param data byte[] * @param privateKey PrivateKey * @return byte[] */ public static byte[] sign(String algorithm, byte[] data, PrivateKey privateKey) throws Exception { Signature signature = Signature.getInstance(algorithm); signature.initSign(privateKey); signature.update(data); return signature.sign(); }
5.散列- SHA1
- SHA256
- SHA384
- SHA512
- MD2
- MD5
apache-commons-所有codec的Digestutils都实现了。
本文是转载内容,我们尊重原作者对文章的权利。如有内容错误或侵权行为,请联系我们更正或删除文章。