@@ -113,8 +113,8 @@ func (c *Client) EnrollPFXV2(ea *EnrollPFXFctArgsV2) (*EnrollResponseV2, error)
113113 var missingFields []string
114114
115115 // TODO: Probably a better way to express these if blocks
116- if ea .Template == "" {
117- missingFields = append (missingFields , "Template" )
116+ if ea .Template == "" && ea . EnrollmentPatternId == 0 {
117+ missingFields = append (missingFields , "Template or EnrollmentPatternId " )
118118 }
119119 if ea .CertificateAuthority == "" {
120120 missingFields = append (missingFields , "CertificateAuthority" )
@@ -151,7 +151,11 @@ func (c *Client) EnrollPFXV2(ea *EnrollPFXFctArgsV2) (*EnrollResponseV2, error)
151151 }
152152 ea .SubjectString = subject
153153 } else {
154- return nil , fmt .Errorf ("subject is required to use enrollpfx(). Please configure either SubjectString or Subject" )
154+ log .Println ("[DEBUG] EnrollPFXV2: Subject is nil checks if there are SANs" )
155+ if ea .SANs == nil || (len (ea .SANs .DNS ) == 0 && len (ea .SANs .URI ) == 0 && len (ea .SANs .IP4 ) == 0 &&
156+ len (ea .SANs .IP6 ) == 0 ) {
157+ return nil , fmt .Errorf ("subject or subject alternative names are required to use enrollpfx(). Please configure either SubjectString or Subject or SANs" )
158+ }
155159 }
156160 }
157161
@@ -191,13 +195,16 @@ func (c *Client) EnrollPFXV2(ea *EnrollPFXFctArgsV2) (*EnrollResponseV2, error)
191195// Returns:
192196// - Leaf certificate
193197// - Certificate chain
198+ // - Raw certificate data (as base64 string, if applicable)
199+ // - Error
194200func (c * Client ) DownloadCertificate (
195201 certId int ,
196202 thumbprint string ,
197203 serialNumber string ,
198204 issuerDn string ,
199205 collectionId int ,
200- ) (* x509.Certificate , []* x509.Certificate , error ) {
206+ certificateFormat string ,
207+ ) (* x509.Certificate , []* x509.Certificate , * string , error ) {
201208 log .Println ("[INFO] Downloading certificate" )
202209
203210 /* The download certificate endpoint requires one of the following to retrieve a cert:
@@ -217,7 +224,7 @@ func (c *Client) DownloadCertificate(
217224 }
218225
219226 if ! validInput {
220- return nil , nil , fmt .Errorf ("certID, thumbprint, or serial number AND issuer DN required to dowload certificate" )
227+ return nil , nil , nil , fmt .Errorf ("certID, thumbprint, or serial number AND issuer DN required to dowload certificate" )
221228 }
222229
223230 payload := & downloadCertificateBody {
@@ -243,11 +250,19 @@ func (c *Client) DownloadCertificate(
243250 }
244251
245252 // Set Keyfactor-specific headers
253+ switch certificateFormat {
254+ case "CER" , "CRT" , "DER" , "PEM" :
255+ // do nothing these are valid formats
256+ break
257+ default :
258+ // if not specified or invalid format then default to P7B
259+ certificateFormat = "P7B"
260+ }
246261 headers := & apiHeaders {
247262 Headers : []StringTuple {
248263 {"x-keyfactor-api-version" , "1" },
249264 {"x-keyfactor-requested-with" , "APIClient" },
250- {"x-certificateformat" , "P7B" },
265+ {"x-certificateformat" , certificateFormat },
251266 },
252267 }
253268
@@ -261,13 +276,13 @@ func (c *Client) DownloadCertificate(
261276
262277 resp , err := c .sendRequest (keyfactorAPIStruct )
263278 if err != nil {
264- return nil , nil , err
279+ return nil , nil , nil , err
265280 }
266281
267282 jsonResp := & downloadCertificateResponse {}
268283 err = json .NewDecoder (resp .Body ).Decode (& jsonResp )
269284 if err != nil {
270- return nil , nil , err
285+ return nil , nil , nil , err
271286 }
272287 //buf, err := base64.StdEncoding.DecodeString(jsonResp.Content)
273288 //if err != nil {
@@ -281,17 +296,17 @@ func (c *Client) DownloadCertificate(
281296
282297 certs , p7bErr := ConvertBase64P7BtoCertificates (jsonResp .Content )
283298 if p7bErr != nil {
284- return nil , nil , p7bErr
299+ return nil , nil , & jsonResp . Content , p7bErr
285300 }
286301
287302 var leaf * x509.Certificate
288303 if len (certs ) > 1 {
289304 //leaf is last cert in chain
290305 leaf = certs [0 ] // First cert in chain is the leaf
291- return leaf , certs , nil
306+ return leaf , certs , & jsonResp . Content , nil
292307 }
293308
294- return certs [0 ], nil , nil
309+ return certs [0 ], nil , & jsonResp . Content , nil
295310}
296311
297312// EnrollCSR takes arguments for EnrollCSRFctArgs to enroll a passed Certificate Signing
@@ -659,7 +674,8 @@ func (c *Client) RecoverCertificate(
659674 issuerDn string ,
660675 password string ,
661676 collectionId int ,
662- ) (interface {}, * x509.Certificate , []* x509.Certificate , error ) {
677+ certificateFormat string ,
678+ ) (interface {}, * x509.Certificate , []* x509.Certificate , * string , error ) {
663679 log .Println ("[DEBUG] Enter RecoverCertificate" )
664680 log .Println ("[INFO] Recovering certificate ID:" , certId )
665681 /* The download certificate endpoint requires one of the following to retrieve a cert:
@@ -669,6 +685,9 @@ func (c *Client) RecoverCertificate(
669685
670686 Check for this input
671687 */
688+ if certificateFormat == "" {
689+ certificateFormat = "PFX"
690+ }
672691 validInput := false
673692 if certId != 0 {
674693 validInput = true
@@ -680,12 +699,12 @@ func (c *Client) RecoverCertificate(
680699
681700 if ! validInput {
682701 log .Println ("[ERROR] RecoverCertificate: certID, thumbprint, or serial number AND issuer DN required to download certificate" )
683- return nil , nil , nil , fmt .Errorf ("certID, thumbprint, or serial number AND issuer DN required to download certificate" )
702+ return nil , nil , nil , nil , fmt .Errorf ("certID, thumbprint, or serial number AND issuer DN required to download certificate" )
684703 }
685704 log .Println ("[DEBUG] RecoverCertificate: Valid input" )
686705
687706 if password == "" {
688- return nil , nil , nil , fmt .Errorf ("password required to recover private key with certificate" )
707+ return nil , nil , nil , nil , fmt .Errorf ("password required to recover private key with certificate" )
689708 }
690709
691710 rca := & recoverCertArgs {
@@ -703,7 +722,7 @@ func (c *Client) RecoverCertificate(
703722 Headers : []StringTuple {
704723 {"x-keyfactor-api-version" , "1" },
705724 {"x-keyfactor-requested-with" , "APIClient" },
706- {"x-certificateformat" , "PFX" },
725+ {"x-certificateformat" , certificateFormat },
707726 },
708727 }
709728
@@ -734,34 +753,152 @@ func (c *Client) RecoverCertificate(
734753 resp , err := c .sendRequest (keyfactorAPIStruct )
735754 if err != nil {
736755 log .Println ("[ERROR] RecoverCertificate: Error recovering certificate from Keyfactor Command" , err .Error ())
737- return nil , nil , nil , err
756+ return nil , nil , nil , nil , err
738757 }
739758
740759 jsonResp := & recoverCertResponse {}
741760 log .Println ("[DEBUG] RecoverCertificate: Decoding response" )
742761 err = json .NewDecoder (resp .Body ).Decode (& jsonResp )
743762 if err != nil {
744763 log .Println ("[ERROR] RecoverCertificate: Error decoding response from Keyfactor Command" , err .Error ())
745- return nil , nil , nil , err
764+ return nil , nil , nil , nil , err
746765 }
747766
748- log .Println ("[DEBUG] RecoverCertificate: Decoding PFX" )
749- pfxDer , err := base64 .StdEncoding .DecodeString (jsonResp .PFX )
750- if err != nil {
751- log .Println ("[ERROR] RecoverCertificate: Error decoding PFX" , err .Error ())
752- return nil , nil , nil , err
767+ switch certificateFormat {
768+ case "PFX" , "pfx" , "pkcs12" , "p12" , "jks" , "JKS" :
769+ log .Println ("[DEBUG] RecoverCertificate: decoding `PFX` response field" )
770+ pfxDer := jsonResp .PFX
771+ if pfxDer == "" {
772+ log .Println ("[ERROR] RecoverCertificate: Error decoding PFX" , err .Error ())
773+ return nil , nil , nil , & pfxDer , fmt .Errorf ("pfx field in response is empty" )
774+ }
775+ log .Println ("[INFO] Recovered certificate successfully" )
776+ log .Println ("[DEBUG] RecoverCertificate returning in PFX format" )
777+ return nil , nil , nil , & pfxDer , nil
778+ case "PEM" , "pem" :
779+ log .Println ("[DEBUG] RecoverCertificate: Decoding PFX" )
780+ pfxDer , dErr := base64 .StdEncoding .DecodeString (jsonResp .PFX )
781+ if dErr != nil {
782+ log .Println ("[ERROR] RecoverCertificate: Error decoding PFX" , dErr .Error ())
783+ return nil , nil , nil , & jsonResp .PFX , dErr
784+ }
785+
786+ log .Println ("[DEBUG] RecoverCertificate: Decoding PFX chain" )
787+ priv , leaf , chain , pErr := pkcs12 .DecodeChain (pfxDer , rca .Password )
788+ if pErr != nil {
789+ log .Println ("[ERROR] RecoverCertificate: Error decoding PFX chain" , pErr .Error ())
790+ return nil , nil , nil , & jsonResp .PFX , pErr
791+ }
792+
793+ log .Println ("[INFO] Recovered certificate successfully" )
794+ log .Println ("[DEBUG] RecoverCertificate: " , leaf , chain )
795+ return priv , leaf , chain , & jsonResp .PFX , nil
796+ default :
797+ log .Println ("[DEBUG] RecoverCertificate: Decoding PFX" )
798+ pfxDer , dErr := base64 .StdEncoding .DecodeString (jsonResp .PFX )
799+ if dErr != nil {
800+ log .Println ("[ERROR] RecoverCertificate: Error decoding PFX" , dErr .Error ())
801+ return nil , nil , nil , & jsonResp .PFX , dErr
802+ }
803+
804+ log .Println ("[DEBUG] RecoverCertificate: Decoding PFX chain" )
805+ priv , leaf , chain , pErr := pkcs12 .DecodeChain (pfxDer , rca .Password )
806+ if pErr != nil {
807+ log .Println ("[ERROR] RecoverCertificate: Error decoding PFX chain" , pErr .Error ())
808+ return nil , nil , nil , & jsonResp .PFX , pErr
809+ }
810+
811+ log .Println ("[INFO] Recovered certificate successfully" )
812+ log .Println ("[DEBUG] RecoverCertificate returning in PEM format" )
813+
814+ var pemCerts []string
815+
816+ // Encode leaf certificate to PEM
817+ pemLeaf := pem .EncodeToMemory (
818+ & pem.Block {
819+ Type : "CERTIFICATE" ,
820+ Bytes : leaf .Raw ,
821+ },
822+ )
823+ pemCerts = append (pemCerts , string (pemLeaf ))
824+
825+ // Encode chain certificates to PEM
826+ for _ , cert := range chain {
827+ pemCert := pem .EncodeToMemory (
828+ & pem.Block {
829+ Type : "CERTIFICATE" ,
830+ Bytes : cert .Raw ,
831+ },
832+ )
833+ pemCerts = append (pemCerts , string (pemCert ))
834+ }
835+
836+ pemData := strings .Join (pemCerts , "\n " )
837+ return priv , leaf , chain , & pemData , nil
838+ }
839+
840+ }
841+
842+ // ChangeCertificateOwnerRole changes the certificate's owner. Users must be in the current owner's role and the new owner's role.
843+ // If removing the owner, leave both NewRoleId and NewRoleName empty in the request.
844+ // Calls PUT /Certificates/{id}/Owner endpoint.
845+ func (c * Client ) ChangeCertificateOwnerRole (
846+ certificateId int ,
847+ req * OwnerRequest ,
848+ params ... * CertificateOwnerChangeParams ,
849+ ) error {
850+ log .Printf ("[INFO] Changing owner of certificate with ID %d in Keyfactor" , certificateId )
851+
852+ // Validate certificate ID
853+ if certificateId <= 0 {
854+ return errors .New ("certificate ID must be a positive integer" )
855+ }
856+
857+ // Set Keyfactor-specific headers
858+ headers := & apiHeaders {
859+ Headers : []StringTuple {
860+ {"x-keyfactor-api-version" , "1" },
861+ {"x-keyfactor-requested-with" , "APIClient" },
862+ {"Content-Type" , "application/json" },
863+ },
864+ }
865+
866+ // Build URL with query parameters
867+ endpoint := fmt .Sprintf ("Certificates/%d/Owner" , certificateId )
868+ var queryParams []string
869+
870+ if len (params ) > 0 && params [0 ] != nil {
871+ param := params [0 ]
872+ if param .CollectionId != nil {
873+ queryParams = append (queryParams , fmt .Sprintf ("collectionId=%d" , * param .CollectionId ))
874+ }
875+ if param .ContainerId != nil {
876+ queryParams = append (queryParams , fmt .Sprintf ("containerId=%d" , * param .ContainerId ))
877+ }
878+ }
879+
880+ if len (queryParams ) > 0 {
881+ endpoint += "?" + strings .Join (queryParams , "&" )
753882 }
754883
755- log .Println ("[DEBUG] RecoverCertificate: Decoding PFX chain" )
756- priv , leaf , chain , err := pkcs12 .DecodeChain (pfxDer , rca .Password )
884+ keyfactorAPIStruct := & request {
885+ Method : "PUT" ,
886+ Endpoint : endpoint ,
887+ Headers : headers ,
888+ Payload : req ,
889+ }
890+
891+ resp , err := c .sendRequest (keyfactorAPIStruct )
757892 if err != nil {
758- log .Println ("[ERROR] RecoverCertificate: Error decoding PFX chain" , err .Error ())
759- return nil , nil , nil , err
893+ return err
760894 }
761895
762- log .Println ("[INFO] Recovered certificate successfully" )
763- log .Println ("[DEBUG] RecoverCertificate: " , leaf , chain )
764- return priv , leaf , chain , nil
896+ // Check if the response indicates success (204 No Content expected)
897+ if resp .StatusCode != http .StatusNoContent {
898+ return fmt .Errorf ("failed to change certificate owner: HTTP %d" , resp .StatusCode )
899+ }
900+
901+ return nil
765902}
766903
767904// createSubject builds the certificate subject string from a passed CertificateSubject argument.
0 commit comments