1 /*! x509-1.1.2.js (c) 2012 Kenji Urushima | kjur.github.com/jsrsasign/license 2 */ 3 /* 4 * x509.js - X509 class to read subject public key from certificate. 5 * 6 * Copyright (c) 2010-2013 Kenji Urushima (kenji.urushima@gmail.com) 7 * 8 * This software is licensed under the terms of the MIT License. 9 * http://kjur.github.com/jsrsasign/license 10 * 11 * The above copyright and license notice shall be 12 * included in all copies or substantial portions of the Software. 13 */ 14 15 /** 16 * @fileOverview 17 * @name x509-1.1.js 18 * @author Kenji Urushima kenji.urushima@gmail.com 19 * @version x509 1.1.2 (2013-Oct-06) 20 * @since jsrsasign 1.x.x 21 * @license <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a> 22 */ 23 24 /* 25 * Depends: 26 * base64.js 27 * rsa.js 28 * asn1hex.js 29 */ 30 31 /** 32 * X.509 certificate class.<br/> 33 * @class X.509 certificate class 34 * @property {RSAKey} subjectPublicKeyRSA Tom Wu's RSAKey object 35 * @property {String} subjectPublicKeyRSA_hN hexadecimal string for modulus of RSA public key 36 * @property {String} subjectPublicKeyRSA_hE hexadecimal string for public exponent of RSA public key 37 * @property {String} hex hexacedimal string for X.509 certificate. 38 * @author Kenji Urushima 39 * @version 1.0.1 (08 May 2012) 40 * @see <a href="http://kjur.github.com/jsrsasigns/">'jwrsasign'(RSA Sign JavaScript Library) home page http://kjur.github.com/jsrsasign/</a> 41 */ 42 function X509() { 43 this.subjectPublicKeyRSA = null; 44 this.subjectPublicKeyRSA_hN = null; 45 this.subjectPublicKeyRSA_hE = null; 46 this.hex = null; 47 48 // ===== get basic fields from hex ===================================== 49 50 /** 51 * get hexadecimal string of serialNumber field of certificate.<br/> 52 * @name getSerialNumberHex 53 * @memberOf X509# 54 * @function 55 */ 56 this.getSerialNumberHex = function() { 57 return ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 1]); 58 }; 59 60 /** 61 * get hexadecimal string of issuer field of certificate.<br/> 62 * @name getIssuerHex 63 * @memberOf X509# 64 * @function 65 */ 66 this.getIssuerHex = function() { 67 return ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 3]); 68 }; 69 70 /** 71 * get string of issuer field of certificate.<br/> 72 * @name getIssuerString 73 * @memberOf X509# 74 * @function 75 */ 76 this.getIssuerString = function() { 77 return X509.hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 3])); 78 }; 79 80 /** 81 * get hexadecimal string of subject field of certificate.<br/> 82 * @name getSubjectHex 83 * @memberOf X509# 84 * @function 85 */ 86 this.getSubjectHex = function() { 87 return ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 5]); 88 }; 89 90 /** 91 * get string of subject field of certificate.<br/> 92 * @name getSubjectString 93 * @memberOf X509# 94 * @function 95 */ 96 this.getSubjectString = function() { 97 return X509.hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 5])); 98 }; 99 100 /** 101 * get notBefore field string of certificate.<br/> 102 * @name getNotBefore 103 * @memberOf X509# 104 * @function 105 */ 106 this.getNotBefore = function() { 107 var s = ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 4, 0]); 108 s = s.replace(/(..)/g, "%$1"); 109 s = decodeURIComponent(s); 110 return s; 111 }; 112 113 /** 114 * get notAfter field string of certificate.<br/> 115 * @name getNotAfter 116 * @memberOf X509# 117 * @function 118 */ 119 this.getNotAfter = function() { 120 var s = ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 4, 1]); 121 s = s.replace(/(..)/g, "%$1"); 122 s = decodeURIComponent(s); 123 return s; 124 }; 125 126 // ===== read certificate public key ========================== 127 128 // ===== read certificate ===================================== 129 /** 130 * read PEM formatted X.509 certificate from string.<br/> 131 * @name readCertPEM 132 * @memberOf X509# 133 * @function 134 * @param {String} sCertPEM string for PEM formatted X.509 certificate 135 */ 136 this.readCertPEM = function(sCertPEM) { 137 var hCert = X509.pemToHex(sCertPEM); 138 var a = X509.getPublicKeyHexArrayFromCertHex(hCert); 139 var rsa = new RSAKey(); 140 rsa.setPublic(a[0], a[1]); 141 this.subjectPublicKeyRSA = rsa; 142 this.subjectPublicKeyRSA_hN = a[0]; 143 this.subjectPublicKeyRSA_hE = a[1]; 144 this.hex = hCert; 145 }; 146 147 this.readCertPEMWithoutRSAInit = function(sCertPEM) { 148 var hCert = X509.pemToHex(sCertPEM); 149 var a = X509.getPublicKeyHexArrayFromCertHex(hCert); 150 this.subjectPublicKeyRSA.setPublic(a[0], a[1]); 151 this.subjectPublicKeyRSA_hN = a[0]; 152 this.subjectPublicKeyRSA_hE = a[1]; 153 this.hex = hCert; 154 }; 155 }; 156 157 X509.pemToBase64 = function(sCertPEM) { 158 var s = sCertPEM; 159 s = s.replace("-----BEGIN CERTIFICATE-----", ""); 160 s = s.replace("-----END CERTIFICATE-----", ""); 161 s = s.replace(/[ \n]+/g, ""); 162 return s; 163 }; 164 165 X509.pemToHex = function(sCertPEM) { 166 var b64Cert = X509.pemToBase64(sCertPEM); 167 var hCert = b64tohex(b64Cert); 168 return hCert; 169 }; 170 171 // NOTE: Without BITSTRING encapsulation. 172 X509.getSubjectPublicKeyPosFromCertHex = function(hCert) { 173 var pInfo = X509.getSubjectPublicKeyInfoPosFromCertHex(hCert); 174 if (pInfo == -1) return -1; 175 var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pInfo); 176 if (a.length != 2) return -1; 177 var pBitString = a[1]; 178 if (hCert.substring(pBitString, pBitString + 2) != '03') return -1; 179 var pBitStringV = ASN1HEX.getStartPosOfV_AtObj(hCert, pBitString); 180 181 if (hCert.substring(pBitStringV, pBitStringV + 2) != '00') return -1; 182 return pBitStringV + 2; 183 }; 184 185 // NOTE: privateKeyUsagePeriod field of X509v2 not supported. 186 // NOTE: v1 and v3 supported 187 X509.getSubjectPublicKeyInfoPosFromCertHex = function(hCert) { 188 var pTbsCert = ASN1HEX.getStartPosOfV_AtObj(hCert, 0); 189 var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pTbsCert); 190 if (a.length < 1) return -1; 191 if (hCert.substring(a[0], a[0] + 10) == "a003020102") { // v3 192 if (a.length < 6) return -1; 193 return a[6]; 194 } else { 195 if (a.length < 5) return -1; 196 return a[5]; 197 } 198 }; 199 200 X509.getPublicKeyHexArrayFromCertHex = function(hCert) { 201 var p = X509.getSubjectPublicKeyPosFromCertHex(hCert); 202 var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, p); 203 if (a.length != 2) return []; 204 var hN = ASN1HEX.getHexOfV_AtObj(hCert, a[0]); 205 var hE = ASN1HEX.getHexOfV_AtObj(hCert, a[1]); 206 if (hN != null && hE != null) { 207 return [hN, hE]; 208 } else { 209 return []; 210 } 211 }; 212 213 X509.getHexTbsCertificateFromCert = function(hCert) { 214 var pTbsCert = ASN1HEX.getStartPosOfV_AtObj(hCert, 0); 215 return pTbsCert; 216 }; 217 218 X509.getPublicKeyHexArrayFromCertPEM = function(sCertPEM) { 219 var hCert = X509.pemToHex(sCertPEM); 220 var a = X509.getPublicKeyHexArrayFromCertHex(hCert); 221 return a; 222 }; 223 224 X509.hex2dn = function(hDN) { 225 var s = ""; 226 var a = ASN1HEX.getPosArrayOfChildren_AtObj(hDN, 0); 227 for (var i = 0; i < a.length; i++) { 228 var hRDN = ASN1HEX.getHexOfTLV_AtObj(hDN, a[i]); 229 s = s + "/" + X509.hex2rdn(hRDN); 230 } 231 return s; 232 }; 233 234 X509.hex2rdn = function(hRDN) { 235 var hType = ASN1HEX.getDecendantHexTLVByNthList(hRDN, 0, [0, 0]); 236 var hValue = ASN1HEX.getDecendantHexVByNthList(hRDN, 0, [0, 1]); 237 var type = ""; 238 try { type = X509.DN_ATTRHEX[hType]; } catch (ex) { type = hType; } 239 hValue = hValue.replace(/(..)/g, "%$1"); 240 var value = decodeURIComponent(hValue); 241 return type + "=" + value; 242 }; 243 244 X509.DN_ATTRHEX = { 245 "0603550406": "C", 246 "060355040a": "O", 247 "060355040b": "OU", 248 "0603550403": "CN", 249 "0603550405": "SN", 250 "0603550408": "ST", 251 "0603550407": "L", 252 }; 253 254 /** 255 * get RSAKey/ECDSA public key object from PEM certificate string 256 * @name getPublicKeyFromCertPEM 257 * @memberOf X509 258 * @function 259 * @param {String} sCertPEM PEM formatted RSA/ECDSA/DSA X.509 certificate 260 * @return returns RSAKey/KJUR.crypto.{ECDSA,DSA} object of public key 261 * @since x509 1.1.1 262 * @description 263 * NOTE: DSA is also supported since x509 1.1.2. 264 */ 265 X509.getPublicKeyFromCertPEM = function(sCertPEM) { 266 var info = X509.getPublicKeyInfoPropOfCertPEM(sCertPEM); 267 268 if (info.algoid == "2a864886f70d010101") { // RSA 269 var aRSA = KEYUTIL.parsePublicRawRSAKeyHex(info.keyhex); 270 var key = new RSAKey(); 271 key.setPublic(aRSA.n, aRSA.e); 272 return key; 273 } else if (info.algoid == "2a8648ce3d0201") { // ECC 274 var curveName = KJUR.crypto.OID.oidhex2name[info.algparam]; 275 var key = new KJUR.crypto.ECDSA({'curve': curveName, 'info': info.keyhex}); 276 key.setPublicKeyHex(info.keyhex); 277 return key; 278 } else if (info.algoid == "2a8648ce380401") { // DSA 1.2.840.10040.4.1 279 var p = ASN1HEX.getVbyList(info.algparam, 0, [0], "02"); 280 var q = ASN1HEX.getVbyList(info.algparam, 0, [1], "02"); 281 var g = ASN1HEX.getVbyList(info.algparam, 0, [2], "02"); 282 var y = ASN1HEX.getHexOfV_AtObj(info.keyhex, 0); 283 y = y.substr(2); 284 var key = new KJUR.crypto.DSA(); 285 key.setPublic(new BigInteger(p, 16), 286 new BigInteger(q, 16), 287 new BigInteger(g, 16), 288 new BigInteger(y, 16)); 289 return key; 290 } else { 291 throw "unsupported key"; 292 } 293 }; 294 295 /** 296 * get public key information from PEM certificate 297 * @name getPublicKeyInfoPropOfCertPEM 298 * @memberOf X509 299 * @function 300 * @param {String} sCertPEM string of PEM formatted certificate 301 * @return {Hash} hash of information for public key 302 * @since x509 1.1.1 303 * @description 304 * Resulted associative array has following properties: 305 * <ul> 306 * <li>algoid - hexadecimal string of OID of asymmetric key algorithm</li> 307 * <li>algparam - hexadecimal string of OID of ECC curve name or null</li> 308 * <li>keyhex - hexadecimal string of key in the certificate</li> 309 * </ul> 310 * @since x509 1.1.1 311 */ 312 X509.getPublicKeyInfoPropOfCertPEM = function(sCertPEM) { 313 var result = {}; 314 result.algparam = null; 315 var hCert = X509.pemToHex(sCertPEM); 316 317 // 1. Certificate ASN.1 318 var a1 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, 0); 319 if (a1.length != 3) 320 throw "malformed X.509 certificate PEM (code:001)"; // not 3 item of seq Cert 321 322 // 2. tbsCertificate 323 if (hCert.substr(a1[0], 2) != "30") 324 throw "malformed X.509 certificate PEM (code:002)"; // tbsCert not seq 325 326 var a2 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a1[0]); 327 328 // 3. subjectPublicKeyInfo 329 if (a2.length < 7) 330 throw "malformed X.509 certificate PEM (code:003)"; // no subjPubKeyInfo 331 332 var a3 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a2[6]); 333 334 if (a3.length != 2) 335 throw "malformed X.509 certificate PEM (code:004)"; // not AlgId and PubKey 336 337 // 4. AlgId 338 var a4 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a3[0]); 339 340 if (a4.length != 2) 341 throw "malformed X.509 certificate PEM (code:005)"; // not 2 item in AlgId 342 343 result.algoid = ASN1HEX.getHexOfV_AtObj(hCert, a4[0]); 344 345 if (hCert.substr(a4[1], 2) == "06") { // EC 346 result.algparam = ASN1HEX.getHexOfV_AtObj(hCert, a4[1]); 347 } else if (hCert.substr(a4[1], 2) == "30") { // DSA 348 result.algparam = ASN1HEX.getHexOfTLV_AtObj(hCert, a4[1]); 349 } 350 351 // 5. Public Key Hex 352 if (hCert.substr(a3[1], 02) != "03") 353 throw "malformed X.509 certificate PEM (code:006)"; // not bitstring 354 355 var unusedBitAndKeyHex = ASN1HEX.getHexOfV_AtObj(hCert, a3[1]); 356 result.keyhex = unusedBitAndKeyHex.substr(2); 357 358 return result; 359 }; 360 361 /* 362 X509.prototype.readCertPEM = _x509_readCertPEM; 363 X509.prototype.readCertPEMWithoutRSAInit = _x509_readCertPEMWithoutRSAInit; 364 X509.prototype.getSerialNumberHex = _x509_getSerialNumberHex; 365 X509.prototype.getIssuerHex = _x509_getIssuerHex; 366 X509.prototype.getSubjectHex = _x509_getSubjectHex; 367 X509.prototype.getIssuerString = _x509_getIssuerString; 368 X509.prototype.getSubjectString = _x509_getSubjectString; 369 X509.prototype.getNotBefore = _x509_getNotBefore; 370 X509.prototype.getNotAfter = _x509_getNotAfter; 371 */ 372