Skip to content

Instantly share code, notes, and snippets.

@tresf
Last active September 27, 2023 06:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tresf/0e11f4277cb0295d64813a0098b23eeb to your computer and use it in GitHub Desktop.
Save tresf/0e11f4277cb0295d64813a0098b23eeb to your computer and use it in GitHub Desktop.
Golang QZ Tray
package main
import (
"log"
"time"
"io/ioutil"
"crypto/rsa"
"encoding/pem"
"crypto/x509"
"crypto/sha1"
"crypto"
"encoding/json"
"crypto/rand"
"encoding/base64"
"net/url"
"github.com/gorilla/websocket"
"crypto/sha256"
"fmt"
"strings"
)
var pemKey = "path/to/private-key-rsa.pem"
var certFile = "path/to/digital-certificate.txt"
var connection *websocket.Conn
func main() {
err := SendCertificate()
if err == nil {
ListPrinters()
}
if connection != nil {
connection.Close()
}
}
func openConnection() (err error){
if connection == nil {
u := url.URL{Scheme: "ws", Host: "localhost:8182", Path: ""}
connection, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
return err
}
return nil
}
type qzSendCertRequest struct {
UID string `json:"uid"`
Certificate string `json:"certificate"`
Timestamp int64 `json:"timestamp"`
}
type qzListPrintersResponse struct {
UID string `json:"uid"`
Result []string `json:"result"`
}
// ListPrinters - list all printers that are available in the Qz.
func ListPrinters() ([]string, error) {
var err error
var printers qzListPrintersResponse
err = openConnection()
if err != nil {
log.Fatalf("Error while opening websocket connection: %s", err)
return nil, err
}
messageToSign := qzListPrinterReqToSign{}
messageToSign.Call = "printers.find"
messageToSign.Params = emptyStruct{}
messageToSign.Timestamp = time.Now().UnixNano() / (int64(time.Millisecond)/int64(time.Nanosecond))
unsignedMessage, _ := json.Marshal(messageToSign)
signature, err := signQzData(unsignedMessage)
if err != nil {
log.Fatalf("Error while signing message: %s", err)
return nil, err
}
log.Printf("Signature is: %s", signature)
message := qzListPrinterReqToSend{}
message.Call = messageToSign.Call
message.Promise = emptyStruct{}
message.Params = messageToSign.Params
message.Signature = signature
message.Timestamp = messageToSign.Timestamp
message.UID = "q1xm8n"
err = connection.WriteJSON(message)
if err != nil {
log.Fatalf("Error getting list of printers: %s", err)
return nil, err
}
log.Printf("Hashed and signed JSON message sent successfully")
var response []byte
_, response, err = connection.ReadMessage()
if err != nil {
log.Fatalf("Error getting list of printers: %s", err)
return nil, err
}
log.Printf("Read response successfully, unmarshalling...")
err = json.Unmarshal(response, &printers)
if err != nil {
log.Fatalf("Error while unmarshalling qz list printers response: %s, qzResponse: %s", err, response)
return nil, err
}
log.Printf("Found printers %s", strings.Join(printers.Result[:], ","))
return printers.Result, nil
}
type emptyStruct struct {
}
type qzListPrinterReqToSign struct {
Call string `json:"call"`
Params emptyStruct `json:"params""`
Timestamp int64 `json:"timestamp"`
}
type qzListPrinterReqToSend struct {
Call string `json:"call"`
Promise emptyStruct `json:"promise""`
Params emptyStruct `json:"params""`
Signature string `json:"signature"`
Timestamp int64 `json:"timestamp"`
UID string `json:"uid"`
}
func decodeKey() (*rsa.PrivateKey, error) {
b, err := ioutil.ReadFile(pemKey)
if err != nil {
return nil, err
}
block, _ := pem.Decode(b)
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
log.Fatalf("Error parsing key: %s", err)
return nil, err
}
return key, nil
}
func signQzData(data []byte) (string, error) {
rng := rand.Reader
log.Printf("Data to sign: %s", data)
sha_256 := sha256.New()
sha_256.Write(data)
dataHash := fmt.Sprintf("%x", sha_256.Sum(nil))
log.Printf("Hashed data to sign: %s", dataHash)
hashed := sha1.Sum([]byte(dataHash))
rsaPrivateKey, err := decodeKey()
if err != nil {
log.Fatalf("Error reading private key: %s\n", err)
return "", err
}
signature, err := rsa.SignPKCS1v15(rng, rsaPrivateKey, crypto.SHA1, hashed[:])
if err != nil {
log.Fatalf("Error from signing: %s\n", err)
return "", err
}
encodedSignature := base64.StdEncoding.EncodeToString(signature)
return encodedSignature, nil
}
func getCertificate() (string, error) {
cert, err := ioutil.ReadFile(certFile)
if err != nil {
log.Fatalf("Error loading certificate file. %s", err)
return "", err
}
return string(cert), nil
}
// SendCertificate - sends a certificate to the QZ
func SendCertificate() error {
var err error
var message qzSendCertRequest
cert, err := getCertificate()
if err != nil {
log.Fatalf("Error loading certificate. %s", err)
return err
}
message = qzSendCertRequest{}
message.Certificate = cert
message.Timestamp = time.Now().UnixNano() / (int64(time.Millisecond)/int64(time.Nanosecond))
message.UID = "abcde"
err = openConnection()
if err != nil {
log.Fatalf("Error opening websocket connection %s", err)
return err
}
err = connection.WriteJSON(message)
if err != nil {
log.Fatalf("Error sending certificate to QZ. %s", err)
return err
}
// Flush the response
_, _, err = connection.ReadMessage()
log.Printf("Certificate sent successfully")
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment