Skip to content

Instantly share code, notes, and snippets.

@vieirin
Last active November 19, 2020 07:33
Show Gist options
  • Save vieirin/05467cc2d9a619fee9f9bc3b12fff56d to your computer and use it in GitHub Desktop.
Save vieirin/05467cc2d9a619fee9f9bc3b12fff56d to your computer and use it in GitHub Desktop.
package main
/*
#cgo CFLAGS: -I/usr/include/
#cgo LDFLAGS: -ldinamo -ltacndlib
#include <dinamo.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
struct AUTH_PWD_EX NewAuth( const char* ip, const char* user, const char* pwd) {
struct AUTH_PWD_EX auth = {0};
strncpy(auth.szAddr, ip, sizeof(auth.szAddr));
strncpy(auth.szUserId, user, sizeof(auth.szUserId));
strncpy(auth.szPassword, pwd, sizeof(auth.szPassword));
auth.nPort = DEFAULT_PORT;
auth.nStrongAuthLen = 0;
auth.pbStrongAuth = NULL;
auth.dwAuthType = SA_AUTH_NONE;
return auth;
}
BYTE* convertAuthToPointer(struct AUTH_PWD_EX *auth) {
return (BYTE *) auth;
}
void closeSession(HSESSIONCTX sess) {
if( sess ) {
DCloseSession(&sess, 0);
}
DFinalize();
}
struct SObject {
void *data;
long len;
};
void deallocObj(struct SObject *obj) {
if (obj->data) {
free(obj->data);
}
if (obj) {
free(obj);
}
}
void * newObj(struct SObject *in, void *data, unsigned int len) {
in = (struct SObject *)malloc(sizeof(struct SObject));
in->data = malloc(len);
memcpy(in->data, data, len);
in->len = len;
return (void *)in;
}
int saveCert(BYTE *pbData, DWORD *pdwDataLen, void *pParam, BOOL *pbFinal){
if (pParam != NULL) {
*pbFinal = TRUE;
struct SObject *param = (struct SObject*) pParam;
memcpy(pbData, param->data, param->len);
*pdwDataLen = param->len;
}
return 0;
}
int readCert (BYTE *pbData, DWORD dwDataLen, void *pParam, BOOL bFinal) {
char *data = pParam;
for (int i = 0; i < dwDataLen; i++){
data[i] = pbData[i];
}
return 0;
}
funcWriteLocalFileCallback readFunc = &readCert;
funcReadLocalFileCallback saveFunc = &saveCert;
*/
import "C"
import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/sha256"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"encoding/json"
"encoding/pem"
"fmt"
"log"
"regexp"
"strings"
"sync"
"unsafe"
"github.com/google/logger"
"github.com/pkg/errors"
guuid "github.com/google/uuid"
)
var keyType = C.int(C.ALG_ECC_X9_62_PRIME256V1)
// HSM is a struct that defines an HSM with necessary functions to generate
// private keys/CSRs
type HSM struct {
once *sync.Once
sess C.HSESSIONCTX
hsmip string
hsmuser string
hsmpwd string
ctx context.Context
}
// IDs defines the key paid ID pub/priv
type IDs struct {
privID, pubID, cert string
}
type pubRef struct {
CKAID string `json:"CKA_ID,omitempty"`
CKALabel string `json:"CKA_LABEL,omitempty"`
CKAECParams string `json:"CKA_EC_PARAMS,omitempty"`
}
func (h *HSM) shutdown() {
// wait for context to finish and put hsm down
<-h.ctx.Done()
logger.Infof("Shutting HSM down")
C.closeSession(h.sess)
}
//NewHSM creates a new hsm instance
func NewHSM(ctx context.Context, ip, user, pwd string) *HSM {
hsm := HSM{
once: &sync.Once{},
sess: nil,
hsmip: ip,
hsmuser: user,
hsmpwd: pwd,
ctx: ctx,
}
go hsm.shutdown()
hsm.initSession()
return &hsm
}
//DefaultHSM creates and returns the default hsm
func DefaultHSM(ctx context.Context) *HSM {
return NewHSM(ctx, "200.201.208.61", "goledger", "12345678")
}
//InitSession initializes HSM session
func (h *HSM) initSession() {
h.once.Do(func() {
auth := C.NewAuth(C.CString(h.hsmip), C.CString(h.hsmuser), C.CString(h.hsmpwd))
C.DInitialize(0)
ret := C.DOpenSession(
&h.sess,
C.SS_USER_PWD,
C.convertAuthToPointer(&auth),
C.sizeof_struct_AUTH_PWD_EX,
C.CACHE_BYPASS|C.LB_BYPASS|C.ENCRYPTED_CONN)
if ret != 0 {
fmt.Printf("DOpenSession failed! %d.\n", ret)
}
})
}
func createHSMKey(key string) string {
keySalted := guuid.New().String()
reg, err := regexp.Compile("[^a-zA-Z0-9]+")
if err != nil {
log.Fatal(err)
}
hsmKey := reg.ReplaceAllString(keySalted, "")
if len(hsmKey) > 32 {
hsmKey = hsmKey[:32]
}
return hsmKey
}
//GenPrivKey ...
func (h *HSM) genPrivKey(commonName string) (string, error) {
var hKey C.HKEYCTX
keyID := C.CString(createHSMKey(commonName))
ret := C.DGenerateKey(h.sess, keyID, keyType, 0, &hKey)
if ret != 0 {
err := fmt.Errorf("Falha na funcao: DGenerateKey \nCodigo de erro: %d", ret)
return "", err
}
return C.GoString(keyID), nil
}
//GenCSR ...
func (h HSM) GenCSR(name pkix.Name) (*pem.Block, string, error) {
keyID, err := h.genPrivKey(name.CommonName)
if err != nil {
return nil, "", err
}
dn := strings.Replace(name.String(), ",", "/", -1)
dn = "/" + dn
keyLength := C.uint(C.CORE_P10_CSR_DN_MAX_LEN)
pbcsr := make([]byte, 2048)
pbptr := (*C.uchar)(C.CBytes(pbcsr))
ret := C.DGeneratePKCS10CSR(
h.sess,
C.CString(keyID),
0,
C.CString(dn),
C.P10_CSR_PEM,
&keyLength,
&pbptr,
0)
if ret != 0 {
err := fmt.Errorf("Falha na funcao: DGeneratePKCS10CSR \nCodigo de erro: %d", ret)
return nil, "", err
}
pbcsr = C.GoBytes(unsafe.Pointer(pbptr), C.int(keyLength))
csrBlock, _ := pem.Decode(pbcsr)
return csrBlock, keyID, nil
}
func (h HSM) getObjMeta(keyID string) C.HOBJMETACTX {
var obj C.HOBJMETACTX
ret := C.DManageObjMetadata(h.sess, C.MNG_OBJ_META_GET, C.CString(keyID), nil, &obj, 0)
if ret != 0 {
fmt.Println("Could not manage obj, cod: ", ret)
return nil
}
return obj
}
// AssociateCertificateToPrivate associates a pub certificate to a private key in HSM
func (h HSM) AssociateCertificateToPrivate(cert []byte, privKeyID string) (*IDs, error) {
certPEM, _ := pem.Decode(cert)
certDER := certPEM.Bytes
certFileID := createHSMKey(guuid.New().String())
fmt.Printf("cert key id: %s, cert key len: %d\n", certFileID, len(certFileID))
saveCertFunc := C.saveFunc
paramObj := C.struct_SObject{}
param := C.newObj(&paramObj, unsafe.Pointer(&certDER[0]), C.uint(len(certDER)))
defer C.deallocObj((*C.struct_SObject)(param))
ret := C.DWriteFile(h.sess, C.CString(certFileID), C.uint(len(certDER)), saveCertFunc, param)
if ret != 0 {
err := errors.Errorf("Could not create cert file in hsm, errorCode: %d", uint(ret))
return nil, err
}
fmt.Printf("priv key id: %s, priv key len: %d\n", privKeyID, len(privKeyID))
pubKeyID := createHSMKey(privKeyID)
fmt.Printf("pub key id: %s, pub key len: %d\n", pubKeyID, len(pubKeyID))
ret = C.DAssociatePKCS11Key(h.sess, C.CString(privKeyID), C.CString(pubKeyID), C.CString(certFileID), nil, 0)
if ret != 0 {
err := errors.Errorf("Could not associate pub key to privateKey, errorCode: %d", uint(ret))
return nil, err
}
logger.Infof("Certificate associated, id: %s, privKeyID: %s, pubkeyID: %s", certFileID, pubKeyID, privKeyID)
return &IDs{privID: privKeyID, pubID: pubKeyID, cert: certFileID}, nil
}
//SetSKI ..
func (h HSM) SetSKI(block *pem.Block, username string, pair *IDs) error {
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
err := fmt.Errorf("Could not parse certificate for node %s: %w", username, err)
fmt.Println(err)
return err
}
pubKey := cert.PublicKey.(*ecdsa.PublicKey)
raw := elliptic.Marshal(pubKey.Curve, pubKey.X, pubKey.Y)
hasher := sha256.New()
hasher.Write(raw)
ski := hasher.Sum(nil)
skiEncoded := hex.EncodeToString(ski)
data := pubRef{
CKAID: strings.ToUpper(skiEncoded),
CKALabel: skiEncoded,
}
dataRaw, err := json.Marshal(data)
if err != nil {
return fmt.Errorf("Could not marshal public reference struct: %w", err)
}
// set CKA_ID to privKey
objMeta := h.getObjMeta(pair.privID)
ret := C.DSetObjMetadataJson(
objMeta,
C.CString(string(dataRaw)),
C.uint(len(dataRaw)),
0,
)
if ret != 0 {
return fmt.Errorf("Could not set obj data %d", ret)
}
ret = C.DManageObjMetadata(
h.sess,
C.MNG_OBJ_META_UPDATE,
C.CString(pair.privID),
objMeta,
nil,
0,
)
if ret != 0 {
return fmt.Errorf("Could not update obj data %d", ret)
}
C.DDestroyObjMetadata(&objMeta, 0)
// set CKA_ID to pubKey
objMeta = h.getObjMeta(pair.pubID)
ret = C.DSetObjMetadataJson(
objMeta,
C.CString(string(dataRaw)),
C.uint(len(dataRaw)),
0,
)
if ret != 0 {
return fmt.Errorf("Could not set obj data %d", ret)
}
ret = C.DManageObjMetadata(
h.sess,
C.MNG_OBJ_META_UPDATE,
C.CString(pair.pubID),
objMeta,
nil,
0,
)
if ret != 0 {
return fmt.Errorf("Could not update obj data %d", ret)
}
fmt.Printf("Node: %s, id: %s, privKey: %s, pubKey: %s\n", username, hex.EncodeToString(ski), pair.privID, pair.pubID)
C.DDestroyObjMetadata(&objMeta, 0)
return nil
}
var cert = `-----BEGIN CERTIFICATE-----
MIICkjCCAjmgAwIBAgIUWNVmpDQeEi9fVlw0Xj0Mr9QPqdEwCgYIKoZIzj0EAwIw
XzELMAkGA1UEBhMCQlIxCzAJBgNVBAgTAkRGMREwDwYDVQQHEwhCcmFzaWxpYTES
MBAGA1UEChMJYW5hY2F1ZGl0MRwwGgYDVQQDExNjYS5hbmFjYXVkaXQuZ292LmJy
MB4XDTE5MTIxMjE1NTgwMFoXDTIwMTIxMTE2MDMwMFowYjELMAkGA1UEBhMCQlIx
CzAJBgNVBAgTAkRGMREwDwYDVQQKEwhHb0xlZGdlcjEPMA0GA1UECxMGY2xpZW50
MSIwIAYDVQQDExlvcmRlcmVyMC5hbmFjYXVkaXQuZ292LmJyMFkwEwYHKoZIzj0C
AQYIKoZIzj0DAQcDQgAEz/JfB+jWVit4zP9NlugwH7oO/dLZO/BNd1MD5iRfgemy
16pjWVyi1irwY/eaVb+ZV1bIObvuWMp2lBmmfBo5WKOBzzCBzDAOBgNVHQ8BAf8E
BAMCB4AwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUgvy1u5VT4wtz7YGFa0pSOCTv
ep0wHwYDVR0jBBgwFoAUEFeW5DFQ5GGQhO5O+89qBC79qQswbAYIKgMEBQYHCAEE
YHsiYXR0cnMiOnsiaGYuQWZmaWxpYXRpb24iOiIiLCJoZi5FbnJvbGxtZW50SUQi
OiJvcmRlcmVyMC5hbmFjYXVkaXQuZ292LmJyIiwiaGYuVHlwZSI6ImNsaWVudCJ9
fTAKBggqhkjOPQQDAgNHADBEAiA5zNex551i7TG7JRZ7eEaZlkVEkKlbbw1FRzTp
JY/45gIgM3CaPDmi9nTAG84NCJxFZKsDJ4hRQ0ASkCFmR5oBBqw=
-----END CERTIFICATE-----`
func main() {
h := DefaultHSM(context.Background())
// pus a chave privada do certificado no HSM
keyID := "e888efc5f3c343219dddc6d70515f9da"
certByte := []byte(cert)
// Essa função aqui não ta associando porque ta tentando extrair
// a chave publica do certificado como uma RSA, mas é ECDSA
// o certificado "lab_test" que tínhamos usado funciona pq é RSA
pairID, err := h.AssociateCertificateToPrivate(certByte, keyID)
if err != nil {
fmt.Println(err)
return
}
block, _ := pem.Decode(certByte)
err = h.SetSKI(block, "user", pairID)
if err != nil {
fmt.Println(err)
return
}
ret := C.DRemoveObj(h.sess, C.CString(pairID.pubID))
if ret != 0 {
fmt.Printf("Could not remove key, err [%d]", ret)
}
ret = C.DRemoveObj(h.sess, C.CString(pairID.cert))
if ret != 0 {
fmt.Printf("Could not remove key, err [%d]", ret)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment