Gerando JWT sem acesso aos certificados/chaves x509 do sistema
Se você quiser gerar um JWT a partir de um certificado/chave x509, qualquer operação (inclusive leitura) em %SYS.X509Credentials exige permissão U no recurso %Admin_Secure. O %Admin_Secure é necessário porque %SYS.X509Credentials é persistente e foi implementado dessa forma para impedir que todos os usuários tenham acesso às chaves privadas.
Se o recurso %Admin_Secure não estiver disponível em tempo de execução, você pode usar a seguinte alternativa.
Ao revisar o código de geração de JWT, descobri que o código de JWT utiliza %SYS.X509Credentials apenas como fonte de dados em tempo de execução para PrivateKey, PrivateKeyPassword, e Certificate. Como alternativa, você pode usar uma implementação não persistente da interface X.509 em tempo de execução, expondo apenas essas propriedades.Se você estiver usando interoperabilidade, o certificado/chave privada (Cert/PK) pode ser armazenado em credenciais para acesso seguro.
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
}
/// Get X509 object from credential./// Username is a Cert, Password is a Private KeyClassMethod 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-----"
}
}
E você pode gerar o JWT da seguinte forma:
ClassMethod JWT() As%Status
{
Set sc = $$$OK//Set x509 = ##class(%SYS.X509Credentials).GetByAlias("TempKeyPair")Set x509 = ##class(User.X509).GetX509()
Set algorithm ="RS256"Set header = {"alg": (algorithm), "typ": "JWT"}
Set claims= {"Key": "Value" }
#; create JWKSet sc = ##class(%Net.JSON.JWK).CreateX509(algorithm,x509,.privateJWK)
If$$$ISERR(sc) {
Write$SYSTEM.OBJ.DisplayError(sc)
}
#; Create JWKSSet 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
}Como alternativa, você pode usar um objeto dinâmico para evitar a criação de uma classe; nesse caso, ficaria assim:
ClassMethod JWT(credential) As%Status
{
Set sc = $$$OK//Set x509 = ##class(%SYS.X509Credentials).GetByAlias("TempKeyPair")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" }
#; create JWKSet sc = ##class(%Net.JSON.JWK).CreateX509(algorithm,x509,.privateJWK)
If$$$ISERR(sc) {
Write$SYSTEM.OBJ.DisplayError(sc)
}
#; Create JWKSSet 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
}