/*! x509-1.1.3.js (c) 2012-2014 Kenji Urushima | kjur.github.com/jsrsasign/license */ /* * x509.js - X509 class to read subject public key from certificate. * * Copyright (c) 2010-2014 Kenji Urushima (kenji.urushima@gmail.com) * * This software is licensed under the terms of the MIT License. * http://kjur.github.com/jsrsasign/license * * The above copyright and license notice shall be * included in all copies or substantial portions of the Software. */ /** * @fileOverview * @name x509-1.1.js * @author Kenji Urushima kenji.urushima@gmail.com * @version x509 1.1.3 (2014-May-17) * @since jsrsasign 1.x.x * @license MIT License */ /* * Depends: * base64.js * rsa.js * asn1hex.js */ /** * X.509 certificate class.
* @class X.509 certificate class * @property {RSAKey} subjectPublicKeyRSA Tom Wu's RSAKey object * @property {String} subjectPublicKeyRSA_hN hexadecimal string for modulus of RSA public key * @property {String} subjectPublicKeyRSA_hE hexadecimal string for public exponent of RSA public key * @property {String} hex hexacedimal string for X.509 certificate. * @author Kenji Urushima * @version 1.0.1 (08 May 2012) * @see 'jwrsasign'(RSA Sign JavaScript Library) home page http://kjur.github.com/jsrsasign/ */ function X509() { this.subjectPublicKeyRSA = null; this.subjectPublicKeyRSA_hN = null; this.subjectPublicKeyRSA_hE = null; this.hex = null; // ===== get basic fields from hex ===================================== /** * get hexadecimal string of serialNumber field of certificate.
* @name getSerialNumberHex * @memberOf X509# * @function */ this.getSerialNumberHex = function() { return ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 1]); }; /** * get hexadecimal string of issuer field TLV of certificate.
* @name getIssuerHex * @memberOf X509# * @function */ this.getIssuerHex = function() { return ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 3]); }; /** * get string of issuer field of certificate.
* @name getIssuerString * @memberOf X509# * @function */ this.getIssuerString = function() { return X509.hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 3])); }; /** * get hexadecimal string of subject field of certificate.
* @name getSubjectHex * @memberOf X509# * @function */ this.getSubjectHex = function() { return ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 5]); }; /** * get string of subject field of certificate.
* @name getSubjectString * @memberOf X509# * @function */ this.getSubjectString = function() { return X509.hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 5])); }; /** * get notBefore field string of certificate.
* @name getNotBefore * @memberOf X509# * @function */ this.getNotBefore = function() { var s = ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 4, 0]); s = s.replace(/(..)/g, "%$1"); s = decodeURIComponent(s); return s; }; /** * get notAfter field string of certificate.
* @name getNotAfter * @memberOf X509# * @function */ this.getNotAfter = function() { var s = ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 4, 1]); s = s.replace(/(..)/g, "%$1"); s = decodeURIComponent(s); return s; }; // ===== read certificate public key ========================== // ===== read certificate ===================================== /** * read PEM formatted X.509 certificate from string.
* @name readCertPEM * @memberOf X509# * @function * @param {String} sCertPEM string for PEM formatted X.509 certificate */ this.readCertPEM = function(sCertPEM) { var hCert = X509.pemToHex(sCertPEM); var a = X509.getPublicKeyHexArrayFromCertHex(hCert); var rsa = new RSAKey(); rsa.setPublic(a[0], a[1]); this.subjectPublicKeyRSA = rsa; this.subjectPublicKeyRSA_hN = a[0]; this.subjectPublicKeyRSA_hE = a[1]; this.hex = hCert; }; this.readCertPEMWithoutRSAInit = function(sCertPEM) { var hCert = X509.pemToHex(sCertPEM); var a = X509.getPublicKeyHexArrayFromCertHex(hCert); this.subjectPublicKeyRSA.setPublic(a[0], a[1]); this.subjectPublicKeyRSA_hN = a[0]; this.subjectPublicKeyRSA_hE = a[1]; this.hex = hCert; }; }; X509.pemToBase64 = function(sCertPEM) { var s = sCertPEM; s = s.replace("-----BEGIN CERTIFICATE-----", ""); s = s.replace("-----END CERTIFICATE-----", ""); s = s.replace(/[ \n]+/g, ""); return s; }; X509.pemToHex = function(sCertPEM) { var b64Cert = X509.pemToBase64(sCertPEM); var hCert = b64tohex(b64Cert); return hCert; }; // NOTE: Without BITSTRING encapsulation. X509.getSubjectPublicKeyPosFromCertHex = function(hCert) { var pInfo = X509.getSubjectPublicKeyInfoPosFromCertHex(hCert); if (pInfo == -1) return -1; var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pInfo); if (a.length != 2) return -1; var pBitString = a[1]; if (hCert.substring(pBitString, pBitString + 2) != '03') return -1; var pBitStringV = ASN1HEX.getStartPosOfV_AtObj(hCert, pBitString); if (hCert.substring(pBitStringV, pBitStringV + 2) != '00') return -1; return pBitStringV + 2; }; // NOTE: privateKeyUsagePeriod field of X509v2 not supported. // NOTE: v1 and v3 supported X509.getSubjectPublicKeyInfoPosFromCertHex = function(hCert) { var pTbsCert = ASN1HEX.getStartPosOfV_AtObj(hCert, 0); var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pTbsCert); if (a.length < 1) return -1; if (hCert.substring(a[0], a[0] + 10) == "a003020102") { // v3 if (a.length < 6) return -1; return a[6]; } else { if (a.length < 5) return -1; return a[5]; } }; X509.getPublicKeyHexArrayFromCertHex = function(hCert) { var p = X509.getSubjectPublicKeyPosFromCertHex(hCert); var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, p); if (a.length != 2) return []; var hN = ASN1HEX.getHexOfV_AtObj(hCert, a[0]); var hE = ASN1HEX.getHexOfV_AtObj(hCert, a[1]); if (hN != null && hE != null) { return [hN, hE]; } else { return []; } }; X509.getHexTbsCertificateFromCert = function(hCert) { var pTbsCert = ASN1HEX.getStartPosOfV_AtObj(hCert, 0); return pTbsCert; }; X509.getPublicKeyHexArrayFromCertPEM = function(sCertPEM) { var hCert = X509.pemToHex(sCertPEM); var a = X509.getPublicKeyHexArrayFromCertHex(hCert); return a; }; X509.hex2dn = function(hDN) { var s = ""; var a = ASN1HEX.getPosArrayOfChildren_AtObj(hDN, 0); for (var i = 0; i < a.length; i++) { var hRDN = ASN1HEX.getHexOfTLV_AtObj(hDN, a[i]); s = s + "/" + X509.hex2rdn(hRDN); } return s; }; X509.hex2rdn = function(hRDN) { var hType = ASN1HEX.getDecendantHexTLVByNthList(hRDN, 0, [0, 0]); var hValue = ASN1HEX.getDecendantHexVByNthList(hRDN, 0, [0, 1]); var type = ""; try { type = X509.DN_ATTRHEX[hType]; } catch (ex) { type = hType; } hValue = hValue.replace(/(..)/g, "%$1"); var value = decodeURIComponent(hValue); return type + "=" + value; }; X509.DN_ATTRHEX = { "0603550406": "C", "060355040a": "O", "060355040b": "OU", "0603550403": "CN", "0603550405": "SN", "0603550408": "ST", "0603550407": "L", }; /** * get RSAKey/ECDSA public key object from PEM certificate string * @name getPublicKeyFromCertPEM * @memberOf X509 * @function * @param {String} sCertPEM PEM formatted RSA/ECDSA/DSA X.509 certificate * @return returns RSAKey/KJUR.crypto.{ECDSA,DSA} object of public key * @since x509 1.1.1 * @description * NOTE: DSA is also supported since x509 1.1.2. */ X509.getPublicKeyFromCertPEM = function(sCertPEM) { var info = X509.getPublicKeyInfoPropOfCertPEM(sCertPEM); if (info.algoid == "2a864886f70d010101") { // RSA var aRSA = KEYUTIL.parsePublicRawRSAKeyHex(info.keyhex); var key = new RSAKey(); key.setPublic(aRSA.n, aRSA.e); return key; } else if (info.algoid == "2a8648ce3d0201") { // ECC var curveName = KJUR.crypto.OID.oidhex2name[info.algparam]; var key = new KJUR.crypto.ECDSA({'curve': curveName, 'info': info.keyhex}); key.setPublicKeyHex(info.keyhex); return key; } else if (info.algoid == "2a8648ce380401") { // DSA 1.2.840.10040.4.1 var p = ASN1HEX.getVbyList(info.algparam, 0, [0], "02"); var q = ASN1HEX.getVbyList(info.algparam, 0, [1], "02"); var g = ASN1HEX.getVbyList(info.algparam, 0, [2], "02"); var y = ASN1HEX.getHexOfV_AtObj(info.keyhex, 0); y = y.substr(2); var key = new KJUR.crypto.DSA(); key.setPublic(new BigInteger(p, 16), new BigInteger(q, 16), new BigInteger(g, 16), new BigInteger(y, 16)); return key; } else { throw "unsupported key"; } }; /** * get public key information from PEM certificate * @name getPublicKeyInfoPropOfCertPEM * @memberOf X509 * @function * @param {String} sCertPEM string of PEM formatted certificate * @return {Hash} hash of information for public key * @since x509 1.1.1 * @description * Resulted associative array has following properties: * * @since x509 1.1.1 */ X509.getPublicKeyInfoPropOfCertPEM = function(sCertPEM) { var result = {}; result.algparam = null; var hCert = X509.pemToHex(sCertPEM); // 1. Certificate ASN.1 var a1 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, 0); if (a1.length != 3) throw "malformed X.509 certificate PEM (code:001)"; // not 3 item of seq Cert // 2. tbsCertificate if (hCert.substr(a1[0], 2) != "30") throw "malformed X.509 certificate PEM (code:002)"; // tbsCert not seq var a2 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a1[0]); // 3. subjectPublicKeyInfo if (a2.length < 7) throw "malformed X.509 certificate PEM (code:003)"; // no subjPubKeyInfo var a3 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a2[6]); if (a3.length != 2) throw "malformed X.509 certificate PEM (code:004)"; // not AlgId and PubKey // 4. AlgId var a4 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a3[0]); if (a4.length != 2) throw "malformed X.509 certificate PEM (code:005)"; // not 2 item in AlgId result.algoid = ASN1HEX.getHexOfV_AtObj(hCert, a4[0]); if (hCert.substr(a4[1], 2) == "06") { // EC result.algparam = ASN1HEX.getHexOfV_AtObj(hCert, a4[1]); } else if (hCert.substr(a4[1], 2) == "30") { // DSA result.algparam = ASN1HEX.getHexOfTLV_AtObj(hCert, a4[1]); } // 5. Public Key Hex if (hCert.substr(a3[1], 2) != "03") throw "malformed X.509 certificate PEM (code:006)"; // not bitstring var unusedBitAndKeyHex = ASN1HEX.getHexOfV_AtObj(hCert, a3[1]); result.keyhex = unusedBitAndKeyHex.substr(2); return result; }; /* X509.prototype.readCertPEM = _x509_readCertPEM; X509.prototype.readCertPEMWithoutRSAInit = _x509_readCertPEMWithoutRSAInit; X509.prototype.getSerialNumberHex = _x509_getSerialNumberHex; X509.prototype.getIssuerHex = _x509_getIssuerHex; X509.prototype.getSubjectHex = _x509_getSubjectHex; X509.prototype.getIssuerString = _x509_getIssuerString; X509.prototype.getSubjectString = _x509_getSubjectString; X509.prototype.getNotBefore = _x509_getNotBefore; X509.prototype.getNotAfter = _x509_getNotAfter; */