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 // Imports 29 var CVC = require('scsh/eac/CVC').CVC; 30 var PublicKeyReference = require('scsh/eac/PublicKeyReference').PublicKeyReference; 31 32 33 34 /** 35 * Define a generator object for CV certificates 36 * 37 * @class Class implementing a generator for CV certificates according to EAC 1.1 and EAC 2.0 specifications. 38 * 39 * @constructor 40 * @param {Crypto} crypto the crypto provider to be used 41 */ 42 function EAC2CVCertificateGenerator(crypto) { 43 this.crypto = crypto; 44 } 45 46 exports.EAC2CVCertificateGenerator = EAC2CVCertificateGenerator; 47 48 49 /** 50 * Convert x/y coordinates to uncompressed format 51 * 52 * @param {ByteString} x the x-coordinate of the point 53 * @param {ByteString} y the y-coordinate of the point 54 * @return the point in uncompressed format 55 * @type ByteString 56 */ 57 EAC2CVCertificateGenerator.encodeUncompressedECPoint = function(x,y) { 58 59 var bb = new ByteBuffer(); 60 61 // uncompressed encoding 62 bb.append(new ByteString("04", HEX)); 63 bb.append(new ByteString(x, HEX)); 64 bb.append(new ByteString(y, HEX)); 65 66 return bb.toByteString(); 67 } 68 69 70 71 /** 72 * Decode x/y coordinates from uncompressed format 73 * 74 * @param {ByteString} uncompressedPoint the uncompressed point 75 * @return the x-/y-coordinate of the point 76 * @type ByteString 77 */ 78 EAC2CVCertificateGenerator.decodeUncompressedECPoint = function(uncompressedPoint) { 79 80 // Determine the size of the coordinates ignoring the indicator byte '04' 81 var length = uncompressedPoint.length - 1; 82 83 var sizeOfCoordinate = length / 2; 84 85 var xValue = uncompressedPoint.bytes(1, sizeOfCoordinate); 86 var yValue = uncompressedPoint.bytes(1 + sizeOfCoordinate, sizeOfCoordinate); 87 88 return { x:xValue, y:yValue }; 89 } 90 91 92 93 /** 94 * Convert integer to fixed length string with leading zeros. 95 * 96 * @private 97 * @param {Number} value the value to convert to a string. 98 * @param {Number} digits the number of digits in output string. Must be <= 20. 99 * @return the 0-padded string 100 * @type String 101 */ 102 EAC2CVCertificateGenerator.itos = function(value, digits) { 103 if (digits > 20) { 104 throw new Error("Digits must be <= 20"); 105 } 106 var str = "" + value; 107 str = "0000000000000000000".substr(19 - (digits - str.length)).concat(str); 108 return str; 109 } 110 111 112 113 /** 114 * Convert date to string with format YYMMDD. 115 * 116 * @param {Date} d the date object. 117 * @return the date/time string. 118 * @type String 119 */ 120 EAC2CVCertificateGenerator.dtos = function(d) { 121 var s = EAC2CVCertificateGenerator.itos(d.getFullYear() % 100, 2) + 122 EAC2CVCertificateGenerator.itos(d.getMonth() + 1, 2) + 123 EAC2CVCertificateGenerator.itos(d.getDate(), 2); 124 return s; 125 } 126 127 128 129 /** 130 * Set the profile identifier 131 * 132 * @param {Number} profileID the profile identifier 133 */ 134 EAC2CVCertificateGenerator.prototype.setProfileIdentifier = function(profileID) { 135 this.profileIdentifier = profileID; 136 } 137 138 139 140 /** 141 * Set the certification authority reference 142 * 143 * @param {String} CAR the CAR value 144 * @param {ByteString} CAR the CAR value 145 * @param {PublicKeyReference} CAR the CAR value 146 */ 147 EAC2CVCertificateGenerator.prototype.setCAR = function(CAR) { 148 if (CAR instanceof ByteString) { 149 this.CAR = CAR; 150 } else if (CAR instanceof PublicKeyReference) { 151 this.CAR = CAR.getBytes(); 152 } else { 153 this.CAR = new ByteString(CAR.toString(), ASCII); 154 } 155 } 156 157 158 159 /** 160 * Set the certificate holder reference 161 * 162 * @param {String} CHR the CHR value 163 * @param {ByteString} CHR the CHR value 164 * @param {PublicKeyReference} CHR the CHR value 165 */ 166 EAC2CVCertificateGenerator.prototype.setCHR = function(CHR) { 167 if (CHR instanceof ByteString) { 168 this.CHR = CHR; 169 } else if (CHR instanceof PublicKeyReference) { 170 this.CHR = CHR.getBytes(); 171 } else { 172 this.CHR = new ByteString(CHR.toString(), ASCII); 173 } 174 } 175 176 177 178 /** 179 * Set the effective date 180 * 181 * @param {String} effectiveDate the effective date in the format YYMMDD 182 * @param {Date} effectiveDate the effective date as Date object 183 */ 184 EAC2CVCertificateGenerator.prototype.setEffectiveDate = function(effectiveDate) { 185 if (effectiveDate instanceof Date) { 186 this.effectiveDate = EAC2CVCertificateGenerator.dtos(effectiveDate); 187 } else { 188 this.effectiveDate = effectiveDate; 189 } 190 } 191 192 193 194 /** 195 * Set the expiry date 196 * 197 * @param {String} expiryDate the expiry date in the format YYMMDD 198 * @param {Date} expiryDate the expiry date as Date object 199 */ 200 EAC2CVCertificateGenerator.prototype.setExpiryDate = function(expiryDate) { 201 if (expiryDate instanceof Date) { 202 this.expiryDate = EAC2CVCertificateGenerator.dtos(expiryDate); 203 } else { 204 this.expiryDate = expiryDate; 205 } 206 } 207 208 209 210 /** 211 * Set the object identifier of the authorization template for the generated certificate 212 * 213 * @param {ByteString} oid the object identifier for the chat 214 */ 215 EAC2CVCertificateGenerator.prototype.setChatOID = function(oid) { 216 this.chatOID = oid; 217 } 218 219 220 221 /** 222 * Set the authorization level of the authorization template for the generated certificate 223 * 224 * @param {ByteString} authLevel the encoded authorization level 225 */ 226 EAC2CVCertificateGenerator.prototype.setChatAuthorizationLevel = function(authLevel) { 227 this.chatAuthorizationLevel = authLevel; 228 } 229 230 231 232 /** 233 * Set the algorithm identifier for terminal authentication 234 * 235 * @param {ByteString} oid the object identifier as specified in appendix A.6.4 236 */ 237 EAC2CVCertificateGenerator.prototype.setTAAlgorithmIdentifier = function(oid) { 238 this.taOID = oid; 239 } 240 241 242 243 /** 244 * Set some additional extensions 245 * 246 * @param {Array of ASN.1 objects} extensions array containing the ASN.1 encoded extensions for the certificate 247 */ 248 EAC2CVCertificateGenerator.prototype.setExtensions = function(extensions) { 249 this.extensions = extensions; 250 } 251 252 253 254 /** 255 * Set the public key to be included in the certificate 256 * 257 * @param {Key} publicKey the public key object to be certified 258 */ 259 EAC2CVCertificateGenerator.prototype.setPublicKey = function(publicKey) { 260 this.publicKey = publicKey; 261 } 262 263 264 265 /** 266 * Set whether to include domain parameters in the certificate or not 267 * 268 * @param {Boolean} value the flag indicator 269 */ 270 EAC2CVCertificateGenerator.prototype.setIncludeDomainParameters = function(value) { 271 this.includeDomainParameters = value; 272 } 273 274 275 276 /** 277 * Internal functions for the generation of a certificate 278 * @private 279 */ 280 EAC2CVCertificateGenerator.prototype.getCAR = function() { 281 var t = new ASN1("Certification Authority Reference", 0x42, this.CAR); 282 return t; 283 } 284 285 286 287 /** 288 * Internal functions for the generation of a certificate 289 * @private 290 */ 291 EAC2CVCertificateGenerator.prototype.getCHR = function() { 292 var t = new ASN1("Certification Holder Reference", 0x5F20, this.CHR); 293 return t; 294 } 295 296 297 298 /** 299 * Internal functions for the generation of a certificate 300 * @private 301 */ 302 EAC2CVCertificateGenerator.convertDate = function(date) { 303 304 var temp = new ByteString(date, ASCII); 305 var bb = new ByteBuffer(); 306 var singleByte; 307 308 for (var i = 0; i < temp.length; i++) { 309 bb.append(temp.byteAt(i) - 0x30); 310 } 311 312 return bb.toByteString(); 313 } 314 315 316 317 /** 318 * Internal functions for the generation of a certificate 319 * @private 320 */ 321 EAC2CVCertificateGenerator.prototype.getEffectiveDate = function() { 322 var t = new ASN1("Certificate Effective Date", 0x5F25, 323 EAC2CVCertificateGenerator.convertDate(this.effectiveDate)); 324 return t; 325 } 326 327 328 329 /** 330 * Internal functions for the generation of a certificate 331 * @private 332 */ 333 EAC2CVCertificateGenerator.prototype.getExpiryDate = function() { 334 var t = new ASN1("Certificate Expiration Date", 0x5F24, 335 EAC2CVCertificateGenerator.convertDate(this.expiryDate)); 336 return t; 337 } 338 339 340 341 /** 342 * Internal functions for the generation of a certificate 343 * @private 344 */ 345 EAC2CVCertificateGenerator.prototype.getCHAT = function() { 346 var t = new ASN1("Certificate Holder Authorization Template", 0x7F4C); 347 348 var oid = new ASN1("Object Identifier", ASN1.OBJECT_IDENTIFIER, this.chatOID); 349 var authLevel = new ASN1("Authorization Level", 0x53, this.chatAuthorizationLevel); 350 351 t.add(oid); 352 t.add(authLevel); 353 354 return t; 355 } 356 357 358 359 /** 360 * Strips leading zeros of a ByteString 361 * 362 * @param {ByteString} value the ByteString value 363 * @return the stripped ByteString object, may be an empty ByteString 364 * @type ByteString 365 */ 366 EAC2CVCertificateGenerator.prototype.stripLeadingZeros = function(value) { 367 var i = 0; 368 for (; (i < value.length) && (value.byteAt(i) == 0); i++); 369 370 return value.right(value.length - i); 371 } 372 373 374 375 /** 376 * Get the encoded public key including domain parameters 377 * 378 * @private 379 */ 380 EAC2CVCertificateGenerator.prototype.getPublicKey = function() { 381 382 var t = new ASN1("Public Key", 0x7F49); 383 t.add(new ASN1("Object Identifier", 0x06, this.taOID)); 384 385 if (typeof(this.publicKey.getComponent(Key.ECC_P)) != "undefined") { 386 if (this.includeDomainParameters == true) { 387 388 t.add(new ASN1("Prime Modulus", 0x81, this.stripLeadingZeros(this.publicKey.getComponent(Key.ECC_P)))); 389 t.add(new ASN1("First coefficient a", 0x82, this.stripLeadingZeros(this.publicKey.getComponent(Key.ECC_A)))); 390 t.add(new ASN1("Second coefficient b", 0x83, this.stripLeadingZeros(this.publicKey.getComponent(Key.ECC_B)))); 391 392 t.add(new ASN1("Base Point G", 0x84, EAC2CVCertificateGenerator.encodeUncompressedECPoint(this.publicKey.getComponent(Key.ECC_GX), this.publicKey.getComponent(Key.ECC_GY)))); 393 394 t.add(new ASN1("Order of the base point", 0x85, this.stripLeadingZeros(this.publicKey.getComponent(Key.ECC_N)))); 395 } 396 397 t.add(new ASN1("Public Point y", 0x86, EAC2CVCertificateGenerator.encodeUncompressedECPoint(this.publicKey.getComponent(Key.ECC_QX), this.publicKey.getComponent(Key.ECC_QY)))); 398 399 if (this.includeDomainParameters == true) { 400 t.add(new ASN1("Cofactor f", 0x87, this.stripLeadingZeros(this.publicKey.getComponent(Key.ECC_H)))); 401 } 402 } else { 403 t.add(new ASN1("Composite Modulus", 0x81, this.stripLeadingZeros(this.publicKey.getComponent(Key.MODULUS)))); 404 t.add(new ASN1("Public Exponent", 0x82, this.stripLeadingZeros(this.publicKey.getComponent(Key.EXPONENT)))); 405 } 406 407 return t; 408 } 409 410 411 412 /** 413 * Internal functions for the generation of a certificate 414 * @private 415 */ 416 EAC2CVCertificateGenerator.prototype.getProfileIdentifier = function() { 417 418 var bb = new ByteBuffer(); 419 bb.append(this.profileIdentifier); 420 421 var t = new ASN1("Certificate Profile Identifier", 0x5F29, bb.toByteString()); 422 return t; 423 } 424 425 426 427 /** 428 * Internal functions for the generation of a certificate 429 * @private 430 */ 431 EAC2CVCertificateGenerator.prototype.getExtensions = function() { 432 var t = new ASN1("Certificate Extensions", 0x65); 433 for (var i = 0; i < this.extensions.length; i++) 434 t.add(this.extensions[i]); 435 return t; 436 } 437 438 439 440 /** 441 * Internal functions for the generation of a certificate 442 * @private 443 */ 444 EAC2CVCertificateGenerator.prototype.getCertificateBody = function() { 445 446 var t = new ASN1("Certificate Body", 0x7F4E); 447 448 t.add(this.getProfileIdentifier()); 449 450 t.add(this.getCAR()); 451 452 t.add(this.getPublicKey()); 453 454 t.add(this.getCHR()); 455 456 t.add(this.getCHAT()); 457 458 t.add(this.getEffectiveDate()); 459 460 t.add(this.getExpiryDate()); 461 462 if (this.extensions) { 463 t.add(this.getExtensions()); 464 } 465 466 return t; 467 } 468 469 470 471 /** 472 * Generate a certificate based on the parameter set using the setter methods. 473 * 474 * @param {Key} signingKey the key to be used for signing the certificate 475 * @param {ByteString} taOID the object identifier associated with the signing key 476 * @return the CVC certificate 477 * @type CVC 478 */ 479 EAC2CVCertificateGenerator.prototype.generateCVCertificate = function(signingKey, outertaOID) { 480 481 var certificate = new ASN1("CV Certificate", 0x7F21); 482 483 var body = this.getCertificateBody(); 484 485 if (typeof(outertaOID) == "undefined") { 486 outertaOID = this.taOID; 487 } 488 var mech = CVC.getSignatureMech(outertaOID); 489 var signature = this.crypto.sign(signingKey, mech, body.getBytes()); 490 491 if (CVC.isECDSA(outertaOID)) { 492 var keylen = signingKey.getSize() >> 3; 493 var signatureValue = new ASN1("Signature", 0x5F37, CVC.unwrapSignature(signature, keylen)); 494 } else { 495 var signatureValue = new ASN1("Signature", 0x5F37, signature); 496 } 497 498 certificate.add(body); 499 500 certificate.add(signatureValue); 501 502 return new CVC(certificate); 503 } 504 505 506 507 EAC2CVCertificateGenerator.test = function() { 508 var test = function(crypto, priKey, pubKey, taOID) { 509 generator = new EAC2CVCertificateGenerator(crypto); 510 511 var CAR = "decvca00000"; 512 generator.setCAR(CAR); 513 514 var CHR = "decvca00000"; 515 generator.setCHR(CHR); 516 517 generator.setEffectiveDate(new Date()); 518 519 var notAfter = "110225"; 520 generator.setExpiryDate(notAfter); 521 522 var chatOID = "0.4.0.127.0.7.3.1.2.1"; // inspection system 523 generator.setChatOID(new ByteString(chatOID, OID)); 524 525 var chatAuth = "E3"; // CVCA, read access to eID, DG3, DG4 526 527 generator.setChatAuthorizationLevel(new ByteString(chatAuth, HEX)); 528 529 generator.setPublicKey(pubKey); 530 531 var profileIdentifier = 0x00; 532 533 generator.setProfileIdentifier(profileIdentifier); 534 535 generator.setTAAlgorithmIdentifier(taOID); 536 537 //var extensions = new Array(); 538 //extensions[0] = new ASN1("ext1", ASN1.OBJECT_IDENTIFIER, new ByteString("2A1200", HEX)); 539 //extensions[1] = new ASN1("ext2", ASN1.OBJECT_IDENTIFIER, new ByteString("2A1200", HEX)); 540 541 //generator.setExtensions(extensions); 542 543 generator.setIncludeDomainParameters(true); 544 545 var cvc = generator.generateCVCertificate(priKey, taOID); 546 GPSystem.trace(cvc); 547 548 GPSystem.trace(new ASN1(cvc.getBytes())); 549 cvc.verifyWith(crypto, cvc.getPublicKey(), cvc.getPublicKeyOID()); 550 } 551 552 var crypto = new Crypto(); 553 554 var priKey = new Key(); 555 var pubKey = new Key(); 556 priKey.setType(Key.PRIVATE); 557 pubKey.setType(Key.PUBLIC); 558 priKey.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256t1", OID)); 559 pubKey.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256t1", OID)); 560 crypto.generateKeyPair(Crypto.EC, pubKey, priKey); 561 562 test(crypto, priKey, pubKey, new ByteString("id-TA-ECDSA-SHA-256", OID)); 563 564 565 var priKey = new Key(); 566 var pubKey = new Key(); 567 priKey.setType(Key.PRIVATE); 568 pubKey.setType(Key.PUBLIC); 569 priKey.setComponent(Key.ECC_CURVE_OID, new ByteString("secp521r1", OID)); 570 pubKey.setComponent(Key.ECC_CURVE_OID, new ByteString("secp521r1", OID)); 571 crypto.generateKeyPair(Crypto.EC, pubKey, priKey); 572 573 test(crypto, priKey, pubKey, new ByteString("id-TA-ECDSA-SHA-512", OID)); 574 575 576 var priKey = new Key(); 577 var pubKey = new Key(); 578 priKey.setType(Key.PRIVATE); 579 pubKey.setType(Key.PUBLIC); 580 pubKey.setSize(1024); 581 crypto.generateKeyPair(Crypto.RSA, pubKey, priKey); 582 583 test(crypto, priKey, pubKey, new ByteString("id-TA-RSA-v1-5-SHA-256", OID)); 584 } 585