Zero-Boilerplate, 100% Cross-Language Compatible Encryption for Go Developers
Cryptography in Go has always been a tale of two worlds.
On one hand, Go's standard library crypto/* packages are well-crafted, secure, and fast. On the other hand, implementing everyday cryptographic workflows—like encrypting a payload with AES-CBC (PKCS#7), performing AES-ECB for legacy systems, or handling RSA encryption for large payloads—requires writing dozens of lines of repetitive boilerplate code.
Even worse, subtle differences in padding schemes, IV handlings, or key truncations often lead to frustrating cross-language decryption failures when communicating with PHP (openssl_encrypt), Java (javax.crypto), Python (pycryptodome), or Node.js.
To solve these headaches once and for all, meet go-think/openssl — a developer-friendly, pure Go cryptographic toolkit that brings simplicity and seamless cross-platform interoperability to Go applications.
💡 Why go-think/openssl?
- Pure Go, No CGO Dependencies: Lightweight, cross-compilation friendly, and built on top of standard Go crypto primitives.
- Zero-Boilerplate API: Do what used to take 40 lines in just 2 or 3 lines.
- 100% Cross-Language Compatibility: Produces identical binary ciphertext to OpenSSL, PHP, Java, Python, and Node.js.
-
Smart Padding & Chunking:
- Native support for PKCS#7 (PKCS#5) and ZeroPadding.
- Automatic chunking for RSA encryption and decryption of arbitrarily long messages.
- Batteries Included: AES, DES, 3DES, RSA, MD5, SHA-1 to SHA-512, and HMAC.
🚀 Quick Installation
go get -u github.com/go-think/openssl
🛠️ Code in Action
1. Symmetric Encryption: AES (CBC, ECB & GCM)
Standard Go requires manual block padding, block mode initialization, and slice slicing. With go-think/openssl, it is as simple as:
AES-CBC with PKCS#7 Padding
package main
import (
"encoding/base64"
"fmt"
"github.com/go-think/openssl"
)
func main() {
src := []byte("Hello, Cross-Language World!")
key := []byte("1234567890123456") // 16-byte key for AES-128
iv := []byte("1234567890123456") // 16-byte IV
// Encrypt
cipherText, err := openssl.AesCBCEncrypt(src, key, iv, openssl.PKCS7_PADDING)
if err != nil {
panic(err)
}
fmt.Println("Base64 Ciphertext:", base64.StdEncoding.EncodeToString(cipherText))
// Decrypt
plainText, err := openssl.AesCBCDecrypt(cipherText, key, iv, openssl.PKCS7_PADDING)
if err != nil {
panic(err)
}
fmt.Println("Decrypted:", string(plainText))
}
Modern AEAD: AES-GCM (Authenticated Encryption)
For applications that require authenticated encryption with integrity checks:
nonce := []byte("123456789012") // 12-byte standard nonce
aad := []byte("authenticated_header") // Additional authenticated data
// Encrypt
dst, err := openssl.AesGCMEncrypt(src, key, nonce, aad)
// Decrypt
plain, err := openssl.AesGCMDecrypt(dst, key, nonce, aad)
Also supports CFB, OFB, CTR, as well as legacy DES and 3DES ciphers out of the box.
2. Painless RSA: Key Generation, Signatures & Auto-Chunking
One notorious limitation of raw RSA is that it cannot encrypt data larger than its key size minus padding overhead. Usually, developers must write chunking and buffer reassembly logic. go-think/openssl handles this automatically:
package main
import (
"bytes"
"crypto"
"fmt"
"github.com/go-think/openssl"
)
func main() {
// 1. Generate a 2048-bit RSA Key Pair in PEM format
var privBuf, pubBuf bytes.Buffer
_ = openssl.RSAGenerateKey(2048, &privBuf)
_ = openssl.RSAGeneratePublicKey(privBuf.Bytes(), &pubBuf)
privKey := privBuf.Bytes()
pubKey := pubBuf.Bytes()
// 2. Encrypt arbitrary-length data (Automatic chunking under the hood)
longData := []byte("A very long message that exceeds standard RSA block size limit...")
encrypted, _ := openssl.RSAEncrypt(longData, pubKey)
// Decrypt
decrypted, _ := openssl.RSADecrypt(encrypted, privKey)
fmt.Println("Decrypted RSA:", string(decrypted))
// 3. Digital Signatures (RSASign & RSAVerify)
signature, _ := openssl.RSASign(longData, privKey, crypto.SHA256)
err := openssl.RSAVerify(longData, signature, pubKey, crypto.SHA256)
if err == nil {
fmt.Println("Signature verified successfully!")
}
}
Note:
RSAEncryptalso seamlessly accepts X.509CERTIFICATEPEM blocks!
3. One-Liner Hashes & HMACs
Stop creating hash.Hash interfaces just to get a hex digest:
// MD5
hexMD5 := openssl.Md5ToString("hello world")
// SHA-256
hexSHA := openssl.Sha256ToString("hello world")
// HMAC-SHA256
hmacStr := openssl.HmacSha256ToString("secret-key", "hello world")
🌐 100% Interoperability: Go vs. PHP vs. Java
Let's test if the ciphertext generated by Go matches other platforms bit-for-bit.
In Go (go-think/openssl):
src := []byte("123456")
key := []byte("1234567890123456")
iv := []byte("1234567890123456")
dst, _ := openssl.AesCBCEncrypt(src, key, iv, openssl.PKCS7_PADDING)
fmt.Println(base64.StdEncoding.EncodeToString(dst))
// Output: 1jdzWuniG6UMtoa3T6uNLA==
In PHP (openssl_encrypt):
<?php
$data = "123456";
$key = "1234567890123456";
$iv = "1234567890123456";
$encrypted = openssl_encrypt($data, 'aes-128-cbc', $key, OPENSSL_RAW_DATA, $iv);
echo base64_encode($encrypted);
// Output: 1jdzWuniG6UMtoa3T6uNLA== (Identical!)
In Java (javax.crypto):
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes(), "AES"), new IvParameterSpec(iv.getBytes()));
byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
System.out.println(Base64.getEncoder().encodeToString(encrypted));
// Output: 1jdzWuniG6UMtoa3T6uNLA== (Identical!)
📦 Summary
If you are building Go microservices that need to interact with external APIs, legacy systems, mobile apps, or other backends without wrestling with cryptographic discrepancies, go-think/openssl will save you hours of debugging.
- GitHub Repository: https://github.com/go-think/openssl
- Go Package Documentation: pkg.go.dev/github.com/go-think/openssl
- License: Apache 2.0
Give it a ⭐ on GitHub if you find it useful, and feel free to submit issues or pull requests!
Top comments (0)