---
# System prepended metadata

title: Example of Cryptographic Transformation Implementation in "Go" Language

---

[УКР](https://hackmd.io/Q6igsNrZShOkdbHLY5zB0Q) | ENG
![](https://i.imgur.com/ULmPUgP.jpg)


# Example of Cryptographic Transformation Implementation in "Go" Language


~~~md
package main

import (
	"bytes"
	"crypto"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"fmt"
	"hash"
	"io"
	"log"
	"os"
)

var (
	private *rsa.PrivateKey
	public  *rsa.PublicKey
	partner *rsa.PublicKey
	err     error

	pssHash hash.Hash
	newHash = crypto.SHA256

	text   = "some test text"
	data   string
	keyAES string
	sign   string
)

func main() {
	pssHash = newHash.New()
	private, err = readPrivKeyFile()
	if err != nil {
		return
	}
	public = &private.PublicKey
	partner, err = readPartnerKey()
	if err != nil {
		return
	}
	log.Println("\n\n1 - crypt\n2 - sign\n3 - verify sign\n4 - decrypt\n9 - exit")

	var todo int

	for {
		fmt.Scanln(&todo)
		switch todo {
		case 1:
			encrypt()
		case 2:
			makeSign()
		case 3:
			signVerify()
		case 4:
			decrypt()
		case 9:
			return
		default:
			log.Println("\n\n1 - crypt\n2 - sign\n3 - verify sign\n4 - decrypt\n9 - exit")
		}
	}

}

// BEGIN of general functions
func encrypt() {

	n := []byte(text)
	aesKey, err := generateKeyAES()
	if err != nil {
		log.Println(err.Error())
		return
	}
	log.Println("keyAES", aesKey)

	data, err = encryptAES(aesKey, n)

	var encryptedKeyAES []byte

	encryptedKeyAES, err = encryptRSA(aesKey, partner)

	if err != nil {
		log.Println(err.Error())
		return
	}
	keyAES = base64.StdEncoding.EncodeToString(encryptedKeyAES)

	log.Println("data:", data)
	log.Println("keyAES:", keyAES)
}

func makeSign() {
	log.Println("makeSign")
	in, err := base64.StdEncoding.DecodeString(keyAES)
	if err != nil {
		log.Println("decode keyAES error", err.Error())
		return
	}

	pssHash.Write(in)
	hashed := pssHash.Sum(nil)
	//PKCS
	out, err := rsa.SignPKCS1v15(rand.Reader, private, newHash, hashed)

	//OAEP
	//out, err := rsa.SignPSS(rand.Reader, k, newHash, hashed, nil)
	if err != nil {
		log.Println("makeSign err:", err.Error())
		return
	}
	pssHash.Reset()

	sign = base64.StdEncoding.EncodeToString(out)
	log.Println("sign:", sign)
}

func signVerify() {
	in, err := base64.StdEncoding.DecodeString(keyAES)
	if err != nil {
		log.Println("decode keyAES error", err.Error())
		return
	}

	signIn, err := base64.StdEncoding.DecodeString(sign)
	if err != nil {
		log.Println("decode sign error:", err.Error())
		return
	}

	pssHash.Write(in)
	hashed := pssHash.Sum(nil)
	//PKCS
	err = rsa.VerifyPKCS1v15(partner, newHash, hashed, signIn)

	//OAEP
	//err := rsa.VerifyPSS(k, newHash, hashed, sign, nil)
	if err != nil {
		log.Println("verify sign: FAIL!")
		pssHash.Reset()
		return
	}
	pssHash.Reset()
	log.Println("verify sign: OK!")
}

func decrypt() {
	key, err := base64.StdEncoding.DecodeString(keyAES)
	if err != nil {
		log.Println("decode keyAES error", err.Error())
		return
	}

	aesKey, err := decryptRSA(key, private)
	if err != nil {
		log.Println(err.Error())
		return
	}

	decodedData, err := decryptAES(aesKey, data)

	log.Println("decoded:", decodedData)
}

//END of general functions

// reading own private key
func readPrivKeyFile() (*rsa.PrivateKey, error) {
	n, err := os.ReadFile("private.key")
	if err != nil {
		log.Println("read pubFile err:", err.Error())
		return nil, err
	}

	block, _ := pem.Decode(n)
	key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		log.Println("parse err:", err.Error())
		return nil, err
	}
	return key, nil
}

// reading partner's public key
func readPartnerKey() (*rsa.PublicKey, error) {
	var key *rsa.PublicKey
	n, err := os.ReadFile("partner.key")
	if err != nil {
		log.Println("read pubFile err:", err.Error())
		return key, err
	}

	pemBlock, _ := pem.Decode(n)
	if pemBlock == nil {
		log.Println("pemBlock decode err")
		return key, fmt.Errorf("pemBlock decode err")
	}

	keyInt, err := x509.ParsePKIXPublicKey(pemBlock.Bytes)
	if err != nil {
		log.Println("ParsePKIXPublicKey err:", err.Error())
		return key, err
	}

	key = keyInt.(*rsa.PublicKey)
	return key, nil
}

// AES crypto functions
// encryptAES encrypts text using AES/CBC with the provided key
func encryptAES(key, text []byte) (string, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		log.Println(err.Error())
		return "", err
	}

	//text padding
	padedText := pad(text)

	ciphertext := make([]byte, aes.BlockSize+len(padedText))
	iv := ciphertext[:aes.BlockSize]
	_, err = io.ReadFull(rand.Reader, iv)
	if err != nil {
		log.Println(err.Error())
		return "", err
	}
	log.Println("encryptAES: IV =", iv)

	cbc := cipher.NewCBCEncrypter(block, iv)
	cbc.CryptBlocks(ciphertext[aes.BlockSize:], []byte(padedText))

	finalMsg := base64.StdEncoding.EncodeToString(ciphertext)
	return finalMsg, nil
}

