SM4 加解密工具

SM4 是国家密码管理局于 2010 年发布的一种国产对称密码算法,它采用了 128 位分组密码,其安全性已被国际公认。SM4 算法的主要特点是安全性高、效率高、易于实现,因此被广泛应用于电子商务、电子政务、金融等领域。

1、Base64工具

import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;

public class Base64 {
   private static final char[] S_BASE64CHAR = 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 static final byte[] S_DECODETABLE = new byte[128];

   public Base64() {
   }

   private static int decode0(char[] ibuf, byte[] obuf, int wp) {
      int outlen = 3;
      if (ibuf[3] == '=') {
         outlen = 2;
      }

      if (ibuf[2] == '=') {
         outlen = 1;
      }

      int b0 = S_DECODETABLE[ibuf[0]];
      int b1 = S_DECODETABLE[ibuf[1]];
      int b2 = S_DECODETABLE[ibuf[2]];
      int b3 = S_DECODETABLE[ibuf[3]];
      switch(outlen) {
         case 1:
            obuf[wp] = (byte)(b0 << 2 & 252 | b1 >> 4 & 3);
            return 1;
         case 2:
            obuf[wp++] = (byte)(b0 << 2 & 252 | b1 >> 4 & 3);
            obuf[wp] = (byte)(b1 << 4 & 240 | b2 >> 2 & 15);
            return 2;
         case 3:
            obuf[wp++] = (byte)(b0 << 2 & 252 | b1 >> 4 & 3);
            obuf[wp++] = (byte)(b1 << 4 & 240 | b2 >> 2 & 15);
            obuf[wp] = (byte)(b2 << 6 & 192 | b3 & 63);
            return 3;
         default:
            throw new RuntimeException("Internal error");
      }
   }

   public static byte[] decode(char[] data, int off, int len) {
      char[] ibuf = new char[4];
      int ibufcount = 0;
      byte[] obuf = new byte[len / 4 * 3 + 3];
      int obufcount = 0;

      for(int i = off; i < off + len; ++i) {
         char ch = data[i];
         if (ch == '=' || ch < S_DECODETABLE.length && S_DECODETABLE[ch] != 127) {
            ibuf[ibufcount++] = ch;
            if (ibufcount == ibuf.length) {
               ibufcount = 0;
               obufcount += decode0(ibuf, obuf, obufcount);
            }
         }
      }

      if (obufcount == obuf.length) {
         return obuf;
      } else {
         byte[] ret = new byte[obufcount];
         System.arraycopy(obuf, 0, ret, 0, obufcount);
         return ret;
      }
   }

   public static byte[] decode(String data) {
      char[] ibuf = new char[4];
      int ibufcount = 0;
      byte[] obuf = new byte[data.length() / 4 * 3 + 3];
      int obufcount = 0;

      for(int i = 0; i < data.length(); ++i) {
         char ch = data.charAt(i);
         if (ch == '=' || ch < S_DECODETABLE.length && S_DECODETABLE[ch] != 127) {
            ibuf[ibufcount++] = ch;
            if (ibufcount == ibuf.length) {
               ibufcount = 0;
               obufcount += decode0(ibuf, obuf, obufcount);
            }
         }
      }

      if (obufcount == obuf.length) {
         return obuf;
      } else {
         byte[] ret = new byte[obufcount];
         System.arraycopy(obuf, 0, ret, 0, obufcount);
         return ret;
      }
   }

   public static void decode(char[] data, int off, int len, OutputStream ostream) throws IOException {
      char[] ibuf = new char[4];
      int ibufcount = 0;
      byte[] obuf = new byte[3];

      for(int i = off; i < off + len; ++i) {
         char ch = data[i];
         if (ch == '=' || ch < S_DECODETABLE.length && S_DECODETABLE[ch] != 127) {
            ibuf[ibufcount++] = ch;
            if (ibufcount == ibuf.length) {
               ibufcount = 0;
               int obufcount = decode0(ibuf, obuf, 0);
               ostream.write(obuf, 0, obufcount);
            }
         }
      }

   }

   public static void decode(String data, OutputStream ostream) throws IOException {
      char[] ibuf = new char[4];
      int ibufcount = 0;
      byte[] obuf = new byte[3];

      for(int i = 0; i < data.length(); ++i) {
         char ch = data.charAt(i);
         if (ch == '=' || ch < S_DECODETABLE.length && S_DECODETABLE[ch] != 127) {
            ibuf[ibufcount++] = ch;
            if (ibufcount == ibuf.length) {
               ibufcount = 0;
               int obufcount = decode0(ibuf, obuf, 0);
               ostream.write(obuf, 0, obufcount);
            }
         }
      }

   }

   public static String encode(byte[] data) {
      return encode(data, 0, data.length);
   }

   public static String encode(byte[] data, int off, int len) {
      if (len <= 0) {
         return "";
      } else {
         char[] out = new char[len / 3 * 4 + 4];
         int rindex = off;
         int windex = 0;

         int rest;
         int i;
         for(rest = len - off; rest >= 3; rest -= 3) {
            i = ((data[rindex] & 255) << 16) + ((data[rindex + 1] & 255) << 8) + (data[rindex + 2] & 255);
            out[windex++] = S_BASE64CHAR[i >> 18];
            out[windex++] = S_BASE64CHAR[i >> 12 & 63];
            out[windex++] = S_BASE64CHAR[i >> 6 & 63];
            out[windex++] = S_BASE64CHAR[i & 63];
            rindex += 3;
         }

         if (rest == 1) {
            i = data[rindex] & 255;
            out[windex++] = S_BASE64CHAR[i >> 2];
            out[windex++] = S_BASE64CHAR[i << 4 & 63];
            out[windex++] = '=';
            out[windex++] = '=';
         } else if (rest == 2) {
            i = ((data[rindex] & 255) << 8) + (data[rindex + 1] & 255);
            out[windex++] = S_BASE64CHAR[i >> 10];
            out[windex++] = S_BASE64CHAR[i >> 4 & 63];
            out[windex++] = S_BASE64CHAR[i << 2 & 63];
            out[windex++] = '=';
         }

         return new String(out, 0, windex);
      }
   }

   public static void encode(byte[] data, int off, int len, OutputStream ostream) throws IOException {
      if (len > 0) {
         byte[] out = new byte[4];
         int rindex = off;

         int rest;
         int i;
         for(rest = len - off; rest >= 3; rest -= 3) {
            i = ((data[rindex] & 255) << 16) + ((data[rindex + 1] & 255) << 8) + (data[rindex + 2] & 255);
            out[0] = (byte)S_BASE64CHAR[i >> 18];
            out[1] = (byte)S_BASE64CHAR[i >> 12 & 63];
            out[2] = (byte)S_BASE64CHAR[i >> 6 & 63];
            out[3] = (byte)S_BASE64CHAR[i & 63];
            ostream.write(out, 0, 4);
            rindex += 3;
         }

         if (rest == 1) {
            i = data[rindex] & 255;
            out[0] = (byte)S_BASE64CHAR[i >> 2];
            out[1] = (byte)S_BASE64CHAR[i << 4 & 63];
            out[2] = 61;
            out[3] = 61;
            ostream.write(out, 0, 4);
         } else if (rest == 2) {
            i = ((data[rindex] & 255) << 8) + (data[rindex + 1] & 255);
            out[0] = (byte)S_BASE64CHAR[i >> 10];
            out[1] = (byte)S_BASE64CHAR[i >> 4 & 63];
            out[2] = (byte)S_BASE64CHAR[i << 2 & 63];
            out[3] = 61;
            ostream.write(out, 0, 4);
         }

      }
   }

   public static void encode(byte[] data, int off, int len, Writer writer) throws IOException {
      if (len > 0) {
         char[] out = new char[4];
         int rindex = off;
         int rest = len - off;
         int output = 0;

         int i;
         while(rest >= 3) {
            i = ((data[rindex] & 255) << 16) + ((data[rindex + 1] & 255) << 8) + (data[rindex + 2] & 255);
            out[0] = S_BASE64CHAR[i >> 18];
            out[1] = S_BASE64CHAR[i >> 12 & 63];
            out[2] = S_BASE64CHAR[i >> 6 & 63];
            out[3] = S_BASE64CHAR[i & 63];
            writer.write(out, 0, 4);
            rindex += 3;
            rest -= 3;
            output += 4;
            if (output % 76 == 0) {
               writer.write("\n");
            }
         }

         if (rest == 1) {
            i = data[rindex] & 255;
            out[0] = S_BASE64CHAR[i >> 2];
            out[1] = S_BASE64CHAR[i << 4 & 63];
            out[2] = '=';
            out[3] = '=';
            writer.write(out, 0, 4);
         } else if (rest == 2) {
            i = ((data[rindex] & 255) << 8) + (data[rindex + 1] & 255);
            out[0] = S_BASE64CHAR[i >> 10];
            out[1] = S_BASE64CHAR[i >> 4 & 63];
            out[2] = S_BASE64CHAR[i << 2 & 63];
            out[3] = '=';
            writer.write(out, 0, 4);
         }

      }
   }

   static {
      int i;
      for(i = 0; i < S_DECODETABLE.length; ++i) {
         S_DECODETABLE[i] = 127;
      }

      for(i = 0; i < S_BASE64CHAR.length; ++i) {
         S_DECODETABLE[S_BASE64CHAR[i]] = (byte)i;
      }

   }
}

2、SM4Context

public class SM4Context {
    public int mode;

    public long[] sk;

    public boolean isPadding;

    public SM4Context()
    {
        this.mode = 1;
        this.isPadding = true;
        this.sk = new long[32];
    }
}

3、SM4

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

/**
 * SM4无线局域网标准的分组数据算法。对称加密,密钥长度和分组长度均为128位
 */
