mirror of
https://github.com/unidoc/unioffice.git
synced 2025-04-25 13:48:53 +08:00
187 lines
4.6 KiB
Go
187 lines
4.6 KiB
Go
// Copyright 2017 Baliance. All rights reserved.
|
|
//
|
|
// Use of this source code is governed by the terms of the Affero GNU General
|
|
// Public License version 3.0 as published by the Free Software Foundation and
|
|
// appearing in the file LICENSE included in the packaging of this file. A
|
|
// commercial license can be purchased by contacting sales@baliance.com.
|
|
|
|
package gooxml
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"crypto"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/sha256"
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"log"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// OpenSourceLicense is the AGPL open source license. It is installed by default
|
|
// if no license has previously been installed.
|
|
const OpenSourceLicense = `
|
|
H4sIAAAAAAAC_xzJsU7DQAwA0J2vqDynks--y9nZ
|
|
2RAMLQubfeegDKRR2kogxL8jkN72vuHZPgImeNli
|
|
PZwu973F4fUaOwxwWt5Xu933v86Fqjfnlq1mKWzU
|
|
SXzEwCKoqWc0sREtvCYlsdlV2b12CwqyntVk7orZ
|
|
eTQRtMhMoonHaDh3DMsYVEir10JzbV3CW1FmyrM4
|
|
wQCPn9uy2225rDABI-Ix0ZHTGXH69wYDPC0t1muc
|
|
v7aAKf08_AYAAP__z2E3oN8A`
|
|
|
|
var license *License
|
|
|
|
const pubKeyHex = `305c300d06092a864886f70d0101010500034b003048024100b87eafb6c07499eb97cc9d3565ecf3168196301907c841addc665086bb3ed8eb12d9da26cafa96450146da8bd0ccf155fcacc686955ef0302fa44aa3ec89417b0203010001`
|
|
|
|
var pubKey *rsa.PublicKey
|
|
|
|
func init() {
|
|
pubKeyBytes, err := hex.DecodeString(pubKeyHex)
|
|
if err != nil {
|
|
log.Fatalf("error reading key: %s", err)
|
|
}
|
|
pkRaw, err := x509.ParsePKIXPublicKey(pubKeyBytes)
|
|
if err != nil {
|
|
log.Fatalf("error reading key: %s", err)
|
|
}
|
|
pubKey = pkRaw.(*rsa.PublicKey)
|
|
}
|
|
|
|
// LicenseType is the type of license
|
|
//go:generate stringer -type=LicenseType
|
|
type LicenseType byte
|
|
|
|
// LicenseType constants
|
|
const (
|
|
LicenseTypeInvalid LicenseType = iota
|
|
LicenseTypeAGPL
|
|
LicenseTypeCommercial
|
|
)
|
|
|
|
// License holds the gooxml license information.
|
|
type License struct {
|
|
Name string
|
|
Signature string `json:",omitempty"`
|
|
Expiration time.Time
|
|
LicenseType LicenseType
|
|
}
|
|
|
|
// Sign signs a license with a private key, setting the license's signature
|
|
// value
|
|
func (l *License) Sign(privKey *rsa.PrivateKey) error {
|
|
l.Signature = ""
|
|
buf := bytes.Buffer{}
|
|
enc := json.NewEncoder(&buf)
|
|
if err := enc.Encode(l); err != nil {
|
|
return err
|
|
}
|
|
|
|
hashed := sha256.Sum256(buf.Bytes())
|
|
signature, err := rsa.SignPKCS1v15(rand.Reader, privKey, crypto.SHA256, hashed[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
l.Signature = hex.EncodeToString(signature)
|
|
return nil
|
|
}
|
|
|
|
// Verify verifies a license by checking the license content and signature
|
|
// against a public key.
|
|
func (l License) Verify(pubKey *rsa.PublicKey) error {
|
|
cp := l
|
|
cp.Signature = ""
|
|
buf := bytes.Buffer{}
|
|
enc := json.NewEncoder(&buf)
|
|
if err := enc.Encode(cp); err != nil {
|
|
return err
|
|
}
|
|
sig, err := hex.DecodeString(l.Signature)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hashed := sha256.Sum256(buf.Bytes())
|
|
err = rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, hashed[:], sig)
|
|
return err
|
|
}
|
|
|
|
func (l License) String() string {
|
|
buf := bytes.Buffer{}
|
|
enc := json.NewEncoder(&buf)
|
|
enc.Encode(l)
|
|
return buf.String()
|
|
}
|
|
|
|
// Encoded returns a base64 encoded version of the license for use with
|
|
// InstallLicense.
|
|
func (l License) Encoded() string {
|
|
buf := bytes.Buffer{}
|
|
w := base64.NewEncoder(base64.RawURLEncoding, &buf)
|
|
gz, _ := gzip.NewWriterLevel(w, gzip.BestCompression)
|
|
enc := json.NewEncoder(gz)
|
|
enc.Encode(l)
|
|
gz.Close()
|
|
|
|
rsp := bytes.Buffer{}
|
|
const maxLen = 40
|
|
raw := buf.Bytes()
|
|
for i := 0; i < buf.Len(); i += maxLen {
|
|
rsp.Write(raw[i : i+maxLen])
|
|
rsp.WriteByte('\r')
|
|
rsp.WriteByte('\n')
|
|
}
|
|
return rsp.String()
|
|
}
|
|
|
|
// InstallLicense installs a license, returning an error if the license is
|
|
// invalid or expired. Expiration checks the ReleaseDate variable in version.go.
|
|
func InstallLicense(s string) error {
|
|
re := regexp.MustCompile("\\s")
|
|
s = re.ReplaceAllString(s, "")
|
|
|
|
var r io.Reader
|
|
r = strings.NewReader(s)
|
|
r = base64.NewDecoder(base64.RawURLEncoding, r)
|
|
r, err := gzip.NewReader(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dec := json.NewDecoder(r)
|
|
l := &License{}
|
|
if err := dec.Decode(l); err != nil {
|
|
return err
|
|
}
|
|
// check signature
|
|
if err := l.Verify(pubKey); err != nil {
|
|
return errors.New("license validatin error")
|
|
}
|
|
|
|
if l.Expiration.Before(ReleaseDate) {
|
|
return errors.New("license expired")
|
|
}
|
|
license = l
|
|
return nil
|
|
}
|
|
|
|
// GetLicense returns the current license. This can be used by commercial
|
|
// customers to assist in ensuring that their license hasn't expired.
|
|
func GetLicense() License {
|
|
if license == nil {
|
|
if err := InstallLicense(OpenSourceLicense); err != nil {
|
|
log.Printf("open source license error: %s", err)
|
|
}
|
|
}
|
|
if license != nil {
|
|
return *license
|
|
}
|
|
return License{}
|
|
}
|