AES对称加密包含(android、java后端、ios、前端js)

492 阅读9分钟

背景

为了提高项目安全性,我们一般都会对接口传输进行加密,那么加密过程中,我们一般采用对称加密和非对称加密。其中对称加密只要双方有相同的秘钥即可完成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…