公司最近需要使用非对称加密算法进行数据加密,本来打算采用RSA的,但是CTO强制使用ECC加密算法;没办法,硬着头皮整吧!
网上的千篇一律,写法都差不多,但是有一个问题,公钥和私钥永远不会发生变法,这就比较尴尬了,而且数学学的又不是特比好,真的是很尴尬。
不过后面还是搞出来了,这里直接上代码吧!
ECC定义的常量,方便调用
package com.ktnw.utils.ecc;
public enum ECCEnum {
ALGORITHM("EC"),
PROVIDER("BC"),
PUBLIC_KEY("PUBLIC_KEY"),
PRIVATE_KEY("PRIVATE_KEY");
private String value;
ECCEnum(String value) {
this.value = value;
}
public String value() {
return this.value;
}
}
ECC加密、解密封装
package com.ktnw.utils.ecc;
import com.ktnw.utils.encrypt.BASE64Decoder;
import javax.crypto.Cipher;
import javax.crypto.NullCipher;
import java.io.Serializable;
import java.security.KeyFactory;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* ecc 加密/解密
*/
public class ECCUtil implements Serializable {
/**
* 加密
* @param data
* @param publicKey
* @return
* @throws Exception
*/
public static byte[] encrypt(byte[] data, String publicKey)
throws Exception {
byte[] keyBytes = BASE64Decoder.decodeBuffer(publicKey);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(ECCEnum.ALGORITHM.value());
ECPublicKey pubKey = (ECPublicKey) keyFactory
.generatePublic(x509KeySpec);
Cipher cipher = new NullCipher();
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return cipher.doFinal(data);
}
/**
* 解密
* @param data
* @param privateKey
* @return
* @throws Exception
*/
public static byte[] decrypt(byte[] data, String privateKey) throws Exception {
byte[] keyBytes = BASE64Decoder.decodeBuffer(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(ECCEnum.ALGORITHM.value());
ECPrivateKey priKey = (ECPrivateKey) keyFactory
.generatePrivate(pkcs8KeySpec);
Cipher cipher = new NullCipher();
cipher.init(Cipher.DECRYPT_MODE, priKey);
return cipher.doFinal(data);
}
}
ECC 公钥、私钥生成器
package com.ktnw.utils.ecc;
import com.ktnw.utils.encrypt.BASE64Encoder;
import org.bouncycastle.jce.interfaces.ECPrivateKey;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import java.io.Serializable;
import java.security.*;
import java.util.HashMap;
import java.util.Map;
public class GenerateKey implements Serializable {
static {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
}
public static Map getGenerateKey() throws NoSuchProviderException, NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ECCEnum.ALGORITHM.value(),
ECCEnum.PROVIDER.value());
keyPairGenerator.initialize(256, new SecureRandom());
KeyPair kp = keyPairGenerator.generateKeyPair();
ECPublicKey publicKey = (ECPublicKey) kp.getPublic();
ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate();
Map map = new HashMap<>();
map.put(ECCEnum.PRIVATE_KEY.value(), BASE64Encoder.encodeBuffer(privateKey.getEncoded()));
map.put(ECCEnum.PUBLIC_KEY.value(), BASE64Encoder.encodeBuffer(publicKey.getEncoded()));
return map;
}
}
到这里ECC 公钥、私钥生成、加密、解密、就写完了,下面在附带两个Base64的转码工具类
package com.ktnw.utils.encrypt;
import java.io.*;
public class BASE64Decoder extends FilterInputStream {
private static final char[] chars = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', '+', '/'
};
// A mapping between char values and six-bit integers
private static final int[] ints = new int[128];
static {
for (int i = 0; i < 64; i++) {
ints[chars[i]] = i;
}
}
private int charCount;
private int carryOver;
public BASE64Decoder(InputStream in) {
super(in);
}
public int read() throws IOException {
// Read the next non-whitespace character
int x;
do {
x = in.read();
if (x == -1) {
return -1;
}
} while (Character.isWhitespace((char)x));
charCount++;
// The '=' sign is just padding
if (x == '=') {
return -1; // effective end of stream
}
// Convert from raw form to 6-bit form
x = ints[x];
// Calculate which character we're decoding now
int mode = (charCount - 1) % 4;
// First char save all six bits, go for another
if (mode == 0) {
carryOver = x & 63;
return read();
}
// Second char use previous six bits and first two new bits,
// save last four bits
else if (mode == 1) {
int decoded = ((carryOver << 2) + (x >> 4)) & 255;
carryOver = x & 15;
return decoded;
}
// Third char use previous four bits and first four new bits,
// save last two bits
else if (mode == 2) {
int decoded = ((carryOver << 4) + (x >> 2)) & 255;
carryOver = x & 3;
return decoded;
}
// Fourth char use previous two bits and all six new bits
else if (mode == 3) {
int decoded = ((carryOver << 6) + x) & 255;
return decoded;
}
return -1; // can't actually reach this line
}
public int read(byte[] buf, int off, int len) throws IOException {
if (buf.length < (len + off - 1)) {
throw new IOException("The input buffer is too small: " + len +
" bytes requested starting at offset " + off + " while the buffer " +
" is only " + buf.length + " bytes long.");
}
// This could of course be optimized
int i;
for (i = 0; i < len; i++) {
int x = read();
if (x == -1 && i == 0) { // an immediate -1 returns -1
return -1;
}
else if (x == -1) { // a later -1 returns the chars read so far
break;
}
buf[off + i] = (byte) x;
}
return i;
}
public static String decode(String encoded) {
return new String(decodeToBytes(encoded));
}
public static byte[] decodeToBytes(String encoded) {
byte[] bytes = null;
try {
bytes = encoded.getBytes("8859_1");
}
catch (UnsupportedEncodingException ignored) { }
BASE64Decoder in = new BASE64Decoder(
new ByteArrayInputStream(bytes));
ByteArrayOutputStream out =
new ByteArrayOutputStream((int) (bytes.length * 0.67));
try {
byte[] buf = new byte[4 * 1024]; // 4K buffer
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) {
out.write(buf, 0, bytesRead);
}
out.close();
return out.toByteArray();
}
catch (IOException ignored) { return null; }
}
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Usage: java Base64Decoder fileToDecode");
return;
}
BASE64Decoder decoder = null;
try {
decoder = new BASE64Decoder(
new BufferedInputStream(
new FileInputStream(args[0])));
byte[] buf = new byte[4 * 1024]; // 4K buffer
int bytesRead;
while ((bytesRead = decoder.read(buf)) != -1) {
System.out.write(buf, 0, bytesRead);
}
}
finally {
if (decoder != null) decoder.close();
}
}
public static byte[] decodeBuffer(String key) {
return decodeToBytes(key);
}
}
package com.ktnw.utils.encrypt;
public class BASE64Encoder {
private static final char last2byte = (char) Integer.parseInt("00000011", 2);
private static final char last4byte = (char) Integer.parseInt("00001111", 2);
private static final char last6byte = (char) Integer.parseInt("00111111", 2);
private static final char lead6byte = (char) Integer.parseInt("11111100", 2);
private static final char lead4byte = (char) Integer.parseInt("11110000", 2);
private static final char lead2byte = (char) Integer.parseInt("11000000", 2);
private static final char[] encodeTable = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
private BASE64Encoder() {
}
public static String encode(byte[] from) {
StringBuffer to = new StringBuffer((int) (from.length * 1.34) + 3);
int num = 0;
char currentByte = 0;
for (int i = 0; i < from.length; i++) {
num = num % 8;
while (num < 8) {
switch (num) {
case 0:
currentByte = (char) (from[i] & lead6byte);
currentByte = (char) (currentByte >>> 2);
break;
case 2:
currentByte = (char) (from[i] & last6byte);
break;
case 4:
currentByte = (char) (from[i] & last4byte);
currentByte = (char) (currentByte << 2);
if ((i + 1) < from.length) {
currentByte |= (from[i + 1] & lead2byte) >>> 6;
}
break;
case 6:
currentByte = (char) (from[i] & last2byte);
currentByte = (char) (currentByte << 4);
if ((i + 1) < from.length) {
currentByte |= (from[i + 1] & lead4byte) >>> 4;
}
break;
}
to.append(encodeTable[currentByte]);
num += 6;
}
}
if (to.length() % 4 != 0) {
for (int i = 4 - to.length() % 4; i > 0; i--) {
to.append("=");
}
}
return to.toString();
}
public static String encodeBuffer(byte[] key) {
return encode(key);
}
}
下面是调用
package com.ktnw.utils.ecc;
import com.ktnw.utils.encrypt.BASE64Encoder;
import java.util.Map;
public class ECTest {
public static void main(String[] argu) throws Exception {
Map map = GenerateKey.getGenerateKey();
String privKey = map.get(ECCEnum.PRIVATE_KEY.value());
String pubKey = map.get(ECCEnum.PUBLIC_KEY.value());
System.out.println("私钥:" + privKey);
System.out.println("公钥:" + pubKey);
String text = "java ECC 加密、解密算法,如果写的有问题,请大家踊跃评论,谢谢!";
byte [] b = ECCUtil.encrypt(text.getBytes("UTF-8"),pubKey);
String str = BASE64Encoder.encodeBuffer(b);
System.out.println("密文:" + str);
String outputStr = new String(ECCUtil.decrypt(b,privKey));
System.out.println("原始文本:" + text);
System.out.println("解密文本:" + outputStr);
}
}
下面贴出我的调用结果
私钥:MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgNknUYyf2PduHcM9VVJDPS8e+GJ/2oiZR5IikDn6TKg2gCgYIKoZIzj0DAQehRANCAATGrYE98aVKYkPh/2aUsXUb39Ar/sp33kN/rnlvjP3jXnfQkJzXhkJRcZMvD3LfDVrD6F8BShPuHJA/7NWY7Xkv
公钥:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExq2BPfGlSmJD4f9mlLF1G9/QK/7Kd95Df655b4z941530JCc14ZCUXGTLw9y3w1aw+hfAUoT7hyQP+zVmO15Lw==
密文:amF2YSBFQ0Mg5Yqg5a+G44CB6Kej5a+G566X5rOV77yM5aaC5p6c5YaZ55qE5pyJ6Zeu6aKY77yM6K+35aSn5a626LiK6LeD6K+E6K6677yM6LCi6LCi77yB
原始文本:java ECC 加密、解密算法,如果写的有问题,请大家踊跃评论,谢谢!
解密文本:java ECC 加密、解密算法,如果写的有问题,请大家踊跃评论,谢谢!
如果大家有什么看不懂的,可以参考这篇文章
http://www.voidcn.com/article/p-xziirffi-bbr.html