Java 一种软件离线认证的方法

129 阅读1分钟
  1. 将认证信息使用 AES 加密
  2. 使用 RSA 算法,私钥签名公钥验签来确保 AES 加密后的内容没有被篡改
  3. 当然只能防君子,反编译或者修改本地时间戳都会被破解

下面是代码示例,会用到 hutool 工具类

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.symmetric.AES;

import java.nio.charset.StandardCharsets;
import java.security.Signature;

public class LicenseUtils {

  // 测试
  public static void main(String[] args) throws Exception {
    String publicKey = RsaDemo.getPublicKey();
    String privateKey = RsaDemo.getPrivateKey();
    String aesKey = "2131jcefue312123";
    String s = LicenseUtils.create(aesKey, privateKey, "3249234234", 1695544844000L);
    System.out.println("许可证:" + s);
    boolean verify = LicenseUtils.verify(aesKey, publicKey, s);
    System.out.println("验证是否成功:" + verify);
  }

  // 创建 许可证
  // 我这里要加密的内容,就只有一个项目ID和一个到期时间
  public static String create(String aesKey, String privateKey, String projectId, long time) {
    String body = projectId + "." + time;
    String s1 = aesEncryptHex(aesKey, body);
    String sign = rsaSign(privateKey, s1);
    return s1 + "." + sign;
  }

  // 验证许可证是否有效,true表示有效
  // 如何验证有效期,可以自行做逻辑处理
  public static boolean verify(String aesKey, String publicKey, String content) {
    String[] split = content.split("\\.");
    String body = split[0];
    String sign = split[1];
    if (verifySign(publicKey, body, sign)) {
      String s = aesDecryptStr(aesKey, body);
      String[] split1 = s.split("\\.");
      long time = Long.parseLong(split1[1]);
      return System.currentTimeMillis() <= time;
    }
    return false;
  }

  // AES 加密
  private static String aesEncryptHex(String aesKey, String body) {
    AES aes = SecureUtil.aes(aesKey.getBytes());
    return aes.encryptHex(body);
  }

  // AES 解密
  private static String aesDecryptStr(String aesKey, String body) {
    AES aes = SecureUtil.aes(aesKey.getBytes());
    return aes.decryptStr(body, CharsetUtil.CHARSET_UTF_8);
  }

  // 使用公钥验证签名
  private static boolean verifySign(String publicKey, String body, String sign) {
    RSA rsa = new RSA(null, publicKey);
    try {
      Signature signature = Signature.getInstance("SHA256withRSA");
      signature.initVerify(rsa.getPublicKey());
      signature.update(body.getBytes(StandardCharsets.UTF_8));
      return signature.verify(HexUtil.decodeHex(sign));
    } catch (Exception e) {
      e.printStackTrace();
    }
    return false;
  }

  // 使用私钥 SHA256withRSA 签名
  private static String rsaSign(String privateKey, String body) {
    RSA rsa = new RSA(privateKey, null);
    try {
      Signature signature = Signature.getInstance("SHA256withRSA");
      signature.initSign(rsa.getPrivateKey());
      signature.update(body.getBytes(StandardCharsets.UTF_8));
      byte[] signatureBytes = signature.sign();
      return HexUtil.encodeHexStr(signatureBytes);
    } catch (Exception e) {
      e.printStackTrace();
      throw new RuntimeException("createLicenseErr");
    }
  }
}