假設場景:假設將身份證號應用於數據庫主鍵,但要知足兩方面要求:1.不能明文存儲。2.壓縮長度。java
目前我國身份證是18位按必定規則生成的字符串。其生成規則以下:數據庫
數字地址碼(6) + 數字出生日期碼(8) + 數字順序碼(3) + 數字校驗碼(1)
地址碼:表示編碼對象常住戶口所在縣(市、旗、區)的行政區劃代碼,按GB/T2260的規定執行。apache
出生日期碼:表示編碼對象出生的年、月、日,按GB/T7408的規定執行,年、月、日代碼之間不用分隔符。編碼
順序碼:表示在同一地址碼所標識的區域範圍內,對同年、同月、同日出生的人編定的順序號,順序碼的奇數分配給男性,偶數分配給女性。加密
校驗碼:spa
(1)十七位數字本體碼加權求和公式 S = Sum(Ai * Wi), i = 0, ... , 16 ,先對前17位數字的權求和
Ai:表示第i位置上的身份證號碼數字值 Wi:表示第i位置上的加權因子code
Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4
(2)計算模 Y = mod(S, 11) , Y取值: 0 1 2 3 4 5 6 7 8 9 10orm
(3)經過模獲得對應的校驗碼: 1 0 X 9 8 7 6 5 4 3 2對象
按上述規範編寫的身份證檢驗邏輯,能夠參考最後的代碼。ci
基本思路:將身份證按必定規律拆解,而後按必定的計算方式進行換算。拆解方式以下:
城市(2)+地址(4)+年份(4)+月份(2)+天(2)+順序號(3)+校驗碼(1)
(1)將城市和校驗碼的對應關係打亂後轉換成36進制字符。
(2)地址、月份、天、順序號直接轉化成36進制字符。固然也能夠加一點點的其它運算。另外還能夠適當地進行位數的壓縮。如月份取值:1-12,在36進制的狀況下,一位字符便可。
(3)年份-固定一個年份(如:1800),將結果轉化成36進制。這裏採用2位36進制保存,能夠支持使用1295年了。
最後生成的結果組成方式以下, 一共13位字符:
城市(2)+地址(4)+年份(2)+月份(1)+天(1)+順序碼(2)+校驗碼(1)
package com.zheng.coderepo.idcard; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import java.math.BigInteger; import java.text.SimpleDateFormat; import java.util.Calendar; /** * Created by zhangchaozheng on 17-2-21. */ public class IdCardUtils { /** * 省、直轄市代碼表 */ public static final String cityCode[] = { "11", "12", "13", "14", "15", "21", "22", "23", "31", "32", "33", "34", "35", "36", "37", "41", "42", "43", "44", "45", "46", "50", "51", "52", "53", "54", "61", "62", "63", "64", "65", "71", "81", "82", "91" }; /** * 每位加權因子 */ public static final int Wi[] = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; /** * 第18位校檢碼 */ public static final String ValCodeArr[] = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"}; /** * 省、直轄市代碼表 */ public static final String cityCode_encrypt[] = { "15", "16", "17", "18", "19", "20", "99", "23", "24", "01", "02", "03", "44", "45", "46", "47", "48", "49", "61", "62", "63", "95", "96", "97", "64", "67", "68", "69", "51", "52", "53", "54", "55", "56", "88" }; /** * 第18位校檢碼 */ public static final String ValCodeArr_encrypt[] = {"5", "8", "4", "1", "2", "3", "7", "12", "13", "15", "21"}; private static final int FROM_YEAR = 1800; /** * 檢查身份證是否合法 * @param idNo * @return */ public static boolean checkIdNo(String idNo) { // 1.檢查身份證長度 if (idNo.length() != 18) { throw new IllegalArgumentException("身份證號碼長度應該爲18位。"); } // 2.檢查身份證號是否符合數字規則 String Ai = idNo.substring(0, 17); if (StringUtils.isNumeric(Ai) == false) { throw new IllegalArgumentException("18位號碼除最後一位外,都應爲數字。"); } // 3.檢查出年日期是否有效 String strYear = Ai.substring(6, 10);// 年份 String strMonth = Ai.substring(10, 12);// 月份 String strDay = Ai.substring(12, 14);// 月份 Calendar cal = Calendar.getInstance(); int currentYear = cal.get(Calendar.YEAR); int year = Integer.parseInt(strYear); if ((currentYear - year) < 0 || (currentYear - year) > 150) { throw new IllegalArgumentException("身份證出生日期年份無效。"); } int month = Integer.parseInt(strMonth); if (month < 0 || month > 12) { throw new IllegalArgumentException("身份證出生日期月份無效。"); } int day = Integer.parseInt(strDay); if (day < 0 || day > 31) { throw new IllegalArgumentException("身份證出生日期的天無效。"); } SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); sdf.setLenient(false); try { sdf.parse(strYear + strMonth + strDay); } catch (Exception e) { throw new IllegalArgumentException("身份證出生日期無效。"); } // 4.地區碼是否有效 if (!ArrayUtils.contains(cityCode, Ai.substring(0, 2))) { throw new IllegalArgumentException("身份證地區編碼錯誤。"); } // 5.驗證最後一位校驗碼 int totalAiWi = 0; for (int i = 0; i < 17; i++) { totalAiWi = totalAiWi + Integer.parseInt(String.valueOf(Ai.charAt(i))) * Wi[i]; } int modValue = totalAiWi % 11; String strVerifyCode = ValCodeArr[modValue]; Ai = Ai + strVerifyCode; if (Ai.equals(idNo) == false) { throw new IllegalArgumentException("身份證無效,不是合法的身份證號碼"); } return true; } public static String encrypt(String idNo) { //檢查證件號的合法性 try { checkIdNo(idNo); } catch (Exception e) { return ""; } String city = idNo.substring(0, 2);//city String addr = idNo.substring(2, 6);//addr String year = idNo.substring(6, 10);// 年份 String month = idNo.substring(10, 12);// 月份 String day = idNo.substring(12, 14);// 月份 String seq = idNo.substring(14, 17);//序號 String valCode = idNo.substring(17, 18);//檢驗位 String cityChange = new BigInteger(getCityChange(city), 10).toString(36); String addrChange = new BigInteger(addr, 10).toString(36); String yearChange = new BigInteger((Integer.parseInt(year) - FROM_YEAR) + "", 10).toString(36); String monthChange = new BigInteger(month, 10).toString(36); String dayChange = new BigInteger(day, 10).toString(36); String seqChange = new BigInteger(seq, 10).toString(36); String valCodeChange = new BigInteger(getValCodeChange(valCode), 10).toString(36); return "" + //保持2位 leftPad(cityChange, 2) + //保持4位 leftPad(addrChange, 4) + //保持2位,使用36進制保存能夠支持1295年 leftPad(yearChange, 2) + //使用1位 leftPad(monthChange, 1) + //使用1位 leftPad(dayChange, 1) + //保持2位 leftPad(seqChange, 2) + //保持1位 valCodeChange; } public static String decrypt(String pk) { String city = pk.substring(0, 2);//city String addr = pk.substring(2, 6);//addr String year = pk.substring(6, 8);// 年份 String month = pk.substring(8, 9);// 月份 String day = pk.substring(9, 10);// 月份 String seq = pk.substring(10, 12);//序號 String valCode = pk.substring(12, 13);//檢驗位 //還原2位 String cityChange = getRealCity(new BigInteger(city, 36).toString(10)); //還原4位 String addrChange = new BigInteger(addr, 36).toString(10); //還原4位 String yearChange = new BigInteger(year, 36).add(BigInteger.valueOf(FROM_YEAR)).toString(10); //還原2位 String monthChange = new BigInteger(month, 36).toString(10); //還原2位 String dayChange = new BigInteger(day, 36).toString(10); //還原3位 String seqChange = new BigInteger(seq, 36).toString(10); //還原1位 String valCodeChange = getRealValCode(new BigInteger(valCode, 36).toString(10)); return cityChange + leftPad(addrChange, 4) + leftPad(yearChange, 2) + leftPad(monthChange, 2) + leftPad(dayChange, 2) + leftPad(seqChange, 3) + valCodeChange; } private static String getValCodeChange(String valCode) { int i = ArrayUtils.indexOf(ValCodeArr, valCode); return ValCodeArr_encrypt[i]; } private static String getRealValCode(String valCode) { int i = ArrayUtils.indexOf(ValCodeArr_encrypt, valCode); return ValCodeArr[i]; } private static String getCityChange(String city) { int i = ArrayUtils.indexOf(cityCode, city); return cityCode_encrypt[i]; } private static String getRealCity(String city) { int i = ArrayUtils.indexOf(cityCode_encrypt, city); return cityCode[i]; } private static String leftPad(String addrChange, int size) { return StringUtils.leftPad(addrChange, size, "0"); } }