是否可以为我的电子应用程序创建产品密钥?

Is it possible to create product keys for my electron application?

我想构建一个桌面应用程序并能够发布产品密钥或序列号 numbers.Before 用户可以使用该应用程序,他将被要求输入产品 key/serial 编号。

类似于 Microsoft Office,因为它们提供像 XXXX-XXXX-XXXX-XXXX 这样的密钥

我的想法是根据许可证销售应用程序,为每台设备提供产品密钥似乎比帐户(用户名和密码)更专业。

所以我的问题是:

1) 是否可以用 electron 完成此操作?

2) 你能告诉我应该去序列号(如果可行的话)还是帐户吗?或者有更好的选择吗?

3) 如果你回答了第二个问题。请说明原因?

  1. 是的,但关于软件注册机制,这很困难,也需要大量测试。
  2. 如果您的 90% 的用户可以访问互联网,您绝对应该使用用户帐户或一些 OAUTH 2.0 即插即用的东西(使用 facebook/gmail/whatever 登录)
  3. 我使用 crypto 和 fs 模块从头开始构建了一个软件许可架构,这是一个相当长的旅程(一年)!

不建议从头开始为您的软件建立良好的注册机制,electron 更难,因为源代码相对公开。

话虽这么说,如果你真的想走那条路,bcrypt 擅长这个(散列),你需要一个唯一的用户标识符来散列,你还需要某种持久性(最好是一个文件),你可以存储用户许可证,并且您需要通过散列散列来隐藏您用于散列的盐...或者将它的一小部分存储在单独的文件中。

这将成为许可的良好起点,但远未完全安全。

希望对您有所帮助!

2021 年编辑:我想修改这个答案,因为它产生了很多关于我在许可证密钥和用户帐户之间进行的比较的查询。我以前几乎总是建议使用用户帐户来许可 Electron 应用程序,但后来我改变了我的立场,变得更加微妙。对于大多数 Electron 应用程序,许可证密钥就可以了。

向 Electron 应用程序添加许可证密钥(与产品密钥同义)验证可以非常简单。首先,您可能希望以某种方式为每个用户生成一个许可证密钥。这可以使用密码学来完成,也可以通过生成 'random' 许可证密钥字符串并将其存储在数据库中,然后构建可以验证给定许可证密钥是否“有效”的 CRUD 许可服务器来完成。 =15=]

对于加密许可证密钥,您可以从客户那里获取一些信息,例如他们的订单号或电子邮件地址,并使用 RSA 加密创建 'signature'。使用 Node,它看起来像这样:

const crypto = require('crypto')

// Generate a new keypair
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
  // Using a larger key size, such as 2048, would be more secure
  // but will result in longer signatures.
  modulusLength: 512,
  privateKeyEncoding: { type: 'pkcs1', format: 'pem' },
  publicKeyEncoding: { type: 'pkcs1', format: 'pem' },
})

// Some data we're going to use to generate a license key from
const data = 'user@store.example'

// Create a RSA signer
const signer = crypto.createSign('rsa-sha256')
signer.update(data)

// Encode the original data
const encoded = Buffer.from(data).toString('base64')

// Generate a signature for the data
const signature = signer.sign(privateKey, 'hex')

// Combine the encoded data and signature to create a license key
const licenseKey = `${encoded}.${signature}`

console.log({ privateKey, publicKey, licenseKey })

然后,要在您的 Electron 应用程序中验证许可证密钥,您需要通过将上面生成的 public(不是私有!)密钥嵌入到您的应用程序中来加密 'verify' 密钥的真实性代码库:

// Split the license key's data and the signature
const [encoded, signature] = licenseKey.split('.')
const data = Buffer.from(encoded, 'base64').toString()

// Create an RSA verifier
const verifier = crypto.createVerify('rsa-sha256')
verifier.update(data)

// Verify the signature for the data using the public key
const valid = verifier.verify(publicKey, signature, 'hex')

console.log({ valid, data })