public class SM4 {
    //定义加密标志
    public static final int SM4_ENCRYPT = 1;
    //定义解密标志
    public static final int SM4_DECRYPT = 0;

    private static long GET_ULONG_BE(byte[] b, int i) {
        long n = (long) (b[i] & 0xff) << 24 | (long) ((b[i + 1] & 0xff) << 16) | (long) ((b[i + 2] & 0xff) << 8)
                | (long) (b[i + 3] & 0xff) & 0xffffffffL;
        return n;
    }

    private static void PUT_ULONG_BE(long n, byte[] b, int i) {
        b[i] = (byte) (int) (0xFF & n >> 24);
        b[i + 1] = (byte) (int) (0xFF & n >> 16);
        b[i + 2] = (byte) (int) (0xFF & n >> 8);
        b[i + 3] = (byte) (int) (0xFF & n);
    }

    private static long SHL(long x, int n) {
        return (x & 0xFFFFFFFF) << n;
    }

    private static long ROTL(long x, int n) {
        return SHL(x, n) | x >> (32 - n);
    }

    private static void SWAP(long[] sk, int i) {
        long t = sk[i];
        sk[i] = sk[(31 - i)];
        sk[(31 - i)] = t;
    }

    public static final byte[] SboxTable = { (byte) 0xd6, (byte) 0x90, (byte) 0xe9, (byte) 0xfe, (byte) 0xcc,
            (byte) 0xe1, 0x3d, (byte) 0xb7, 0x16, (byte) 0xb6, 0x14, (byte) 0xc2, 0x28, (byte) 0xfb, 0x2c, 0x05, 0x2b,
            0x67, (byte) 0x9a, 0x76, 0x2a, (byte) 0xbe, 0x04, (byte) 0xc3, (byte) 0xaa, 0x44, 0x13, 0x26, 0x49,
            (byte) 0x86, 0x06, (byte) 0x99, (byte) 0x9c, 0x42, 0x50, (byte) 0xf4, (byte) 0x91, (byte) 0xef, (byte) 0x98,
            0x7a, 0x33, 0x54, 0x0b, 0x43, (byte) 0xed, (byte) 0xcf, (byte) 0xac, 0x62, (byte) 0xe4, (byte) 0xb3, 0x1c,
            (byte) 0xa9, (byte) 0xc9, 0x08, (byte) 0xe8, (byte) 0x95, (byte) 0x80, (byte) 0xdf, (byte) 0x94,
            (byte) 0xfa, 0x75, (byte) 0x8f, 0x3f, (byte) 0xa6, 0x47, 0x07, (byte) 0xa7, (byte) 0xfc, (byte) 0xf3, 0x73,
            0x17, (byte) 0xba, (byte) 0x83, 0x59, 0x3c, 0x19, (byte) 0xe6, (byte) 0x85, 0x4f, (byte) 0xa8, 0x68, 0x6b,
            (byte) 0x81, (byte) 0xb2, 0x71, 0x64, (byte) 0xda, (byte) 0x8b, (byte) 0xf8, (byte) 0xeb, 0x0f, 0x4b, 0x70,
            0x56, (byte) 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, (byte) 0xd1, (byte) 0xa2, 0x25, 0x22, 0x7c,
            0x3b, 0x01, 0x21, 0x78, (byte) 0x87, (byte) 0xd4, 0x00, 0x46, 0x57, (byte) 0x9f, (byte) 0xd3, 0x27, 0x52,
            0x4c, 0x36, 0x02, (byte) 0xe7, (byte) 0xa0, (byte) 0xc4, (byte) 0xc8, (byte) 0x9e, (byte) 0xea, (byte) 0xbf,
            (byte) 0x8a, (byte) 0xd2, 0x40, (byte) 0xc7, 0x38, (byte) 0xb5, (byte) 0xa3, (byte) 0xf7, (byte) 0xf2,
            (byte) 0xce, (byte) 0xf9, 0x61, 0x15, (byte) 0xa1, (byte) 0xe0, (byte) 0xae, 0x5d, (byte) 0xa4, (byte) 0x9b,
            0x34, 0x1a, 0x55, (byte) 0xad, (byte) 0x93, 0x32, 0x30, (byte) 0xf5, (byte) 0x8c, (byte) 0xb1, (byte) 0xe3,
            0x1d, (byte) 0xf6, (byte) 0xe2, 0x2e, (byte) 0x82, 0x66, (byte) 0xca, 0x60, (byte) 0xc0, 0x29, 0x23,
            (byte) 0xab, 0x0d, 0x53, 0x4e, 0x6f, (byte) 0xd5, (byte) 0xdb, 0x37, 0x45, (byte) 0xde, (byte) 0xfd,
            (byte) 0x8e, 0x2f, 0x03, (byte) 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51, (byte) 0x8d, 0x1b, (byte) 0xaf,
            (byte) 0x92, (byte) 0xbb, (byte) 0xdd, (byte) 0xbc, 0x7f, 0x11, (byte) 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a,
            (byte) 0xd8, 0x0a, (byte) 0xc1, 0x31, (byte) 0x88, (byte) 0xa5, (byte) 0xcd, 0x7b, (byte) 0xbd, 0x2d, 0x74,
            (byte) 0xd0, 0x12, (byte) 0xb8, (byte) 0xe5, (byte) 0xb4, (byte) 0xb0, (byte) 0x89, 0x69, (byte) 0x97, 0x4a,
            0x0c, (byte) 0x96, 0x77, 0x7e, 0x65, (byte) 0xb9, (byte) 0xf1, 0x09, (byte) 0xc5, 0x6e, (byte) 0xc6,
            (byte) 0x84, 0x18, (byte) 0xf0, 0x7d, (byte) 0xec, 0x3a, (byte) 0xdc, 0x4d, 0x20, 0x79, (byte) 0xee, 0x5f,
            0x3e, (byte) 0xd7, (byte) 0xcb, 0x39, 0x48 };

    public static final int[] FK = { 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc };

    public static final int[] CK = { 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269, 0x70777e85, 0x8c939aa1, 0xa8afb6bd,
            0xc4cbd2d9, 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249, 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
            0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229, 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299, 0xa0a7aeb5,
            0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209, 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279 };

    private static byte sm4Sbox(byte inch) {
        int i = inch & 0xFF;
        byte retVal = SboxTable[i];
        return retVal;
    }

    private static long sm4Lt(long ka) {
        long bb = 0L;
        long c = 0L;
        byte[] a = new byte[4];
        byte[] b = new byte[4];
        PUT_ULONG_BE(ka, a, 0);
        b[0] = sm4Sbox(a[0]);
        b[1] = sm4Sbox(a[1]);
        b[2] = sm4Sbox(a[2]);
        b[3] = sm4Sbox(a[3]);
        bb = GET_ULONG_BE(b, 0);
        c = bb ^ ROTL(bb, 2) ^ ROTL(bb, 10) ^ ROTL(bb, 18) ^ ROTL(bb, 24);
        return c;
    }

    private static long sm4F(long x0, long x1, long x2, long x3, long rk) {
        return x0 ^ sm4Lt(x1 ^ x2 ^ x3 ^ rk);
    }

    private static long sm4CalciRK(long ka) {
        long bb = 0L;
        long rk = 0L;
        byte[] a = new byte[4];
        byte[] b = new byte[4];
        PUT_ULONG_BE(ka, a, 0);
        b[0] = sm4Sbox(a[0]);
        b[1] = sm4Sbox(a[1]);
        b[2] = sm4Sbox(a[2]);
        b[3] = sm4Sbox(a[3]);
        bb = GET_ULONG_BE(b, 0);
        rk = bb ^ ROTL(bb, 13) ^ ROTL(bb, 23);
        return rk;
    }

    private static void sm4_setkey(long[] SK, byte[] key) {
        long[] MK = new long[4];
        long[] k = new long[36];
        int i = 0;
        MK[0] = GET_ULONG_BE(key, 0);
        MK[1] = GET_ULONG_BE(key, 4);
        MK[2] = GET_ULONG_BE(key, 8);
        MK[3] = GET_ULONG_BE(key, 12);
        k[0] = MK[0] ^ (long) FK[0];
        k[1] = MK[1] ^ (long) FK[1];
        k[2] = MK[2] ^ (long) FK[2];
        k[3] = MK[3] ^ (long) FK[3];
        for (; i < 32; i++) {
            k[(i + 4)] = (k[i] ^ sm4CalciRK(k[(i + 1)] ^ k[(i + 2)] ^ k[(i + 3)] ^ (long) CK[i]));
            SK[i] = k[(i + 4)];
        }
    }

    private static void sm4_one_round(long[] sk, byte[] input, byte[] output) {
        int i = 0;
        long[] ulbuf = new long[36];
        ulbuf[0] = GET_ULONG_BE(input, 0);
        ulbuf[1] = GET_ULONG_BE(input, 4);
        ulbuf[2] = GET_ULONG_BE(input, 8);
        ulbuf[3] = GET_ULONG_BE(input, 12);
        while (i < 32) {
            ulbuf[(i + 4)] = sm4F(ulbuf[i], ulbuf[(i + 1)], ulbuf[(i + 2)], ulbuf[(i + 3)], sk[i]);
            i++;
        }
        PUT_ULONG_BE(ulbuf[35], output, 0);
        PUT_ULONG_BE(ulbuf[34], output, 4);
        PUT_ULONG_BE(ulbuf[33], output, 8);
        PUT_ULONG_BE(ulbuf[32], output, 12);
    }

    private static byte[] padding(byte[] input, int mode) {
        if (input == null) {
            return null;
        }

        byte[] ret = (byte[]) null;
        if (mode == SM4_ENCRYPT) {
            int p = 16 - input.length % 16;
            ret = new byte[input.length + p];
            System.arraycopy(input, 0, ret, 0, input.length);
            for (int i = 0; i < p; i++) {
                ret[input.length + i] = (byte) p;
            }
        } else {
            int p = input[input.length - 1];
            ret = new byte[input.length - p];
            System.arraycopy(input, 0, ret, 0, input.length - p);
        }
        return ret;
    }

