如何在CryptoJS中使用私钥(pem)对JWT进行签名? [英] How to sign a JWT with a private key (pem) in CryptoJS?

查看:220
本文介绍了如何在CryptoJS中使用私钥(pem)对JWT进行签名?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用以下代码在邮递员中创建一个签名的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);

代码取自 https://gist.github.com/corbanb/db03150abbe899285d6a86cc480f674d . /p>

我一直在尝试输入PEM作为密码,但是不起作用.也找不到任何需要PEM的HmacSHA256重载.

那怎么办?

解决方案

邮递员的提及改变了这一点.我为您提供了一个解决方案,但这绝对不是一种干净的方法.

您将需要创建一个请求,无论何时打开邮递员,都需要执行该请求.进行如下操作:

此请求的目的是侧面加载jsrsasign-js并将其存储在全局Postman变量中.

完成此操作后,您就可以在其他地方使用此内容了.对于每个请求,您都需要RSA256 JWT签名,以下预请求脚本将使用令牌更新变量(此处为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);

顺序: -我定义了模拟windownavigator对象,因为jsrsasign-js需要它们. -然后,我eval()先前获取的内容,以使所有内容重新水化 -您其余的代码是jsrsasign-js的简单用法.您的令牌信息在那里,并且我在那里定义了私钥.您可以更改此设置或使用环境变量.它只是用于演示目的.然后,我仅使用重新水化的库对其进行签名,并将变量设置为已签名的JWT的值.


如您所指,

A PEM是一种容器格式,用于指定公钥和/或私钥的组合.您正在使用它使用HMAC-SHA256进行签名,该文件在共享机密上运行.显然,这是行不通的(除非您采取穷人的方法,并使用您的公共密钥作为共享密钥).

幸运的是,RFC中定义了其他签名方法.例如,有一种使用RSA进行签名的方法,还有一种非常方便的将公钥定义为 JSON Web密钥(JWK).我们将同时利用两者.

我已经生成了一个用于测试的密钥对,它们分别命名为outout.pub.生成工具是genrsa(因此,它们是RSA密钥对).

要签名,我们将不得不更改一些内容:

  • 如上所述,我们正在将算法从HS256更改为RS256
  • 我们将需要一个新的库来进行签名,因为crypto-js不支持非对称密钥加密.我们将回到原生的crypto模块,尽管有纯JS替代方案

代码:

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);
});

就是这样!从字面上讲,这很简单,尽管我不建议从头开始从crypto重写sign()函数.将其交给经过社区全面检查的图书馆,而加密是非常严肃的事情.

I am trying to create a signed JWT in postman with the following code

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 .

I've been trying to input the PEM as the secret but does not work. Also can't find any HmacSHA256 overload that takes a PEM.

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:

The purpose of this request is to side-load jsrsasign-js and storing it in a global Postman variable.

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:

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. - 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. 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.


A PEM, as you refer to it, is a container format specifying a combination of public and/or private key. You're using it to sign using HMAC-SHA256, which operates on a shared secret. 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. 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). We're going to be leveraging both.

I've generated a key pair for testing, they're named out and out.pub. Generation tool is genrsa (and as such, they're an RSA keypair).

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
  • We're going to need a new library to do the signing itself, as crypto-js does not support asymmetric key crypto. We'll fall back to the native crypto module, though there are pure-JS alternatives

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. Leave it to a library that has had thorough inspection by the community, and crypto is pretty serious business.

这篇关于如何在CryptoJS中使用私钥(pem)对JWT进行签名?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