// decryptAES decrypts text using AES/CBC with the provided key
func decryptAES(key []byte, text string) (string, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		log.Println(err.Error())
		return "", err
	}

	decodedMsg, err := base64.StdEncoding.DecodeString(text)
	if err != nil {
		log.Println(err.Error())
		return "", err
	}

	iv := decodedMsg[:aes.BlockSize]
	log.Println("decryptAES: IV =", iv)

	msg := decodedMsg[aes.BlockSize:]

	cbc := cipher.NewCBCDecrypter(block, iv)
	cbc.CryptBlocks(msg, msg)

	log.Println("padedText", string(msg))

	//text unpadding
	unpadedText, err := unpad(msg)
	if err != nil {
		log.Println(err.Error())
		return "", err
	}

	return string(unpadedText), nil
}

// encryptAES encrypts text using AES/GCM with the provided key
func encryptAESGCM(key []byte, text string) (string, error) {
	const nonceSize = 12 // AES-GCM standard nonce size
	// Create a new AES cipher block
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", fmt.Errorf("failed to create cipher: %w", err)
	}

	// Use GCM mode with the AES cipher
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", fmt.Errorf("failed to create GCM: %w", err)
	}

	// Generate a nonce with the required size for GCM
	nonce := make([]byte, nonceSize) // AES-GCM standard nonce size
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		return "", fmt.Errorf("failed to generate nonce: %w", err)
	}

	// Encrypt and add the authentication tag using Seal
	ciphertext := gcm.Seal(nil, nonce, []byte(text), nil)

	// Prepend the nonce to the ciphertext (this is typically done for AES-GCM)
	finalMessage := append(nonce, ciphertext...)

	// Encode the final message in Base64 for easier handling
	return base64.StdEncoding.EncodeToString(finalMessage), nil
}

// decryptAESGCM decrypts text using AES-GCM with the provided key
func decryptAESGCM(key []byte, text string) (string, error) {
	const nonceSize = 12 // AES-GCM standard nonce size
	// Create a new AES cipher block
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	// Decode the Base64-encoded ciphertext
	data, err := base64.StdEncoding.DecodeString(text)
	if err != nil {
		return "", err
	}

	// Check if the ciphertext is valid and minimum contains the nonce
	if len(data) < nonceSize { // GCM Nonce size is typically 12 bytes
		return "", errors.New("ciphertext too short")
	}

	// Split the ciphertext into nonce and ciphertext
	nonce, ciphertext := data[:nonceSize], data[nonceSize:] // GCM Nonce size is typically 12 bytes

	// Use GCM mode with the AES cipher
	aesgcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", err
	}

	// Decrypt and verify
	plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		return "", err
	}

	return string(plaintext), nil
}

// AES utility functions
func generateKeyAES() ([]byte, error) {
	key := make([]byte, 16) //RSA crypto functions PKCS
	_, err := rand.Read(key)
	if err != nil {
		log.Println(err.Error())
		return nil, err
	}
	return key, err
}

func pad(src []byte) []byte {
	padding := aes.BlockSize - len(src)%aes.BlockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(src, padtext...)
}

func unpad(src []byte) ([]byte, error) {
	length := len(src)
	unpadding := int(src[length-1])

	if unpadding > length {
		return nil, errors.New("unpad error. This could happen when incorrect encryption key is used")
	}

	return src[:(length - unpadding)], nil
}

// RSA crypto functions PKCS 1.5
func encryptRSA(in []byte, k *rsa.PublicKey) ([]byte, error) {
	out, err := rsa.EncryptPKCS1v15(rand.Reader, k, in)
	if err != nil {
		log.Println(err.Error())
		return out, err
	}
	return out, nil
}

func decryptRSA(in []byte, k *rsa.PrivateKey) ([]byte, error) {
	out, err := rsa.DecryptPKCS1v15(rand.Reader, k, in)
	if err != nil {
		log.Println(err.Error())
		return out, err
	}
	return out, nil
}

// RSA crypto functions OAEP
func encryptRSAOAEP(in []byte, k *rsa.PublicKey) ([]byte, error) {
	out, err := rsa.EncryptOAEP(pssHash, rand.Reader, k, in, nil)
	if err != nil {
		log.Println(err.Error())
		return out, err
	}
	return out, nil
}

func decryptRSAOAEP(in []byte, k *rsa.PrivateKey) ([]byte, error) {
	out, err := rsa.DecryptOAEP(pssHash, rand.Reader, k, in, nil)
	if err != nil {
		log.Println(err.Error())
		return out, err
	}
	return out, nil
}
~~~


![](https://i.imgur.com/17gAyWb.png)
:arrow_left: [XPAY API Guide](https://hackmd.io/QA2NYRliRLGviJl5P4-ZCw)
:arrow_left: [XPASS API Guide](https://hackmd.io/pa6TRNZTTa67jO_qYDuRBQ)
:arrow_left: [General API Integration Information](https://hackmd.io/ijxPm0xtTpycgTjuU_NHQQ)

<details>
<summary>XPAY Support</summary>
</br>

Phone: +38 093 891 92 00
Email: info@xpay.com.ua
Telegram: @xpaysupportbot
</details>