    public static void sm4_setkey_enc(SM4Context ctx, byte[] key) throws Exception {
        if (ctx == null) {
            throw new Exception("ctx is null!");
        }

        if (key == null || key.length != 16) {
            throw new Exception("key error!");
        }

        ctx.mode = SM4_ENCRYPT;
        sm4_setkey(ctx.sk, key);
    }

    public static void sm4_setkey_dec(SM4Context ctx, byte[] key) throws Exception {
        if (ctx == null) {
            throw new Exception("ctx is null!");
        }

        if (key == null || key.length != 16) {
            throw new Exception("key error!");
        }

        int i = 0;
        ctx.mode = SM4_DECRYPT;
        sm4_setkey(ctx.sk, key);
        for (i = 0; i < 16; i++) {
            SWAP(ctx.sk, i);
        }
    }

    public static byte[] sm4_crypt_ecb(SM4Context ctx, byte[] input) throws Exception {
        if (input == null) {
            throw new Exception("input is null!");
        }

        if ((ctx.isPadding) && (ctx.mode == SM4_ENCRYPT)) {
            input = padding(input, SM4_ENCRYPT);
        }

        int length = input.length;
        ByteArrayInputStream bins = new ByteArrayInputStream(input);
        ByteArrayOutputStream bous = new ByteArrayOutputStream();
        for (; length > 0; length -= 16) {
            byte[] in = new byte[16];
            byte[] out = new byte[16];
            bins.read(in);
            sm4_one_round(ctx.sk, in, out);
            bous.write(out);
        }

        byte[] output = bous.toByteArray();
        if (ctx.isPadding && ctx.mode == SM4_DECRYPT) {
            output = padding(output, SM4_DECRYPT);
        }
        bins.close();
        bous.close();
        return output;
    }

    public byte[] sm4_crypt_cbc(SM4Context ctx, byte[] iv, byte[] input) throws Exception {
        if (iv == null || iv.length != 16) {
            throw new Exception("iv error!");
        }

        if (input == null) {
            throw new Exception("input is null!");
        }

        if (ctx.isPadding && ctx.mode == SM4_ENCRYPT) {
            input = padding(input, SM4_ENCRYPT);
        }

        int i = 0;
        int length = input.length;
        ByteArrayInputStream bins = new ByteArrayInputStream(input);
        ByteArrayOutputStream bous = new ByteArrayOutputStream();
        if (ctx.mode == SM4_ENCRYPT) {
            for (; length > 0; length -= 16) {
                byte[] in = new byte[16];
                byte[] out = new byte[16];
                byte[] out1 = new byte[16];

                bins.read(in);
                for (i = 0; i < 16; i++) {
                    out[i] = ((byte) (in[i] ^ iv[i]));
                }
                sm4_one_round(ctx.sk, out, out1);
                System.arraycopy(out1, 0, iv, 0, 16);
                bous.write(out1);
            }
        } else {
            byte[] temp = new byte[16];
            for (; length > 0; length -= 16) {
                byte[] in = new byte[16];
                byte[] out = new byte[16];
                byte[] out1 = new byte[16];

                bins.read(in);
                System.arraycopy(in, 0, temp, 0, 16);
                sm4_one_round(ctx.sk, in, out);
                for (i = 0; i < 16; i++) {
                    out1[i] = ((byte) (out[i] ^ iv[i]));
                }
                System.arraycopy(temp, 0, iv, 0, 16);
                bous.write(out1);
            }
        }

        byte[] output = bous.toByteArray();
        if (ctx.isPadding && ctx.mode == SM4_DECRYPT) {
            output = padding(output, SM4_DECRYPT);
        }
        bins.close();
        bous.close();
        return output;
    }
}

4、ByteUtils

/*
 * jPOS Project [http://jpos.org]
 * Copyright (C) 2000-2008 Alejandro P. Revilla
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.BitSet;
import java.util.StringTokenizer;

/**
 * 工具类
 *
 */
