背景
为了提高项目安全性,我们一般都会对接口传输进行加密,那么加密过程中,我们一般采用对称加密和非对称加密。其中对称加密只要双方有相同的秘钥即可完成jia、解密;而非对称加密侧需要公钥与私钥进行认证。后者安全性比前者高。考虑到项目对传输安全等级不是太高,所以选择了对称加密,但这里面有个问题,android、java后端加密是可以一致的,但是在IOS端加密出来的密文,在java后端无法破解,这就需要统一加密编码及统一指定加密格式,具体操作如下。
1.android端
(1)新建AESUtils工具类
public class AESUtils {
private static final BASE64Encoder B64_EC = new BASE64Encoder();
private static final BASE64Decoder B64_DC = new BASE64Decoder();
private static final String AES = "AES";
/**
* 注意:CRYPT_KEY必须是16位,如果修改为32位或者更高位的的将会报java.security.InvalidKeyException。
*下载对应的jar文件替换原有的:local_policy.jar和US_export_policy.jar,将其复制到JAVA_HOME下的\jre\lib\security中替换,即可。
*下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
*/
private static final String CRYPT_KEY = "ABC123,";
private static final String IV_STRING = "ABC123,";
/**
* 加密
* @param content 加密内容
* @return 密文
* @throws Exception e
*/
public static String encrypt(String content) {
byte[] encryptedBytes = new byte[0];
try {
byte[] byteContent = content.getBytes("UTF-8");
// 注意,为了能与 iOS 统一
// 这里的 key 不可以使用 KeyGenerator、SecureRandom、SecretKey 生成
byte[] enCodeFormat = CRYPT_KEY.getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, AES);
byte[] initParam = IV_STRING.getBytes();
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
// 指定加密的算法、工作模式和填充方式
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
encryptedBytes = cipher.doFinal(byteContent);
// 同样对加密后数据进行 base64 编码
}catch(Exception ex){
throw new RuntimeException("加密失败", ex);
}
//特殊字符处理:"="转换为!,"+"转换为@
return B64_EC.encode(encryptedBytes).replaceAll("=", "!")
.replace("+","@");
}
/**
* 解密
* @param content 密文
* @return 明文
* @throws Exception e
*/
public static String decrypt(String content) {
try {
byte[] encryptedBytes = B64_DC.decodeBuffer(content.replaceAll("!", "=") .replace("+","@"));
byte[] enCodeFormat = CRYPT_KEY.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, AES);
byte[] initParam = IV_STRING.getBytes();
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] result = cipher.doFinal(encryptedBytes);
return new String(result, "UTF-8");
}catch(Exception ex){
throw new RuntimeException("解密失败", ex);
}
}
public static void main(String[] args) {
String src = "abc123~!@#¥%……—*()-+=/,.{abc测试}";
src += "对于服务器接收特殊符号处理:如\"+\"号后台接收为空格,前后端需约定特殊字符处理格式";
src += " * 将密文里的\"!\"号替换为正常加密的\"+\"";
String mw = encrypt(src);
System.out.println("密文:\n" + mw);
mw = "M4ULyXAHaairNEAuxc2VwJyN0AeMtndqgiZs08fqA6+ZbkYeODFJCwEpVnqeVXBI33ibrZqzjBTT3Lohb75B8UlKk9k/Ph+emEM1H0/Xkj/Cq/1g35P9sgndIttA6Irt";
String mmw = decrypt(mw);
System.out.println("明文:" + mmw);
System.out.println(src.equals(mmw));
}
}
注:由于android端没有BASE64Encoder、BASE64Decoder编码jar包,测需重写BASE64Encoder和BASE64Decoder编码类,代码如下
(2)新建encoder文件,重写BASE64Encoder和BASE64Decoder
1.BASE64Decoder
public class BASE64Decoder extends CharacterDecoder {
/** This class has 4 bytes per atom */
protected int bytesPerAtom() {
return (4);
}
/** Any multiple of 4 will do, 72 might be common */
protected int bytesPerLine() {
return (72);
}
/**
74 * This character array provides the character to value map
75 * based on RFC1521.
76 */
private final static char pem_array[] = {
// 0 1 2 3 4 5 6 7
'A','B','C','D','E','F','G','H', // 0
'I','J','K','L','M','N','O','P', // 1
'Q','R','S','T','U','V','W','X', // 2
'Y','Z','a','b','c','d','e','f', // 3
'g','h','i','j','k','l','m','n', // 4
'o','p','q','r','s','t','u','v', // 5
'w','x','y','z','0','1','2','3', // 6
'4','5','6','7','8','9','+','/' // 7
};
private final static byte pem_convert_array[] = new byte[256];
static {
for (int i = 0; i < 255; i++) {
pem_convert_array[i] = -1;
}
for (int i = 0; i < pem_array.length; i++) {
pem_convert_array[pem_array[i]] = (byte) i;
}
}
byte decode_buffer[] = new byte[4];
/**
103 * Decode one BASE64 atom into 1, 2, or 3 bytes of data.
104 */
protected void decodeAtom(PushbackInputStream inStream, OutputStream outStream, int rem)
throws java.io.IOException
{
int i;
byte a = -1, b = -1, c = -1, d = -1;
if (rem < 2) {
throw new CEFormatException("BASE64Decoder: Not enough bytes for an atom.");
}
do {
i = inStream.read();
if (i == -1) {
throw new CEStreamExhausted();
}
} while (i == '\n' || i == '\r');
decode_buffer[0] = (byte) i;
i = readFully(inStream, decode_buffer, 1, rem-1);
if (i == -1) {
throw new CEStreamExhausted();
}
if (rem > 3 && decode_buffer[3] == '=') {
rem = 3;
}
if (rem > 2 && decode_buffer[2] == '=') {
rem = 2;
}
switch (rem) {
case 4:
d = pem_convert_array[decode_buffer[3] & 0xff];
// NOBREAK
case 3:
c = pem_convert_array[decode_buffer[2] & 0xff];
// NOBREAK
case 2:
b = pem_convert_array[decode_buffer[1] & 0xff];
a = pem_convert_array[decode_buffer[0] & 0xff];
break;
}
switch (rem) {
case 2:
outStream.write( (byte)(((a << 2) & 0xfc) | ((b >>> 4) & 3)) );
break;
case 3:
outStream.write( (byte) (((a << 2) & 0xfc) | ((b >>> 4) & 3)) );
outStream.write( (byte) (((b << 4) & 0xf0) | ((c >>> 2) & 0xf)) );
break;
case 4:
outStream.write( (byte) (((a << 2) & 0xfc) | ((b >>> 4) & 3)) );
outStream.write( (byte) (((b << 4) & 0xf0) | ((c >>> 2) & 0xf)) );
outStream.write( (byte) (((c << 6) & 0xc0) | (d & 0x3f)) );
break;
}
return;
}
}
2.BASE64Encoder
public class BASE64Encoder extends CharacterEncoder
{
/** this class encodes three bytes per atom. */
protected int bytesPerAtom()
{
return (3);
}
/**
* this class encodes 57 bytes per line. This results in a maximum of 57/3 *
* 4 or 76 characters per output line. Not counting the line termination.
*/
protected int bytesPerLine()
{
return (57);
}
/** This array maps the characters to their 6 bit values */
private final static char pem_array[] =
{
// 0 1 2 3 4 5 6 7
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5
'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6
'4', '5', '6', '7', '8', '9', '+', '/' // 7
};
/**
* encodeAtom - Take three bytes of input and encode it as 4 printable
* characters. Note that if the length in len is less than three is encodes
* either one or two '=' signs to indicate padding characters.
*/
protected void encodeAtom(OutputStream outStream, byte data[], int offset,
int len) throws IOException
{
byte a, b, c;
if (len == 1)
{
a = data[offset];
b = 0;
c = 0;
outStream.write(pem_array[(a >>> 2) & 0x3F]);
outStream.write(pem_array[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
outStream.write('=');
outStream.write('=');
} else if (len == 2)
{
a = data[offset];
b = data[offset + 1];
c = 0;
outStream.write(pem_array[(a >>> 2) & 0x3F]);
outStream.write(pem_array[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
outStream.write(pem_array[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
outStream.write('=');
} else
{
a = data[offset];
b = data[offset + 1];
c = data[offset + 2];
outStream.write(pem_array[(a >>> 2) & 0x3F]);
outStream.write(pem_array[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
outStream.write(pem_array[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
outStream.write(pem_array[c & 0x3F]);
}
}
}
3.CEFormatException
public class CEFormatException extends IOException
{
public CEFormatException(String s)
{
super(s);
}
}
4.CEStreamExhausted
public class CEStreamExhausted extends IOException
{
}
5.CharacterDecoder
public abstract class CharacterDecoder
{
/** Return the number of bytes per atom of decoding */
abstract protected int bytesPerAtom();
/** Return the maximum number of bytes that can be encoded per line */
abstract protected int bytesPerLine();
/** decode the beginning of the buffer, by default this is a NOP. */
protected void decodeBufferPrefix(PushbackInputStream aStream,
OutputStream bStream) throws IOException
{
}
/** decode the buffer suffix, again by default it is a NOP. */
protected void decodeBufferSuffix(PushbackInputStream aStream,
OutputStream bStream) throws IOException
{
}
/**
* 103 * This method should return, if it knows, the number of bytes 104 *
* that will be decoded. Many formats such as uuencoding provide 105 * this
* information. By default we return the maximum bytes that 106 * could have
* been encoded on the line. 107
*/
protected int decodeLinePrefix(PushbackInputStream aStream,
OutputStream bStream) throws IOException
{
return (bytesPerLine());
}
/**
* 113 * This method post processes the line, if there are error detection
* 114 * or correction codes in a line, they are generally processed by 115
* * this method. The simplest version of this method looks for the 116 *
* (newline) character. 117
*/
protected void decodeLineSuffix(PushbackInputStream aStream,
OutputStream bStream) throws IOException
{
}
/**
* 121 * This method does an actual decode. It takes the decoded bytes and
* 122 * writes them to the OutputStream. The integer <i>l</i> tells the 123
* * method how many bytes are required. This is always <= bytesPerAtom().
* 124
*/
protected void decodeAtom(PushbackInputStream aStream,
OutputStream bStream, int l) throws IOException
{
throw new CEStreamExhausted();
}
/**
* 130 * This method works around the bizarre semantics of
* BufferedInputStream's 131 * read method. 132
*/
protected int readFully(InputStream in, byte buffer[], int offset, int len)
throws IOException
{
for (int i = 0; i < len; i++)
{
int q = in.read();
if (q == -1)
return ((i == 0) ? -1 : i);
buffer[i + offset] = (byte) q;
}
return len;
}
/**
* 145 * Decode the text from the InputStream and write the decoded 146 *
* octets to the OutputStream. This method runs until the stream 147 * is
* exhausted. 148 * @exception CEFormatException An error has occured while
* decoding 149 * @exception CEStreamExhausted The input stream is
* unexpectedly out of data 150
*/
public void decodeBuffer(InputStream aStream, OutputStream bStream)
throws IOException
{
int i;
int totalBytes = 0;
PushbackInputStream ps = new PushbackInputStream(aStream);
decodeBufferPrefix(ps, bStream);
while (true)
{
int length;
try
{
length = decodeLinePrefix(ps, bStream);
for (i = 0; (i + bytesPerAtom()) < length; i += bytesPerAtom())
{
decodeAtom(ps, bStream, bytesPerAtom());
totalBytes += bytesPerAtom();
}
if ((i + bytesPerAtom()) == length)
{
decodeAtom(ps, bStream, bytesPerAtom());
totalBytes += bytesPerAtom();
} else
{
decodeAtom(ps, bStream, length - i);
totalBytes += (length - i);
}
decodeLineSuffix(ps, bStream);
} catch (CEStreamExhausted e)
{
break;
}
}
decodeBufferSuffix(ps, bStream);
}
/**
* 182 * Alternate decode interface that takes a String containing the
* encoded 183 * buffer and returns a byte array containing the data. 184 * @exception
* CEFormatException An error has occured while decoding 185
*/
public byte decodeBuffer(String inputString)[] throws IOException
{
byte inputBuffer[] = new byte[inputString.length()];
ByteArrayInputStream inStream;
ByteArrayOutputStream outStream;
inputString.getBytes(0, inputString.length(), inputBuffer, 0);
inStream = new ByteArrayInputStream(inputBuffer);
outStream = new ByteArrayOutputStream();
decodeBuffer(inStream, outStream);
return (outStream.toByteArray());
}
/**
* 199 * Decode the contents of the inputstream into a buffer. 200
*/
public byte decodeBuffer(InputStream in)[] throws IOException
{
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
decodeBuffer(in, outStream);
return (outStream.toByteArray());
}
/**
* 208 * Decode the contents of the String into a ByteBuffer. 209
*/
public ByteBuffer decodeBufferToByteBuffer(String inputString)
throws IOException
{
return ByteBuffer.wrap(decodeBuffer(inputString));
}
/**
* 216 * Decode the contents of the inputStream into a ByteBuffer. 217
*/
public ByteBuffer decodeBufferToByteBuffer(InputStream in)
throws IOException
{
return ByteBuffer.wrap(decodeBuffer(in));
}
}
6.CharacterEncoder
public abstract class CharacterEncoder
{
/** Stream that understands "printing" */
protected PrintStream pStream;
/** Return the number of bytes per atom of encoding */
abstract protected int bytesPerAtom();
/** Return the number of bytes that can be encoded per line */
abstract protected int bytesPerLine();
/**
* 88 * Encode the prefix for the entire buffer. By default is simply 89 *
* opens the PrintStream for use by the other functions. 90
*/
protected void encodeBufferPrefix(OutputStream aStream) throws IOException
{
pStream = new PrintStream(aStream);
}
/**
* 96 * Encode the suffix for the entire buffer. 97
*/
protected void encodeBufferSuffix(OutputStream aStream) throws IOException
{
}
/**
* 102 * Encode the prefix that starts every output line. 103
*/
protected void encodeLinePrefix(OutputStream aStream, int aLength)
throws IOException
{
}
/**
* 109 * Encode the suffix that ends every output line. By default 110 *
* this method just prints a <newline> into the output stream. 111
*/
protected void encodeLineSuffix(OutputStream aStream) throws IOException
{
pStream.println();
}
/** Encode one "atom" of information into characters. */
abstract protected void encodeAtom(OutputStream aStream, byte someBytes[],
int anOffset, int aLength) throws IOException;
/**
* 121 * This method works around the bizarre semantics of
* BufferedInputStream's 122 * read method. 123
*/
protected int readFully(InputStream in, byte buffer[])
throws IOException
{
for (int i = 0; i < buffer.length; i++)
{
int q = in.read();
if (q == -1)
return i;
buffer[i] = (byte) q;
}
return buffer.length;
}
/**
* 136 * Encode bytes from the input stream, and write them as text
* characters 137 * to the output stream. This method will run until it
* exhausts the 138 * input stream, but does not print the line suffix for a
* final 139 * line that is shorter than bytesPerLine(). 140
*/
public void encode(InputStream inStream, OutputStream outStream)
throws IOException
{
int j;
int numBytes;
byte tmpbuffer[] = new byte[bytesPerLine()];
encodeBufferPrefix(outStream);
while (true)
{
numBytes = readFully(inStream, tmpbuffer);
if (numBytes == 0)
{
break;
}
encodeLinePrefix(outStream, numBytes);
for (j = 0; j < numBytes; j += bytesPerAtom())
{
if ((j + bytesPerAtom()) <= numBytes)
{
encodeAtom(outStream, tmpbuffer, j, bytesPerAtom());
} else
{
encodeAtom(outStream, tmpbuffer, j, (numBytes) - j);
}
}
if (numBytes < bytesPerLine())
{
break;
} else
{
encodeLineSuffix(outStream);
}
}
encodeBufferSuffix(outStream);
}
/**
* 173 * Encode the buffer in <i>aBuffer</i> and write the encoded 174 *
* result to the OutputStream <i>aStream</i>. 175
*/
public void encode(byte aBuffer[], OutputStream aStream) throws IOException
{
ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer);
encode(inStream, aStream);
}
/**
* 183 * A 'streamless' version of encode that simply takes a buffer of 184
* * bytes and returns a string containing the encoded buffer. 185
*/
public String encode(byte aBuffer[])
{
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer);
String retVal = null;
try
{
encode(inStream, outStream);
// explicit ascii->unicode conversion
retVal = outStream.toString("8859_1");
} catch (Exception IOException)
{
// This should never happen.
throw new Error("CharacterEncoder.encode internal error");
}
return (retVal);
}
/**
* 202 * Return a byte array from the remaining bytes in this ByteBuffer.
* 203 *
* <P>
* 204 * The ByteBuffer's position will be advanced to ByteBuffer's limit.
* 205 *
* <P>
* 206 * To avoid an extra copy, the implementation will attempt to return
* the 207 * byte array backing the ByteBuffer. If this is not possible, a
* 208 * new byte array will be created. 209
*/
private byte[] getBytes(ByteBuffer bb)
{
/*
* This should never return a BufferOverflowException, as we're 213 *
* careful to allocate just the right amount. 214
*/
byte[] buf = null;
/*
* 218 * If it has a usable backing byte buffer, use it. Use only 219 *
* if the array exactly represents the current ByteBuffer. 220
*/
if (bb.hasArray())
{
byte[] tmp = bb.array();
if ((tmp.length == bb.capacity()) && (tmp.length == bb.remaining()))
{
buf = tmp;
bb.position(bb.limit());
}
}
if (buf == null)
{
/*
* 232 * This class doesn't have a concept of encode(buf, len, off),
* 233 * so if we have a partial buffer, we must reallocate 234 *
* space. 235
*/
buf = new byte[bb.remaining()];
/*
* 239 * position() automatically updated 240
*/
bb.get(buf);
}
return buf;
}
/**
* 248 * Encode the <i>aBuffer</i> ByteBuffer and write the encoded 249 *
* result to the OutputStream <i>aStream</i>. 250 *
* <P>
* 251 * The ByteBuffer's position will be advanced to ByteBuffer's limit.
* 252
*/
public void encode(ByteBuffer aBuffer, OutputStream aStream)
throws IOException
{
byte[] buf = getBytes(aBuffer);
encode(buf, aStream);
}
/**
* 260 * A 'streamless' version of encode that simply takes a ByteBuffer 261
* * and returns a string containing the encoded buffer. 262 *
* <P>
* 263 * The ByteBuffer's position will be advanced to ByteBuffer's limit.
* 264
*/
public String encode(ByteBuffer aBuffer)
{
byte[] buf = getBytes(aBuffer);
return encode(buf);
}
/**
* 271 * Encode bytes from the input stream, and write them as text
* characters 272 * to the output stream. This method will run until it
* exhausts the 273 * input stream. It differs from encode in that it will
* add the 274 * line at the end of a final line that is shorter than
* bytesPerLine(). 275
*/
public void encodeBuffer(InputStream inStream, OutputStream outStream)
throws IOException
{
int j;
int numBytes;
byte tmpbuffer[] = new byte[bytesPerLine()];
encodeBufferPrefix(outStream);
while (true)
{
numBytes = readFully(inStream, tmpbuffer);
if (numBytes == 0)
{
break;
}
encodeLinePrefix(outStream, numBytes);
for (j = 0; j < numBytes; j += bytesPerAtom())
{
if ((j + bytesPerAtom()) <= numBytes)
{
encodeAtom(outStream, tmpbuffer, j, bytesPerAtom());
} else
{
encodeAtom(outStream, tmpbuffer, j, (numBytes) - j);
}
}
encodeLineSuffix(outStream);
if (numBytes < bytesPerLine())
{
break;
}
}
encodeBufferSuffix(outStream);
}
/**
* 306 * Encode the buffer in <i>aBuffer</i> and write the encoded 307 *
* result to the OutputStream <i>aStream</i>. 308
*/
public void encodeBuffer(byte aBuffer[], OutputStream aStream)
throws IOException
{
ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer);
encodeBuffer(inStream, aStream);
}
/**
* 316 * A 'streamless' version of encode that simply takes a buffer of 317
* * bytes and returns a string containing the encoded buffer. 318
*/
public String encodeBuffer(byte aBuffer[])
{
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer);
try
{
encodeBuffer(inStream, outStream);
} catch (Exception IOException)
{
// This should never happen.
throw new Error("CharacterEncoder.encodeBuffer internal error");
}
return (outStream.toString());
}
/**
* 332 * Encode the <i>aBuffer</i> ByteBuffer and write the encoded 333 *
* result to the OutputStream <i>aStream</i>. 334 *
* <P>
* 335 * The ByteBuffer's position will be advanced to ByteBuffer's limit.
* 336
*/
public void encodeBuffer(ByteBuffer aBuffer, OutputStream aStream)
throws IOException
{
byte[] buf = getBytes(aBuffer);
encodeBuffer(buf, aStream);
}
/**
* 344 * A 'streamless' version of encode that simply takes a ByteBuffer 345
* * and returns a string containing the encoded buffer. 346 *
* <P>
* 347 * The ByteBuffer's position will be advanced to ByteBuffer's limit.
* 348
*/
public String encodeBuffer(ByteBuffer aBuffer)
{
byte[] buf = getBytes(aBuffer);
return encodeBuffer(buf);
}
}
2.java 后端
public class AESUtils {
private static final BASE64Encoder B64_EC = new BASE64Encoder();
private static final BASE64Decoder B64_DC = new BASE64Decoder();
private static final String AES = "AES";
/**
* 注意:CRYPT_KEY必须是16位,如果修改为32位或者更高位的的将会报java.security.InvalidKeyException。
*下载对应的jar文件替换原有的:local_policy.jar和US_export_policy.jar,将其复制到JAVA_HOME下的\jre\lib\security中替换,即可。
*下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
*/
private static final String CRYPT_KEY = "ABC123,";
private static final String IV_STRING = "ABC123,";
/**
* 加密
* @param content 加密内容
* @return 密文
* @throws Exception e
*/
public static String encrypt(String content) {
byte[] encryptedBytes = new byte[0];
try {
byte[] byteContent = content.getBytes("UTF-8");
// 注意,为了能与 iOS 统一
// 这里的 key 不可以使用 KeyGenerator、SecureRandom、SecretKey 生成
byte[] enCodeFormat = CRYPT_KEY.getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, AES);
byte[] initParam = IV_STRING.getBytes();
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
// 指定加密的算法、工作模式和填充方式
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
encryptedBytes = cipher.doFinal(byteContent);
// 同样对加密后数据进行 base64 编码
}catch(Exception ex){
throw new RuntimeException("加密失败", ex);
}
//特殊字符处理:"="转换为!,"+"转换为@
return B64_EC.encode(encryptedBytes).replaceAll("=", "!")
.replace("+","@");
}
/**
* 解密
* @param content 密文
* @return 明文
* @throws Exception e
*/
public static String decrypt(String content) {
try {
byte[] encryptedBytes = B64_DC.decodeBuffer(content.replaceAll("!", "=") .replace("+","@"));
byte[] enCodeFormat = CRYPT_KEY.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, AES);
byte[] initParam = IV_STRING.getBytes();
IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] result = cipher.doFinal(encryptedBytes);
return new String(result, "UTF-8");
}catch(Exception ex){
throw new RuntimeException("解密失败", ex);
}
}
public static void main(String[] args) {
String src = "abc123~!@#¥%……—*()-+=/,.{abc测试}";
src += "对于服务器接收特殊符号处理:如\"+\"号后台接收为空格,前后端需约定特殊字符处理格式";
src += " * 将密文里的\"!\"号替换为正常加密的\"+\"";
String mw = encrypt(src);
System.out.println("密文:\n" + mw);
mw = "M4ULyXAHaairNEAuxc2VwJyN0AeMtndqgiZs08fqA6+ZbkYeODFJCwEpVnqeVXBI33ibrZqzjBTT3Lohb75B8UlKk9k/Ph+emEM1H0/Xkj/Cq/1g35P9sgndIttA6Irt";
String mmw = decrypt(mw);
System.out.println("明文:" + mmw);
System.out.println(src.equals(mmw));
}
}
3.ios端加密
//先定义一个初始向量的值。
NSString *const kInitVector = @"ABC123,";
NSString *const key= @"ABC123,";
//确定密钥长度,这里选择 AES-128。
size_t const kKeySize = kCCKeySizeAES128;
/**
AES加密方法
@param content 需要加密的字符串
@param key key
@return 加密后的字符串
*/
+ (NSString *)encryptAES:(NSString *)content{
NSData *contentData = [content dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLength = contentData.length;
// 为结束符'\\0' +1
char keyPtr[kKeySize + 1];
memset(keyPtr, 0, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
// 密文长度 <= 明文长度 + BlockSize
size_t encryptSize = dataLength + kCCBlockSizeAES128;
void *encryptedBytes = malloc(encryptSize);
size_t actualOutSize = 0;
NSData *initVector = [kInitVector dataUsingEncoding:NSUTF8StringEncoding];
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding, // 系统默认使用 CBC,然后指明使用 PKCS7Padding
keyPtr,
kKeySize,
initVector.bytes,
contentData.bytes,
dataLength,
encryptedBytes,
encryptSize,
&actualOutSize);
if (cryptStatus == kCCSuccess) {
// 对加密后的数据进行 base64 编码
return [[NSData dataWithBytesNoCopy:encryptedBytes length:actualOutSize] base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
}
free(encryptedBytes);
return nil;
}
/**
AES解密方法
@param content 需要解密的字符串
@param key key
@return 解密后的字符串
*/
+ (NSString *)decryptAES:(NSString *)content{
// 把 base64 String 转换成 Data
NSData *contentData = [[NSData alloc] initWithBase64EncodedString:content options:NSDataBase64DecodingIgnoreUnknownCharacters];
NSUInteger dataLength = contentData.length;
char keyPtr[kKeySize + 1];
memset(keyPtr, 0, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
size_t decryptSize = dataLength + kCCBlockSizeAES128;
void *decryptedBytes = malloc(decryptSize);
size_t actualOutSize = 0;
NSData *initVector = [kInitVector dataUsingEncoding:NSUTF8StringEncoding];
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
keyPtr,
kKeySize,
initVector.bytes,
contentData.bytes,
dataLength,
decryptedBytes,
decryptSize,
&actualOutSize);
if (cryptStatus == kCCSuccess) {
return [[NSString alloc] initWithData:[NSData dataWithBytesNoCopy:decryptedBytes length:actualOutSize] encoding:NSUTF8StringEncoding];
}
free(decryptedBytes);
return nil;
}
4.js端
接下来是js的代码,js中aes加密需要用到 CryptoJS工具包 下载地址download.csdn.net/download/ga…
<script type="text/javascript" src="./CryptoJS/crypto-js.js"></script>
<script>
//直接上代码
var key = CryptoJS.enc.Utf8.parse('ABC123,');
var iv = CryptoJS.enc.Utf8.parse('ABC123,');
var source = '要加密的字符串';
var password=CryptoJS.enc.Utf8.parse(source);
console.log("原始字符串:"+source);
console.log("utf8处理后:"+password);
var encrypted = CryptoJS.AES.encrypt(password, key, { iv: iv,mode:CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}); //CryptoJS.pad.Pkcs7
var decrypted = CryptoJS.AES.decrypt(encrypted, key, { iv: iv,mode:CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}); //CryptoJS.pad.Pkcs7
console.log("加密后base64:"+encrypted);
var encryptedStr=encrypted.ciphertext.toString();
console.log("加密后16进制:"+encryptedStr);
console.log("解密后utf8:"+decrypted);
console.log("解密后原始字符串:"+decrypted.toString(CryptoJS.enc.Utf8));
</script>
4.最后
感谢奔跑博客作者提供思路: blog.csdn.net/gao36951/ar…