diff --git a/SETUP.md b/SETUP.md index cf92649..f15470e 100644 --- a/SETUP.md +++ b/SETUP.md @@ -155,7 +155,7 @@ or ###### Gen Root CA Cert -`openssl req -new -x509 -days 3560 -sha512 -extensions v3_ca -engine pkcs11 -keyform engine -key 1601805484:0001 -out softhsm-root-0001.ca.cert.pem -set_serial 5000 +`openssl req -new -x509 -days 3560 -sha512 -extensions v3_ca -engine pkcs11 -keyform engine -key 1601805484:0001 -out softhsm-root-0001.ca.cert.pem -set_serial 5000` ###### Gen Intermediate CA CSR @@ -205,8 +205,15 @@ The signed Intermediate CA is now ready for use with [TESTING](TESTING.md) ##### SafeNet Configuration -Using a SafeNet DPoD account, download the -source yoursafenetdpodpath/setenv needs to be run first. +Using a SafeNet DPoD account, download the installation files. + +All of the following commands need a shell where the DPoD environment has been included using the source command: + +``` +cd /yoursafenetdpodpath +. ./setenv +``` + If you are using an IDE then source this script in a terminal and then start the IDE from the terminal. The unit tests should then work with debug enabled within the IDE. @@ -234,64 +241,96 @@ MODULE_PATH = /yoursafenetpath/libs/64/libCryptoki2.so ##### Signing +``` +...the value of the "id" attribute can contain non-textual data. +This is because the corresponding PKCS#11 "CKA_ID" object attribute can contain arbitrary binary data. +``` + ###### Gen Root and Intermediate CA RSA Keys -`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --keypairgen --key-type rsa:4096 --label RSATestCARootKey0001 --id "0001"` +`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --keypairgen --key-type rsa:4096 --label RSATestCARootKey0001 --id 1` + +`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --keypairgen --key-type rsa:2048 --label RSATestCAInterKey0002 --id 2` + +###### Extract the Root and Intermediate CAs' public keys + +`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --id 1 --type pubkey -r -o safenet-root-01.ca.pub.der` + +`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --id 2 --type pubkey -r -o safenet-inter-02.ca.pub.der` -`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --keypairgen --key-type rsa:2048 --label RSATestCAInterKey0002 --id "0002"` +###### Convert the Root and Intermediate CAs' public keys to PEM format + +`openssl rsa -pubin -inform DER -in ./safenet-root-01.ca.pub.der -out ./safenet-root-01.ca.pub.pem` + +`openssl rsa -pubin -inform DER -in ./safenet-inter-02.ca.pub.der -out ./safenet-inter-02.ca.pub.pem` ###### Gen Root CA Cert -`openssl req -new -x509 -days 7300 -sha512 -extensions v3_ca -engine pkcs11 -keyform engine -key "pkcs11:id=%00%01" -out safenet-root-0001.ca.cert.pem -set_serial 5000` +`openssl req -new -x509 -days 7300 -sha512 -extensions v3_ca -engine pkcs11 -keyform engine -key "pkcs11:id=%01" -out safenet-root-01.ca.cert.pem -set_serial 5000` ###### Gen Intermediate CA CSR -`openssl req -new -sha512 -engine pkcs11 -keyform engine -key "pkcs11:id=%00%02" -out safenet-inter-0002.ca.csr.pem` +`openssl req -new -sha512 -engine pkcs11 -keyform engine -key "pkcs11:id=%02" -out safenet-inter-02.ca.csr.pem` ###### Sign Intermediate CA CSR -`openssl ca -days 3650 -md sha512 -notext -extensions v3_intermediate_ca -engine pkcs11 -keyform engine -keyfile "pkcs11:id=%00%09" -in safenet-inter-0009.ca.csr.pem -out safenet-inter-0009.ca.cert.pem -cert safenet-root-0009.ca.cert.pem -noemailDN` +`openssl ca -days 3650 -md sha512 -notext -extensions v3_intermediate_ca -engine pkcs11 -keyform engine -keyfile "pkcs11:id=%01" -in safenet-inter-02.ca.csr.pem -out safenet-inter-02.ca.cert.pem -cert safenet-root-01.ca.cert.pem -noemailDN` -###### Extract the Intermediate CA's public key +###### Convert to DER -`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --id "0010" --type pubkey -r -o /tmp/safenet-inter.ca.pub.der` +`openssl x509 -in ./safenet-inter-02.ca.cert.pem -outform DER -out safenet-inter-02.ca.cert.der` ###### Gen Root and Intermediate CA ECDSA Keys -`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --keypairgen --key-type EC:secp521r1 --label ECTestCARootKey0015 --id "0015"` +`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --keypairgen --key-type EC:secp521r1 --label ECTestCARootKey03 --id 3` + +`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --keypairgen --key-type EC:secp384r1 --label ECTestCAInterKey04 --id 4` + +###### Extract the Root and Intermediate CAs' public keys + +`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --id 3 --type pubkey -r -o safenet-root-03.ca.pub.der` + +`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --id 4 --type pubkey -r -o safenet-inter-04.ca.pub.der` -`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --keypairgen --key-type EC:secp384r1 --label ECTestCAInterKey0016 --id "0016"` +###### Convert the Root and Intermediate CAs' public keys to PEM format + +`openssl ec -pubin -inform DER -in ./safenet-root-03.ca.pub.der -out ./safenet-root-03.ca.pub.pem` + +`openssl ec -pubin -inform DER -in ./safenet-inter-04.ca.pub.der -out ./safenet-inter-04.ca.pub.pem` ###### Gen Root CA Cert -`openssl req -new -x509 -days 7300 -sha512 -extensions v3_ca -engine pkcs11 -keyform engine -key "pkcs11:id=%00%15" -out safenet-root-0015.ca.cert.pem -set_serial 5010` + +`openssl req -new -x509 -days 7300 -sha512 -extensions v3_ca -engine pkcs11 -keyform engine -key "pkcs11:id=%03" -out safenet-root-03.ca.cert.pem -set_serial 5010` ###### Gen Intermediate CA CSR -`openssl req -new -sha512 -engine pkcs11 -keyform engine -key "pkcs11:id=%00%16" -out safenet-inter-0016.ca.csr.pem` + +`openssl req -new -sha512 -engine pkcs11 -keyform engine -key "pkcs11:id=%04" -out safenet-inter-04.ca.csr.pem` ###### Sign Intermediate CA CSR -`openssl ca -days 3650 -md sha512 -notext -extensions v3_intermediate_ca -engine pkcs11 -keyform engine -keyfile "pkcs11:id=%00%15" -in safenet-inter-0016.ca.csr.pem -out safenet-inter-0016.ca.cert.pem -cert safenet-root-0015.ca.cert.pem -noemailDN` -###### Extract the Intermediate CA's public key -`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --id "0016" --type pubkey -r -o safenet-inter-0016.ca.pub.der` +`openssl ca -days 3650 -md sha512 -notext -extensions v3_intermediate_ca -engine pkcs11 -keyform engine -keyfile "pkcs11:id=%03" -in safenet-inter-04.ca.csr.pem -out safenet-inter-04.ca.cert.pem -cert safenet-root-03.ca.cert.pem -noemailDN` + +###### Convert to DER +`openssl x509 -in ./safenet-inter-04.ca.cert.pem -outform DER -out safenet-inter-04.ca.cert.der` ##### Encryption ###### Create RSA key -`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --keypairgen --key-type rsa:2048 --label RSATestKey0020 --id "0020"` +`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --keypairgen --key-type rsa:2048 --label RSATestKey0020 --id 5` ###### Create EC key -`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --keypairgen --key-type EC:secp384r1 --label ECTestKey0014 --id 30303134` +`pkcs11-tool --module=/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so --login --login-type user --slot 3 --keypairgen --key-type EC:secp384r1 --label ECTestKey0014 --id 6` ###### Encryption test -`openssl pkeyutl -encrypt -engine pkcs11 -keyform engine -inkey "pkcs11:id=0007;type=public;" -in ./test.txt -out ./testsafe.enc` +`openssl pkeyutl -encrypt -engine pkcs11 -keyform engine -inkey "pkcs11:id=%05;type=public;" -in ./test.txt -out ./testsafe.enc` ###### Decryption test -`openssl pkeyutl -decrypt -engine pkcs11 -keyform engine -inkey "pkcs11:id=0007;type=private;" -in ./testsafe.enc -out ./testsafe.dec` +`openssl pkeyutl -decrypt -engine pkcs11 -keyform engine -inkey "pkcs11:id=%05;type=private;" -in ./testsafe.enc -out ./testsafe.dec` -## Entrust nShield +### Entrust nShield ``` openssl_conf = openssl_init @@ -308,22 +347,22 @@ dynamic_path = /usr/lib/x86_64-linux-gnu/engines-1.1/libpkcs11.so MODULE_PATH = /opt/apps/nfast/20201219/bin/libcknfast.so ``` -### Commands +#### Commands -#### nCipher Encryption Test +##### nCipher Encryption Test `openssl pkeyutl -encrypt -engine pkcs11 -keyform engine -inkey "pkcs11:id=%61%02%1f%1f%ed%1e%fc%39%f9%d6%0f%28%9b%d5%5f%e9%78%91%6c%e9;type=public;" -in ./test.txt -out ./testncipher.enc` -#### nCipher Decryption Test +##### nCipher Decryption Test `openssl pkeyutl -decrypt -engine pkcs11 -keyform engine -inkey "pkcs11:id=%61%02%1f%1f%ed%1e%fc%39%f9%d6%0f%28%9b%d5%5f%e9%78%91%6c%e9;type=public;" -in ./testncipher.enc -out ./testncipher.dec` -#### OpenSSL Gen Root CA Cert +##### OpenSSL Gen Root CA Cert `openssl req -new -x509 -days 7300 -sha512 -extensions v3_ca -engine pkcs11 -keyform engine -key "pkcs11:id=%61%02%1f%1f%ed%1e%fc%39%f9%d6%0f%28%9b%d5%5f%e9%78%91%6c%e9;type=public;" -out ncipher-root-0005.ca.cert.pem -set_serial 5001` -#### OpenSSL Gen Intermediate CA CSR +##### OpenSSL Gen Intermediate CA CSR `openssl req -new -sha512 -engine pkcs11 -keyform engine -key "pkcs11:id=%88%d8%42%c8%6f%7a%49%ae%92%be%d6%0f%3b%e7%41%51%94%27%69%86" -out ncipher-inter-0006.ca.csr.pem` -#### OpenSSL Sign Intermediate CA CSR +##### OpenSSL Sign Intermediate CA CSR `openssl ca -days 3650 -md sha512 -notext -extensions v3_intermediate_ca -engine pkcs11 -keyform engine -keyfile "pkcs11:id=%61%02%1f%1f%ed%1e%fc%39%f9%d6%0f%28%9b%d5%5f%e9%78%91%6c%e9" -in ncipher-inter-0006.ca.csr.pem -out ncipher-inter-0006.ca.cert.pem -cert ncipher-root-0005.ca.cert.pem -noemailDN` -#### Extract the Intermediate CA's public key +##### Extract the Intermediate CA's public key `pkcs11-tool --module=/opt/apps/nfast/20201219/bin/libcknfast.so --id "61021f1fed1efc39f9d60f289bd55fe978916ce9" --type pubkey -r -o /tmp/ncipher-inter.ca.pub.der` diff --git a/pkg/pkcs11client/keyconfig.go b/pkg/pkcs11client/keyconfig.go index 1f57139..f175b46 100644 --- a/pkg/pkcs11client/keyconfig.go +++ b/pkg/pkcs11client/keyconfig.go @@ -1,30 +1,236 @@ package pkcs11client import ( + "crypto/elliptic" + "encoding/asn1" "errors" "github.com/miekg/pkcs11" ) type KeyConfig struct { + // CKA_LABEL Label string // CKA_ID doesn't appear to work with SoftHSM - Id string + Id []byte // for CKA_KEY_TYPE Type uint + // CKA_MODULUS BITS only needed for key creation + KeyBits int + // The mechanism will be auto populated but it can be manually set Mechanism []*pkcs11.Mechanism } +type KeyConfigKeyPairTemplate struct { + keyConfig KeyConfig + keyTemplatePrivConfig KeyTemplatePrivConfig + keyTemplatePubConfig KeyTemplatePubConfig + keyTemplateSecurity KeyTemplateSecurity +} + +type KeyTemplatePrivConfig struct { + + // CKA_DECRYPT + IsDecrypt bool + + // CKA_UNWRAP + IsUnwrap bool + + // CKA_SIGN + IsSign bool + + // CKA_DERIVE + IsDerive bool +} + +type KeyTemplatePubConfig struct { + + // CKA_ENCRYPT + IsEncrypt bool + + // CKA_WRAP + IsWrap bool + + // CKA_VERIFY + IsVerify bool + + // CKA_PUBLIC_EXPONTENT + Exponent []byte +} + +type KeyTemplateSecurity struct { + + // CKA_TOKEN token or session object + IsToken bool + + // CKA_PRIVATE requires elevated privileges to report the presence of a key object + IsPrivate bool + + // CKA_SENSITIVE + IsSensitive bool + + // CKA_ALWAYS_SENSITIVE + IsAlwaysSensitive bool + + // CKA_MODIFIABLE + IsModifiable bool + + // CKA_EXTRACTABLE + IsExtractable bool + + // CKA_NEVER_EXTRACTABLE + IsNeverExtractable bool +} + +// an ID and label are needed when creating a key, though when using a key either may be used +func (k *KeyConfig) checkNewKeyIntegrity() bool { + return k.KeyBits > 0 && len(k.Id) > 0 && len(k.Label) > 0 +} + func (k *KeyConfig) appendKeyIdentity(attribs []*pkcs11.Attribute) (fullAttribs []*pkcs11.Attribute, err error) { + found := false + + var extraAttribs []*pkcs11.Attribute + if len(k.Id) > 0 { - fullAttribs = append(attribs, pkcs11.NewAttribute(pkcs11.CKA_ID, k.Id)) - } else if len(k.Label) > 0 { - fullAttribs = append(attribs, pkcs11.NewAttribute(pkcs11.CKA_LABEL, k.Label)) - } else { + extraAttribs = append(extraAttribs, pkcs11.NewAttribute(pkcs11.CKA_ID, k.Id)) + found = true + } + if len(k.Label) > 0 { + extraAttribs = append(extraAttribs, pkcs11.NewAttribute(pkcs11.CKA_LABEL, k.Label)) + found = true + } + if !found { return nil, errors.New("Provide a key id or label") } + fullAttribs = append(attribs, extraAttribs...) + return +} + +func (kp *KeyConfigKeyPairTemplate) appendKeyPairGenParams(attribs []*pkcs11.Attribute) (fullAttribs []*pkcs11.Attribute, err error) { + found := false + + var extraAttribs []*pkcs11.Attribute + + if kp.keyConfig.Type == pkcs11.CKK_RSA { + extraAttribs = append(extraAttribs, pkcs11.NewAttribute(pkcs11.CKA_MODULUS_BITS, kp.keyConfig.KeyBits), + pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, kp.keyTemplatePubConfig.Exponent)) + found = true + } else if kp.keyConfig.Type == pkcs11.CKK_EC || kp.keyConfig.Type == pkcs11.CKK_ECDSA { + + var curve elliptic.Curve + switch kp.keyConfig.KeyBits { + case 224: + curve = elliptic.P224() + case 256: + curve = elliptic.P256() + case 384: + curve = elliptic.P384() + case 521: + curve = elliptic.P521() + default: + return nil, errors.New(ERR_UNSUPPORTEDCURVESIZE) + } + if curveOID, err := asn1.Marshal(curveOIDs[curve.Params().Name]); err != nil { + return nil, errors.New(ERR_UNSUPPORTEDCURVESIZE) + } else { + extraAttribs = append(extraAttribs, pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, curveOID)) + } + found = true + } + if !found { + return nil, errors.New(ERR_UNSUPPORTEDKEYTYPE) + } + fullAttribs = append(attribs, extraAttribs...) + return +} + +func (kp *KeyConfigKeyPairTemplate) GenDefaultKeyPairPrivTemplateForSigning() { + + kp.keyTemplatePrivConfig = KeyTemplatePrivConfig{ + IsDecrypt: false, + IsUnwrap: false, + IsSign: true, + IsDerive: false, + } + +} + +func (kp *KeyConfigKeyPairTemplate) GenDefaultKeyPairPubTemplateForSigning() { + + kp.keyTemplatePubConfig = KeyTemplatePubConfig{ + IsEncrypt: false, + IsWrap: false, + IsVerify: true, + Exponent: []byte{1, 0, 1}, + } +} + +func GenKeyConfigKeyPairTemplate(keyConfig *KeyConfig) KeyConfigKeyPairTemplate { + kp := KeyConfigKeyPairTemplate{} + kp.keyConfig = *keyConfig + return kp +} + +func (kp *KeyConfigKeyPairTemplate) GenDefaultKeyPairTemplateForSigning() { + + kp.GenDefaultKeySecurityTemplate() + kp.GenDefaultKeyPairPrivTemplateForSigning() + kp.GenDefaultKeyPairPubTemplateForSigning() +} + +func (kp *KeyConfigKeyPairTemplate) GenDefaultKeySecurityTemplate() { + kp.keyTemplateSecurity = KeyTemplateSecurity{ + IsToken: true, + IsPrivate: true, + IsSensitive: true, + IsAlwaysSensitive: true, + IsModifiable: false, + IsExtractable: false, + IsNeverExtractable: true, + } +} + +func (kp *KeyConfigKeyPairTemplate) GenKeyPairTemplateAttribs() (privAttribs []*pkcs11.Attribute, pubAttribs []*pkcs11.Attribute, err error) { + + privAttribs = []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY), + pkcs11.NewAttribute(pkcs11.CKA_TOKEN, kp.keyTemplateSecurity.IsToken), + pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, kp.keyTemplateSecurity.IsPrivate), + pkcs11.NewAttribute(pkcs11.CKA_SENSITIVE, kp.keyTemplateSecurity.IsSensitive), + // pkcs11.NewAttribute(pkcs11.CKA_ALWAYS_SENSITIVE, kp.keyTemplateSecurity.IsAlwaysSensitive), + pkcs11.NewAttribute(pkcs11.CKA_DECRYPT, kp.keyTemplatePrivConfig.IsDecrypt), + pkcs11.NewAttribute(pkcs11.CKA_UNWRAP, kp.keyTemplatePrivConfig.IsUnwrap), + pkcs11.NewAttribute(pkcs11.CKA_SIGN, kp.keyTemplatePrivConfig.IsSign), + pkcs11.NewAttribute(pkcs11.CKA_DERIVE, kp.keyTemplatePrivConfig.IsDerive), + pkcs11.NewAttribute(pkcs11.CKA_MODIFIABLE, kp.keyTemplateSecurity.IsModifiable), + pkcs11.NewAttribute(pkcs11.CKA_EXTRACTABLE, kp.keyTemplateSecurity.IsExtractable), + } + privAttribs, err = (kp.keyConfig).appendKeyIdentity(privAttribs) + if err != nil { + return + } + + pubAttribs = []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY), + pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, kp.keyConfig.Type), + pkcs11.NewAttribute(pkcs11.CKA_TOKEN, kp.keyTemplateSecurity.IsToken), + pkcs11.NewAttribute(pkcs11.CKA_ENCRYPT, kp.keyTemplatePubConfig.IsEncrypt), + pkcs11.NewAttribute(pkcs11.CKA_WRAP, kp.keyTemplatePubConfig.IsWrap), + pkcs11.NewAttribute(pkcs11.CKA_VERIFY, kp.keyTemplatePubConfig.IsVerify), + } + + pubAttribs, err = (kp.keyConfig).appendKeyIdentity(pubAttribs) + if err != nil { + return + } + pubAttribs, err = kp.appendKeyPairGenParams(pubAttribs) + if err != nil { + return + } + return } diff --git a/pkg/pkcs11client/keymech.go b/pkg/pkcs11client/keymech.go index 2699d46..80f5afd 100644 --- a/pkg/pkcs11client/keymech.go +++ b/pkg/pkcs11client/keymech.go @@ -55,6 +55,18 @@ func GenSignerMechanismById(mechanismId uint, opts crypto.SignerOpts) ([]*pkcs11 return nil, nil } +func genKeyGenMechanismById(id uint) ([]*pkcs11.Mechanism, error) { + + switch id { + case pkcs11.CKK_RSA: + return []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_KEY_PAIR_GEN, nil)}, nil + case pkcs11.CKK_EC: + return []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_EC_KEY_PAIR_GEN, nil)}, nil + default: + return nil, errors.New(ERR_NOMECHANISMCREATE) + } +} + // mechanisms vs functions: http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/os/pkcs11-curr-v2.40-os.html#_Toc416959967 func genMechanismByIdWithOaepParams(mechanismId uint, hashAlg crypto.Hash) (mechanism []*pkcs11.Mechanism, err error) { switch mechanismId { diff --git a/pkg/pkcs11client/pkcs11client.go b/pkg/pkcs11client/pkcs11client.go index 31b8eff..b6c3603 100644 --- a/pkg/pkcs11client/pkcs11client.go +++ b/pkg/pkcs11client/pkcs11client.go @@ -470,6 +470,28 @@ func (p *Pkcs11Client) GetECDSAPublicKey(object pkcs11.ObjectHandle) (*ecdsa.Pub return pubKey, nil } +// Check the public part of the key exists by label and/or ID +func (p *Pkcs11Client) ExistsPublicKey(keyConfig *KeyConfig) (exists bool, err error) { + attribs := []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY), + } + + var fullAttribs []*pkcs11.Attribute + if fullAttribs, err = (*keyConfig).appendKeyIdentity(attribs); err != nil { + return + } + var objHandles []pkcs11.ObjectHandle + if objHandles, err = p.FindObjects(fullAttribs, 1); err != nil { + return false, nil + } + if len(objHandles) > 0 { + exists = true + } else { + exists = false + } + return +} + func (p *Pkcs11Client) ReadExistsPublicKey(keyConfig *KeyConfig) (publicKey []byte, err error) { attribs := []*pkcs11.Attribute{ @@ -496,66 +518,100 @@ func (p *Pkcs11Client) ReadExistsPublicKey(keyConfig *KeyConfig) (publicKey []by } return nil, errors.New("GetAttributeValue error") - //pKey := rsa.PublicKey{N: n, E: int(e)} +} + +// Fetch the key handles if exist +func (p *Pkcs11Client) FetchKeyPairHandles(keyConfig *KeyConfig) (privKeyHandle *[]pkcs11.ObjectHandle, pubKeyHandle *[]pkcs11.ObjectHandle, err error) { - //return nil + pubAttribs := []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY), + } + + var fullPubAttribs []*pkcs11.Attribute + if fullPubAttribs, err = (*keyConfig).appendKeyIdentity(pubAttribs); err != nil { + return + } + + var pubObjHandles []pkcs11.ObjectHandle + if pubObjHandles, err = p.FindObjects(fullPubAttribs, 1); err != nil { + return + } + pubKeyHandle = &pubObjHandles + + privAttribs := []*pkcs11.Attribute{ + pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY), + } + + var fullPrivAttribs []*pkcs11.Attribute + if fullPrivAttribs, err = (*keyConfig).appendKeyIdentity(privAttribs); err != nil { + return + } + + var privObjHandles []pkcs11.ObjectHandle + if privObjHandles, err = p.FindObjects(fullPrivAttribs, 1); err != nil { + return + } + privKeyHandle = &privObjHandles + return } -// getPublicKeyID looks up the given public key in the PKCS#11 token, and -// returns its ID as a []byte, for use in looking up the corresponding private -// key. -/*func (p *Pkcs11Client) GetPublicKey(label string, publicKey crypto.PublicKey) ([]byte, error) { +// first see if the key already exists, whether identified by ID or by LABEL +func (p *Pkcs11Client) CheckExistsCreateKeyPair(keyConfig *KeyConfig) error { - var template []*pkcs11.Attribute - switch key := publicKey.(type) { - case *rsa.PublicKey: - template = []*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY), - pkcs11.NewAttribute(pkcs11.CKA_LABEL, []byte(label)), - pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, pkcs11.CKK_RSA), - pkcs11.NewAttribute(pkcs11.CKA_MODULUS, key.N.Bytes()), - pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, big.NewInt(int64(key.E)).Bytes()), - } - case *ecdsa.PublicKey: - // http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/os/pkcs11-curr-v2.40-os.html#_ftn1 - // PKCS#11 v2.20 specified that the CKA_EC_POINT was to be store in a DER-encoded - // OCTET STRING. - rawValue := asn1.RawValue{ - Tag: 4, // in Go 1.6+ this is asn1.TagOctetString - Bytes: elliptic.Marshal(key.Curve, key.X, key.Y), - } - marshalledPoint, err := asn1.Marshal(rawValue) - if err != nil { - return nil, err - } - curveOID, err := asn1.Marshal(curveOIDs[key.Curve.Params().Name]) - if err != nil { - return nil, err - } - template = []*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY), - pkcs11.NewAttribute(pkcs11.CKA_LABEL, []byte(label)), - pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, pkcs11.CKK_EC), - pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, curveOID), - pkcs11.NewAttribute(pkcs11.CKA_EC_POINT, marshalledPoint), + if !keyConfig.checkNewKeyIntegrity() { + return errors.New(ERR_NEWKEYINTEGRITY) + } + + if exists, err := p.ExistsPublicKey(keyConfig); err != nil || exists { + if exists { + return errors.New(ERR_NEWKEYALREADYEXISTS) + } else { + return err } - default: - return nil, fmt.Errorf("unsupported public key of type %T", publicKey) } + return p.createKeyPair(keyConfig) +} - publicKeyHandle, err := s.FindObject(template) +// No existence check here, which means a new key can be created with the same label but a different ID +func (p *Pkcs11Client) CreateKeyPair(keyConfig *KeyConfig) error { + + if !keyConfig.checkNewKeyIntegrity() { + return errors.New(ERR_NEWKEYALREADYEXISTS) + } + return p.createKeyPair(keyConfig) +} + +// https://github.com/ThalesIgnite/crypto11/blob/cloudhsm/rsa.go#L83 +func (p *Pkcs11Client) createKeyPair(keyConfig *KeyConfig) error { + + keyTemplates := GenKeyConfigKeyPairTemplate(keyConfig) + keyTemplates.GenDefaultKeyPairTemplateForSigning() + privAttribs, pubAttribs, err := keyTemplates.GenKeyPairTemplateAttribs() if err != nil { - return nil, err + return err } - attrs, err := s.Module.GetAttributeValue(p.session, publicKeyHandle, []*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_ID, nil), - }) + mech, err := genKeyGenMechanismById(keyConfig.Type) + if err != nil { - return nil, err + return err } - if len(attrs) == 1 && attrs[0].Type == pkcs11.CKA_ID { - return attrs[0].Value, nil + if _, _, err = p.context.GenerateKeyPair(p.session, mech, pubAttribs, privAttribs); err != nil { + return err + } + return nil +} + +func (p *Pkcs11Client) DeleteKeyPair(keyConfig *KeyConfig) (err error) { + + privKeyHandle, pubKeyHandle, err := p.FetchKeyPairHandles(keyConfig) + + if pubKeyHandle != nil && len(*pubKeyHandle) > 0 { + err = p.context.DestroyObject(p.session, (*pubKeyHandle)[0]) } - return nil, fmt.Errorf("invalid result from GetAttributeValue") -}*/ + if privKeyHandle != nil && len(*privKeyHandle) > 0 { + err = p.context.DestroyObject(p.session, (*privKeyHandle)[0]) + } + + return +} diff --git a/pkg/pkcs11client/pkcs11client_test.go b/pkg/pkcs11client/pkcs11client_test.go index 27ac0c1..a1a75db 100644 --- a/pkg/pkcs11client/pkcs11client_test.go +++ b/pkg/pkcs11client/pkcs11client_test.go @@ -9,6 +9,7 @@ import ( "github.com/miekg/pkcs11" "github.com/rs/zerolog/log" "github.com/stretchr/testify/assert" + "math/rand" "testing" ) @@ -17,21 +18,39 @@ var pkcs11Client Pkcs11Client // test signing var caFiles = CASigningRequest{ csrFile: "../../data/localhost512.csr.der", - caPubkeyFile: "../../data/softhsm-inter-0002.ca.pub.pem", - caCertFile: "../../data/softhsm-inter-0002.ca.cert.der", + caPubkeyFile: "../../data/softhsm-inter-0002.ca.pub.pem", // softhsm inter CA pubkey + //caPubkeyFile: "../../data/safenet-inter-04.ca.pub.pem", // safenet inter CA pubkey + caCertFile: "../../data/softhsm-inter-0002.ca.cert.der", // softhsm inter CA cert + //caCertFile: "../../data/safenet-inter-04.ca.cert.der", // safenet inter CA cert } +// test signing key +const keyLabelForSigning = "RSATestCAInterKey0002" // softhsm test CA +//const keyLabelForSigning= "ECTestCAInterKey04" // safenet test CA + +// test signing hash algo +const keySigningAlgo = x509.SHA512WithRSA // softhsm RSA key +//const keySigningAlgo = x509.ECDSAWithSHA512 // safenet EC key + // test encryption var keyConfig = KeyConfig{Label: "RSATestKey0020", Type: pkcs11.CKK_RSA} func init() { - pkcs11Client.HsmConfig = &HsmConfig{ + /* pkcs11Client.HsmConfig = &HsmConfig{ Lib: "/opt/server/softhsm/current/lib/softhsm/libsofthsm2.so", SlotId: 288648064, Pin: "1234", ConnectTimeoutS: 10, ReadTimeoutS: 30, + }*/ + + pkcs11Client.HsmConfig = &HsmConfig{ + Lib: "/opt/apps/safenet/dpod/current/libs/64/libCryptoki2.so", + SlotId: 3, + Pin: "9e9515e556bd995e", + ConnectTimeoutS: 10, + ReadTimeoutS: 30, } } @@ -75,11 +94,11 @@ func TestCASigner(t *testing.T) { log.Info().Msgf("Loaded CA pubkey") // with E=%d", caPubKey.E) var caSigner HsmSigner - caSigner.Serial = 4128 + caSigner.Serial = int64(rand.Uint64()) caSigner.PublicKey = caPubKey - caSigner.KeyConfig.Label = "RSATestCAInterKey0002" + caSigner.KeyConfig.Label = keyLabelForSigning caSigner.Pkcs11Client = &pkcs11Client - caSigner.SignatureAlgo = x509.SHA512WithRSA //ECDSAWithSHA512 + caSigner.SignatureAlgo = keySigningAlgo if signedCsr, err := GenSignedCert(csr, caCert, &caSigner); err != nil { t.Fatal(err) @@ -176,3 +195,30 @@ func TestEncryptThenDecrypt(t *testing.T) { } } } + +func TestCreateRSAKeyPair(t *testing.T) { + registerTest(t) + + if err := pkcs11Client.CheckExistsCreateKeyPair( + &KeyConfig{Label: "testkeytest6", Id: []byte{31}, Type: pkcs11.CKK_RSA, KeyBits: 2048}); err != nil { + t.Error(err) + } +} + +func TestCreateECKeyPair(t *testing.T) { + registerTest(t) + + if err := pkcs11Client.CheckExistsCreateKeyPair( + &KeyConfig{Label: "testkey42", Id: []byte{42}, Type: pkcs11.CKK_EC, KeyBits: 521}); err != nil { + t.Error(err) + } +} + +func TestDeleteKeyPair(t *testing.T) { + registerTest(t) + + if err := pkcs11Client.DeleteKeyPair( + &KeyConfig{Label: "testkey42", Type: pkcs11.CKK_EC}); err != nil { + t.Error(err) + } +} diff --git a/pkg/pkcs11client/pkcs11const.go b/pkg/pkcs11client/pkcs11const.go index 2ff7e45..ad88557 100644 --- a/pkg/pkcs11client/pkcs11const.go +++ b/pkg/pkcs11client/pkcs11const.go @@ -11,12 +11,20 @@ const ( CKM_EDDSA = (pkcs11.CKM_VENDOR_DEFINED + 0xC03) // ed25519 sign/verify CKK_EC_EDWARDS = (pkcs11.CKK_VENDOR_DEFINED + 0x12) + + ERR_NEWKEYINTEGRITY = "For new keys check that all of KeyBits, ID and Label are set" + ERR_NEWKEYALREADYEXISTS = "Key already exists" + ERR_NOMECHANISMCREATE = "Unable to find a key mechanism for key creation" + ERR_UNSUPPORTEDKEYTYPE = "Unsupported key type. Please use CKK_RSA or CKK_EC" + ERR_UNSUPPORTEDCURVESIZE = "No curve for key bit size" ) -// https://tools.ietf.org/html/rfc5759#section-3.2 +// https://tools.ietf.org/html/rfc5480 Appendix A p.19 var curveOIDs = map[string]asn1.ObjectIdentifier{ + "P-224": {1, 3, 132, 0, 33}, "P-256": {1, 2, 840, 10045, 3, 1, 7}, "P-384": {1, 3, 132, 0, 34}, + "P-521": {1, 3, 132, 0, 35}, } // https://github.com/letsencrypt/boulder/blob/release-2021-02-08/pkcs11helpers/helpers.go#L208