@@ -292,6 +292,128 @@ func TestNewCertificate(t *testing.T) {
292292 }
293293}
294294
295+ func TestNewCertificateTemplate (t * testing.T ) {
296+ marshal := func (t * testing.T , value interface {}, params string ) []byte {
297+ t .Helper ()
298+ b , err := asn1 .MarshalWithParams (value , params )
299+ assert .NoError (t , err )
300+ return b
301+ }
302+
303+ tpl := `{
304+ "subject": {{ set (toJson .Subject | fromJson) "extraNames" (list (dict "type" "1.2.840.113556.1.4.656" "value" .Token.upn )) | toJson }},
305+ "sans": {{ concat .SANs (list
306+ (dict "type" "dn" "value" ` + "`" + `{"country":"US","organization":"ACME","commonName":"rocket"}` + "`" + `)
307+ (dict "type" "permanentIdentifier" "value" .Token.pi)
308+ (dict "type" "hardwareModuleName" "value" .Insecure.User.hmn)
309+ (dict "type" "upn" "value" .Token.upn)
310+ (dict "type" "1.2.3.4" "value" (printf "int:%s" .Insecure.User.id))
311+ ) | toJson }},
312+ {{- if typeIs "*rsa.PublicKey" .Insecure.CR.PublicKey }}
313+ "keyUsage": ["keyEncipherment", "digitalSignature"],
314+ {{- else }}
315+ "keyUsage": ["digitalSignature"],
316+ {{- end }}
317+ "extKeyUsage": ["serverAuth", "clientAuth"],
318+ "extensions": [
319+ {"id": "1.2.3.4", "value": {{ asn1Enc (first .Insecure.CR.DNSNames) | toJson }}},
320+ {"id": "1.2.3.5", "value": {{ asn1Marshal (first .Insecure.CR.DNSNames) | toJson }}},
321+ {"id": "1.2.3.6", "value": {{ asn1Seq (asn1Enc (first .Insecure.CR.DNSNames)) (asn1Enc "int:123456") | toJson }}},
322+ {"id": "1.2.3.7", "value": {{ asn1Set (asn1Marshal (first .Insecure.CR.DNSNames) "utf8") (asn1Enc "int:123456") | toJson }}}
323+ ]
324+ }`
325+
326+ // Regular sans
327+ sans := []string {"foo.com" , "www.foo.com" , "root@foo.com" }
328+ // Template data
329+ data := CreateTemplateData ("commonName" , sans )
330+ data .SetUserData (map [string ]any {
331+ "id" : "123456" ,
332+ "hmn" : `{"type":"1.2.3.1", "serialNumber": "MTIzNDU2"}` ,
333+ })
334+ data .SetToken (map [string ]any {
335+ "upn" : "foo@upn.com" ,
336+ "pi" : "0123456789" ,
337+ })
338+
339+ iss , issPriv := createIssuerCertificate (t , "issuer" )
340+ cr , priv := createCertificateRequest (t , "commonName" , sans )
341+
342+ cert , err := NewCertificate (cr , WithTemplate (tpl , data ))
343+ require .NoError (t , err )
344+
345+ crt , err := CreateCertificate (cert .GetCertificate (), iss , priv .Public (), issPriv )
346+ require .NoError (t , err )
347+
348+ // Create expected subject
349+ assert .Equal (t , pkix.Name {
350+ CommonName : "commonName" ,
351+ Names : []pkix.AttributeTypeAndValue {
352+ {Type : asn1.ObjectIdentifier {2 , 5 , 4 , 3 }, Value : "commonName" },
353+ {Type : asn1.ObjectIdentifier {1 , 2 , 840 , 113556 , 1 , 4 , 656 }, Value : "foo@upn.com" },
354+ },
355+ }, crt .Subject )
356+
357+ // Create expected SAN extension
358+ var rawValues []asn1.RawValue
359+ for _ , san := range []SubjectAlternativeName {
360+ {Type : DNSType , Value : "foo.com" },
361+ {Type : DNSType , Value : "www.foo.com" },
362+ {Type : EmailType , Value : "root@foo.com" },
363+ {Type : DirectoryNameType , ASN1Value : []byte (`{"country":"US","organization":"ACME","commonName":"rocket"}` )},
364+ {Type : PermanentIdentifierType , Value : "0123456789" },
365+ {Type : HardwareModuleNameType , ASN1Value : []byte (`{"type":"1.2.3.1", "serialNumber": "MTIzNDU2"}` )},
366+ {Type : UPNType , Value : "foo@upn.com" },
367+ {Type : "1.2.3.4" , Value : "int:123456" },
368+ } {
369+ rawValue , err := san .RawValue ()
370+ require .NoError (t , err )
371+ rawValues = append (rawValues , rawValue )
372+ }
373+ rawBytes , err := asn1 .Marshal (rawValues )
374+ require .NoError (t , err )
375+
376+ var found int
377+ for _ , ext := range crt .Extensions {
378+ switch {
379+ case ext .Id .Equal (oidExtensionSubjectAltName ):
380+ assert .Equal (t , pkix.Extension {
381+ Id : oidExtensionSubjectAltName ,
382+ Value : rawBytes ,
383+ }, ext )
384+ case ext .Id .Equal ([]int {1 , 2 , 3 , 4 }):
385+ assert .Equal (t , pkix.Extension {
386+ Id : ext .Id ,
387+ Value : marshal (t , "foo.com" , "printable" ),
388+ }, ext )
389+ case ext .Id .Equal ([]int {1 , 2 , 3 , 5 }):
390+ assert .Equal (t , pkix.Extension {
391+ Id : ext .Id ,
392+ Value : marshal (t , "foo.com" , "" ),
393+ }, ext )
394+ case ext .Id .Equal ([]int {1 , 2 , 3 , 6 }):
395+ assert .Equal (t , pkix.Extension {
396+ Id : ext .Id ,
397+ Value : marshal (t , []any {"foo.com" , 123456 }, "" ),
398+ }, ext )
399+ case ext .Id .Equal ([]int {1 , 2 , 3 , 7 }):
400+ assert .Equal (t , pkix.Extension {
401+ Id : ext .Id ,
402+ Value : marshal (t , struct {
403+ String string `asn1:"utf8"`
404+ Int int
405+ }{"foo.com" , 123456 }, "set" ),
406+ }, ext )
407+ default :
408+ continue
409+ }
410+ found ++
411+ }
412+
413+ assert .Equal (t , 5 , found , "some of the expected extension where not found" )
414+
415+ }
416+
295417func TestNewCertificateFromX509 (t * testing.T ) {
296418 priv , err := ecdsa .GenerateKey (elliptic .P256 (), rand .Reader )
297419 require .NoError (t , err )
0 commit comments