在工具类中经常使用到工具类的格式化描述,这个主要是一个日期的操作类,所以日志格式主要使用 SimpleDateFormat的定义格式.
+ * 格式的意义如下: 日期和时间模式 日期和时间格式由日期和时间模式字符串指定。在日期和时间模式字符串中,未加引号的字yy-MM-dd HH:mm母 'A' 到 'Z' 和 'a' 到 'z'
+ * 被解释为模式字母,用来表示日期或时间字符串元素。文本可以使用单引号 (') 引起来,以免进行解释。"''"
+ * 表示单引号。所有其他字符均不解释;只是在格式化时将它们简单复制到输出字符串,或者在分析时与输入字符串进行匹配。
+ *
+ * 定义了以下模式字母(所有其他字符 'A' 到 'Z' 和 'a' 到 'z' 都被保留): }
+ */
+
+ private static final DateFormat DEFAULT_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
+
+ private TimeUtils() {
+ throw new UnsupportedOperationException("u can't instantiate me...");
+ }
+
+ /**
+ * 将时间戳转为时间字符串
+ * 格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param millis 毫秒时间戳
+ * @return 时间字符串
+ */
+ public static String millis2String(final long millis) {
+ return millis2String(millis, DEFAULT_FORMAT);
+ }
+
+ /**
+ * 将时间戳转为时间字符串
+ * 格式为format
+ *
+ * @param millis 毫秒时间戳
+ * @param format 时间格式
+ * @return 时间字符串
+ */
+ public static String millis2String(final long millis, final DateFormat format) {
+ return format.format(new Date(millis));
+ }
+
+ /**
+ * 将时间字符串转为时间戳
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 时间字符串
+ * @return 毫秒时间戳
+ */
+ public static long string2Millis(final String time) {
+ return string2Millis(time, DEFAULT_FORMAT);
+ }
+
+ /**
+ * 将时间字符串转为时间戳
+ * time格式为format
+ *
+ * @param time 时间字符串
+ * @param format 时间格式
+ * @return 毫秒时间戳
+ */
+ public static long string2Millis(final String time, final DateFormat format) {
+ try {
+ return format.parse(time).getTime();
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ return -1;
+ }
+
+ /**
+ * 将时间字符串转为Date类型
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 时间字符串
+ * @return Date类型
+ */
+ public static Date string2Date(final String time) {
+ return string2Date(time, DEFAULT_FORMAT);
+ }
+
+ /**
+ * 将时间字符串转为Date类型
+ * time格式为format
+ *
+ * @param time 时间字符串
+ * @param format 时间格式
+ * @return Date类型
+ */
+ public static Date string2Date(final String time, final DateFormat format) {
+ try {
+ return format.parse(time);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 将Date类型转为时间字符串
+ * 格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param date Date类型时间
+ * @return 时间字符串
+ */
+ public static String date2String(final Date date) {
+ return date2String(date, DEFAULT_FORMAT);
+ }
+
+ /**
+ * 将Date类型转为时间字符串
+ * 格式为format
+ *
+ * @param date Date类型时间
+ * @param format 时间格式
+ * @return 时间字符串
+ */
+ public static String date2String(final Date date, final DateFormat format) {
+ return format.format(date);
+ }
+
+ /**
+ * 将Date类型转为时间戳
+ *
+ * @param date Date类型时间
+ * @return 毫秒时间戳
+ */
+ public static long date2Millis(final Date date) {
+ return date.getTime();
+ }
+
+ /**
+ * 将时间戳转为Date类型
+ *
+ * @param millis 毫秒时间戳
+ * @return Date类型时间
+ */
+ public static Date millis2Date(final long millis) {
+ return new Date(millis);
+ }
+
+ /**
+ * 获取两个时间差(单位:unit)
+ * time0和time1格式都为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time0 时间字符串0
+ * @param time1 时间字符串1
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return unit时间戳
+ */
+ public static long getTimeSpan(final String time0, final String time1, @TimeConstants.Unit final int unit) {
+ return getTimeSpan(time0, time1, DEFAULT_FORMAT, unit);
+ }
+
+ /**
+ * 获取两个时间差(单位:unit)
+ * time0和time1格式都为format
+ *
+ * @param time0 时间字符串0
+ * @param time1 时间字符串1
+ * @param format 时间格式
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return unit时间戳
+ */
+ public static long getTimeSpan(final String time0, final String time1, final DateFormat format, @TimeConstants.Unit final int unit) {
+ return millis2TimeSpan(Math.abs(string2Millis(time0, format) - string2Millis(time1, format)), unit);
+ }
+
+ /**
+ * 获取两个时间差(单位:unit)
+ *
+ * @param date0 Date类型时间0
+ * @param date1 Date类型时间1
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return unit时间戳
+ */
+ public static long getTimeSpan(final Date date0, final Date date1, @TimeConstants.Unit final int unit) {
+ return millis2TimeSpan(Math.abs(date2Millis(date0) - date2Millis(date1)), unit);
+ }
+
+ /**
+ * 获取两个时间差(单位:unit)
+ *
+ * @param millis0 毫秒时间戳0
+ * @param millis1 毫秒时间戳1
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return unit时间戳
+ */
+ public static long getTimeSpan(final long millis0, final long millis1, @TimeConstants.Unit final int unit) {
+ return millis2TimeSpan(Math.abs(millis0 - millis1), unit);
+ }
+
+ /**
+ * 获取合适型两个时间差
+ * time0和time1格式都为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time0 时间字符串0
+ * @param time1 时间字符串1
+ * @param precision 精度
+ * precision = 0,返回null
+ * precision = 1,返回天
+ * precision = 2,返回天和小时
+ * precision = 3,返回天、小时和分钟
+ * precision = 4,返回天、小时、分钟和秒
+ * precision >= 5,返回天、小时、分钟、秒和毫秒
+ * @return 合适型两个时间差
+ */
+ public static String getFitTimeSpan(final String time0, final String time1, final int precision) {
+ return millis2FitTimeSpan(Math.abs(string2Millis(time0, DEFAULT_FORMAT) - string2Millis(time1, DEFAULT_FORMAT)), precision);
+ }
+
+ /**
+ * 获取合适型两个时间差
+ * time0和time1格式都为format
+ *
+ * @param time0 时间字符串0
+ * @param time1 时间字符串1
+ * @param format 时间格式
+ * @param precision 精度
+ * precision = 0,返回null
+ * precision = 1,返回天
+ * precision = 2,返回天和小时
+ * precision = 3,返回天、小时和分钟
+ * precision = 4,返回天、小时、分钟和秒
+ * precision >= 5,返回天、小时、分钟、秒和毫秒
+ * @return 合适型两个时间差
+ */
+ public static String getFitTimeSpan(final String time0, final String time1, final DateFormat format, final int precision) {
+ return millis2FitTimeSpan(Math.abs(string2Millis(time0, format) - string2Millis(time1, format)), precision);
+ }
+
+ /**
+ * 获取合适型两个时间差
+ *
+ * @param date0 Date类型时间0
+ * @param date1 Date类型时间1
+ * @param precision 精度
+ * precision = 0,返回null
+ * precision = 1,返回天
+ * precision = 2,返回天和小时
+ * precision = 3,返回天、小时和分钟
+ * precision = 4,返回天、小时、分钟和秒
+ * precision >= 5,返回天、小时、分钟、秒和毫秒
+ * @return 合适型两个时间差
+ */
+ public static String getFitTimeSpan(final Date date0, final Date date1, final int precision) {
+ return millis2FitTimeSpan(Math.abs(date2Millis(date0) - date2Millis(date1)), precision);
+ }
+
+ /**
+ * 获取合适型两个时间差
+ *
+ * @param millis0 毫秒时间戳1
+ * @param millis1 毫秒时间戳2
+ * @param precision 精度
+ * precision = 0,返回null
+ * precision = 1,返回天
+ * precision = 2,返回天和小时
+ * precision = 3,返回天、小时和分钟
+ * precision = 4,返回天、小时、分钟和秒
+ * precision >= 5,返回天、小时、分钟、秒和毫秒
+ * @return 合适型两个时间差
+ */
+ public static String getFitTimeSpan(final long millis0, final long millis1, final int precision) {
+ return millis2FitTimeSpan(Math.abs(millis0 - millis1), precision);
+ }
+
+ /**
+ * 获取当前毫秒时间戳
+ *
+ * @return 毫秒时间戳
+ */
+ public static long getNowMills() {
+ return System.currentTimeMillis();
+ }
+
+ /**
+ * 获取当前时间字符串
+ * 格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @return 时间字符串
+ */
+ public static String getNowString() {
+ return millis2String(System.currentTimeMillis(), DEFAULT_FORMAT);
+ }
+
+ /**
+ * 获取当前时间字符串
+ * 格式为format
+ *
+ * @param format 时间格式
+ * @return 时间字符串
+ */
+ public static String getNowString(final DateFormat format) {
+ return millis2String(System.currentTimeMillis(), format);
+ }
+
+ /**
+ * 获取当前Date
+ *
+ * @return Date类型时间
+ */
+ public static Date getNowDate() {
+ return new Date();
+ }
+
+ /**
+ * 获取与当前时间的差(单位:unit)
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 时间字符串
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return unit时间戳
+ */
+ public static long getTimeSpanByNow(final String time, @TimeConstants.Unit final int unit) {
+ return getTimeSpan(getNowString(), time, DEFAULT_FORMAT, unit);
+ }
+
+ /**
+ * 获取与当前时间的差(单位:unit)
+ * time格式为format
+ *
+ * @param time 时间字符串
+ * @param format 时间格式
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return unit时间戳
+ */
+ public static long getTimeSpanByNow(final String time, final DateFormat format, @TimeConstants.Unit final int unit) {
+ return getTimeSpan(getNowString(format), time, format, unit);
+ }
+
+ /**
+ * 获取与当前时间的差(单位:unit)
+ *
+ * @param date Date类型时间
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return unit时间戳
+ */
+ public static long getTimeSpanByNow(final Date date, @TimeConstants.Unit final int unit) {
+ return getTimeSpan(new Date(), date, unit);
+ }
+
+ /**
+ * 获取与当前时间的差(单位:unit)
+ *
+ * @param millis 毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return unit时间戳
+ */
+ public static long getTimeSpanByNow(final long millis, @TimeConstants.Unit final int unit) {
+ return getTimeSpan(System.currentTimeMillis(), millis, unit);
+ }
+
+ /**
+ * 获取合适型与当前时间的差
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 时间字符串
+ * @param precision 精度
+ *
+ * precision = 0,返回null
+ * precision = 1,返回天
+ * precision = 2,返回天和小时
+ * precision = 3,返回天、小时和分钟
+ * precision = 4,返回天、小时、分钟和秒
+ * precision >= 5,返回天、小时、分钟、秒和毫秒
+ *
+ * @return 合适型与当前时间的差
+ */
+ public static String getFitTimeSpanByNow(final String time, final int precision) {
+ return getFitTimeSpan(getNowString(), time, DEFAULT_FORMAT, precision);
+ }
+
+ /**
+ * 获取合适型与当前时间的差
+ * time格式为format
+ *
+ * @param time 时间字符串
+ * @param format 时间格式
+ * @param precision 精度
+ *
+ * precision = 0,返回null
+ * precision = 1,返回天
+ * precision = 2,返回天和小时
+ * precision = 3,返回天、小时和分钟
+ * precision = 4,返回天、小时、分钟和秒
+ * precision >= 5,返回天、小时、分钟、秒和毫秒
+ *
+ * @return 合适型与当前时间的差
+ */
+ public static String getFitTimeSpanByNow(final String time, final DateFormat format, final int precision) {
+ return getFitTimeSpan(getNowString(format), time, format, precision);
+ }
+
+ /**
+ * 获取合适型与当前时间的差
+ *
+ * @param date Date类型时间
+ * @param precision 精度
+ *
+ * precision = 0,返回null
+ * precision = 1,返回天
+ * precision = 2,返回天和小时
+ * precision = 3,返回天、小时和分钟
+ * precision = 4,返回天、小时、分钟和秒
+ * precision >= 5,返回天、小时、分钟、秒和毫秒
+ *
+ * @return 合适型与当前时间的差
+ */
+ public static String getFitTimeSpanByNow(final Date date, final int precision) {
+ return getFitTimeSpan(getNowDate(), date, precision);
+ }
+
+ /**
+ * 获取合适型与当前时间的差
+ *
+ * @param millis 毫秒时间戳
+ * @param precision 精度
+ *
+ * precision = 0,返回null
+ * precision = 1,返回天
+ * precision = 2,返回天和小时
+ * precision = 3,返回天、小时和分钟
+ * precision = 4,返回天、小时、分钟和秒
+ * precision >= 5,返回天、小时、分钟、秒和毫秒
+ *
+ * @return 合适型与当前时间的差
+ */
+ public static String getFitTimeSpanByNow(final long millis, final int precision) {
+ return getFitTimeSpan(System.currentTimeMillis(), millis, precision);
+ }
+
+ /**
+ * 获取友好型与当前时间的差
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 时间字符串
+ * @return 友好型与当前时间的差
+ *
+ * 如果小于1秒钟内,显示刚刚
+ * 如果在1分钟内,显示XXX秒前
+ * 如果在1小时内,显示XXX分钟前
+ * 如果在1小时外的今天内,显示今天15:32
+ * 如果是昨天的,显示昨天15:32
+ * 其余显示,2016-10-15
+ * 时间不合法的情况全部日期和时间信息,如星期六 十月 27 14:21:20 CST 2007
+ *
+ */
+ public static String getFriendlyTimeSpanByNow(final String time) {
+ return getFriendlyTimeSpanByNow(time, DEFAULT_FORMAT);
+ }
+
+ /**
+ * 获取友好型与当前时间的差
+ * time格式为format
+ *
+ * @param time 时间字符串
+ * @param format 时间格式
+ * @return 友好型与当前时间的差
+ *
+ * 如果小于1秒钟内,显示刚刚
+ * 如果在1分钟内,显示XXX秒前
+ * 如果在1小时内,显示XXX分钟前
+ * 如果在1小时外的今天内,显示今天15:32
+ * 如果是昨天的,显示昨天15:32
+ * 其余显示,2016-10-15
+ * 时间不合法的情况全部日期和时间信息,如星期六 十月 27 14:21:20 CST 2007
+ *
+ */
+ public static String getFriendlyTimeSpanByNow(final String time, final DateFormat format) {
+ return getFriendlyTimeSpanByNow(string2Millis(time, format));
+ }
+
+ /**
+ * 获取友好型与当前时间的差
+ *
+ * @param date Date类型时间
+ * @return 友好型与当前时间的差
+ *
+ * 如果小于1秒钟内,显示刚刚
+ * 如果在1分钟内,显示XXX秒前
+ * 如果在1小时内,显示XXX分钟前
+ * 如果在1小时外的今天内,显示今天15:32
+ * 如果是昨天的,显示昨天15:32
+ * 其余显示,2016-10-15
+ * 时间不合法的情况全部日期和时间信息,如星期六 十月 27 14:21:20 CST 2007
+ *
+ */
+ public static String getFriendlyTimeSpanByNow(final Date date) {
+ return getFriendlyTimeSpanByNow(date.getTime());
+ }
+
+ /**
+ * 获取友好型与当前时间的差
+ *
+ * @param millis 毫秒时间戳
+ * @return 友好型与当前时间的差
+ *
+ * 如果小于1秒钟内,显示刚刚
+ * 如果在1分钟内,显示XXX秒前
+ * 如果在1小时内,显示XXX分钟前
+ * 如果在1小时外的今天内,显示今天15:32
+ * 如果是昨天的,显示昨天15:32
+ * 其余显示,2016-10-15
+ * 时间不合法的情况全部日期和时间信息,如星期六 十月 27 14:21:20 CST 2007
+ *
+ */
+ public static String getFriendlyTimeSpanByNow(final long millis) {
+ long now = System.currentTimeMillis();
+ long span = now - millis;
+ if (span < 0)
+ return String.format("%tc", millis);// U can read http://www.apihome.cn/api/java/Formatter.html to understand it.
+ if (span < 1000) {
+ return "刚刚";
+ } else if (span < TimeConstants.MIN) {
+ return String.format(Locale.getDefault(), "%d秒前", span / TimeConstants.SEC);
+ } else if (span < TimeConstants.HOUR) {
+ return String.format(Locale.getDefault(), "%d分钟前", span / TimeConstants.MIN);
+ }
+ // 获取当天00:00
+ long wee = getWeeOfToday();
+ if (millis >= wee) {
+ return String.format("今天%tR", millis);
+ } else if (millis >= wee - TimeConstants.DAY) {
+ return String.format("昨天%tR", millis);
+ } else {
+ return String.format("%tF", millis);
+ }
+ }
+
+ private static long getWeeOfToday() {
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ return cal.getTimeInMillis();
+ }
+
+ /**
+ * 获取与给定时间等于时间差的时间戳
+ *
+ * @param millis 给定时间
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与给定时间等于时间差的时间戳
+ */
+ public static long getMillis(final long millis, final long timeSpan, @TimeConstants.Unit final int unit) {
+ return millis + timeSpan2Millis(timeSpan, unit);
+ }
+
+ /**
+ * 获取与给定时间等于时间差的时间戳
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 给定时间
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与给定时间等于时间差的时间戳
+ */
+ public static long getMillis(final String time, final long timeSpan, @TimeConstants.Unit final int unit) {
+ return getMillis(time, DEFAULT_FORMAT, timeSpan, unit);
+ }
+
+ /**
+ * 获取与给定时间等于时间差的时间戳
+ * time格式为format
+ *
+ * @param time 给定时间
+ * @param format 时间格式
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与给定时间等于时间差的时间戳
+ */
+ public static long getMillis(final String time, final DateFormat format, final long timeSpan, @TimeConstants.Unit final int unit) {
+ return string2Millis(time, format) + timeSpan2Millis(timeSpan, unit);
+ }
+
+ /**
+ * 获取与给定时间等于时间差的时间戳
+ *
+ * @param date 给定时间
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与给定时间等于时间差的时间戳
+ */
+ public static long getMillis(final Date date, final long timeSpan, @TimeConstants.Unit final int unit) {
+ return date2Millis(date) + timeSpan2Millis(timeSpan, unit);
+ }
+
+ /**
+ * 获取与给定时间等于时间差的时间字符串
+ * 格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param millis 给定时间
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与给定时间等于时间差的时间字符串
+ */
+ public static String getString(final long millis, final long timeSpan, @TimeConstants.Unit final int unit) {
+ return getString(millis, DEFAULT_FORMAT, timeSpan, unit);
+ }
+
+ /**
+ * 获取与给定时间等于时间差的时间字符串
+ * 格式为format
+ *
+ * @param millis 给定时间
+ * @param format 时间格式
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与给定时间等于时间差的时间字符串
+ */
+ public static String getString(final long millis, final DateFormat format, final long timeSpan, @TimeConstants.Unit final int unit) {
+ return millis2String(millis + timeSpan2Millis(timeSpan, unit), format);
+ }
+
+ /**
+ * 获取与给定时间等于时间差的时间字符串
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 给定时间
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与给定时间等于时间差的时间字符串
+ */
+ public static String getString(final String time, final long timeSpan, @TimeConstants.Unit final int unit) {
+ return getString(time, DEFAULT_FORMAT, timeSpan, unit);
+ }
+
+ /**
+ * 获取与给定时间等于时间差的时间字符串
+ * 格式为format
+ *
+ * @param time 给定时间
+ * @param format 时间格式
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与给定时间等于时间差的时间字符串
+ */
+ public static String getString(final String time, final DateFormat format, final long timeSpan, @TimeConstants.Unit final int unit) {
+ return millis2String(string2Millis(time, format) + timeSpan2Millis(timeSpan, unit), format);
+ }
+
+ /**
+ * 获取与给定时间等于时间差的时间字符串
+ * 格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param date 给定时间
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与给定时间等于时间差的时间字符串
+ */
+ public static String getString(final Date date, final long timeSpan, @TimeConstants.Unit final int unit) {
+ return getString(date, DEFAULT_FORMAT, timeSpan, unit);
+ }
+
+ /**
+ * 获取与给定时间等于时间差的时间字符串
+ * 格式为format
+ *
+ * @param date 给定时间
+ * @param format 时间格式
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与给定时间等于时间差的时间字符串
+ */
+ public static String getString(final Date date, final DateFormat format, final long timeSpan, @TimeConstants.Unit final int unit) {
+ return millis2String(date2Millis(date) + timeSpan2Millis(timeSpan, unit), format);
+ }
+
+ /**
+ * 获取与给定时间等于时间差的Date
+ *
+ * @param millis 给定时间
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与给定时间等于时间差的Date
+ */
+ public static Date getDate(final long millis, final long timeSpan, @TimeConstants.Unit final int unit) {
+ return millis2Date(millis + timeSpan2Millis(timeSpan, unit));
+ }
+
+ /**
+ * 获取与给定时间等于时间差的Date
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 给定时间
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与给定时间等于时间差的Date
+ */
+ public static Date getDate(final String time, final long timeSpan, @TimeConstants.Unit final int unit) {
+ return getDate(time, DEFAULT_FORMAT, timeSpan, unit);
+ }
+
+ /**
+ * 获取与给定时间等于时间差的Date
+ * 格式为format
+ *
+ * @param time 给定时间
+ * @param format 时间格式
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与给定时间等于时间差的Date
+ */
+ public static Date getDate(final String time, final DateFormat format, final long timeSpan, @TimeConstants.Unit final int unit) {
+ return millis2Date(string2Millis(time, format) + timeSpan2Millis(timeSpan, unit));
+ }
+
+ /**
+ * 获取与给定时间等于时间差的Date
+ *
+ * @param date 给定时间
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与给定时间等于时间差的Date
+ */
+ public static Date getDate(final Date date, final long timeSpan, @TimeConstants.Unit final int unit) {
+ return millis2Date(date2Millis(date) + timeSpan2Millis(timeSpan, unit));
+ }
+
+ /**
+ * 获取与当前时间等于时间差的时间戳
+ *
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与当前时间等于时间差的时间戳
+ */
+ public static long getMillisByNow(final long timeSpan, @TimeConstants.Unit final int unit) {
+ return getMillis(getNowMills(), timeSpan, unit);
+ }
+
+ /**
+ * 获取与当前时间等于时间差的时间字符串
+ * 格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与当前时间等于时间差的时间字符串
+ */
+ public static String getStringByNow(final long timeSpan, @TimeConstants.Unit final int unit) {
+ return getStringByNow(timeSpan, DEFAULT_FORMAT, unit);
+ }
+
+ /**
+ * 获取与当前时间等于时间差的时间字符串
+ * 格式为format
+ *
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param format 时间格式
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与当前时间等于时间差的时间字符串
+ */
+ public static String getStringByNow(final long timeSpan, final DateFormat format, @TimeConstants.Unit final int unit) {
+ return getString(getNowMills(), format, timeSpan, unit);
+ }
+
+ /**
+ * 获取与当前时间等于时间差的Date
+ *
+ * @param timeSpan 时间差的毫秒时间戳
+ * @param unit 单位类型
+ *
+ * {@link TimeConstants#MSEC}: 毫秒
+ * {@link TimeConstants#SEC }: 秒
+ * {@link TimeConstants#MIN }: 分
+ * {@link TimeConstants#HOUR}: 小时
+ * {@link TimeConstants#DAY }: 天
+ *
+ * @return 与当前时间等于时间差的Date
+ */
+ public static Date getDateByNow(final long timeSpan, @TimeConstants.Unit final int unit) {
+ return getDate(getNowMills(), timeSpan, unit);
+ }
+
+ /**
+ * 判断是否今天
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 时间字符串
+ * @return {@code true}: 是 {@code false}: 否
+ */
+ public static boolean isToday(final String time) {
+ return isToday(string2Millis(time, DEFAULT_FORMAT));
+ }
+
+ /**
+ * 判断是否今天
+ * time格式为format
+ *
+ * @param time 时间字符串
+ * @param format 时间格式
+ * @return {@code true}: 是 {@code false}: 否
+ */
+ public static boolean isToday(final String time, final DateFormat format) {
+ return isToday(string2Millis(time, format));
+ }
+
+ /**
+ * 判断是否今天
+ *
+ * @param date Date类型时间
+ * @return {@code true}: 是 {@code false}: 否
+ */
+ public static boolean isToday(final Date date) {
+ return isToday(date.getTime());
+ }
+
+ /**
+ * 判断是否今天
+ *
+ * @param millis 毫秒时间戳
+ * @return {@code true}: 是 {@code false}: 否
+ */
+ public static boolean isToday(final long millis) {
+ long wee = getWeeOfToday();
+ return millis >= wee && millis < wee + TimeConstants.DAY;
+ }
+
+ /**
+ * 判断是否闰年
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 时间字符串
+ * @return {@code true}: 闰年 {@code false}: 平年
+ */
+ public static boolean isLeapYear(final String time) {
+ return isLeapYear(string2Date(time, DEFAULT_FORMAT));
+ }
+
+ /**
+ * 判断是否闰年
+ * time格式为format
+ *
+ * @param time 时间字符串
+ * @param format 时间格式
+ * @return {@code true}: 闰年 {@code false}: 平年
+ */
+ public static boolean isLeapYear(final String time, final DateFormat format) {
+ return isLeapYear(string2Date(time, format));
+ }
+
+ /**
+ * 判断是否闰年
+ *
+ * @param date Date类型时间
+ * @return {@code true}: 闰年 {@code false}: 平年
+ */
+ public static boolean isLeapYear(final Date date) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ int year = cal.get(Calendar.YEAR);
+ return isLeapYear(year);
+ }
+
+ /**
+ * 判断是否闰年
+ *
+ * @param millis 毫秒时间戳
+ * @return {@code true}: 闰年 {@code false}: 平年
+ */
+ public static boolean isLeapYear(final long millis) {
+ return isLeapYear(millis2Date(millis));
+ }
+
+ /**
+ * 判断是否闰年
+ *
+ * @param year 年份
+ * @return {@code true}: 闰年 {@code false}: 平年
+ */
+ public static boolean isLeapYear(final int year) {
+ return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
+ }
+
+ /**
+ * 获取中式星期
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 时间字符串
+ * @return 中式星期
+ */
+ public static String getChineseWeek(final String time) {
+ return getChineseWeek(string2Date(time, DEFAULT_FORMAT));
+ }
+
+ /**
+ * 获取中式星期
+ * time格式为format
+ *
+ * @param time 时间字符串
+ * @param format 时间格式
+ * @return 中式星期
+ */
+ public static String getChineseWeek(final String time, final DateFormat format) {
+ return getChineseWeek(string2Date(time, format));
+ }
+
+ /**
+ * 获取中式星期
+ *
+ * @param date Date类型时间
+ * @return 中式星期
+ */
+ public static String getChineseWeek(final Date date) {
+ return new SimpleDateFormat("E", Locale.CHINA).format(date);
+ }
+
+ /**
+ * 获取中式星期
+ *
+ * @param millis 毫秒时间戳
+ * @return 中式星期
+ */
+ public static String getChineseWeek(final long millis) {
+ return getChineseWeek(new Date(millis));
+ }
+
+ /**
+ * 获取美式星期
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 时间字符串
+ * @return 美式星期
+ */
+ public static String getUSWeek(final String time) {
+ return getUSWeek(string2Date(time, DEFAULT_FORMAT));
+ }
+
+ /**
+ * 获取美式星期
+ * time格式为format
+ *
+ * @param time 时间字符串
+ * @param format 时间格式
+ * @return 美式星期
+ */
+ public static String getUSWeek(final String time, final DateFormat format) {
+ return getUSWeek(string2Date(time, format));
+ }
+
+ /**
+ * 获取美式星期
+ *
+ * @param date Date类型时间
+ * @return 美式星期
+ */
+ public static String getUSWeek(final Date date) {
+ return new SimpleDateFormat("EEEE", Locale.US).format(date);
+ }
+
+ /**
+ * 获取美式星期
+ *
+ * @param millis 毫秒时间戳
+ * @return 美式星期
+ */
+ public static String getUSWeek(final long millis) {
+ return getUSWeek(new Date(millis));
+ }
+
+ /**
+ * 获取星期索引
+ * 注意:周日的Index才是1,周六为7
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 时间字符串
+ * @return 1...7
+ * @see Calendar#SUNDAY
+ * @see Calendar#MONDAY
+ * @see Calendar#TUESDAY
+ * @see Calendar#WEDNESDAY
+ * @see Calendar#THURSDAY
+ * @see Calendar#FRIDAY
+ * @see Calendar#SATURDAY
+ */
+ public static int getWeekIndex(final String time) {
+ return getWeekIndex(string2Date(time, DEFAULT_FORMAT));
+ }
+
+ /**
+ * 获取星期索引
+ * 注意:周日的Index才是1,周六为7
+ * time格式为format
+ *
+ * @param time 时间字符串
+ * @param format 时间格式
+ * @return 1...7
+ * @see Calendar#SUNDAY
+ * @see Calendar#MONDAY
+ * @see Calendar#TUESDAY
+ * @see Calendar#WEDNESDAY
+ * @see Calendar#THURSDAY
+ * @see Calendar#FRIDAY
+ * @see Calendar#SATURDAY
+ */
+ public static int getWeekIndex(final String time, final DateFormat format) {
+ return getWeekIndex(string2Date(time, format));
+ }
+
+ /**
+ * 获取星期索引
+ * 注意:周日的Index才是1,周六为7
+ *
+ * @param date Date类型时间
+ * @return 1...7
+ * @see Calendar#SUNDAY
+ * @see Calendar#MONDAY
+ * @see Calendar#TUESDAY
+ * @see Calendar#WEDNESDAY
+ * @see Calendar#THURSDAY
+ * @see Calendar#FRIDAY
+ * @see Calendar#SATURDAY
+ */
+ public static int getWeekIndex(final Date date) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ return cal.get(Calendar.DAY_OF_WEEK);
+ }
+
+ /**
+ * 获取星期索引
+ * 注意:周日的Index才是1,周六为7
+ *
+ * @param millis 毫秒时间戳
+ * @return 1...7
+ * @see Calendar#SUNDAY
+ * @see Calendar#MONDAY
+ * @see Calendar#TUESDAY
+ * @see Calendar#WEDNESDAY
+ * @see Calendar#THURSDAY
+ * @see Calendar#FRIDAY
+ * @see Calendar#SATURDAY
+ */
+ public static int getWeekIndex(final long millis) {
+ return getWeekIndex(millis2Date(millis));
+ }
+
+ /**
+ * 获取月份中的第几周
+ * 注意:国外周日才是新的一周的开始
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 时间字符串
+ * @return 1...5
+ */
+ public static int getWeekOfMonth(final String time) {
+ return getWeekOfMonth(string2Date(time, DEFAULT_FORMAT));
+ }
+
+ /**
+ * 获取月份中的第几周
+ * 注意:国外周日才是新的一周的开始
+ * time格式为format
+ *
+ * @param time 时间字符串
+ * @param format 时间格式
+ * @return 1...5
+ */
+ public static int getWeekOfMonth(final String time, final DateFormat format) {
+ return getWeekOfMonth(string2Date(time, format));
+ }
+
+ /**
+ * 获取月份中的第几周
+ * 注意:国外周日才是新的一周的开始
+ *
+ * @param date Date类型时间
+ * @return 1...5
+ */
+ public static int getWeekOfMonth(final Date date) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ return cal.get(Calendar.WEEK_OF_MONTH);
+ }
+
+ /**
+ * 获取月份中的第几周
+ * 注意:国外周日才是新的一周的开始
+ *
+ * @param millis 毫秒时间戳
+ * @return 1...5
+ */
+ public static int getWeekOfMonth(final long millis) {
+ return getWeekOfMonth(millis2Date(millis));
+ }
+
+ /**
+ * 获取年份中的第几周
+ * 注意:国外周日才是新的一周的开始
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 时间字符串
+ * @return 1...54
+ */
+ public static int getWeekOfYear(final String time) {
+ return getWeekOfYear(string2Date(time, DEFAULT_FORMAT));
+ }
+
+ /**
+ * 获取年份中的第几周
+ * 注意:国外周日才是新的一周的开始
+ * time格式为format
+ *
+ * @param time 时间字符串
+ * @param format 时间格式
+ * @return 1...54
+ */
+ public static int getWeekOfYear(final String time, final DateFormat format) {
+ return getWeekOfYear(string2Date(time, format));
+ }
+
+ /**
+ * 获取年份中的第几周
+ * 注意:国外周日才是新的一周的开始
+ *
+ * @param date Date类型时间
+ * @return 1...54
+ */
+ public static int getWeekOfYear(final Date date) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ return cal.get(Calendar.WEEK_OF_YEAR);
+ }
+
+ /**
+ * 获取年份中的第几周
+ * 注意:国外周日才是新的一周的开始
+ *
+ * @param millis 毫秒时间戳
+ * @return 1...54
+ */
+ public static int getWeekOfYear(final long millis) {
+ return getWeekOfYear(millis2Date(millis));
+ }
+
+ private static final String[] CHINESE_ZODIAC = {"猴", "鸡", "狗", "猪", "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊"};
+
+ /**
+ * 获取生肖
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 时间字符串
+ * @return 生肖
+ */
+ public static String getChineseZodiac(final String time) {
+ return getChineseZodiac(string2Date(time, DEFAULT_FORMAT));
+ }
+
+ /**
+ * 获取生肖
+ * time格式为format
+ *
+ * @param time 时间字符串
+ * @param format 时间格式
+ * @return 生肖
+ */
+ public static String getChineseZodiac(final String time, final DateFormat format) {
+ return getChineseZodiac(string2Date(time, format));
+ }
+
+ /**
+ * 获取生肖
+ *
+ * @param date Date类型时间
+ * @return 生肖
+ */
+ public static String getChineseZodiac(final Date date) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ return CHINESE_ZODIAC[cal.get(Calendar.YEAR) % 12];
+ }
+
+ /**
+ * 获取生肖
+ *
+ * @param millis 毫秒时间戳
+ * @return 生肖
+ */
+ public static String getChineseZodiac(final long millis) {
+ return getChineseZodiac(millis2Date(millis));
+ }
+
+ /**
+ * 获取生肖
+ *
+ * @param year 年
+ * @return 生肖
+ */
+ public static String getChineseZodiac(final int year) {
+ return CHINESE_ZODIAC[year % 12];
+ }
+
+ private static final String[] ZODIAC = {"水瓶座", "双鱼座", "白羊座", "金牛座", "双子座", "巨蟹座", "狮子座", "处女座", "天秤座", "天蝎座", "射手座", "魔羯座"};
+ private static final int[] ZODIAC_FLAGS = {20, 19, 21, 21, 21, 22, 23, 23, 23, 24, 23, 22};
+
+ /**
+ * 获取星座
+ * time格式为yyyy-MM-dd HH:mm:ss
+ *
+ * @param time 时间字符串
+ * @return 生肖
+ */
+ public static String getZodiac(final String time) {
+ return getZodiac(string2Date(time, DEFAULT_FORMAT));
+ }
+
+ /**
+ * 获取星座
+ * time格式为format
+ *
+ * @param time 时间字符串
+ * @param format 时间格式
+ * @return 生肖
+ */
+ public static String getZodiac(final String time, final DateFormat format) {
+ return getZodiac(string2Date(time, format));
+ }
+
+ /**
+ * 获取星座
+ *
+ * @param date Date类型时间
+ * @return 星座
+ */
+ public static String getZodiac(final Date date) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ int month = cal.get(Calendar.MONTH) + 1;
+ int day = cal.get(Calendar.DAY_OF_MONTH);
+ return getZodiac(month, day);
+ }
+
+ /**
+ * 获取星座
+ *
+ * @param millis 毫秒时间戳
+ * @return 星座
+ */
+ public static String getZodiac(final long millis) {
+ return getZodiac(millis2Date(millis));
+ }
+
+ /**
+ * 获取星座
+ *
+ * @param month 月
+ * @param day 日
+ * @return 星座
+ */
+ public static String getZodiac(final int month, final int day) {
+ return ZODIAC[day >= ZODIAC_FLAGS[month - 1]
+ ? month - 1
+ : (month + 10) % 12];
+ }
+
+ private static long timeSpan2Millis(final long timeSpan, @TimeConstants.Unit final int unit) {
+ return timeSpan * unit;
+ }
+
+ private static long millis2TimeSpan(final long millis, @TimeConstants.Unit final int unit) {
+ return millis / unit;
+ }
+
+ private static String millis2FitTimeSpan(long millis, int precision) {
+ if (millis < 0 || precision <= 0) return null;
+ precision = Math.min(precision, 5);
+ String[] units = {"天", "小时", "分钟", "秒", "毫秒"};
+ if (millis == 0) return 0 + units[precision - 1];
+ StringBuilder sb = new StringBuilder();
+ int[] unitLen = {86400000, 3600000, 60000, 1000, 1};
+ for (int i = 0; i < precision; i++) {
+ if (millis >= unitLen[i]) {
+ long mode = millis / unitLen[i];
+ millis -= mode * unitLen[i];
+ sb.append(mode).append(units[i]);
+ }
+ }
+ return sb.toString();
+ }
+
+ @SuppressLint("SimpleDateFormat")
+ public static String generateTimeStr(String prefix) {
+ SimpleDateFormat formatter = new SimpleDateFormat("_yyyy-MM-dd_HH-mm-ss");
+ return prefix + formatter.format(new Date());
+ }
+
+ /**
+ * 格式化时间
+ *
+ * @param time 时间戳 毫秒
+ */
+ @SuppressLint("SimpleDateFormat")
+ public static String fromatTimeStamp(long time) {
+ Date date = new Date(time);
+ SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+ return formatter.format(date);
+ }
+
+ @SuppressLint("SimpleDateFormat")
+ public static String formatTimeStampWithSecond(long time) {
+ Date date = new Date(time);
+ SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ return formatter.format(date);
+ }
+
+}
diff --git a/app/src/main/java/cn/org/landcloud/survey/util/Utils.java b/app/src/main/java/cn/org/landcloud/survey/util/Utils.java
new file mode 100644
index 0000000..77854dd
--- /dev/null
+++ b/app/src/main/java/cn/org/landcloud/survey/util/Utils.java
@@ -0,0 +1,31 @@
+package cn.org.landcloud.survey.util;
+
+import java.nio.charset.StandardCharsets;
+
+import cn.org.landcloud.security.Util;
+import cn.org.landcloud.security.sm2.SM2;
+import cn.org.landcloud.security.sm2.SM2EncDecUtils;
+import cn.org.landcloud.security.sm2.SM2SignVO;
+import cn.org.landcloud.security.sm2.SM2SignVerUtils;
+import cn.org.landcloud.security.sm3.SM3Utils;
+
+public class Utils {
+
+
+ /**
+ * 生成附件校验码
+ * @param content
+ * @param privateKey
+ * @return
+ */
+ public static String makeJYM(String content,String privateKey) {
+ String hash = SM3Utils.sm3(content);
+ try {
+ SM2SignVO sm2SignVO = SM2SignVerUtils.Sign2SM2(Util.hexStringToBytes(privateKey),hash.getBytes(StandardCharsets.UTF_8));
+ return sm2SignVO.getSm2_signForSoft();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+}
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..4c2c2ae
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..a6089a7
--- /dev/null
+++ b/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..f8c6127
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..27367d7
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ 举证数据接口样例
+
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..0500f83
--- /dev/null
+++ b/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000..fa0f996
--- /dev/null
+++ b/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000..9ee9997
--- /dev/null
+++ b/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/java/cn/org/landcloud/survey/ExampleUnitTest.java b/app/src/test/java/cn/org/landcloud/survey/ExampleUnitTest.java
new file mode 100644
index 0000000..a8ea9e4
--- /dev/null
+++ b/app/src/test/java/cn/org/landcloud/survey/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package cn.org.landcloud.survey;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..90f9008
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,5 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ id 'com.android.application' version '7.3.0' apply false
+ id 'com.android.library' version '7.3.0' apply false
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..3e927b1
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..ca4f6da
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Oct 12 10:45:05 CST 2022
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..4f906e0
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..107acd3
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..f02b3a1
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,17 @@
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+rootProject.name = "landsurvey"
+include ':app'
+include ':ssl'
diff --git a/ssl/.gitignore b/ssl/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/ssl/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/ssl/build.gradle b/ssl/build.gradle
new file mode 100644
index 0000000..0b2e45b
--- /dev/null
+++ b/ssl/build.gradle
@@ -0,0 +1,21 @@
+plugins {
+ id 'java-library'
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_7
+ targetCompatibility = JavaVersion.VERSION_1_7
+
+/* task makeJar(type: jar) {
+ delete 'ssl.jar'
+ from file('src/main/java/')
+ destinationDir = file('build/libs')
+ archiveName = 'ssl.jar'
+ }
+ makeJar.dependsOn(build)*/
+}
+
+dependencies {
+ implementation 'org.bouncycastle:bcprov-jdk15on:1.63'
+ compileOnly 'junit:junit:4.13.2'
+}
\ No newline at end of file
diff --git a/ssl/src/main/java/cn/org/landcloud/security/Base64.java b/ssl/src/main/java/cn/org/landcloud/security/Base64.java
new file mode 100644
index 0000000..c30f840
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/Base64.java
@@ -0,0 +1,128 @@
+package cn.org.landcloud.security;
+
+//
+// Source code recreated from a .class file by IntelliJ IDEA
+// (powered by Fernflower decompiler)
+//
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class Base64 {
+ static final char[] charTab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
+
+ public Base64() {
+ }
+
+ public static String encode(byte[] data) {
+ return encode(data, 0, data.length, (StringBuffer) null).toString();
+ }
+
+ public static StringBuffer encode(byte[] data, int start, int len, StringBuffer buf) {
+ if (null == data || data.length == 0) {
+ return new StringBuffer();
+ }
+
+ if (buf == null) {
+ buf = new StringBuffer(data.length * 3 / 2);
+ }
+
+ int end = len - 3;
+ int i = start;
+ int n = 0;
+
+ int d;
+ while (i <= end) {
+ d = (data[i] & 255) << 16 | (data[i + 1] & 255) << 8 | data[i + 2] & 255;
+ buf.append(charTab[d >> 18 & 63]);
+ buf.append(charTab[d >> 12 & 63]);
+ buf.append(charTab[d >> 6 & 63]);
+ buf.append(charTab[d & 63]);
+ i += 3;
+ if (n++ >= 14) {
+ n = 0;
+ buf.append("\r\n");
+ }
+ }
+
+ if (i == start + len - 2) {
+ d = (data[i] & 255) << 16 | (data[i + 1] & 255) << 8;
+ buf.append(charTab[d >> 18 & 63]);
+ buf.append(charTab[d >> 12 & 63]);
+ buf.append(charTab[d >> 6 & 63]);
+ buf.append("=");
+ } else if (i == start + len - 1) {
+ d = (data[i] & 255) << 16;
+ buf.append(charTab[d >> 18 & 63]);
+ buf.append(charTab[d >> 12 & 63]);
+ buf.append("==");
+ }
+
+ return buf;
+ }
+
+ static int decode(char c) {
+ if (c >= 65 && c <= 90) {
+ return c - 65;
+ } else if (c >= 97 && c <= 122) {
+ return c - 97 + 26;
+ } else if (c >= 48 && c <= 57) {
+ return c - 48 + 26 + 26;
+ } else {
+ switch (c) {
+ case '+':
+ return 62;
+ case '/':
+ return 63;
+ case '=':
+ return 0;
+ default:
+ throw new RuntimeException("unexpected code: " + c);
+ }
+ }
+ }
+
+ public static byte[] decode(String s) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+ try {
+ decode(s, bos);
+ } catch (IOException var3) {
+ throw new RuntimeException();
+ }
+
+ return bos.toByteArray();
+ }
+
+ public static void decode(String s, OutputStream os) throws IOException {
+ int i = 0;
+ int len = s.length();
+
+ while (true) {
+ while (i < len && s.charAt(i) <= 32) {
+ ++i;
+ }
+
+ if (i == len) {
+ break;
+ }
+
+ int tri = (decode(s.charAt(i)) << 18) + (decode(s.charAt(i + 1)) << 12) + (decode(s.charAt(i + 2)) << 6) + decode(s.charAt(i + 3));
+ os.write(tri >> 16 & 255);
+ if (s.charAt(i + 2) == 61) {
+ break;
+ }
+
+ os.write(tri >> 8 & 255);
+ if (s.charAt(i + 3) == 61) {
+ break;
+ }
+
+ os.write(tri & 255);
+ i += 4;
+ }
+
+ }
+}
+
diff --git a/ssl/src/main/java/cn/org/landcloud/security/Util.java b/ssl/src/main/java/cn/org/landcloud/security/Util.java
new file mode 100644
index 0000000..7b364d5
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/Util.java
@@ -0,0 +1,617 @@
+package cn.org.landcloud.security;
+
+import java.math.BigInteger;
+
+public class Util {
+ /**
+ * 整形转换成网络传输的字节流(字节数组)型数据
+ *
+ * @param num 一个整型数据
+ * @return 4个字节的自己数组
+ */
+ public static byte[] intToBytes(int num) {
+ byte[] bytes = new byte[4];
+ bytes[0] = (byte) (0xff & (num >> 0));
+ bytes[1] = (byte) (0xff & (num >> 8));
+ bytes[2] = (byte) (0xff & (num >> 16));
+ bytes[3] = (byte) (0xff & (num >> 24));
+ return bytes;
+ }
+
+ /**
+ * 四个字节的字节数据转换成一个整形数据
+ *
+ * @param bytes 4个字节的字节数组
+ * @return 一个整型数据
+ */
+ public static int byteToInt(byte[] bytes) {
+ int num = 0;
+ int temp;
+ temp = (0x000000ff & (bytes[0])) << 0;
+ num = num | temp;
+ temp = (0x000000ff & (bytes[1])) << 8;
+ num = num | temp;
+ temp = (0x000000ff & (bytes[2])) << 16;
+ num = num | temp;
+ temp = (0x000000ff & (bytes[3])) << 24;
+ num = num | temp;
+ return num;
+ }
+
+ /**
+ * 长整形转换成网络传输的字节流(字节数组)型数据
+ *
+ * @param num 一个长整型数据
+ * @return 4个字节的自己数组
+ */
+ public static byte[] longToBytes(long num) {
+ byte[] bytes = new byte[8];
+ for (int i = 0; i < 8; i++) {
+ bytes[i] = (byte) (0xff & (num >> (i * 8)));
+ }
+
+ return bytes;
+ }
+
+ /**
+ * 大数字转换字节流(字节数组)型数据
+ *
+ * @param n
+ * @return
+ */
+ public static byte[] byteConvert32Bytes(BigInteger n) {
+ byte tmpd[] = (byte[]) null;
+ if (n == null) {
+ return null;
+ }
+
+ if (n.toByteArray().length == 33) {
+ tmpd = new byte[32];
+ System.arraycopy(n.toByteArray(), 1, tmpd, 0, 32);
+ } else if (n.toByteArray().length == 32) {
+ tmpd = n.toByteArray();
+ } else {
+ tmpd = new byte[32];
+ for (int i = 0; i < 32 - n.toByteArray().length; i++) {
+ tmpd[i] = 0;
+ }
+ System.arraycopy(n.toByteArray(), 0, tmpd, 32 - n.toByteArray().length, n.toByteArray().length);
+ }
+ return tmpd;
+ }
+
+ /**
+ * 换字节流(字节数组)型数据转大数字
+ *
+ * @param b
+ * @return
+ */
+ public static BigInteger byteConvertInteger(byte[] b) {
+ if (b[0] < 0) {
+ byte[] temp = new byte[b.length + 1];
+ temp[0] = 0;
+ System.arraycopy(b, 0, temp, 1, b.length);
+ return new BigInteger(temp);
+ }
+ return new BigInteger(b);
+ }
+
+ /**
+ * 根据字节数组获得值(十六进制数字)
+ *
+ * @param bytes
+ * @return
+ */
+ public static String getHexString(byte[] bytes) {
+ return getHexString(bytes, true);
+ }
+
+ /**
+ * 根据字节数组获得值(十六进制数字)
+ *
+ * @param bytes
+ * @param upperCase
+ * @return
+ */
+ public static String getHexString(byte[] bytes, boolean upperCase) {
+ String ret = "";
+ for (int i = 0; i < bytes.length; i++) {
+ ret += Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1);
+ }
+ return upperCase ? ret.toUpperCase() : ret;
+ }
+
+ /**
+ * 打印十六进制字符串
+ *
+ * @param bytes
+ */
+ public static void printHexString(byte[] bytes) {
+ for (int i = 0; i < bytes.length; i++) {
+ String hex = Integer.toHexString(bytes[i] & 0xFF);
+ if (hex.length() == 1) {
+ hex = '0' + hex;
+ }
+ System.out.print("0x" + hex.toUpperCase() + ",");
+ }
+ System.out.println("");
+ }
+
+ /**
+ * Convert hex string to byte[]
+ *
+ * @param hexString the hex string
+ * @return byte[]
+ */
+ public static byte[] hexStringToBytes(String hexString) {
+ if (hexString == null || hexString.equals("")) {
+ return null;
+ }
+
+ hexString = hexString.toUpperCase();
+ int length = hexString.length() / 2;
+ char[] hexChars = hexString.toCharArray();
+ byte[] d = new byte[length];
+ for (int i = 0; i < length; i++) {
+ int pos = i * 2;
+ d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
+ }
+ return d;
+ }
+
+ /**
+ * Convert char to byte
+ *
+ * @param c char
+ * @return byte
+ */
+ public static byte charToByte(char c) {
+ return (byte) "0123456789ABCDEF".indexOf(c);
+ }
+
+ /**
+ * 用于建立十六进制字符的输出的小写字符数组
+ */
+ private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+ /**
+ * 用于建立十六进制字符的输出的大写字符数组
+ */
+ private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+ /**
+ * 将字节数组转换为十六进制字符数组
+ *
+ * @param data byte[]
+ * @return 十六进制char[]
+ */
+ public static char[] encodeHex(byte[] data) {
+ return encodeHex(data, true);
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符数组
+ *
+ * @param data byte[]
+ * @param toLowerCase true
传换成小写格式 , false
传换成大写格式
+ * @return 十六进制char[]
+ */
+ public static char[] encodeHex(byte[] data, boolean toLowerCase) {
+ return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符数组
+ *
+ * @param data byte[]
+ * @param toDigits 用于控制输出的char[]
+ * @return 十六进制char[]
+ */
+ protected static char[] encodeHex(byte[] data, char[] toDigits) {
+ int l = data.length;
+ char[] out = new char[l << 1];
+ // two characters form the hex value.
+ for (int i = 0, j = 0; i < l; i++) {
+ out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
+ out[j++] = toDigits[0x0F & data[i]];
+ }
+ return out;
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符串
+ *
+ * @param data byte[]
+ * @return 十六进制String
+ */
+ public static String encodeHexString(byte[] data) {
+ return encodeHexString(data, true);
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符串
+ *
+ * @param data byte[]
+ * @param toLowerCase true
传换成小写格式 , false
传换成大写格式
+ * @return 十六进制String
+ */
+ public static String encodeHexString(byte[] data, boolean toLowerCase) {
+ return encodeHexString(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符串
+ *
+ * @param data byte[]
+ * @param toDigits 用于控制输出的char[]
+ * @return 十六进制String
+ */
+ protected static String encodeHexString(byte[] data, char[] toDigits) {
+ return new String(encodeHex(data, toDigits));
+ }
+
+ /**
+ * 将十六进制字符数组转换为字节数组
+ *
+ * @param data 十六进制char[]
+ * @return byte[]
+ * @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常
+ */
+ public static byte[] decodeHex(char[] data) {
+ int len = data.length;
+
+ if ((len & 0x01) != 0) {
+ throw new RuntimeException("Odd number of characters.");
+ }
+
+ byte[] out = new byte[len >> 1];
+
+ // two characters form the hex value.
+ for (int i = 0, j = 0; j < len; i++) {
+ int f = toDigit(data[j], j) << 4;
+ j++;
+ f = f | toDigit(data[j], j);
+ j++;
+ out[i] = (byte) (f & 0xFF);
+ }
+
+ return out;
+ }
+
+ /**
+ * 将十六进制字符转换成一个整数
+ *
+ * @param ch 十六进制char
+ * @param index 十六进制字符在字符数组中的位置
+ * @return 一个整数
+ * @throws RuntimeException 当ch不是一个合法的十六进制字符时,抛出运行时异常
+ */
+ protected static int toDigit(char ch, int index) {
+ int digit = Character.digit(ch, 16);
+ if (digit == -1) {
+ throw new RuntimeException("Illegal hexadecimal character " + ch
+ + " at index " + index);
+ }
+ return digit;
+ }
+
+ /**
+ * 数字字符串转ASCII码字符串
+ *
+ * @param String 字符串
+ * @return ASCII字符串
+ */
+ public static String StringToAsciiString(String content) {
+ String result = "";
+ int max = content.length();
+ for (int i = 0; i < max; i++) {
+ char c = content.charAt(i);
+ String b = Integer.toHexString(c);
+ result = result + b;
+ }
+ return result;
+ }
+
+ /**
+ * 十六进制转字符串
+ *
+ * @param hexString 十六进制字符串
+ * @param encodeType 编码类型4:Unicode,2:普通编码
+ * @return 字符串
+ */
+ public static String hexStringToString(String hexString, int encodeType) {
+ String result = "";
+ int max = hexString.length() / encodeType;
+ for (int i = 0; i < max; i++) {
+ char c = (char) hexStringToAlgorism(hexString
+ .substring(i * encodeType, (i + 1) * encodeType));
+ result += c;
+ }
+ return result;
+ }
+
+ /**
+ * 十六进制字符串装十进制
+ *
+ * @param hex 十六进制字符串
+ * @return 十进制数值
+ */
+ public static int hexStringToAlgorism(String hex) {
+ hex = hex.toUpperCase();
+ int max = hex.length();
+ int result = 0;
+ for (int i = max; i > 0; i--) {
+ char c = hex.charAt(i - 1);
+ int algorism = 0;
+ if (c >= '0' && c <= '9') {
+ algorism = c - '0';
+ } else {
+ algorism = c - 55;
+ }
+ result += Math.pow(16, max - i) * algorism;
+ }
+ return result;
+ }
+
+ /**
+ * 十六转二进制
+ *
+ * @param hex 十六进制字符串
+ * @return 二进制字符串
+ */
+ public static String hexStringToBinary(String hex) {
+ hex = hex.toUpperCase();
+ String result = "";
+ int max = hex.length();
+ for (int i = 0; i < max; i++) {
+ char c = hex.charAt(i);
+ switch (c) {
+ case '0':
+ result += "0000";
+ break;
+ case '1':
+ result += "0001";
+ break;
+ case '2':
+ result += "0010";
+ break;
+ case '3':
+ result += "0011";
+ break;
+ case '4':
+ result += "0100";
+ break;
+ case '5':
+ result += "0101";
+ break;
+ case '6':
+ result += "0110";
+ break;
+ case '7':
+ result += "0111";
+ break;
+ case '8':
+ result += "1000";
+ break;
+ case '9':
+ result += "1001";
+ break;
+ case 'A':
+ result += "1010";
+ break;
+ case 'B':
+ result += "1011";
+ break;
+ case 'C':
+ result += "1100";
+ break;
+ case 'D':
+ result += "1101";
+ break;
+ case 'E':
+ result += "1110";
+ break;
+ case 'F':
+ result += "1111";
+ break;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * ASCII码字符串转数字字符串
+ *
+ * @param String ASCII字符串
+ * @return 字符串
+ */
+ public static String AsciiStringToString(String content) {
+ String result = "";
+ int length = content.length() / 2;
+ for (int i = 0; i < length; i++) {
+ String c = content.substring(i * 2, i * 2 + 2);
+ int a = hexStringToAlgorism(c);
+ char b = (char) a;
+ String d = String.valueOf(b);
+ result += d;
+ }
+ return result;
+ }
+
+ /**
+ * 将十进制转换为指定长度的十六进制字符串
+ *
+ * @param algorism int 十进制数字
+ * @param maxLength int 转换后的十六进制字符串长度
+ * @return String 转换后的十六进制字符串
+ */
+ public static String algorismToHexString(int algorism, int maxLength) {
+ String result = "";
+ result = Integer.toHexString(algorism);
+
+ if (result.length() % 2 == 1) {
+ result = "0" + result;
+ }
+ return patchHexString(result.toUpperCase(), maxLength);
+ }
+
+ /**
+ * 字节数组转为普通字符串(ASCII对应的字符)
+ *
+ * @param bytearray byte[]
+ * @return String
+ */
+ public static String byteToString(byte[] bytearray) {
+ String result = "";
+ char temp;
+
+ int length = bytearray.length;
+ for (int i = 0; i < length; i++) {
+ temp = (char) bytearray[i];
+ result += temp;
+ }
+ return result;
+ }
+
+ /**
+ * 二进制字符串转十进制
+ *
+ * @param binary 二进制字符串
+ * @return 十进制数值
+ */
+ public static int binaryToAlgorism(String binary) {
+ int max = binary.length();
+ int result = 0;
+ for (int i = max; i > 0; i--) {
+ char c = binary.charAt(i - 1);
+ int algorism = c - '0';
+ result += Math.pow(2, max - i) * algorism;
+ }
+ return result;
+ }
+
+ /**
+ * 十进制转换为十六进制字符串
+ *
+ * @param algorism int 十进制的数字
+ * @return String 对应的十六进制字符串
+ */
+ public static String algorismToHEXString(int algorism) {
+ String result = "";
+ result = Integer.toHexString(algorism);
+
+ if (result.length() % 2 == 1) {
+ result = "0" + result;
+
+ }
+ result = result.toUpperCase();
+
+ return result;
+ }
+
+ /**
+ * HEX字符串前补0,主要用于长度位数不足。
+ *
+ * @param str String 需要补充长度的十六进制字符串
+ * @param maxLength int 补充后十六进制字符串的长度
+ * @return 补充结果
+ */
+ static public String patchHexString(String str, int maxLength) {
+ String temp = "";
+ for (int i = 0; i < maxLength - str.length(); i++) {
+ temp = "0" + temp;
+ }
+ str = (temp + str).substring(0, maxLength);
+ return str;
+ }
+
+ /**
+ * 将一个字符串转换为int
+ *
+ * @param s String 要转换的字符串
+ * @param defaultInt int 如果出现异常,默认返回的数字
+ * @param radix int 要转换的字符串是什么进制的,如16 8 10.
+ * @return int 转换后的数字
+ */
+ public static int parseToInt(String s, int defaultInt, int radix) {
+ int i = 0;
+ try {
+ i = Integer.parseInt(s, radix);
+ } catch (NumberFormatException ex) {
+ i = defaultInt;
+ }
+ return i;
+ }
+
+ /**
+ * 将一个十进制形式的数字字符串转换为int
+ *
+ * @param s String 要转换的字符串
+ * @param defaultInt int 如果出现异常,默认返回的数字
+ * @return int 转换后的数字
+ */
+ public static int parseToInt(String s, int defaultInt) {
+ int i = 0;
+ try {
+ i = Integer.parseInt(s);
+ } catch (NumberFormatException ex) {
+ i = defaultInt;
+ }
+ return i;
+ }
+
+ /**
+ * 十六进制串转化为byte数组
+ *
+ * @return the array of byte
+ */
+ public static byte[] hexToByte(String hex)
+ throws IllegalArgumentException {
+ if (hex.length() % 2 != 0) {
+ throw new IllegalArgumentException();
+ }
+ char[] arr = hex.toCharArray();
+ byte[] b = new byte[hex.length() / 2];
+ for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) {
+ String swap = "" + arr[i++] + arr[i];
+ int byteint = Integer.parseInt(swap, 16) & 0xFF;
+ b[j] = new Integer(byteint).byteValue();
+ }
+ return b;
+ }
+
+ /**
+ * 字节数组转换为十六进制字符串
+ *
+ * @param b byte[] 需要转换的字节数组
+ * @return String 十六进制字符串
+ */
+ public static String byteToHex(byte b[]) {
+ if (b == null) {
+ throw new IllegalArgumentException(
+ "Argument b ( byte array ) is null! ");
+ }
+ String hs = "";
+ String stmp = "";
+ for (int n = 0; n < b.length; n++) {
+ stmp = Integer.toHexString(b[n] & 0xff);
+ if (stmp.length() == 1) {
+ hs = hs + "0" + stmp;
+ } else {
+ hs = hs + stmp;
+ }
+ }
+// return hs.toLowerCase();
+ return hs.toUpperCase();
+ }
+
+ public static byte[] subByte(byte[] input, int startIndex, int length) {
+ byte[] bt = new byte[length];
+ for (int i = 0; i < length; i++) {
+ bt[i] = input[i + startIndex];
+ }
+ return bt;
+ }
+}
diff --git a/ssl/src/main/java/cn/org/landcloud/security/sm2/Cipher.java b/ssl/src/main/java/cn/org/landcloud/security/sm2/Cipher.java
new file mode 100644
index 0000000..d834bf4
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/sm2/Cipher.java
@@ -0,0 +1,107 @@
+package cn.org.landcloud.security.sm2;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.digests.SM3Digest;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.math.BigInteger;
+
+import cn.org.landcloud.security.Util;
+
+public class Cipher {
+ private int ct;
+ private ECPoint p2;
+ private SM3Digest sm3keybase;
+ private SM3Digest sm3c3;
+ private byte key[];
+ private byte keyOff;
+
+ public Cipher()
+ {
+ this.ct = 1;
+ this.key = new byte[32];
+ this.keyOff = 0;
+ }
+
+ private void Reset()
+ {
+ this.sm3keybase = new SM3Digest();
+ this.sm3c3 = new SM3Digest();
+
+ byte p[] = Util.byteConvert32Bytes(p2.normalize().getXCoord().toBigInteger());
+ this.sm3keybase.update(p, 0, p.length);
+ this.sm3c3.update(p, 0, p.length);
+
+ p = Util.byteConvert32Bytes(p2.normalize().getYCoord().toBigInteger());
+ this.sm3keybase.update(p, 0, p.length);
+ this.ct = 1;
+ NextKey();
+ }
+
+ private void NextKey()
+ {
+ SM3Digest sm3keycur = new SM3Digest(this.sm3keybase);
+ sm3keycur.update((byte) (ct >> 24 & 0xff));
+ sm3keycur.update((byte) (ct >> 16 & 0xff));
+ sm3keycur.update((byte) (ct >> 8 & 0xff));
+ sm3keycur.update((byte) (ct & 0xff));
+ sm3keycur.doFinal(key, 0);
+ this.keyOff = 0;
+ this.ct++;
+ }
+
+ public ECPoint Init_enc(SM2 sm2, ECPoint userKey)
+ {
+ AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair();
+ ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate();
+ ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic();
+ BigInteger k = ecpriv.getD();
+ ECPoint c1 = ecpub.getQ();
+ this.p2 = userKey.multiply(k);
+ Reset();
+ return c1;
+ }
+
+ public void Encrypt(byte data[])
+ {
+ this.sm3c3.update(data, 0, data.length);
+ for (int i = 0; i < data.length; i++)
+ {
+ if (keyOff == key.length)
+ {
+ NextKey();
+ }
+ data[i] ^= key[keyOff++];
+ }
+ }
+
+ public void Init_dec(BigInteger userD, ECPoint c1)
+ {
+ this.p2 = c1.multiply(userD);
+ Reset();
+ }
+
+ public void Decrypt(byte data[])
+ {
+ for (int i = 0; i < data.length; i++)
+ {
+ if (keyOff == key.length)
+ {
+ NextKey();
+ }
+ data[i] ^= key[keyOff++];
+ }
+
+ this.sm3c3.update(data, 0, data.length);
+ }
+
+ public void Dofinal(byte c3[])
+ {
+ byte p[] = Util.byteConvert32Bytes(p2.normalize().getYCoord().toBigInteger());
+ this.sm3c3.update(p, 0, p.length);
+ this.sm3c3.doFinal(c3, 0);
+ Reset();
+ }
+}
diff --git a/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2.java b/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2.java
new file mode 100644
index 0000000..54980e7
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2.java
@@ -0,0 +1,67 @@
+package cn.org.landcloud.security.sm2;
+
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECFieldElement.Fp;
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+public class SM2 {
+
+ //国密参数
+ public static String[] ecc_param = {
+ "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",
+ "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",
+ "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",
+ "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",
+ "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",
+ "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"
+ };
+
+ public static SM2 Instance()
+ {
+ return new SM2();
+ }
+
+ public final BigInteger ecc_p;
+ public final BigInteger ecc_a;
+ public final BigInteger ecc_b;
+ public final BigInteger ecc_n;
+ public final BigInteger ecc_gx;
+ public final BigInteger ecc_gy;
+ public final ECCurve ecc_curve;
+ public final ECPoint ecc_point_g;
+ public final ECDomainParameters ecc_bc_spec;
+ public final ECKeyPairGenerator ecc_key_pair_generator;
+ public final ECFieldElement ecc_gx_fieldelement;
+ public final ECFieldElement ecc_gy_fieldelement;
+
+ public SM2()
+ {
+ this.ecc_p = new BigInteger(ecc_param[0], 16);
+ this.ecc_a = new BigInteger(ecc_param[1], 16);
+ this.ecc_b = new BigInteger(ecc_param[2], 16);
+ this.ecc_n = new BigInteger(ecc_param[3], 16);
+ this.ecc_gx = new BigInteger(ecc_param[4], 16);
+ this.ecc_gy = new BigInteger(ecc_param[5], 16);
+
+ this.ecc_gx_fieldelement = new Fp(this.ecc_p, this.ecc_gx);
+ this.ecc_gy_fieldelement = new Fp(this.ecc_p, this.ecc_gy);
+
+ this.ecc_curve = new ECCurve.Fp(this.ecc_p, this.ecc_a, this.ecc_b);
+ this.ecc_point_g = new ECPoint.Fp(this.ecc_curve, this.ecc_gx_fieldelement, this.ecc_gy_fieldelement,false);
+
+ this.ecc_bc_spec = new ECDomainParameters(this.ecc_curve, this.ecc_point_g, this.ecc_n);
+
+ ECKeyGenerationParameters ecc_ecgenparam;
+ ecc_ecgenparam = new ECKeyGenerationParameters(this.ecc_bc_spec, new SecureRandom());
+
+ this.ecc_key_pair_generator = new ECKeyPairGenerator();
+ this.ecc_key_pair_generator.init(ecc_ecgenparam);
+ }
+}
diff --git a/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2EncDecUtils.java b/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2EncDecUtils.java
new file mode 100644
index 0000000..ba1ca87
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2EncDecUtils.java
@@ -0,0 +1,208 @@
+package cn.org.landcloud.security.sm2;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import cn.org.landcloud.security.Util;
+
+public class SM2EncDecUtils {
+ //生成随机秘钥对
+ public static SM2KeyVO generateKeyPair(){
+ SM2 sm2 = SM2.Instance();
+ AsymmetricCipherKeyPair key = null;
+ while (true){
+ key=sm2.ecc_key_pair_generator.generateKeyPair();
+ if(((ECPrivateKeyParameters) key.getPrivate()).getD().toByteArray().length==32){
+ break;
+ }
+ }
+ ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate();
+ ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic();
+ BigInteger privateKey = ecpriv.getD();
+ ECPoint publicKey = ecpub.getQ();
+ SM2KeyVO sm2KeyVO = new SM2KeyVO();
+ sm2KeyVO.setPublicKey(publicKey);
+ sm2KeyVO.setPrivateKey(privateKey);
+ //System.out.println("公钥: " + Util.byteToHex(publicKey.getEncoded()));
+ //System.out.println("私钥: " + Util.byteToHex(privateKey.toByteArray()));
+ return sm2KeyVO;
+ }
+
+ //数据加密
+ public static String encrypt(byte[] publicKey, byte[] data) throws IOException
+ {
+ if (publicKey == null || publicKey.length == 0)
+ {
+ return null;
+ }
+
+ if (data == null || data.length == 0)
+ {
+ return null;
+ }
+
+ byte[] source = new byte[data.length];
+ System.arraycopy(data, 0, source, 0, data.length);
+
+ Cipher cipher = new Cipher();
+ SM2 sm2 = SM2.Instance();
+ ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);
+
+ ECPoint c1 = cipher.Init_enc(sm2, userKey);
+ cipher.Encrypt(source);
+ byte[] c3 = new byte[32];
+ cipher.Dofinal(c3);
+
+// System.out.println("C1 " + Util.byteToHex(c1.getEncoded()));
+// System.out.println("C2 " + Util.byteToHex(source));
+// System.out.println("C3 " + Util.byteToHex(c3));
+ //C1 C2 C3拼装成加密字串
+ // C1 | C2 | C3
+ //return Util.byteToHex(c1.getEncoded()) + Util.byteToHex(source) + Util.byteToHex(c3);
+ // C1 | C3 | C2
+ return Util.byteToHex(c1.getEncoded()) + Util.byteToHex(c3) + Util.byteToHex(source);
+ }
+
+ //数据解密
+ public static byte[] decrypt(byte[] privateKey, byte[] encryptedData) throws IOException
+ {
+ if (privateKey == null || privateKey.length == 0)
+ {
+ return null;
+ }
+
+ if (encryptedData == null || encryptedData.length == 0)
+ {
+ return null;
+ }
+ //加密字节数组转换为十六进制的字符串 长度变为encryptedData.length * 2
+ String data = Util.byteToHex(encryptedData);
+ /***分解加密字串 C1 | C2 | C3
+ * (C1 = C1标志位2位 + C1实体部分128位 = 130)
+ * (C3 = C3实体部分64位 = 64)
+ * (C2 = encryptedData.length * 2 - C1长度 - C2长度)
+
+ byte[] c1Bytes = Util.hexToByte(data.substring(0,130));
+ int c2Len = encryptedData.length - 97;
+ byte[] c2 = Util.hexToByte(data.substring(130,130 + 2 * c2Len));
+ byte[] c3 = Util.hexToByte(data.substring(130 + 2 * c2Len,194 + 2 * c2Len));
+ */
+ /***分解加密字串 C1 | C3 | C2
+ * (C1 = C1标志位2位 + C1实体部分128位 = 130)
+ * (C3 = C3实体部分64位 = 64)
+ * (C2 = encryptedData.length * 2 - C1长度 - C2长度)
+ */
+ byte[] c1Bytes = Util.hexToByte(data.substring(0,130));
+ int c2Len = encryptedData.length - 97;
+ byte[] c3 = Util.hexToByte(data.substring(130,130 + 64));
+ byte[] c2 = Util.hexToByte(data.substring(194,194 + 2 * c2Len));
+
+ SM2 sm2 = SM2.Instance();
+ BigInteger userD = new BigInteger(1, privateKey);
+
+ //通过C1实体字节来生成ECPoint
+ ECPoint c1 = sm2.ecc_curve.decodePoint(c1Bytes);
+ Cipher cipher = new Cipher();
+ cipher.Init_dec(userD, c1);
+ cipher.Decrypt(c2);
+ cipher.Dofinal(c3);
+
+ //返回解密结果
+ return c2;
+ }
+
+/* public static BigInteger[] Sm2Sign(byte[] md, AsymmetricCipherKeyPair keypair)
+ {
+ SM3Digest sm3 = new SM3Digest();
+
+ ECPublicKeyParameters ecpub = (ECPublicKeyParameters)keypair.getPublic();
+
+ byte[] z = SM2CryptoServiceProvider.Sm2GetZ(Encoding.Default.GetBytes(SM2CryptoServiceProvider.userId), ecpub.getQ());
+ sm3.update(z, 0, z.length);
+
+ byte[] p = md;
+ sm3.update(p, 0, p.length);
+
+ byte[] hashData = new byte[32];
+ sm3.doFinal(hashData, 0);
+
+ // e
+ BigInteger e = new BigInteger(1, hashData);
+ // k
+ BigInteger k = null;
+ ECPoint kp = null;
+ BigInteger r = null;
+ BigInteger s = null;
+ BigInteger userD = null;
+
+ do
+ {
+ do
+ {
+
+ ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)keypair.getPrivate();
+ k = ecpriv.getD();
+ kp = ecpub.getQ();
+
+ userD = ecpriv.getD();
+
+ // r
+ r = e.add(kp.getX().toBigInteger());
+ r = r.mod(ecc_n);
+ }
+ while (r.equals(BigInteger.ZERO) || r.add(k).equals(ecc_n));
+
+ // (1 + dA)~-1
+ BigInteger da_1 = userD.add(BigInteger.ONE);
+ da_1 = da_1.modInverse(ecc_n);
+ // s
+ s = r.multiply(userD);
+ s = k.subtract(s).mod(ecc_n);
+ s = da_1.multiply(s).mod(ecc_n);
+ }
+ while (s.equals(BigInteger.ZERO));
+
+ byte[] btRS = new byte[64];
+ byte[] btR = r.toByteArray();
+ byte[] btS = s.toByteArray();
+ Array.Copy(btR, btR.length - 32, btRS, 0, 32);
+ Array.Copy(btS, btS.length - 32, btRS, 32, 32);
+
+ return new BigInteger[] { r, s };
+ }*/
+
+ public static void main(String[] args) throws Exception
+ {
+ String plainText = "ILoveYou11";
+ //SM3测试
+ //生成密钥对
+ //generateKeyPair();
+ byte[] sourceData = plainText.getBytes();
+
+ //下面的秘钥可以使用generateKeyPair()生成的秘钥内容
+ // 国密规范正式私钥
+ //String prik = "3690655E33D5EA3D9A4AE1A1ADD766FDEA045CDEAA43A9206FB8C430CEFE0D94";
+ // 国密规范正式公钥
+ //String pubk = "04F6E0C3345AE42B51E06BF50B98834988D54EBC7460FE135A48171BC0629EAE205EEDE253A530608178A98F1E19BB737302813BA39ED3FA3C51639D7A20C7391A";
+
+ String prik = "4cf170068e9c47ebdb521fb9fc62c4a55a5773fb9da33b0acf8129e28d09d205";
+ String pubk = "04aabda53043e8dcb86d42f690b61a4db869821dadf9f851ec3c5c43d0c8f95a6677fdba984afc3bb010a8436b1d17cefc2011a34e01e9e801124d29ffa928d803";
+ String publicKey ="04BB34D657EE7E8490E66EF577E6B3CEA28B739511E787FB4F71B7F38F241D87F18A5A93DF74E90FF94F4EB907F271A36B295B851F971DA5418F4915E2C1A23D6E";
+ String privatekey = "0B1CE43098BC21B8E82B5C065EDB534CB86532B1900A49D49F3C53762D2997FA";
+ prik=privatekey;
+ pubk=publicKey;
+ System.out.println("加密: ");
+ String cipherText = SM2EncDecUtils.encrypt(Util.hexToByte(pubk), sourceData);
+ //cipherText = "0452ba81cf5119c9f29c81c2be9c4a49ad8c0a33ed899b60548d21a62971a8e994cafc0e9fbc710a0a220b055804bb890833b50ac04ec4e130a5db75338c0c1d49a52a6d373076a5db370564a5cebb5300f79877003c52adf49dac16370e51e14e0754110547bb3b";
+ System.out.println(cipherText);
+ System.out.println("解密: ");
+ plainText = new String(SM2EncDecUtils.decrypt(Util.hexToByte(prik), Util.hexToByte(cipherText)));
+ System.out.println(plainText);
+
+ }
+}
\ No newline at end of file
diff --git a/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2Factory.java b/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2Factory.java
new file mode 100644
index 0000000..0bbbd1a
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2Factory.java
@@ -0,0 +1,179 @@
+package cn.org.landcloud.security.sm2;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.digests.SM3Digest;
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECFieldElement.Fp;
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import cn.org.landcloud.security.Util;
+
+
+public class SM2Factory {
+ /*-----------------------国密算法相关参数begin-----------
+ * ------------------*/
+ //A 第一系数
+ private static final BigInteger a = new BigInteger("fffffffeffffffffffffffffffffffffffffffff00000000fffffffffffffffc",16);
+ //B 第二系数
+ private static final BigInteger b = new BigInteger("28e9fa9e9d9f5e344d5a9e4bcf6509a7f39789f515ab8f92ddbcbd414d940e93",16);
+ //曲线X系数
+ private static final BigInteger gx = new BigInteger("32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",16);
+ //曲线Y系数
+ private static final BigInteger gy = new BigInteger("bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",16);
+ //生产者顺序系数
+ private static final BigInteger n = new BigInteger("fffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123",16);
+ //素数
+ private static final BigInteger p = new BigInteger("fffffffeffffffffffffffffffffffffffffffff00000000ffffffffffffffff",16);
+ //因子系数 1
+ private static final int h = 1;
+ /*-----------------------国密算法相关参数end-----------------------------*/
+ //一些必要类
+ public final ECFieldElement ecc_gx_fieldelement;
+ public final ECFieldElement ecc_gy_fieldelement;
+ public final ECCurve ecc_curve;
+ public final ECPoint ecc_point_g;
+ public final ECDomainParameters ecc_bc_spec;
+ public final ECKeyPairGenerator ecc_key_pair_generator;
+ /**
+ * 初始化方法
+ * @return
+ */
+ public static SM2Factory getInstance(){
+ return new SM2Factory();
+ }
+ public SM2Factory() {
+
+ this.ecc_gx_fieldelement = new Fp(this.p,this.gx);
+ this.ecc_gy_fieldelement = new Fp(this.p, this.gy);
+
+ this.ecc_curve = new ECCurve.Fp(this.p, this.a, this.b);
+
+ this.ecc_point_g = new ECPoint.Fp(this.ecc_curve, this.ecc_gx_fieldelement,this.ecc_gy_fieldelement,false);
+ this.ecc_bc_spec = new ECDomainParameters(this.ecc_curve, this.ecc_point_g, this.n);
+
+ ECKeyGenerationParameters ecc_ecgenparam;
+ ecc_ecgenparam = new ECKeyGenerationParameters(this.ecc_bc_spec, new SecureRandom());
+
+ this.ecc_key_pair_generator = new ECKeyPairGenerator();
+ this.ecc_key_pair_generator.init(ecc_ecgenparam);
+ }
+ /**
+ * 根据私钥、曲线参数计算Z
+ * @param userId
+ * @param userKey
+ * @return
+ */
+ public byte[] sm2GetZ(byte[] userId, ECPoint userKey){
+ SM3Digest sm3 = new SM3Digest();
+
+ int len = userId.length * 8;
+ sm3.update((byte) (len >> 8 & 0xFF));
+ sm3.update((byte) (len & 0xFF));
+ sm3.update(userId, 0, userId.length);
+
+ byte[] p = Util.byteConvert32Bytes(this.a);
+ sm3.update(p, 0, p.length);
+
+ p = Util.byteConvert32Bytes(this.b);
+ sm3.update(p, 0, p.length);
+
+ p = Util.byteConvert32Bytes(this.gx);
+ sm3.update(p, 0, p.length);
+
+ p = Util.byteConvert32Bytes(this.gy);
+ sm3.update(p, 0, p.length);
+
+ p = Util.byteConvert32Bytes(userKey.normalize().getXCoord().toBigInteger());
+ sm3.update(p, 0, p.length);
+
+ p = Util.byteConvert32Bytes(userKey.normalize().getYCoord().toBigInteger());
+ sm3.update(p, 0, p.length);
+
+ byte[] md = new byte[sm3.getDigestSize()];
+ sm3.doFinal(md, 0);
+ return md;
+ }
+ /**
+ * 签名相关值计算
+ * @param md
+ * @param userD
+ * @param userKey
+ * @param sm2Result
+ */
+ public void sm2Sign(byte[] md, BigInteger userD, ECPoint userKey, SM2Result sm2Result) {
+ BigInteger e = new BigInteger(1, md);
+ BigInteger k = null;
+ ECPoint kp = null;
+ BigInteger r = null;
+ BigInteger s = null;
+ do {
+ do {
+ // 正式环境
+ AsymmetricCipherKeyPair keypair = ecc_key_pair_generator.generateKeyPair();
+ ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) keypair.getPrivate();
+ ECPublicKeyParameters ecpub = (ECPublicKeyParameters) keypair.getPublic();
+ k = ecpriv.getD();
+ kp = ecpub.getQ();
+ //System.out.println("BigInteger:" + k + "\nECPoint:" + kp);
+
+ //System.out.println("计算曲线点X1: "+ kp.getXCoord().toBigInteger().toString(16));
+ //System.out.println("计算曲线点Y1: "+ kp.getYCoord().toBigInteger().toString(16));
+ //System.out.println("");
+ // r
+ r = e.add(kp.getXCoord().toBigInteger());
+ r = r.mod(this.n);
+ } while (r.equals(BigInteger.ZERO) || r.add(k).equals(this.n)||r.toString(16).length()!=64);
+
+ // (1 + dA)~-1
+ BigInteger da_1 = userD.add(BigInteger.ONE);
+ da_1 = da_1.modInverse(this.n);
+ // s
+ s = r.multiply(userD);
+ s = k.subtract(s).mod(this.n);
+ s = da_1.multiply(s).mod(this.n);
+ } while (s.equals(BigInteger.ZERO)||(s.toString(16).length()!=64));
+
+ sm2Result.r = r;
+ sm2Result.s = s;
+ }
+ /**
+ * 验签
+ * @param md sm3摘要
+ * @param userKey 根据公钥decode一个ecpoint对象
+ * @param r 没有特殊含义
+ * @param s 没有特殊含义
+ * @param sm2Result 接收参数的对象
+ */
+ public void sm2Verify(byte md[], ECPoint userKey, BigInteger r,
+ BigInteger s, SM2Result sm2Result) {
+ sm2Result.R = null;
+ BigInteger e = new BigInteger(1, md);
+ BigInteger t = r.add(s).mod(this.n);
+ if (t.equals(BigInteger.ZERO)) {
+ return;
+ } else {
+ ECPoint x1y1 = ecc_point_g.multiply(sm2Result.s);
+ //System.out.println("计算曲线点X0: "+ x1y1.normalize().getXCoord().toBigInteger().toString(16));
+ //System.out.println("计算曲线点Y0: "+ x1y1.normalize().getYCoord().toBigInteger().toString(16));
+ //System.out.println("");
+
+ x1y1 = x1y1.add(userKey.multiply(t));
+ //System.out.println("计算曲线点X1: "+ x1y1.normalize().getXCoord().toBigInteger().toString(16));
+ //System.out.println("计算曲线点Y1: "+ x1y1.normalize().getYCoord().toBigInteger().toString(16));
+ //System.out.println("");
+ sm2Result.R = e.add(x1y1.normalize().getXCoord().toBigInteger()).mod(this.n);
+ //System.out.println("R: " + sm2Result.R.toString(16));
+ return;
+ }
+ }
+
+}
diff --git a/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2KeyVO.java b/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2KeyVO.java
new file mode 100644
index 0000000..b7d3ff4
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2KeyVO.java
@@ -0,0 +1,42 @@
+package cn.org.landcloud.security.sm2;
+
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.math.BigInteger;
+
+import cn.org.landcloud.security.Util;
+
+public class SM2KeyVO {
+ BigInteger privateKey ;
+ ECPoint publicKey ;
+
+ public BigInteger getPrivateKey() {
+ return privateKey;
+ }
+
+ public void setPrivateKey(BigInteger privateKey) {
+ this.privateKey = privateKey;
+ }
+
+ public ECPoint getPublicKey() {
+ return publicKey;
+ }
+
+ public void setPublicKey(ECPoint publicKey) {
+ this.publicKey = publicKey;
+ }
+
+
+ //HardPubKey:3059301306072A8648CE3D020106082A811CCF5501822D03420004+X+Y
+ //SoftPubKey:04+X+Y
+ public String getPubHexInSoft(){
+ return Util.byteToHex(publicKey.getEncoded());
+ //System.out.println("公钥: " + );
+ }
+ /*public String getPubHexInHard(){
+ return SecurityTestAll.SM2PubHardKeyHead +Util.byteToHex(publicKey.getEncoded());
+ }*/
+ public String getPriHexInSoft(){
+ return Util.byteToHex(privateKey.toByteArray());
+ }
+}
diff --git a/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2Result.java b/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2Result.java
new file mode 100644
index 0000000..e356d77
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2Result.java
@@ -0,0 +1,28 @@
+package cn.org.landcloud.security.sm2;
+
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.math.BigInteger;
+
+/**
+ * SM2
+ */
+public class SM2Result {
+ public SM2Result() {
+ }
+ // 签名r
+ public BigInteger r;
+ public BigInteger s;
+ //验签R
+ public BigInteger R;
+
+ // 密钥交换
+ public byte[] sa;
+ public byte[] sb;
+ public byte[] s1;
+ public byte[] s2;
+
+ public ECPoint keyra;
+ public ECPoint keyrb;
+
+}
diff --git a/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2SignVO.java b/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2SignVO.java
new file mode 100644
index 0000000..5593b64
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2SignVO.java
@@ -0,0 +1,116 @@
+package cn.org.landcloud.security.sm2;
+
+/**
+ * SM2签名所计算的值 可以根据实际情况增加删除字段属性
+ */
+public class SM2SignVO {
+ //16进制的私钥
+ public String sm2_userd;
+ //椭圆曲线点X
+ public String x_coord;
+ //椭圆曲线点Y
+ public String y_coord;
+ //SM3摘要Z
+ public String sm3_z;
+ //明文数据16进制
+ public String sign_express;
+ //SM3摘要值
+ public String sm3_digest;
+ //R
+ public String sign_r;
+ //S
+ public String sign_s;
+ //R
+ public String verify_r;
+ //S
+ public String verify_s;
+ //签名值
+ public String sm2_sign;
+ //sign 签名 verfiy验签
+ public String sm2_type;
+ //是否验签成功 true false
+ public boolean isVerify;
+ public String getX_coord() {
+ return x_coord;
+ }
+ public void setX_coord(String x_coord) {
+ this.x_coord = x_coord;
+ }
+ public String getY_coord() {
+ return y_coord;
+ }
+ public void setY_coord(String y_coord) {
+ this.y_coord = y_coord;
+ }
+ public String getSm3_z() {
+ return sm3_z;
+ }
+ public void setSm3_z(String sm3_z) {
+ this.sm3_z = sm3_z;
+ }
+ public String getSm3_digest() {
+ return sm3_digest;
+ }
+ public void setSm3_digest(String sm3_digest) {
+ this.sm3_digest = sm3_digest;
+ }
+ public String getSm2_signForSoft() {
+ return sm2_sign;
+ }
+ public String getSm2_signForHard() {
+ //System.out.println("R:"+getSign_r());
+ //System.out.println("s:"+getSign_s());
+ return getSign_r()+getSign_s();
+ }
+ public void setSm2_sign(String sm2_sign) {
+ this.sm2_sign = sm2_sign;
+ }
+ public String getSign_express() {
+ return sign_express;
+ }
+ public void setSign_express(String sign_express) {
+ this.sign_express = sign_express;
+ }
+ public String getSm2_userd() {
+ return sm2_userd;
+ }
+ public void setSm2_userd(String sm2_userd) {
+ this.sm2_userd = sm2_userd;
+ }
+ public String getSm2_type() {
+ return sm2_type;
+ }
+ public void setSm2_type(String sm2_type) {
+ this.sm2_type = sm2_type;
+ }
+ public boolean isVerify() {
+ return isVerify;
+ }
+ public void setVerify(boolean isVerify) {
+ this.isVerify = isVerify;
+ }
+ public String getSign_r() {
+ return sign_r;
+ }
+ public void setSign_r(String sign_r) {
+ this.sign_r = sign_r;
+ }
+ public String getSign_s() {
+ return sign_s;
+ }
+ public void setSign_s(String sign_s) {
+ this.sign_s = sign_s;
+ }
+ public String getVerify_r() {
+ return verify_r;
+ }
+ public void setVerify_r(String verify_r) {
+ this.verify_r = verify_r;
+ }
+ public String getVerify_s() {
+ return verify_s;
+ }
+ public void setVerify_s(String verify_s) {
+ this.verify_s = verify_s;
+ }
+}
diff --git a/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2SignVerUtils.java b/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2SignVerUtils.java
new file mode 100644
index 0000000..96fa144
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/sm2/SM2SignVerUtils.java
@@ -0,0 +1,166 @@
+package cn.org.landcloud.security.sm2;
+
+import org.bouncycastle.asn1.*;
+import org.bouncycastle.crypto.digests.SM3Digest;
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.util.Enumeration;
+
+import cn.org.landcloud.security.Util;
+import cn.org.landcloud.security.sm3.SM3Utils;
+
+/**
+ * 国密算法的签名、验签
+ */
+public class SM2SignVerUtils {
+ /**
+ * 默认USERID
+ */
+ public static String USER_ID = "1234567812345678";
+ /**
+ * 私钥签名
+ * 使用SM3进行对明文数据计算一个摘要值
+ * @param privatekey 私钥
+ * @param sourceData 明文数据
+ * @return 签名后的值
+ * @throws Exception
+ */
+ public static SM2SignVO Sign2SM2(byte[] privatekey,byte[] sourceData) throws Exception{
+ SM2SignVO sm2SignVO = new SM2SignVO();
+ sm2SignVO.setSm2_type("sign");
+ SM2Factory factory = SM2Factory.getInstance();
+ BigInteger userD = new BigInteger(1,privatekey);
+ //System.out.println("userD:"+userD.toString(16));
+ sm2SignVO.setSm2_userd(userD.toString(16));
+
+ ECPoint userKey = factory.ecc_point_g.multiply(userD);
+ //System.out.println("椭圆曲线点X: "+ userKey.getXCoord().toBigInteger().toString(16));
+ //System.out.println("椭圆曲线点Y: "+ userKey.getYCoord().toBigInteger().toString(16));
+
+ SM3Digest sm3Digest = new SM3Digest();
+ byte [] z = factory.sm2GetZ(USER_ID.getBytes(), userKey);
+ //System.out.println("SM3摘要Z: " + Util.getHexString(z));
+ //System.out.println("被加密数据的16进制: " + Util.getHexString(sourceData));
+ sm2SignVO.setSm3_z(Util.getHexString(z));
+ sm2SignVO.setSign_express(Util.getHexString(sourceData));
+
+ sm3Digest.update(z, 0, z.length);
+ sm3Digest.update(sourceData,0,sourceData.length);
+ byte [] md = new byte[32];
+ sm3Digest.doFinal(md, 0);
+ //System.out.println("SM3摘要值: " + Util.getHexString(md));
+ sm2SignVO.setSm3_digest(Util.getHexString(md));
+
+ SM2Result sm2Result = new SM2Result();
+ factory.sm2Sign(md, userD, userKey, sm2Result);
+ //System.out.println("r: " + sm2Result.r.toString(16));
+ //System.out.println("s: " + sm2Result.s.toString(16));
+// sm2SignVO.setSign_r(sm2Result.r.toString(16));
+// sm2SignVO.setSign_s(sm2Result.s.toString(16));
+
+ sm2SignVO.setSign_r(Util.byteToHex(sm2Result.r.toByteArray()));
+ sm2SignVO.setSign_s(Util.byteToHex(sm2Result.s.toByteArray()));
+
+ ASN1Integer d_r = new ASN1Integer(sm2Result.r);
+ ASN1Integer d_s = new ASN1Integer(sm2Result.s);
+ ASN1EncodableVector v2 = new ASN1EncodableVector();
+ v2.add(d_r);
+ v2.add(d_s);
+ DERSequence sign = new DERSequence(v2);
+ String result = Util.byteToHex(sign.getEncoded());
+ sm2SignVO.setSm2_sign(result);
+ return sm2SignVO;
+ }
+ /**
+ * 验证签名
+ * @param publicKey 公钥信息
+ * @param sourceData 密文信息
+ * @param signData 签名信息
+ * @return 验签的对象 包含了相关参数和验签结果
+ */
+ @SuppressWarnings("unchecked")
+ public static SM2SignVO VerifySignSM2(byte[] publicKey,byte[] sourceData,byte[] signData){
+ try {
+ byte[] formatedPubKey;
+ SM2SignVO verifyVo = new SM2SignVO();
+ verifyVo.setSm2_type("verify");
+ if (publicKey.length == 64) {
+ // 添加一字节标识,用于ECPoint解析
+ formatedPubKey = new byte[65];
+ formatedPubKey[0] = 0x04;
+ System.arraycopy(publicKey, 0, formatedPubKey, 1, publicKey.length);
+ } else{
+ formatedPubKey = publicKey;
+ }
+ SM2Factory factory = SM2Factory.getInstance();
+ ECPoint userKey = factory.ecc_curve.decodePoint(formatedPubKey);
+
+ SM3Digest sm3Digest = new SM3Digest();
+ byte [] z = factory.sm2GetZ(USER_ID.getBytes(), userKey);
+ //System.out.println("SM3摘要Z: " + Util.getHexString(z));
+ verifyVo.setSm3_z(Util.getHexString(z));
+ sm3Digest.update(z,0,z.length);
+ sm3Digest.update(sourceData,0,sourceData.length);
+ byte [] md = new byte[32];
+ sm3Digest.doFinal(md, 0);
+ //System.out.println("SM3摘要值: " + Util.getHexString(md));
+ verifyVo.setSm3_digest(Util.getHexString(md));
+ ByteArrayInputStream bis = new ByteArrayInputStream(signData);
+ ASN1InputStream dis = new ASN1InputStream(bis);
+ SM2Result sm2Result = null;
+ ASN1Primitive derObj = dis.readObject();
+ Enumeration e = ((ASN1Sequence)derObj).getObjects();
+ BigInteger r = ((ASN1Integer) e.nextElement()).getValue();
+ BigInteger s = ((ASN1Integer) e.nextElement()).getValue();
+ sm2Result = new SM2Result();
+ sm2Result.r = r;
+ sm2Result.s = s;
+ //System.out.println("vr: " + sm2Result.r.toString(16));
+ //System.out.println("vs: " + sm2Result.s.toString(16));
+// verifyVo.setVerify_r(sm2Result.r.toString(16));
+// verifyVo.setVerify_s(sm2Result.s.toString(16));
+
+ verifyVo.setVerify_r(Util.byteToHex(sm2Result.r.toByteArray()));
+ verifyVo.setVerify_s(Util.byteToHex(sm2Result.s.toByteArray()));
+
+ factory.sm2Verify(md, userKey, sm2Result.r, sm2Result.s, sm2Result);
+ boolean verifyFlag = sm2Result.r.equals(sm2Result.R);
+ verifyVo.setVerify(verifyFlag);
+ return verifyVo;
+ } catch (IllegalArgumentException e) {
+ return null;
+ }catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+ public static void main(String[] args) throws Exception {
+
+// String text = "913301106798859401,杭州今奥信息科技股份有限公司,1001,12100000400010398P,中国国土勘测规划院,1697269051,047828D5466AB6AF21FD8528A901622AE100A3ED80E2AFEFAA093BEDF3662C5327305DAA0A853770BA0605F6266B05535ECCC7747BAA122F185C47D9F65E74E60C";
+ String text = "0D9E4559EAA489079CA46A11BAD09528BDE0E2390226FD825AE1AC10DEA8FFEA,2022-10-18 15:42:56,119.98547,30.277615,41.0,239.0,2.0,test,1001";
+// byte [] sourceData = SM3Utils.sm3(text.toString()).getBytes(StandardCharsets.UTF_8);
+ byte [] sourceData = text.getBytes(StandardCharsets.UTF_8);
+ //String publicKey ="FA05C51AD1162133DFDF862ECA5E4A481B52FB37FF83E53D45FD18BBD6F32668A92C4692EEB305684E3B9D4ACE767F91D5D108234A9F07936020A92210BA9447";
+ //String privatekey = "5EB4DF17021CC719B678D970C620690A11B29C8357D71FA4FF9BF7FB6D89767A";
+// String publicKey ="04BB34D657EE7E8490E66EF577E6B3CEA28B739511E787FB4F71B7F38F241D87F18A5A93DF74E90FF94F4EB907F271A36B295B851F971DA5418F4915E2C1A23D6E";
+// String privatekey = "0B1CE43098BC21B8E82B5C065EDB534CB86532B1900A49D49F3C53762D2997FA";
+
+// String publicKey = "048D9B68E8DECD33A19A73F87169BB4A65BFE557AA1F58462BAF5D63EFBC5EE5385D1184C3B2A5A0D6EE2ED401182D0E6AF362DCAA8931231399D941AD318D21AD";
+// String privatekey = "2DD789162386F0E0508D0B4AAE26BE9D7D4075CCBE7536200174FF8D200124B1";
+
+ String publicKey = "047828D5466AB6AF21FD8528A901622AE100A3ED80E2AFEFAA093BEDF3662C5327305DAA0A853770BA0605F6266B05535ECCC7747BAA122F185C47D9F65E74E60C";
+ String privatekey = "00D7D395E6093C63A50A0370D5F6CD89964AB148A846EA96C9969AF08EC1E97513";
+
+// String publicKey ="047828D5466AB6AF21FD8528A901622AE100A3ED80E2AFEFAA093BEDF3662C5327305DAA0A853770BA0605F6266B05535ECCC7747BAA122F185C47D9F65E74E60C";
+// String sign = "3046022100A1C51CF002DB42A35AFE36E43E63BDC920397EB101D6A62EC5E016FC4EFFE273022100C3594CBB9D4A5401833F0057F4EC77B41899699C98F5BEF0F079B3BA9D3EB324";
+ SM2SignVO sign = SM2SignVerUtils.Sign2SM2(Util.hexStringToBytes(privatekey), sourceData);
+// SM2SignVO verify = SM2SignVerUtils.VerifySignSM2(Util.hexStringToBytes(publicKey), SM3Utils.sm3(text.toString()).getBytes(StandardCharsets.UTF_8), Util.hexStringToBytes(sign));
+ SM2SignVO verify = SM2SignVerUtils.VerifySignSM2(Util.hexStringToBytes(publicKey), sourceData, Util.hexStringToBytes(sign.getSm2_signForSoft()));
+ System.out.println("签名得到的r值:"+sign.getSign_r()+"\n签名值 "+sign.getSm2_signForSoft());
+ System.out.println("验签得到的R值:"+verify.getVerify_r());
+ System.err.println("\n验签结果" +verify.isVerify());
+ }
+}
diff --git a/ssl/src/main/java/cn/org/landcloud/security/sm3/SM3.java b/ssl/src/main/java/cn/org/landcloud/security/sm3/SM3.java
new file mode 100644
index 0000000..66b549d
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/sm3/SM3.java
@@ -0,0 +1,328 @@
+package cn.org.landcloud.security.sm3;
+
+import org.bouncycastle.crypto.digests.SM3Digest;
+import org.bouncycastle.util.encoders.Hex;
+
+import cn.org.landcloud.security.Util;
+
+/**
+ * Created by $(USER) on $(DATE)
+ */
+public class SM3 {
+ public static final byte[] iv = { 0x73, (byte) 0x80, 0x16, 0x6f, 0x49,
+ 0x14, (byte) 0xb2, (byte) 0xb9, 0x17, 0x24, 0x42, (byte) 0xd7,
+ (byte) 0xda, (byte) 0x8a, 0x06, 0x00, (byte) 0xa9, 0x6f, 0x30,
+ (byte) 0xbc, (byte) 0x16, 0x31, 0x38, (byte) 0xaa, (byte) 0xe3,
+ (byte) 0x8d, (byte) 0xee, 0x4d, (byte) 0xb0, (byte) 0xfb, 0x0e,
+ 0x4e };
+
+ public static int[] Tj = new int[64];
+
+ static
+ {
+ for (int i = 0; i < 16; i++)
+ {
+ Tj[i] = 0x79cc4519;
+ }
+
+ for (int i = 16; i < 64; i++)
+ {
+ Tj[i] = 0x7a879d8a;
+ }
+ }
+
+ public static byte[] CF(byte[] V, byte[] B)
+ {
+ int[] v, b;
+ v = convert(V);
+ b = convert(B);
+ return convert(CF(v, b));
+ }
+
+ private static int[] convert(byte[] arr)
+ {
+ int[] out = new int[arr.length / 4];
+ byte[] tmp = new byte[4];
+ for (int i = 0; i < arr.length; i += 4)
+ {
+ System.arraycopy(arr, i, tmp, 0, 4);
+ out[i / 4] = bigEndianByteToInt(tmp);
+ }
+ return out;
+ }
+
+ private static byte[] convert(int[] arr)
+ {
+ byte[] out = new byte[arr.length * 4];
+ byte[] tmp = null;
+ for (int i = 0; i < arr.length; i++)
+ {
+ tmp = bigEndianIntToByte(arr[i]);
+ System.arraycopy(tmp, 0, out, i * 4, 4);
+ }
+ return out;
+ }
+
+ public static int[] CF(int[] V, int[] B)
+ {
+ int a, b, c, d, e, f, g, h;
+ int ss1, ss2, tt1, tt2;
+ a = V[0];
+ b = V[1];
+ c = V[2];
+ d = V[3];
+ e = V[4];
+ f = V[5];
+ g = V[6];
+ h = V[7];
+
+ int[][] arr = expand(B);
+ int[] w = arr[0];
+ int[] w1 = arr[1];
+
+ for (int j = 0; j < 64; j++)
+ {
+ ss1 = (bitCycleLeft(a, 12) + e + bitCycleLeft(Tj[j], j));
+ ss1 = bitCycleLeft(ss1, 7);
+ ss2 = ss1 ^ bitCycleLeft(a, 12);
+ tt1 = FFj(a, b, c, j) + d + ss2 + w1[j];
+ tt2 = GGj(e, f, g, j) + h + ss1 + w[j];
+ d = c;
+ c = bitCycleLeft(b, 9);
+ b = a;
+ a = tt1;
+ h = g;
+ g = bitCycleLeft(f, 19);
+ f = e;
+ e = P0(tt2);
+
+ /*System.out.print(j+" ");
+ System.out.print(Integer.toHexString(a)+" ");
+ System.out.print(Integer.toHexString(b)+" ");
+ System.out.print(Integer.toHexString(c)+" ");
+ System.out.print(Integer.toHexString(d)+" ");
+ System.out.print(Integer.toHexString(e)+" ");
+ System.out.print(Integer.toHexString(f)+" ");
+ System.out.print(Integer.toHexString(g)+" ");
+ System.out.print(Integer.toHexString(h)+" ");
+ System.out.println("");*/
+ }
+// System.out.println("");
+
+ int[] out = new int[8];
+ out[0] = a ^ V[0];
+ out[1] = b ^ V[1];
+ out[2] = c ^ V[2];
+ out[3] = d ^ V[3];
+ out[4] = e ^ V[4];
+ out[5] = f ^ V[5];
+ out[6] = g ^ V[6];
+ out[7] = h ^ V[7];
+
+ return out;
+ }
+
+ private static int[][] expand(int[] B)
+ {
+ int W[] = new int[68];
+ int W1[] = new int[64];
+ for (int i = 0; i < B.length; i++)
+ {
+ W[i] = B[i];
+ }
+
+ for (int i = 16; i < 68; i++)
+ {
+ W[i] = P1(W[i - 16] ^ W[i - 9] ^ bitCycleLeft(W[i - 3], 15))
+ ^ bitCycleLeft(W[i - 13], 7) ^ W[i - 6];
+ }
+
+ for (int i = 0; i < 64; i++)
+ {
+ W1[i] = W[i] ^ W[i + 4];
+ }
+
+ int arr[][] = new int[][] { W, W1 };
+ return arr;
+ }
+
+ private static byte[] bigEndianIntToByte(int num)
+ {
+ return back(Util.intToBytes(num));
+ }
+
+ private static int bigEndianByteToInt(byte[] bytes)
+ {
+ return Util.byteToInt(back(bytes));
+ }
+
+ private static int FFj(int X, int Y, int Z, int j)
+ {
+ if (j >= 0 && j <= 15)
+ {
+ return FF1j(X, Y, Z);
+ }
+ else
+ {
+ return FF2j(X, Y, Z);
+ }
+ }
+
+ private static int GGj(int X, int Y, int Z, int j)
+ {
+ if (j >= 0 && j <= 15)
+ {
+ return GG1j(X, Y, Z);
+ }
+ else
+ {
+ return GG2j(X, Y, Z);
+ }
+ }
+
+ // 逻辑位运算函数
+ private static int FF1j(int X, int Y, int Z)
+ {
+ int tmp = X ^ Y ^ Z;
+ return tmp;
+ }
+
+ private static int FF2j(int X, int Y, int Z)
+ {
+ int tmp = ((X & Y) | (X & Z) | (Y & Z));
+ return tmp;
+ }
+
+ private static int GG1j(int X, int Y, int Z)
+ {
+ int tmp = X ^ Y ^ Z;
+ return tmp;
+ }
+
+ private static int GG2j(int X, int Y, int Z)
+ {
+ int tmp = (X & Y) | (~X & Z);
+ return tmp;
+ }
+
+ private static int P0(int X)
+ {
+ int y = rotateLeft(X, 9);
+ y = bitCycleLeft(X, 9);
+ int z = rotateLeft(X, 17);
+ z = bitCycleLeft(X, 17);
+ int t = X ^ y ^ z;
+ return t;
+ }
+
+ private static int P1(int X)
+ {
+ int t = X ^ bitCycleLeft(X, 15) ^ bitCycleLeft(X, 23);
+ return t;
+ }
+
+ /**
+ * 对最后一个分组字节数据padding
+ *
+ * @param in
+ * @param bLen
+ * 分组个数
+ * @return
+ */
+ public static byte[] padding(byte[] in, int bLen)
+ {
+ int k = 448 - (8 * in.length + 1) % 512;
+ if (k < 0)
+ {
+ k = 960 - (8 * in.length + 1) % 512;
+ }
+ k += 1;
+ byte[] padd = new byte[k / 8];
+ padd[0] = (byte) 0x80;
+ long n = in.length * 8 + bLen * 512;
+ byte[] out = new byte[in.length + k / 8 + 64 / 8];
+ int pos = 0;
+ System.arraycopy(in, 0, out, 0, in.length);
+ pos += in.length;
+ System.arraycopy(padd, 0, out, pos, padd.length);
+ pos += padd.length;
+ byte[] tmp = back(Util.longToBytes(n));
+ System.arraycopy(tmp, 0, out, pos, tmp.length);
+ return out;
+ }
+
+ /**
+ * 字节数组逆序
+ *
+ * @param in
+ * @return
+ */
+ private static byte[] back(byte[] in)
+ {
+ byte[] out = new byte[in.length];
+ for (int i = 0; i < out.length; i++)
+ {
+ out[i] = in[out.length - i - 1];
+ }
+
+ return out;
+ }
+
+ public static int rotateLeft(int x, int n)
+ {
+ return (x << n) | (x >> (32 - n));
+ }
+
+ private static int bitCycleLeft(int n, int bitLen)
+ {
+ bitLen %= 32;
+ byte[] tmp = bigEndianIntToByte(n);
+ int byteLen = bitLen / 8;
+ int len = bitLen % 8;
+ if (byteLen > 0)
+ {
+ tmp = byteCycleLeft(tmp, byteLen);
+ }
+
+ if (len > 0)
+ {
+ tmp = bitSmall8CycleLeft(tmp, len);
+ }
+
+ return bigEndianByteToInt(tmp);
+ }
+
+ private static byte[] bitSmall8CycleLeft(byte[] in, int len)
+ {
+ byte[] tmp = new byte[in.length];
+ int t1, t2, t3;
+ for (int i = 0; i < tmp.length; i++)
+ {
+ t1 = (byte) ((in[i] & 0x000000ff) << len);
+ t2 = (byte) ((in[(i + 1) % tmp.length] & 0x000000ff) >> (8 - len));
+ t3 = (byte) (t1 | t2);
+ tmp[i] = (byte) t3;
+ }
+
+ return tmp;
+ }
+
+ private static byte[] byteCycleLeft(byte[] in, int byteLen)
+ {
+ byte[] tmp = new byte[in.length];
+ System.arraycopy(in, byteLen, tmp, 0, in.length - byteLen);
+ System.arraycopy(in, 0, tmp, in.length - byteLen, byteLen);
+ return tmp;
+ }
+
+ public static void main(String[] args) {
+ byte[] md = new byte[32];
+ byte[] msg1 = "ererfeiisgod".getBytes();
+ System.out.println(Util.byteToHex(msg1));
+ SM3Digest sm3 = new SM3Digest();
+ sm3.update(msg1, 0, msg1.length);
+ sm3.doFinal(md, 0);
+ String s = new String(Hex.encode(md));
+ System.out.println(s.toUpperCase());
+ }
+}
diff --git a/ssl/src/main/java/cn/org/landcloud/security/sm3/SM3Utils.java b/ssl/src/main/java/cn/org/landcloud/security/sm3/SM3Utils.java
new file mode 100644
index 0000000..29c9b60
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/sm3/SM3Utils.java
@@ -0,0 +1,90 @@
+package cn.org.landcloud.security.sm3;
+
+import org.bouncycastle.crypto.digests.SM3Digest;
+import org.bouncycastle.util.encoders.Hex;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+public class SM3Utils {
+
+ /**
+ * SM3加密,生成16进制SM3字符串
+ *
+ * @param data 数据
+ * @return SM3字符串
+ */
+ public static String sm3(byte[] data) {
+ byte[] md = new byte[32];
+ SM3Digest sm3 = new SM3Digest();
+ sm3.update(data, 0, data.length);
+ sm3.doFinal(md, 0);
+ String s = new String(Hex.encode(md));
+ return s.toUpperCase();
+ }
+
+ /**
+ * SM3加密,生成16进制SM3字符串
+ *
+ * @param data 数据
+ * @return SM3字符串
+ */
+ public static String sm3(String data) {
+ byte[] md = new byte[32];
+ byte[] msg1 = data.getBytes(StandardCharsets.UTF_8);
+ //System.out.println(Util.byteToHex(msg1));
+ SM3Digest sm3 = new SM3Digest();
+ sm3.update(msg1, 0, msg1.length);
+ sm3.doFinal(md, 0);
+ String s = new String(Hex.encode(md));
+ return s.toUpperCase();
+ }
+
+ /**
+ * SM3加密,生成16进制SM3字符串
+ *
+ * @param data 数据
+ * @return SM3字符串
+ */
+ public static String sm3(InputStream data) throws IOException {
+ int bufferLength = 8096;
+ final byte[] buffer = new byte[bufferLength];
+ int read;
+ byte[] md = new byte[32];
+ SM3Digest sm3Digest = new SM3Digest();
+ while ((read = data.read(buffer, 0, bufferLength)) > -1) {
+ sm3Digest.update(buffer, 0, read);
+ }
+ sm3Digest.doFinal(md, 0);
+ String s = new String(Hex.encode(md));
+ return s.toUpperCase();
+ }
+
+ /**
+ * SM3加密文件,生成16进制SM3字符串
+ *
+ * @param dataFile 被加密文件
+ * @return SM3字符串
+ */
+ public static String sm3(File dataFile) throws IOException {
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(dataFile);
+ return sm3(fis);
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new IOException("文件读取异常");
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+}
diff --git a/ssl/src/main/java/cn/org/landcloud/security/sm4/SM4.java b/ssl/src/main/java/cn/org/landcloud/security/sm4/SM4.java
new file mode 100644
index 0000000..adf319b
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/sm4/SM4.java
@@ -0,0 +1,315 @@
+package cn.org.landcloud.security.sm4; /**
+ * Created by $(USER) on $(DATE)
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import cn.org.landcloud.security.Util;
+
+public class SM4 {
+ public static final int SM4_ENCRYPT = 1;
+
+ public static final int SM4_DECRYPT = 0;
+
+ private 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 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 long SHL(long x, int n) {
+ return (x & 0xFFFFFFFF) << n;
+ }
+
+ private long ROTL(long x, int n) {
+ return SHL(x, n) | x >> (32 - n);
+ }
+
+ private 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 byte sm4Sbox(byte inch) {
+ int i = inch & 0xFF;
+ byte retVal = SboxTable[i];
+ return retVal;
+ }
+
+ private 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 long sm4F(long x0, long x1, long x2, long x3, long rk) {
+ return x0 ^ sm4Lt(x1 ^ x2 ^ x3 ^ rk);
+ }
+
+ private 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 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 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 byte[] padding(byte[] input, int mode) {
+ if (input == null) {
+ return null;
+ }
+
+ byte[] ret = (byte[]) null;
+ if (mode == SM4_ENCRYPT) {
+ //填充:hex必须是32的整数倍填充 ,填充的是80 00 00 00
+ int p = 16 - input.length % 16;
+ String inputHex = Util.byteToHex(input)+ "80";
+ StringBuffer stringBuffer =new StringBuffer(inputHex);
+ for (int i = 0; i 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(SM4_Context 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;
+ }
+}
diff --git a/ssl/src/main/java/cn/org/landcloud/security/sm4/SM4Utils.java b/ssl/src/main/java/cn/org/landcloud/security/sm4/SM4Utils.java
new file mode 100644
index 0000000..8f278b3
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/sm4/SM4Utils.java
@@ -0,0 +1,173 @@
+package cn.org.landcloud.security.sm4; /**
+ * Created by $(USER) on $(DATE)
+ */
+
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import cn.org.landcloud.security.Base64;
+import cn.org.landcloud.security.Util;
+
+public class SM4Utils {
+// private String secretKey = "";
+// private String iv = "";
+// private boolean hexString = false;
+
+ public String secretKey = "";
+ public String iv = "";
+ public boolean hexString = false;
+
+ public SM4Utils() {
+ }
+
+
+ public String encryptData_ECB(String plainText) {
+ try {
+ SM4_Context ctx = new SM4_Context();
+ ctx.isPadding = true;
+ ctx.mode = SM4.SM4_ENCRYPT;
+
+ byte[] keyBytes;
+ if (hexString) {
+ keyBytes = Util.hexStringToBytes(secretKey);
+ } else {
+ //keyBytes = secretKey.getBytes();
+ keyBytes = Util.hexStringToBytes(secretKey);
+ }
+
+ SM4 sm4 = new SM4();
+ sm4.sm4_setkey_enc(ctx, keyBytes);
+ byte[] encrypted = sm4.sm4_crypt_ecb(ctx, plainText.getBytes("UTF-8"));
+ return Util.byteToHex(encrypted);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public String decryptData_ECB(String cipherText) {
+ try {
+ byte[] encrypted = Util.hexToByte(cipherText);
+ cipherText=Base64.encode(encrypted);;
+ //cipherText = new BASE64Encoder().encode(encrypted);
+ if (cipherText != null && cipherText.trim().length() > 0) {
+ Pattern p = Pattern.compile("\\s*|\t|\r|\n");
+ Matcher m = p.matcher(cipherText);
+ cipherText = m.replaceAll("");
+ }
+
+ SM4_Context ctx = new SM4_Context();
+ ctx.isPadding = true;
+ ctx.mode = SM4.SM4_DECRYPT;
+
+ byte[] keyBytes;
+ if (hexString) {
+ keyBytes = Util.hexStringToBytes(secretKey);
+ } else {
+ keyBytes = secretKey.getBytes();
+ }
+
+ SM4 sm4 = new SM4();
+ sm4.sm4_setkey_dec(ctx, keyBytes);
+ byte[] decrypted = sm4.sm4_crypt_ecb(ctx, Base64.decode(cipherText));
+ //byte[] decrypted = sm4.sm4_crypt_ecb(ctx, new BASE64Decoder().decodeBuffer(cipherText));
+ return new String(decrypted, "UTF-8");
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public String encryptData_CBC(String plainText) {
+ try {
+ SM4_Context ctx = new SM4_Context();
+ ctx.isPadding = true;
+ ctx.mode = SM4.SM4_ENCRYPT;
+
+ byte[] keyBytes;
+ byte[] ivBytes;
+ if (hexString) {
+ keyBytes = Util.hexStringToBytes(secretKey);
+ ivBytes = Util.hexStringToBytes(iv);
+ } else {
+ keyBytes = secretKey.getBytes();
+ ivBytes = iv.getBytes();
+ }
+
+ SM4 sm4 = new SM4();
+ sm4.sm4_setkey_enc(ctx, keyBytes);
+ byte[] encrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, plainText.getBytes("UTF-8"));
+ return Util.byteToHex(encrypted);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public String decryptData_CBC(String cipherText) {
+ try {
+ byte[] encrypted = Util.hexToByte(cipherText);
+ cipherText=Base64.encode(encrypted);;
+ //cipherText = new BASE64Encoder().encode(encrypted);
+ if (cipherText != null && cipherText.trim().length() > 0) {
+ Pattern p = Pattern.compile("\\s*|\t|\r|\n");
+ Matcher m = p.matcher(cipherText);
+ cipherText = m.replaceAll("");
+ }
+ SM4_Context ctx = new SM4_Context();
+ ctx.isPadding = true;
+ ctx.mode = SM4.SM4_DECRYPT;
+
+ byte[] keyBytes;
+ byte[] ivBytes;
+ if (hexString) {
+ keyBytes = Util.hexStringToBytes(secretKey);
+ ivBytes = Util.hexStringToBytes(iv);
+ } else {
+ keyBytes = secretKey.getBytes();
+ ivBytes = iv.getBytes();
+ }
+
+ SM4 sm4 = new SM4();
+ sm4.sm4_setkey_dec(ctx, keyBytes);
+ //byte[] decrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, new BASE64Decoder().decodeBuffer(cipherText));
+ byte[] decrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, Base64.decode(cipherText));
+ /*String text = new String(decrypted, "UTF-8");
+ return text.substring(0,text.length()-1);*/
+ return new String(decrypted, "UTF-8");
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static void main(String[] args) throws IOException {
+ String plainText = "I Love You Every Day";
+ String s = Util.byteToHex(plainText.getBytes());
+ System.out.println("原文" + s);
+ SM4Utils sm4 = new SM4Utils();
+ //sm4.secretKey = "JeF8U9wHFOMfs2Y8";
+ sm4.secretKey = "64EC7C763AB7BF64E2D75FF83A319918";
+ sm4.hexString = true;
+
+ System.out.println("ECB模式加密");
+ String cipherText = sm4.encryptData_ECB(plainText);
+ System.out.println("密文: " + cipherText);
+ System.out.println("");
+
+ String plainText2 = sm4.decryptData_ECB(cipherText);
+ System.out.println("明文: " + plainText2);
+ System.out.println("");
+
+ System.out.println("CBC模式加密");
+ sm4.iv = "31313131313131313131313131313131";
+ String cipherText2 = sm4.encryptData_CBC(plainText);
+ System.out.println("加密密文: " + cipherText2);
+ System.out.println("");
+
+ String plainText3 = sm4.decryptData_CBC(cipherText2);
+ System.out.println("解密明文: " + plainText3);
+
+ }
+}
diff --git a/ssl/src/main/java/cn/org/landcloud/security/sm4/SM4_Context.java b/ssl/src/main/java/cn/org/landcloud/security/sm4/SM4_Context.java
new file mode 100644
index 0000000..e52c7c6
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/sm4/SM4_Context.java
@@ -0,0 +1,19 @@
+package cn.org.landcloud.security.sm4;
+
+/**
+ * Created by $(USER) on $(DATE)
+ */
+public class SM4_Context {
+ public int mode;
+
+ public long[] sk;
+
+ public boolean isPadding;
+
+ public SM4_Context()
+ {
+ this.mode = 1;
+ this.isPadding = true;
+ this.sk = new long[32];
+ }
+}
diff --git a/ssl/src/main/java/cn/org/landcloud/security/test/SecurityTest.java b/ssl/src/main/java/cn/org/landcloud/security/test/SecurityTest.java
new file mode 100644
index 0000000..680c208
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/test/SecurityTest.java
@@ -0,0 +1,131 @@
+package cn.org.landcloud.security.test;
+
+import org.bouncycastle.crypto.digests.SM3Digest;
+import org.bouncycastle.util.encoders.Hex;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import cn.org.landcloud.security.Util;
+import cn.org.landcloud.security.sm2.SM2EncDecUtils;
+import cn.org.landcloud.security.sm2.SM2SignVO;
+import cn.org.landcloud.security.sm2.SM2SignVerUtils;
+
+public class SecurityTest {
+
+ @Test
+ public void sm2sign() throws Exception {
+ String src = "0653F3748DFD938FE83935800FF3F526B85C30C2331DD56FCB1794AA99F2A416";
+ String text = "这是一段明文";
+ byte[] sourceData = text.getBytes();
+ //String publicKey ="FA05C51AD1162133DFDF862ECA5E4A481B52FB37FF83E53D45FD18BBD6F32668A92C4692EEB305684E3B9D4ACE767F91D5D108234A9F07936020A92210BA9447";
+ //String privatekey = "5EB4DF17021CC719B678D970C620690A11B29C8357D71FA4FF9BF7FB6D89767A";
+ String publicKey = "042780f0963a428a7b030ac1c14a90b967bf365f5394ebf1f0ca1598d4d9bece4fdfa05ba043817fef68bef497088e3992362ce55b1858444fa5a3e00c5042b207";
+ String privatekey = "73e83d33d95274eeeb23f01834d02fe920b4afece377410435698dfdf1d84203";
+
+ SM2SignVO sign = SM2SignVerUtils.Sign2SM2(Util.hexStringToBytes(privatekey), Util.hexToByte(src));
+ System.out.println("R:"+sign.sign_r);
+ System.out.println("S:"+sign.sign_s);
+ //验签硬加密的串
+ String signYJ = "54720652E5EE53D14F338A03EDAC10E7F93D877EC2168F9287810807D02D2409F3EEE542638AD0B204BC3C8F93EDBCFBE87DEEFB07C0B36F34508AB49B6F90EF";
+ SM2SignVO verify = SM2SignVerUtils.VerifySignSM2(Util.hexStringToBytes(publicKey), Util.hexToByte(src), Util.hexToByte(SecurityTestAll.SM2SignHardToSoft(signYJ)));
+ System.err.println("验签结果" + verify.isVerify());
+ }
+
+ @Test
+ public void sm2sign2() throws Exception {
+ String src = "32472f598b61ea4ff28f54b00f12ca0a8c1596e2867c5cce4afcb19ee93c2cde";
+ String text = "这是一段明文";
+ byte[] sourceData = text.getBytes();
+ //String publicKey ="FA05C51AD1162133DFDF862ECA5E4A481B52FB37FF83E53D45FD18BBD6F32668A92C4692EEB305684E3B9D4ACE767F91D5D108234A9F07936020A92210BA9447";
+ //String privatekey = "5EB4DF17021CC719B678D970C620690A11B29C8357D71FA4FF9BF7FB6D89767A";
+ String publicKey = "0485B52403AEB742F952EFF7200BDBA0A399F0971FEB0EAA0CBE00A1A5EE922A34A24BD9CD2EA740B84290838A862E432BD9BBC0CD0659FD6D172CD1871CD76068";
+ String privatekey = "62329467E71E70960C7A479C03CA7FC0A2BE92E000240C4F4080F0B2437C536D";
+
+ SM2SignVO sign = SM2SignVerUtils.Sign2SM2(Util.hexStringToBytes(privatekey), Util.hexToByte(src));
+ System.out.println("R:"+sign.sign_r);
+ System.out.println("S:"+sign.sign_s);
+ //验签硬加密的串
+ String signYJ = "233fabe6f81002fbee8c69d9561114d99e0640ecf27d63561d850d77ac76ee5f5d0530bd6eca60e960784f9ad883b77dcfa3c8b274918034faf509faeee2e5ea";
+ SM2SignVO verify = SM2SignVerUtils.VerifySignSM2(Util.hexStringToBytes(publicKey), Util.hexToByte(src), Util.hexToByte(SecurityTestAll.SM2SignHardToSoft(signYJ)));
+ System.err.println("验签结果" + verify.isVerify());
+ }
+
+ @Test
+ public void sm2enc() throws IOException {
+ String plainText = "ILoveYou11";
+ //SM3测试
+ //生成密钥对
+ //generateKeyPair();
+ byte[] sourceData = plainText.getBytes();
+
+ //下面的秘钥可以使用generateKeyPair()生成的秘钥内容
+ // 国密规范正式私钥
+ //String prik = "3690655E33D5EA3D9A4AE1A1ADD766FDEA045CDEAA43A9206FB8C430CEFE0D94";
+ // 国密规范正式公钥
+ //String pubk = "04F6E0C3345AE42B51E06BF50B98834988D54EBC7460FE135A48171BC0629EAE205EEDE253A530608178A98F1E19BB737302813BA39ED3FA3C51639D7A20C7391A";
+
+ String prik = "4cf170068e9c47ebdb521fb9fc62c4a55a5773fb9da33b0acf8129e28d09d205";
+ String pubk = "04aabda53043e8dcb86d42f690b61a4db869821dadf9f851ec3c5c43d0c8f95a6677fdba984afc3bb010a8436b1d17cefc2011a34e01e9e801124d29ffa928d803";
+ String publicKey = "04BB34D657EE7E8490E66EF577E6B3CEA28B739511E787FB4F71B7F38F241D87F18A5A93DF74E90FF94F4EB907F271A36B295B851F971DA5418F4915E2C1A23D6E";
+ String privatekey = "0B1CE43098BC21B8E82B5C065EDB534CB86532B1900A49D49F3C53762D2997FA";
+ prik = privatekey;
+ pubk = publicKey;
+ System.out.println("加密: ");
+ String cipherText = SM2EncDecUtils.encrypt(Util.hexToByte(pubk), sourceData);
+ //cipherText = "0452ba81cf5119c9f29c81c2be9c4a49ad8c0a33ed899b60548d21a62971a8e994cafc0e9fbc710a0a220b055804bb890833b50ac04ec4e130a5db75338c0c1d49a52a6d373076a5db370564a5cebb5300f79877003c52adf49dac16370e51e14e0754110547bb3b";
+ System.out.println(cipherText);
+ System.out.println("解密: ");
+ plainText = new String(SM2EncDecUtils.decrypt(Util.hexToByte(prik), Util.hexToByte(cipherText)));
+ System.out.println(plainText);
+
+ }
+
+ @Test
+ public void sm3() {
+ byte[] md = new byte[32];
+ byte[] msg1 = "ererfeiisgod".getBytes();
+ System.out.println(Util.byteToHex(msg1));
+ SM3Digest sm3 = new SM3Digest();
+ sm3.update(msg1, 0, msg1.length);
+ sm3.doFinal(md, 0);
+ String s = new String(Hex.encode(md));
+ System.out.println(s.toUpperCase());
+ }
+
+ @Test
+ public void sm4() throws IOException {
+ /*String plainText = "我爱中国啊1我爱我我他2";
+ String s = Util.byteToHex(plainText.getBytes());
+ System.out.println("原文" + s);
+ SM4Utils sm4 = new SM4Utils();
+ //sm4.secretKey = "JeF8U9wHFOMfs2Y8";
+ sm4.secretKey = "E76E9B4E0245BC56FCE4E29B208C6A50";
+ sm4.hexString = true;
+
+ System.out.println("ECB模式加密");
+ String cipherText = sm4.encryptData_ECB(plainText);
+ System.out.println("密文: " + cipherText);
+ System.out.println("");
+
+ plainText = sm4.decryptData_ECB(cipherText);
+ System.out.println("明文: " + plainText);
+ System.out.println("");
+
+ System.out.println("CBC模式加密");
+ sm4.iv = "30303030303030303030303030303030";
+ cipherText = sm4.encryptData_CBC(plainText);
+ System.out.println("加密密文: " + cipherText);
+ System.out.println("");
+
+ plainText = sm4.decryptData_CBC(cipherText);
+ System.out.println("解密明文: " + plainText);*/
+
+ }
+
+
+ public static void main(String[] args) throws IOException {
+
+
+ }
+}
diff --git a/ssl/src/main/java/cn/org/landcloud/security/test/SecurityTestAll.java b/ssl/src/main/java/cn/org/landcloud/security/test/SecurityTestAll.java
new file mode 100644
index 0000000..78d4a32
--- /dev/null
+++ b/ssl/src/main/java/cn/org/landcloud/security/test/SecurityTestAll.java
@@ -0,0 +1,219 @@
+package cn.org.landcloud.security.test;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.crypto.digests.SM3Digest;
+import org.bouncycastle.util.encoders.Hex;
+
+import java.io.IOException;
+import java.util.UUID;
+
+import cn.org.landcloud.security.Util;
+import cn.org.landcloud.security.sm2.SM2EncDecUtils;
+import cn.org.landcloud.security.sm2.SM2KeyVO;
+import cn.org.landcloud.security.sm2.SM2SignVO;
+import cn.org.landcloud.security.sm2.SM2SignVerUtils;
+import cn.org.landcloud.security.sm4.SM4Utils;
+
+public class SecurityTestAll {
+ //SM2公钥编码格式
+ //HardPubKey:3059301306072A8648CE3D020106082A811CCF5501822D03420004+X+Y
+ //SoftPubKey:04+X+Y
+ public static final String SM2PubHardKeyHead = "3059301306072A8648CE3D020106082A811CCF5501822D034200";
+ //SM2加密 密文区别:软加密多了04
+ //SM2加密机签名编码格式
+ //HardSign:R+S
+ //public static final String SM2PubHardKeyHead="3059301306072A8648CE3D020106082A811CCF5501822D034200";
+ //private final String SM4_CBC_IV="";
+ //private final String SM2="";
+
+
+ public static void main(String[] args) throws Exception {
+ System.out.println("--产生SM2秘钥--:");
+ SM2KeyVO sm2KeyVO = generateSM2Key();
+ System.out.println("公钥:" + sm2KeyVO.getPubHexInSoft());
+ System.out.println("私钥:" + sm2KeyVO.getPriHexInSoft());
+ //数据加密
+ System.out.println("--测试加密开始--");
+ String src = "I Love You";
+ System.out.println("原文UTF-8转hex:" + Util.byteToHex(src.getBytes()));
+ String SM2Enc = SM2Enc(sm2KeyVO.getPubHexInSoft(), src);
+ System.out.println("加密:");
+ System.out.println("密文:" + SM2Enc);
+ String SM2Dec = SM2Dec(sm2KeyVO.getPriHexInSoft(), SM2Enc);
+ System.out.println("解密:" + SM2Dec);
+ System.out.println("--测试加密结束--");
+
+ System.out.println("--测试SM2签名--");
+ System.out.println("原文hex:" + Util.byteToHex(src.getBytes()));
+ String s5 = Util.byteToHex(src.getBytes());
+
+ System.out.println("签名测试开始:");
+ SM2SignVO sign = genSM2Signature(sm2KeyVO.getPriHexInSoft(), s5);
+ System.out.println("软加密签名结果:" + sign.getSm2_signForSoft());
+ System.out.println("加密机签名结果:" + sign.getSm2_signForHard());
+ //System.out.println("转签名测试:"+SM2SignHardToSoft(sign.getSm2_signForHard()));
+ System.out.println("验签1,软件加密方式:");
+ boolean b = verifySM2Signature(sm2KeyVO.getPubHexInSoft(), s5, sign.getSm2_signForSoft());
+ System.out.println("软件加密方式验签结果:" + b);
+ System.out.println("验签2,硬件加密方式:");
+ String sm2_signForHard = sign.getSm2_signForHard();
+ System.out.println("签名R:"+sign.sign_r);
+ System.out.println("签名S:"+sign.sign_s);
+ //System.out.println("硬:"+sm2_signForHard);
+ b = verifySM2Signature(sm2KeyVO.getPubHexInSoft(), s5, SM2SignHardToSoft(sign.getSm2_signForHard()));
+ System.out.println("硬件加密方式验签结果:" + b);
+ if (!b) {
+ throw new RuntimeException();
+ }
+ System.out.println("--签名测试结束--");
+
+ System.out.println("--SM3摘要测试--");
+ String s = generateSM3HASH(src);
+ System.out.println("hash:"+s);
+ System.out.println("--SM3摘要结束--");
+
+ System.out.println("--生成SM4秘钥--");
+ String sm4Key = generateSM4Key();
+ System.out.println("sm4Key:"+sm4Key);
+ System.out.println("--生成SM4结束--");
+ System.out.println("--SM4的CBC加密--");
+ String s1 = SM4EncForCBC(sm4Key, src);
+ System.out.println("密文:"+s1);
+ System.out.println("CBC解密");
+ String s2 = SM4DecForCBC(sm4Key, s1);
+ System.out.println("解密结果:"+s2);
+ System.out.println("--ECB加密--");
+ String s3 = SM4EncForECB(sm4Key, src);
+ System.out.println("ECB密文:"+s3);
+ System.out.println("ECB解密");
+ String s4 = SM4DecForECB(sm4Key, s3);
+ System.out.println("ECB解密结果:"+s4);
+ }
+
+ //SM2公钥soft和Hard转换
+ public static String SM2PubKeySoftToHard(String softKey) {
+ return SM2PubHardKeyHead + softKey;
+ }
+
+ //SM2公钥Hard和soft转换
+ public static String SM2PubKeyHardToSoft(String hardKey) {
+ return hardKey.replaceFirst(SM2PubHardKeyHead, "");
+ }
+
+ //产生非对称秘钥
+ public static SM2KeyVO generateSM2Key() throws IOException {
+ SM2KeyVO sm2KeyVO = SM2EncDecUtils.generateKeyPair();
+ return sm2KeyVO;
+ }
+
+ //公钥加密
+ public static String SM2Enc(String pubKey, String src) throws IOException {
+ String encrypt = SM2EncDecUtils.encrypt(Util.hexStringToBytes(pubKey), src.getBytes());
+ //删除04
+ encrypt=encrypt.substring(2,encrypt.length());
+ return encrypt;
+ }
+
+ //私钥解密
+ public static String SM2Dec(String priKey, String encryptedData) throws IOException {
+ //填充04
+ encryptedData="04"+encryptedData;
+ byte[] decrypt = SM2EncDecUtils.decrypt(Util.hexStringToBytes(priKey), Util.hexStringToBytes(encryptedData));
+ return new String(decrypt);
+ }
+
+ //私钥签名,参数二:原串必须是hex!!!!因为是直接用于计算签名的,可能是SM3串,也可能是普通串转Hex
+ public static SM2SignVO genSM2Signature(String priKey, String sourceData) throws Exception {
+ SM2SignVO sign = SM2SignVerUtils.Sign2SM2(Util.hexToByte(priKey), Util.hexToByte(sourceData));
+ return sign;
+ }
+
+ //公钥验签,参数二:原串必须是hex!!!!因为是直接用于计算签名的,可能是SM3串,也可能是普通串转Hex
+ public static boolean verifySM2Signature(String pubKey, String sourceData, String hardSign) {
+ SM2SignVO verify = SM2SignVerUtils.VerifySignSM2(Util.hexStringToBytes(pubKey), Util.hexToByte(sourceData), Util.hexToByte(hardSign));
+ return verify.isVerify();
+ }
+
+ //SM2签名Hard转soft
+ public static String SM2SignHardToSoft(String hardSign) {
+ byte[] bytes = Util.hexToByte(hardSign);
+ byte[] r = new byte[bytes.length / 2];
+ byte[] s = new byte[bytes.length / 2];
+ System.arraycopy(bytes, 0, r, 0, bytes.length / 2);
+ System.arraycopy(bytes, bytes.length / 2, s, 0, bytes.length / 2);
+ ASN1Integer d_r = new ASN1Integer(Util.byteConvertInteger(r));
+ ASN1Integer d_s = new ASN1Integer(Util.byteConvertInteger(s));
+ ASN1EncodableVector v2 = new ASN1EncodableVector();
+ v2.add(d_r);
+ v2.add(d_s);
+ DERSequence sign = new DERSequence(v2);
+
+ String result = null;
+ try {
+ result = Util.byteToHex(sign.getEncoded());
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+ //SM2加密机转软加密编码格式
+ //return SM2SignHardKeyHead+hardSign.substring(0, hardSign.length()/2)+SM2SignHardKeyMid+hardSign.substring(hardSign.length()/2);
+ return result;
+ }
+
+ //摘要计算
+ public static String generateSM3HASH(String src) {
+ byte[] md = new byte[32];
+ byte[] msg1 = src.getBytes();
+ //System.out.println(Util.byteToHex(msg1));
+ SM3Digest sm3 = new SM3Digest();
+ sm3.update(msg1, 0, msg1.length);
+ sm3.doFinal(md, 0);
+ String s = new String(Hex.encode(md));
+ return s.toUpperCase();
+ }
+
+ //产生对称秘钥
+ public static String generateSM4Key() {
+ return UUID.randomUUID().toString().replace("-", "");
+ }
+
+
+ //对称秘钥加密(CBC)
+ public static String SM4EncForCBC(String key,String text) {
+ SM4Utils sm4 = new SM4Utils();
+ sm4.secretKey = key;
+ sm4.hexString = true;
+ sm4.iv = "31313131313131313131313131313131";
+ String cipherText = sm4.encryptData_CBC(text);
+ return cipherText;
+ }
+
+ //对称秘钥解密(CBC)
+ public static String SM4DecForCBC(String key,String text) {
+ SM4Utils sm4 = new SM4Utils();
+ sm4.secretKey = key;
+ sm4.hexString = true;
+ sm4.iv = "31313131313131313131313131313131";
+ String plainText = sm4.decryptData_CBC(text);
+ return plainText;
+ }
+ //对称秘钥加密(ECB)
+ public static String SM4EncForECB(String key,String text) {
+ SM4Utils sm4 = new SM4Utils();
+ sm4.secretKey = key;
+ sm4.hexString = true;
+ String cipherText = sm4.encryptData_ECB(text);
+ return cipherText;
+ }
+ //对称秘钥解密(ECB)
+ public static String SM4DecForECB(String key,String text) {
+ SM4Utils sm4 = new SM4Utils();
+ sm4.secretKey = key;
+ sm4.hexString = true;
+ String plainText = sm4.decryptData_ECB(text);
+ return plainText;
+ }
+
+}