├── README.md ├── bower.json ├── test.html ├── webcrypto.min.js └── webcrypto.js /README.md: -------------------------------------------------------------------------------- 1 | # webcrypto.js 2 | Library to sign and verify signatures using RSA keys and the WebCrypto API. 3 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webcrypto.js", 3 | "version": "1.0.3", 4 | "homepage": "https://github.com/deiu/webcrypto.js", 5 | "authors": [ 6 | "Andrei Sambra " 7 | ], 8 | "description": "Lightweight library to sign and verify signatures using RSA keys and the WebCrypto API", 9 | "main": "webcrypto.js", 10 | "moduleType": [ 11 | "globals" 12 | ], 13 | "keywords": [ 14 | "RSA", 15 | "cryptographic signatures", 16 | "cryptographic signature verification", 17 | "WebCrypto", 18 | "WebCrypto API" 19 | ], 20 | "license": "MIT", 21 | "ignore": [ 22 | "**/.*", 23 | "node_modules", 24 | "bower_components", 25 | "test", 26 | "tests" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test sign/verify using the WebCrypto API 6 | 7 | 8 |
9 |

10 |
11 |

12 | Signature: 13 | 14 |

15 |

16 | Signature verified using the generated (fresh) keys: 17 | 18 |

19 |

20 | Signature verified using the parsed public key: 21 | 22 |

23 |

24 | Private key (PEM): 25 |


26 |     

27 |

28 | Public key (PEM): 29 |


30 |     