public class ByteUtils {
    public static final byte[] EBCDIC2ASCII = new byte[] {
            (byte)0x0,  (byte)0x1,  (byte)0x2,  (byte)0x3,
            (byte)0x9C, (byte)0x9,  (byte)0x86, (byte)0x7F,
            (byte)0x97, (byte)0x8D, (byte)0x8E, (byte)0xB,
            (byte)0xC,  (byte)0xD,  (byte)0xE,  (byte)0xF,
            (byte)0x10, (byte)0x11, (byte)0x12, (byte)0x13,
            (byte)0x9D, (byte)0xA,  (byte)0x8,  (byte)0x87,
            (byte)0x18, (byte)0x19, (byte)0x92, (byte)0x8F,
            (byte)0x1C, (byte)0x1D, (byte)0x1E, (byte)0x1F,
            (byte)0x80, (byte)0x81, (byte)0x82, (byte)0x83,
            (byte)0x84, (byte)0x85, (byte)0x17, (byte)0x1B,
            (byte)0x88, (byte)0x89, (byte)0x8A, (byte)0x8B,
            (byte)0x8C, (byte)0x5,  (byte)0x6,  (byte)0x7,
            (byte)0x90, (byte)0x91, (byte)0x16, (byte)0x93,
            (byte)0x94, (byte)0x95, (byte)0x96, (byte)0x4,
            (byte)0x98, (byte)0x99, (byte)0x9A, (byte)0x9B,
            (byte)0x14, (byte)0x15, (byte)0x9E, (byte)0x1A,
            (byte)0x20, (byte)0xA0, (byte)0xE2, (byte)0xE4,
            (byte)0xE0, (byte)0xE1, (byte)0xE3, (byte)0xE5,
            (byte)0xE7, (byte)0xF1, (byte)0xA2, (byte)0x2E,
            (byte)0x3C, (byte)0x28, (byte)0x2B, (byte)0x7C,
            (byte)0x26, (byte)0xE9, (byte)0xEA, (byte)0xEB,
            (byte)0xE8, (byte)0xED, (byte)0xEE, (byte)0xEF,
            (byte)0xEC, (byte)0xDF, (byte)0x21, (byte)0x24,
            (byte)0x2A, (byte)0x29, (byte)0x3B, (byte)0x5E,
            (byte)0x2D, (byte)0x2F, (byte)0xC2, (byte)0xC4,
            (byte)0xC0, (byte)0xC1, (byte)0xC3, (byte)0xC5,
            (byte)0xC7, (byte)0xD1, (byte)0xA6, (byte)0x2C,
            (byte)0x25, (byte)0x5F, (byte)0x3E, (byte)0x3F,
            (byte)0xF8, (byte)0xC9, (byte)0xCA, (byte)0xCB,
            (byte)0xC8, (byte)0xCD, (byte)0xCE, (byte)0xCF,
            (byte)0xCC, (byte)0x60, (byte)0x3A, (byte)0x23,
            (byte)0x40, (byte)0x27, (byte)0x3D, (byte)0x22,
            (byte)0xD8, (byte)0x61, (byte)0x62, (byte)0x63,
            (byte)0x64, (byte)0x65, (byte)0x66, (byte)0x67,
            (byte)0x68, (byte)0x69, (byte)0xAB, (byte)0xBB,
            (byte)0xF0, (byte)0xFD, (byte)0xFE, (byte)0xB1,
            (byte)0xB0, (byte)0x6A, (byte)0x6B, (byte)0x6C,
            (byte)0x6D, (byte)0x6E, (byte)0x6F, (byte)0x70,
            (byte)0x71, (byte)0x72, (byte)0xAA, (byte)0xBA,
            (byte)0xE6, (byte)0xB8, (byte)0xC6, (byte)0xA4,
            (byte)0xB5, (byte)0x7E, (byte)0x73, (byte)0x74,
            (byte)0x75, (byte)0x76, (byte)0x77, (byte)0x78,
            (byte)0x79, (byte)0x7A, (byte)0xA1, (byte)0xBF,
            (byte)0xD0, (byte)0x5B, (byte)0xDE, (byte)0xAE,
            (byte)0xAC, (byte)0xA3, (byte)0xA5, (byte)0xB7,
            (byte)0xA9, (byte)0xA7, (byte)0xB6, (byte)0xBC,
            (byte)0xBD, (byte)0xBE, (byte)0xDD, (byte)0xA8,
            (byte)0xAF, (byte)0x5D, (byte)0xB4, (byte)0xD7,
            (byte)0x7B, (byte)0x41, (byte)0x42, (byte)0x43,
            (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47,
            (byte)0x48, (byte)0x49, (byte)0xAD, (byte)0xF4,
            (byte)0xF6, (byte)0xF2, (byte)0xF3, (byte)0xF5,
            (byte)0x7D, (byte)0x4A, (byte)0x4B, (byte)0x4C,
            (byte)0x4D, (byte)0x4E, (byte)0x4F, (byte)0x50,
            (byte)0x51, (byte)0x52, (byte)0xB9, (byte)0xFB,
            (byte)0xFC, (byte)0xF9, (byte)0xFA, (byte)0xFF,
            (byte)0x5C, (byte)0xF7, (byte)0x53, (byte)0x54,
            (byte)0x55, (byte)0x56, (byte)0x57, (byte)0x58,
            (byte)0x59, (byte)0x5A, (byte)0xB2, (byte)0xD4,
            (byte)0xD6, (byte)0xD2, (byte)0xD3, (byte)0xD5,
            (byte)0x30, (byte)0x31, (byte)0x32, (byte)0x33,
            (byte)0x34, (byte)0x35, (byte)0x36, (byte)0x37,
            (byte)0x38, (byte)0x39, (byte)0xB3, (byte)0xDB,
            (byte)0xDC, (byte)0xD9, (byte)0xDA, (byte)0x9F
    };
    public static final byte[] ASCII2EBCDIC = new byte[] {
            (byte)0x0,  (byte)0x1,  (byte)0x2,  (byte)0x3,
            (byte)0x37, (byte)0x2D, (byte)0x2E, (byte)0x2F,
            (byte)0x16, (byte)0x5,  (byte)0x15, (byte)0xB,
            (byte)0xC,  (byte)0xD,  (byte)0xE,  (byte)0xF,
            (byte)0x10, (byte)0x11, (byte)0x12, (byte)0x13,
            (byte)0x3C, (byte)0x3D, (byte)0x32, (byte)0x26,
            (byte)0x18, (byte)0x19, (byte)0x3F, (byte)0x27,
            (byte)0x1C, (byte)0x1D, (byte)0x1E, (byte)0x1F,
            (byte)0x40, (byte)0x5A, (byte)0x7F, (byte)0x7B,
            (byte)0x5B, (byte)0x6C, (byte)0x50, (byte)0x7D,
            (byte)0x4D, (byte)0x5D, (byte)0x5C, (byte)0x4E,
            (byte)0x6B, (byte)0x60, (byte)0x4B, (byte)0x61,
            (byte)0xF0, (byte)0xF1, (byte)0xF2, (byte)0xF3,
            (byte)0xF4, (byte)0xF5, (byte)0xF6, (byte)0xF7,
            (byte)0xF8, (byte)0xF9, (byte)0x7A, (byte)0x5E,
            (byte)0x4C, (byte)0x7E, (byte)0x6E, (byte)0x6F,
            (byte)0x7C, (byte)0xC1, (byte)0xC2, (byte)0xC3,
            (byte)0xC4, (byte)0xC5, (byte)0xC6, (byte)0xC7,
            (byte)0xC8, (byte)0xC9, (byte)0xD1, (byte)0xD2,
            (byte)0xD3, (byte)0xD4, (byte)0xD5, (byte)0xD6,
            (byte)0xD7, (byte)0xD8, (byte)0xD9, (byte)0xE2,
            (byte)0xE3, (byte)0xE4, (byte)0xE5, (byte)0xE6,
            (byte)0xE7, (byte)0xE8, (byte)0xE9, (byte)0xAD,
            (byte)0xE0, (byte)0xBD, (byte)0x5F, (byte)0x6D,
            (byte)0x79, (byte)0x81, (byte)0x82, (byte)0x83,
            (byte)0x84, (byte)0x85, (byte)0x86, (byte)0x87,
            (byte)0x88, (byte)0x89, (byte)0x91, (byte)0x92,
            (byte)0x93, (byte)0x94, (byte)0x95, (byte)0x96,
            (byte)0x97, (byte)0x98, (byte)0x99, (byte)0xA2,
            (byte)0xA3, (byte)0xA4, (byte)0xA5, (byte)0xA6,
            (byte)0xA7, (byte)0xA8, (byte)0xA9, (byte)0xC0,
            (byte)0x4F, (byte)0xD0, (byte)0xA1, (byte)0x7,
            (byte)0x20, (byte)0x21, (byte)0x22, (byte)0x23,
            (byte)0x24, (byte)0x25, (byte)0x6,  (byte)0x17,
            (byte)0x28, (byte)0x29, (byte)0x2A, (byte)0x2B,
            (byte)0x2C, (byte)0x9,  (byte)0xA,  (byte)0x1B,
            (byte)0x30, (byte)0x31, (byte)0x1A, (byte)0x33,
            (byte)0x34, (byte)0x35, (byte)0x36, (byte)0x8,
            (byte)0x38, (byte)0x39, (byte)0x3A, (byte)0x3B,
            (byte)0x4,  (byte)0x14, (byte)0x3E, (byte)0xFF,
            (byte)0x41, (byte)0xAA, (byte)0x4A, (byte)0xB1,
            (byte)0x9F, (byte)0xB2, (byte)0x6A, (byte)0xB5,
            (byte)0xBB, (byte)0xB4, (byte)0x9A, (byte)0x8A,
            (byte)0xB0, (byte)0xCA, (byte)0xAF, (byte)0xBC,
            (byte)0x90, (byte)0x8F, (byte)0xEA, (byte)0xFA,
            (byte)0xBE, (byte)0xA0, (byte)0xB6, (byte)0xB3,
            (byte)0x9D, (byte)0xDA, (byte)0x9B, (byte)0x8B,
            (byte)0xB7, (byte)0xB8, (byte)0xB9, (byte)0xAB,
            (byte)0x64, (byte)0x65, (byte)0x62, (byte)0x66,
            (byte)0x63, (byte)0x67, (byte)0x9E, (byte)0x68,
            (byte)0x74, (byte)0x71, (byte)0x72, (byte)0x73,
            (byte)0x78, (byte)0x75, (byte)0x76, (byte)0x77,
            (byte)0xAC, (byte)0x69, (byte)0xED, (byte)0xEE,
            (byte)0xEB, (byte)0xEF, (byte)0xEC, (byte)0xBF,
            (byte)0x80, (byte)0xFD, (byte)0xFE, (byte)0xFB,
            (byte)0xFC, (byte)0xBA, (byte)0xAE, (byte)0x59,
            (byte)0x44, (byte)0x45, (byte)0x42, (byte)0x46,
            (byte)0x43, (byte)0x47, (byte)0x9C, (byte)0x48,
            (byte)0x54, (byte)0x51, (byte)0x52, (byte)0x53,
            (byte)0x58, (byte)0x55, (byte)0x56, (byte)0x57,
            (byte)0x8C, (byte)0x49, (byte)0xCD, (byte)0xCE,
            (byte)0xCB, (byte)0xCF, (byte)0xCC, (byte)0xE1,
            (byte)0x70, (byte)0xDD, (byte)0xDE, (byte)0xDB,
            (byte)0xDC, (byte)0x8D, (byte)0x8E, (byte)0xDF
    };
    public static final byte STX = 0x02;
    public static final byte FS  = 0x1C;
    public static final byte US  = 0x1F;
    public static final byte RS  = 0x1D;
    public static final byte GS  = 0x1E;
    public static final byte ETX = 0x03;

    public static String ebcdicToAscii(byte[] e) {
        try {
            return new String (
                    ebcdicToAsciiBytes (e, 0, e.length), "ISO8859_1"
            );
        } catch (UnsupportedEncodingException ex) {
            return ex.toString(); // should never happen
        }
    }
    public static String ebcdicToAscii(byte[] e, int offset, int len) {
        try {
            return new String (
                    ebcdicToAsciiBytes (e, offset, len), "ISO8859_1"
            );
        } catch (UnsupportedEncodingException ex) {
            return ex.toString(); // should never happen
        }
    }
    public static byte[] ebcdicToAsciiBytes (byte[] e) {
        return ebcdicToAsciiBytes (e, 0, e.length);
    }
    public static byte[] ebcdicToAsciiBytes(byte[] e, int offset, int len) {
        byte[] a = new byte[len];
        for (int i=0; i<len; i++)
            a[i] = EBCDIC2ASCII[e[offset+i]&0xFF];
        return a;
    }
    public static byte[] asciiToEbcdic(String s) {
        return asciiToEbcdic (s.getBytes());
    }
    public static byte[] asciiToEbcdic(byte[] a) {
        byte[] e = new byte[a.length];
        for (int i=0; i<a.length; i++)
            e[i] = ASCII2EBCDIC[a[i]&0xFF];
        return e;
    }
    public static void asciiToEbcdic(String s, byte[] e, int offset) {
        int len = s.length();
        for (int i=0; i<len; i++)
            e[offset + i] = ASCII2EBCDIC[s.charAt(i)&0xFF];
    }

    /**
     * pad to the left
     * @param s - original string
     * @param len - desired len
     * @param c - padding char
     * @return padded string
     */
    public static String padleft(String s, int len, char c)
            throws Exception
    {
        s = s.trim();
        if (s.length() > len)
            throw new Exception("invalid len " +s.length() + "/" +len);
        StringBuffer d = new StringBuffer (len);
        int fill = len - s.length();
        while (fill-- > 0)
            d.append (c);
        d.append(s);
        return d.toString();
    }

    /**
     * trim String (if not null)
     * @param s String to trim
     * @return String (may be null)
     */
    public static String trim (String s) {
        return s != null ? s.trim() : null;
    }

    /**
     * left pad with '0'
     * @param s - original string
     * @param len - desired len
     * @return zero padded string
     */
    public static String zeropad(String s, int len) throws Exception {
        return padleft(s, len, '0');
    }

    /**
     * pads to the right
     * @param s - original string
     * @param len - desired len
     * @return space padded string
     */
    public static String strpad(String s, int len) {
        StringBuffer d = new StringBuffer(s);
        while (d.length() < len)
            d.append(' ');
        return d.toString();
    }
    public static String zeropadRight (String s, int len) {
        StringBuffer d = new StringBuffer(s);
        while (d.length() < len)
            d.append('0');
        return d.toString();
    }
    /**
     * converts to BCD
     * @param s - the number
     * @param padLeft - flag indicating left/right padding
     * @param d The byte array to copy into.
     * @param offset Where to start copying into.
     * @return BCD representation of the number
     */
    public static byte[] str2bcd(String s, boolean padLeft, byte[] d, int offset) {
        int len = s.length();
        int start = (((len & 1) == 1) && padLeft) ? 1 : 0;
        for (int i=start; i < len+start; i++)
            d [offset + (i >> 1)] |= (s.charAt(i-start)-'0') << ((i & 1) == 1 ? 0 : 4);
        return d;
    }
    /**
     * converts to BCD
     * @param s - the number
     * @param padLeft - flag indicating left/right padding
     * @return BCD representation of the number
     */
    public static byte[] str2bcd(String s, boolean padLeft) {
        int len = s.length();
        byte[] d = new byte[ (len+1) >> 1 ];
        return str2bcd(s, padLeft, d, 0);
    }
    /**
     * converts to BCD
     * @param s - the number
     * @param padLeft - flag indicating left/right padding
     * @param fill - fill value
     * @return BCD representation of the number
     */
    public static byte[] str2bcd(String s, boolean padLeft, byte fill) {
        int len = s.length();
        byte[] d = new byte[ (len+1) >> 1 ];
        Arrays.fill (d, fill);
        int start = (((len & 1) == 1) && padLeft) ? 1 : 0;
        for (int i=start; i < len+start; i++)
            d [i >> 1] |= (s.charAt(i-start)-'0') << ((i & 1) == 1 ? 0 : 4);
        return d;
    }
    /**
     * converts a BCD representation of a number to a String
     * @param b - BCD representation
     * @param offset - starting offset
     * @param len - BCD field len
     * @param padLeft - was padLeft packed?
     * @return the String representation of the number
     */
    public static String bcd2str(byte[] b, int offset,
                                 int len, boolean padLeft)
    {
        StringBuffer d = new StringBuffer(len);
        int start = (((len & 1) == 1) && padLeft) ? 1 : 0;
        for (int i=start; i < len+start; i++) {
            int shift = ((i & 1) == 1 ? 0 : 4);
            char c = Character.forDigit (
                    ((b[offset+(i>>1)] >> shift) & 0x0F), 16);
            if (c == 'd')
                c = '=';
            d.append (Character.toUpperCase (c));
        }
        return d.toString();
    }
    /**
     * converts a byte array to hex string
     * (suitable for dumps and ASCII packaging of Binary fields
     * @param b - byte array
     * @return String representation
     */
    public static String hexString(byte[] b) {
        StringBuffer d = new StringBuffer(b.length * 2);
        for (int i=0; i<b.length; i++) {
            char hi = Character.forDigit ((b[i] >> 4) & 0x0F, 16);
            char lo = Character.forDigit (b[i] & 0x0F, 16);
            d.append(Character.toUpperCase(hi));
            d.append(Character.toUpperCase(lo));
        }
        return d.toString();
    }
    /**
     * converts a byte array to printable characters
     * @param b - byte array
     * @return String representation
     */
    public static String dumpString(byte[] b) {
        StringBuffer d = new StringBuffer(b.length * 2);
        for (int i=0; i<b.length; i++) {
            char c = (char) b[i];
            if (Character.isISOControl (c)) {
                // TODO: complete list of control characters,
                // use a String[] instead of this weird switch
                switch (c) {
                    case '\r'  : d.append ("{CR}");   break;
                    case '\n'  : d.append ("{LF}");   break;
                    case '\000': d.append ("{NULL}"); break;
                    case '\001': d.append ("{SOH}");  break;
                    case '\002': d.append ("{STX}");  break;
                    case '\003': d.append ("{ETX}");  break;
                    case '\004': d.append ("{EOT}");  break;
                    case '\005': d.append ("{ENQ}");  break;
                    case '\006': d.append ("{ACK}");  break;
                    case '\007': d.append ("{BEL}");  break;
                    case '\020': d.append ("{DLE}");  break;
                    case '\025': d.append ("{NAK}");  break;
                    case '\026': d.append ("{SYN}");  break;
                    case '\034': d.append ("{FS}");  break;
                    case '\036': d.append ("{RS}");  break;
                    default:
                        char hi = Character.forDigit ((b[i] >> 4) & 0x0F, 16);
                        char lo = Character.forDigit (b[i] & 0x0F, 16);
                        d.append('[');
                        d.append(Character.toUpperCase(hi));
                        d.append(Character.toUpperCase(lo));
                        d.append(']');
                        break;
                }
            }
            else
                d.append (c);

        }
        return d.toString();
    }
    /**
     * converts a byte array to hex string
     * (suitable for dumps and ASCII packaging of Binary fields
     * @param b - byte array
     * @param offset  - starting position
     * @param len
     * @return String representation
     */
    public static String hexString(byte[] b, int offset, int len) {
        StringBuffer d = new StringBuffer(len * 2);
        len += offset;
        for (int i=offset; i<len; i++) {
            char hi = Character.forDigit ((b[i] >> 4) & 0x0F, 16);
            char lo = Character.forDigit (b[i] & 0x0F, 16);
            d.append(Character.toUpperCase(hi));
            d.append(Character.toUpperCase(lo));
        }
        return d.toString();
    }

    /**
     * bit representation of a BitSet
     * suitable for dumps and debugging
     * @param b - the BitSet
     * @return string representing the bits (i.e. 011010010...)
     */
    public static String bitSet2String (BitSet b) {
        int len = b.size();
        len = (len > 128) ? 128: len;
        StringBuffer d = new StringBuffer(len);
        for (int i=0; i<len; i++)
            d.append (b.get(i) ? '1' : '0');
        return d.toString();
    }
    /**
     * converts a BitSet into a binary field
     * used in pack routines
     * @param b - the BitSet
     * @return binary representation
     */
    public static byte[] bitSet2byte (BitSet b)
    {
        int len = (((b.length()+62)>>6)<<6);
        byte[] d = new byte[len >> 3];
        for (int i=0; i<len; i++)
            if (b.get(i+1))
                d[i >> 3] |= (0x80 >> (i % 8));
        if (len>64)
            d[0] |= 0x80;
        if (len>128)
            d[8] |= 0x80;
        return d;
    }

    /**
     * converts a BitSet into a binary field
     * used in pack routines
     * @param b - the BitSet
     * @param bytes - number of bytes to return
     * @return binary representation
     */
    public static byte[] bitSet2byte (BitSet b, int bytes)
    {
        int len = bytes * 8;

        byte[] d = new byte[bytes];
        for (int i=0; i<len; i++)
            if (b.get(i+1))
                d[i >> 3] |= (0x80 >> (i % 8));
        if (len>64)
            d[0] |= 0x80;
        if (len>128)
            d[8] |= 0x80;
        return d;
    }

    /**
     * Converts a binary representation of a Bitmap field
     * into a Java BitSet
     * @param b - binary representation
     * @param offset - staring offset
     * @param bitZeroMeansExtended - true for ISO-8583
     * @return java BitSet object
     */
    public static BitSet byte2BitSet
    (byte[] b, int offset, boolean bitZeroMeansExtended)
    {
        int len = bitZeroMeansExtended ?
                ((b[offset] & 0x80) == 0x80 ? 128 : 64) : 64;
        BitSet bmap = new BitSet (len);
        for (int i=0; i<len; i++)
            if (((b[offset + (i >> 3)]) & (0x80 >> (i % 8))) > 0)
                bmap.set(i+1);
        return bmap;
    }
    /**
     * Converts a binary representation of a Bitmap field
     * into a Java BitSet
     * @param b - binary representation
     * @param offset - staring offset
     * @param maxBits - max number of bits (supports 64,128 or 192)
     * @return java BitSet object
     */
    public static BitSet byte2BitSet (byte[] b, int offset, int maxBits) {
        int len = maxBits > 64 ?
                ((b[offset] & 0x80) == 0x80 ? 128 : 64) : maxBits;

        if (maxBits > 128 &&
                b.length > offset+8 &&
                (b[offset+8] & 0x80) == 0x80)
        {
            len = 192;
        }
        BitSet bmap = new BitSet (len);
        for (int i=0; i<len; i++)
            if (((b[offset + (i >> 3)]) & (0x80 >> (i % 8))) > 0)
                bmap.set(i+1);
        return bmap;
    }

    /**
     * Converts a binary representation of a Bitmap field
     * into a Java BitSet
     * @param bmap - BitSet
     * @param b - hex representation
     * @param bitOffset - (i.e. 0 for primary bitmap, 64 for secondary)
     * @return java BitSet object
     */
    public static BitSet byte2BitSet (BitSet bmap, byte[] b, int bitOffset)
    {
        int len = b.length << 3;
        for (int i=0; i<len; i++)
            if (((b[i >> 3]) & (0x80 >> (i % 8))) > 0)
                bmap.set(bitOffset + i + 1);
        return bmap;
    }

    /**
     * Converts an ASCII representation of a Bitmap field
     * into a Java BitSet
     * @param b - hex representation
     * @param offset - starting offset
     * @param bitZeroMeansExtended - true for ISO-8583
     * @return java BitSet object
     */
    public static BitSet hex2BitSet
    (byte[] b, int offset, boolean bitZeroMeansExtended)
    {
        int len = bitZeroMeansExtended ?
                ((Character.digit((char)b[offset],16) & 0x08) == 8 ? 128 : 64) :
                64;
        BitSet bmap = new BitSet (len);
        for (int i=0; i<len; i++) {
            int digit = Character.digit((char)b[offset + (i >> 2)], 16);
            if ((digit & (0x08 >> (i%4))) > 0)
                bmap.set(i+1);
        }
        return bmap;
    }

    /**
     * Converts an ASCII representation of a Bitmap field
     * into a Java BitSet
     * @param b - hex representation
     * @param offset - starting offset
     * @param maxBits - max number of bits (supports 8, 16, 24, 32, 48, 52, 64,.. 128 or 192)
     * @return java BitSet object
     */
    public static BitSet hex2BitSet (byte[] b, int offset, int maxBits) {
        int len = maxBits > 64?
                ((Character.digit((char)b[offset],16) & 0x08) == 8 ? 128 : 64) :
                maxBits;
        BitSet bmap = new BitSet (len);
        for (int i=0; i<len; i++) {
            int digit = Character.digit((char)b[offset + (i >> 2)], 16);
            if ((digit & (0x08 >> (i%4))) > 0) {
                bmap.set(i+1);
                if (i==65 && maxBits > 128)
                    len = 192;
            }
        }
        return bmap;
    }

    /**
     * Converts an ASCII representation of a Bitmap field
     * into a Java BitSet
     * @param bmap - BitSet
     * @param b - hex representation
     * @param bitOffset - (i.e. 0 for primary bitmap, 64 for secondary)
     * @return java BitSet object
     */
    public static BitSet hex2BitSet (BitSet bmap, byte[] b, int bitOffset)
    {
        int len = b.length << 2;
        for (int i=0; i<len; i++) {
            int digit = Character.digit((char)b[i >> 2], 16);
            if ((digit & (0x08 >> (i%4))) > 0)
                bmap.set (bitOffset + i + 1);
        }
        return bmap;
    }

    /**
     * @param   b       source byte array
     * @param   offset  starting offset
     * @param   len     number of bytes in destination (processes len*2)
     * @return  byte[len]
     */
    public static byte[] hex2byte (byte[] b, int offset, int len) {
        byte[] d = new byte[len];
        for (int i=0; i<len*2; i++) {
            int shift = i%2 == 1 ? 0 : 4;
            d[i>>1] |= Character.digit((char) b[offset+i], 16) << shift;
        }
        return d;
    }
    /**
     * @param s source string (with Hex representation)
     * @return byte array
     */
    public static byte[] hex2byte (String s) {
        if (s.length() % 2 == 0) {
            return hex2byte (s.getBytes(), 0, s.length() >> 1);
        } else {
            throw new RuntimeException("Uneven number("+s.length()+") of hex digits passed to hex2byte.");
        }
    }

    /**
     * format double value
     * @param d    the double
     * @param len  the field len
     * @return a String of fieldLen characters (right justified)
     */
    public static String formatDouble(double d, int len) {
        String prefix = Long.toString((long) d);
        String suffix = Integer.toString (
                (int) ((Math.round(d * 100f)) % 100) );
        try {
            if (len > 3)
                prefix = ByteUtils.padleft(prefix,len-3,' ');
            suffix = ByteUtils.zeropad(suffix, 2);
        } catch (Exception e) {
            // should not happen
        }
        return prefix + "." + suffix;
    }
    /**
     * prepare long value used as amount for display
     * (implicit 2 decimals)
     * @param l value
     * @param len display len
     * @return formated field
     * @exception ISOException
     */
    public static String formatAmount(long l, int len) throws Exception {
        String buf = Long.toString(l);
        if (l < 100)
            buf = zeropad(buf, 3);
        StringBuffer s = new StringBuffer(padleft (buf, len-1, ' ') );
        s.insert(len-3, '.');
        return s.toString();
    }

    /**
     * XML normalizer
     * @param s source String
     * @param canonical true if we want to normalize \r and \n as well
     * @return normalized string suitable for XML Output
     */
    public static String normalize (String s, boolean canonical) {
        StringBuffer str = new StringBuffer();

        int len = (s != null) ? s.length() : 0;
        for (int i = 0; i < len; i++) {
            char ch = s.charAt(i);
            switch (ch) {
                case '<':
                    str.append("&lt;");
                    break;
                case '>':
                    str.append("&gt;");
                    break;
                case '&':
                    str.append("&amp;");
                    break;
                case '"':
                    str.append("&quot;");
                    break;
                case '\r':
                case '\n':
                    if (canonical) {
                        str.append("&#");
                        str.append(Integer.toString((int) (ch & 0xFF)));
                        str.append(';');
                        break;
                    }
                    // else, default append char
                default:
                    if (ch < 0x20) {
                        str.append("&#");
                        str.append(Integer.toString((int) (ch & 0xFF)));
                        str.append(';');
                    }
                    else if (ch > 0xff00) {
                        str.append((char) (ch & 0xFF));
                    } else
                        str.append(ch);
            }
        }
        return (str.toString());
    }
    /**
     * XML normalizer (default canonical)
     * @param s source String
     * @return normalized string suitable for XML Output
     */
    public static String normalize (String s) {
        return normalize (s, true);
    }
    /**
     * Protects PAN, Track2, CVC (suitable for logs).
     *
     * <pre>
     * "40000101010001" is converted to "400001____0001"
     * "40000101010001=020128375" is converted to "400001____0001=0201_____"
     * "123" is converted to "___"
     * </pre>
     * @param s string to be protected
     * @return 'protected' String
     */
    public static String protect (String s) {
        StringBuffer sb = new StringBuffer();
        int len   = s.length();
        int clear = len > 6 ? 6 : 0;
        int lastFourIndex = -1;
        if (clear > 0) {
            lastFourIndex = s.indexOf ('=') - 4;
            if (lastFourIndex < 0) {
                lastFourIndex = s.indexOf ('^') - 4;
                if (lastFourIndex < 0)
                    lastFourIndex = len - 4;
            }
        }
        for (int i=0; i<len; i++) {
            if (s.charAt(i) == '=')
                clear = 1;  // use clear=5 to keep the expiration date
            else if (s.charAt(i) == '^') {
                lastFourIndex = 0;
                clear = len - i;
            }
            else if (i == lastFourIndex)
                clear = 4;
            sb.append (clear-- > 0 ? s.charAt(i) : '_');
        }
        return sb.toString();
    }
    public static int[] toIntArray(String s) {
        StringTokenizer st = new StringTokenizer (s);
        int[] array = new int [st.countTokens()];
        for (int i=0; st.hasMoreTokens(); i++)
            array[i] = Integer.parseInt (st.nextToken());
        return array;
    }
    public static String[] toStringArray(String s) {
        StringTokenizer st = new StringTokenizer (s);
        String[] array = new String [st.countTokens()];
        for (int i=0; st.hasMoreTokens(); i++)
            array[i] = st.nextToken();
        return array;
    }
    /**
     * Bitwise XOR between corresponding bytes
     * @param op1 byteArray1
     * @param op2 byteArray2
     * @return an array of length = the smallest between op1 and op2
     */
    public static byte[] xor (byte[] op1, byte[] op2) {
        byte[] result = null;
        // Use the smallest array
        if (op2.length > op1.length) {
            result = new byte[op1.length];
        }
        else {
            result = new byte[op2.length];
        }
        for (int i = 0; i < result.length; i++) {
            result[i] = (byte)(op1[i] ^ op2[i]);
        }
        return  result;
    }
    /**
     * Bitwise XOR between corresponding byte arrays represented in hex
     * @param op1 hexstring 1
     * @param op2 hexstring 2
     * @return an array of length = the smallest between op1 and op2
     */
    public static String hexor (String op1, String op2) {
        byte[] xor = xor (hex2byte (op1), hex2byte (op2));
        return hexString (xor);
    }

    /**
     * Trims a byte[] to a certain length
     * @param array the byte[] to be trimmed
     * @param length the wanted length
     * @return the trimmed byte[]
     */
    public static byte[] trim (byte[] array, int length) {
        byte[] trimmedArray = new byte[length];
        System.arraycopy(array, 0, trimmedArray, 0, length);
        return  trimmedArray;
    }

    /**
     * Concatenates two byte arrays (array1 and array2)
     * @param array1
     * @param array2
     * @return the concatenated array
     */
    public static byte[] concat (byte[] array1, byte[] array2) {
        byte[] concatArray = new byte[array1.length + array2.length];
        System.arraycopy(array1, 0, concatArray, 0, array1.length);
        System.arraycopy(array2, 0, concatArray, array1.length, array2.length);
        return  concatArray;
    }

    /**
     * Concatenates two byte arrays (array1 and array2)
     * @param array1
     * @param beginIndex1
     * @param length1
     * @param array2
     * @param beginIndex2
     * @param length2
     * @return the concatenated array
     */
    public static byte[] concat (byte[] array1, int beginIndex1, int length1, byte[] array2,
                                 int beginIndex2, int length2) {
        byte[] concatArray = new byte[length1 + length2];
        System.arraycopy(array1, beginIndex1, concatArray, 0, length1);
        System.arraycopy(array2, beginIndex2, concatArray, length1, length2);
        return  concatArray;
    }
    /**
     * Causes the currently executing thread to sleep (temporarily cease
     * execution) for the specified number of milliseconds. The thread
     * does not lose ownership of any monitors.
     *
     * This is the same as Thread.sleep () without throwing InterruptedException
     *
     * @param      millis   the length of time to sleep in milliseconds.
     */
    public static void sleep (long millis) {
        try {
            Thread.sleep (millis);
        } catch (InterruptedException e) { }
    }

    /**
     * Left unPad with '0'
     * @param s - original string
     * @return zero unPadded string
     */
    public static String zeroUnPad( String s ) {
        return unPadLeft(s, '0');
    }
    /**
     * Right unPad with ' '
     * @param s - original string
     * @return blank unPadded string
     */
    public static String blankUnPad( String s ) {
        return unPadRight(s, ' ');
    }

    /**
     * Unpad from right.
     * @param s - original string
     * @param c - padding char
     * @return unPadded string.
     */
    public static String unPadRight(String s, char c) {
        int end = s.length();
        if (end == 0)
            return s;
        while ( ( 0 < end) && (s.charAt(end-1) == c) ) end --;
        return ( 0 < end )? s.substring( 0, end ): s.substring( 0, 1 );
    }

    /**
     * Unpad from left.
     * @param s - original string
     * @param c - padding char
     * @return unPadded string.
     */
    public static String unPadLeft(String s, char c) {
        int fill = 0, end = s.length();
        if (end == 0)
            return s;
        while ( (fill < end) && (s.charAt(fill) == c) ) fill ++;
        return ( fill < end )? s.substring( fill, end ): s.substring( fill-1, end );
    }

    /**
     * @return true if the string is zero-filled ( 0 char filled )
     **/
    public static boolean isZero( String s ) {
        int i = 0, len = s.length();
        while ( i < len && ( s.charAt( i ) == '0' ) ){
            i++;
        }
        return ( i >= len );
    }

    /**
     * @return true if the string is blank filled (space char filled)
     */
    public static boolean isBlank( String s ){
        return (s.trim().length() == 0);
    }

    /**
     * Return true if the string is alphanum.
     * <code>{letter digit (.) (_) (-) ( ) (?) }</code>
     *
     **/
    public static boolean isAlphaNumeric ( String s ) {
        int i = 0, len = s.length();
        while ( i < len && ( Character.isLetterOrDigit( s.charAt( i ) ) ||
                s.charAt( i ) == ' ' || s.charAt( i ) == '.' ||
                s.charAt( i ) == '-' || s.charAt( i ) == '_' )
                || s.charAt( i ) == '?' ){
            i++;
        }
        return ( i >= len );
    }

    /**
     * Return true if the string represent a number
     * in the specified radix.
     * <br><br>
     **/
    public static boolean isNumeric ( String s, int radix ) {
        int i = 0, len = s.length();
        while ( i < len && Character.digit( s.charAt( i ), radix ) > -1  ){
            i++;
        }
        return ( i >= len );
    }

    /**
     * Converts a BitSet into an extended binary field
     * used in pack routines. The result is always in the
     * extended format: (16 bytes of length)
     * <br><br>
     * @param b the BitSet
     * @return binary representation
     */
    public static byte[] bitSet2extendedByte ( BitSet b ){
        int len = 128;
        byte[] d = new byte[len >> 3];
        for ( int i=0; i<len; i++ )
            if (b.get(i+1))
                d[i >> 3] |= (0x80 >> (i % 8));
        d[0] |= 0x80;
        return d;
    }

    /**
     * Converts a String to an integer of base radix.
     * <br><br>
     * String constraints are:
     * <li>Number must be less than 10 digits</li>
     * <li>Number must be positive</li>
     * @param s String representation of number
     * @param radix Number base to use
     * @return integer value of number
     * @throws NumberFormatException
     */
    public static int parseInt (String s, int radix) throws NumberFormatException {
        int length = s.length();
        if (length > 9)
            throw new NumberFormatException ("Number can have maximum 9 digits");
        int result = 0;
        int index = 0;
        int digit = Character.digit (s.charAt(index++), radix);
        if (digit == -1)
            throw new NumberFormatException ("String contains non-digit");
        result = digit;
        while (index < length) {
            result *= radix;
            digit = Character.digit (s.charAt(index++), radix);
            if (digit == -1)
                throw new NumberFormatException ("String contains non-digit");
            result += digit;
        }
        return result;
    }

    /**
     * Converts a String to an integer of radix 10.
     * <br><br>
     * String constraints are:
     * <li>Number must be less than 10 digits</li>
     * <li>Number must be positive</li>
     * @param s String representation of number
     * @return integer value of number
     * @throws NumberFormatException
     */
    public static int parseInt (String s) throws NumberFormatException {
        return parseInt (s, 10);
    }

    /**
     * Converts a character array to an integer of base radix.
     * <br><br>
     * Array constraints are:
     * <li>Number must be less than 10 digits</li>
     * <li>Number must be positive</li>
     * @param cArray Character Array representation of number
     * @param radix Number base to use
     * @return integer value of number
     * @throws NumberFormatException
     */
    public static int parseInt (char[] cArray, int radix) throws NumberFormatException {
        int length = cArray.length;
        if (length > 9)
            throw new NumberFormatException ("Number can have maximum 9 digits");
        int result = 0;
        int index = 0;
        int digit = Character.digit(cArray[index++], radix);
        if (digit == -1)
            throw new NumberFormatException ("Char array contains non-digit");
        result = digit;
        while (index < length) {
            result *= radix;
            digit = Character.digit(cArray[index++],radix);
            if (digit == -1)
                throw new NumberFormatException ("Char array contains non-digit");
            result += digit;
        }
        return result;
    }

    /**
     * Converts a character array to an integer of radix 10.
     * <br><br>
     * Array constraints are:
     * <li>Number must be less than 10 digits</li>
     * <li>Number must be positive</li>
     * @param cArray Character Array representation of number
     * @return integer value of number
     * @throws NumberFormatException
     */
    public static int parseInt (char[] cArray) throws NumberFormatException {
        return parseInt (cArray,10);
    }

    /**
     * Converts a byte array to an integer of base radix.
     * <br><br>
     * Array constraints are:
     * <li>Number must be less than 10 digits</li>
     * <li>Number must be positive</li>
     * @param bArray Byte Array representation of number
     * @param radix Number base to use
     * @return integer value of number
     * @throws NumberFormatException
     */
    public static int parseInt (byte[] bArray, int radix) throws NumberFormatException {
        int length = bArray.length;
        if (length > 9)
            throw new NumberFormatException ("Number can have maximum 9 digits");
        int result = 0;
        int index = 0;
        int digit = Character.digit((char)bArray[index++], radix);
        if (digit == -1)
            throw new NumberFormatException ("Byte array contains non-digit");
        result = digit;
        while (index < length) {
            result *= radix;
            digit = Character.digit((char)bArray[index++],radix);
            if (digit == -1)
                throw new NumberFormatException ("Byte array contains non-digit");
            result += digit;
        }
        return result;
    }

    /**
     * Converts a byte array to an integer of radix 10.
     * <br><br>
     * Array constraints are:
     * <li>Number must be less than 10 digits</li>
     * <li>Number must be positive</li>
     * @param bArray Byte Array representation of number
     * @return integer value of number
     * @throws NumberFormatException
     */
    public static int parseInt (byte[] bArray) throws NumberFormatException {
        return parseInt (bArray,10);
    }
    private static String hexOffset (int i) {
        i = (i>>4) << 4;
        int w = i > 0xFFFF ? 8 : 4;
        try {
            return zeropad (Integer.toString (i, 16), w);
        } catch (Exception e) {
            // should not happen
            return e.getMessage();
        }
    }

    /**
     * @param b a byte[] buffer
     * @return hexdump
     */
    public static String hexdump (byte[] b) {
        return hexdump (b, 0, b.length);
    }
    /**
     * @param b a byte[] buffer
     * @param offset starting offset
     * @param len the Length
     * @return hexdump
     */
    public static String hexdump (byte[] b, int offset, int len) {
        StringBuffer sb    = new StringBuffer ();
        StringBuffer hex   = new StringBuffer ();
        StringBuffer ascii = new StringBuffer ();
        String sep         = "  ";
        String lineSep     = System.getProperty ("line.separator");

        for (int i=offset; i<len; i++) {
            char hi = Character.forDigit ((b[i] >> 4) & 0x0F, 16);
            char lo = Character.forDigit (b[i] & 0x0F, 16);
            hex.append (Character.toUpperCase(hi));
            hex.append (Character.toUpperCase(lo));
            hex.append (' ');
            char c = (char) b[i];
            ascii.append ((c >= 32 && c < 127) ? c : '.');

            int j = i % 16;
            switch (j) {
                case 7 :
                    hex.append (' ');
                    break;
                case 15 :
                    sb.append (hexOffset (i));
                    sb.append (sep);
                    sb.append (hex.toString());
                    sb.append (' ');
                    sb.append (ascii.toString());
                    sb.append (lineSep);
                    hex   = new StringBuffer ();
                    ascii = new StringBuffer ();
                    break;
            }
        }
        if (hex.length() > 0) {
            while (hex.length () < 49)
                hex.append (' ');

            sb.append (hexOffset (len));
            sb.append (sep);
            sb.append (hex.toString());
            sb.append (' ');
            sb.append (ascii.toString());
            sb.append (lineSep);
        }
        return sb.toString();
    }
    /**
     * pads a string with 'F's (useful for pinoffset management)
     * @param s an [hex]string
     * @param len desired length
     * @return string right padded with 'F's
     */
    public static String strpadf (String s, int len) {
        StringBuffer d = new StringBuffer(s);
        while (d.length() < len)
            d.append('F');
        return d.toString();
    }
    /**
     * reverse the effect of strpadf
     * @param s F padded string
     * @return trimmed string
     */
    public static String trimf (String s) {
        if (s != null) {
            int l = s.length();
            if (l > 0) {
                while (--l >= 0) {
                    if (s.charAt (l) != 'F')
                        break;
                }
                s = l == 0 ? "" : s.substring (0, l+1);
            }
        }
        return s;
    }

    /**
     * 将byte数据转化为整数
     *
     * @param byte0
     *            待转化的单字节数据
     * @return 转化后的整数
     */
    public static int byteToInt(byte byte0) {
        return (0x00FF & byte0);
    }

    /**
     * 将byte数组数据转化为整数
     *
     * @param abyte
     *            四位byte数组
     * @return 转化后的整数
     */
    public static int bytes2ToInt(byte[] abyte) {
        int i = (0xff & abyte[0]) << 8 | abyte[1];
        if(i<0) {
            i = i+256;
        }
        return i;
    }

    public static int bytes2ToInt_2(byte[] abyte) {
        int i = abyte[1] & 0xFF |  (abyte[0] & 0xFF) << 8 ;
        return i;
    }

    /**
     * 将四位byte数据转化为整数
     *
     * @param abyte
     *            四位byte数组
     * @return 转化后的整数
     */
    public static int bytes4ToInt(byte[] abyte) {
        int i = (0xff & abyte[0]) << 24 | (0xff & abyte[1]) << 16
                | (0xff & abyte[2]) << 8 | 0xff & abyte[3];
        return i;
    }

    public static int lbytes4ToInt(byte[] abyte) {
        int i = (0xff & abyte[3]) << 24 | (0xff & abyte[2]) << 16
                | (0xff & abyte[1]) << 8 | 0xff & abyte[0];
        return i;
    }

    /**
     * 将四位byte数据转化为长整数
     *
     * @param abyte
     *            四位byte数组
     * @return 转化后的长整数
     */
    public static long bytes4ToLong(byte[] abyte) {
        long l = (0xff & abyte[0]) << 24 | (0xff & abyte[1]) << 16
                | (0xff & abyte[2]) << 8 | 0xff & abyte[3];
        return l;
    }

    /**
     * 将八位byte数据转化为长整数
     *
     * @param abyte
     *            八位byte数组
     * @return 转化后的长整数
     */
    public static long bytes8ToLong(byte[] abyte) {
        long l = ((long) (0xff & abyte[0]) << 56)
                | ((long) (0xff & abyte[1]) << 48)
                | ((long) (0xff & abyte[2]) << 40)
                | ((long) (0xff & abyte[3]) << 32)
                | ((long) (0xff & abyte[4]) << 24)
                | ((long) (0xff & abyte[5]) << 16)
                | ((long) (0xff & abyte[6]) << 8) | (long) (0xff & abyte[7]);
        return l;
    }

    /**
     * 将整数转化为一位byte
     * @param i 待转化的整数
     * @return 转化后的byte
     */
    public static byte intToByte(int i) {
        return (byte) i;
    }

    /**
     * 将整数转化为byte两位数组
     *
     * @param i
     *            待转化的整数
     * @return 转化后的byte数组
     */
    public static byte[] intTo2Bytes(int i) {
        byte[] abyte = new byte[2];
        abyte[1] = (byte) (0xff & i);
        abyte[0] = (byte) ((0xff00 & i) >> 8);
        return abyte;
    }

    /**
     * 高低位反转
     * @param i
     * @return
     */
    public static byte[] intTo2Bytes_2(int i) {
        byte[] abyte = new byte[2];
        abyte[0] = (byte) (0xff & i);
        abyte[1] = (byte) ((0xff00 & i) >> 8);
        return abyte;
    }


    /**
     * 将整数转化为1位byte数组
     *
     * @param i
     *            待转化的整数
     * @return 存储转化结果的1位byte数组
     */
    public static byte[] intToByte1(int i) {
        byte[] abyte = new byte[1];
        abyte[0] = (byte) i;
        return abyte;
    }

    /**
     * 将整数转化为四位byte数据
     *
     * @param i
     *            待转化的整数
     * @return 四位byte数组
     */
    public static byte[] intTo4Bytes(int i) {
        byte[] abyte = new byte[4];
        abyte[3] = (byte) (0xff & i);
        abyte[2] = (byte) ((0xff00 & i) >> 8);
        abyte[1] = (byte) ((0xff0000 & i) >> 16);
        abyte[0] = (byte) ((0xff000000 & i) >> 24);
        return abyte;
    }

    /**
     * 将整数转化为四位byte数据
     *
     * @param i
     *            待转化的整数
     * @param abyte
     *            存储转化结果的四位byte数组
     */
    public static void intTo4Bytes(int i, byte[] abyte) {
        abyte[3] = (byte) (0xff & i);
        abyte[2] = (byte) ((0xff00 & i) >> 8);
        abyte[1] = (byte) ((0xff0000 & i) >> 16);
        abyte[0] = (byte) ((0xff000000 & i) >> 24);
    }

    /**
     * 将长整数转化为八位byte数据
     *
     * @param l
     *            待转化的长整数
     * @return 存储转化结果的八位byte数组
     */
    public static byte[] longTo8Bytes(long l) {
        byte[] abyte = new byte[8];
        abyte[7] = (byte) (int) (255L & l);
        abyte[6] = (byte) (int) ((65280L & l) >> 8);
        abyte[5] = (byte) (int) ((0xff0000L & l) >> 16);
        abyte[4] = (byte) (int) ((0xffffffffff000000L & l) >> 24);
        int i = (int) (l >> 32);
        abyte[3] = (byte) (0xff & i);
        abyte[2] = (byte) ((0xff00 & i) >> 8);
        abyte[1] = (byte) ((0xff0000 & i) >> 16);
        abyte[0] = (byte) ((0xff000000 & i) >> 24);
        return abyte;
    }

    /**
     * 将长整形转化为四位byte数据
     *
     * @param l
     *            待转化的长整数
     * @return 存储转化结果的四位byte数组
     */
    public static byte[] longTo4Bytes(long l) {
        byte[] abyte = new byte[4];
        abyte[3] = (byte) (int) (255L & l);
        abyte[2] = (byte) (int) ((65280L & l) >> 8);
        abyte[1] = (byte) (int) ((0xff0000L & l) >> 16);
        abyte[0] = (byte) (int) ((0xffffffffff000000L & l) >> 24);
        return abyte;
    }

    public static double bytes2double(byte[] bs, int off){
        double d;
        long l=0;

        for(int n=0;n<8;n++){
            l += (((long)bs[off+7-n])& 0xFF) <<(n)*8;
        }

        d = Double.longBitsToDouble(l);
        return d;
    }

    public static String byte2bit(byte b, int off) {
        return ""+(byte) ((b >> off) & 0x1);
    }

    public static byte getLRC(byte[] data, int pos, int len) {
        byte lrc = 0;
        lrc = data[pos];
        for (int i = pos + 1; i < len; i++) {
            lrc = (byte) (lrc ^ data[i]);
        }
        return lrc;
    }

    /**
     * 将低字节数组转换为int
     *
     * @param b
     *            byte[]
     * @return int
     */
    public static long l4BytesToLong(byte[] b) {
        long s = 0;
        for (int i = 0; i < 3; i++) {
            if (b[3 - i] >= 0) {
                s = s + b[3 - i];
            } else {
                s = s + 256 + b[3 - i];
            }
            s = s * 256;
        }
        if (b[0] >= 0) {
            s = s + b[0];
        } else {
            s = s + 256 + b[0];
        }
        return s;
    }

    /**
     * 将高字节数组转换为int
     *
     * @param b
     *            byte[]
     * @return int
     */
    public static long h4BytesToLong(byte[] b) {
        long s = 0;
        for (int i = 0; i < 3; i++) {
            if (b[i] >= 0) {
                s = s + b[i];
            } else {
                s = s + 256 + b[i];
            }
            s = s * 256;
        }
        if (b[3] >= 0) {
            s = s + b[3];
        } else {
            s = s + 256 + b[3];
        }
        return s;
    }
}

5、Sm4Utils

import java.io.UnsupportedEncodingException;
import org.apache.commons.lang3.StringUtils;

/**
 * SM4 是国家密码管理局于 2010 年发布的一种国产对称密码算法,它采用了 128 位分组密码,其安全性已被国际公认。
 * SM4 算法的主要特点是安全性高、效率高、易于实现,因此被广泛应用于电子商务、电子政务、金融等领域。
 *
 * SM4 算法由以下几个部分组成:
 * * 初始化:SM4 算法使用一个 128 位的初始向量 IV 进行初始化。
 * * 加密:SM4 算法使用一个 128 位的密钥 K 进行加密。
 * * 解密:SM4 算法使用相同的密钥 K 进行解密。
 *
 */
public class Sm4Utils {
   public static String encryptBase64(String content, String secretKey) {
      byte[] enc = sm4Enc(content, secretKey);
      return Base64.encode(enc);
   }

