简体   繁体   English

如何在CryptoJS中使用私钥(pem)签名JWT?

[英]How to sign a JWT with a private key (pem) in CryptoJS?

I am trying to create a signed JWT in postman with the following code 我正在尝试使用以下代码在邮递员中创建签名的JWT

function base64url(source) {
    // Encode in classical base64
    encodedSource = CryptoJS.enc.Base64.stringify(source);

    // Remove padding equal characters
    encodedSource = encodedSource.replace(/=+$/, '');

    // Replace characters according to base64url specifications
    encodedSource = encodedSource.replace(/\+/g, '-');
    encodedSource = encodedSource.replace(/\//g, '_');

    return encodedSource;
}

function addIAT(request) {
    var iat = Math.floor(Date.now() / 1000) + 257;
    data.iat = iat;
    return data;
}


var header = {
    "typ": "JWT",
    "alg": "HS256"
};

var data = {
    "fname": "name",
    "lname": "name",
    "email": "email@domain.com",
    "password": "abc123$"
};

data = addIAT(data);

var secret = 'myjwtsecret';

// encode header
var stringifiedHeader = CryptoJS.enc.Utf8.parse(JSON.stringify(header));
var encodedHeader = base64url(stringifiedHeader);

// encode data
var stringifiedData = CryptoJS.enc.Utf8.parse(JSON.stringify(data));
var encodedData = base64url(stringifiedData);

// build token
var token = encodedHeader + "." + encodedData;

// sign token
var signature = CryptoJS.HmacSHA256(token, secret);
signature = base64url(signature);
var signedToken = token + "." + signature;

postman.setEnvironmentVariable("payload", signedToken);

Code taken from https://gist.github.com/corbanb/db03150abbe899285d6a86cc480f674d . 来自https://gist.github.com/corbanb/db03150abbe899285d6a86cc480f674d的代码。

I've been trying to input the PEM as the secret but does not work. 我一直在尝试输入PEM作为密码,但是不起作用。 Also can't find any HmacSHA256 overload that takes a PEM. 也找不到任何需要PEM的HmacSHA256重载。

How can that be done? 那怎么办?

The mention of postman changed this. 提到邮递员改变了这一情况。 I have a solution for you, but it's not exactly a clean way by any mean. 我为您提供了一个解决方案,但这绝不是一种干净的方法。

You'll need to create a request that you will need to execute whenever you open postman. 您需要创建一个请求,无论何时打开邮递员,都需要执行该请求。 Go as follows: 进行如下操作:

侧面加载jsrsasign-js

The purpose of this request is to side-load jsrsasign-js and storing it in a global Postman variable. 该请求的目的是侧加载jsrsasign-js并将其存储在全局Postman变量中。

Once this is done, you can then use this content elsewhere. 完成此操作后,您可以在其他地方使用此内容。 For every request you need a RSA256 JWT signature, the following pre-request script will update a variable (here, token ) with the token: 对于每个请求,您都需要一个RSA256 JWT签名,以下预请求脚本将使用token更新变量(此处为token ):

var navigator = {};
var window = {};
eval(pm.globals.get("jsrsasign-js"));

function addIAT(request) {
    var iat = Math.floor(Date.now() / 1000) + 257;
    data.iat = iat;
    return data;
}

var header = {"alg" : "RS256","typ" : "JWT"};
var data = {
    "fname": "name",
    "lname": "name",
    "email": "email@domain.com",
    "password": "abc123$"
};

data = addIAT(data);

var privateKey = "-----BEGIN RSA PRIVATE KEY----- \
MIIBOQIBAAJAcrqH0L91/j8sglOeroGyuKr1ABvTkZj0ATLBcvsA91/C7fipAsOn\
RqRPZr4Ja+MCx0Qvdc6JKXa5tSb51bNwxwIDAQABAkBPzI5LE+DuRuKeg6sLlgrJ\
h5+Bw9kUnF6btsH3R78UUANOk0gGlu9yUkYKUkT0SC9c6HDEKpSqILAUsXdx6SOB\
AiEA1FbR++FJ56CEw1BiP7l1drM9Mr1UVvUp8W71IsoZb1MCIQCKUafDLg+vPj1s\
HiEdrPZ3pvzvteXLSuniH15AKHEuPQIhAIsgB519UysMpXBDbtxJ64jGj8Z6/pOr\
NrwV80/EEz45AiBlgTLZ2w2LjuNIWnv26R0eBZ+M0jHGlD06wcZK0uLsCQIgT1kC\
uNcDTERjwEbFKJpXC8zTLSPcaEOlbiriIKMnpNw=\
-----END RSA PRIVATE KEY-----";

var sHeader = JSON.stringify(header);
var sPayload = JSON.stringify(data);

var sJWT = KJUR.jws.JWS.sign(header.alg, sHeader, sPayload, privateKey);

pm.variables.set('token', sJWT);

In order: - I define mock window and navigator objects as jsrsasign-js needs them. 为了:-我定义了模拟windownavigator对象,因为jsrsasign-js需要它们。 - I then eval() the content of what we fetched earlier in order to rehydrate everything - The rest of your code is simple usage of jsrsasign-js . -然后我eval()我们先前获取的内容,以使所有内容重新水化-其余代码是jsrsasign-js简单用法。 Your token info is there, and I've defined a private key there. 您的令牌信息在那里,并且我在那里定义了私钥。 You can change this or use an environment variable; 您可以更改此设置或使用环境变量。 it's just there for demo purposes. 它只是用于演示目的。 I then simply use the rehydrated library to sign it, and set the variable to the value of the signed JWT. 然后,我仅使用重新水化的库对其进行签名,并将变量设置为已签名的JWT的值。


A PEM , as you refer to it, is a container format specifying a combination of public and/or private key. 如您所指, PEM是一种容器格式,指定了公钥和/或私钥的组合。 You're using it to sign using HMAC-SHA256 , which operates on a shared secret. 您正在使用它通过HMAC-SHA256进行签名,该协议在共享机密上运行。 This obviously isn't going to work (unless you take the poor man's approach and use your public key as the shared secret). 这显然是行不通的(除非您采取穷人的方法,并使用您的公共密钥作为共享密钥)。

Fortunately enough, there are other signature methods defined in the RFCs. 幸运的是,RFC中定义了其他签名方法。 For instance, there is a way to sign using RSA , and a very convenient way of defining a public key as a JSON web key ( JWK ). 例如,有一种使用RSA进行签名的方法,以及一种将公钥定义为JSON Web密钥JWK )的非常方便的方法。 We're going to be leveraging both. 我们将同时利用两者。

I've generated a key pair for testing, they're named out and out.pub . 我已经生成了一个用于测试的密钥对,它们被命名为outout.pub Generation tool is genrsa (and as such, they're an RSA keypair). 生成工具是genrsa (因此,它们是RSA密钥对)。

In order to sign, we're going to have to change a few things: 为了签名,我们将不得不更改一些内容:

  • We're changing algorithms from HS256 to RS256 , as explained above RS256 ,我们正在将算法从HS256更改为RS256
  • We're going to need a new library to do the signing itself, as crypto-js does not support asymmetric key crypto. 我们将需要一个新的库来进行自身签名,因为crypto-js不支持非对称密钥加密。 We'll fall back to the native crypto module, though there are pure-JS alternatives 我们将回到本机crypto模块,尽管有纯JS替代方案

The code: 编码:

var CryptoJS = require("crypto-js");
var keyFileContent = require("fs").readFileSync("./out");
var pubkey = require("fs").readFileSync("./out.pub");
var base64url = require("base64url");
var nJwt = require("njwt");
function addIAT(request) {
    var iat = Math.floor(Date.now() / 1000) + 257;
    data.iat = iat;
    return data;
}


var header = {
    "typ": "JWT",
    "alg": "RS256"
};

var data = {
    "fname": "name",
    "lname": "name",
    "email": "email@domain.com",
    "password": "abc123$"
};

data = addIAT(data);

// encode header
var stringifiedHeader = JSON.stringify(header);
var encodedHeader = base64url(stringifiedHeader);

// encode data
var stringifiedData = JSON.stringify(data);
var encodedData = base64url(stringifiedData);

// build token
var token = encodedHeader + "." + encodedData;

// sign token
var signatureAlg = require("crypto").createSign("sha256");
signatureAlg.update(token);
var signature = signatureAlg.sign(keyFileContent);
signature = base64url(signature);
var signedToken = token + "." + signature;

console.log(signedToken);

// Verify
var verifier = new nJwt.Verifier();
verifier.setSigningAlgorithm('RS256');
verifier.setSigningKey(pubkey);
verifier.verify(signedToken, function() {
  console.log(arguments);
});

And that's it! 就是这样! It's quite literally that simple, although I would not recommend rewriting the sign() function from crypto from scratch. 从字面上讲,这很简单,尽管我不建议您从头开始从crypto重写sign()函数。 Leave it to a library that has had thorough inspection by the community, and crypto is pretty serious business. 将其交给经过社区全面检查的图书馆,而加密是非常严肃的事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM