ID驗證系列|身分證字號驗證

最近看到一則有趣的新聞,原來身分字號 A123456789 真有其人阿!決定來稍微了解一下,身分證字號是怎產生出來的,順便寫寫身分證字號檢查器…原本想寫產生器的,但想想還是算了 XD

中華民國身分證 中華民國身分證

編號規則

目前現行的身分證字號一共有 10 碼,包括起首的大寫的英文字母與接續的九個阿拉伯數字(如:A123456789),大抵可以將身分證字號分成五區:區域碼性別碼身分碼流水碼檢核碼。 。

區域碼 性別碼 身分碼 流水碼 檢核碼
A-Z 男:1
女:2
其他:0-5
取得國籍之外國人:6
無戶籍國民:7
港澳居民:8
大陸地區人民:9
阿拉伯數字 阿拉伯數字

其中首碼的縣市代碼是以報戶口的地區來區分的、而性別碼則是指首位數字,其中男性為1、女性為2,最後第三碼是身分碼,其中 0-5 是保留給國人,6-9 則是保留給歸化的外國人與中港澳人民使用。

關於第三碼的身分碼,在我 2020-08 第一次寫這篇的時候,我並沒有注意到身分碼的資料,但在今天為了寫《【臺灣ID驗證系列】居留證驗證》查資料的時候,發現了這東西。不過再細查資料,這條規則是在內政部 92 年 4 月 24 日台內戶字第 0920063929 號函規定就被定下了,所以應該是我上次資料漏看了!?


整體來說,一個完整的身分證字號如下:

區域碼 性別碼 身分碼 流水碼 檢核碼
A 1 2 3 4 5 6 7 8 9


在檢查檢核碼時,會將縣市代碼轉換成相對應的數值,如 A 就會被轉換成 10

10 11 12 13 14 15 16 17 34 18
19 20 21 22 35 23 24 25 26 27
28 29 32 30 31 33


將轉換完成的數值,乘上相對應的權重後進行加總:

Index $n_0$ $n_1$ $n_2$ $n_3$ $n_4$ $n_5$ $n_6$ $n_7$ $n_8$ $n_9$ $n_{10}$
權重 1 9 8 7 6 5 4 3 2 1 1

若總和為 10 的倍數,即為有效的驗證碼。


若改寫成數學判斷式:

\[(n_0\times 1+n_1\times 9+n_2\times 8+n_3\times 7+n_4\times 6+n_5\times 5+n_6\times 4+n_7\times 3+n_8\times 2+n_9\times 1+n_{10}\times 1)\%10 = 0\]


A123456789 轉換成 10123456789 後套入公式如下:

\[\begin{aligned} &(1\times 1+0\times 9+1\times 8+2\times 7+3\times 6+4\times 5+5\times 4+6\times 3+7\times 2+8\times 1+9\times 1)\%10 \\ &= (1+0+8+14+18+20+20+18+14+8+9)\%10\\ &= 130\%10\\ &= 0 \end{aligned}\]

餘數為 0,表有效的 ID。

程式碼

有點久沒寫 js 了,順便寫寫 js 練練手好了。把上面的規則寫成程式,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
function verifyId(id) {
    id = id.trim();

    if (id.length != 10) {
        console.log("Fail,長度不正確");
        return false
    }


    let countyCode = id.charCodeAt(0);
    if (countyCode < 65 | countyCode > 90) {
        console.log("Fail,字首英文代號,縣市不正確");
        return false
    }

    let genderCode = id.charCodeAt(1);
    if (genderCode != 49 && genderCode != 50) {
        console.log("Fail,性別代碼不正確");
        return false
    }

    let serialCode = id.slice(2)
    for (let i in serialCode) {
        let c = serialCode.charCodeAt(i);
        if (c < 48 | c > 57) {
            console.log("Fail,數字區出現非數字字元");
            return false
        }
    }

    let conver = "ABCDEFGHJKLMNPQRSTUVXYWZIO"
    let weights = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1]

    id = String(conver.indexOf(id[0]) + 10) + id.slice(1);

    checkSum = 0
    for (let i = 0; i < id.length; i++) {
        c = parseInt(id[i])
        w = weights[i]
        checkSum += c * w
    }

    verification = checkSum % 10 == 0

    if (verification) {
        console.log("Pass");
    } else {
        console.log("Fail,檢核碼錯誤");
    }

    return verification
}

console.log(verifyId("A123456789"));


是說,如果不要顯示 log , Regular Expression 可以涵蓋前半段的檢查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function verifyId(id) {
    id = id.trim();
    
    <!--  js 中遇到反斜線要跳脫所以這邊用兩個反斜線 -->
    <!-- 如果你看到四個反斜線那是我為了讓 NexT.Mist 主題順利渲染所再做跳脫 -->
    verification = id.match("^[A-Z][12]\\d{8}$")
	if(!verification){
		return false
	}

    let conver = "ABCDEFGHJKLMNPQRSTUVXYWZIO"
    let weights = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1]

    id = String(conver.indexOf(id[0]) + 10) + id.slice(1);

    checkSum = 0
    for (let i = 0; i < id.length; i++) {
        c = parseInt(id[i])
        w = weights[i]
        checkSum += c * w
    }
	
    return checkSum % 10 == 0
}

console.log(verifyId("A123456789"));

進一步驗證規則

因為上述的驗證規則是用於僅輸入身分證字號的情境,若使用情境中有輸入戶籍地與出生日期的情況,可在新增:

  1. 性別
    當然就是檢查第二碼來確認啦。

  2. 縣市代碼與戶籍地的對照:
    不過這個用到的機會不大,鮮少有情境是輸入戶籍地,多數時候都是輸入通訊地 XD

  3. 縣市代碼與出生日期的比較:
    因為縣市合併的關係,目前有部分縣市代碼已不再賦配。所以可以比較出生日期與停發日期做進一步檢查。

    縣市代碼 原行政區 停發日期
    L 臺中縣 2010/12/25
    R 臺南縣 2010/12/25
    S 高雄縣 2010/12/25
    Y 陽明山管理局 1974/01/01

參考資料

  1. 林姸君 (2020-07-10)。身分證A123456789真有人 一條龍伯「信用破產」冤跑法庭:別再害我了 。檢自 ctwant (2020-07-10)。
  2. (2006-02-17)。身分證「A123456789」老被冒用 。檢自 ctwant阿特拉斯的部落格 (2020-07-10)。
  3. 協同撰寫。中華民國國民身分證。檢自 維基百科 (2020-07-10)。

更新紀錄

最後更新日期:2021-03-10
  • 2021-03-10 更新:新增身分碼資料
  • 2020-08-25 更新:新增 Regular expression
  • 2020-08-10 發布
  • 2020-07-13 完稿
  • 2020-07-10 起稿