Artigo
· jan 6 3min de leitura

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 Key
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-----"
}

}

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 JWK
    Set sc = ##class(%Net.JSON.JWK).CreateX509(algorithm,x509,.privateJWK)
    
    If $$$ISERR(sc) {
        Write $SYSTEM.OBJ.DisplayError(sc)
    }

    #; Create JWKS
    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
}

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 JWK
    Set sc = ##class(%Net.JSON.JWK).CreateX509(algorithm,x509,.privateJWK)
    
    If $$$ISERR(sc) {
        Write $SYSTEM.OBJ.DisplayError(sc)
    }

    #; Create JWKS
    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
}
Discussão (0)1
Entre ou crie uma conta para continuar