像这样生成和验证加密签名的许可证密钥的真实性对于许多简单的许可需求来说非常有用。它们相对简单,离线时也能很好地工作,但有时验证许可证密钥是否为 'valid' 是不够的。有时要求规定许可证密钥不是永久性的(即 'valid' 永远),或者他们需要更复杂的许可系统,例如一次只有有限数量的设备(或席位)可以使用该应用程序的系统。或者许可证密钥可能需要一个可更新的到期日期。这就是许可证服务器可以发挥作用的地方。

许可证服务器可以帮助管理许可证的激活、到期等,例如用于将多个许可证或功能许可证与单个用户或团队相关联的用户帐户。我不推荐用户帐户,除非您有特定需要,例如您需要额外的用户个人资料信息,或者您需要将多个许可证与单个用户相关联。

但是,如果您不是特别热衷于编写和维护自己的内部许可系统,或者您只是不想像上面那样编写自己的许可证密钥生成器,我名为 Keygen 的软件许可 API 的创始人,它可以帮助您快速启动和 运行ning,而无需编写和托管您自己的许可服务器。 :)

Keygen 是一种典型的 HTTP JSON API 服务(即没有任何软件需要与您的应用程序打包在一起)。它可以用于任何编程语言和 Electron 等框架。

在最简单的形式中,使用 Keygen 验证许可证密钥就像点击单个 JSON API 端点一样简单(在终端中随意 运行 ):

curl -X POST https://api.keygen.sh/v1/accounts/demo/licenses/actions/validate-key \
  -d '{
        "meta": {
          "key": "C1B6DE-39A6E3-DE1529-8559A0-4AF593-V3"
        }
      }'

我最近整理了一个向 Electron 应用程序添加许可证密钥验证以及设备激活和管理的示例。您可以在 GitHub 上查看该回购协议:https://github.com/keygen-sh/example-electron-license-activation.

我希望这能回答您的问题并给您一些见解。很高兴回答您的任何其他问题,因为我现在已经为 Electron 应用程序实施了几次许可。 :)

有许多服务可以帮助您将基于许可证密钥的软件许可添加到您的应用程序。为了确保您的客户不会重复使用密钥,您需要强大的设备指纹识别算法。

您可以尝试 Cryptlex. It offers a very robust licensing solution with advanced device fingerprinting algo. You can check out the Node.js example on Github 为您的电子应用程序添加许可。

是的,可能的。

我自己想要这个功能,我找到了相关的解决方案,例如 paid video tutorials, online solutions [with Keygen] 和其他随机 hack,但我想要的是 offline免费,所以我创建了自己的存储库供 myself/others 使用。这是它的工作原理。

概述

  1. 安装secure-electron-license-keys-cli。 (即 npm i -g secure-electron-license-keys-cli)。
  2. 通过 运行ning secure-electron-license-keys-cli 创建许可证密钥。这会生成 public.keyprivate.keylicense.data.
  3. 保持 private.key 安全,但将 public.keylicense.data 粘贴在您的 Electron 应用程序的根目录中。
  4. 安装secure-electron-license-keys。 (即 npm i secure-electron-license-keys)。
  5. 在您的 main.js 文件中,查看此示例代码并添加必要的绑定。
const {
    app,
    BrowserWindow,
    ipcMain,
} = require("electron");
const SecureElectronLicenseKeys = require("secure-electron-license-keys");
const path = require("path");
const fs = require("fs");
const crypto = require("crypto");

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;

async function createWindow() {

    // Create the browser window.
    win = new BrowserWindow({
        width: 800,
        height: 600,
        title: "App title",
        webPreferences: {
            preload: path.join(
                __dirname,
                "preload.js"
            )
        },
    });

    // Setup bindings for offline license verification
    SecureElectronLicenseKeys.mainBindings(ipcMain, win, fs, crypto, {
        root: process.cwd(),
        version: app.getVersion(),
    });

    // Load app
    win.loadURL("index.html");

    // Emitted when the window is closed.
    win.on("closed", () => {
        // Dereference the window object, usually you would store windows
        // in an array if your app supports multi windows, this is the time
        // when you should delete the corresponding element.
        win = null;
    });
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on("ready", createWindow);

// Quit when all windows are closed.
app.on("window-all-closed", () => {
    // On macOS it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== "darwin") {
        app.quit();
    } else {
        SecureElectronLicenseKeys.clearMainBindings(ipcMain);
    }
});
  1. 在您的 preload.js 文件中,查看示例代码并添加支持代码。
