You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
573 lines
25 KiB
573 lines
25 KiB
/*
|
|
* Copyright (c) 2015 Eric Wilde.
|
|
* Copyright 1998-2015 David Shapiro.
|
|
*
|
|
* RSA.js is a suite of routines for performing RSA public-key computations
|
|
* in JavaScript. The cryptographic functions herein are used for encoding
|
|
* and decoding strings to be sent over unsecure channels.
|
|
*
|
|
* To use these routines, a pair of public/private keys is created through a
|
|
* number of means (OpenSSL tools on Linux/Unix, Dave Shapiro's
|
|
* RSAKeyGenerator program on Windows). These keys are passed to RSAKeyPair
|
|
* as hexadecimal strings to create an encryption key object. This key object
|
|
* is then used with encryptedString to encrypt blocks of plaintext using the
|
|
* public key. The resulting cyphertext blocks can be decrypted with
|
|
* decryptedString.
|
|
*
|
|
* Note that the cryptographic functions herein are complementary to those
|
|
* found in CryptoFuncs.php and CryptoFuncs.pm. Hence, encrypted messages may
|
|
* be sent between programs written in any of those languages. The most
|
|
* useful, of course is to send messages encrypted by a Web page using RSA.js
|
|
* to a PHP or Perl script running on a Web servitron.
|
|
*
|
|
* Also, the optional padding flag may be specified on the call to
|
|
* encryptedString, in which case blocks of cyphertext that are compatible
|
|
* with real crypto libraries such as OpenSSL or Microsoft will be created.
|
|
* These blocks of cyphertext can then be sent to Web servitron that uses one
|
|
* of these crypto libraries for decryption. This allows messages encrypted
|
|
* with longer keys to be decrypted quickly on the Web server as well as
|
|
* making for more secure communications when a padding algorithm such as
|
|
* PKCS1v1.5 is used.
|
|
*
|
|
* These routines require BigInt.js and Barrett.js.
|
|
*/
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Modifications
|
|
* -------------
|
|
*
|
|
* 2014 Jan 11 E. Wilde Add optional padding flag to encryptedString
|
|
* for compatibility with real crypto libraries
|
|
* such as OpenSSL or Microsoft. Add PKCS1v1.5
|
|
* padding.
|
|
*
|
|
* 2015 Jan 5 D. Shapiro Add optional encoding flag for encryptedString
|
|
* and encapsulate padding and encoding constants
|
|
* in RSAAPP object.
|
|
*
|
|
* Original Code
|
|
* -------------
|
|
*
|
|
* Copyright 1998-2005 David Shapiro.
|
|
*
|
|
* You may use, re-use, abuse, copy, and modify this code to your liking, but
|
|
* please keep this header.
|
|
*
|
|
* Thanks!
|
|
*
|
|
* Dave Shapiro
|
|
* dave@ohdave.com
|
|
*/
|
|
|
|
/*****************************************************************************/
|
|
|
|
var RSAAPP = {};
|
|
|
|
RSAAPP.NoPadding = "NoPadding";
|
|
RSAAPP.PKCS1Padding = "PKCS1Padding";
|
|
RSAAPP.RawEncoding = "RawEncoding";
|
|
RSAAPP.NumericEncoding = "NumericEncoding"
|
|
|
|
/*****************************************************************************/
|
|
|
|
function RSAKeyPair(encryptionExponent, decryptionExponent, modulus, keylen)
|
|
/*
|
|
* encryptionExponent The encryption exponent (i.e. public
|
|
* encryption key) to be used for
|
|
* encrypting messages. If you aren't
|
|
* doing any encrypting, a dummy
|
|
* exponent such as "10001" can be
|
|
* passed.
|
|
*
|
|
* decryptionExponent The decryption exponent (i.e. private
|
|
* decryption key) to be used for
|
|
* decrypting messages. If you aren't
|
|
* doing any decrypting, a dummy
|
|
* exponent such as "10001" can be
|
|
* passed.
|
|
*
|
|
* modulus The modulus to be used both for
|
|
* encrypting and decrypting messages.
|
|
*
|
|
* keylen The optional length of the key, in
|
|
* bits. If omitted, RSAKeyPair will
|
|
* attempt to derive a key length (but,
|
|
* see the notes below).
|
|
*
|
|
* returns The "new" object creator returns an
|
|
* instance of a key object that can be
|
|
* used to encrypt/decrypt messages.
|
|
*
|
|
* This routine is invoked as the first step in the encryption or decryption
|
|
* process to take the three numbers (expressed as hexadecimal strings) that
|
|
* are used for RSA asymmetric encryption/decryption and turn them into a key
|
|
* object that can be used for encrypting and decrypting.
|
|
*
|
|
* The key object is created thusly:
|
|
*
|
|
* RSAKey = new RSAKeyPair("ABC12345", 10001, "987654FE");
|
|
*
|
|
* or:
|
|
*
|
|
* RSAKey = new RSAKeyPair("ABC12345", 10001, "987654FE", 64);
|
|
*
|
|
* Note that RSAKeyPair will try to derive the length of the key that is being
|
|
* used, from the key itself. The key length is especially useful when one of
|
|
* the padding options is used and/or when the encrypted messages created by
|
|
* the routine encryptedString are exchanged with a real crypto library such
|
|
* as OpenSSL or Microsoft, as it determines how many padding characters are
|
|
* appended.
|
|
*
|
|
* Usually, RSAKeyPair can determine the key length from the modulus of the
|
|
* key but this doesn't always work properly, depending on the actual value of
|
|
* the modulus. If you are exchanging messages with a real crypto library,
|
|
* such as OpenSSL or Microsoft, that depends on the fact that the blocks
|
|
* being passed to it are properly padded, you'll want the key length to be
|
|
* set properly. If that's the case, of if you just want to be sure, you
|
|
* should specify the key length that you used to generated the key, in bits
|
|
* when this routine is invoked.
|
|
*/
|
|
{
|
|
/*
|
|
* Convert from hexadecimal and save the encryption/decryption exponents and
|
|
* modulus as big integers in the key object.
|
|
*/
|
|
this.e = biFromHex(encryptionExponent);
|
|
this.d = biFromHex(decryptionExponent);
|
|
this.m = biFromHex(modulus);
|
|
/*
|
|
* Using big integers, we can represent two bytes per element in the big
|
|
* integer array, so we calculate the chunk size as:
|
|
*
|
|
* chunkSize = 2 * (number of digits in modulus - 1)
|
|
*
|
|
* Since biHighIndex returns the high index, not the number of digits, the
|
|
* number 1 has already been subtracted from its answer.
|
|
*
|
|
* However, having said all this, "User Knows Best". If our caller passes us
|
|
* a key length (in bits), we'll treat it as gospel truth.
|
|
*/
|
|
if (typeof(keylen) != 'number') { this.chunkSize = 2 * biHighIndex(this.m); }
|
|
else { this.chunkSize = keylen / 8; }
|
|
|
|
this.radix = 16;
|
|
/*
|
|
* Precalculate the stuff used for Barrett modular reductions.
|
|
*/
|
|
this.barrett = new BarrettMu(this.m);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
function encryptedString(key, s, pad, encoding)
|
|
/*
|
|
* key The previously-built RSA key whose
|
|
* public key component is to be used to
|
|
* encrypt the plaintext string.
|
|
*
|
|
* s The plaintext string that is to be
|
|
* encrypted, using the RSA assymmetric
|
|
* encryption method.
|
|
*
|
|
* pad The optional padding method to use
|
|
* when extending the plaintext to the
|
|
* full chunk size required by the RSA
|
|
* algorithm. To maintain compatibility
|
|
* with other crypto libraries, the
|
|
* padding method is described by a
|
|
* string. The default, if not
|
|
* specified is "OHDave". Here are the
|
|
* choices:
|
|
*
|
|
* OHDave - this is the original
|
|
* padding method employed by Dave
|
|
* Shapiro and Rob Saunders. If
|
|
* this method is chosen, the
|
|
* plaintext can be of any length.
|
|
* It will be padded to the correct
|
|
* length with zeros and then broken
|
|
* up into chunks of the correct
|
|
* length before being encrypted.
|
|
* The resultant cyphertext blocks
|
|
* will be separated by blanks.
|
|
*
|
|
* Note that the original code by
|
|
* Dave Shapiro reverses the byte
|
|
* order to little-endian, as the
|
|
* plaintext is encrypted. If
|
|
* either these JavaScript routines
|
|
* or one of the complementary
|
|
* PHP/Perl routines derived from
|
|
* this code is used for decryption,
|
|
* the byte order will be reversed
|
|
* again upon decryption so as to
|
|
* come out correctly.
|
|
*
|
|
* Also note that this padding
|
|
* method is claimed to be less
|
|
* secure than PKCS1Padding.
|
|
*
|
|
* NoPadding - this method truncates
|
|
* the plaintext to the length of
|
|
* the RSA key, if it is longer. If
|
|
* its length is shorter, it is
|
|
* padded with zeros. In either
|
|
* case, the plaintext string is
|
|
* reversed to preserve big-endian
|
|
* order before it is encrypted to
|
|
* maintain compatibility with real
|
|
* crypto libraries such as OpenSSL
|
|
* or Microsoft. When the
|
|
* cyphertext is to be decrypted
|
|
* by a crypto library, the
|
|
* library routine's RSAAPP.NoPadding
|
|
* flag, or its equivalent, should
|
|
* be used.
|
|
*
|
|
* Note that this padding method is
|
|
* claimed to be less secure than
|
|
* PKCS1Padding.
|
|
*
|
|
* PKCS1Padding - the PKCS1v1.5
|
|
* padding method (as described in
|
|
* RFC 2313) is employed to pad the
|
|
* plaintext string. The plaintext
|
|
* string must be no longer than the
|
|
* length of the RSA key minus 11,
|
|
* since PKCS1v1.5 requires 3 bytes
|
|
* of overhead and specifies a
|
|
* minimum pad of 8 bytes. The
|
|
* plaintext string is padded with
|
|
* randomly-generated bytes and then
|
|
* its order is reversed to preserve
|
|
* big-endian order before it is
|
|
* encrypted to maintain
|
|
* compatibility with real crypto
|
|
* libraries such as OpenSSL or
|
|
* Microsoft. When the cyphertext
|
|
* is to be decrypted by a crypto
|
|
* library, the library routine's
|
|
* RSAAPP.PKCS1Padding flag, or its
|
|
* equivalent, should be used.
|
|
*
|
|
* encoding The optional encoding scheme to use
|
|
* for the return value. If ommitted,
|
|
* numeric encoding will be used.
|
|
*
|
|
* RawEncoding - The return value
|
|
* is given as its raw value.
|
|
* This is the easiest method when
|
|
* interoperating with server-side
|
|
* OpenSSL, as no additional conversion
|
|
* is required. Use the constant
|
|
* RSAAPP.RawEncoding for this option.
|
|
*
|
|
* NumericEncoding - The return value
|
|
* is given as a number in hexadecimal.
|
|
* Perhaps useful for debugging, but
|
|
* will need to be translated back to
|
|
* its raw equivalent (e.g. using
|
|
* PHP's hex2bin) before using with
|
|
* OpenSSL. Use the constant
|
|
* RSAAPP.NumericEncoding for this option.
|
|
*
|
|
* returns The cyphertext block that results
|
|
* from encrypting the plaintext string
|
|
* s with the RSA key.
|
|
*
|
|
* This routine accepts a plaintext string that is to be encrypted with the
|
|
* public key component of the previously-built RSA key using the RSA
|
|
* assymmetric encryption method. Before it is encrypted, the plaintext
|
|
* string is padded to the same length as the encryption key for proper
|
|
* encryption.
|
|
*
|
|
* Depending on the padding method chosen, an optional header with block type
|
|
* is prepended, the plaintext is padded using zeros or randomly-generated
|
|
* bytes, and then the plaintext is possibly broken up into chunks.
|
|
*
|
|
* Note that, for padding with zeros, this routine was altered by Rob Saunders
|
|
* (rob@robsaunders.net). The new routine pads the string after it has been
|
|
* converted to an array. This fixes an incompatibility with Flash MX's
|
|
* ActionScript.
|
|
*
|
|
* The various padding schemes employed by this routine, and as presented to
|
|
* RSA for encryption, are shown below. Note that the RSA encryption done
|
|
* herein reverses the byte order as encryption is done:
|
|
*
|
|
* Plaintext In
|
|
* ------------
|
|
*
|
|
* d5 d4 d3 d2 d1 d0
|
|
*
|
|
* OHDave
|
|
* ------
|
|
*
|
|
* d5 d4 d3 d2 d1 d0 00 00 00 /.../ 00 00 00 00 00 00 00 00
|
|
*
|
|
* NoPadding
|
|
* ---------
|
|
*
|
|
* 00 00 00 00 00 00 00 00 00 /.../ 00 00 d0 d1 d2 d3 d4 d5
|
|
*
|
|
* PKCS1Padding
|
|
* ------------
|
|
*
|
|
* d0 d1 d2 d3 d4 d5 00 p0 p1 /.../ p2 p3 p4 p5 p6 p7 02 00
|
|
* \------------ ------------/
|
|
* \/
|
|
* Minimum 8 bytes pad length
|
|
*/
|
|
{
|
|
var a = new Array(); // The usual Alice and Bob stuff
|
|
var sl = s.length; // Plaintext string length
|
|
var i, j, k; // The usual Fortran index stuff
|
|
var padtype; // Type of padding to do
|
|
var encodingtype; // Type of output encoding
|
|
var rpad; // Random pad
|
|
var al; // Array length
|
|
var result = ""; // Cypthertext result
|
|
var block; // Big integer block to encrypt
|
|
var crypt; // Big integer result
|
|
var text; // Text result
|
|
/*
|
|
* Figure out the padding type.
|
|
*/
|
|
if (typeof(pad) == 'string') {
|
|
if (pad == RSAAPP.NoPadding) { padtype = 1; }
|
|
else if (pad == RSAAPP.PKCS1Padding) { padtype = 2; }
|
|
else { padtype = 0; }
|
|
}
|
|
else { padtype = 0; }
|
|
/*
|
|
* Determine encoding type.
|
|
*/
|
|
if (typeof(encoding) == 'string' && encoding == RSAAPP.RawEncoding) {
|
|
encodingtype = 1;
|
|
}
|
|
else { encodingtype = 0; }
|
|
|
|
/*
|
|
* If we're not using Dave's padding method, we need to truncate long
|
|
* plaintext blocks to the correct length for the padding method used:
|
|
*
|
|
* NoPadding - key length
|
|
* PKCS1Padding - key length - 11
|
|
*/
|
|
if (padtype == 1) {
|
|
if (sl > key.chunkSize) { sl = key.chunkSize; }
|
|
}
|
|
else if (padtype == 2) {
|
|
if (sl > (key.chunkSize-11)) { sl = key.chunkSize - 11; }
|
|
}
|
|
/*
|
|
* Convert the plaintext string to an array of characters so that we can work
|
|
* with individual characters.
|
|
*
|
|
* Note that, if we're talking to a real crypto library at the other end, we
|
|
* reverse the plaintext order to preserve big-endian order.
|
|
*/
|
|
i = 0;
|
|
|
|
if (padtype == 2) { j = sl - 1; }
|
|
else { j = key.chunkSize - 1; }
|
|
|
|
while (i < sl) {
|
|
if (padtype) { a[j] = s.charCodeAt(i); }
|
|
else { a[i] = s.charCodeAt(i); }
|
|
|
|
i++; j--;
|
|
}
|
|
/*
|
|
* Now is the time to add the padding.
|
|
*
|
|
* If we're doing PKCS1v1.5 padding, we pick up padding where we left off and
|
|
* pad the remainder of the block. Otherwise, we pad at the front of the
|
|
* block. This gives us the correct padding for big-endian blocks.
|
|
*
|
|
* The padding is either a zero byte or a randomly-generated non-zero byte.
|
|
*/
|
|
if (padtype == 1) { i = 0; }
|
|
|
|
j = key.chunkSize - (sl % key.chunkSize);
|
|
|
|
while (j > 0) {
|
|
if (padtype == 2) {
|
|
rpad = Math.floor(Math.random() * 256);
|
|
|
|
while (!rpad) { rpad = Math.floor(Math.random() * 256); }
|
|
|
|
a[i] = rpad;
|
|
}
|
|
else { a[i] = 0; }
|
|
|
|
i++; j--;
|
|
}
|
|
/*
|
|
* For PKCS1v1.5 padding, we need to fill in the block header.
|
|
*
|
|
* According to RFC 2313, a block type, a padding string, and the data shall
|
|
* be formatted into the encryption block:
|
|
*
|
|
* EncrBlock = 00 || BlockType || PadString || 00 || Data
|
|
*
|
|
* The block type shall be a single octet indicating the structure of the
|
|
* encryption block. For this version of the document it shall have value 00,
|
|
* 01, or 02. For a private-key operation, the block type shall be 00 or 01.
|
|
* For a public-key operation, it shall be 02.
|
|
*
|
|
* The padding string shall consist of enough octets to pad the encryption
|
|
* block to the length of the encryption key. For block type 00, the octets
|
|
* shall have value 00; for block type 01, they shall have value FF; and for
|
|
* block type 02, they shall be pseudorandomly generated and nonzero.
|
|
*
|
|
* Note that in a previous step, we wrote padding bytes into the first three
|
|
* bytes of the encryption block because it was simpler to do so. We now
|
|
* overwrite them.
|
|
*/
|
|
if (padtype == 2)
|
|
{
|
|
a[sl] = 0;
|
|
a[key.chunkSize-2] = 2;
|
|
a[key.chunkSize-1] = 0;
|
|
}
|
|
/*
|
|
* Carve up the plaintext and encrypt each of the resultant blocks.
|
|
*/
|
|
al = a.length;
|
|
|
|
for (i = 0; i < al; i += key.chunkSize) {
|
|
/*
|
|
* Get a block.
|
|
*/
|
|
block = new BigInt();
|
|
|
|
j = 0;
|
|
|
|
for (k = i; k < (i+key.chunkSize); ++j) {
|
|
block.digits[j] = a[k++];
|
|
block.digits[j] += a[k++] << 8;
|
|
}
|
|
/*
|
|
* Encrypt it, convert it to text, and append it to the result.
|
|
*/
|
|
crypt = key.barrett.powMod(block, key.e);
|
|
if (encodingtype == 1) {
|
|
text = biToBytes(crypt);
|
|
}
|
|
else {
|
|
text = (key.radix == 16) ? biToHex(crypt) : biToString(crypt, key.radix);
|
|
}
|
|
result += text;
|
|
}
|
|
/*
|
|
* Return the result, removing the last space.
|
|
*/
|
|
//result = (result.substring(0, result.length - 1));
|
|
return result;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
function decryptedString(key, c)
|
|
/*
|
|
* key The previously-built RSA key whose
|
|
* private key component is to be used
|
|
* to decrypt the cyphertext string.
|
|
*
|
|
* c The cyphertext string that is to be
|
|
* decrypted, using the RSA assymmetric
|
|
* encryption method.
|
|
*
|
|
* returns The plaintext block that results from
|
|
* decrypting the cyphertext string c
|
|
* with the RSA key.
|
|
*
|
|
* This routine is the complementary decryption routine that is meant to be
|
|
* used for JavaScript decryption of cyphertext blocks that were encrypted
|
|
* using the OHDave padding method of the encryptedString routine (in this
|
|
* module). It can also decrypt cyphertext blocks that were encrypted by
|
|
* RSAEncode (in CryptoFuncs.pm or CryptoFuncs.php) so that encrypted
|
|
* messages can be sent of insecure links (e.g. HTTP) to a Web page.
|
|
*
|
|
* It accepts a cyphertext string that is to be decrypted with the public key
|
|
* component of the previously-built RSA key using the RSA assymmetric
|
|
* encryption method. Multiple cyphertext blocks are broken apart, if they
|
|
* are found in c, and each block is decrypted. All of the decrypted blocks
|
|
* are concatenated back together to obtain the original plaintext string.
|
|
*
|
|
* This routine assumes that the plaintext was padded to the same length as
|
|
* the encryption key with zeros. Therefore, it removes any zero bytes that
|
|
* are found at the end of the last decrypted block, before it is appended to
|
|
* the decrypted plaintext string.
|
|
*
|
|
* Note that the encryptedString routine (in this module) works fairly quickly
|
|
* simply by virtue of the fact that the public key most often chosen is quite
|
|
* short (e.g. 0x10001). This routine does not have that luxury. The
|
|
* decryption key that it must employ is the full key length. For long keys,
|
|
* this can result in serious timing delays (e.g. 7-8 seconds to decrypt using
|
|
* 2048 bit keys on a reasonably fast machine, under the Firefox Web browser).
|
|
*
|
|
* If you intend to send encrypted messagess to a JavaScript program running
|
|
* under a Web browser, you might consider using shorter keys to keep the
|
|
* decryption times low. Alternately, a better scheme is to generate a random
|
|
* key for use by a symmetric encryption algorithm and transmit it to the
|
|
* other end, after encrypting it with encryptedString. The other end can use
|
|
* a real crypto library (e.g. OpenSSL or Microsoft) to decrypt the key and
|
|
* then use it to encrypt all of the messages (with a symmetric encryption
|
|
* algorithm such as Twofish or AES) bound for the JavaScript program.
|
|
* Symmetric decryption is orders of magnitude faster than asymmetric and
|
|
* should yield low decryption times, even when executed in JavaScript.
|
|
*
|
|
* Also note that only the OHDave padding method (e.g. zeros) is supported by
|
|
* this routine *AND* that this routine expects little-endian cyphertext, as
|
|
* created by the encryptedString routine (in this module) or the RSAEncode
|
|
* routine (in either CryptoFuncs.pm or CryptoFuncs.php). You can use one of
|
|
* the real crypto libraries to create cyphertext that can be decrypted by
|
|
* this routine, if you reverse the plaintext byte order first and then
|
|
* manually pad it with zero bytes. The plaintext should then be encrypted
|
|
* with the NoPadding flag or its equivalent in the crypto library of your
|
|
* choice.
|
|
*/
|
|
{
|
|
var blocks = c.split(" "); // Multiple blocks of cyphertext
|
|
var b; // The usual Alice and Bob stuff
|
|
var i, j; // The usual Fortran index stuff
|
|
var bi; // Cyphertext as a big integer
|
|
var result = ""; // Plaintext result
|
|
/*
|
|
* Carve up the cyphertext into blocks.
|
|
*/
|
|
for (i = 0; i < blocks.length; ++i) {
|
|
/*
|
|
* Depending on the radix being used for the key, convert this cyphertext
|
|
* block into a big integer.
|
|
*/
|
|
if (key.radix == 16) { bi = biFromHex(blocks[i]); }
|
|
else { bi = biFromString(blocks[i], key.radix); }
|
|
/*
|
|
* Decrypt the cyphertext.
|
|
*/
|
|
b = key.barrett.powMod(bi, key.d);
|
|
/*
|
|
* Convert the decrypted big integer back to the plaintext string. Since
|
|
* we are using big integers, each element thereof represents two bytes of
|
|
* plaintext.
|
|
*/
|
|
for (j = 0; j <= biHighIndex(b); ++j) {
|
|
result += String.fromCharCode(b.digits[j] & 255, b.digits[j] >> 8);
|
|
}
|
|
}
|
|
/*
|
|
* Remove trailing null, if any.
|
|
*/
|
|
if (result.charCodeAt(result.length - 1) == 0) {
|
|
result = result.substring(0, result.length - 1);
|
|
}
|
|
/*
|
|
* Return the plaintext.
|
|
*/
|
|
return (result);
|
|
}
|