Last active
September 27, 2023 06:50
-
-
Save tresf/0e11f4277cb0295d64813a0098b23eeb to your computer and use it in GitHub Desktop.
Golang QZ Tray
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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