1 /** 2 * --------- 3 * |.##> <##.| Open Smart Card Development Platform (www.openscdp.org) 4 * |# #| 5 * |# #| Copyright (c) 1999-2006 CardContact Software & System Consulting 6 * |'##> <##'| Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de) 7 * --------- 8 * 9 * This file is part of OpenSCDP. 10 * 11 * OpenSCDP is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License version 2 as 13 * published by the Free Software Foundation. 14 * 15 * OpenSCDP is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with OpenSCDP; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 * 24 * @fileoverview EAC2CVCertificateGenerator - Simple CV certificate generator class 25 * based on "Advanced Security Mechanisms for Machine Readable Travel Documents", Version 2.0 26 * 27 * 28 * TODO: For now we only support ECC crypto 29 * 30 */ 31 32 load("tools/eccutils.js"); 33 34 if (typeof(__ScriptingServer) == "undefined") { 35 load("cvc.js"); 36 } 37 38 39 40 /** 41 * Define a generator object for CV certificates 42 * 43 * @class Class implementing a generator for CV certificates according to EAC 1.1 and EAC 2.0 specifications. 44 * 45 * @constructor 46 * @param {Crypto} crypto the crypto provider to be used 47 */ 48 function EAC2CVCertificateGenerator(crypto) { 49 this.crypto = crypto; 50 } 51 52 53 54 /** 55 * Convert x/y coordinates to uncompressed format 56 * 57 * @param {ByteString} x the x-coordinate of the point 58 * @param {ByteString} y the y-coordinate of the point 59 * @return the point in uncompressed format 60 * @type ByteString 61 */ 62 EAC2CVCertificateGenerator.encodeUncompressedECPoint = function(x,y) { 63 64 bb = new ByteBuffer(); 65 66 // uncompressed encoding 67 bb.append(new ByteString("04", HEX)); 68 bb.append(new ByteString(x, HEX)); 69 bb.append(new ByteString(y, HEX)); 70 71 return bb.toByteString(); 72 } 73 74 75 76 /** 77 * Decode x/y coordinates from uncompressed format 78 * 79 * @param {ByteString} uncompressedPoint the uncompressed point 80 * @return the x-/y-coordinate of the point 81 * @type ByteString 82 */ 83 EAC2CVCertificateGenerator.decodeUncompressedECPoint = function(uncompressedPoint) { 84 85 // Determine the size of the coordinates ignoring the indicator byte '04' 86 var length = uncompressedPoint.length - 1; 87 88 var sizeOfCoordinate = length / 2; 89 90 var xValue = uncompressedPoint.bytes(1, sizeOfCoordinate); 91 var yValue = uncompressedPoint.bytes(1 + sizeOfCoordinate, sizeOfCoordinate); 92 93 return { x:xValue, y:yValue }; 94 } 95 96 97 98 /** 99 * Convert integer to fixed length string with leading zeros. 100 * 101 * @private 102 * @param {Number} value the value to convert to a string. 103 * @param {Number} digits the number of digits in output string. Must be <= 20. 104 * @return the 0-padded string 105 * @type String 106 */ 107 EAC2CVCertificateGenerator.itos = function(value, digits) { 108 if (digits > 20) { 109 throw new Error("Digits must be <= 20"); 110 } 111 var str = "" + value; 112 str = "0000000000000000000".substr(19 - (digits - str.length)).concat(str); 113 return str; 114 } 115 116 117 118 /** 119 * Convert date to string with format YYMMDD. 120 * 121 * @param {Date} d the date object. 122 * @return the date/time string. 123 * @type String 124 */ 125 EAC2CVCertificateGenerator.dtos = function(d) { 126 var s = EAC2CVCertificateGenerator.itos(d.getFullYear() % 100, 2) + 127 EAC2CVCertificateGenerator.itos(d.getMonth() + 1, 2) + 128 EAC2CVCertificateGenerator.itos(d.getDate(), 2); 129 return s; 130 } 131 132 133 134 /** 135 * Set the profile identifier 136 * 137 * @param {Number} profileID the profile identifier 138 */ 139 EAC2CVCertificateGenerator.prototype.setProfileIdentifier = function(profileID) { 140 this.profileIdentifier = profileID; 141 } 142 143 144 145 /** 146 * Set the certification authority reference 147 * 148 * @param {String} CAR the CAR value 149 * @param {ByteString} CAR the CAR value 150 * @param {PublicKeyReference} CAR the CAR value 151 */ 152 EAC2CVCertificateGenerator.prototype.setCAR = function(CAR) { 153 if (CAR instanceof ByteString) { 154 this.CAR = CAR; 155 } else if (CAR instanceof PublicKeyReference) { 156 this.CAR = CAR.getBytes(); 157 } else { 158 this.CAR = new ByteString(CAR.toString(), ASCII); 159 } 160 } 161 162 163 164 /** 165 * Set the certificate holder reference 166 * 167 * @param {String} CHR the CHR value 168 * @param {ByteString} CHR the CHR value 169 * @param {PublicKeyReference} CHR the CHR value 170 */ 171 EAC2CVCertificateGenerator.prototype.setCHR = function(CHR) { 172 if (CHR instanceof ByteString) { 173 this.CHR = CHR; 174 } else if (CHR instanceof PublicKeyReference) { 175 this.CHR = CHR.getBytes(); 176 } else { 177 this.CHR = new ByteString(CHR.toString(), ASCII); 178 } 179 } 180 181 182 183 /** 184 * Set the effective date 185 * 186 * @param {String} effectiveDate the effective date in the format YYMMDD 187 * @param {Date} effectiveDate the effective date as Date object 188 */ 189 EAC2CVCertificateGenerator.prototype.setEffectiveDate = function(effectiveDate) { 190 if (effectiveDate instanceof Date) { 191 this.effectiveDate = EAC2CVCertificateGenerator.dtos(effectiveDate); 192 } else { 193 this.effectiveDate = effectiveDate; 194 } 195 } 196 197 198 199 /** 200 * Set the expiry date 201 * 202 * @param {String} expiryDate the expiry date in the format YYMMDD 203 * @param {Date} expiryDate the expiry date as Date object 204 */ 205 EAC2CVCertificateGenerator.prototype.setExpiryDate = function(expiryDate) { 206 if (expiryDate instanceof Date) { 207 this.expiryDate = EAC2CVCertificateGenerator.dtos(expiryDate); 208 } else { 209 this.expiryDate = expiryDate; 210 } 211 } 212 213 214 215 /** 216 * Set the object identifier of the authorization template for the generated certificate 217 * 218 * @param {ByteString} oid the object identifier for the chat 219 */ 220 EAC2CVCertificateGenerator.prototype.setChatOID = function(oid) { 221 this.chatOID = oid; 222 } 223 224 225 226 /** 227 * Set the authorization level of the authorization template for the generated certificate 228 * 229 * @param {ByteString} authLevel the encoded authorization level 230 */ 231 EAC2CVCertificateGenerator.prototype.setChatAuthorizationLevel = function(authLevel) { 232 this.chatAuthorizationLevel = authLevel; 233 } 234 235 236 237 /** 238 * Set the algorithm identifier for terminal authentication 239 * 240 * @param {ByteString} oid the object identifier as specified in appendix A.6.4 241 */ 242 EAC2CVCertificateGenerator.prototype.setTAAlgorithmIdentifier = function(oid) { 243 this.taOID = oid; 244 } 245 246 247 248 /** 249 * Set some additional extensions 250 * 251 * @param {Array of ASN.1 objects} extensions array containing the ASN.1 encoded extensions for the certificate 252 */ 253 EAC2CVCertificateGenerator.prototype.setExtensions = function(extensions) { 254 this.extensions = extensions; 255 } 256 257 258 259 /** 260 * Set the public key to be included in the certificate 261 * 262 * @param {Key} publicKey the public key object to be certified 263 */ 264 EAC2CVCertificateGenerator.prototype.setPublicKey = function(publicKey) { 265 this.publicKey = publicKey; 266 } 267 268 269 270 /** 271 * Set whether to include domain parameters in the certificate or not 272 * 273 * @param {Boolean} value the flag indicator 274 */ 275 EAC2CVCertificateGenerator.prototype.setIncludeDomainParameters = function(value) { 276 this.includeDomainParameters = value; 277 } 278 279 280 281 /** 282 * Internal functions for the generation of a certificate 283 * @private 284 */ 285 EAC2CVCertificateGenerator.prototype.getCAR = function() { 286 var t = new ASN1("Certification Authority Reference", 0x42, this.CAR); 287 return t; 288 } 289 290 291 292 /** 293 * Internal functions for the generation of a certificate 294 * @private 295 */ 296 EAC2CVCertificateGenerator.prototype.getCHR = function() { 297 var t = new ASN1("Certification Holder Reference", 0x5F20, this.CHR); 298 return t; 299 } 300 301 302 303 /** 304 * Internal functions for the generation of a certificate 305 * @private 306 */ 307 EAC2CVCertificateGenerator.convertDate = function(date) { 308 309 var temp = new ByteString(date, ASCII); 310 var bb = new ByteBuffer(); 311 var singleByte; 312 313 for (var i = 0; i < temp.length; i++) { 314 bb.append(temp.byteAt(i) - 0x30); 315 } 316 317 return bb.toByteString(); 318 } 319 320 321 322 /** 323 * Internal functions for the generation of a certificate 324 * @private 325 */ 326 EAC2CVCertificateGenerator.prototype.getEffectiveDate = function() { 327 var t = new ASN1("Certificate Effective Date", 0x5F25, 328 EAC2CVCertificateGenerator.convertDate(this.effectiveDate)); 329 return t; 330 } 331 332 333 334 /** 335 * Internal functions for the generation of a certificate 336 * @private 337 */ 338 EAC2CVCertificateGenerator.prototype.getExpiryDate = function() { 339 var t = new ASN1("Certificate Expiration Date", 0x5F24, 340 EAC2CVCertificateGenerator.convertDate(this.expiryDate)); 341 return t; 342 } 343 344 345 346 /** 347 * Internal functions for the generation of a certificate 348 * @private 349 */ 350 EAC2CVCertificateGenerator.prototype.getCHAT = function() { 351 var t = new ASN1("Certificate Holder Authorization Template", 0x7F4C); 352 353 var oid = new ASN1("Object Identifier", ASN1.OBJECT_IDENTIFIER, this.chatOID); 354 var authLevel = new ASN1("Authorization Level", 0x53, this.chatAuthorizationLevel); 355 356 t.add(oid); 357 t.add(authLevel); 358 359 return t; 360 } 361 362 363 364 /** 365 * Strips leading zeros of a ByteString 366 * 367 * @param {ByteString} value the ByteString value 368 * @return the stripped ByteString object, may be an empty ByteString 369 * @type ByteString 370 */ 371 EAC2CVCertificateGenerator.prototype.stripLeadingZeros = function(value) { 372 var i = 0; 373 for (; (i < value.length) && (value.byteAt(i) == 0); i++); 374 375 return value.right(value.length - i); 376 } 377 378 379 380 /** 381 * Get the encoded public key including domain parameters 382 * 383 * @private 384 */ 385 EAC2CVCertificateGenerator.prototype.getPublicKey = function() { 386 387 var t = new ASN1("Public Key", 0x7F49); 388 t.add(new ASN1("Object Identifier", 0x06, this.taOID)); 389 390 if (typeof(this.publicKey.getComponent(Key.ECC_P)) != "undefined") { 391 if (this.includeDomainParameters == true) { 392 393 t.add(new ASN1("Prime Modulus", 0x81, this.publicKey.getComponent(Key.ECC_P))); 394 t.add(new ASN1("First coefficient a", 0x82, this.publicKey.getComponent(Key.ECC_A))); 395 t.add(new ASN1("Second coefficient b", 0x83, this.publicKey.getComponent(Key.ECC_B))); 396 397 t.add(new ASN1("Base Point G", 0x84, EAC2CVCertificateGenerator.encodeUncompressedECPoint(this.publicKey.getComponent(Key.ECC_GX), this.publicKey.getComponent(Key.ECC_GY)))); 398 399 t.add(new ASN1("Order of the base point", 0x85, this.publicKey.getComponent(Key.ECC_N))); 400 } 401 402 t.add(new ASN1("Public Point y", 0x86, EAC2CVCertificateGenerator.encodeUncompressedECPoint(this.publicKey.getComponent(Key.ECC_QX), this.publicKey.getComponent(Key.ECC_QY)))); 403 404 if (this.includeDomainParameters == true) { 405 t.add(new ASN1("Cofactor f", 0x87, this.stripLeadingZeros(this.publicKey.getComponent(Key.ECC_H)))); 406 } 407 } else { 408 t.add(new ASN1("Composite Modulus", 0x81, this.publicKey.getComponent(Key.MODULUS))); 409 t.add(new ASN1("Public Exponent", 0x82, this.publicKey.getComponent(Key.EXPONENT))); 410 } 411 412 return t; 413 } 414 415 416 417 /** 418 * Internal functions for the generation of a certificate 419 * @private 420 */ 421 EAC2CVCertificateGenerator.prototype.getProfileIdentifier = function() { 422 423 var bb = new ByteBuffer(); 424 bb.append(this.profileIdentifier); 425 426 var t = new ASN1("Certificate Profile Identifier", 0x5F29, bb.toByteString()); 427 return t; 428 } 429 430 431 432 /** 433 * Internal functions for the generation of a certificate 434 * @private 435 */ 436 EAC2CVCertificateGenerator.prototype.getExtensions = function() { 437 var t = new ASN1("Certificate Extensions", 0x65); 438 for (var i = 0; i < this.extensions.length; i++) 439 t.add(this.extensions[i]); 440 return t; 441 } 442 443 444 445 /** 446 * Internal functions for the generation of a certificate 447 * @private 448 */ 449 EAC2CVCertificateGenerator.prototype.getCertificateBody = function() { 450 451 var t = new ASN1("Certificate Body", 0x7F4E); 452 453 t.add(this.getProfileIdentifier()); 454 455 t.add(this.getCAR()); 456 457 t.add(this.getPublicKey()); 458 459 t.add(this.getCHR()); 460 461 t.add(this.getCHAT()); 462 463 t.add(this.getEffectiveDate()); 464 465 t.add(this.getExpiryDate()); 466 467 if (this.extensions) { 468 t.add(this.getExtensions()); 469 } 470 471 return t; 472 } 473 474 475 476 /** 477 * Generate a certificate based on the parameter set using the setter methods. 478 * 479 * @param {Key} signingKey the key to be used for signing the certificate 480 * @param {ByteString} taOID the object identifier associated with the signing key 481 * @return the CVC certificate 482 * @type CVC 483 */ 484 EAC2CVCertificateGenerator.prototype.generateCVCertificate = function(signingKey, outertaOID) { 485 486 var certificate = new ASN1("CV Certificate", 0x7F21); 487 488 var body = this.getCertificateBody(); 489 490 if (typeof(outertaOID) == "undefined") { 491 outertaOID = this.taOID; 492 } 493 var mech = CVC.getSignatureMech(outertaOID); 494 var signature = this.crypto.sign(signingKey, mech, body.getBytes()); 495 496 if (CVC.isECDSA(outertaOID)) { 497 var keylen = signingKey.getSize() >> 3; 498 var signatureValue = new ASN1("Signature", 0x5F37, ECCUtils.unwrapSignature(signature, keylen)); 499 } else { 500 var signatureValue = new ASN1("Signature", 0x5F37, signature); 501 } 502 503 certificate.add(body); 504 505 certificate.add(signatureValue); 506 507 return new CVC(certificate); 508 } 509