const {
    contextBridge,
    ipcRenderer
} = require("electron");
const SecureElectronLicenseKeys = require("secure-electron-license-keys");

// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld("api", {
    licenseKeys: SecureElectronLicenseKeys.preloadBindings(ipcRenderer)
});
  1. 查看示例 React 组件如何验证您的许可证的有效性,并在您的应用程序中采取相应的行动。
import React from "react";
import {
  validateLicenseRequest,
  validateLicenseResponse,
} from "secure-electron-license-keys";

class Component extends React.Component {
  constructor(props) {
    super(props);

    this.checkLicense = this.checkLicense.bind(this);
  }

  componentWillUnmount() {
    window.api.licenseKeys.clearRendererBindings();
  }

  componentDidMount() {
    // Set up binding to listen when the license key is
    // validated by the main process
    const _ = this;

    window.api.licenseKeys.onReceive(validateLicenseResponse, function (data) {
      console.log("License response:");
      console.log(data);
    });
  }

  // Fire event to check the validity of our license
  checkLicense(event) {
    window.api.licenseKeys.send(validateLicenseRequest);
  }

  render() {
    return (
      <div>
        <button onClick={this.checkLicense}>Check license</button>
      </div>
    );
  }
}

export default Component;

大功告成!

更多详情

为了进一步解释,许可证由来自客户端(即前端)页面的请求验证。客户端通过此调用 (window.api.licenseKeys.send(validateLicenseRequest)).

向主(即后端)进程发送一个 IPC request

一旦后端进程接收到这个调用(因为我们用这个调用设置它(SecureElectronLicenseKeys.mainBindings),它被连接起来),库代码尝试用 [= 解密 license.data 17=]。无论是否成功,成功状态都会发送回客户端页面(通过 IPC)。

如何按版本限制许可证密钥

我所解释的内容非常有限,因为它不限制您可能提供给特定用户的应用程序的版本。 secure-electron-license-keys-cli 包括您在生成许可证密钥时可能传递的标志,以便为许可证设置特定的 major/minor/patch/expire 值。

如果你想允许最多 7 个主要版本,你可以 运行 命令生成许可证文件,如下所示: secure-electron-license-keys-cli --major "7"

如果您想允许最多 7 个主要版本并在 2022 年 12 月 31 日到期,您可以 运行 命令生成许可证文件,如下所示: secure-electron-license-keys-cli --major "7" --expire "2022-12-31"

如果您执行 运行这些命令,您将需要更新您的客户端页面以便与它们进行比较,即:

window.api.licenseKeys.onReceive(validateLicenseResponse, function (data) {

    // If the license key/data is valid
    if (data.success) {

        if (data.appVersion.major <= data.major &&
            new Date() <= Date.parse(data.expire)) {

            // User is able to use app
        } else {
            // License has expired
        }
    } else {
        // License isn't valid
    }
});

repository page 有更多选项的详细信息,但这应该会给您 要点 您必须做的事情。

限制

这并不完美,但可能会处理 90% 的用户。这不能防止:

  • 有人反编译您的应用程序并制作他们自己的许可证 use/removing 完全许可代码
  • 有人复制许可证并将其交给另一个人

如果您要打包多个或自动 .exe,还需要关注如何 运行 这个库,因为这些许可文件需要包含在源代码中。我会把它留给你的创造力来解决。

额外资源/免责声明

我构建了这个问题中提到的所有 secure-electron-* 存储库,并且我还维护 secure-electron-template,其中已经预先设置了许可证密钥如果您需要一些交钥匙的东西,可以加入解决方案。