Skip to content

Commit 6887e94

Browse files
committed
Handle plain RSA public keys gracefully.
1 parent 6d20adc commit 6887e94

File tree

2 files changed

+32
-42
lines changed

2 files changed

+32
-42
lines changed

src/editor/jwt.js

Lines changed: 30 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import jose from 'node-jose';
22
import b64u from 'base64url';
33
import any from 'promise.any';
4+
import { pki } from 'node-forge';
45

56
import log from 'loglevel';
67

@@ -21,6 +22,31 @@ function paddedKey(key, alg, base64Secret) {
2122
return b64u.encode(buf);
2223
}
2324

25+
/*
26+
* This function handles plain RSA keys not wrapped in a
27+
* X.509 SubjectPublicKeyInfo structure. It returns a PEM encoded public key
28+
* wrapper in that structure.
29+
* See: https://stackoverflow.com/questions/18039401/how-can-i-transform-between-the-two-styles-of-public-key-format-one-begin-rsa
30+
* @param {String} publicKey The public key as a PEM string.
31+
* @returns {String} The PEM encoded public key in
32+
* X509 SubjectPublicKeyInfo format.
33+
*/
34+
function plainRsaKeyToX509Key(key) {
35+
try {
36+
const startTag = '-----BEGIN RSA PUBLIC KEY-----';
37+
const endTag = '-----END RSA PUBLIC KEY-----';
38+
const startTagPos = key.indexOf(startTag);
39+
const endTagPos = key.indexOf(endTag);
40+
41+
return startTagPos !== -1 && endTagPos !== -1 ?
42+
pki.publicKeyToPem(pki.publicKeyFromPem(key)) :
43+
key;
44+
} catch(e) {
45+
// If anything fails, it may not be a plain RSA key, so return the same key.
46+
return key;
47+
}
48+
}
49+
2450
function getJoseKey(header, key, base64Secret) {
2551
if(header.alg.indexOf('HS') === 0) {
2652
return jose.JWK.asKey({
@@ -30,6 +56,10 @@ function getJoseKey(header, key, base64Secret) {
3056
k: paddedKey(key, header.alg, base64Secret)
3157
});
3258
} else {
59+
if(header.alg.indexOf('RS') === 0) {
60+
key = plainRsaKeyToX509Key(key);
61+
}
62+
3363
return any(['pem', 'json'].map(form => {
3464
try {
3565
return jose.JWK.asKey(key, form);
@@ -65,45 +95,6 @@ export function sign(header,
6595
);
6696
}
6797

68-
// TODO: reenable this (and tests)
69-
/**
70-
* This function takes a PEM string with a public key and returns a
71-
* jsrsasign key object (RSAKey, KJUR.crypto.DSA, KJUR.crypto.ECDSA). It also
72-
* handles plain RSA keys not wrapped in a X.509 SubjectPublicKeyInfo
73-
* structure.
74-
* See: https://stackoverflow.com/questions/18039401/how-can-i-transform-between-the-two-styles-of-public-key-format-one-begin-rsa
75-
* @param {String} publicKey The public key as a PEM string.
76-
* @returns {Object} The public key as a jsrsasign key object.
77-
* /
78-
function getPublicKeyObject(publicKey) {
79-
try {
80-
const startTag = '-----BEGIN RSA PUBLIC KEY-----';
81-
const endTag = '-----END RSA PUBLIC KEY-----';
82-
const startTagPos = publicKey.indexOf(startTag);
83-
const endTagPos = publicKey.indexOf(endTag);
84-
85-
if(startTagPos !== -1 && endTagPos !== -1) {
86-
const plainDataBase64 =
87-
publicKey.substr(0, endTagPos)
88-
.substr(startTagPos + startTag.length);
89-
90-
const plainDataDER = b64tohex(plainDataBase64);
91-
92-
const barePublicKey = {
93-
n: ASN1HEX.getVbyList(plainDataDER, 0, [0], '02'),
94-
e: ASN1HEX.getVbyList(plainDataDER, 0, [1], '02')
95-
};
96-
97-
return KEYUTIL.getKey(barePublicKey);
98-
}
99-
} catch(e) {
100-
log.error('Failed to make public key into X.509 ' +
101-
'SubjectPublicKeyInfo key:', e);
102-
}
103-
104-
return KEYUTIL.getKey(publicKey);
105-
}*/
106-
10798
export function verify(jwt, secretOrPublicKeyString, base64Secret = false) {
10899
if(!isToken(jwt)) {
109100
return Promise.resolve(false);

test/unit/editor/jwt.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,7 @@ describe('JWT', function() {
188188
});
189189
});
190190

191-
// TODO: reenable this test after converting plain RSA keys.
192-
/*it('verifies tokens (RS256) using a plain RSA public key', function() {
191+
it('verifies tokens (RS256) using a plain RSA public key', function() {
193192
const header = {
194193
alg: 'RS256'
195194
};
@@ -200,7 +199,7 @@ describe('JWT', function() {
200199
return jwt.sign(header, payload, tokens.rs256.privateKey).then(token => {
201200
return jwt.verify(token, publicKeyPlainRSA).should.eventually.be.true;
202201
});
203-
});*/
202+
});
204203

205204
describe('isValidBase64String', function() {
206205
// Generate random data of different sizes.

0 commit comments

Comments
 (0)