31 | 32 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /webcrypto.min.js: -------------------------------------------------------------------------------- 1 | (function(root){if(typeof $webCrypto==='undefined'){var $webCrypto={}}else{return $webCrypto;throw"Internal error: WebCrypto libray has already been loaded: $webCrypto already exists"}var crypto=window.crypto||window.msCrypto;if(!crypto.subtle){return;throw"WebCrypto API support missing from browser."}$webCrypto.signAlgorithm={name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}}$webCrypto.generateRSAKey=function(){var alg={name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"},modulusLength:2048,extractable:true,publicExponent:new Uint8Array([1,0,1])};return new Promise(function(resolve){var genkey=crypto.subtle.generateKey(alg,true,["sign","verify"]);genkey.then(function(pair){resolve(pair)}).catch(function(e){console.log(e);resolve(null)})})}$webCrypto.arrayBufferToBase64String=function(arrayBuffer){var byteArray=new Uint8Array(arrayBuffer)var byteString='';for(var i=0;i0&&lines[i].indexOf('-BEGIN RSA PRIVATE KEY-')<0&&lines[i].indexOf('-BEGIN RSA PUBLIC KEY-')<0&&lines[i].indexOf('-END RSA PRIVATE KEY-')<0&&lines[i].indexOf('-END RSA PUBLIC KEY-')<0){encoded+=lines[i].trim()}}return $webCrypto.base64StringToArrayBuffer(encoded)};$webCrypto.importPublicKey=function(pemKey,format){if(!format){format='spki'}return new Promise(function(resolve){var importer=crypto.subtle.importKey(format,$webCrypto.convertPemToBinary(pemKey),$webCrypto.signAlgorithm,true,["verify"]);importer.then(function(key){resolve({value:key,error:null})}).catch(function(e){console.log(e);resolve({value:null,error:e})})})};$webCrypto.importPrivateKey=function(pemKey,format){if(!format){format='pkcs8'}return new Promise(function(resolve){var importer=crypto.subtle.importKey(format,$webCrypto.convertPemToBinary(pemKey),$webCrypto.signAlgorithm,false,["sign"]);importer.then(function(key){if(!key||key.length===0){console.log("Problem parsing key..empty result")}resolve({value:key,error:null})}).catch(function(e){console.log(e);resolve({value:null,error:e})})})};$webCrypto.exportPublicKey=function(keys,format){if(!format){format='spki'}return new Promise(function(resolve){window.crypto.subtle.exportKey(format,keys.publicKey).then(function(key){resolve($webCrypto.convertBinaryToPem(key,"RSA PUBLIC KEY"))}).catch(function(e){console.log(e);resolve(null)})})};$webCrypto.exportPrivateKey=function(keys,format){if(!format){format='pkcs8'}return new Promise(function(resolve){var expK=window.crypto.subtle.exportKey(format,keys.privateKey);expK.then(function(key){resolve($webCrypto.convertBinaryToPem(key,"RSA PRIVATE KEY"))}).catch(function(e){console.log(e);resolve(null)})})};$webCrypto.exportPemKeys=function(keys){return new Promise(function(resolve){$webCrypto.exportPublicKey(keys).then(function(pubKey){$webCrypto.exportPrivateKey(keys).then(function(privKey){resolve({publicKey:pubKey,privateKey:privKey})}).catch(function(e){console.log(e);resolve(null)})}).catch(function(e){console.log(e);resolve(null)})})};$webCrypto.signData=function(key,data){var buffer=$webCrypto.textToArrayBuffer(data);return window.crypto.subtle.sign($webCrypto.signAlgorithm,key,buffer)};$webCrypto.verifySig=function(pub,sig,data){data=$webCrypto.textToArrayBuffer(data);return crypto.subtle.verify($webCrypto.signAlgorithm,pub,sig,data)};if(typeof exports!=='undefined'){if(typeof module!=='undefined'&&module.exports){exports=module.exports=$webCrypto}exports.$webCrypto=$webCrypto}else{if(typeof define==='function'&&define.amd){define([],function(){return $webCrypto})}root['$webCrypto']=$webCrypto}})(this); -------------------------------------------------------------------------------- /webcrypto.js: -------------------------------------------------------------------------------- 1 | (function(root) { 2 | if( typeof $webCrypto === 'undefined' ) { 3 | var $webCrypto = {}; 4 | } else { 5 | return $webCrypto; 6 | throw "Internal error: WebCrypto libray has already been loaded: $webCrypto already exists"; 7 | } 8 | 9 | var crypto = window.crypto || window.msCrypto; 10 | if (!crypto.subtle) { 11 | return; 12 | throw "WebCrypto API support missing from browser."; 13 | } 14 | 15 | $webCrypto.signAlgorithm = { 16 | name: "RSASSA-PKCS1-v1_5", 17 | hash: { 18 | name: "SHA-256" 19 | } 20 | } 21 | 22 | $webCrypto.generateRSAKey = function() { 23 | var alg = { 24 | name: "RSASSA-PKCS1-v1_5", 25 | hash: {name: "SHA-256"}, 26 | modulusLength: 2048, 27 | extractable: true, 28 | publicExponent: new Uint8Array([1, 0, 1]) 29 | }; 30 | 31 | return new Promise(function(resolve) { 32 | var genkey = crypto.subtle.generateKey(alg, true, ["sign", "verify"]); 33 | genkey.then(function (pair) { 34 | resolve(pair); 35 | }).catch(function(e) { 36 | console.log(e); 37 | resolve(null); 38 | }); 39 | }); 40 | } 41 | 42 | $webCrypto.arrayBufferToBase64String = function(arrayBuffer) { 43 | var byteArray = new Uint8Array(arrayBuffer) 44 | var byteString = ''; 45 | for (var i=0; i 0 && 95 | lines[i].indexOf('-BEGIN RSA PRIVATE KEY-') < 0 && 96 | lines[i].indexOf('-BEGIN RSA PUBLIC KEY-') < 0 && 97 | lines[i].indexOf('-END RSA PRIVATE KEY-') < 0 && 98 | lines[i].indexOf('-END RSA PUBLIC KEY-') < 0) { 99 | encoded += lines[i].trim(); 100 | } 101 | } 102 | return $webCrypto.base64StringToArrayBuffer(encoded); 103 | }; 104 | 105 | $webCrypto.importPublicKey = function(pemKey, format) { 106 | if (!format) { 107 | format = 'spki'; 108 | } 109 | return new Promise(function(resolve) { 110 | var importer = crypto.subtle.importKey(format, $webCrypto.convertPemToBinary(pemKey), $webCrypto.signAlgorithm, true, ["verify"]); 111 | importer.then(function(key) { 112 | resolve({value: key, error: null}); 113 | }).catch(function(e) { 114 | console.log(e); 115 | resolve({value: null, error: e}); 116 | }); 117 | }); 118 | }; 119 | 120 | $webCrypto.importPrivateKey = function(pemKey, format) { 121 | if (!format) { 122 | format = 'pkcs8'; 123 | } 124 | return new Promise(function(resolve) { 125 | var importer = crypto.subtle.importKey(format, $webCrypto.convertPemToBinary(pemKey), $webCrypto.signAlgorithm, false, ["sign"]); 126 | importer.then(function(key) { 127 | if (!key || key.length === 0) { 128 | console.log("Problem parsing key..empty result"); 129 | } 130 | resolve({value: key, error: null}); 131 | }).catch(function(e) { 132 | console.log(e); 133 | resolve({value: null, error: e}); 134 | }); 135 | }); 136 | }; 137 | 138 | $webCrypto.exportPublicKey = function(keys, format) { 139 | if (!format) { 140 | format = 'spki'; 141 | } 142 | return new Promise(function(resolve) { 143 | window.crypto.subtle.exportKey(format, keys.publicKey). 144 | then(function(key) { 145 | resolve($webCrypto.convertBinaryToPem(key, "RSA PUBLIC KEY")); 146 | }).catch(function(e) { 147 | console.log(e); 148 | resolve(null); 149 | }); 150 | }); 151 | }; 152 | 153 | $webCrypto.exportPrivateKey = function(keys, format) { 154 | if (!format) { 155 | format = 'pkcs8'; 156 | } 157 | return new Promise(function(resolve) { 158 | var expK = window.crypto.subtle.exportKey(format, keys.privateKey); 159 | expK.then(function(key) { 160 | resolve($webCrypto.convertBinaryToPem(key, "RSA PRIVATE KEY")); 161 | }).catch(function(e) { 162 | console.log(e); 163 | resolve(null); 164 | }); 165 | }); 166 | }; 167 | 168 | $webCrypto.exportPemKeys = function(keys) { 169 | return new Promise(function(resolve) { 170 | $webCrypto.exportPublicKey(keys).then(function(pubKey) { 171 | $webCrypto.exportPrivateKey(keys).then(function(privKey) { 172 | resolve({publicKey: pubKey, privateKey: privKey}); 173 | }).catch(function(e) { 174 | console.log(e); 175 | resolve(null); 176 | }); 177 | }).catch(function(e) { 178 | console.log(e); 179 | resolve(null); 180 | }); 181 | }); 182 | }; 183 | 184 | $webCrypto.signData = function(key, data) { 185 | var buffer = $webCrypto.textToArrayBuffer(data); 186 | return window.crypto.subtle.sign($webCrypto.signAlgorithm, key, buffer); 187 | }; 188 | 189 | $webCrypto.verifySig = function(pub, sig, data) { 190 | data = $webCrypto.textToArrayBuffer(data); 191 | return crypto.subtle.verify($webCrypto.signAlgorithm, pub, sig, data); 192 | }; 193 | 194 | // Handle node, amd, and global systems 195 | if (typeof exports !== 'undefined') { 196 | if (typeof module !== 'undefined' && module.exports) { 197 | exports = module.exports = $webCrypto; 198 | } 199 | exports.$webCrypto = $webCrypto; 200 | } else { 201 | if (typeof define === 'function' && define.amd) { 202 | define([], function() { 203 | return $webCrypto; 204 | }); 205 | } 206 | 207 | // Leak a global regardless of module system 208 | root['$webCrypto'] = $webCrypto; 209 | } 210 | })(this); --------------------------------------------------------------------------------