Add custom QZ Tray fork with pairing key authentication
- Custom fork of QZ Tray 2.2.x with certificate validation bypassed - Implemented pairing key (HMAC) authentication as replacement - Modified files: PrintSocketClient.java (certificate check disabled) - New files: PairingAuth.java, PairingConfigDialog.java - Excluded build artifacts (out/, lib/javafx*) from repository - Library JARs included for dependency management
This commit is contained in:
BIN
tray/assets/branding/apple-icon.icns
Normal file
BIN
tray/assets/branding/apple-icon.icns
Normal file
Binary file not shown.
23
tray/assets/branding/linux-icon.svg
Normal file
23
tray/assets/branding/linux-icon.svg
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg id="svg4208" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="128" width="128" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="0 0 128.00001 128.00001">
|
||||
<metadata id="metadata4213">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g id="layer1" transform="translate(-125.25 -253.66)">
|
||||
<path id="XMLID_48_" d="m253.07 310.83v2.3662l-0.78875 3.9437-5.5211 32.338-65.465 30.761v-41.014c0-5.5212-2.3662-11.042-5.5212-14.197l-0.78873-0.78874-49.69-46.535 73.352-22.085 48.113 40.226 0.78875 0.78875c3.1549 3.1549 5.5211 7.8874 5.5211 14.197z" fill="#44aa53"/>
|
||||
<path id="rect3864" d="m172.93 304.22 46.535-15.775 11.042 8.6761-46.535 17.352-11.042-10.254z" fill="#2a6831"/>
|
||||
<path id="path3860" d="m161.58 270.6" fill="none"/>
|
||||
<path id="XMLID_47_" d="m182.08 308.46 37.071-13.408s0-22.085-22.085-34.704l-36.282 11.042c0.78873 0 22.873 14.197 21.296 37.071z" fill="#fff"/>
|
||||
<path id="XMLID_45_" d="m156.84 339.22" fill="#597239"/>
|
||||
<path id="XMLID_37_" d="m181.29 338.92v41.32l-43.977-34.169-11.994-68.337 50.373 46.882 0.79958 0.79461c2.3987 2.3839 4.7974 7.9462 4.7974 13.508z" fill="#2a6831"/>
|
||||
<path id="XMLID_35_" d="m181.29 380.24z" fill="#597239"/>
|
||||
<polygon id="XMLID_5_" points="425.9 74 425.9 74" fill="#597239" transform="matrix(7.8874 0 0 7.8874 -3177.9 -203.43)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
BIN
tray/assets/branding/windows-icon.ico
Normal file
BIN
tray/assets/branding/windows-icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 361 KiB |
33
tray/assets/epl_multiples.txt
Normal file
33
tray/assets/epl_multiples.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
N
|
||||
q812;four inch width for 203dpi printer
|
||||
Q1218,26;six inch height for 203dpi printer
|
||||
B5,26,0,1A,3,7,152,B,"0001"
|
||||
A310,26,0,3,1,1,N,"SKU 00001 MFG 0001"
|
||||
A310,56,0,3,1,1,N,"QZ-Tray Java Application"
|
||||
A310,86,0,3,1,1,N,"TEST PRINT SUCCESSFUL"
|
||||
A310,116,0,3,1,1,N,"FROM SAMPLE.HTML"
|
||||
A310,146,0,3,1,1,N,"EDIT EPL_MULTIPLES.TXT"
|
||||
P1,1
|
||||
|
||||
N
|
||||
q812
|
||||
Q1218,26
|
||||
B5,26,0,1A,3,7,152,B,"0002"
|
||||
A310,26,0,3,1,1,N,"SKU 00002 MFG 0002"
|
||||
A310,56,0,3,1,1,N,"QZ-Tray Java Application"
|
||||
A310,86,0,3,1,1,N,"TEST PRINT SUCCESSFUL"
|
||||
A310,116,0,3,1,1,N,"FROM SAMPLE.HTML"
|
||||
A310,146,0,3,1,1,N,"EDIT EPL_MULTIPLES.TXT"
|
||||
P1,1
|
||||
|
||||
N
|
||||
q812
|
||||
Q1218,26
|
||||
B5,26,0,1A,3,7,152,B,"0003"
|
||||
A310,26,0,3,1,1,N,"SKU 00003 MFG 0003"
|
||||
A310,56,0,3,1,1,N,"QZ-Tray Java Application"
|
||||
A310,86,0,3,1,1,N,"TEST PRINT SUCCESSFUL"
|
||||
A310,116,0,3,1,1,N,"FROM SAMPLE.HTML"
|
||||
A310,146,0,3,1,1,N,"EDIT EPL_MULTIPLES.TXT"
|
||||
P1,1
|
||||
11
tray/assets/epl_sample.txt
Normal file
11
tray/assets/epl_sample.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
N
|
||||
q812
|
||||
Q1218,26; q and Q values edited to reflect 4x6 inch size at 203 dpi
|
||||
B5,26,0,1A,3,7,152,B,"1234"
|
||||
A310,26,0,3,1,1,N,"SKU 00000 MFG 0000"
|
||||
A310,56,0,3,1,1,N,"QZ-Tray Java Application"
|
||||
A310,86,0,3,1,1,N,"TEST PRINT SUCCESSFUL"
|
||||
A310,116,0,3,1,1,N,"FROM SAMPLE.HTML"
|
||||
A310,146,0,3,1,1,N,"EDIT EPL_SAMPLE.TXT"
|
||||
P1,1
|
||||
BIN
tray/assets/escpos_sample.bin
Normal file
BIN
tray/assets/escpos_sample.bin
Normal file
Binary file not shown.
25
tray/assets/fgl_sample.txt
Normal file
25
tray/assets/fgl_sample.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
<n><DA><RL><RC300,20><F12>BOCA <F6><RC100,40>
|
||||
<NR><RC290,110><RL><F6><HW1,1>PROM=
|
||||
<RC300,160><RL><HW1,1><VA7><RC370,740><RU>GHOSTWRITER
|
||||
<RC48,880><RR><F2>FRIENDLY GHOST LANGUAGE
|
||||
<RC52,862>PLACE LETTERS ANYWHERE
|
||||
<RC80,1170><F6><RR>VOID
|
||||
<RC15,1100><F3>TEST TICKET ONLY
|
||||
<RC8,1010><X2><NXL10>*GHOST 123*<RR>
|
||||
<RC78,915> CODE 39
|
||||
<RU><RC320,740><BS17,30>TICKET & LABEL PRINTER
|
||||
<NR><F3><RC0,300>EXCELLENT PRINT QUALITY
|
||||
<NR><RC30,300> Print any bar code
|
||||
<RC70,300><X2><OL6>^CODE 128^
|
||||
<RC90,250><RR>CODE 128
|
||||
<RC96,340><X3><NXP10>*MONKEY*
|
||||
<RC206,340><X2><NP6>*MONKEY*
|
||||
<F2><HW1,1><NR><F1><SP180,640><LO1>
|
||||
<RC371,385><F2><NR>SW1= SW2= SW3=<RC371,824>LEFT = 1 RIGHT = 0
|
||||
<RC385,150><F11>Script printing is now standard.
|
||||
<RC430,150><F9>High density printing is clear and readable
|
||||
<RC450,150><F2>High density printing is clear and readable
|
||||
<RC470,150><F1>Legal size printing
|
||||
<RC0,100><LT4><VX1200>
|
||||
<p>
|
||||
|
||||
BIN
tray/assets/img/fade-test.png
Normal file
BIN
tray/assets/img/fade-test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.3 KiB |
BIN
tray/assets/img/image_sample.png
Normal file
BIN
tray/assets/img/image_sample.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
BIN
tray/assets/img/image_sample_bw.png
Normal file
BIN
tray/assets/img/image_sample_bw.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
BIN
tray/assets/pdf_sample.pdf
Normal file
BIN
tray/assets/pdf_sample.pdf
Normal file
Binary file not shown.
12
tray/assets/pgl_sample.txt
Normal file
12
tray/assets/pgl_sample.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
~CREATE;DATAMATRIX;144
|
||||
SCALE;DOT;300;300
|
||||
ALPHA
|
||||
POINT;50;100;16;9;*Printed using QZ Tray*
|
||||
STOP
|
||||
BARCODE
|
||||
DATAMATRIX;XD16;C20;R20;ECC200;ID5;150;150
|
||||
*0100000123000017*
|
||||
STOP
|
||||
END
|
||||
~EXECUTE;DATAMATRIX;1
|
||||
~NORMAL
|
||||
23
tray/assets/sbpl_sample.txt
Normal file
23
tray/assets/sbpl_sample.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
AA1V01678H0812
|
||||
PS%0H0040V01510FW02H0735
|
||||
%2H200V1590P02RDB@1,057,056,SATO
|
||||
%0H0040V01076FW02H0735
|
||||
%2H0353V01215P02RDB@0,026,025,BEST BEFORE
|
||||
%2H0761V01215P02RDB@0,026,025,BATCH/LOT
|
||||
%2H0761V01370P02RDB@0,026,025,CONTENT
|
||||
%2H0761V01489P02RDB@0,026,025,SSCC
|
||||
%2H0758V01590P02RDB@1,057,056,Product
|
||||
%2H0777V01669P02RDB@0,057,056,
|
||||
%2H0761V01183P02RDB@0,061,062,223344
|
||||
%2H0589V00109P02RDB@1,038,037,(00)038312345600000018
|
||||
%2H0741V01052BG04256>I>F0203831234560087370001
|
||||
%2H0719V00374BG04256>I>F00038312345600000018
|
||||
%2H0617V00786P02RDB@1,038,037,(02)03831234560087(37)0001
|
||||
%2H0139V01370P02RDB@0,026,025,COUNT
|
||||
%2H0139V01344P02RDB@0,061,062,0001
|
||||
%2H0761V01344P02RDB@0,061,062,03831234560087
|
||||
%2H0761V01463P02RDB@1,061,062,038312345600000018
|
||||
%2H0675V00697BG04240>I>F1525052010223344
|
||||
%2H0559V00443P02RDB@1,038,037,(15)250520(10)223344
|
||||
%2H0355V01182P02RDB@0,061,062,20.05.25
|
||||
Q1Z
|
||||
38
tray/assets/signing/sign-message.R
Normal file
38
tray/assets/signing/sign-message.R
Normal file
@@ -0,0 +1,38 @@
|
||||
#
|
||||
# Echoes the signed message and exits
|
||||
# usage: R sign-message.R "test"
|
||||
#
|
||||
|
||||
#########################################################
|
||||
# WARNING WARNING WARNING #
|
||||
#########################################################
|
||||
# #
|
||||
# This file is intended for demonstration purposes #
|
||||
# only. #
|
||||
# #
|
||||
# It is the SOLE responsibility of YOU, the programmer #
|
||||
# to prevent against unauthorized access to any signing #
|
||||
# functions. #
|
||||
# #
|
||||
# Organizations that do not protect against un- #
|
||||
# authorized signing will be black-listed to prevent #
|
||||
# software piracy. #
|
||||
# #
|
||||
# -QZ Industries, LLC #
|
||||
# #
|
||||
#########################################################
|
||||
|
||||
library(openssl)
|
||||
|
||||
mykey <- "private-key.pem"
|
||||
|
||||
# Treat command line argument as message to be signed
|
||||
message <- enc2utf8(commandArgs(trailingOnly = TRUE))
|
||||
|
||||
# Load the private key
|
||||
key <- read_key(file = mykey, password = mypass)
|
||||
|
||||
# Create the signature
|
||||
sig <- signature_create(serialize(message, NULL), hash = sha512, key = key) # Use hash = sha1 for QZ Tray 2.0 and older
|
||||
|
||||
print(sig)
|
||||
50
tray/assets/signing/sign-message.asp
Normal file
50
tray/assets/signing/sign-message.asp
Normal file
@@ -0,0 +1,50 @@
|
||||
<%
|
||||
'#########################################################
|
||||
'# WARNING WARNING WARNING #
|
||||
'#########################################################
|
||||
'# #
|
||||
'# This file is intended for demonstration purposes #
|
||||
'# only. #
|
||||
'# #
|
||||
'# It is the SOLE responsibility of YOU, the programmer #
|
||||
'# to prevent against unauthorized access to any signing #
|
||||
'# functions. #
|
||||
'# #
|
||||
'# Organizations that do not protect against un- #
|
||||
'# authorized signing will be black-listed to prevent #
|
||||
'# software piracy. #
|
||||
'# #
|
||||
'# -QZ Industries, LLC #
|
||||
'# #
|
||||
'#########################################################
|
||||
Option Explicit
|
||||
Dim rsa, pem, sig, data, glob, success, password
|
||||
' New unlock method for Chilkat - Unregistered version, only good for 30 days
|
||||
Set glob = Server.CreateObject("Chilkat_9_5_0.Global")
|
||||
success = glob.UnlockBundle("Anything for 30-day trial")
|
||||
If (success <> 1) Then
|
||||
Response.Write "<pre>" & Server.HTMLEncode(glob.LastErrorText) & "</pre>"
|
||||
Response.End
|
||||
End If
|
||||
|
||||
' ActiveX library http://www.chilkatsoft.com/
|
||||
Set pem = CreateObject("Chilkat_9_5_0.Pem")
|
||||
Set rsa = CreateObject("Chilkat_9_5_0.Rsa")
|
||||
|
||||
data = request("request")
|
||||
|
||||
password = ""
|
||||
success = pem.LoadPemFile("private-key.pem", password)
|
||||
If (success <> 1) Then
|
||||
Response.Write "<pre>" & Server.HTMLEncode(pem.LastErrorText) & "</pre>"
|
||||
Response.End
|
||||
End If
|
||||
|
||||
rsa.ImportPrivateKey(pem.GetPrivateKey(0).getXml())
|
||||
rsa.EncodingMode = "base64"
|
||||
sig = rsa.SignStringENC(data, "SHA-512") ' Use "SHA-1" for QZ Tray 2.0 and older
|
||||
|
||||
Response.ContentType = "text/plain"
|
||||
Response.Write Server.HTMLEncode(sig)
|
||||
|
||||
%>
|
||||
67
tray/assets/signing/sign-message.bouncycastle.cs
Normal file
67
tray/assets/signing/sign-message.bouncycastle.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
// #########################################################
|
||||
// # WARNING WARNING WARNING #
|
||||
// #########################################################
|
||||
// # #
|
||||
// # This file is intended for demonstration purposes #
|
||||
// # only. #
|
||||
// # #
|
||||
// # It is the SOLE responsibility of YOU, the programmer #
|
||||
// # to prevent against unauthorized access to any signing #
|
||||
// # functions. #
|
||||
// # #
|
||||
// # Organizations that do not protect against un- #
|
||||
// # authorized signing will be black-listed to prevent #
|
||||
// # software piracy. #
|
||||
// # #
|
||||
// # -QZ Industries, LLC #
|
||||
// # #
|
||||
// #########################################################
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.OpenSsl;
|
||||
using Org.BouncyCastle.Security;
|
||||
|
||||
|
||||
// Public method for signing the input string with the private key
|
||||
// ===============================================================
|
||||
|
||||
string privateKey = "private-key.pem"; // PKCS#8 PEM file
|
||||
|
||||
string SignMessage(string msg)
|
||||
{
|
||||
// Convert the input string to a byte array
|
||||
byte[] input = Encoding.ASCII.GetBytes(msg);
|
||||
|
||||
// Initialize the signer with the algorithm and the private key
|
||||
ISigner sig = SignerUtilities.GetSigner("SHA512withRSA");
|
||||
sig.Init(true, getPrivateKey());
|
||||
|
||||
// Generate signature and return it as a base64 string
|
||||
sig.BlockUpdate(input, 0, input.Length);
|
||||
return Convert.ToBase64String(sig.GenerateSignature());
|
||||
}
|
||||
|
||||
AsymmetricKeyParameter getPrivateKey()
|
||||
{
|
||||
using (var reader = System.IO.File.OpenText(privateKey))
|
||||
{
|
||||
var pem = new PemReader(reader).ReadObject();
|
||||
return pem as AsymmetricKeyParameter ?? (pem as AsymmetricCipherKeyPair).Private;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Public method for returning the certificate
|
||||
// ===========================================
|
||||
|
||||
string certificate = "digital-certificate.txt";
|
||||
|
||||
string GetCertificate()
|
||||
{
|
||||
using (var reader = System.IO.File.OpenText(certificate))
|
||||
{
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
73
tray/assets/signing/sign-message.cfm
Normal file
73
tray/assets/signing/sign-message.cfm
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* ColdFusion signing example
|
||||
* Echoes the signed message and exits
|
||||
*/
|
||||
|
||||
// #########################################################
|
||||
// # WARNING WARNING WARNING #
|
||||
// #########################################################
|
||||
// # #
|
||||
// # This file is intended for demonstration purposes #
|
||||
// # only. #
|
||||
// # #
|
||||
// # It is the SOLE responsibility of YOU, the programmer #
|
||||
// # to prevent against unauthorized access to any signing #
|
||||
// # functions. #
|
||||
// # #
|
||||
// # Organizations that do not protect against un- #
|
||||
// # authorized signing will be black-listed to prevent #
|
||||
// # software piracy. #
|
||||
// # #
|
||||
// # -QZ Industries, LLC #
|
||||
// # #
|
||||
// #########################################################
|
||||
|
||||
<cfset signature = sign("private-key.pem", url.request)>
|
||||
<cfcontent type="text/plain">
|
||||
<cfoutput>#signature#</cfoutput>
|
||||
<cfscript>
|
||||
|
||||
/**
|
||||
* Sign the given string
|
||||
* @keyPath Path to PEM formatted private key
|
||||
* @message message to be signed
|
||||
* @encoding I am the encoding used when returning the signature (base64 by default).
|
||||
* @output false
|
||||
*/
|
||||
public any function sign(required string keyPath, required string message, string algorithm = "SHA512withRSA", string encoding = "base64") {
|
||||
// Note: change algorithm to "SHA1withRSA" for QZ Tray 2.0 and older
|
||||
createObject("java", "java.security.Security")
|
||||
.addProvider(createObject("java", "org.bouncycastle.jce.provider.BouncyCastleProvider").init());
|
||||
privateKey = createPrivateKey(fileRead(expandPath(keyPath)));
|
||||
var signer = createObject("java", "java.security.Signature").getInstance(javaCast( "string", algorithm ));
|
||||
signer.initSign(privateKey);
|
||||
signer.update(charsetDecode(message, "utf-8"));
|
||||
var signedBytes = signer.sign();
|
||||
return encoding == "binary" ? signedBytes : binaryEncode(signedBytes, encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the private key using the provided pem formatted content.
|
||||
*
|
||||
* @contents PEM key contents
|
||||
* @output false
|
||||
*/
|
||||
private any function createPrivateKey(required string contents) {
|
||||
var pkcs8 = createObject("java", "java.security.spec.PKCS8EncodedKeySpec").init(
|
||||
binaryDecode(stripKeyDelimiters(contents), "base64")
|
||||
);
|
||||
|
||||
return createObject("java", "java.security.KeyFactory")
|
||||
.getInstance(javaCast( "string", "RSA" )).generatePrivate(pkcs8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip X509 cert delimiters
|
||||
*
|
||||
* @keyText PEM formatted key data
|
||||
* @output false
|
||||
*/
|
||||
private string function stripKeyDelimiters(required string keyText) {
|
||||
return trim(reReplace(keyText, "-----(BEGIN|END)[^\r\n]+", "", "all" ));
|
||||
}
|
||||
</cfscript>
|
||||
54
tray/assets/signing/sign-message.cls
Normal file
54
tray/assets/signing/sign-message.cls
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Salesforce APEX Signing Example
|
||||
* Returns the signed message to a wired controller
|
||||
*/
|
||||
|
||||
// #########################################################
|
||||
// # WARNING WARNING WARNING #
|
||||
// #########################################################
|
||||
// # #
|
||||
// # This file is intended for demonstration purposes #
|
||||
// # only. #
|
||||
// # #
|
||||
// # It is the SOLE responsibility of YOU, the programmer #
|
||||
// # to prevent against unauthorized access to any signing #
|
||||
// # functions. #
|
||||
// # #
|
||||
// # Organizations that do not protect against un- #
|
||||
// # authorized signing will be black-listed to prevent #
|
||||
// # software piracy. #
|
||||
// # #
|
||||
// # -QZ Industries, LLC #
|
||||
// # #
|
||||
// #########################################################
|
||||
|
||||
public with sharing class SignMessage {
|
||||
@AuraEnabled(cacheable = true)
|
||||
public static String signMessage(String toSign){
|
||||
String privateKeyBase64 = '<private-key.pem content without header or footer>';
|
||||
|
||||
Blob sig = Crypto.sign('RSA-SHA512',
|
||||
Blob.valueOf(toSign),
|
||||
EncodingUtil.base64Decode(privateKeyBase64));
|
||||
|
||||
return EncodingUtil.base64Encode(sig);
|
||||
}
|
||||
}
|
||||
|
||||
/** JavaScript - Adjust as needed
|
||||
|
||||
import signMessage from '@salesforce/apex/SignMessage.signMessage';
|
||||
@wire(signMessage)
|
||||
|
||||
qz.security.setSignatureAlgorithm("SHA512");
|
||||
qz.security.setSignaturePromise(function(toSign) {
|
||||
return function (resolve, reject) {
|
||||
try {
|
||||
resolve(signMessage({toSign : toSign}));
|
||||
} catch(err) {
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
**/
|
||||
53
tray/assets/signing/sign-message.core.cs
Normal file
53
tray/assets/signing/sign-message.core.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
// #########################################################
|
||||
// # WARNING WARNING WARNING #
|
||||
// #########################################################
|
||||
// # #
|
||||
// # This file is intended for demonstration purposes #
|
||||
// # only. #
|
||||
// # #
|
||||
// # It is the SOLE responsibility of YOU, the programmer #
|
||||
// # to prevent against unauthorized access to any signing #
|
||||
// # functions. #
|
||||
// # #
|
||||
// # Organizations that do not protect against un- #
|
||||
// # authorized signing will be black-listed to prevent #
|
||||
// # software piracy. #
|
||||
// # #
|
||||
// # -QZ Industries, LLC #
|
||||
// # #
|
||||
// #########################################################
|
||||
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Web.Services;
|
||||
|
||||
// To convert a .PEM PrivateKey:
|
||||
// openssl pkcs12 -export -inkey private-key.pem -in digital-certificate.txt -out private-key.pfx
|
||||
private static X509KeyStorageFlags STORAGE_FLAGS = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable;
|
||||
|
||||
public IActionResult Index(string request)
|
||||
{
|
||||
var KEY = "/path/to/private-key.pfx";
|
||||
var PASS = "";
|
||||
|
||||
try
|
||||
{
|
||||
byte[] data = new ASCIIEncoding().GetBytes(request);
|
||||
var cert = new X509Certificate2(KEY, PASS, STORAGE_FLAGS);
|
||||
RSA rsa = (RSA)cert.GetRSAPrivateKey();
|
||||
var signed = rsa.SignData(data, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
|
||||
string base64 = Convert.ToBase64String(signed);
|
||||
return Content(base64);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
if((STORAGE_FLAGS & X509KeyStorageFlags.MachineKeySet) == X509KeyStorageFlags.MachineKeySet)
|
||||
{
|
||||
// IISExpress may fail with "Invalid provider type specified"; remove MachineKeySet flag, try again
|
||||
STORAGE_FLAGS = STORAGE_FLAGS & ~X509KeyStorageFlags.MachineKeySet;
|
||||
return Index(request);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
80
tray/assets/signing/sign-message.cs
Normal file
80
tray/assets/signing/sign-message.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
// #########################################################
|
||||
// # WARNING WARNING WARNING #
|
||||
// #########################################################
|
||||
// # #
|
||||
// # This file is intended for demonstration purposes #
|
||||
// # only. #
|
||||
// # #
|
||||
// # It is the SOLE responsibility of YOU, the programmer #
|
||||
// # to prevent against unauthorized access to any signing #
|
||||
// # functions. #
|
||||
// # #
|
||||
// # Organizations that do not protect against un- #
|
||||
// # authorized signing will be black-listed to prevent #
|
||||
// # software piracy. #
|
||||
// # #
|
||||
// # -QZ Industries, LLC #
|
||||
// # #
|
||||
// #########################################################
|
||||
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Web.Services;
|
||||
|
||||
// To convert a .PEM PrivateKey:
|
||||
// openssl pkcs12 -export -inkey private-key.pem -in digital-certificate.txt -out private-key.pfx
|
||||
private static X509KeyStorageFlags STORAGE_FLAGS = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable;
|
||||
|
||||
/**
|
||||
* Note, this example is for .NET Forms/PageMethods
|
||||
* For MVC, change the following:
|
||||
*
|
||||
* public ActionResult SignMessage() {
|
||||
* string request = Request.QueryString["request"];
|
||||
* ...
|
||||
* return Content(base64, "text/plain");
|
||||
* ...
|
||||
* return SignMessage();
|
||||
*
|
||||
* ... and replace PageMethods calls with fetch("@Url.Content("./SignMessage/?request=")" + toSign
|
||||
*/
|
||||
[WebMethod]
|
||||
public static string SignMessage(string request)
|
||||
{
|
||||
//var WEBROOT_PATH = HttpContext.Current.Server.MapPath("/");
|
||||
//var CURRENT_PATH = HttpContext.Current.Server.MapPath("~");
|
||||
//var PARENT_PATH = System.IO.Directory.GetParent(WEBROOT).Parent.FullName;
|
||||
var KEY = "/path/to/private-key.pfx";
|
||||
var PASS = "";
|
||||
|
||||
try
|
||||
{
|
||||
var cert = new X509Certificate2(KEY, PASS, STORAGE_FLAGS);
|
||||
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PrivateKey; // PFX defaults to the weaker "SHA1"
|
||||
byte[] data = new ASCIIEncoding().GetBytes(request);
|
||||
RSACryptoServiceProvider cspStrong = new RSACryptoServiceProvider(); // 2.1 and higher: Make RSACryptoServiceProvider that can handle SHA256, SHA512
|
||||
cspStrong.ImportParameters(csp.ExportParameters(true)); // Copy to stronger RSACryptoServiceProvider
|
||||
byte[] hash = new SHA512CryptoServiceProvider().ComputeHash(data); // Use SHA1CryptoServiceProvider for QZ Tray 2.0 and older
|
||||
string base64 = Convert.ToBase64String(cspStrong.SignHash(hash, CryptoConfig.MapNameToOID("SHA512"))); // Use "SHA1" for QZ Tray 2.0 and older
|
||||
return base64;
|
||||
|
||||
/*
|
||||
* // Or per https://stackoverflow.com/a/50104158/3196753
|
||||
* var cert = new X509Certificate2(KEY, PASS, STORAGE_FLAGS);
|
||||
* var csp = cert.GetRSAPrivateKey();
|
||||
* byte[] data = new ASCIIEncoding().GetBytes(request);
|
||||
* return Convert.ToBase64String(csp.SignData(data, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1)); // Use "SHA1" for QZ Tray 2.0 and older
|
||||
*/
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
if((STORAGE_FLAGS & X509KeyStorageFlags.MachineKeySet) == X509KeyStorageFlags.MachineKeySet)
|
||||
{
|
||||
// IISExpress may fail with "Invalid provider type specified"; remove MachineKeySet flag, try again
|
||||
STORAGE_FLAGS = STORAGE_FLAGS & ~X509KeyStorageFlags.MachineKeySet;
|
||||
return SignMessage(request);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
99
tray/assets/signing/sign-message.go
Normal file
99
tray/assets/signing/sign-message.go
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Echos the signed message
|
||||
*/
|
||||
// #########################################################
|
||||
// # WARNING WARNING WARNING #
|
||||
// #########################################################
|
||||
// # #
|
||||
// # This file is intended for demonstration purposes #
|
||||
// # only. #
|
||||
// # #
|
||||
// # It is the SOLE responsibility of YOU, the programmer #
|
||||
// # to prevent against unauthorized access to any signing #
|
||||
// # functions. #
|
||||
// # #
|
||||
// # Organizations that do not protect against un- #
|
||||
// # authorized signing will be black-listed to prevent #
|
||||
// # software piracy. #
|
||||
// # #
|
||||
// # -QZ Industries, LLC #
|
||||
// # #
|
||||
// #########################################################
|
||||
/* Steps:
|
||||
* 1. Convert private key to golang compatible format:
|
||||
* openssl rsa -in private-key.pem -out private-key-updated.pem
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var privateKey = "C:\\path\\to\\private-key-updated.pem"
|
||||
var password = "S3cur3P@ssw0rd"
|
||||
var listenPort = ":8080"
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", handler)
|
||||
http.ListenAndServe(listenPort, nil)
|
||||
}
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
|
||||
rsaPrivateKey, err := decodeKey(privateKey)
|
||||
|
||||
if err != nil {
|
||||
displayError(w, "Error reading key", err)
|
||||
return
|
||||
}
|
||||
|
||||
data := r.URL.Query().Get("request")
|
||||
|
||||
if len(data) < 1 {
|
||||
displayError(w, "Request cannot be blank", err)
|
||||
return
|
||||
}
|
||||
|
||||
hash := sha1.Sum([]byte(data))
|
||||
rng := rand.Reader
|
||||
signature, err := rsa.SignPKCS1v15(rng, rsaPrivateKey, crypto.SHA512, hash[:]) // Use crypto.SHA1 for QZ Tray 2.0 and older
|
||||
if err != nil {
|
||||
displayError(w, "Error from signing: %s\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, base64.StdEncoding.EncodeToString(signature))
|
||||
}
|
||||
|
||||
func displayError(w http.ResponseWriter, msg string, err error) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprintf(w, "500 - Internal Server Error\n\n"+msg+"\n\nDetails:\n", err)
|
||||
}
|
||||
|
||||
func decodeKey(path string) (*rsa.PrivateKey, error) {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(b)
|
||||
if x509.IsEncryptedPEMBlock(block) {
|
||||
der, err := x509.DecryptPEMBlock(block, []byte(password))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x509.ParsePKCS1PrivateKey(der)
|
||||
}
|
||||
|
||||
return x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
}
|
||||
143
tray/assets/signing/sign-message.java
Normal file
143
tray/assets/signing/sign-message.java
Normal file
@@ -0,0 +1,143 @@
|
||||
// package foo.bar;
|
||||
|
||||
/*
|
||||
* Java signing example
|
||||
* Echoes the signed message and exits
|
||||
*/
|
||||
|
||||
// #########################################################
|
||||
// # WARNING WARNING WARNING #
|
||||
// #########################################################
|
||||
// # #
|
||||
// # This file is intended for demonstration purposes #
|
||||
// # only. #
|
||||
// # #
|
||||
// # It is the SOLE responsibility of YOU, the programmer #
|
||||
// # to prevent against unauthorized access to any signing #
|
||||
// # functions. #
|
||||
// # #
|
||||
// # Organizations that do not protect against un- #
|
||||
// # authorized signing will be black-listed to prevent #
|
||||
// # software piracy. #
|
||||
// # #
|
||||
// # -QZ Industries, LLC #
|
||||
// # #
|
||||
// #########################################################
|
||||
|
||||
import java.io.*;
|
||||
import java.security.*;
|
||||
import java.security.spec.*;
|
||||
import java.util.Base64;
|
||||
import java.util.logging.*;
|
||||
|
||||
/**
|
||||
* Utility for creating an RSA SHA1 signature based on a supplied PEM formatted private key
|
||||
*/
|
||||
public class MessageSigner {
|
||||
private static Logger logger = Logger.getLogger(MessageSigner.class.getName());
|
||||
private Signature sig;
|
||||
|
||||
/**
|
||||
* Standard Java usage example, safe to remove
|
||||
*/
|
||||
public static void main(String args[]) throws Exception {
|
||||
if (args.length < 2) {
|
||||
logger.severe("Usage:\nMessageSigner.class [path to private key] [data to sign]\n");
|
||||
System.exit(1);
|
||||
}
|
||||
byte[] key = MessageSigner.readFile(args[0]);
|
||||
String toSign = args[1];
|
||||
MessageSigner ms = new MessageSigner(key);
|
||||
String signature = ms.sign(toSign);
|
||||
|
||||
logger.log(Level.INFO, "Request: {0}", toSign);
|
||||
logger.log(Level.INFO, "Response: {0}", signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Servlet usage example, safe to remove
|
||||
*
|
||||
protected void doProcessRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
// Get request from URL
|
||||
String data = request.getParameter("request");
|
||||
|
||||
String signature = new MessageSigner("private-key.pem").sign(data);
|
||||
|
||||
// Send signed message back
|
||||
response.setContentType("text/plain");
|
||||
PrintWriter out = response.getWriter();
|
||||
out.write(signature);
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an RSA SHA1 signature object for signing
|
||||
* @param keyData
|
||||
* @throws Exception
|
||||
*/
|
||||
public MessageSigner(byte[] keyData) throws Exception {
|
||||
// Warning: PKCS#8 required. If PKCS#1 (RSA) key is provided convert using:
|
||||
// $ openssl pkcs8 -topk8 -inform PEM -outform PEM -in private-key.pem -out private-key-pkcs8.pem -nocrypt
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(parseKeyData(keyData));
|
||||
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||
PrivateKey key = kf.generatePrivate(keySpec);
|
||||
sig = Signature.getInstance("SHA512withRSA"); // Use "SHA1withRSA" for QZ Tray 2.0 and older
|
||||
sig.initSign(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs the specified data with the provided private key, returning the
|
||||
* RSA SHA1 signature
|
||||
* @param data Message to sign
|
||||
* @return Base64 encoded signature
|
||||
* @throws Exception
|
||||
*/
|
||||
public String sign(String data) throws Exception {
|
||||
sig.update(data.getBytes());
|
||||
return Base64.getEncoder().encodeToString(sig.sign());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the raw byte[] data from a file resource
|
||||
* @param path File path to read
|
||||
* @return the raw byte data from file
|
||||
* @throws IOException
|
||||
*/
|
||||
public static byte[] readFile(String path) throws IOException {
|
||||
InputStream is = MessageSigner.class.getResourceAsStream(path);
|
||||
if (is == null) {
|
||||
throw new IOException(String.format("Can't open resource \"%s\"", path));
|
||||
}
|
||||
DataInputStream dis = new DataInputStream(is);
|
||||
byte[] data = new byte[dis.available()];
|
||||
dis.readFully(data);
|
||||
dis.close();
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a base64 encoded private key by stripping the header and footer lines
|
||||
* @param keyData PEM file contents
|
||||
* @return Raw key byes
|
||||
* @throws IOException
|
||||
*/
|
||||
private static byte[] parseKeyData(byte[] keyData) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String[] lines = new String(keyData).split("[\r?\n]+");
|
||||
String[] skips = new String[]{"-----BEGIN", "-----END", ": "};
|
||||
for (String line : lines) {
|
||||
boolean skipLine = false;
|
||||
for (String skip : skips) {
|
||||
if (line.contains(skip)) {
|
||||
skipLine = true;
|
||||
}
|
||||
}
|
||||
if (!skipLine) {
|
||||
sb.append(line.trim());
|
||||
}
|
||||
}
|
||||
return Base64.getDecoder().decode(sb.toString());
|
||||
}
|
||||
|
||||
}
|
||||
89
tray/assets/signing/sign-message.js
Normal file
89
tray/assets/signing/sign-message.js
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* JavaScript client-side example using jsrsasign
|
||||
*/
|
||||
|
||||
// #########################################################
|
||||
// # WARNING WARNING WARNING #
|
||||
// #########################################################
|
||||
// # #
|
||||
// # This file is intended for demonstration purposes #
|
||||
// # only. #
|
||||
// # #
|
||||
// # It is the SOLE responsibility of YOU, the programmer #
|
||||
// # to prevent against unauthorized access to any signing #
|
||||
// # functions. #
|
||||
// # #
|
||||
// # Organizations that do not protect against un- #
|
||||
// # authorized signing will be black-listed to prevent #
|
||||
// # software piracy. #
|
||||
// # #
|
||||
// # -QZ Industries, LLC #
|
||||
// # #
|
||||
// #########################################################
|
||||
|
||||
/**
|
||||
* Depends:
|
||||
* - jsrsasign-latest-all-min.js
|
||||
* - qz-tray.js
|
||||
*
|
||||
* Steps:
|
||||
*
|
||||
* 1. Include jsrsasign 10.9.0 into your web page
|
||||
* <script src="https://cdnjs.cloudflare.com/ajax/libs/jsrsasign/11.1.0/jsrsasign-all-min.js"></script>
|
||||
*
|
||||
* 2. Update the privateKey below with contents from private-key.pem
|
||||
*
|
||||
* 3. Include this script into your web page
|
||||
* <script src="path/to/sign-message.js"></script>
|
||||
*
|
||||
* 4. Remove or comment out any other references to "setSignaturePromise"
|
||||
*
|
||||
* 5. IMPORTANT: Before deploying to production, copy "jsrsasign-all-min.js"
|
||||
* to the web server. Don't trust the CDN above to be available.
|
||||
*/
|
||||
var privateKey = "-----BEGIN PRIVATE KEY-----\n" +
|
||||
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC0z9FeMynsC8+u\n" +
|
||||
"dvX+LciZxnh5uRj4C9S6tNeeAlIGCfQYk0zUcNFCoCkTknNQd/YEiawDLNbxBqut\n" +
|
||||
"bMDZ1aarys1a0lYmUeVLCIqvzBkPJTSQsCopQQ9V8WuT252zzNzs68dVGNdCJd5J\n" +
|
||||
"NRQykpwexmnjPPv0mvj7i8XgG379TyW6P+WWV5okeUkXJ9eJS2ouDYdR2SM9BoVW\n" +
|
||||
"+FgxDu6BmXhozW5EfsnajFp7HL8kQClI0QOc79yuKl3492rH6bzFsFn2lfwWy9ic\n" +
|
||||
"7cP8EpCTeFp1tFaD+vxBhPZkeTQ1HKx6hQ5zeHIB5ySJJZ7af2W8r4eTGYzbdRW2\n" +
|
||||
"4DDHCPhZAgMBAAECggEATvofR3gtrY8TLe+ET3wMDS8l3HU/NMlmKA9pxvjYfw7F\n" +
|
||||
"8h4VBw4oOWPfzU7A07syWJUR72kckbcKMfw42G18GbnBrRQG0UIgV3/ppBQQNg9Y\n" +
|
||||
"QILSR6bFXhLPnIvm/GxVa58pOEBbdec4it2Gbvie/MpJ4hn3K8atTqKk0djwxQ+b\n" +
|
||||
"QNBWtVgTkyIqMpUTFDi5ECiVXaGWZ5AOVK2TzlLRNQ5Y7US8lmGxVWzt0GONjXSE\n" +
|
||||
"iO/eBk8A7wI3zknMx5o1uZa/hFCPQH33uKeuqU5rmphi3zS0BY7iGY9EoKu/o+BO\n" +
|
||||
"HPwLQJ3wCDA3O9APZ3gmmbHFPMFPr/mVGeAeGP/BAQKBgQDaPELRriUaanWrZpgT\n" +
|
||||
"VnKKrRSqPED3anAVgmDfzTQwuR/3oD506F3AMBzloAo3y9BXmDfe8qLn6kgdZQKy\n" +
|
||||
"SFNLz888at96oi+2mEKPpvssqiwE6F3OtEM6yv4DP9KJHaHmXaWv+/sjwjzpFNjs\n" +
|
||||
"wGThBxFvrTWRJqBYsM1XNJJ2EQKBgQDUGbTSwHKqRCYWhQ1GPCZKE98l5UtMKvUb\n" +
|
||||
"hyWWOXoyoeYbJEMfG1ynX4JeXIkl6YtBjYCqszv9PjHa1rowTZaAPJ0V70zyhTcF\n" +
|
||||
"t581ii9LpiejIGrELHvJnW87QmjjStkjwGIqgKLp7Qe6CDjHI9HP1NM0uav/IQLW\n" +
|
||||
"pB6wyEz1yQKBgQCuxPut+Ax2rzM05KB9PAnWzO1zt3U/rtm8IAF8uVVGf7r+EDJ0\n" +
|
||||
"ZXJO6zj5G8WTEYHz5E86GI4ltBW0lKQoKouqdu27sMrv5trXG/CSImOcTVubQot9\n" +
|
||||
"chc1CkOKTp5IeJajafO6j817wZ4N+0gNsbYYEBUCnm/7ojdfT5ficpOoQQKBgQDB\n" +
|
||||
"PgKPmaNfGeQR1Ht5qEfCakR/RF/ML79Nq15FdmytQPBjfjBhYQ6Tt+MRkgGqtxOX\n" +
|
||||
"UBMQc2iOnGHT3puYcrhScec1GufidhjhbqDxqMrag7HNYDWmMlk+IeA7/4+Mtp8L\n" +
|
||||
"gbZuvvCvbLQDfIYueaYpUuBzQ08/jZYGdVU4/+WOcQKBgAGUN0kIB6EM1K/iZ0TN\n" +
|
||||
"jlt8P5UEV3ZCyATWFiGZRhhE2WAh8gv1jx4J26pcUs1n8sd2a1h6ZuBSqsyIlNSp\n" +
|
||||
"xtKsm3bqQFDHRrPcsBX4nanrw9DzkpH1k/I3WMSdGqkDAR3DtL7yXTJXJo2Sbrp5\n" +
|
||||
"EjzSn7DcDE1tL2En/tSVXeUY\n" +
|
||||
"-----END PRIVATE KEY-----";
|
||||
|
||||
qz.security.setSignatureAlgorithm("SHA512"); // Since 2.1
|
||||
qz.security.setSignaturePromise(function(toSign) {
|
||||
return function(resolve, reject) {
|
||||
try {
|
||||
var pk = KEYUTIL.getKey(privateKey);
|
||||
var sig = new KJUR.crypto.Signature({"alg": "SHA512withRSA"}); // Use "SHA1withRSA" for QZ Tray 2.0 and older
|
||||
sig.init(pk);
|
||||
sig.updateString(toSign);
|
||||
var hex = sig.sign();
|
||||
console.log("DEBUG: \n\n" + stob64(hextorstr(hex)));
|
||||
resolve(stob64(hextorstr(hex)));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
reject(err);
|
||||
}
|
||||
};
|
||||
});
|
||||
48
tray/assets/signing/sign-message.jsl
Normal file
48
tray/assets/signing/sign-message.jsl
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// J# Signing Example
|
||||
// Echoes the signed message and exits
|
||||
//
|
||||
|
||||
// #########################################################
|
||||
// # WARNING WARNING WARNING #
|
||||
// #########################################################
|
||||
// # #
|
||||
// # This file is intended for demonstration purposes #
|
||||
// # only. #
|
||||
// # #
|
||||
// # It is the SOLE responsibility of YOU, the programmer #
|
||||
// # to prevent against unauthorized access to any signing #
|
||||
// # functions. #
|
||||
// # #
|
||||
// # Organizations that do not protect against un- #
|
||||
// # authorized signing will be black-listed to prevent #
|
||||
// # software piracy. #
|
||||
// # #
|
||||
// # -QZ Industries, LLC #
|
||||
// # #
|
||||
// #########################################################
|
||||
|
||||
module sample
|
||||
|
||||
open System
|
||||
open System.Security.Cryptography
|
||||
open System.Security.Cryptography.X509Certificates
|
||||
open System.IO
|
||||
open System.Text
|
||||
|
||||
let request = "test data"
|
||||
|
||||
// How to associate a private key with the X509Certificate2 class in .net
|
||||
// openssl pkcs12 -export -in private-key.pem -inkey digital-certificate.txt -out private-key.pfx
|
||||
let cert = new X509Certificate2("private-key.pfx")
|
||||
|
||||
let sha1 = new SHA512CryptoServiceProvider() // Use "SHA1CryptoServiceProvider" for QZ Tray 2.0 and older
|
||||
|
||||
let csp = cert.PrivateKey :?> RSACryptoServiceProvider
|
||||
let encoder = new ASCIIEncoding()
|
||||
|
||||
let data = encoder.GetBytes(request)
|
||||
let binaryData = csp.SignData(data, sha1)
|
||||
let output = Convert.ToBase64String(binaryData)
|
||||
|
||||
Console.WriteLine output
|
||||
120
tray/assets/signing/sign-message.jsp
Normal file
120
tray/assets/signing/sign-message.jsp
Normal file
@@ -0,0 +1,120 @@
|
||||
<%
|
||||
/*
|
||||
* JSP signing example
|
||||
* Echoes the signed message and exits
|
||||
*/
|
||||
|
||||
// #########################################################
|
||||
// # WARNING WARNING WARNING #
|
||||
// #########################################################
|
||||
// # #
|
||||
// # This file is intended for demonstration purposes #
|
||||
// # only. #
|
||||
// # #
|
||||
// # It is the SOLE responsibility of YOU, the programmer #
|
||||
// # to prevent against unauthorized access to any signing #
|
||||
// # functions. #
|
||||
// # #
|
||||
// # Organizations that do not protect against un- #
|
||||
// # authorized signing will be black-listed to prevent #
|
||||
// # software piracy. #
|
||||
// # #
|
||||
// # -QZ Industries, LLC #
|
||||
// # #
|
||||
// #########################################################
|
||||
%>
|
||||
|
||||
<%@ page import="java.io.*" %>
|
||||
<%@ page import="java.util.*" %>
|
||||
<%@ page import="java.lang.*" %>
|
||||
<%@ page import="java.security.*" %>
|
||||
<%@ page import="java.security.spec.*" %>
|
||||
<%@ page import="java.util.logging.*" %>
|
||||
<%@ page import="javax.xml.bind.DatatypeConverter" %>
|
||||
<%@ page trimDirectiveWhitespaces="true" %>
|
||||
<%@ page language="java" contentType="text/plain charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
|
||||
<%= getSignature(request.getParameter("request")) %>
|
||||
|
||||
|
||||
<%!
|
||||
/**
|
||||
* Creates a signature using the provided private key and the provided (String) data inside Object o.
|
||||
*/
|
||||
private String getSignature(Object o) {
|
||||
// Private key path if placed in CATALINA_HOME/private/ **AND** if JSP is
|
||||
// placed in CATALINA_HOME/webapps/examples/. Adjust as needed.
|
||||
String keyPath = "../../private/private-key.pem";
|
||||
|
||||
// Prepend servlet context path
|
||||
keyPath = getServletContext().getRealPath("/") + keyPath;
|
||||
String req = o == null ? "" : (String)o;
|
||||
|
||||
try {
|
||||
byte[] keyData = cleanseKeyData(readData(keyPath));
|
||||
// Warning: PKCS#8 required. If PKCS#1 (RSA) key is provided convert using:
|
||||
// $ openssl pkcs8 -topk8 -inform PEM -outform PEM -in private-key.pem -out private-key-pkcs8.pem -nocrypt
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyData);
|
||||
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||
PrivateKey key = kf.generatePrivate(keySpec);
|
||||
Signature sig = Signature.getInstance("SHA512withRSA"); // Use "SHA1withRSA" for QZ Tray 2.0 and older
|
||||
sig.initSign(key);
|
||||
sig.update(req.getBytes());
|
||||
String output = DatatypeConverter.printBase64Binary(sig.sign());
|
||||
return output;
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
return "Something went wrong while signing the message.\n" +
|
||||
"Please check server console for sign-message.jsp";
|
||||
}
|
||||
}
|
||||
%>
|
||||
|
||||
<%!
|
||||
/**
|
||||
* Reads the raw byte[] data from a file resource
|
||||
* @param resourcePath
|
||||
* @return the raw byte data from a resource file
|
||||
* @throws IOException
|
||||
*/
|
||||
public byte[] readData(String resourcePath) throws IOException {
|
||||
FileInputStream is = new FileInputStream(resourcePath);
|
||||
|
||||
//InputStream is = getServletContext().getResourceAsStream(resourcePath);
|
||||
if (is == null) {
|
||||
throw new IOException(String.format("Can't open resource \"%s\"", resourcePath));
|
||||
}
|
||||
DataInputStream dis = new DataInputStream(is);
|
||||
byte[] data = new byte[dis.available()];
|
||||
dis.readFully(data);
|
||||
dis.close();
|
||||
return data;
|
||||
}
|
||||
%>
|
||||
|
||||
<%!
|
||||
/**
|
||||
* Parses an X509 PEM formatted base64 encoded private key, returns the decoded
|
||||
* private key byte data
|
||||
* @param keyData PEM file contents, a X509 base64 encoded private key
|
||||
* @return Private key data
|
||||
* @throws IOException
|
||||
*/
|
||||
private byte[] cleanseKeyData(byte[] keyData) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String[] lines = new String(keyData).split("\n");
|
||||
String[] skips = new String[]{"-----BEGIN", "-----END", ": "};
|
||||
for (String line : lines) {
|
||||
boolean skipLine = false;
|
||||
for (String skip : skips) {
|
||||
if (line.contains(skip)) {
|
||||
skipLine = true;
|
||||
}
|
||||
}
|
||||
if (!skipLine) {
|
||||
sb.append(line.trim());
|
||||
}
|
||||
}
|
||||
return DatatypeConverter.parseBase64Binary(sb.toString());
|
||||
}
|
||||
%>
|
||||
43
tray/assets/signing/sign-message.node.js
Normal file
43
tray/assets/signing/sign-message.node.js
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Node.js signing example
|
||||
* Echoes the signed message and exits
|
||||
*/
|
||||
|
||||
// #########################################################
|
||||
// # WARNING WARNING WARNING #
|
||||
// #########################################################
|
||||
// # #
|
||||
// # This file is intended for demonstration purposes #
|
||||
// # only. #
|
||||
// # #
|
||||
// # It is the SOLE responsibility of YOU, the programmer #
|
||||
// # to prevent against unauthorized access to any signing #
|
||||
// # functions. #
|
||||
// # #
|
||||
// # Organizations that do not protect against un- #
|
||||
// # authorized signing will be black-listed to prevent #
|
||||
// # software piracy. #
|
||||
// # #
|
||||
// # -QZ Industries, LLC #
|
||||
// # #
|
||||
// #########################################################
|
||||
|
||||
var key = "private-key.pem";
|
||||
//var pass = "S3cur3P@ssw0rd";
|
||||
|
||||
app.get('/sign', function(req, res) {
|
||||
var crypto = require('crypto');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var toSign = req.query.requestToSign;
|
||||
|
||||
fs.readFile(path.join(__dirname, '\\' + key), 'utf-8', function(err, privateKey) {
|
||||
var sign = crypto.createSign('SHA512'); // Use "SHA1" for QZ Tray 2.0 and older
|
||||
|
||||
sign.update(toSign);
|
||||
var signature = sign.sign({ key: privateKey/*, passphrase: pass */ }, 'base64');
|
||||
|
||||
res.set('Content-Type', 'text/plain');
|
||||
res.send(signature);
|
||||
});
|
||||
});
|
||||
43
tray/assets/signing/sign-message.odoo.py
Normal file
43
tray/assets/signing/sign-message.odoo.py
Normal file
@@ -0,0 +1,43 @@
|
||||
#
|
||||
# Python Odoo example for controller.py
|
||||
# Echoes the signed message and exits
|
||||
#
|
||||
|
||||
#########################################################
|
||||
# WARNING WARNING WARNING #
|
||||
#########################################################
|
||||
# #
|
||||
# This file is intended for demonstration purposes #
|
||||
# only. #
|
||||
# #
|
||||
# It is the SOLE responsibility of YOU, the programmer #
|
||||
# to prevent against unauthorized access to any signing #
|
||||
# functions. #
|
||||
# #
|
||||
# Organizations that do not protect against un- #
|
||||
# authorized signing will be black-listed to prevent #
|
||||
# software piracy. #
|
||||
# #
|
||||
# -QZ Industries, LLC #
|
||||
# #
|
||||
#########################################################
|
||||
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
from OpenSSL import crypto
|
||||
import base64
|
||||
|
||||
|
||||
class SignMessage(http.Controller):
|
||||
|
||||
@http.route('/sign-message/', auth='public')
|
||||
def index(self, **kwargs):
|
||||
mypass = None
|
||||
key_file = open('path/to/private-key.pem', 'r')
|
||||
key = key_file.read()
|
||||
key_file.close()
|
||||
password = None
|
||||
pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, key, password)
|
||||
sign = crypto.sign(pkey, kwargs.get('request', ''), 'sha512') # Use 'sha1' for QZ Tray 2.0 and older
|
||||
data_base64 = base64.b64encode(sign)
|
||||
return request.make_response(data_base64, [('Content-Type', 'text/plain')])
|
||||
55
tray/assets/signing/sign-message.php
Normal file
55
tray/assets/signing/sign-message.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/*
|
||||
* Echoes the signed message and exits
|
||||
*/
|
||||
|
||||
// #########################################################
|
||||
// # WARNING WARNING WARNING #
|
||||
// #########################################################
|
||||
// # #
|
||||
// # This file is intended for demonstration purposes #
|
||||
// # only. #
|
||||
// # #
|
||||
// # It is the SOLE responsibility of YOU, the programmer #
|
||||
// # to prevent against unauthorized access to any signing #
|
||||
// # functions. #
|
||||
// # #
|
||||
// # Organizations that do not protect against un- #
|
||||
// # authorized signing will be black-listed to prevent #
|
||||
// # software piracy. #
|
||||
// # #
|
||||
// # -QZ Industries, LLC #
|
||||
// # #
|
||||
// #########################################################
|
||||
|
||||
// Sample key. Replace with one used for CSR generation
|
||||
$KEY = 'private-key.pem';
|
||||
//$PASS = 'S3cur3P@ssw0rd';
|
||||
|
||||
$req = $_GET['request'];
|
||||
$privateKey = openssl_get_privatekey(file_get_contents($KEY) /*, $PASS */);
|
||||
|
||||
$signature = null;
|
||||
openssl_sign($req, $signature, $privateKey, "sha512"); // Use "sha1" for QZ Tray 2.0 and older
|
||||
|
||||
/*
|
||||
// Or alternately, via phpseclib
|
||||
include('Crypt/RSA.php');
|
||||
$rsa = new Crypt_RSA();
|
||||
$rsa.setHash('sha512'); // Use 'sha1' for QZ Tray 2.0 and older
|
||||
$rsa->loadKey(file_get_contents($KEY));
|
||||
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
|
||||
$signature = $rsa->sign($req);
|
||||
*/
|
||||
|
||||
if ($signature) {
|
||||
header("Content-type: text/plain");
|
||||
echo base64_encode($signature);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
echo '<h1>Error signing message</h1>';
|
||||
http_response_code(500);
|
||||
exit(1);
|
||||
|
||||
?>
|
||||
52
tray/assets/signing/sign-message.pl
Normal file
52
tray/assets/signing/sign-message.pl
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# Echoes the signed message and exits
|
||||
# usage: ./sign-message.pl "test"
|
||||
#
|
||||
#########################################################
|
||||
# WARNING WARNING WARNING #
|
||||
#########################################################
|
||||
# #
|
||||
# This file is intended for demonstration purposes #
|
||||
# only. #
|
||||
# #
|
||||
# It is the SOLE responsibility of YOU, the programmer #
|
||||
# to prevent against unauthorized access to any signing #
|
||||
# functions. #
|
||||
# #
|
||||
# Organizations that do not protect against un- #
|
||||
# authorized signing will be black-listed to prevent #
|
||||
# software piracy. #
|
||||
# #
|
||||
# -QZ Industries, LLC #
|
||||
# #
|
||||
#########################################################
|
||||
|
||||
# RSA Crypto libs provided by:
|
||||
# Debian: libcrypt-openssl-rsa-perl
|
||||
# RedHat: perl-Crypt-OpenSSL-RSA
|
||||
use Crypt::OpenSSL::RSA;
|
||||
use MIME::Base64 qw(encode_base64);
|
||||
|
||||
# Get first argument passed to script
|
||||
my $request = $ARGV[0];
|
||||
|
||||
# Path to the private key
|
||||
my $pem_file = "private-key.pem";
|
||||
|
||||
# Read private key
|
||||
my $private_key = do {
|
||||
local $/ = undef;
|
||||
open my $fh, "<", $pem_file
|
||||
or die "could not open $file: $!";
|
||||
<$fh>;
|
||||
};
|
||||
|
||||
# Load private key
|
||||
my $rsa = Crypt::OpenSSL::RSA->new_private_key($private_key);
|
||||
|
||||
# Create signature
|
||||
$rsa->use_sha512_hash(); # use_sha1_hash for QZ Tray 2.0 and older
|
||||
my $sig = encode_base64($rsa->sign($request));
|
||||
|
||||
print $sig;
|
||||
39
tray/assets/signing/sign-message.py
Normal file
39
tray/assets/signing/sign-message.py
Normal file
@@ -0,0 +1,39 @@
|
||||
#
|
||||
# Python Django example for views.py
|
||||
# Echoes the signed message and exits
|
||||
#
|
||||
|
||||
#########################################################
|
||||
# WARNING WARNING WARNING #
|
||||
#########################################################
|
||||
# #
|
||||
# This file is intended for demonstration purposes #
|
||||
# only. #
|
||||
# #
|
||||
# It is the SOLE responsibility of YOU, the programmer #
|
||||
# to prevent against unauthorized access to any signing #
|
||||
# functions. #
|
||||
# #
|
||||
# Organizations that do not protect against un- #
|
||||
# authorized signing will be black-listed to prevent #
|
||||
# software piracy. #
|
||||
# #
|
||||
# -QZ Industries, LLC #
|
||||
# #
|
||||
#########################################################
|
||||
|
||||
import base64
|
||||
import os
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
from django.http import HttpResponse, HttpResponseBadRequest
|
||||
|
||||
def get(self, request, message):
|
||||
# Load signature
|
||||
key = serialization.load_pem_private_key(open("private-key.pem","rb").read(), None, backend=default_backend())
|
||||
# Create the signature
|
||||
signature = key.sign(message.encode('utf-8'), padding.PKCS1v15(), hashes.SHA512()) # Use hashes.SHA1() for QZ Tray 2.0 and older
|
||||
# Echo the signature
|
||||
return HttpResponse(base64.b64encode(signature), content_type="text/plain")
|
||||
|
||||
36
tray/assets/signing/sign-message.rb
Normal file
36
tray/assets/signing/sign-message.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env ruby
|
||||
#
|
||||
# Echoes the signed message and exits
|
||||
# usage: ./sign-message.rb "request=test"
|
||||
#
|
||||
#########################################################
|
||||
# WARNING WARNING WARNING #
|
||||
#########################################################
|
||||
# #
|
||||
# This file is intended for demonstration purposes #
|
||||
# only. #
|
||||
# #
|
||||
# It is the SOLE responsibility of YOU, the programmer #
|
||||
# to prevent against unauthorized access to any signing #
|
||||
# functions. #
|
||||
# #
|
||||
# Organizations that do not protect against un- #
|
||||
# authorized signing will be black-listed to prevent #
|
||||
# software piracy. #
|
||||
# #
|
||||
# -QZ Industries, LLC #
|
||||
# #
|
||||
#########################################################
|
||||
|
||||
# Typical rails controller
|
||||
class PrintingController < ActionController::Base
|
||||
def sign
|
||||
digest = OpenSSL::Digest.new('sha512') # Use 'sha1' for QZ Tray 2.0 and older
|
||||
pkey = OpenSSL::PKey::read(File.read(Rails.root.join('lib', 'certs', 'private-key.pem')))
|
||||
|
||||
signed = pkey.sign(digest, params[:request])
|
||||
encoded = Base64.encode64(signed)
|
||||
|
||||
render text: encoded
|
||||
end
|
||||
end
|
||||
86
tray/assets/signing/sign-message.ts
Normal file
86
tray/assets/signing/sign-message.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
|
||||
/*
|
||||
* AngularJS example using jsrsasign (client) or fetch (server)
|
||||
*/
|
||||
|
||||
// #########################################################
|
||||
// # WARNING WARNING WARNING #
|
||||
// #########################################################
|
||||
// # #
|
||||
// # This file is intended for demonstration purposes #
|
||||
// # only. #
|
||||
// # #
|
||||
// # It is the SOLE responsibility of YOU, the programmer #
|
||||
// # to prevent against unauthorized access to any signing #
|
||||
// # functions. #
|
||||
// # #
|
||||
// # Organizations that do not protect against un- #
|
||||
// # authorized signing will be black-listed to prevent #
|
||||
// # software piracy. #
|
||||
// # #
|
||||
// # -QZ Industries, LLC #
|
||||
// # #
|
||||
// #########################################################
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import * as qz from 'qz-tray';
|
||||
import { KJUR, KEYUTIL, stob64, hextorstr } from 'jsrsasign';
|
||||
|
||||
qz.security.setCertificatePromise((resolve, reject) => {
|
||||
fetch("assets/digital-certificate.txt", {cache: 'no-store', headers: {'Content-Type': 'text/plain'}})
|
||||
.then(data => resolve(data.text()));
|
||||
});
|
||||
|
||||
/*
|
||||
* Client-side using jsrsasign
|
||||
*/
|
||||
qz.security.setSignatureAlgorithm("SHA512"); // Since 2.1
|
||||
qz.security.setSignaturePromise(hash => {
|
||||
return (resolve, reject) => {
|
||||
fetch("assets/private-key.pem", {cache: 'no-store', headers: {'Content-Type': 'text/plain'}})
|
||||
.then(wrapped => wrapped.text())
|
||||
.then(data => {
|
||||
var pk = KEYUTIL.getKey(data);
|
||||
var sig = new KJUR.crypto.Signature({"alg": "SHA512withRSA"}); // Use "SHA1withRSA" for QZ Tray 2.0 and older
|
||||
sig.init(pk);
|
||||
sig.updateString(hash);
|
||||
var hex = sig.sign();
|
||||
console.log("DEBUG: \n\n" + stob64(hextorstr(hex)));
|
||||
resolve(stob64(hextorstr(hex)));
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* Preferred, from a secure controller
|
||||
*
|
||||
qz.security.setSignaturePromise(hash => {
|
||||
return (resolve, reject) => {
|
||||
fetch("/path/to/controller?request=" + hash, {cache: 'no-store', headers: {'Content-Type': 'text/plain'}})
|
||||
.then(wrapped => wrapped.text())
|
||||
.then(data => resolve(data))
|
||||
.catch(err => console.error(err));
|
||||
});
|
||||
};
|
||||
});
|
||||
*/
|
||||
|
||||
qz.api.setSha256Type(data => sha256(data));
|
||||
qz.api.setPromiseType(resolver => new Promise(resolver));
|
||||
|
||||
qz.websocket.connect()
|
||||
.then(qz.printers.getDefault)
|
||||
.then(printer => console.log("The default printer is: " + printer))
|
||||
.then(qz.websocket.disconnect)
|
||||
.catch(err => console.error(err));
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'QZ Tray AngularJS Signing';
|
||||
}
|
||||
43
tray/assets/signing/sign-message.vb
Normal file
43
tray/assets/signing/sign-message.vb
Normal file
@@ -0,0 +1,43 @@
|
||||
'
|
||||
' Echoes the signed message and exits
|
||||
'
|
||||
Public Sub SignMessage(message As String)
|
||||
|
||||
'**********************************************************
|
||||
'* WARNING WARNING WARNING *
|
||||
'**********************************************************
|
||||
'* *
|
||||
'* This file is intended for demonstration purposes only. *
|
||||
'* only. *
|
||||
'* *
|
||||
'* It is the SOLE responsibility of YOU, the programmer *
|
||||
'* to prevent against unauthorized access to any signing *
|
||||
'* functions. *
|
||||
'* *
|
||||
'* Organizations that do not protect against un- *
|
||||
'* authorized signing will be black-listed to prevent *
|
||||
'* software piracy. *
|
||||
'* *
|
||||
'* -QZ Industries, LLC *
|
||||
'* *
|
||||
'**********************************************************
|
||||
|
||||
' Sample key. Replace with one used for CSR generation
|
||||
' How to associate a private key with the X509Certificate2 class in .net
|
||||
' openssl pkcs12 -export -inkey private-key.pem -in digital-certificate.txt -out private-key.pfx
|
||||
|
||||
Dim KEY = "private-key.pfx"
|
||||
|
||||
Dim cert = New X509Certificate2(KEY, X509KeyStorageFlags.MachineKeySet Or X509KeyStorageFlags.PersistKeySet Or X509KeyStorageFlags.Exportable)
|
||||
Dim csp As RSACryptoServiceProvider = CType(cert.PrivateKey,RSACryptoServiceProvider)
|
||||
|
||||
Dim cspStrong as RSACryptoServiceProvider() = New RSACryptoServiceProvider() ' 2.1 and higher: Make RSACryptoServiceProvider that can handle SHA256, SHA512
|
||||
cspStrong.ImportParameters(csp.ExportParameters(true)) ' Copy to stronger RSACryptoServiceProvider
|
||||
|
||||
Dim data As Byte() = New ASCIIEncoding().GetBytes(message)
|
||||
Dim hash As Byte() = New SHA512Managed().ComputeHash(data) ' Use SHA1Managed() for QZ Tray 2.0 and older
|
||||
|
||||
Response.ContentType = "text/plain"
|
||||
Response.Write(Convert.ToBase64String(cspStrong.SignHash(hash, CryptoConfig.MapNameToOID("SHA512")))) ' Use "SHA1" for QZ Tray 2.0 and older
|
||||
Environment.[Exit](0)
|
||||
End Sub
|
||||
88
tray/assets/signing/sign-message.vue.js
Normal file
88
tray/assets/signing/sign-message.vue.js
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* JavaScript client-side example using jwa
|
||||
*/
|
||||
|
||||
// #########################################################
|
||||
// # WARNING WARNING WARNING #
|
||||
// #########################################################
|
||||
// # #
|
||||
// # This file is intended for demonstration purposes #
|
||||
// # only. #
|
||||
// # #
|
||||
// # It is the SOLE responsibility of YOU, the programmer #
|
||||
// # to prevent against unauthorized access to any signing #
|
||||
// # functions. #
|
||||
// # #
|
||||
// # Organizations that do not protect against un- #
|
||||
// # authorized signing will be black-listed to prevent #
|
||||
// # software piracy. #
|
||||
// # #
|
||||
// # -QZ Industries, LLC #
|
||||
// # #
|
||||
// #########################################################
|
||||
|
||||
import Vue from "vue";
|
||||
|
||||
import qz from "qz-tray";
|
||||
import jwa from "jwa";
|
||||
|
||||
const vue = new Vue({
|
||||
el: "#app",
|
||||
data: {
|
||||
message: "QZ Tray Vue.js Demo"
|
||||
},
|
||||
template: "<div>{{ message }}</div>"
|
||||
}).$mount();
|
||||
|
||||
const rsa512 = jwa("RS512");
|
||||
|
||||
const privateKey = getPrivateKey();
|
||||
qz.security.setSignatureAlgorithm("SHA512"); // Since 2.1
|
||||
qz.security.setSignaturePromise(function (toSign) {
|
||||
return function (resolve, reject) {
|
||||
try {
|
||||
const hexUrl = rsa512.sign(toSign, privateKey);
|
||||
// Crude base64URL to base64 conversion
|
||||
const hex = hexUrl.replace(/_/g, "/").replace(/-/g, "+");
|
||||
resolve(hex);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const certificate = getCertificate();
|
||||
qz.security.setCertificatePromise((resolve, reject) => {
|
||||
resolve(certificate);
|
||||
});
|
||||
|
||||
function getPrivateKey() {
|
||||
// TODO: Switch to fetch()/AJAX/etc
|
||||
return (
|
||||
"-----BEGIN PRIVATE KEY-----\n" +
|
||||
"..." +
|
||||
"-----END PRIVATE KEY-----"
|
||||
);
|
||||
}
|
||||
|
||||
function getCertificate() {
|
||||
// TODO: Switch to fetch()/AJAX/etc
|
||||
return (
|
||||
"-----BEGIN CERTIFICATE-----\n" +
|
||||
"..." +
|
||||
"-----END CERTIFICATE-----"
|
||||
);
|
||||
}
|
||||
|
||||
qz.websocket
|
||||
.connect()
|
||||
.then(() => {
|
||||
vue.message = "Looking for printers...";
|
||||
return qz.printers.find();
|
||||
})
|
||||
.then((printers) => {
|
||||
vue.message = "Found printers: " + printers;
|
||||
})
|
||||
.catch((err) => {
|
||||
vue.message = err;
|
||||
});
|
||||
50
tray/assets/signing/sign_message.erl
Normal file
50
tray/assets/signing/sign_message.erl
Normal file
@@ -0,0 +1,50 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% #########################################################
|
||||
%%% # WARNING WARNING WARNING #
|
||||
%%% #########################################################
|
||||
%%% # #
|
||||
%%% # This file is intended for demonstration purposes #
|
||||
%%% # only. #
|
||||
%%% # #
|
||||
%%% # It is the SOLE responsibility of YOU, the programmer #
|
||||
%%% # to prevent against unauthorized access to any signing #
|
||||
%%% # functions. #
|
||||
%%% # #
|
||||
%%% # Organizations that do not protect against un- #
|
||||
%%% # authorized signing will be black-listed to prevent #
|
||||
%%% # software piracy. #
|
||||
%%% # #
|
||||
%%% # -QZ Industries, LLC #
|
||||
%%% # #
|
||||
%%% #########################################################
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(sign_message).
|
||||
-export([sign/2]).
|
||||
|
||||
%%%
|
||||
%%% Usage:
|
||||
%%% -import(sign_message, [sign/2]).
|
||||
%%% [...]
|
||||
%%% sign_message:sign(GetRequest, "path/to/private-key.rsa").
|
||||
%%% * Where GetRequest is the the "foo" portion of "?request=foo"
|
||||
%%% * Web framework must echo the base64 encoded signature in plain text
|
||||
%%% * Browser must use ajax technique to fetch base64 signature
|
||||
%%% * See also qz.api.setSignaturePromise(...)
|
||||
%%%
|
||||
%%% Important:
|
||||
%%% * Private key MUST be converted to newer RSA format using the following command:
|
||||
%%% openssl rsa -in "private-key.pem" -out "private-key.rsa"
|
||||
%%%
|
||||
%%% Watch for:
|
||||
%%% badmatch,{error,enoent} key cannot be read; check for valid path
|
||||
%%% function_clause,[{public_key,sign,...}] key must be converted to newer RSA format
|
||||
%%%
|
||||
%%%
|
||||
|
||||
sign(Message, KeyPath) ->
|
||||
{ok, Data} = file:read_file(KeyPath),
|
||||
[KeyEntry] = public_key:pem_decode(Data),
|
||||
PrivateKey = public_key:pem_entry_decode(KeyEntry),
|
||||
Signature = public_key:sign(list_to_binary(Message), sha512, PrivateKey), % Use sha1 for QZ Tray 2.0 and older
|
||||
Base64 = base64:encode(Signature),
|
||||
io:fwrite(Base64).
|
||||
51
tray/assets/zpl_sample.txt
Normal file
51
tray/assets/zpl_sample.txt
Normal file
@@ -0,0 +1,51 @@
|
||||
^XA^CF,0,0,0
|
||||
^FX Please avoid using PR (print rate) or MD (media darkness) commands;
|
||||
They may change or be outside the range of current printer settings for subsequent print jobs and result in bad prints -lite1979^FS
|
||||
^PW812^PON^CI13
|
||||
^FO0,147^GB800,4,4^FS
|
||||
^FO0,401^GB800,4,4^FS
|
||||
^FO0,736^GB800,4,4^FS
|
||||
^FO35,12^AdN,0,0^FWN^FH^FDFrom:^FS
|
||||
^FO35,31^AdN,0,0^FWN^FH^FDAlex^FS
|
||||
^FO35,51^AdN,0,0^FWN^FH^FDFlagship^FS
|
||||
^FO35,71^AdN,0,0^FWN^FH^FD79 Spike St.^FS
|
||||
^FO35,92^AdN,0,0^FWN^FH^FD^FS
|
||||
^FO35,112^AdN,0,0^FWN^FH^FDNorcross, GA 30071^FS
|
||||
^FO35,132^AdN,0,0^FWN^FH^FD(123) 456-7890^FS
|
||||
^FO490,31^AdN,0,0^FWN^FH^FDShip Date: 31DEC07^FS
|
||||
^FO490,51^AdN,0,0^FWN^FH^FDActWgt: 10.5 LB^FS
|
||||
^FO490,72^AdN,0,0^FWN^FH^FDSystem#: 1195167/WSXI0100^FS
|
||||
^FO490,91^AdN,0,0^FWN^FH^FDAccount: S *********^FS
|
||||
^FO43,158^A0N,25,27^FWN^FH^FDSteve Jobs^FS
|
||||
^FO615,156^AdN,0,0^FWN^FH^FD(123) 456-7890^FS
|
||||
^FO43,193^A0N,25,27^FWN^FH^FDApple Inc.^FS
|
||||
^FO43,228^A0N,25,27^FWN^FH^FD1 Infinite Loop.^FS
|
||||
^FO43,263^A0N,25,27^FWN^FH^FD^FS
|
||||
^FO43,296^A0N,30,30^FWN^FH^FDCupertino, CA 95014^FS
|
||||
^FO530,296^A0N,35,45^FWN^FH^FD(US)^FS
|
||||
^FO725,216^AdN,0,0^FWN^FH^FDGround^FS
|
||||
^FO670,238^GB105,10,10^FS
|
||||
^FO670,248^GB10,112,10^FS
|
||||
^FO765,248^GB10,112,10^FS
|
||||
^FO670,360^GB105,10,10^FS
|
||||
^FO650,173^A0N,50,55^FWN^FH^FDFedEx^FS
|
||||
^FO690,256^A0N,130,130^FWN^FH^FDG^FS
|
||||
^FO476,3^GB4,145,4^FS
|
||||
^FO10,358^A0N,15,15^FWN^FH^FDRef:^FS
|
||||
^FO406,388^A0N,15,15^FWN^FH^FDDept: Work^FS
|
||||
^FO10,373^A0N,15,15^FWN^FH^FDInv:^FS
|
||||
^FO10,388^A0N,15,15^FWN^FH^FDPO:^FS
|
||||
^FO625,495^A0N,20,35^FWN^FH^FD1^FS
|
||||
^FO640,535^A0N,20,35^FWN^FH^FDof^FS
|
||||
^FO625,575^A0N,20,35^FWN^FH^FD1^FS
|
||||
^FO80,771^BY4,2^BCN,290,N,N,N,N^FWN^FD>;>89612019333075610004019^FS
|
||||
^FO135,1083^A0N,25,27^FWN^FH^FD(9612019) 3330756 10004019^FS
|
||||
^FO783,288^A0N,15,15^FWB^FH^FDCLS090607/23/23^FS
|
||||
^FO10,162^A0N,20,18^FWN^FH^FDTO^FS
|
||||
^FO25,1108^A0N,50,55^FWN^FH^FDGND^FS
|
||||
^FO25,1150^A0N,35,45^FWN^FH^FDPrepaid^FS
|
||||
^FO300,1115^A0N,35,45^FWN^FH^FD^FS
|
||||
^FO300,1149^A0N,35,45^FWN^FH^FD^FS
|
||||
^FO30,428^BY2,2^B7N,10,5,12^FH^FWN^FH^FD[)>_1E01_1D02950142083_1D840_1D019_1D333075610004019_1DFDEB_1D3330756_1D365_1D _1D1/1_1D10.5LB_1DN_1D1 Infinite Loop._1DCupertino_1DCA_1DSteve Jobs_1E06_1D10ZGD004_1D11ZApple Inc._1D12Z1234567890_1D23ZN_1D22Z _1CN_1D20Z _1C0_1D29Z_1D28Z_1D26Z3632_1C_1D_1E_04^FS
|
||||
^PQ1
|
||||
^XZ
|
||||
228
tray/assets/zpl_sample.xml
Normal file
228
tray/assets/zpl_sample.xml
Normal file
@@ -0,0 +1,228 @@
|
||||
|
||||
|
||||
|
||||
|
||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<soapenv:Header xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
|
||||
<soapenv:Body>
|
||||
<v7:ProcessShipmentReply xmlns:v7="http://fedex.com/ws/ship/v7" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<v7:HighestSeverity xmlns:java="java:com.fedex.nxgen.ship.v6.ientities">NOTE</v7:HighestSeverity>
|
||||
<v7:Notifications xmlns:java="java:com.fedex.nxgen.ship.v6.ientities">
|
||||
<v7:Severity>NOTE</v7:Severity>
|
||||
<v7:Source>ship</v7:Source>
|
||||
<v7:Code>6028</v7:Code>
|
||||
<v7:Message>LabelPrintingOrientationType not specified - using default value of N</v7:Message>
|
||||
<v7:LocalizedMessage>LabelPrintingOrientationType not specified - using default value of N</v7:LocalizedMessage>
|
||||
</v7:Notifications>
|
||||
<q0:TransactionDetail xmlns:q0="http://fedex.com/ws/ship/v7" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<q0:CustomerTransactionId>Ship Service V7</q0:CustomerTransactionId>
|
||||
</q0:TransactionDetail>
|
||||
<q0:Version xmlns:q0="http://fedex.com/ws/ship/v7" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<q0:ServiceId>ship</q0:ServiceId>
|
||||
<q0:Major>7</q0:Major>
|
||||
<q0:Intermediate>0</q0:Intermediate>
|
||||
<q0:Minor>0</q0:Minor>
|
||||
</q0:Version>
|
||||
<v7:CompletedShipmentDetail xmlns:java="java:com.fedex.nxgen.ship.v6.ientities">
|
||||
<v7:UsDomestic>true</v7:UsDomestic>
|
||||
<v7:CarrierCode>FDXG</v7:CarrierCode>
|
||||
<v7:ServiceTypeDescription>GROUND</v7:ServiceTypeDescription>
|
||||
<v7:PackagingDescription>YOUR_PACKAGING</v7:PackagingDescription>
|
||||
<v7:RoutingDetail>
|
||||
<v7:TransitTime>TWO_DAYS</v7:TransitTime>
|
||||
</v7:RoutingDetail>
|
||||
<v7:GroundServiceCode>019</v7:GroundServiceCode>
|
||||
<v7:ShipmentRating>
|
||||
<v7:ActualRateType>PAYOR_ACCOUNT</v7:ActualRateType>
|
||||
<v7:ShipmentRateDetails>
|
||||
<v7:RateType>PAYOR_ACCOUNT</v7:RateType>
|
||||
<v7:RateZone>4</v7:RateZone>
|
||||
<v7:RatedWeightMethod>ACTUAL</v7:RatedWeightMethod>
|
||||
<v7:DimDivisor>0</v7:DimDivisor>
|
||||
<v7:FuelSurchargePercent>5.25</v7:FuelSurchargePercent>
|
||||
<v7:TotalBillingWeight>
|
||||
<v7:Units>LB</v7:Units>
|
||||
<v7:Value>36.0</v7:Value>
|
||||
</v7:TotalBillingWeight>
|
||||
<v7:TotalBaseCharge>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>13.74</v7:Amount>
|
||||
</v7:TotalBaseCharge>
|
||||
<v7:TotalFreightDiscounts>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>7.0</v7:Amount>
|
||||
</v7:TotalFreightDiscounts>
|
||||
<v7:TotalNetFreight>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>6.74</v7:Amount>
|
||||
</v7:TotalNetFreight>
|
||||
<v7:TotalSurcharges>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>0.35</v7:Amount>
|
||||
</v7:TotalSurcharges>
|
||||
<v7:TotalNetFedExCharge>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>7.09</v7:Amount>
|
||||
</v7:TotalNetFedExCharge>
|
||||
<v7:TotalTaxes>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>0.0</v7:Amount>
|
||||
</v7:TotalTaxes>
|
||||
<v7:TotalNetCharge>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>7.09</v7:Amount>
|
||||
</v7:TotalNetCharge>
|
||||
<v7:TotalRebates>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>0.0</v7:Amount>
|
||||
</v7:TotalRebates>
|
||||
<v7:FreightDiscounts>
|
||||
<v7:RateDiscountType>VOLUME</v7:RateDiscountType>
|
||||
<v7:Description>Base</v7:Description>
|
||||
<v7:Amount>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>4.38</v7:Amount>
|
||||
</v7:Amount>
|
||||
<v7:Percent>0.0</v7:Percent>
|
||||
</v7:FreightDiscounts>
|
||||
<v7:FreightDiscounts>
|
||||
<v7:RateDiscountType>VOLUME</v7:RateDiscountType>
|
||||
<v7:Description>Matrix</v7:Description>
|
||||
<v7:Amount>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>2.62</v7:Amount>
|
||||
</v7:Amount>
|
||||
<v7:Percent>0.0</v7:Percent>
|
||||
</v7:FreightDiscounts>
|
||||
<v7:Surcharges>
|
||||
<v7:SurchargeType>INSURED_VALUE</v7:SurchargeType>
|
||||
<v7:Level>PACKAGE</v7:Level>
|
||||
<v7:Description>Insured value</v7:Description>
|
||||
<v7:Amount>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>0.0</v7:Amount>
|
||||
</v7:Amount>
|
||||
</v7:Surcharges>
|
||||
<v7:Surcharges>
|
||||
<v7:SurchargeType>FUEL</v7:SurchargeType>
|
||||
<v7:Level>PACKAGE</v7:Level>
|
||||
<v7:Description>FedEx Ground Fuel</v7:Description>
|
||||
<v7:Amount>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>0.35</v7:Amount>
|
||||
</v7:Amount>
|
||||
</v7:Surcharges>
|
||||
</v7:ShipmentRateDetails>
|
||||
</v7:ShipmentRating>
|
||||
<v7:IneligibleForMoneyBackGuarantee>false</v7:IneligibleForMoneyBackGuarantee>
|
||||
<v7:CompletedPackageDetails>
|
||||
<v7:SequenceNumber>1</v7:SequenceNumber>
|
||||
<v7:TrackingIds>
|
||||
<v7:TrackingIdType>GROUND</v7:TrackingIdType>
|
||||
<v7:TrackingNumber>422268010001723</v7:TrackingNumber>
|
||||
</v7:TrackingIds>
|
||||
<v7:GroupNumber>0</v7:GroupNumber>
|
||||
<v7:PackageRating>
|
||||
<v7:ActualRateType>PAYOR_ACCOUNT</v7:ActualRateType>
|
||||
<v7:PackageRateDetails>
|
||||
<v7:RateType>PAYOR_ACCOUNT</v7:RateType>
|
||||
<v7:RatedWeightMethod>ACTUAL</v7:RatedWeightMethod>
|
||||
<v7:BillingWeight>
|
||||
<v7:Units>LB</v7:Units>
|
||||
<v7:Value>36.0</v7:Value>
|
||||
</v7:BillingWeight>
|
||||
<v7:BaseCharge>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>13.74</v7:Amount>
|
||||
</v7:BaseCharge>
|
||||
<v7:TotalFreightDiscounts>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>7.0</v7:Amount>
|
||||
</v7:TotalFreightDiscounts>
|
||||
<v7:NetFreight>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>6.74</v7:Amount>
|
||||
</v7:NetFreight>
|
||||
<v7:TotalSurcharges>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>0.35</v7:Amount>
|
||||
</v7:TotalSurcharges>
|
||||
<v7:NetFedExCharge>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>7.09</v7:Amount>
|
||||
</v7:NetFedExCharge>
|
||||
<v7:TotalTaxes>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>0.0</v7:Amount>
|
||||
</v7:TotalTaxes>
|
||||
<v7:NetCharge>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>7.09</v7:Amount>
|
||||
</v7:NetCharge>
|
||||
<v7:TotalRebates>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>0.0</v7:Amount>
|
||||
</v7:TotalRebates>
|
||||
<v7:FreightDiscounts>
|
||||
<v7:RateDiscountType>VOLUME</v7:RateDiscountType>
|
||||
<v7:Description>Base</v7:Description>
|
||||
<v7:Amount>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>4.38</v7:Amount>
|
||||
</v7:Amount>
|
||||
<v7:Percent>0.0</v7:Percent>
|
||||
</v7:FreightDiscounts>
|
||||
<v7:FreightDiscounts>
|
||||
<v7:RateDiscountType>VOLUME</v7:RateDiscountType>
|
||||
<v7:Description>Matrix</v7:Description>
|
||||
<v7:Amount>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>2.62</v7:Amount>
|
||||
</v7:Amount>
|
||||
<v7:Percent>0.0</v7:Percent>
|
||||
</v7:FreightDiscounts>
|
||||
<v7:Surcharges>
|
||||
<v7:SurchargeType>INSURED_VALUE</v7:SurchargeType>
|
||||
<v7:Level>PACKAGE</v7:Level>
|
||||
<v7:Description>Insured value</v7:Description>
|
||||
<v7:Amount>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>0.0</v7:Amount>
|
||||
</v7:Amount>
|
||||
</v7:Surcharges>
|
||||
<v7:Surcharges>
|
||||
<v7:SurchargeType>FUEL</v7:SurchargeType>
|
||||
<v7:Level>PACKAGE</v7:Level>
|
||||
<v7:Description>FedEx Ground Fuel</v7:Description>
|
||||
<v7:Amount>
|
||||
<v7:Currency>USD</v7:Currency>
|
||||
<v7:Amount>0.35</v7:Amount>
|
||||
</v7:Amount>
|
||||
</v7:Surcharges>
|
||||
</v7:PackageRateDetails>
|
||||
</v7:PackageRating>
|
||||
<v7:Barcodes>
|
||||
<v7:BinaryBarcodes>
|
||||
<v7:Type>COMMON_2D</v7:Type>
|
||||
<v7:Value>Wyk+HjAxHTAyMzgwMTcdODQwHTAxOR0wMDAwMDAwMDAwMDAwMDAdRkRFQh0wMDAwMDAwHTMzNR0dMS8xHTM2LjBMQh1OHVJlY2lwaWVudCBBZGRyZXNzIExpbmUgMR1Db2xsaWVydmlsbGUdVE4dUmVjaXBpZW50IENvbnRhY3QeMDYdMTBaR0QwMDQdMTJaMTIzNDU2Nzg5MB0xNFoqKlRFU1QgTEFCRUwgLSBETyBOT1QgU0hJUCoqHTIzWk4dMjJaHE4dMjBaIBw4MB0yNlo1MzEwHB0eBA==</v7:Value>
|
||||
</v7:BinaryBarcodes>
|
||||
<v7:StringBarcodes>
|
||||
<v7:Type>GROUND</v7:Type>
|
||||
<v7:Value>9612019000000000000000</v7:Value>
|
||||
</v7:StringBarcodes>
|
||||
</v7:Barcodes>
|
||||
<v7:Label>
|
||||
<v7:Type>OUTBOUND_LABEL</v7:Type>
|
||||
<v7:CopiesToPrint>1</v7:CopiesToPrint>
|
||||
<v7:Parts>
|
||||
<v7:DocumentPartSequenceNumber>1</v7:DocumentPartSequenceNumber>
|
||||
<v7:Image>XlhBXkNGLDAsMCwwXlBSMTJeTUQzMF5QVzgwMF5QT0leQ0kxM15MSDAsMjAKXkZPMCwxNDdeR0I4MDAsNCw0XkZTCl5GTzAsNDAxXkdCODAwLDQsNF5GUwpeRk8wLDczNl5HQjgwMCw0LDReRlMKXkZPMzUsMTJeQWROLDAsMF5GV05eRkheRkRGcm9tOl5GUwpeRk8zNSwzMV5BZE4sMCwwXkZXTl5GSF5GRF5GUwpeRk8zNSw1MV5BZE4sMCwwXkZXTl5GSF5GRFRHS15GUwpeRk8zNSw3MV5BZE4sMCwwXkZXTl5GSF5GRFNOMjAwMCBUZXN0IE1ldGVyIDheRlMKXkZPMzUsOTJeQWROLDAsMF5GV05eRkheRkQxMCBGZWRleCBQYXJrd2F5XkZTCl5GTzM1LDEzMl5BZE4sMCwwXkZXTl5GSF5GRCgxMjMpIDQ1Ni03ODkwXkZTCl5GTzQ5MCw3Ml5BZE4sMCwwXkZXTl5GSF5GRENBRDogMTE4NTAwOTA3L1dTWEkyMzUwXkZTCl5GTzQzLDE5M15BME4sMjUsMjdeRldOXkZIXkZEXkZTCl5GTzQzLDIyOF5BME4sMjUsMjdeRldOXkZIXkZEUmVjaXBpZW50IEFkZHJlc3MgTGluZSAxXkZTCl5GTzQzLDI2M15BME4sMjUsMjdeRldOXkZIXkZEKipURVNUIExBQkVMIC0gRE8gTk9UIFNISVAqKl5GUwpeRk8zNSwxMTJeQWROLDAsMF5GV05eRkheRkRBdXN0aW4sIFRYIDczMzAxXkZTCl5GTzQ5MCwzMV5BZE4sMCwwXkZXTl5GSF5GRFNoaXAgRGF0ZTogMDFERUMwOV5GUwpeRk80OTAsNTFeQWROLDAsMF5GV05eRkheRkRBY3RXZ3Q6IDM2LjAgTEJeRlMKXkZPNDkwLDkxXkFkTiwwLDBeRldOXkZIXkZEQWNjb3VudDogUyAyOTY1NTY5NjNeRlMKXkZPNDMsMTU4XkEwTiwyNSwyN15GV05eRkheRkRSZWNpcGllbnQgQ29udGFjdF5GUwpeRk82MTUsMTU2XkFkTiwwLDBeRldOXkZIXkZEKDEyMykgNDU2LTc4OTBeRlMKXkZPNDMsMjk2XkEwTiwzMCwzMF5GV05eRkheRkRDb2xsaWVydmlsbGUsIFROIDM4MDE3XkZTCl5GTzUzMCwyOTZeQTBOLDM1LDQ1XkZXTl5GSF5GRChVUyleRlMKXkZPNzI1LDIxNl5BZE4sMCwwXkZXTl5GSF5GREdyb3VuZF5GUwpeRk82NzAsMjM4XkdCMTA1LDEwLDEwXkZTCl5GTzY3MCwyNDheR0IxMCwxMTIsMTBeRlMKXkZPNzY1LDI0OF5HQjEwLDExMiwxMF5GUwpeRk82NzAsMzYwXkdCMTA1LDEwLDEwXkZTCl5GTzQ3NiwzXkdCNCwxNDUsNF5GUwpeRk82NTAsMTczXkEwTiw1MCw1NV5GV05eRkheRkRGZWRFeF5GUwpeRk82OTAsMjU2XkEwTiwxMzAsMTMwXkZXTl5GSF5GREdeRlMKXkZPODAsNzcxXkJZNCwyXkJDTiwyOTAsTixOLE4sTl5GV05eRkQ+Oz44OTYxMjAxOTAwMDAwMDAwMDAwMDAwMF5GUwpeRk8xMzUsMTA4M15BME4sMjUsMjdeRldOXkZIXkZEKDk2MTIwMTkpIDAwMDAwMDAgIDAwMDAwMDAwXkZTCl5GTzc4MywyNTheQTBOLDE1LDE1XkZXQl5GSF5GREowOTMwMDkwNzMxMjEyM15GUwpeRk8xMCwxNjJeQTBOLDIwLDE4XkZXTl5GSF5GRFRPXkZTCl5GTzI1LDExMDheQTBOLDUwLDU1XkZXTl5GSF5GREdORF5GUwpeRk8yNSwxMTUwXkEwTiwzNSw0NV5GV05eRkheRkRQcmVwYWlkXkZTCl5GTzMwMCwxMTE1XkEwTiwzNSw0NV5GV05eRkheRkReRlMKXkZPMzAwLDExNDleQTBOLDM1LDQ1XkZXTl5GSF5GRF5GUwpeRk8zMCw0MjheQlkyLDJeQjdOLDEwLDUsMTJeRkheRldOXkZIXkZEWyk+XzFFMDFfMUQwMjM4MDE3XzFEODQwXzFEMDE5XzFEMDAwMDAwMDAwMDAwMDAwXzFERkRFQl8xRDAwMDAwMDBfMUQzMzVfMURfMUQxLzFfMUQzNi4wTEJfMUROXzFEUmVjaXBpZW50IEFkZHJlc3MgTGluZSAxXzFEQ29sbGllcnZpbGxlXzFEVE5fMURSZWNpcGllbnQgQ29udGFjdF8xRTA2XzFEMTBaR0QwMDRfMUQxMloxMjM0NTY3ODkwXzFEMTRaKipURVNUIExBQkVMIC0gRE8gTk9UIFNISVAqKl8xRDIzWk5fMUQyMlpfMUNOXzFEMjBaIF8xQzgwXzFEMjZaNTMxMF8xQ18xRF8xRV8wNF5GUwpeRk8xNiwzNTheQTBOLDE1LDE1XkZXTl5GSF5GRFJlZjogXkZTCl5GTzE2LDM3M15BME4sMTUsMTVeRldOXkZIXkZESU5WOiBeRlMKXkZPMTYsMzg4XkEwTiwxNSwxNV5GV05eRkheRkRQTzogXkZTCl5GTzQwNiwzODheQTBOLDE1LDE1XkZXTl5GSF5GRERlcHQ6IF5GUwpeRk82MjUsNDk1XkEwTiwyMCwzNV5GV05eRkheRkQxXkZTCl5GTzY0MCw1MzVeQTBOLDIwLDM1XkZXTl5GSF5GRG9mXkZTCl5GTzYyNSw1NzVeQTBOLDIwLDM1XkZXTl5GSF5GRDFeRlMKXlBRMQpeWFoK</v7:Image>
|
||||
</v7:Parts>
|
||||
</v7:Label>
|
||||
<v7:SignatureOption>SERVICE_DEFAULT</v7:SignatureOption>
|
||||
</v7:CompletedPackageDetails>
|
||||
</v7:CompletedShipmentDetail>
|
||||
</v7:ProcessShipmentReply>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
||||
|
||||
Reference in New Issue
Block a user