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