├── LICENSE
├── README.md
├── Client Example.go
└── License Server.go
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Adam
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HWID-Based-License-System
2 | A GoLANG based HWID license system, basic.
3 |
4 | Vary simple, basic HWID (hardware ID) license system.
5 |
6 | You generate keys with the license server and give the program to the client with a key, on first run the program looks for the license.dat file that contains the key if not found asks the client if they want to register, if so they imput the key and the program generates a HWID for that system and user, connects to the license server where the server checks for the key making sure its not already registerd with another HWID and that its not expired. If good it adds the HWID to the row in the database.
7 |
8 | You can generate new keys will the following information;
9 |
10 | Email
11 | Experation Date
12 |
13 | The key is generated from a random char generater set to 4x4x4 chars 0-9 and A-Z.
14 |
15 | You can also bulk generate keys (without registerd email)
16 | You also can remove keys (by email)
17 |
18 | This is not fail proof, its a simple to use deterent.
19 |
20 | The database is just a text file, ity can be edited by hand.
21 |
22 | THE PROGRAM WILL NEED TO BE ABLE TO CONNECT TO THE SERVER TO VERIFY THE LICENSE ANYTIME ITS CALLED.
23 | THE LICENSE CHECK CAN BE RUN AT ANYTIME OR ON A TIMED LOOP.
24 | LICENSE SERVER CAN RUN ON ANY OPEN PORT.
25 |
26 | # Donations
27 |
28 |
Please Donate To Bitcoin Address: 1AEbR1utjaYu3SGtBKZCLJMRR5RS7Bp7eE
29 | -------------------------------------------------------------------------------- /Client Example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "crypto/md5" 7 | "encoding/hex" 8 | "fmt" 9 | "io/ioutil" 10 | "net/http" 11 | "net/url" 12 | "os" 13 | "os/user" 14 | ) 15 | 16 | var licenseServer string = "http://127.0.0.1:9347/" //Your license server address 17 | 18 | func checkFileExist(filePath string) bool { 19 | if _, err := os.Stat(filePath); os.IsNotExist(err) { 20 | return false 21 | } else { 22 | return true 23 | } 24 | } 25 | 26 | func md5Hash(text string) string { 27 | hasher := md5.New() 28 | hasher.Write([]byte(text)) 29 | return hex.EncodeToString(hasher.Sum(nil)) 30 | } 31 | 32 | func licenseCheck() { 33 | if !checkFileExist("license.dat") { 34 | 35 | fmt.Println("License file not found.") 36 | 37 | fmt.Print("Would you like to register?: ") 38 | scan := bufio.NewScanner(os.Stdin) 39 | scan.Scan() 40 | 41 | if scan.Text() == "yes" || scan.Text() == "Yes" || scan.Text() == "YES" { 42 | fmt.Println("If you do not have a key contact your supplier.") 43 | fmt.Print("Please entor your key: ") 44 | scan := bufio.NewScanner(os.Stdin) 45 | scan.Scan() 46 | 47 | key := scan.Text() 48 | 49 | name, _ := os.Hostname() 50 | usr, _ := user.Current() 51 | 52 | fmt.Println("Key:", key) 53 | 54 | ioutil.WriteFile("license.dat", []byte(key), 0600) 55 | 56 | fmt.Println("HWID:", md5Hash(name+usr.Username)) 57 | 58 | fmt.Println("Connecting to license server...") 59 | 60 | client := &http.Client{} 61 | data := url.Values{} 62 | data.Set("license", key) 63 | data.Set("hwid", md5Hash(name+usr.Username)) 64 | u, _ := url.ParseRequestURI(licenseServer) 65 | urlStr := fmt.Sprintf("%v", u) 66 | r, _ := http.NewRequest("POST", urlStr, bytes.NewBufferString(data.Encode())) 67 | r.Header.Add("Content-Type", "application/x-www-form-urlencoded") 68 | resp, err := client.Do(r) 69 | if err != nil { 70 | fmt.Println("Unable to connect to license server.") 71 | os.Exit(0) 72 | } else { 73 | defer resp.Body.Close() 74 | resp_body, _ := ioutil.ReadAll(resp.Body) 75 | if resp.StatusCode == 200 { 76 | if string(resp_body) != "0" { 77 | if string(resp_body) == "1" { 78 | fmt.Println("License is Expired.") 79 | os.Exit(0) 80 | } else if string(resp_body) == "2" { 81 | fmt.Println("Registered!") 82 | fmt.Println("DO NOT DELETE THE 'license.dat' FILE!") 83 | fmt.Println(" ") 84 | } else { 85 | fmt.Println("Unable to verify to license server.") 86 | os.Exit(0) 87 | } 88 | } 89 | } else { 90 | fmt.Println(resp.StatusCode) 91 | } 92 | } 93 | 94 | } else { 95 | os.Exit(0) 96 | } 97 | } else { 98 | name, _ := os.Hostname() 99 | usr, _ := user.Current() 100 | 101 | dat, _ := ioutil.ReadFile("license.dat") 102 | 103 | key := string(dat) 104 | 105 | client := &http.Client{} 106 | data := url.Values{} 107 | data.Set("license", key) 108 | data.Set("hwid", md5Hash(name+usr.Username)) 109 | u, _ := url.ParseRequestURI(licenseServer) 110 | urlStr := fmt.Sprintf("%v", u) 111 | r, _ := http.NewRequest("POST", urlStr, bytes.NewBufferString(data.Encode())) 112 | r.Header.Add("Content-Type", "application/x-www-form-urlencoded") 113 | resp, err := client.Do(r) 114 | if err != nil { 115 | fmt.Println("Unable to connect to license server.") 116 | os.Exit(0) 117 | } else { 118 | defer resp.Body.Close() 119 | resp_body, _ := ioutil.ReadAll(resp.Body) 120 | if resp.StatusCode == 200 { 121 | if string(resp_body) != "0" { 122 | if string(resp_body) == "1" { 123 | fmt.Println("License is Expired.") 124 | os.Exit(0) 125 | } else { 126 | fmt.Println("Unable to verify to license server.") 127 | os.Exit(0) 128 | } 129 | } 130 | } 131 | } 132 | 133 | } 134 | } 135 | 136 | func main() { 137 | licenseCheck() 138 | for { 139 | 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /License Server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io/ioutil" 7 | "math/rand" 8 | "net/http" 9 | "os" 10 | "regexp" 11 | "strconv" 12 | "strings" 13 | "time" 14 | 15 | "github.com/gorilla/mux" 16 | ) 17 | 18 | var ( 19 | PORT int = 9347 20 | ) 21 | 22 | func checkFileExist(filePath string) bool { 23 | if _, err := os.Stat(filePath); os.IsNotExist(err) { 24 | return false 25 | } else { 26 | return true 27 | } 28 | } 29 | 30 | func readLines(path string) ([]string, error) { 31 | file, err := os.Open(path) 32 | if err != nil { 33 | return nil, err 34 | } 35 | defer file.Close() 36 | 37 | var lines []string 38 | scanner := bufio.NewScanner(file) 39 | for scanner.Scan() { 40 | lines = append(lines, scanner.Text()) 41 | } 42 | return lines, scanner.Err() 43 | } 44 | 45 | func CheckFileExist(filePath string) bool { 46 | if _, err := os.Stat(filePath); os.IsNotExist(err) { 47 | return false 48 | } else { 49 | return true 50 | } 51 | } 52 | 53 | func createFile(pathFile string) error { 54 | file, err := os.Create(pathFile) 55 | if err != nil { 56 | return err 57 | } 58 | defer file.Close() 59 | return nil 60 | } 61 | 62 | func randomString(n int) string { 63 | var letterRunes = []rune("1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ") 64 | 65 | b := make([]rune, n) 66 | for i := range b { 67 | b[i] = letterRunes[rand.Intn(len(letterRunes))] 68 | } 69 | return string(b) 70 | } 71 | 72 | //LICENSE:EXPDATE:EMAIL:HWID = 0,1,2 73 | func checkHandler(response http.ResponseWriter, request *http.Request) { 74 | 75 | request.ParseForm() 76 | license := request.FormValue("license") 77 | hwid := request.FormValue("hwid") 78 | 79 | database, _ := readLines("db") 80 | for _, table := range database { 81 | 82 | row := strings.Split(table, ":") 83 | 84 | t, err := time.Parse("2006-01-02", row[1]) 85 | if err != nil { 86 | fmt.Println("ERROR: Error reading database") 87 | } 88 | 89 | t2, _ := time.Parse("2006-01-02", time.Now().Format("2006-01-02")) 90 | 91 | if license == row[0] && t.After(t2) { 92 | if hwid == row[3] { 93 | fmt.Fprintf(response, "0") //Registed, Good licnese 94 | } else if row[3] == "NOTSET" { 95 | b, err := ioutil.ReadFile("db") 96 | if err != nil { 97 | fmt.Println("READfromCHECK") 98 | os.Exit(0) 99 | } 100 | 101 | str := string(b) 102 | edit := row[0] + ":" + row[1] + ":" + row[2] + ":" + hwid 103 | res := strings.Replace(str, table, edit, -1) 104 | 105 | err = ioutil.WriteFile("db", []byte(res), 0644) 106 | if err != nil { 107 | fmt.Println("WRITEfromCHECK") 108 | os.Exit(0) 109 | } 110 | 111 | fmt.Fprintf(response, "2") //Registed, Good licnese 112 | } 113 | } else if license == row[0] && !t.After(t2) { 114 | fmt.Fprintf(response, "1") //registerd but license experied 115 | } 116 | } 117 | } 118 | 119 | func serverAPI() { 120 | router := mux.NewRouter() 121 | router.HandleFunc("/", checkHandler).Methods("POST") 122 | http.Handle("/", router) 123 | 124 | http.ListenAndServe(":"+string(strconv.Itoa(PORT)), nil) 125 | } 126 | 127 | func main() { 128 | fmt.Println("License Server") 129 | fmt.Println("Github: github.com/SaturnsVoid") 130 | 131 | if !checkFileExist("db") { 132 | fmt.Println("Database does not exist, creating new database.") 133 | _ = createFile("db") 134 | } 135 | 136 | database, _ := readLines("db") 137 | 138 | fmt.Println("Total Licenses:", len(database)) 139 | 140 | go serverAPI() 141 | for { 142 | fmt.Println(" ") 143 | fmt.Print("$> ") 144 | scan := bufio.NewScanner(os.Stdin) 145 | scan.Scan() 146 | switch scan.Text() { 147 | case "list": 148 | database, _ = readLines("db") 149 | for _, table := range database { 150 | fmt.Println(table) 151 | } 152 | case "add": 153 | var email string 154 | var experation string 155 | var license string 156 | 157 | fmt.Print("License Email: ") 158 | scan = bufio.NewScanner(os.Stdin) 159 | scan.Scan() 160 | email = scan.Text() 161 | 162 | exp: 163 | fmt.Print("License Experation (YYYY-MM-DD): ") 164 | scan = bufio.NewScanner(os.Stdin) 165 | scan.Scan() 166 | _, err := time.Parse("2006-01-02", scan.Text()) 167 | if err != nil { 168 | fmt.Println("Experation must be in the YYYY-MM-DD Format.") 169 | goto exp 170 | } 171 | experation = scan.Text() 172 | 173 | license = randomString(4) + "-" + randomString(4) + "-" + randomString(4) 174 | 175 | b, err := ioutil.ReadFile("db") 176 | if err != nil { 177 | os.Exit(0) 178 | } 179 | 180 | str := string(b) 181 | str = str + "\r\n" + license + ":" + experation + ":" + email + ":NOTSET" 182 | 183 | re := regexp.MustCompile("(?m)^\\s*$[\r\n]*") 184 | str2 := strings.Trim(re.ReplaceAllString(str, ""), "\r\n") 185 | 186 | err = ioutil.WriteFile("db", []byte(str2), 0644) 187 | if err != nil { 188 | os.Exit(0) 189 | } 190 | 191 | fmt.Println("New License Generated:", license, "for", email) 192 | case "add bulk": 193 | var experation string 194 | 195 | fmt.Println("Bulk accounts will be added to database without emails. You can add emails at a later time.") 196 | fmt.Println(" ") 197 | 198 | reader := bufio.NewReader(os.Stdin) 199 | fmt.Print("How many keys to generate? (#): ") 200 | bytes, _, err := reader.ReadLine() 201 | if err != nil { 202 | os.Exit(0) 203 | } 204 | 205 | amount := string(bytes) 206 | 207 | n, err := strconv.Atoi(amount) 208 | if err != nil { 209 | os.Exit(0) 210 | } 211 | 212 | expb: 213 | fmt.Print("License Experation (YYYY-MM-DD): ") 214 | scan = bufio.NewScanner(os.Stdin) 215 | scan.Scan() 216 | _, err = time.Parse("2006-01-02", scan.Text()) 217 | if err != nil { 218 | fmt.Println("Experation must be in the YYYY-MM-DD Format.") 219 | goto expb 220 | } 221 | experation = scan.Text() 222 | 223 | for i := 0; i < n; i++ { 224 | restart: 225 | var old string 226 | license := randomString(4) + "-" + randomString(4) + "-" + randomString(4) 227 | if license != old { 228 | b, err := ioutil.ReadFile("db") 229 | if err != nil { 230 | os.Exit(0) 231 | } 232 | 233 | str := string(b) 234 | str = str + "\r\n" + license + ":" + experation + ":null" + ":NOTSET" 235 | 236 | re := regexp.MustCompile("(?m)^\\s*$[\r\n]*") 237 | str2 := strings.Trim(re.ReplaceAllString(str, ""), "\r\n") 238 | 239 | err = ioutil.WriteFile("db", []byte(str2), 0644) 240 | if err != nil { 241 | os.Exit(0) 242 | } 243 | fmt.Println("New License Generated:", license) 244 | old = license 245 | } else { 246 | goto restart 247 | } 248 | } 249 | 250 | case "remove": 251 | fmt.Print("What email would you like to remove?: ") 252 | scan := bufio.NewScanner(os.Stdin) 253 | scan.Scan() 254 | 255 | for _, table := range database { 256 | 257 | row := strings.Split(table, ":") 258 | 259 | if scan.Text() == row[2] { //Found in DB 260 | 261 | b, err := ioutil.ReadFile("db") 262 | if err != nil { 263 | os.Exit(0) 264 | } 265 | 266 | str := string(b) 267 | res := strings.Replace(str, table, "", -1) 268 | 269 | re := regexp.MustCompile("(?m)^\\s*$[\r\n]*") 270 | reres := strings.Trim(re.ReplaceAllString(res, ""), "\r\n") 271 | 272 | err = ioutil.WriteFile("db", []byte(reres), 0644) 273 | if err != nil { 274 | os.Exit(0) 275 | } 276 | } 277 | } 278 | 279 | fmt.Println("Done") 280 | case "exit": 281 | os.Exit(0) 282 | default: 283 | fmt.Println("Unknown Command") 284 | } 285 | } 286 | } 287 | --------------------------------------------------------------------------------