   public static String decyptBase64(String content, String secretKey) {
      byte[] dec = Base64.decode(content);

      try {
         return (new String(sm4Dec(dec, secretKey), "utf-8")).replaceAll("\\u0000", "");
      } catch (UnsupportedEncodingException var4) {
         var4.printStackTrace();
         return null;
      }
   }

   public static boolean checkSign(String cipherText, String paramStr, String secretKey) {
      boolean flag = false;

      try {
         String str = encryptBase64(paramStr, secretKey);
         if (cipherText.equals(str)) {
            flag = true;
         }
      } catch (Exception var5) {
         System.out.println("验签异常: " + var5);
      }

      return flag;
   }

   private static byte[] sm4Enc(String content, String secretKey) {
      try {
         if (StringUtils.isEmpty(content)) {
            return null;
         } else {
            SM4Context ctx = new SM4Context();
            ctx.isPadding = false;
            ctx.mode = 1;
            byte[] key = ByteUtils.hex2byte(secretKey);
            byte[] plaintext = content.getBytes("utf-8");
            SM4.sm4_setkey_enc(ctx, key);
            byte[] encrypted = SM4.sm4_crypt_ecb(ctx, plaintext);
            return encrypted;
         }
      } catch (Exception var6) {
         System.out.println("加密报文失败: " + var6);
         return null;
      }
   }

   private static byte[] sm4Dec(byte[] content, String secretKey) {
      try {
         if (content != null && content.length > 0) {
            SM4Context ctx = new SM4Context();
            ctx.isPadding = false;
            ctx.mode = 0;
            byte[] key = ByteUtils.hex2byte(secretKey);
            SM4.sm4_setkey_dec(ctx, key);
            byte[] encrypted = SM4.sm4_crypt_ecb(ctx, content);
            return encrypted;
         } else {
            return null;
         }
      } catch (Exception var6) {
         System.out.println("解密报文失败:" + var6);
         return null;
      }
   }

   public static void main(String[] args) {
      String DEFAULT_KEY = "715ba3d7b63f413aad7bd6d83b3787c0";
      String content = "SM4 Test";

      String encTxt = encryptBase64(content, DEFAULT_KEY);
      System.out.println("密文:" + encTxt);

      String decTxt = decyptBase64(encTxt, DEFAULT_KEY);
      System.out.println("明文:" + decTxt);

      System.out.println("验签结果:" + checkSign(encTxt, content, DEFAULT_KEY));

   }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值