├── src
├── keygen.php
├── login.html
├── sha1.js
├── login.txt
├── login.php
├── rsa.js
└── jsbn.js
├── README.md
└── LICENSE
/src/keygen.php:
--------------------------------------------------------------------------------
1 | 512
5 | );
6 |
7 | // Create the keypair
8 | $res=openssl_pkey_new($config);
9 | openssl_pkey_export($res, $privkey, $pass);
10 | echo $privkey."\n";
11 | // Get public key
12 | $pubkey=openssl_pkey_get_details($res);
13 | //var_dump($res);
14 | $pubkey=$pubkey["key"];
15 | echo $pubkey;
16 | ?>
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | javascript-rsa
2 | ==============
3 |
4 | RSA encryption with javascript
5 |
6 | * relies on jsbn library from Tom Wu
7 | http://www-cs-students.stanford.edu/~tjw/jsbn/
8 |
9 | Security
10 | --------
11 |
12 | This is a pet project done a long time ago just for fun. In general, don't do cryptography in javascript, see:
13 |
86 | 94 |95 | The source code for this php file is available here. 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/login.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
86 | 94 |95 | The source code for this php file is available here. 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/rsa.js: -------------------------------------------------------------------------------- 1 | var RSAPublicKey = function($modulus, $encryptionExponent) { 2 | this.modulus = new BigInteger(Hex.encode($modulus), 16); 3 | this.encryptionExponent = new BigInteger(Hex.encode($encryptionExponent), 16); 4 | } 5 | 6 | var UTF8 = { 7 | encode: function($input) { 8 | $input = $input.replace(/\r\n/g,"\n"); 9 | var $output = ""; 10 | for (var $n = 0; $n < $input.length; $n++) { 11 | var $c = $input.charCodeAt($n); 12 | if ($c < 128) { 13 | $output += String.fromCharCode($c); 14 | } else if (($c > 127) && ($c < 2048)) { 15 | $output += String.fromCharCode(($c >> 6) | 192); 16 | $output += String.fromCharCode(($c & 63) | 128); 17 | } else { 18 | $output += String.fromCharCode(($c >> 12) | 224); 19 | $output += String.fromCharCode((($c >> 6) & 63) | 128); 20 | $output += String.fromCharCode(($c & 63) | 128); 21 | } 22 | } 23 | return $output; 24 | }, 25 | decode: function($input) { 26 | var $output = ""; 27 | var $i = 0; 28 | var $c = $c1 = $c2 = 0; 29 | while ( $i < $input.length ) { 30 | $c = $input.charCodeAt($i); 31 | if ($c < 128) { 32 | $output += String.fromCharCode($c); 33 | $i++; 34 | } else if(($c > 191) && ($c < 224)) { 35 | $c2 = $input.charCodeAt($i+1); 36 | $output += String.fromCharCode((($c & 31) << 6) | ($c2 & 63)); 37 | $i += 2; 38 | } else { 39 | $c2 = $input.charCodeAt($i+1); 40 | $c3 = $input.charCodeAt($i+2); 41 | $output += String.fromCharCode((($c & 15) << 12) | (($c2 & 63) << 6) | ($c3 & 63)); 42 | $i += 3; 43 | } 44 | } 45 | return $output; 46 | } 47 | }; 48 | 49 | var Base64 = { 50 | base64: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 51 | encode: function($input) { 52 | if (!$input) { 53 | return false; 54 | } 55 | //$input = UTF8.encode($input); 56 | var $output = ""; 57 | var $chr1, $chr2, $chr3; 58 | var $enc1, $enc2, $enc3, $enc4; 59 | var $i = 0; 60 | do { 61 | $chr1 = $input.charCodeAt($i++); 62 | $chr2 = $input.charCodeAt($i++); 63 | $chr3 = $input.charCodeAt($i++); 64 | $enc1 = $chr1 >> 2; 65 | $enc2 = (($chr1 & 3) << 4) | ($chr2 >> 4); 66 | $enc3 = (($chr2 & 15) << 2) | ($chr3 >> 6); 67 | $enc4 = $chr3 & 63; 68 | if (isNaN($chr2)) $enc3 = $enc4 = 64; 69 | else if (isNaN($chr3)) $enc4 = 64; 70 | $output += this.base64.charAt($enc1) + this.base64.charAt($enc2) + this.base64.charAt($enc3) + this.base64.charAt($enc4); 71 | } while ($i < $input.length); 72 | return $output; 73 | }, 74 | decode: function($input) { 75 | if(!$input) return false; 76 | $input = $input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 77 | var $output = ""; 78 | var $enc1, $enc2, $enc3, $enc4; 79 | var $i = 0; 80 | do { 81 | $enc1 = this.base64.indexOf($input.charAt($i++)); 82 | $enc2 = this.base64.indexOf($input.charAt($i++)); 83 | $enc3 = this.base64.indexOf($input.charAt($i++)); 84 | $enc4 = this.base64.indexOf($input.charAt($i++)); 85 | $output += String.fromCharCode(($enc1 << 2) | ($enc2 >> 4)); 86 | if ($enc3 != 64) $output += String.fromCharCode((($enc2 & 15) << 4) | ($enc3 >> 2)); 87 | if ($enc4 != 64) $output += String.fromCharCode((($enc3 & 3) << 6) | $enc4); 88 | } while ($i < $input.length); 89 | return $output; //UTF8.decode($output); 90 | } 91 | }; 92 | 93 | var Hex = { 94 | hex: "0123456789abcdef", 95 | encode: function($input) { 96 | if(!$input) return false; 97 | var $output = ""; 98 | var $k; 99 | var $i = 0; 100 | do { 101 | $k = $input.charCodeAt($i++); 102 | $output += this.hex.charAt(($k >> 4) &0xf) + this.hex.charAt($k & 0xf); 103 | } while ($i < $input.length); 104 | return $output; 105 | }, 106 | decode: function($input) { 107 | if(!$input) return false; 108 | $input = $input.replace(/[^0-9abcdef]/g, ""); 109 | var $output = ""; 110 | var $i = 0; 111 | do { 112 | $output += String.fromCharCode(((this.hex.indexOf($input.charAt($i++)) << 4) & 0xf0) | (this.hex.indexOf($input.charAt($i++)) & 0xf)); 113 | } while ($i < $input.length); 114 | return $output; 115 | } 116 | }; 117 | 118 | var ASN1Data = function($data) { 119 | this.error = false; 120 | this.parse = function($data) { 121 | if (!$data) { 122 | this.error = true; 123 | return null; 124 | } 125 | var $result = []; 126 | while($data.length > 0) { 127 | // get the tag 128 | var $tag = $data.charCodeAt(0); 129 | $data = $data.substr(1); 130 | // get length 131 | var $length = 0; 132 | // ignore any null tag 133 | if (($tag & 31) == 0x5) $data = $data.substr(1); 134 | else { 135 | if ($data.charCodeAt(0) & 128) { 136 | var $lengthSize = $data.charCodeAt(0) & 127; 137 | $data = $data.substr(1); 138 | if($lengthSize > 0) $length = $data.charCodeAt(0); 139 | if($lengthSize > 1) $length = (($length << 8) | $data.charCodeAt(1)); 140 | if($lengthSize > 2) { 141 | this.error = true; 142 | return null; 143 | } 144 | $data = $data.substr($lengthSize); 145 | } else { 146 | $length = $data.charCodeAt(0); 147 | $data = $data.substr(1); 148 | } 149 | } 150 | // get value 151 | var $value = ""; 152 | if($length) { 153 | if ($length > $data.length){ 154 | this.error = true; 155 | return null; 156 | } 157 | $value = $data.substr(0, $length); 158 | $data = $data.substr($length); 159 | } 160 | if ($tag & 32) 161 | $result.push(this.parse($value)); // sequence 162 | else 163 | $result.push(this.value(($tag & 128) ? 4 : ($tag & 31), $value)); 164 | } 165 | return $result; 166 | }; 167 | this.value = function($tag, $data) { 168 | if ($tag == 1) 169 | return $data ? true : false; 170 | else if ($tag == 2) //integer 171 | return $data; 172 | else if ($tag == 3) //bit string 173 | return this.parse($data.substr(1)); 174 | else if ($tag == 5) //null 175 | return null; 176 | else if ($tag == 6){ //ID 177 | var $res = []; 178 | var $d0 = $data.charCodeAt(0); 179 | $res.push(Math.floor($d0 / 40)); 180 | $res.push($d0 - $res[0]*40); 181 | var $stack = []; 182 | var $powNum = 0; 183 | var $i; 184 | for($i=1;$i<$data.length;$i++){ 185 | var $token = $data.charCodeAt($i); 186 | $stack.push($token & 127); 187 | if ( $token & 128 ) 188 | $powNum++; 189 | else { 190 | var $j; 191 | var $sum = 0; 192 | for($j=0;$j<$stack.length;$j++) 193 | $sum += $stack[$j] * Math.pow(128, $powNum--); 194 | $res.push($sum); 195 | $powNum = 0; 196 | $stack = []; 197 | } 198 | } 199 | return $res.join("."); 200 | } 201 | return null; 202 | } 203 | this.data = this.parse($data); 204 | }; 205 | 206 | var RSA = { 207 | getPublicKey: function($pem) { 208 | if($pem.length<50) return false; 209 | if($pem.substr(0,26)!="-----BEGIN PUBLIC KEY-----") return false; 210 | $pem = $pem.substr(26); 211 | if($pem.substr($pem.length-24)!="-----END PUBLIC KEY-----") return false; 212 | $pem = $pem.substr(0,$pem.length-24); 213 | $pem = new ASN1Data(Base64.decode($pem)); 214 | if($pem.error) return false; 215 | $pem = $pem.data; 216 | if($pem[0][0][0]=="1.2.840.113549.1.1.1") 217 | return new RSAPublicKey($pem[0][1][0][0], $pem[0][1][0][1]); 218 | return false; 219 | }, 220 | encrypt: function($data, $pubkey) { 221 | if (!$pubkey) return false; 222 | var bytes = ($pubkey.modulus.bitLength()+7)>>3; 223 | $data = this.pkcs1pad2($data,bytes); 224 | if(!$data) return false; 225 | $data = $data.modPowInt($pubkey.encryptionExponent, $pubkey.modulus); 226 | if(!$data) return false; 227 | $data = $data.toString(16); 228 | while ($data.length < bytes*2) 229 | $data = '0' + $data; 230 | return Base64.encode(Hex.decode($data)); 231 | }, 232 | pkcs1pad2: function($data, $keysize) { 233 | if($keysize < $data.length + 11) 234 | return null; 235 | var $buffer = []; 236 | var $i = $data.length - 1; 237 | while($i >= 0 && $keysize > 0) 238 | $buffer[--$keysize] = $data.charCodeAt($i--); 239 | $buffer[--$keysize] = 0; 240 | while($keysize > 2) 241 | $buffer[--$keysize] = Math.floor(Math.random()*254) + 1; 242 | $buffer[--$keysize] = 2; 243 | $buffer[--$keysize] = 0; 244 | return new BigInteger($buffer); 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /src/jsbn.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2005 Tom Wu 2 | // All Rights Reserved. 3 | // See "LICENSE" for details. 4 | 5 | // Basic JavaScript BN library - subset useful for RSA encryption. 6 | 7 | // Bits per digit 8 | var dbits; 9 | 10 | // JavaScript engine analysis 11 | var canary = 0xdeadbeefcafe; 12 | var j_lm = ((canary&0xffffff)==0xefcafe); 13 | 14 | // (public) Constructor 15 | function BigInteger(a,b,c) { 16 | if(a != null) 17 | if("number" == typeof a) this.fromNumber(a,b,c); 18 | else if(b == null && "string" != typeof a) this.fromString(a,256); 19 | else this.fromString(a,b); 20 | } 21 | 22 | // return new, unset BigInteger 23 | function nbi() { return new BigInteger(null); } 24 | 25 | // am: Compute w_j += (x*this_i), propagate carries, 26 | // c is initial carry, returns final carry. 27 | // c < 3*dvalue, x < 2*dvalue, this_i < dvalue 28 | // We need to select the fastest one that works in this environment. 29 | 30 | // am1: use a single mult and divide to get the high bits, 31 | // max digit bits should be 26 because 32 | // max internal value = 2*dvalue^2-2*dvalue (< 2^53) 33 | function am1(i,x,w,j,c,n) { 34 | while(--n >= 0) { 35 | var v = x*this[i++]+w[j]+c; 36 | c = Math.floor(v/0x4000000); 37 | w[j++] = v&0x3ffffff; 38 | } 39 | return c; 40 | } 41 | // am2 avoids a big mult-and-extract completely. 42 | // Max digit bits should be <= 30 because we do bitwise ops 43 | // on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) 44 | function am2(i,x,w,j,c,n) { 45 | var xl = x&0x7fff, xh = x>>15; 46 | while(--n >= 0) { 47 | var l = this[i]&0x7fff; 48 | var h = this[i++]>>15; 49 | var m = xh*l+h*xl; 50 | l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff); 51 | c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); 52 | w[j++] = l&0x3fffffff; 53 | } 54 | return c; 55 | } 56 | // Alternately, set max digit bits to 28 since some 57 | // browsers slow down when dealing with 32-bit numbers. 58 | function am3(i,x,w,j,c,n) { 59 | var xl = x&0x3fff, xh = x>>14; 60 | while(--n >= 0) { 61 | var l = this[i]&0x3fff; 62 | var h = this[i++]>>14; 63 | var m = xh*l+h*xl; 64 | l = xl*l+((m&0x3fff)<<14)+w[j]+c; 65 | c = (l>>28)+(m>>14)+xh*h; 66 | w[j++] = l&0xfffffff; 67 | } 68 | return c; 69 | } 70 | if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) { 71 | BigInteger.prototype.am = am2; 72 | dbits = 30; 73 | } 74 | else if(j_lm && (navigator.appName != "Netscape")) { 75 | BigInteger.prototype.am = am1; 76 | dbits = 26; 77 | } 78 | else { // Mozilla/Netscape seems to prefer am3 79 | BigInteger.prototype.am = am3; 80 | dbits = 28; 81 | } 82 | 83 | BigInteger.prototype.DB = dbits; 84 | BigInteger.prototype.DM = ((1<
>(p+=this.DB-k);
191 | }
192 | else {
193 | d = (this[i]>>(p-=k))&km;
194 | if(p <= 0) { p += this.DB; --i; }
195 | }
196 | if(d > 0) m = true;
197 | if(m) r += int2char(d);
198 | }
199 | }
200 | return m?r:"0";
201 | }
202 |
203 | // (public) -this
204 | function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }
205 |
206 | // (public) |this|
207 | function bnAbs() { return (this.s<0)?this.negate():this; }
208 |
209 | // (public) return + if this > a, - if this < a, 0 if equal
210 | function bnCompareTo(a) {
211 | var r = this.s-a.s;
212 | if(r != 0) return r;
213 | var i = this.t;
214 | r = i-a.t;
215 | if(r != 0) return (this.s<0)?-r:r;
216 | while(--i >= 0) if((r=this[i]-a[i]) != 0) return r;
217 | return 0;
218 | }
219 |
220 | // returns bit length of the integer x
221 | function nbits(x) {
222 | var r = 1, t;
223 | if((t=x>>>16) != 0) { x = t; r += 16; }
224 | if((t=x>>8) != 0) { x = t; r += 8; }
225 | if((t=x>>4) != 0) { x = t; r += 4; }
226 | if((t=x>>2) != 0) { x = t; r += 2; }
227 | if((t=x>>1) != 0) { x = t; r += 1; }
228 | return r;
229 | }
230 |
231 | // (public) return the number of bits in "this"
232 | function bnBitLength() {
233 | if(this.t <= 0) return 0;
234 | return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));
235 | }
236 |
237 | // (protected) r = this << n*DB
238 | function bnpDLShiftTo(n,r) {
239 | var i;
240 | for(i = this.t-1; i >= 0; --i) r[i+n] = this[i];
241 | for(i = n-1; i >= 0; --i) r[i] = 0;
242 | r.t = this.t+n;
243 | r.s = this.s;
244 | }
245 |
246 | // (protected) r = this >> n*DB
247 | function bnpDRShiftTo(n,r) {
248 | for(var i = n; i < this.t; ++i) r[i-n] = this[i];
249 | r.t = Math.max(this.t-n,0);
250 | r.s = this.s;
251 | }
252 |
253 | // (protected) r = this << n
254 | function bnpLShiftTo(n,r) {
255 | var bs = n%this.DB;
256 | var cbs = this.DB-bs;
257 | var bm = (1<