Implementation of Web system License authorization and authentication with springboot

Keywords: Java Spring Boot

When we make a system level framework, we must consider the copyright of the system to a certain extent. It can not be used by anyone in any environment. Therefore, we need to make an authorization and authentication mechanism for our system. It can be used normally only after uploading the lic file issued by us and passing the verification. Let's start to realize this function step by step

1. Generate machine code

The first thing we need to do is to limit the uniqueness of the software deployment environment. MAC adders are used here. Of course, you can also change it to cpu serial number without much impact. First, add the code:

private static String getMac() {
        try {
            Enumeration<NetworkInterface> el = NetworkInterface
                    .getNetworkInterfaces();
            while (el.hasMoreElements()) {
                byte[] mac = el.nextElement().getHardwareAddress();
                if (mac == null)
                    continue;
                String hexstr = bytesToHexString(mac);
                return getSplitString(hexstr, "-", 2).toUpperCase();
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return null;
    } 
 
public static String getMachineCode() throws Exception{
        Set<String> result = new HashSet<>();
        String mac = getMac();
        result.add(mac);
        Properties props = System.getProperties();
        String javaVersion = props.getProperty("java.version");
        result.add(javaVersion);
        String javaVMVersion = props.getProperty("java.vm.version");
        result.add(javaVMVersion);
        String osVersion = props.getProperty("os.version");
        result.add(osVersion);
        String code = Encrpt.GetMD5Code(result.toString());
        return getSplitString(code, "-", 4);
 
    }

The operation here is to take out the machine code, mix it with java version, jvm and operating system parameters, and perform MD5 operation

2. Generate lic file

 

This is the interface between my certificate generation and authorization certificate. You can see that the authorization certificate mainly includes three elements: machine code, permanent valid identification and certificate aging. We will write these data into the text and encrypt them. See the code for generating the certificate:

public static void getLicense(String isNoTimeLimit, String licenseLimit, String machineCode, String licensePath, String priavateKeyPath) throws Exception{
        String[] liccontent = {
                "LICENSEID=yanpeng19940119@gmail.com",
                "LICENSENAME=YBLOG Use certificate",
                MessageFormat.format("LICENSETYPE={0}",isNoTimeLimit),
                MessageFormat.format("EXPIREDAY={0}",licenseLimit), //The date is in yyyy MM DD date format
                MessageFormat.format("MACHINECODE={0}",machineCode),
                ""
        };
 
        //The lic content is mixed signed and written to the content
        StringBuilder sign = new StringBuilder();
        for(String item:liccontent){
            sign.append(item+"yblog");
        }
        liccontent[5] = MessageFormat.format("LICENSESIGN={0}",Encrpt.GetMD5Code(sign.toString()));
        FileUtil.createFileAndWriteLines(licensePath,liccontent);
        //Encrypt and replace the written content as a whole
        String filecontent =FileUtil.readFileToString(licensePath);
        String encrptfilecontent = Encrpt.EncriptWRSA_Pri(filecontent,priavateKeyPath);
        File file = new File(licensePath);
        file.delete();
        FileUtil.createFile(licensePath,encrptfilecontent);
    }

 

Here, we splice some information with a specific ID and then encrypt it. RSA encryption is used. We use private key encryption and public key decryption to ensure the openness of verification and the privacy of the generated certificate. The key can be generated using the keytool tool provided by java. Tutorial address: http://note.youdao.com/noteshare?id=09e2bfc902b21a335a4505f7946a45c9 , At the end of the lic file, we add a LICENSESIGN parameter to encrypt other information once to prevent the information from being tampered with. After the file is generated, we encrypt the text as a whole

Here, the length of the generated key is 2048 instead of 1024, so the length of the decryption block is 256. Note that the public key encryption method is. For your convenience, the following specific encryption code is provided:

private static final int MAX_ENCRYPT_BLOCK = 117;
private static final int MAX_DECRYPT_BLOCK=256; 
public static String EncriptWRSA_Pri(String data,String path) throws Exception{
        String encryptData ="";
 
        FileInputStream in = new FileInputStream(path);
        KeyStore ks = KeyStore.getInstance("JKS");// JKS: Java KeyStoreJKS, which can be of many types
        ks.load(in, "123".toCharArray());
        in.close();
 
        String alias = "yblogkey"; // Alias of the record
        String pswd = "123"; // Logged access password
        java.security.cert.Certificate cert = ks.getCertificate(alias);
        //Get private key
        PrivateKey privateKey = (PrivateKey) ks.getKey(alias, pswd.toCharArray());
        //Private key encryption
        Cipher cipher = Cipher.getInstance("rsa");
        SecureRandom random = new SecureRandom();
        cipher.init(Cipher.ENCRYPT_MODE, privateKey, random);
 
        try {
            //  Cipher cipher = Cipher.getInstance("RSA");
            //   cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            int length = data.getBytes().length;
            int offset = 0;
            byte[] cache;
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            int i = 0;
            while(length - offset > 0){
                if(length - offset > MAX_ENCRYPT_BLOCK){
                    cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
                }else{
                    cache = cipher.doFinal(data.getBytes(), offset, length - offset);
                }
                outStream.write(cache, 0, cache.length);
                i++;
                offset = i * MAX_ENCRYPT_BLOCK;
            }
            return encode.encode(outStream.toByteArray());
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return encryptData;
    }
 
 public static String DecriptWithRSA_Pub(String data,String path) throws Exception{
        X509Certificate x509Certificate = (X509Certificate) getCertificate(path);
        // Get public key
        PublicKey publicKey = x509Certificate.getPublicKey();
 
        Cipher cipher = Cipher.getInstance("rsa");
        SecureRandom random = new SecureRandom();
 
        byte[] bEncrypt = decoder.decodeBuffer(data);
        //Public key decryption
        cipher.init(Cipher.DECRYPT_MODE, publicKey, random);
        String decryptData = "";
        // byte[] plainData = cipher.doFinal(bEncrypt);
        //  System.out.println("11111:"+new String(plainData));
        int inputLen = bEncrypt.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // Decrypt data segments
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(bEncrypt, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(bEncrypt, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return  new String(decryptedData);
    }

3. Verify lic

We will register an interceptor in the system. If we fail to pass the system authorization and authentication, we will automatically jump to the lic file upload interface. The files received by springboot are different from those in conventional java. The MultipartFile object used will obtain the array of uploaded files for operation. See the code to save the uploaded lic file:

@RequestMapping(value="/login/licenseauth",method= RequestMethod.POST)
    @ResponseBody
    public Map<Object,Object> licenseauth(MultipartHttpServletRequest multiReq){
        Map<Object,Object>  map = new HashMap<Object,Object>();
        try {
            String savePath = ResourceUtils.getURL("src/main/resources/static/lic").getPath();
            MultipartFile file = multiReq.getFile("file");
            String filename = file.getOriginalFilename();
            File uploadfile = new File(savePath + "\\" + filename);
            if (!uploadfile.exists()){
                //Get the input stream of the uploaded file in item
                InputStream in = file.getInputStream();
                //Create a file output stream
                FileOutputStream out = new FileOutputStream(savePath + "\\" + filename);
                //Create a buffer
                byte buffer[] = new byte[1024];
                //Identification to judge whether the data in the input stream has been read
                int len = 0;
                //The loop reads the input stream into the buffer, (len = in. Read (buffer)) > 0 indicates that there is data in the in
                while((len=in.read(buffer))>0){
                    //Use the FileOutputStream output stream to write the buffer data to the specified directory (savePath + "\" + filename)
                    out.write(buffer, 0, len);
                }
                //Close input stream
                in.close();
                //Close output stream
                out.close();
            }
            map.put("executestatus","1");
 
        }catch (Exception e){
            e.printStackTrace();
            map.put("executestatus","0");
        }
 
        return map;
    }

With the uploaded file, we can verify the machine code and authorization time of the lic file through the built-in public key of the system to determine whether we can access the system normally

public static boolean authLicense() throws Exception{
        boolean isauth = false;
        String pubkpath = ResourceUtils.getURL("src/main/resources/static/lic/").getPath()+"yblog.crt";
        String licpath = ResourceUtils.getURL("src/main/resources/static/lic/").getPath();
        File lic = new File(licpath);
        String[] filelist = lic.list();
        if (filelist.length>0){
            for (int i = 0; i < filelist.length; i++) {
                if (filelist[i].contains(".lic")){
                    File readfile = new File(licpath + filelist[i]);
                    if (readfile.isFile()) {
                        String liccontent = FileUtil.readFileToString(readfile);
                        String decriptliccontent = Encrpt.DecriptWithRSA_Pub(liccontent,pubkpath);
                        HashMap<String, String> props = genDataFromArrayByte(decriptliccontent.getBytes());
                        String licenseid = props.get("LICENSEID");
                        String licensename= props.get("LICENSENAME");
                        String licensetype = props.get("LICENSETYPE");
                        String liclimit = props.get("EXPIREDAY");
                        String machinecode = props.get("MACHINECODE");
                        String lincensesign = props.get("LICENSESIGN");
                        //Verify signature
                        String allinfogroup = "LICENSEID="+licenseid+"yblog"+"LICENSENAME="+licensename+"yblog"+
                                "LICENSETYPE="+licensetype+"yblog"+"EXPIREDAY="+liclimit+"yblog"+"MACHINECODE="+machinecode+"yblogyblog";
                        if (lincensesign.equals(Encrpt.GetMD5Code(allinfogroup))){
                            //Verify machine code
                            if (getMachineCode().equals(machinecode)){
                                SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
                                Date bt=new Date();
                                Date et=sdf.parse(liclimit);
                                //Verification time
                                if(bt.compareTo(et)<=0){
                                    isauth = true;
                                    System.out.println("Registration file:"+filelist[i]+",Verified");
                                    break;
                                }else{
                                    System.out.println("Certificate expiration");
                                }
                            }else{
                                System.out.println("Inconsistent machine code");
                            }
                        }else{
                            System.out.println("Inconsistent signature");
                        }
                    }
                }
            }
        }else{
            System.out.println("Certificate not uploaded");
        }
        return isauth;
    }

Posted by fotakis on Wed, 29 Sep 2021 20:33:58 -0700