Last active
November 19, 2020 07:33
-
-
Save vieirin/05467cc2d9a619fee9f9bc3b12fff56d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
/* | |
#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(¶mObj, 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