Pour générer un JWT à partir d'un certificat/clé X.509, toute opération (y compris la lecture) sur %SYS.X509Credentials requiert l'autorisation d'accès (U) à la ressource %Admin_Secure. Cette dernière est nécessaire car %SYS.X509Credentials est persistant ; cette implémentation vise à empêcher tout accès non autorisé aux clés privées.
Si la ressource %Admin_Secure n'est pas disponible lors de l'exécution, vous pouvez utiliser la solution de contournement suivante.
Lors de l'examen du code de génération des JWT, j'ai constaté que ce code utilise %SYS.X509Credentials uniquement comme source de données d'exécution pour PrivateKey, PrivateKeyPassword et Certificate. Pour contourner ce problème, vous pouvez utiliser une implémentation non persistante de l'interface X.509, exposant uniquement ces propriétés. Si vous utilisez l'interopérabilité, le certificat/clé privée peut être stocké dans les informations d'identification pour un accès sécurisé.
Class User.X509 Extends %RegisteredObject
{
Property PrivateKey As %VarString
Property PrivateKeyPassword As %String
Property Certificate As %VarString
Property HasPrivateKey As %Boolean [ InitialExpression = {$$$YES} ]
ClassMethod GetX509() As User.X509
{
set x509 = ..%New()
set x509.PrivateKey = ..Key()
set x509.Certificate = ..Cert()
quit x509
}
ClassMethod GetX509FromCredential(credential) As User.X509
{
set credentialObj = ##class(Ens.Config.Credentials).%OpenId(credential,,.sc)
throw:$$$ISERR(sc) ##class(%Exception.StatusException).ThrowIfInterrupt(sc)
set x509 = ..%New()
set x509.PrivateKey = credentialObj.Password
set x509.Certificate = credentialObj.Username
quit x509
}
ClassMethod Key()
{
q "-----BEGIN RSA PRIVATE KEY-----"_$C(13,10)
_"YOUR_TEST_KEY"_$C(13,10)
_"-----END RSA PRIVATE KEY-----"
}
ClassMethod Cert() As %VarString
{
q "-----BEGIN CERTIFICATE-----"_$C(13,10)
_"YOUR_TEST_CERT"_$C(13,10)
_"-----END CERTIFICATE-----"
}
}
Vous pouvez générer un JWT de la manière suivante :
ClassMethod JWT() As %Status
{
Set sc = $$$OK
Set x509 = ##class(User.X509).GetX509()
Set algorithm ="RS256"
Set header = {"alg": (algorithm), "typ": "JWT"}
Set claims= {"Key": "Value" }
#
Set sc = ##class(%Net.JSON.JWK).CreateX509(algorithm,x509,.privateJWK)
If $$$ISERR(sc) {
Write $SYSTEM.OBJ.DisplayError(sc)
}
#
Set sc = ##class(%Net.JSON.JWKS).PutJWK(privateJWK,.privateJWKS)
If $$$ISERR(sc) {
Write $SYSTEM.OBJ.DisplayError(sc)
}
Set sc = ##Class(%Net.JSON.JWT).Create(header,,claims,privateJWKS,,.pJWT)
If $$$ISERR(sc) {
Write $SYSTEM.OBJ.DisplayError(sc)
}
Write pJWT
Return sc
}
Vous pouvez également utiliser un objet dynamique pour éviter la création de classe ; dans ce cas, cela ressemblerait à ceci :
ClassMethod JWT(credential) As %Status
{
Set sc = $$$OK
Set credentialObj = ##class(Ens.Config.Credentials).%OpenId(credential,,.sc)
throw:$$$ISERR(sc) ##class(%Exception.StatusException).ThrowIfInterrupt(sc)
Set x509 = {
"HasPrivateKey": true,
"PrivateKey": (credentialObj.Password),
"PrivateKeyPassword":"",
"Certificate":(credentialObj.Username)
}
Set algorithm ="RS256"
Set header = {"alg": (algorithm), "typ": "JWT"}
Set claims= {"Key": "Value" }
#
Set sc = ##class(%Net.JSON.JWK).CreateX509(algorithm,x509,.privateJWK)
If $$$ISERR(sc) {
Write $SYSTEM.OBJ.DisplayError(sc)
}
#
Set sc = ##class(%Net.JSON.JWKS).PutJWK(privateJWK,.privateJWKS)
If $$$ISERR(sc) {
Write $SYSTEM.OBJ.DisplayError(sc)
}
Set sc = ##Class(%Net.JSON.JWT).Create(header,,claims,privateJWKS,,.pJWT)
If $$$ISERR(sc) {
Write $SYSTEM.OBJ.DisplayError(sc)
}
Write pJWT
Return sc
}