wip
This commit is contained in:
		
							parent
							
								
									1c1b9d00a9
								
							
						
					
					
						commit
						b9b403ef4f
					
				
							
								
								
									
										134
									
								
								browser.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								browser.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | |||||||
|  | ;(function (exports) { | ||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | var PromiseA; | ||||||
|  | try { | ||||||
|  |   /*global Promise*/ | ||||||
|  |   PromiseA = Promise; | ||||||
|  | } catch(e) { | ||||||
|  |   PromiseA = require('bluebird'); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // https://stackoverflow.com/questions/40314257/export-webcrypto-key-to-pem-format
 | ||||||
|  | function derToPem(keydata, pemName, privacy){ | ||||||
|  |   var keydataS = arrayBufferToString(keydata); | ||||||
|  |   var keydataB64 = window.btoa(keydataS); | ||||||
|  |   var keydataB64Pem = formatAsPem(keydataB64, pemName, privacy); | ||||||
|  |   return keydataB64Pem; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function arrayBufferToString( buffer ) { | ||||||
|  |   var binary = []; | ||||||
|  |   var bytes = new Uint8Array( buffer ); | ||||||
|  |   var len = bytes.byteLength; | ||||||
|  |   for (var i = 0; i < len; i++) { | ||||||
|  |       binary.push(String.fromCharCode( bytes[ i ] )); | ||||||
|  |   } | ||||||
|  |   return binary.join(''); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | function formatAsPem(str, pemName, privacy) { | ||||||
|  |   var privstr = (privacy ? privacy + ' ' : ''); | ||||||
|  |   var finalString = '-----BEGIN ' + pemName + ' ' + privstr + 'KEY-----\n'; | ||||||
|  | 
 | ||||||
|  |   while (str.length > 0) { | ||||||
|  |       finalString += str.substring(0, 64) + '\n'; | ||||||
|  |       str = str.substring(64); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   finalString = finalString + '-----END ' + pemName + ' ' + privstr + 'KEY-----'; | ||||||
|  | 
 | ||||||
|  |   return finalString; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var Keypairs = exports.Keypairs = { | ||||||
|  |   generate: function(opts) { | ||||||
|  |     if (!opts) { opts = {}; } | ||||||
|  |     if (!opts.type) { opts.type = 'EC'; } | ||||||
|  | 
 | ||||||
|  |     var supported = [ 'EC', 'RSA' ]; | ||||||
|  |     if (-1 === supported.indexOf(opts.type)) { | ||||||
|  |       return PromiseA.reject(new Error("'" + opts.type + "' not implemented. Try one of " + supported.join(', '))); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if ('EC' === opts.type) { | ||||||
|  |       return Keypairs._generateEc(opts); | ||||||
|  |     } | ||||||
|  |     if ('RSA' === opts.type) { | ||||||
|  |       return Keypairs._generateRsa(opts); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | , _generateEc: function (opts) { | ||||||
|  |     if (!opts.namedCurve) { opts.namedCurve = 'P-256'; } | ||||||
|  |     if ('P-256' !== opts.namedCurve) { | ||||||
|  |       console.warn("'" + opts.namedCurve + "' is not supported, but it _might_ happen to work anyway."); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // https://github.com/diafygi/webcrypto-examples#ecdsa---generatekey
 | ||||||
|  |     var extractable = true; | ||||||
|  | 
 | ||||||
|  |     return window.crypto.subtle.generateKey( | ||||||
|  |       { name: "ECDSA", namedCurve: opts.namedCurve } | ||||||
|  |     , extractable | ||||||
|  |     , [ 'sign', 'verify' ] | ||||||
|  |     ).then(function (result) { | ||||||
|  |       return window.crypto.subtle.exportKey( | ||||||
|  |         "jwk" | ||||||
|  |       , result.privateKey | ||||||
|  |       ).then(function (jwk) { | ||||||
|  |         return window.crypto.subtle.exportKey( | ||||||
|  |           "pkcs8" | ||||||
|  |         , result.privateKey | ||||||
|  |         ).then(function (keydata) { | ||||||
|  |           return { | ||||||
|  |             type: 'EC' | ||||||
|  |           , privateJwk: jwk | ||||||
|  |           , privatePem: derToPem(keydata, 'EC', 'PRIVATE') | ||||||
|  |           }; | ||||||
|  |         }); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | , _generateRsa: function (opts) { | ||||||
|  |     if (!opts.bitlength) { opts.bitlength = 2048; } | ||||||
|  |     if (-1 === [ 2048, 4096 ].indexOf(opts.bitlength)) { | ||||||
|  |       return PromiseA.reject("opts.bitlength = (" + typeof opts.bitlength + ") " + opts.bitlength + ": Are you serious?"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // https://github.com/diafygi/webcrypto-examples#rsa---generatekey
 | ||||||
|  |     var extractable = true; | ||||||
|  | 
 | ||||||
|  |     return window.crypto.subtle.generateKey( | ||||||
|  |       { name: "RSASSA-PKCS1-v1_5" | ||||||
|  |       , modulusLength: opts.bitlength | ||||||
|  |       , publicExponent: new Uint8Array([0x01, 0x00, 0x01]) | ||||||
|  |       , hash: { name: "SHA-256" } | ||||||
|  |       } | ||||||
|  |     , extractable | ||||||
|  |     , [ 'sign', 'verify' ] | ||||||
|  |     ).then(function (result) { | ||||||
|  |       return window.crypto.subtle.exportKey( | ||||||
|  |         "jwk" | ||||||
|  |       , result.privateKey | ||||||
|  |       ).then(function (jwk) { | ||||||
|  |         return window.crypto.subtle.exportKey( | ||||||
|  |           "pkcs8" | ||||||
|  |         , result.privateKey | ||||||
|  |         ).then(function (keydata) { | ||||||
|  |           return { | ||||||
|  |             type: 'RSA' | ||||||
|  |           , privateJwk: jwk | ||||||
|  |           , privatePem: derToPem(keydata, 'RSA', 'PRIVATE') | ||||||
|  |           }; | ||||||
|  |         }); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }('undefined' === typeof module ? window : module.exports)); | ||||||
|  | 
 | ||||||
|  | // How we might use this
 | ||||||
|  | // var Keypairs = require('keypairs').Keypairs
 | ||||||
							
								
								
									
										133
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										133
									
								
								index.js
									
									
									
									
									
								
							| @ -1,134 +1,3 @@ | |||||||
| ;(function (exports) { |  | ||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| var PromiseA; | module.exports = require('./lib/keypairs.js'); | ||||||
| try { |  | ||||||
|   /*global Promise*/ |  | ||||||
|   PromiseA = Promise; |  | ||||||
| } catch(e) { |  | ||||||
|   PromiseA = require('bluebird'); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| // https://stackoverflow.com/questions/40314257/export-webcrypto-key-to-pem-format
 |  | ||||||
| function derToPem(keydata, pemName, privacy){ |  | ||||||
|   var keydataS = arrayBufferToString(keydata); |  | ||||||
|   var keydataB64 = window.btoa(keydataS); |  | ||||||
|   var keydataB64Pem = formatAsPem(keydataB64, pemName, privacy); |  | ||||||
|   return keydataB64Pem; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function arrayBufferToString( buffer ) { |  | ||||||
|   var binary = []; |  | ||||||
|   var bytes = new Uint8Array( buffer ); |  | ||||||
|   var len = bytes.byteLength; |  | ||||||
|   for (var i = 0; i < len; i++) { |  | ||||||
|       binary.push(String.fromCharCode( bytes[ i ] )); |  | ||||||
|   } |  | ||||||
|   return binary.join(''); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| function formatAsPem(str, pemName, privacy) { |  | ||||||
|   var privstr = (privacy ? privacy + ' ' : ''); |  | ||||||
|   var finalString = '-----BEGIN ' + pemName + ' ' + privstr + 'KEY-----\n'; |  | ||||||
| 
 |  | ||||||
|   while (str.length > 0) { |  | ||||||
|       finalString += str.substring(0, 64) + '\n'; |  | ||||||
|       str = str.substring(64); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   finalString = finalString + '-----END ' + pemName + ' ' + privstr + 'KEY-----'; |  | ||||||
| 
 |  | ||||||
|   return finalString; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| var Keypairs = exports.Keypairs = { |  | ||||||
|   generate: function(opts) { |  | ||||||
|     if (!opts) { opts = {}; } |  | ||||||
|     if (!opts.type) { opts.type = 'EC'; } |  | ||||||
| 
 |  | ||||||
|     var supported = [ 'EC', 'RSA' ]; |  | ||||||
|     if (-1 === supported.indexOf(opts.type)) { |  | ||||||
|       return PromiseA.reject(new Error("'" + opts.type + "' not implemented. Try one of " + supported.join(', '))); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if ('EC' === opts.type) { |  | ||||||
|       return Keypairs._generateEc(opts); |  | ||||||
|     } |  | ||||||
|     if ('RSA' === opts.type) { |  | ||||||
|       return Keypairs._generateRsa(opts); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| , _generateEc: function (opts) { |  | ||||||
|     if (!opts.namedCurve) { opts.namedCurve = 'P-256'; } |  | ||||||
|     if ('P-256' !== opts.namedCurve) { |  | ||||||
|       console.warn("'" + opts.namedCurve + "' is not supported, but it _might_ happen to work anyway."); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // https://github.com/diafygi/webcrypto-examples#ecdsa---generatekey
 |  | ||||||
|     var extractable = true; |  | ||||||
| 
 |  | ||||||
|     return window.crypto.subtle.generateKey( |  | ||||||
|       { name: "ECDSA", namedCurve: opts.namedCurve } |  | ||||||
|     , extractable |  | ||||||
|     , [ 'sign', 'verify' ] |  | ||||||
|     ).then(function (result) { |  | ||||||
|       return window.crypto.subtle.exportKey( |  | ||||||
|         "jwk" |  | ||||||
|       , result.privateKey |  | ||||||
|       ).then(function (jwk) { |  | ||||||
|         return window.crypto.subtle.exportKey( |  | ||||||
|           "pkcs8" |  | ||||||
|         , result.privateKey |  | ||||||
|         ).then(function (keydata) { |  | ||||||
|           return { |  | ||||||
|             type: 'EC' |  | ||||||
|           , privateJwk: jwk |  | ||||||
|           , privatePem: derToPem(keydata, 'EC', 'PRIVATE') |  | ||||||
|           }; |  | ||||||
|         }); |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| , _generateRsa: function (opts) { |  | ||||||
|     if (!opts.bitlength) { opts.bitlength = 2048; } |  | ||||||
|     if (-1 === [ 2048, 4096 ].indexOf(opts.bitlength)) { |  | ||||||
|       return PromiseA.reject("opts.bitlength = (" + typeof opts.bitlength + ") " + opts.bitlength + ": Are you serious?"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // https://github.com/diafygi/webcrypto-examples#rsa---generatekey
 |  | ||||||
|     var extractable = true; |  | ||||||
| 
 |  | ||||||
|     return window.crypto.subtle.generateKey( |  | ||||||
|       { name: "RSASSA-PKCS1-v1_5" |  | ||||||
|       , modulusLength: opts.bitlength |  | ||||||
|       , publicExponent: new Uint8Array([0x01, 0x00, 0x01]) |  | ||||||
|       , hash: { name: "SHA-256" } |  | ||||||
|       } |  | ||||||
|     , extractable |  | ||||||
|     , [ 'sign', 'verify' ] |  | ||||||
|     ).then(function (result) { |  | ||||||
|       return window.crypto.subtle.exportKey( |  | ||||||
|         "jwk" |  | ||||||
|       , result.privateKey |  | ||||||
|       ).then(function (jwk) { |  | ||||||
|         return window.crypto.subtle.exportKey( |  | ||||||
|           "pkcs8" |  | ||||||
|         , result.privateKey |  | ||||||
|         ).then(function (keydata) { |  | ||||||
|           return { |  | ||||||
|             type: 'RSA' |  | ||||||
|           , privateJwk: jwk |  | ||||||
|           , privatePem: derToPem(keydata, 'RSA', 'PRIVATE') |  | ||||||
|           }; |  | ||||||
|         }); |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| }('undefined' === typeof module ? window : module.exports)); |  | ||||||
| 
 |  | ||||||
| // How we might use this
 |  | ||||||
| // var Keypairs = require('keypairs').Keypairs
 |  | ||||||
|  | |||||||
							
								
								
									
										62
									
								
								lib/ec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								lib/ec.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | |||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | var ASN1 = require('./asn1-packer.js'); | ||||||
|  | var Enc = require('./encoding.js'); | ||||||
|  | var x509 = module.exports; | ||||||
|  | 
 | ||||||
|  | // 1.2.840.10045.3.1.7
 | ||||||
|  | // prime256v1 (ANSI X9.62 named elliptic curve)
 | ||||||
|  | var OBJ_ID_EC  = '06 08 2A8648CE3D030107'.replace(/\s+/g, '').toLowerCase(); | ||||||
|  | // 1.3.132.0.34
 | ||||||
|  | // secp384r1 (SECG (Certicom) named elliptic curve)
 | ||||||
|  | var OBJ_ID_EC_384 = '06 05 2B81040022'.replace(/\s+/g, '').toLowerCase(); | ||||||
|  | // 1.2.840.10045.2.1
 | ||||||
|  | // ecPublicKey (ANSI X9.62 public key type)
 | ||||||
|  | var OBJ_ID_EC_PUB = '06 07 2A8648CE3D0201'.replace(/\s+/g, '').toLowerCase(); | ||||||
|  | 
 | ||||||
|  | x509.packSec1 = function (jwk) { | ||||||
|  |   var d = Enc.base64ToHex(jwk.d); | ||||||
|  |   var x = Enc.base64ToHex(jwk.x); | ||||||
|  |   var y = Enc.base64ToHex(jwk.y); | ||||||
|  |   var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384; | ||||||
|  |   return Enc.hexToUint8( | ||||||
|  |     ASN1('30' | ||||||
|  |     , ASN1.UInt('01') | ||||||
|  |     , ASN1('04', d) | ||||||
|  |     , ASN1('A0', objId) | ||||||
|  |     , ASN1('A1', ASN1.BitStr('04' + x + y))) | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | x509.packPkcs8 = function (jwk) { | ||||||
|  |   var d = Enc.base64ToHex(jwk.d); | ||||||
|  |   var x = Enc.base64ToHex(jwk.x); | ||||||
|  |   var y = Enc.base64ToHex(jwk.y); | ||||||
|  |   var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384; | ||||||
|  |   return Enc.hexToUint8( | ||||||
|  |     ASN1('30' | ||||||
|  |     , ASN1.UInt('00') | ||||||
|  |     , ASN1('30' | ||||||
|  |       , OBJ_ID_EC_PUB | ||||||
|  |       , objId | ||||||
|  |       ) | ||||||
|  |     , ASN1('04' | ||||||
|  |       , ASN1('30' | ||||||
|  |         , ASN1.UInt('01') | ||||||
|  |         , ASN1('04', d) | ||||||
|  |         , ASN1('A1', ASN1.BitStr('04' + x + y))))) | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | x509.packSpki = function (jwk) { | ||||||
|  |   var x = Enc.base64ToHex(jwk.x); | ||||||
|  |   var y = Enc.base64ToHex(jwk.y); | ||||||
|  |   var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384; | ||||||
|  |   return Enc.hexToUint8( | ||||||
|  |     ASN1('30' | ||||||
|  |     , ASN1('30' | ||||||
|  |       , OBJ_ID_EC_PUB | ||||||
|  |       , objId | ||||||
|  |       ) | ||||||
|  |     , ASN1.BitStr('04' + x + y)) | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | x509.packPkix = x509.packSpki; | ||||||
							
								
								
									
										104
									
								
								lib/keypairs.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								lib/keypairs.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,104 @@ | |||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | /*global Promise*/ | ||||||
|  | var keypairs = module.exports; | ||||||
|  | 
 | ||||||
|  | var PEM = require('./pem-parser.js'); | ||||||
|  | PEM.packBlock = require('./pem-packer.js').packBlock; | ||||||
|  | 
 | ||||||
|  | var ASN1 = require('./asn1-parser.js'); | ||||||
|  | ASN1.pack = require('./asn1-packer.js').pack; | ||||||
|  | 
 | ||||||
|  | var x509 = require('./x509-parser.js'); | ||||||
|  | 
 | ||||||
|  | var SSH = require('./ssh-parser.js'); | ||||||
|  | SSH.pack = require('./ssh-packer.js').pack; | ||||||
|  | 
 | ||||||
|  | // sign, signJws, signJwt
 | ||||||
|  | var JWS = require('./jws.js'); | ||||||
|  | var JWT = require('./jwt.js'); | ||||||
|  | 
 | ||||||
|  | var RSA = require('./rsa.js'); | ||||||
|  | var EC = require('./ec.js'); | ||||||
|  | 
 | ||||||
|  | keypairs.import = function (opts) { | ||||||
|  |   return Promise.resolve().then(function () { | ||||||
|  |     var jwk = opts.jwk; | ||||||
|  |     var pem; | ||||||
|  |     var der; | ||||||
|  |     var typ; | ||||||
|  | 
 | ||||||
|  |     if (opts.pem) { | ||||||
|  |       pem = PEM.parseBlock(opts.pem); | ||||||
|  |       if (/OPENSSH/.test(pem.type)) { | ||||||
|  |         jwk = SSH.parse(pem); | ||||||
|  |       } else { | ||||||
|  |         der = pem.bytes; | ||||||
|  |         jwk = x509.parse(der); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (opts.ssh) { | ||||||
|  |       jwk = SSH.parse(opts.ssh); | ||||||
|  |     } | ||||||
|  |     if (jwk) { | ||||||
|  |       // Both RSA and EC use 'd' as part of the private key
 | ||||||
|  |       if (jwk.d) { | ||||||
|  |         typ = 'PRIVATE KEY'; | ||||||
|  |         der = x509.pack({ jwk: jwk, format: 'pkcs8', encoding: 'pem' }); | ||||||
|  |       } else { | ||||||
|  |         typ = 'PUBLIC KEY'; | ||||||
|  |         der = x509.pack({ jwk: jwk, format: 'spki', encoding: 'pem' }); | ||||||
|  |       } | ||||||
|  |       pem = PEM.packBlock({ type: typ, bytes: der }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return { pem: pem, jwk: jwk }; | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | keypairs.export = function (opts) { | ||||||
|  |   // { pem, jwk, format, encoding }
 | ||||||
|  |   var format = opts.format; | ||||||
|  |   var encoding = opts.encoding; | ||||||
|  |   var jwk = opts.jwk; | ||||||
|  |   var pem = opts.pem; | ||||||
|  |   var der = opts.der; | ||||||
|  |   var pub = opts.public; | ||||||
|  | 
 | ||||||
|  |   if (opts.key) { | ||||||
|  |     if ('string' === typeof opts.key) { | ||||||
|  |       pem = opts.key; | ||||||
|  |     } else if (opts.key.d) { | ||||||
|  |       jwk = opts.key; | ||||||
|  |     } else if (opts.key.length) { | ||||||
|  |       der = opts.der; | ||||||
|  |     } else { | ||||||
|  |       throw new Error("'key' must be of type 'string' (PEM), 'object' (JWK), Buffer, or Array (DER)"); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (!format) { format = 'jwk'; } | ||||||
|  | 
 | ||||||
|  |   if (!jwk) { | ||||||
|  |     jwk = keypairs.import({ pem: pem }).jwk; | ||||||
|  |   } | ||||||
|  |   if (pub) { | ||||||
|  |     if ('RSA' === jwk.kty) { | ||||||
|  |       jwk = { kty: jwk.kty, n: jwk.n, e: jwk.e }; | ||||||
|  |     } else { | ||||||
|  |       jwk = { kty: jwk.kty, x: jwk.x, y: jwk.y }; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if ('jwk' === format) { | ||||||
|  |     if (encoding && 'json' !== encoding) { | ||||||
|  |       throw new Error("'encoding' must be 'json' for 'jwk'"); | ||||||
|  |     } | ||||||
|  |     return jwk; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if ('openssh' === format || 'ssh' === format) { | ||||||
|  |     // TODO if ('ssh' === format) { format = 'pkcs8'; }
 | ||||||
|  |     // TODO 'ssh2' public key is a special variant of pkcs8
 | ||||||
|  |     return SSH.pack({ jwk: jwk, public: opts.public }); | ||||||
|  |   } | ||||||
|  |   return x509.pack({ jwk: jwk, format: opts.format, encoding: opts.encoding, public: opts.public }); | ||||||
|  | }; | ||||||
							
								
								
									
										14
									
								
								lib/x509-packer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								lib/x509-packer.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | var x509 = module.exports; | ||||||
|  | 
 | ||||||
|  | var RSA = require('./rsa.js'); | ||||||
|  | var EC = require('./ec.js'); | ||||||
|  | 
 | ||||||
|  | x509.pack = function (opts) { | ||||||
|  |   if ('RSA' === opts.jwk.kty) { | ||||||
|  |     return RSA.pack(opts); | ||||||
|  |   } else { | ||||||
|  |     return EC.pack(opts); | ||||||
|  |   } | ||||||
|  | }; | ||||||
							
								
								
									
										119
									
								
								lib/x509-parser.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								lib/x509-parser.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,119 @@ | |||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | var Enc = require('./encoding.js'); | ||||||
|  | var x509 = module.exports; | ||||||
|  | 
 | ||||||
|  | // 1.2.840.10045.3.1.7
 | ||||||
|  | // prime256v1 (ANSI X9.62 named elliptic curve)
 | ||||||
|  | var OBJ_ID_EC  = '06 08 2A8648CE3D030107'.replace(/\s+/g, '').toLowerCase(); | ||||||
|  | // 1.3.132.0.34
 | ||||||
|  | // secp384r1 (SECG (Certicom) named elliptic curve)
 | ||||||
|  | var OBJ_ID_EC_384 = '06 05 2B81040022'.replace(/\s+/g, '').toLowerCase(); | ||||||
|  | 
 | ||||||
|  | x509.parseSec1 = function parseEcOnlyPrivkey(u8, jwk) { | ||||||
|  |   var index = 7; | ||||||
|  |   var len = 32; | ||||||
|  |   var olen = OBJ_ID_EC.length/2; | ||||||
|  | 
 | ||||||
|  |   if ("P-384" === jwk.crv) { | ||||||
|  |     olen = OBJ_ID_EC_384.length/2; | ||||||
|  |     index = 8; | ||||||
|  |     len = 48; | ||||||
|  |   } | ||||||
|  |   if (len !== u8[index - 1]) { | ||||||
|  |     throw new Error("Unexpected bitlength " + len); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // private part is d
 | ||||||
|  |   var d = u8.slice(index, index + len); | ||||||
|  |   // compression bit index
 | ||||||
|  |   var ci = index + len + 2 + olen + 2 + 3; | ||||||
|  |   var c = u8[ci]; | ||||||
|  |   var x, y; | ||||||
|  | 
 | ||||||
|  |   if (0x04 === c) { | ||||||
|  |     y = u8.slice(ci + 1 + len, ci + 1 + len + len); | ||||||
|  |   } else if (0x02 !== c) { | ||||||
|  |     throw new Error("not a supported EC private key"); | ||||||
|  |   } | ||||||
|  |   x = u8.slice(ci + 1, ci + 1 + len); | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     kty: jwk.kty | ||||||
|  |   , crv: jwk.crv | ||||||
|  |   , d: Enc.bufToUrlBase64(d) | ||||||
|  |   //, dh: Enc.bufToHex(d)
 | ||||||
|  |   , x: Enc.bufToUrlBase64(x) | ||||||
|  |   //, xh: Enc.bufToHex(x)
 | ||||||
|  |   , y: Enc.bufToUrlBase64(y) | ||||||
|  |   //, yh: Enc.bufToHex(y)
 | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | x509.parsePkcs8 = function parseEcPkcs8(u8, jwk) { | ||||||
|  |   var index = 24 + (OBJ_ID_EC.length/2); | ||||||
|  |   var len = 32; | ||||||
|  |   if ("P-384" === jwk.crv) { | ||||||
|  |     index = 24 + (OBJ_ID_EC_384.length/2) + 2; | ||||||
|  |     len = 48; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   //console.log(index, u8.slice(index));
 | ||||||
|  |   if (0x04 !== u8[index]) { | ||||||
|  |     //console.log(jwk);
 | ||||||
|  |     throw new Error("privkey not found"); | ||||||
|  |   } | ||||||
|  |   var d = u8.slice(index+2, index+2+len); | ||||||
|  |   var ci = index+2+len+5; | ||||||
|  |   var xi = ci+1; | ||||||
|  |   var x = u8.slice(xi, xi + len); | ||||||
|  |   var yi = xi+len; | ||||||
|  |   var y; | ||||||
|  |   if (0x04 === u8[ci]) { | ||||||
|  |     y = u8.slice(yi, yi + len); | ||||||
|  |   } else if (0x02 !== u8[ci]) { | ||||||
|  |     throw new Error("invalid compression bit (expected 0x04 or 0x02)"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     kty: jwk.kty | ||||||
|  |   , crv: jwk.crv | ||||||
|  |   , d: Enc.bufToUrlBase64(d) | ||||||
|  |   //, dh: Enc.bufToHex(d)
 | ||||||
|  |   , x: Enc.bufToUrlBase64(x) | ||||||
|  |   //, xh: Enc.bufToHex(x)
 | ||||||
|  |   , y: Enc.bufToUrlBase64(y) | ||||||
|  |   //, yh: Enc.bufToHex(y)
 | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | x509.parseSpki = function parsePem(u8, jwk) { | ||||||
|  |   var ci = 16 + OBJ_ID_EC.length/2; | ||||||
|  |   var len = 32; | ||||||
|  | 
 | ||||||
|  |   if ("P-384" === jwk.crv) { | ||||||
|  |     ci = 16 + OBJ_ID_EC_384.length/2; | ||||||
|  |     len = 48; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   var c = u8[ci]; | ||||||
|  |   var xi = ci + 1; | ||||||
|  |   var x = u8.slice(xi, xi + len); | ||||||
|  |   var yi = xi + len; | ||||||
|  |   var y; | ||||||
|  |   if (0x04 === c) { | ||||||
|  |     y = u8.slice(yi, yi + len); | ||||||
|  |   } else if (0x02 !== c) { | ||||||
|  |     throw new Error("not a supported EC private key"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     kty: jwk.kty | ||||||
|  |   , crv: jwk.crv | ||||||
|  |   , x: Enc.bufToUrlBase64(x) | ||||||
|  |   //, xh: Enc.bufToHex(x)
 | ||||||
|  |   , y: Enc.bufToUrlBase64(y) | ||||||
|  |   //, yh: Enc.bufToHex(y)
 | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | x509.parsePkix = x509.parseSpki; | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user