姜永辉 2 tahun lalu
induk
melakukan
476acaac9b

+ 33 - 5
src/main/java/com/dk/oauth/controller/oauth/AccessTokenController.java

@@ -22,10 +22,10 @@ import com.dk.oauth.service.IAuthClientService;
 import com.dk.oauth.service.IAuthCodeService;
 import com.dk.oauth.shiro.jwt.JWTGenerator;
 import com.dk.oauth.shiro.jwt.JWTToken;
-import com.dk.oauth.util.AESSecurityUtil;
-import com.dk.oauth.util.DateUtil;
-import com.dk.oauth.util.IpUtil;
-import com.dk.oauth.util.UUID;
+import com.dk.oauth.util.*;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
 import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
@@ -36,6 +36,7 @@ import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
 import org.apache.oltu.oauth2.common.message.OAuthResponse;
 import org.apache.oltu.oauth2.common.message.types.GrantType;
 import org.apache.shiro.SecurityUtils;
+import org.bouncycastle.util.encoders.Base64;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
@@ -71,11 +72,38 @@ public class AccessTokenController {
      * @author : 姜永辉
      * @date   : 2022/5/11 17:30
      */
-    @PostMapping("login_wechat")
+    @PostMapping("/oauth/wx/login_wechat")
     public ResponseResultVO<JSONObject> loginWechat(@RequestBody Map<String, Object> map){
         return authAccessTokenService.loginWechat(map);
     }
 
+
+    /**
+     * 获取开发平台信息详细信息
+     */
+    @ApiOperation(value = "微信鉴权", notes = "根据凭据code获取openId")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "encryptedData", value = "加密数据", required = true, paramType = "body"),
+            @ApiImplicitParam(name = "iv", value = "偏移量", required = true, paramType = "body"),
+            @ApiImplicitParam(name = "sessionKey", value = "凭据", required = true, paramType = "body"),
+    })
+    @PostMapping(value = "/oauth/wx/decrypt")
+    public ResponseResultVO decrypt(@RequestBody WxMaEncryptedInfo info) {
+        try {
+            byte[] resultByte = AesUtil.decrypt(org.bouncycastle.util.encoders.Base64.decode(info.getEncryptedData()),
+                    org.bouncycastle.util.encoders.Base64.decode(info.getSessionKey()), Base64.decode(info.getIv()));
+            String userInfo = "";
+            if (null != resultByte && resultByte.length > 0) {
+                userInfo = new String(resultByte, "UTF-8");
+                JSONObject jsonObject = JSONObject.parseObject(userInfo);
+                return ResponseResultUtil.success(jsonObject);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return ResponseResultUtil.error();
+    }
+
     /**
      * @desc : 获取token
      * @author : 周兴

+ 32 - 0
src/main/java/com/dk/oauth/entity/WxMaEncryptedInfo.java

@@ -0,0 +1,32 @@
+package com.dk.oauth.entity;
+
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @className: WxMaEncryptedInfo
+ * @author: jyh
+ * @create: 2021-04-06 17:42
+ * @description: 微信加密信息
+ */
+@Data
+public class WxMaEncryptedInfo implements Serializable {
+
+    /**
+     * 加密数据
+     */
+    private String encryptedData;
+
+    /**
+     * 偏移
+     */
+    private String iv;
+
+    /**
+     * session_key
+     */
+    private String sessionKey;
+}
+

+ 1 - 0
src/main/java/com/dk/oauth/service/impl/AuthAccessTokenServiceImpl.java

@@ -113,6 +113,7 @@ public class AuthAccessTokenServiceImpl extends ServiceImpl<AuthAccessTokenMappe
         return res;
     }
 
+
     /**
      * @desc   : 获取token
      * @author : 洪旭东

+ 390 - 0
src/main/java/com/dk/oauth/util/AesUtil.java

@@ -0,0 +1,390 @@
+package com.dk.oauth.util;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import sun.misc.BASE64Decoder;
+import sun.misc.BASE64Encoder;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.*;
+import java.util.Arrays;
+import java.util.Date;
+
+public class AesUtil {
+	public static boolean initialized = false;
+	// 算法名称
+		final String KEY_ALGORITHM = "AES";
+		// 加解密算法/模式/填充方式
+		final String algorithmStr = "AES/CBC/PKCS7Padding";
+		private Key key;
+		private Cipher cipher;
+	public void init(byte[] keyBytes) {
+		 
+		// 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
+		int base = 16;
+		if (keyBytes.length % base != 0) {
+			int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
+			byte[] temp = new byte[groups * base];
+			Arrays.fill(temp, (byte) 0);
+			System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
+			keyBytes = temp;
+		}
+		// 初始化
+		Security.addProvider(new BouncyCastleProvider());
+		// 转化成JAVA的密钥格式
+		key = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
+		try {
+			// 初始化cipher
+			Cipher cipher = Cipher.getInstance(algorithmStr);
+		} catch (NoSuchAlgorithmException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (NoSuchPaddingException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+	// 加密
+	public static String Encrypt(String sSrc, String sKey) throws Exception {
+		if (sKey == null) {
+			// System.out.print("Key为空null");
+			return null;
+		}
+		// 判断Key是否为16位
+		if (sKey.length() != 16) {
+			// System.out.print("Key长度不是16位");
+			return null;
+		}
+		byte[] raw = sKey.getBytes();
+		SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
+		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");// "算法/模式/补码方式"
+		IvParameterSpec iv = new IvParameterSpec("0102030405060708".getBytes());// 使用CBC模式,需要一个向量iv,可增加加密算法的强度
+		cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
+		byte[] encrypted = cipher.doFinal(sSrc.getBytes());
+
+		return new BASE64Encoder().encode(encrypted);// 此处使用BASE64做转码功能,同时能起到2次加密的作用。
+	}
+
+	// 加密
+	public static String Encrypt(String sSrc, String sKey, String sIv)
+			throws Exception {
+		sSrc = new BASE64Encoder().encode(sSrc.getBytes());
+		if (sKey == null) {
+			// System.out.print("Key为空null");
+			return null;
+		}
+		int base = 16;
+		byte[] raw = sKey.getBytes();
+		// 判断Key是否为16位
+		if (sKey.length() != 16) {
+			// System.out.print("Key长度不是16位");
+			int groups = raw.length / base + (raw.length % base != 0 ? 1 : 0);
+			byte[] temp = new byte[groups * base];
+			Arrays.fill(temp, (byte) 0);
+			System.arraycopy(raw, 0, temp, 0, raw.length);
+			raw = temp;
+		}
+		SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
+		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");// "算法/模式/补码方式"
+		IvParameterSpec iv = new IvParameterSpec(sIv.getBytes());// 使用CBC模式,需要一个向量iv,可增加加密算法的强度
+		cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
+		byte[] encrypted = cipher.doFinal(sSrc.getBytes());
+
+		return new BASE64Encoder().encode(encrypted);// 此处使用BASE64做转码功能,同时能起到2次加密的作用。
+	}
+
+	// 解密
+	public static String Decrypt(String sSrc, String sKey) throws Exception {
+		try {
+			// 判断Key是否正确
+			if (sKey == null) {
+				// System.out.print("Key为空null");
+				return null;
+			}
+			// 判断Key是否为16位
+			if (sKey.length() != 16) {
+				// System.out.print("Key长度不是16位");
+				return null;
+			}
+			byte[] raw = sKey.getBytes("UTF-8");
+			SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
+			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+			IvParameterSpec iv = new IvParameterSpec(
+					"0102030405060708".getBytes());
+			cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
+			byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);// 先用base64解密
+			try {
+				byte[] original = cipher.doFinal(encrypted1);
+				String originalString = new String(original);
+				return originalString;
+			} catch (Exception e) {
+				// System.out.println(e.toString());
+				return null;
+			}
+		} catch (Exception ex) {
+			// System.out.println(ex.toString());
+			return null;
+		}
+	}
+
+	// 解密
+	public static String Decrypt(String sSrc, String sKey, String sIv)
+			throws Exception {
+		try {
+			// 判断Key是否正确
+			if (sKey == null) {
+				// System.out.print("Key为空null");
+				return null;
+			}
+			int base=16;
+			byte[] raw = sKey.getBytes();
+			// 判断Key是否为16位
+			if (sKey.length() != 16) {
+				// System.out.print("Key长度不是16位");
+				int groups = raw.length / base + (raw.length % base != 0 ? 1 : 0);
+				byte[] temp = new byte[groups * base];
+				Arrays.fill(temp, (byte) 0);
+				System.arraycopy(raw, 0, temp, 0, raw.length);
+				raw = temp;
+			}
+			SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
+			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+			IvParameterSpec iv = new IvParameterSpec(sIv.getBytes());
+			cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
+			byte[] encrypted = new BASE64Decoder().decodeBuffer(sSrc);// 先用base64解密
+			try {
+				byte[] original = cipher.doFinal(encrypted);
+				String originalString = new String(original);
+				return originalString;
+			} catch (Exception e) {
+				// System.out.println(e.toString());
+				e.printStackTrace();
+				return null;
+			}
+		} catch (Exception ex) {
+			// System.out.println(ex.toString());
+			ex.printStackTrace();
+			return null;
+		}
+	}
+
+	public static String sendPost(String url, String param) {
+		PrintWriter out = null;
+		BufferedReader in = null;
+		String result = "";
+		try {
+			URL realUrl = new URL(url);
+			// 打开和URL之间的连接
+			URLConnection conn = realUrl.openConnection();
+			// 设置通用的请求属性
+			conn.setRequestProperty("accept", "*/*");
+			conn.setRequestProperty("connection", "Keep-Alive");
+			conn.setRequestProperty("user-agent",
+					"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
+			// 发送POST请求必须设置如下两行
+			conn.setDoOutput(true);
+			conn.setDoInput(true);
+			// 获取URLConnection对象对应的输出流
+			out = new PrintWriter(conn.getOutputStream());
+			// 发送请求参数
+			out.print(param);
+			// flush输出流的缓冲
+			out.flush();
+			// 定义BufferedReader输入流来读取URL的响应
+			in = new BufferedReader(
+					new InputStreamReader(conn.getInputStream()));
+			String line;
+			while ((line = in.readLine()) != null) {
+				result += "\n" + line;
+			}
+		} catch (Exception e) {
+			System.out.println("发送POST请求出现异常!" + e);
+			e.printStackTrace();
+		}
+		// 使用finally块来关闭输出流、输入流
+		finally {
+			try {
+				if (out != null) {
+					out.close();
+				}
+				if (in != null) {
+					in.close();
+				}
+			} catch (IOException ex) {
+				ex.printStackTrace();
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * AES解密
+	 * 
+	 * @param content
+	 *            密文
+	 * @return
+	 * @throws InvalidAlgorithmParameterException
+	 * @throws NoSuchProviderException
+	 */
+	public static byte[] decrypt(byte[] content, byte[] keyBytes, byte[] ivByte)
+			throws InvalidAlgorithmParameterException {
+		initialize();
+		int base=16;
+		try {
+			if (keyBytes.length % base != 0) {
+				int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
+				byte[] temp = new byte[groups * base];
+				Arrays.fill(temp, (byte) 0);
+				System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
+				keyBytes = temp;
+			}
+			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+			Key sKeySpec = new SecretKeySpec(keyBytes, "AES");
+
+			cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化
+			byte[] result = cipher.doFinal(content);
+			return result;
+		} catch (NoSuchAlgorithmException e) {
+			e.printStackTrace();
+		} catch (NoSuchPaddingException e) {
+			e.printStackTrace();
+		} catch (InvalidKeyException e) {
+			e.printStackTrace();
+		} catch (IllegalBlockSizeException e) {
+			e.printStackTrace();
+		} catch (BadPaddingException e) {
+			e.printStackTrace();
+		} catch (NoSuchProviderException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+	public static void initialize() {
+		if (initialized)
+			return;
+		Security.addProvider(new BouncyCastleProvider());
+		initialized = true;
+	}
+
+	// 生成iv
+	public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
+		AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
+		params.init(new IvParameterSpec(iv));
+		return params;
+	}
+	/**
+     * AES-128 CBC解密方式
+     *
+     * @param content        待解密的Base64字符串
+     * @param encodingFormat
+     * @param key            密钥
+     * @param initVector     初始化向量
+     * @return
+     */
+    public static String aesCBCDecrypt(String content, String encodingFormat, String key, String initVector) {
+        try {
+        	int base=16;
+        	byte[] keyBytes=key.getBytes();
+        	if (keyBytes.length % base != 0) {
+				int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
+				byte[] temp = new byte[groups * base];
+				Arrays.fill(temp, (byte) 0);
+				System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
+				keyBytes = temp;
+			}
+            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
+            IvParameterSpec vector = new IvParameterSpec(initVector.getBytes());
+            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+            cipher.init(Cipher.DECRYPT_MODE, keySpec, vector);
+            //先用base64编码,因为对应的加密使用Base64解码
+            byte[] base64Bytes = new BASE64Decoder().decodeBuffer(content);
+            byte[] original = cipher.doFinal(base64Bytes);
+            String result = new String(original, encodingFormat);
+            return result;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+	public static void main(String[] args) throws Exception {
+		/*
+		 * 加密用的Key 可以用26个字母和数字组成,最好不要用保留字符,虽然不会错,至于怎么裁决,个人看情况而定
+		 * 此处使用AES-128-CBC加密模式,key需要为16位。
+		 */
+		// String cKey = "1234567890abcdef";
+		// JSONObject json1 = new JSONObject();
+		// json1.put("name", "1");
+		// JSONObject json = new JSONObject();
+		// json.put("name", json1.toString());
+		// // 需要加密的字串
+		// String cSrc = json.toString();
+		// System.out.println(cSrc);
+		// // 加密
+		// long lStart = System.currentTimeMillis();
+		// String enString = AesUtil.Encrypt(cSrc, cKey);
+		// System.out.println("加密后的字串是:" + enString);
+		//
+		// long lUseTime = System.currentTimeMillis() - lStart;
+		// System.out.println("加密耗时:" + lUseTime + "毫秒");
+		// // 解密
+		// lStart = System.currentTimeMillis();
+		//
+		// String DeString =
+		// "2dgKpMudbxK8oVuMqjIqZ20wehgGBy/jPTrU5Ztp4rebpehVSDe+rp2jalOXx2ZI3qtbZEZ9K1Vu\n"
+		// +
+		// "/lXPDL34pzv/VDZZh903fdW2AHR3HFkj49cyjVn8QbiMUxls7k8ZLdzX/YloxEHfpj7W1Q9TM9CS\n"
+		// +
+		// "BSqok6iM7f3oQ4E4KoaylKRJzdeW5j7pr6H1EY10Zj1oDr4U5dU1U9eD8IRB2FzUN4rlKzBuX30M\n"
+		// +
+		// "BmEIvcQr7I9FBjXmh9a5F/b8oUdOsBaAMuVZbHB5bb41n9snA76NKtzqoJjD7mAxSQXCcS2AE2p5\n"
+		// +
+		// "t2jgtmUpYHzzKLnbrCppSjEXQ263ni4YAo8cDOg2NpgnMKBwVZR+E1oDmS7RDMOtbDBBnnyqhlxM\n"
+		// +
+		// "HAPXsXxyGID7HExzPqfHbPDAEwBzs0CJUq9BTAWRdWL3yIePPHk3k1N21dcqmvvnfXfB56+dymMF\n"
+		// + "il+CJxvykh8iypPXO7oV+W5gZO3K1VKHKqE=";
+		// System.out.println(DeString.length());
+		// DeString = AesUtil.Decrypt(DeString, cKey);
+		// System.out.println("解密后的字串是:" + DeString);
+		// lUseTime = System.currentTimeMillis() - lStart;
+		// System.out.println("解密耗时:" + lUseTime + "毫秒");
+
+		// String ss = sendPost(
+		// "http://10.58.160.37:8081/gllaPlatFront/memberInfo.do?accountNo=12&passWord=C20AD4D76FE97759AA27A0C99BFF6710",
+		// "");
+		// System.out.println(ss.length());
+
+		// ss = AesUtil.Decrypt(ss, cKey);
+		// System.out.println("解密后的字串是:" + ss);
+		// System.out.println(URLDecoder.decode(ss, "utf-8"));
+		System.out.println(new Date().getTime());
+		String sSrc = "P20171208164708782" + "1537495349" + "IcBO0oUYWS*a";
+		String sKey = "IcBO0oUYWS*a";
+		String sIv = "4343435455545552";
+		String enString = AesUtil.Encrypt(sSrc, sKey, sIv);
+		System.out.println("加密后的字串是:" + enString);
+		String ss = "zIeJSkuGPpWha/JjbwZWc2DHzAamcztUPlKyJK/VSQMmJSVgnNpApy+N8vc/2fShyuzCaP2wNdcw8X82fUrsxy77lMjgZ9DO1rwebaU0ORs=";
+		//请求方解密数据 公共key
+		String public_key = "tuniaokeji000000";
+		//请求方解密数据 公共iv
+		String public_iv = "2015031320180313";
+		String ensString = AesUtil.Decrypt(ss, public_key, public_iv);
+		byte[] base64Bytes = new BASE64Decoder().decodeBuffer(ensString);
+		String result = new String(base64Bytes, "utf-8");
+		System.out.println("解密后的字串是:" + result);
+	}
+}

+ 291 - 0
src/main/java/com/dk/oauth/util/Base64.java

@@ -0,0 +1,291 @@
+package com.dk.oauth.util;
+
+/**
+ * Base64工具类
+ * 
+ * @author ruoyi
+ */
+public final class Base64
+{
+    static private final int     BASELENGTH           = 128;
+    static private final int     LOOKUPLENGTH         = 64;
+    static private final int     TWENTYFOURBITGROUP   = 24;
+    static private final int     EIGHTBIT             = 8;
+    static private final int     SIXTEENBIT           = 16;
+    static private final int     FOURBYTE             = 4;
+    static private final int     SIGN                 = -128;
+    static private final char    PAD                  = '=';
+    static final private byte[]  base64Alphabet       = new byte[BASELENGTH];
+    static final private char[]  lookUpBase64Alphabet = new char[LOOKUPLENGTH];
+
+    static
+    {
+        for (int i = 0; i < BASELENGTH; ++i)
+        {
+            base64Alphabet[i] = -1;
+        }
+        for (int i = 'Z'; i >= 'A'; i--)
+        {
+            base64Alphabet[i] = (byte) (i - 'A');
+        }
+        for (int i = 'z'; i >= 'a'; i--)
+        {
+            base64Alphabet[i] = (byte) (i - 'a' + 26);
+        }
+
+        for (int i = '9'; i >= '0'; i--)
+        {
+            base64Alphabet[i] = (byte) (i - '0' + 52);
+        }
+
+        base64Alphabet['+'] = 62;
+        base64Alphabet['/'] = 63;
+
+        for (int i = 0; i <= 25; i++)
+        {
+            lookUpBase64Alphabet[i] = (char) ('A' + i);
+        }
+
+        for (int i = 26, j = 0; i <= 51; i++, j++)
+        {
+            lookUpBase64Alphabet[i] = (char) ('a' + j);
+        }
+
+        for (int i = 52, j = 0; i <= 61; i++, j++)
+        {
+            lookUpBase64Alphabet[i] = (char) ('0' + j);
+        }
+        lookUpBase64Alphabet[62] = (char) '+';
+        lookUpBase64Alphabet[63] = (char) '/';
+    }
+
+    private static boolean isWhiteSpace(char octect)
+    {
+        return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
+    }
+
+    private static boolean isPad(char octect)
+    {
+        return (octect == PAD);
+    }
+
+    private static boolean isData(char octect)
+    {
+        return (octect < BASELENGTH && base64Alphabet[octect] != -1);
+    }
+
+    /**
+     * Encodes hex octects into Base64
+     *
+     * @param binaryData Array containing binaryData
+     * @return Encoded Base64 array
+     */
+    public static String encode(byte[] binaryData)
+    {
+        if (binaryData == null)
+        {
+            return null;
+        }
+
+        int lengthDataBits = binaryData.length * EIGHTBIT;
+        if (lengthDataBits == 0)
+        {
+            return "";
+        }
+
+        int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
+        int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
+        int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;
+        char encodedData[] = null;
+
+        encodedData = new char[numberQuartet * 4];
+
+        byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
+
+        int encodedIndex = 0;
+        int dataIndex = 0;
+
+        for (int i = 0; i < numberTriplets; i++)
+        {
+            b1 = binaryData[dataIndex++];
+            b2 = binaryData[dataIndex++];
+            b3 = binaryData[dataIndex++];
+
+            l = (byte) (b2 & 0x0f);
+            k = (byte) (b1 & 0x03);
+
+            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+            byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
+
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
+        }
+
+        // form integral number of 6-bit groups
+        if (fewerThan24bits == EIGHTBIT)
+        {
+            b1 = binaryData[dataIndex];
+            k = (byte) (b1 & 0x03);
+            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
+            encodedData[encodedIndex++] = PAD;
+            encodedData[encodedIndex++] = PAD;
+        }
+        else if (fewerThan24bits == SIXTEENBIT)
+        {
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex + 1];
+            l = (byte) (b2 & 0x0f);
+            k = (byte) (b1 & 0x03);
+
+            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
+            encodedData[encodedIndex++] = PAD;
+        }
+        return new String(encodedData);
+    }
+
+    /**
+     * Decodes Base64 data into octects
+     *
+     * @param encoded string containing Base64 data
+     * @return Array containind decoded data.
+     */
+    public static byte[] decode(String encoded)
+    {
+        if (encoded == null)
+        {
+            return null;
+        }
+
+        char[] base64Data = encoded.toCharArray();
+        // remove white spaces
+        int len = removeWhiteSpace(base64Data);
+
+        if (len % FOURBYTE != 0)
+        {
+            return null;// should be divisible by four
+        }
+
+        int numberQuadruple = (len / FOURBYTE);
+
+        if (numberQuadruple == 0)
+        {
+            return new byte[0];
+        }
+
+        byte decodedData[] = null;
+        byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
+        char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
+
+        int i = 0;
+        int encodedIndex = 0;
+        int dataIndex = 0;
+        decodedData = new byte[(numberQuadruple) * 3];
+
+        for (; i < numberQuadruple - 1; i++)
+        {
+
+            if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))
+                    || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++])))
+            {
+                return null;
+            } // if found "no data" just return null
+
+            b1 = base64Alphabet[d1];
+            b2 = base64Alphabet[d2];
+            b3 = base64Alphabet[d3];
+            b4 = base64Alphabet[d4];
+
+            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
+        }
+
+        if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])))
+        {
+            return null;// if found "no data" just return null
+        }
+
+        b1 = base64Alphabet[d1];
+        b2 = base64Alphabet[d2];
+
+        d3 = base64Data[dataIndex++];
+        d4 = base64Data[dataIndex++];
+        if (!isData((d3)) || !isData((d4)))
+        {// Check if they are PAD characters
+            if (isPad(d3) && isPad(d4))
+            {
+                if ((b2 & 0xf) != 0)// last 4 bits should be zero
+                {
+                    return null;
+                }
+                byte[] tmp = new byte[i * 3 + 1];
+                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
+                tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+                return tmp;
+            }
+            else if (!isPad(d3) && isPad(d4))
+            {
+                b3 = base64Alphabet[d3];
+                if ((b3 & 0x3) != 0)// last 2 bits should be zero
+                {
+                    return null;
+                }
+                byte[] tmp = new byte[i * 3 + 2];
+                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
+                tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+                tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+                return tmp;
+            }
+            else
+            {
+                return null;
+            }
+        }
+        else
+        { // No PAD e.g 3cQl
+            b3 = base64Alphabet[d3];
+            b4 = base64Alphabet[d4];
+            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
+
+        }
+        return decodedData;
+    }
+
+    /**
+     * remove WhiteSpace from MIME containing encoded Base64 data.
+     *
+     * @param data the byte array of base64 data (with WS)
+     * @return the new length
+     */
+    private static int removeWhiteSpace(char[] data)
+    {
+        if (data == null)
+        {
+            return 0;
+        }
+
+        // count characters that's not whitespace
+        int newSize = 0;
+        int len = data.length;
+        for (int i = 0; i < len; i++)
+        {
+            if (!isWhiteSpace(data[i]))
+            {
+                data[newSize++] = data[i];
+            }
+        }
+        return newSize;
+    }
+}