Artículo
· 2 hr atrás Lectura de 3 min

Generando JWT sin acceso a los certificados/keys x509 del sistema

Si queréis generar JWT a partir de un certificado/clave x509, cualquier operación (incluida la lectura) sobre %SYS.X509Credentials requiere el permiso U en el recurso %Admin_Secure. Esto se debe a que %SYS.X509Credentials es persistente y está implementado así para evitar que todos los usuarios tengan acceso a las claves privadas.

Si el recurso %Admin_Secure no está disponible en tiempo de ejecución, podéis usar la siguiente solución alternativa.

Revisándo el código de generación de JWT, descubrí que sólo utiliza %SYS.X509Credentials como fuente de datos en tiempo de ejecución para PrivateKey, PrivateKeyPasswordy Certificate. Como alternativa, podéis usar una implementación no persistente en tiempo de ejecución de la interfaz X.509, exponiendo únicamente estas propiedades.

Si estáis usando interoperabilidad, el certificado/clave privada se puede almacenar en credenciales para un acceso 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-----"
}

}

Y podéis generar JWT de la siguiente manera:

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
}

Alternativamente, podéis usar un objeto dinámico para evitar crear la clase; en ese caso, se vería así:

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
}
Comentarios (0)1
Inicie sesión o regístrese para continuar